Annotation
Spring 을 공부하면서 가장 러닝커브가 높았던 것이 Annotation이었다.
Annotation을 쓰지 않던 (현재는 지원하는 것도 많지만) 언어를 먼저 공부하기도 했고, 의존성 주입이나, 작성한 Configuration을 Framework가 알아서 가져와 사용한다는 개념이 익숙지 않았었다.
그래서 항상 기본 도서를 보고, 실무에 사용된 코드를 보면서 기능은 알겠는데, 명확한 차이를 몰라 익숙한대로 써왔었는데, 하나씩 포스팅하며 정리해보도록 하겠다.
XML
아직 XML을 사용하는 곳은 많다.
시스템 전체에 영향을 주거나 이후에 변경 가능성이 있는 내용은 XML로 정의하면서 결합도를 낮추고 유지보수성을 높여준다.
다만, 너무 내용이 많아지면 오히려 유지보수성이 낮아지는 상황도 발생한다.
한 페이지의 모든 설정들을 다 빼두었다 생각하면 좀 더 이해하기 쉬울지 모른다.
물론 간단한 개발 프로젝트일 경우, XML로 정의하는게 더 깔끔하다고 느낄 수 있다.
다만, IT가 발전하고, 그 규모가 방대해지면서 XML이 가지게 될 설정의 양이 점차 많아진다.
Annotation은 의미 있는 주석이라는 뜻을 가졌고, 주석이 선언적 프로그래밍 모델(메타데이터)을 지원하는 기술로, 컴파일, 런타임 시에 반영한다.
Annotation의 종류
크게 Annotation은 두 가지로 정리할 수 있다.
- 빈을 선언하는 어노테이션
- 의존성을 연결하는 어노테이션
빈 선언
크게 @Component, @Service, @Controller, @Repository 를 포함한 스테레오 타입 어노테이션 세트를 제공한다.
스프링이 관리해야 하는 클래스에 이러한 어노테이션을 적용할 수 있다.
스프링은 @ComponentScan 어노테이션에 입력한 기본 패키지부터 스캔해 해당 어노테이션이 달린 클래스를 수집한다.
@Component | 제네릭 스테레오 타입 해당 클래스를 인스턴스화 |
@Service | @Component를 특수화 DDD에서의 서비스 혹은 비즈니스 서비스 파사드(facade) 비즈니스 계층에서 사용 |
@Repository | DDD에서의 레포지토리(repository) 또는 DAO(Data Access Object) 영속성 계층에서 사용 |
@Controller | 컴포넌트가 HTTP 요청을 받을 수 있는 웹 콘트롤러임을 나타냄 프레젠테이션 계층에서 사용 |
클래스 레벨에 @Component를 사용해두면, 스프링에서 관리하는 빈을 가지게 된다.
그럼 AppConfig class (ApplicationContext에 넘겨주는 설정파일) 에 굳이, 해당 클래스의 인스턴스를 리턴해주는 메소드들을 만들어 줄 필요가 없다.
@Configuration
@ComponentScan("app.messages")
public class AppConfig {
@Bean
public MessageRepository messageRepository() {
return new MessageRepository();
}
@Bean
MessageService messageService() {
return new MessageService(messageRepository());
}
}
먼저, @Configuration을 통해, AppConfig.java 파일이 빈을 정의하기 위함을 알려준다.
@ComponentScan을 통해서, 컴포넌트를 스캔할 기본 패키지를 스프링에 알려준다.
MessageRepository와 MessageService라는 클래스가 있다고 할 때, @Bean을 통해, 메소드들이 Bean 을 생성하도록 한다. (메소드 이름이 빈의 이름이다)
그럼 Application.java에서 다음과 같이 쓸 수 있다. (main 함수)
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MessageService messageService = context.getBean(MessageService.class);
messageService.save("Hello, Spring!");
}
}
AppConfig를 ApplicationContext에 전달하면서, 스프링 컨테이너가 생성되었다.
getBean은 MessageService 빈의 인스턴스를 가져오며, 이 순간 스프링 컨테이너에서 가져온 messageService 빈은 완전히 인스턴스화, 특히 MessageRepository가 주입된다.
그런데, @Component를 MessageRepository와 MessageService 클래스 위에 선언하면, @Bean이 붙어있던 두 메소드는 필요가 없어진다.
의존성 주입
의존성을 연결할 때 스프링은 @Required 와 @Autowired 어노테이션을 사용할 수 있다.
@Required 어노테이션을 세터 메소드에, @Autowired 어노테이션을 생성자와 메소드, 필드에 적용할 수 있다.
의존성을 주입할 때는 다음 3가지 방법이 있다.
- 생성자 기반의 주입
- 세터 / 메소드 기반의 주입
- 필드 기반의 주입
생성자 기반의 주입은 말 그대로 생성자에 @Autowired를 선언하면서, Bean을 주입할 수 있다.
세터/메소드 기반에서는 선언된 메소드 (일반적으로 세터)에서 @Autowired 또는 @Required 를 적용한다.
필드에서는 @Autowired를 선언해 적용이 가능하며 이 때는 세터가 따로 필요 없다.
위 2가지 말고도, @Resource, @Inject 등의 어노테이션이 있다.
의존성을 주입할 때는 무작정 주입하는 건 좋지 못하다.
SOLID라고 하는 객체지향의 원칙 중, SRP(단일 책임 원칙)에 맞게, 항상 생성자를 통해 주입하는 것이 좋다. (생성 이후에 인스턴스는 초기화되고, 읽기 전용으로 만든다)
특히나, 필드 기반의 주입은 사용하지 않도록 하는 것이 좋다.
스프링 MVC
웹 애플리케이션을 구현할 때 가장 자주 나오는 단어 중 한가지가, MVC이다.
이 MVC 전에, 스프링에서의 서블릿이 어떻게 동작하는 지 알아보자.
자바 EE 서블릿 (Java EE Servlet) 는 일반적으로 톰캣과 같은 애플리케이션 서버인 서블릿 컨테이너 내에서 동작한다.
HTTP 요청이 서버에 도착하면 인증, 로깅, 감사같은 필터링 작업을 수행하고, WAS는 특정 패턴과 일치하는 URI를 포함하는 요청을 처리할 수 있게 등록된 서블릿으로 요청을 넘겨준다.
자바 EE 에서 모든 HTTP 요청에 대해 HttpServletRequest 인스턴스가 생성된다. 그리고 모든 HTTP 응답에 대해 HttpServletResponse 인스턴스가 생성된다.
요청에서 사용자 식별을 위해 WAS는 HttpSesstion 인스턴스를 생성하고, 각 HttpSession 인스턴스는 세션ID라고 부르는 ID를 가진다.
해당 ID와 쿠키를 통해, 개인을 식별하며 HttpSessionListener를 통해 HttpSession 라이프 사이클을 관리하는 등의 작업을 할 수 있다.
서블릿에서는 HTTP Method와 연결된 doGet, doPost, doPut, doDelete 작업을 수행하며, 이 때, in-memory 데이터 혹은 I/O 작업을 수행한다면 공유 리소스에 대한 동시 요청 등 하나의 요청이 다른 요청에 대한 영향이 있을 수 있다는 것을 염두해야 한다.
DispatcherServlet
스프링 MVC를 사용하면 서블릿을 생성할 필요가 없다. 클래스를 생성해 @Controller 어노테이션을 추가하고 @RequestMapping 어노테이션으로 특정 URI 패턴에 매핑할 수 있다.
스프링은 요청을 받기 위해 핵심 서블릿인 DispatcherServlet을 활용한다. 이 DispatcherServlet은 모든 요청을 처리할 수 있어야 하며, @RequestMapping 어노테이션에 지정된 URI 패턴에 따라 스프링은 요청을 처리할 패턴에 맞는 컨트롤러를 찾는다.
조금 더 DispatcherServlet에 대해 알아보자.
"보내다" 라는 뜻이 있는 Servlet의 의미에 맞게 좀더 구체적인 정의는 다음과 같다.
Servlet Container에서 HTTP 프로토콜을 통해 들어오는 모든 요청을 프레젠테이션 계층의 제일 앞에 둬서 중앙집중식으로 처리해주는 프론트 컨트롤러(Front Controller)
조금 더 구체적인 내용은 아래 블로그를 참고하고, 나중에 다시 정리하자
[ 참고 : https://galid1.tistory.com/521?category=783055 ]
[ 참고 : https://mangkyu.tistory.com/18 ]
MVC
C(Controller)를 통해, URI 매핑으로 전달, HTML 마크업이 위치하는 V(View) 그리고 컨트롤러가 생성하고 HTTP 응답을 통해 클라이언트에 전송되어 뷰가 최종 결과를 렌더링하는 데 사용할 수 있는 데이터를 포함하는 M(Model)을 통해 간단한 웹사이트부터 거대한 웹사이트까지 만들 수 있다.
특히, SpringMVC에서는 템플릿 엔진을 Import 받아, resources에 html 파일에서 렌더링 할 수 있도록 쓸 수 있으며, ModelAndView와 같은 인스턴스로 만들어 줄 수 있다.
필터
필터는 디자인 패턴인 책임 연쇄 패턴(Chain of Responsibility)를 구현한 것이다.
쉽게 말하면, 자신이 처리할 수 있는 일은 처리하고 그 외의 일은 같은 인터페이스를 가지고 본인보다 우선순위가 낮은 다른 객체에 넘기는 것이다.
여기서는 Servlet으로 처리를 넘기기 전에, 사전에 처리하고 보낸다.
AOP나 Interceptor도 비슷한 개념이다.
AOP의 자세한 사항은 잠시 후 보도록 하고 위 3가지를 비교하면 다음과 같다.
위와 같이, Filter는 스프링 영역이 아닌, Servlet으로 들어가기 전 단계이다. 따라서, 전체 요청/응답을 관리하지만, Interceptor와 AOP 는 Servlet 단위로 생각해야 한다.
흔히, Filter는 인코딩 변환 처리, XSS 방어 등에 사용하는 등으로 이야기 하지만 전체적으로 비슷한 경우에 사용할 수 있다.
다만, 그 이상 분간하여 개발하기 위해서는 다음 블로그를 참고하자
[ 참고 : http://meetup.toast.com/posts/151 ]
[ 참고 : https://velog.io/@sa833591/Spring-Filter-Interceptor-AOP-%EC%B0%A8%EC%9D%B4-yvmv4k96 ]
[ 참고 : https://goddaehee.tistory.com/154 ]
AOP
AOP는 OOP를 보완하기 위해 나왔다고 보면 더욱 이해하기 쉽다.
OOP를 통해 각 모듈을 개발하여 나가지만, Logging / Security 등의 내용은 전체적인 모듈에 적용되거나, 공통적으로 적용될 경우가 많다.
즉, 적용되고자 하는 관점에 집중하여 관점 지향 프로그래밍 (Aspect Oriented Programming)이라 한다.
따라서, Aspects는 관심사를 모듈화한 것으로, 해당 관심에 대한 로직을 하나의 Aspect에 작성한다.
스프링 AOP에서는 @Aspect 어노테이션을 적용해 사용할 수 있다.
조인 포인트(Join points)는 특정 프로그램이 실행되는 지점이다.
스프링 AOP에서 조인 포인트는 항상 메소드 호출을 나타낸다.
어드바이스(Advices)는 특정 관심사를 처리하는 행동으로 볼 수 있다 (EX> 보안, 로깅 등)
따라서 다양한 어드바이스가 존재한다. (행동이 언제 진행되는지)
'Framework > Spring' 카테고리의 다른 글
[Spring] 스프링의 DI가 뭘까? (0) | 2022.07.26 |
---|---|
[Spring] 스프링의 Bean을 어떻게 쓸 수 있을까? (0) | 2022.07.23 |
[Spring] 스프링의 IoC Container가 뭘까? (0) | 2022.07.23 |
[Spring] 스프링 프레임워크가 뭘까? (0) | 2022.07.22 |
Spring Framework 아주 조금만 알아보자 - INTRO (0) | 2020.11.19 |