Servlet
동적 웹 페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술이다. 서블릿은 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해준다.
Dispatcher Servlet
HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임(보내다=dispatch) 해주는 프론트 컨트롤러(Front Controller)라고 정의할 수 있다.
이것을 보다 자세히 설명하자면, 클라이언트로부터 어떠한 요청이 오면 Tomcat(톰캣)과 같은 서블릿 컨테이너가 요청을 받게 된다. 그리고 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 된다. 그러면 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임한다.
여기서 Front Controller(프론트 컨트롤러)라는 용어가 사용되는데, Front Controller는 주로 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러로써, MVC 구조에서 함께 사용되는 디자인 패턴이다.
Dispatcher-Servlet(디스패처 서블릿)의 장점
Spring MVC는 DispatcherServlet이 등장함에 따라 web.xml의 역할을 상당히 축소시켜주었다. 과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주어야 했지만, dispatcher-servlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리면서 상당히 편리하게 이용할 수 있게 되었다. 개발자는 컨트롤러를 구현해두기만 하면 디스패처 서블릿가 알아서 적합한 컨트롤러로 위임을 해주는 구조가 되었다.
Q. Spring 사용하지 않을 때는 @WebServlet으로 컨트롤러(서블릿)이랑 url을 매핑했지만
Spring을 사용할 때는 @Controller와 @RequestMapping을 사용해서 url을 매핑한다. 이 두 방식의 차이가 뭘까?
A. DispatcherServlet의 관리를 받느냐 아니냐 차이!
Spring을 사용할 때는 DispatcherServlet에 @WebServlet이 붙어있는 상황이라고 보면 된다. (@WerbServlet == web.xml)
하나의 서블릿만 사용하여 요청이 여러 Servlet으로 흩어지지 않고 모아서 처리하는게 가능해진 상황이 되는 것!
따라서 DispatcherServlet에 요청이 모이게 해서 parameter 처리나 로그인 체크 같은 공통 작업을 DispatcherServlet이 수행하여 개발자는 Controller만 개발하면 되는 상황이 되는 것이다~~
정적 자원(Static Resources)의 처리
Dispatcher Servlet이 요청을 Controller로 넘겨주는 방식은 효율적으로 보인다. 하지만 Dispatcher Servlet이 모든 요청을 처리하다보니 이미지나 HTML/CSS/JavaScript 등과 같은 정적 파일에 대한 요청마저 모두 가로채는 때문에정적자원(Static Resources)을 불러오지 못하는 상황도 발생하곤 했다. 이러한 문제를 해결하기 위해 개발자들은 2가지 방법을 고안했다.
- 정적 자원 요청과 애플리케이션 요청을 분리
- 애플리케이션 요청을 탐색하고 없으면 정적 자원 요청으로 처리
Dispatcher-Servlet(디스패처 서블릿)의 동작 방식
앞서 설명한대로 디스패처 서블릿은 적합한 컨트롤러와 메소드를 찾아 요청을 위임해야 한다. Dispatcher Servlet의 처리 과정을 살펴보면 다음과 같다.
- 클라이언트의 요청을 디스패처 서블릿이 받음
- 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
- 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
- 핸들러 어댑터가 컨트롤러로 요청을 위임함
- 비지니스 로직을 처리함
- 컨트롤러가 반환값을 반환함
- 핸들러 어댑터가 반환값을 처리함
- 서버의 응답을 클라이언트로 반환함
즉, 디스패처 서블릿을 통해 요청을 처리할 컨트롤러를 찾아서 위임하고, 그 결과를 받아온다.
DI (Dependency Injection)
DI란 의존 관계 주입 혹은 의존성 주입이라 불리고, Spring은 3가지 핵심 프로그래밍 모델(AOP, DI, IOC)을 지원하는데, 그 중 하나이다.
Spring은 객체의 의존 관계를 의존 관계 주입을 통해 관리한다.
DI는 외부에서 객체 간의 관계(의존성)를 결정해 주는데 즉, 객체를 직접 생성하는 것이 아니라 외부에서 생성 후 주입시켜 주는 방식이라 할 수 있다.
Spring에서의 DI 방법 3가지
Spring에서 의존 관계 주입(DI) 방법에는 총 3가지가 있다.
들어가기 전에 앞서 Spring 3.x 버전까지만 해도 Setter 주입을 권장하였으나, 최근에는 순환 참조 등의 문제로 인해 Spring 4.3 이후 버전부터는 생성자 주입(Construct Injection) 방법을 권장하고 있다.
- Construct Injection(생성자 주입)
- Field Injection(필드 주입)
- Setter Injection(Setter 주입)
(1) Constructor Injection(생성자 주입)
- 현재 가장 권장되는 의존 관계 주입 방식이다.
- Spring 공식 문서에서도 생성자 주입을 권장한다.
- 하나의 생성자가 존재할 때 필드 주입의 대부분의 단점을 극복한다.
- Spring 4.3부터는 @Autowired가 생략 가능해서 최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용한다.
- 추가로 Lombok 라이브러리의 @RequiredArgsConstructor를 함께 사용하면 생성자를 생략 가능해서 코드가 깔끔해진다.
- 오직 생성자 주입만이 final 키워드를 사용할 수 있고, 생성자를 통해 주입되는 방식이다.
- final 키워드를 사용하기에 생성자로 인해 인스턴스가 생성될 때 1번만 할당된다.
- 초기에 할당되기에 NPE(Null Pointer Exception)이 절대 발생하지 않는다.
public class Injection {
private InjectionService injectionService;
// 생성자 DI // @Autowired -> Spring4.3부터는 @Autowired 생략 가능
public Injection(InjectionService injectionService) {
this.injectionService = injectionService;
}
}
위에서는 생성자에 @Autowired 어노테이션을 사용하였다. 하지만 Spring 4.3부터는 @Autowired를 생략 가능하다.
생성자 주입이 가장 좋은 방법이지만, 이렇게 하면 매번 생성자를 만들어야 해서 귀찮고, 코드가 길어질 수 있다.
@RequiredArgsConstructor
public class Injection {
private final InjectionService injectionService;
}
위의 코드를 보면 @RequiredArgsConstructor 은 롬복(Lombok)의 어노테이션 중 하나이다.
해당 어노테이션은 final 키워드가 붙은 주입에만 생성자를 만들어준다. 만약 final 키워드를 사용하지 않으면 @AllArgsConstructor 어노테이션을 사용하면 된다.
(2) Field Injection(필드 주입)
[ 필드 주입의 장점 ]
- 코드가 간결해진다.
[ 필드 주입의 단점 ]
- Solid 원칙 중에 단일 책임 원칙(SRP)을 위반한다.
- Unit Test가 어렵다.
- final 키워드를 사용할 수 없다.
- 불변성이 보장되지 않는다. 따라서 객체가 변할 수 있다.
→ 사용하지말자
(3) Setter Injection(Setter 주입)
- Spring에서 @Autowired 어노테이션을 사용해서 Setter 메서드를 통해 주입하는 방법이다.
- Spring 3.x 버전까지는 DI 권장 방식이였지만, 현재는 아니다.
- NPE(Null Pointer Exception) 발생할 수 있다.
- 생성자 주입을 뺀 나머지(필드 주입, Setter 주입)은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다.
public class Injection {
private InjectionService injectionService;
@Autowired
public void setInjectionService( InjectionService injectionService) {
this.injectionService = injectionService;
}
}
※ 생성자 주입을 지향하자.
'데브코스 웹 백엔드' 카테고리의 다른 글
Public Cloud(AWS) -> On-premise(홈서버) 구축 전환 (1) | 2024.12.18 |
---|
댓글