스프링 MVC의 핵심 - DispatcherServlet

DispatcherServlet 은 클라이언트의 요청을 가장 앞단에서 받아 처리하는 스프링의 FrontController  이다.

따라서 DispatcherServlet 를 통해 처리되는 요청은 일련의 스프링 MVC 의 처리 과정을 거친다는의미이다.

 

요청 흐름

  1. 서블릿이 호출되면 HttpServlet 이 제공하는 serivce() 를 호출
  2. 스프링 MVC는 DispatcherServlet 의 부모인 FrameworkServlet 에서 service() 를 Override 해둠
  3. FrameworkServlet.service() 를 시작으로 여러 메서드가 호출되면서 DispacherServlet.doDispatch() 를 호출

 

동작 순서

  1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
  2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
  3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.
  4. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
  5. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  6. viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
    JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.
  7. View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
    JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.
  8. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

'백엔드 > Spring' 카테고리의 다른 글

BeanValidation 써보기  (0) 2023.09.22
@ModelAttribute 와 @RequestBody 써보기  (0) 2023.09.21
Thread Pool  (0) 2023.07.25
Servlet  (0) 2023.07.25
의존성 주입: Spring을 사용할 때 의존성 주입 방법  (0) 2023.07.18

자바 애플리케이션에서 쓰레드란?

애플리케이션 코드를 하나하나 순차적으로 실행하는 것은 쓰레드이다.

자바 메인 메서드를 처음 실행하면 main이라는 이름의 쓰레드가 실행된다.

쓰레드가 없다면 자바 애플리케이션 실행이 불가능하며 쓰레드는 한번에 하나의 코드 라인만 수행한다.

동시 처리가 필요하면 쓰레드를 추가로 생성하여 처리해야한다.

 

요청마다 쓰레드 생성

 

장점

  • 동시 요청을 처리할 수 있다.
  • 리소스(CPU, 메모리)가 허용할 때 까지 처리가능
  • 하나의 쓰레드가 지연돼도 나머지 쓰레드는 정상 동작한다.

단점

  • 쓰레드는 생성 비용이 매우 비싸다.
    -> 고객의 요청이 올 때 마다 쓰레드를 생성하면 응답 속도가 늦어진다.

  • 쓰레드는 컨텍스트 스위칭 비용이 발생한다.
  • 쓰레드 생성에 제한이 없다.
    -> 고객 요청이 너무 많이 오면 CPU, 메모리 임계점을 넘어서 서버가 죽을 수 있다.

 

쓰레드 풀

 

특징

필요한 쓰레드를 쓰레드 풀에 보관하고 관리한다.

쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리한다.(톰캣은 최대 200개 기본 설정)

사용

  1. 쓰레드가 필요하면, 이미 생성되어 있는 쓰레드를 쓰레드 풀에서 꺼내서 사용한다
  2. 사용을 종료하면 쓰레드 풀에 해당 쓰레드를 반납한다
  3. 최대 쓰레드가 모두 사용중이어서 쓰레드 풀에 쓰레드가 없으면?
    -> 기다리는 요청을 거절하거나 특정 숫자만큼만 대기하도록 설정할 수 있다

 

장점

  • 쓰레드가 미리 생성되어 있으므로 쓰레드를 생성하고 종료하는 비용(CPU)이 절약되고 응답 시간이 빠르다.
  • 생성 가능한 쓰레드의 최대치가 있으므로 너무 많은 요청이 들어와도 기존 요청은 안전하게 처리할 수 있다.

 

WAS의 주요 튜닝 포인트는 최대 쓰레드(max thread) 수이다.

이 값을 너무 낮게 설정하면?

동시 요청이 많으면, 서버 리소스는 여유롭지만, 클라이언트는 금방 응답 지연

이 값을 너무 높게 설정하면?

동시 요청이 많으면, CPU, 메모리 리소스 임계점 초과로 서버 다운

장애 발생시?

클라우드면 일단 서버부터 늘려서 급한불부터 끄고 이후에 튜닝하고, 클라우드가 아니면 즉시 열심히 튜닝해야한다.

 

결론

멀티 쓰레드에 대한 부분은 WAS에서 처리하므로 개발자는 멀티 쓰레드 관련 코드를 신경쓰지 않아도 된다.

따라서 개발자는 마치 싱글 쓰레드 프로그래밍을 하듯이 편리하게 소스 코드를 개발할 수 있다.

단, 멀티 쓰레드 환경이므로 싱글톤 객체(서블릿, 스프링 빈)는 주의해서 사용해야한다.

'백엔드 > Spring' 카테고리의 다른 글

BeanValidation 써보기  (0) 2023.09.22
@ModelAttribute 와 @RequestBody 써보기  (0) 2023.09.21
DispatcherServlet  (0) 2023.08.01
Servlet  (0) 2023.07.25
의존성 주입: Spring을 사용할 때 의존성 주입 방법  (0) 2023.07.18

서블릿

서블릿(Servlet)이란 동적 웹 페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술이다.

 

 

위의 그림에서 개발자가 실제로 만드는 의미있는 비즈니스 로직은 초록색 부분에 불과하다.

하지만 웹 애플리케이션 동작에는 초록색 이외의 모든 과정 또한 필요한데

이 과정들을 대신해서 처리하여 개발자가 비즈니스 로직에 집중할 수 있게 해주는 것이 서블릿이다.

즉, 서블릿은 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해주는 기술이다.

 


서블릿 컨테이너

클라이언트로부터 요청을 받을 때 서블릿을 실행시켜주는 톰캣과 같은 WAS 를 서블릿 컨테이너라고 한다.

 

서블릿 컨테이너는 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기를 관리한다.

JSP도 서블릿으로 변환 되어서 사용되며 동시 요청을 위한 멀티 쓰레드 처리를 지원한다.

 

HTTP 요청, 응답 흐름

  1. HTTP 요청 시 WAS는 Request, Response 객체를 새로 만들어서 서블릿 객체 호출
  2. 개발자는 Request 객체에서 HTTP 요청 정보를 편리하게 꺼내서 사용
  3. 개발자는 Response 객체에 HTTP 응답 정보를 편리하게 입력
  4. WAS는 Response 객체에 담겨있는 내용으로 HTTP 응답 정보를 생성

 

서블릿 객체는 싱글톤으로 관리한다.

고객의 요청이 올 때 마다 계속 객체를 생성하는 것은 비효율적이다.

따라서 서블릿 객체는 최초 로딩 시점에 싱글톤으로 미리 만들어두고 재활용한다.

이후 서블릿 컨테이너 종료시 함께 종료된다.

  • 모든 고객 요청은 동일한 서블릿 객체 인스턴스에 접근
  • 싱글톤이므로 공유 변수 사용에 주의 !

 


DispatcherServlet 

스프링을 사용해봤다면 DispatcherServlet 을 들어봤을것이다. 그렇다면 DispatcherServlet 은 뭘까?

이 앞에서 클라이언트가 요청 시 서블릿 컨테이너가 요청을 받는다고 했다.
스프링은 이 요청들을 가장 앞단에서 받아 처리하는 FrontController 라는 개념을 두었다.

FrontController 가 바로 DispatcherServlet 이다.

따라서 DispatcherServlet 을 통해 처리되는 요청은 일련의 스프링 MVC 의 처리 과정을 거친다는의미이다.

 

DispatcherServlet 의 동작 방식은 여기를 참고하자.

'백엔드 > Spring' 카테고리의 다른 글

BeanValidation 써보기  (0) 2023.09.22
@ModelAttribute 와 @RequestBody 써보기  (0) 2023.09.21
DispatcherServlet  (0) 2023.08.01
Thread Pool  (0) 2023.07.25
의존성 주입: Spring을 사용할 때 의존성 주입 방법  (0) 2023.07.18

필드 주입

필드에 바로 주입하는 방법이다.

  • 코드가 간결해 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트하기 힘들다는 치명적인 단점이 있다.
  • DI 프레임 워크가 없으면 아무것도 할 수 없다.
  • 사용하지 않는것을 권장한다.
public class OrderServiceImpl implements OrderService {
	@Autowired
	private MemberRepository memberRepository;
	@Autowired
	private DiscountPolicy discountPolicy;
}

 

수정자(setter) 주입

setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.

  • 선택, 변경 가능성이 있는 의존관계에 사용
public class OrderServiceImpl implements OrderService {
	private MemberRepository memberRepository;
	private DiscountPolicy discountPolicy;
    
	public void setMemberRepository(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
    
	public void setDiscountPolicy(DiscountPolicy discountPolicy) {
		this.discountPolicy = discountPolicy;
	}
}

 

생성자 주입

생성자를 통해서 의존 관계를 주입받는 방법이다.

  • 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
  • 불변, 필수 의존관계에 사용한다.
public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository;
	private final DiscountPolicy discountPolicy;
    
	public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
		this.memberRepository = memberRepository;
		this.discountPolicy = discountPolicy;
	}
}

 

결론

애플리케이션을 개발할 때 의존성을 변경하는 경우는 거의 없다.

변경 가능성이 있는 setter는 웬만해서는 열지 않는 것을 추천하므로 의존성 주입은 되도록 생성자 주입을 사용하자.

 

'백엔드 > Spring' 카테고리의 다른 글

BeanValidation 써보기  (0) 2023.09.22
@ModelAttribute 와 @RequestBody 써보기  (0) 2023.09.21
DispatcherServlet  (0) 2023.08.01
Thread Pool  (0) 2023.07.25
Servlet  (0) 2023.07.25

+ Recent posts