톰캣 구현하기

조현근·2022년 11월 15일
0
post-thumbnail

레벨4의 첫번째 미션, 톰캣 구현하기를 정리해보자!

학습목표

  • HTTP와 서블릿에 대한 이해도를 높인다.
  • 스레드, 스레드풀을 적용해보고 동시성 처리를 경험한다.

1단계 - HTTP 서버 구현하기

요구사항

  • Request에서 요청한 html 파일 찾아 반환하기
  • Request에서 요청한 css 파일 찾아 반환하기
  • uri로 전달되는 Query String 파싱하기

총 3가지 요구사항이 있다. string으로 들어오는 request를 적절하게 파싱해서 로직을 구현하면 된다.
단순히 메서드 분리를 통해 Http11Processor 객체에서 처리하게 구현했다. html인지 css인지에 따라 contentType을 알맞게 설정해주고, 정적파일은 responseBody에 넣어주었다.

2단계, 3단계

1단계에서 구현한 코드 리팩터링이 시급해 2단계와 3단계를 같이 진행했다.
각 단계의 요구사항을 살펴보자.

2단계 요구사항

  • 회원가입, 로그인하기 버튼은 GET이 아닌 POST로 HTTP method 변경
  • 로그인 성공/실패에 따라 다른 페이지로 리다이렉트
  • 로그인에 성공하면 세션을 사용해 로그인 상태 유지. Set-Cookie를 사용해 Response Header에 JSESSIONID=~~~~ 형태로 값을 전달하면 클라이언트가 보내는 Request Header의 Cookie필드에 값이 추가됨.

3단계 요구사항

  • HttpRequest 클래스 구현
  • HttpResponse 클래스 구현
  • Controller 인터페이스 추가

HttpRequest에서 rawString을 파싱하는 책임을 가지게 했다.
ServletContainer에서 uri에 따라 알맞은 controller를 찾고 로직을 실행시켰다. 다형성을 이용해 컨트롤러의 종류가 무엇이든 코드가 실행될 수 있게 했다.
그리고 responseManager를 통해 response를 만들어주었다.

실제 서블릿의 동작방식

실제 자바 서블릿이 어떻게 동작하는지 디버깅을 통해 알아보자.
Spring MVC 프레임워크를 사용했을때 HTTP 요청이 어떻게 처리되는지 디버깅을 통해 알아보자.
Spring boot에 내장된 tomcat이 시작되는 곳 부터 확인하자.(그 이전은 나중에 기회가 되면 알아보자..)

요약(디버깅을 통해 알아본건 이정도입니다.)

  1. tomcat이 시작되면 여러가지 객체의 start, init 메서드가 실행되는데, 이때 Connector라는 객체의 init관련 메서드에서 CoyoteAdapter가 생성된다. CoyoteAdapter는 protocolHandler라는 곳에 저장되는데 이 어뎁터는 추후에 Http11processor로 주입된다. (protocolHandler 인터페이스의 머나먼 자식이 Http11processor이다.)
  2. Http11processor는 다음과 같은 역할을 한다.
    1. rawString을 request 객체로 바꿔줌
    2. coyoteAdapter에 의해 low-level request가 HttpServletRequest관련 request로 바뀜
  3. Request는 applicationFilterChain으로 전달되고, 필터를 거쳐 dispatcherServlet으로 전달된다.

Tomcat 시작, CoyoteAdapter 생성, CoyoteAdapter를 주입받은 Http11Processor생성

TomcatWebServer라는 클래스에서 tomcat을 시작한다.

Tomcat은 server.start()를 호출한다.

LifeCycle 인터페이스의 구현체인 LifecycleBase의 start() 메서드가 실행된다.

start() 메서드 내부의 init()을 계속 따라가면 Connector라는 객체의 initInternal()이 호출되는 것을 알 수 있다.
Connector의 initInternal()에서 CoyoteAdapter가 adapter로 할당되고, ProtocolHandler라는 곳에 저장된다.
여기에서 할당된 adapter가 추후 Http11Processor로 주입된다.

Http11ProcessorAbtractHttp11Protocol이라는 객체에 의해 생성되는데, 이 객체는 위의 ProtocolHandler를 구현한 AbstractProtocol이라는 객체를 상속받은 객체이다.

Http11Processor

이제 Http11Processor부터 살펴보자.
Http11Processor의 service() 메서드가 호출되는데, 여기서 rawString이 Request 객체로 변환된다. 여기서의 Request 클래스는 low-level 서버 request이다.

CoyoteAdapter에 의해 low-level Request는 HttpServletRequest를 구현한 다른 Request객체로 변환된다.

이렇게 변환된 Request가 이후에 ApplicationFilterChain의 doFilter() 메서드로 전달되는 것이다. ApplicationFilterChain에서 동작방식은 아래 포스팅에 정리해놓았다.
링크

필터가 다 실행되고 난 후 이제 Servlet이 호출된다.
Servlet인터페이스를 구현한 GenericServlet을 상속한 HttpServlet의 service() 메서드가 호출된다.

그 이후엔 FrameworkServlet이란 객체가 호출되는데, 아무래도 스프링을 이용했기에 그런것 같다(추측입니다.).
여기서 다시 (PATCH 제외) HttpServlet의 또 다른 service()메서드가 호출되는데, 이 곳에서 doGet, doPost와 같은 메서드가 호출된다.
이후에 몇번의 과정을 거치고 FrameworkServlet을 상속한 DispatcherServlet으로 흐름이 넘어간다.

DispatcherServlet이후의 과정은 아래 포스팅에 정리해놓았다.
링크

profile
안녕하세요!

0개의 댓글