스프링이 클라이언트의 요청을 처리하는 전반적인 로직 (servlet , dispatcher servlet, interceptor, filter, aop)

안녕하세요. 코딩 신생아입니다.

 

학교 드림학기제로 보안 부분을 신경써서 개발 하기 전, 비식별화와 관련된 부분을 알아보고

로그인, 회원가입 쪽에서 이 부분을 적용할 예정이라 이를 구현하던 와중,  보다 더 정확하고 다양하게 보안을 적용하기 위해 

스프링이 클라이언트의 요청을 어떻게 처리하는지 알아보았습니다.

 

전체적인 프로세스는 아래와 같으며, 하나씩 알아보겠습니다.

 

전체적인 프로세스

 

Tomcat

 

 

탐캣(tomcat)은 was(web application server)의 미들웨어로 알려져 있다. 

웹 서버와 웹 컨테이너의 결합으로, 탐캣은 현재 가장 많이 사용되는 was이다.

 

스프링을 시작할때 보이는 'Tomcat started on port 8080' 에서도 보이듯이,

JSP와 서블릿 처리, 서블릿의 수명 주기 관리, 요청 URL을 서블릿 코드로 매핑, HTTP 요청 수신 및 응답, 필터 체인 관리 등을 처리해준다.

 

Web server

 

웹 서버는 클라이언트가 요청한 정적인 콘텐츠를 http프로토콜을 통하여 제공해주는 서버이다. 정적인 콘텐츠 제공 역할 뿐아니라, 동적인 요청이 클라이언트로부터 들어왔을 때, 해당 요청을 웹 서버에서 처리할 수 없기에, 컨테이너 (웹 컨테이너) 로 보내주는 역할을 한다. 

 

웹 서버를 통해 정적인 파일들을 application server까지 안가고 앞 부분에서 빠르게 보여줄 수 있고, 기능을 분배해 서버의 부담을 줄일 수 있다.

 

web container

 

동적인 데이터들을 처리하여 정적인 페이지로 생성해주는 소프트웨어 모듈이다.

 

사용자가 로그인해서 My Page 메뉴에 들어간다고 가정해보자
이 메뉴에서는 각자 사용자에 따라 보여질 정보가 다르다. 사용자의 요청이 들어오면 웹 서버는 정적인 요소만 클라이언트 측에 보낼 수 있고, 동적으로 처리해야 하는 부분은 처리할 수 없다.
컨테이너는 이러한 부분을 대신 처리해서 웹 서버에 정적인 파일로 만들어서 보내주는 모듈이라고 생각하면 될 것 같다.

 

web server + web container 조합의  장점

 

  • 기능을 분리해 서버 부하 방지
    • 정적 콘텐츠와 동적 콘텐츠의 처리를 분배하여 데이터 처리로 인한 부하를 줄이고, 수행 속도 또한 빠르게 한다.
  • 물리적으로 분리하여 보안 강화
    • SSL에 대한 복호화, 암호화 처리에 web server를 사용한다.
  • 여러 대의 was 연결 가능

 

servlet

 

클라이언트의 요청에 대해 상응하는 결과를 Return해주어야 하는데, 웹 페이지 혹은 결과값을 동적으로 생성해 주기 위한 역할을 하는 자바 프로그램을 서블릿(servlet)이라고 한다.

 

서블릿 인터페이스는 다음과 같이 구현되며, init -> service -> destroy 의 라이프사이클을 따른다.

 

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    public String getServletInfo();

    public void destroy();
}

 

서블릿 컨테이너에서 서블릿의 생명주기를 관리하는데, 

클라이언트 1이 요청을 하면, 톰캣은 servlet이 메모리에 로드되어 있는지 확인한다. 없으므로, init()으로 servlet 인스턴스를 메모리에 로드하고, doGet()이나 doPost() 메서드를 호출하며 서비스를 한다.

클라이언트 2가 동일 서블릿에 대해 요청하면, 로드된 servlet을 재사용하여 서비스 메서드를 호출한다.

 

서블릿 동작 과정

 

 

스프링MVC에서는 Dispatcher Servlet 이라고 클라이언트 요청을 받으면 제일 앞에서 서버로 들어오는 모든 요청을 처리하는 Front Controller 가 존재한다. 

 

기존에는 모든 servlet에 대해 url 매핑을 활용하기 위해서 web.xml에 모두 등록해주어야 했지만, 

Dispatcher Servlet덕에 모든 요청을 핸들링할 수 있어 작업의 효율을 높였다.

 

Dispatcher Servlet 동작 과정
web 동작 과정

 

공통 프로세스에 대한 처리

 

자바 웹 개발을 하면, 공통으로 처리를 해야하는 업무들이 많다.

공통 업무의 예로는 로그인 처리, 권한 체크, xss(cross site script)방어, pc와 모바일웹의 분기처리, 로그, 페이지 인코딩 변환 등이 있다.

 

공통 업무에 대한 코드를 매번 반복해서 짜게되면, 중복 코드가 많아지고, 소스 관리도 힘들고, 서버 부하 또한 줄 수 있다.

따라서 공통 부분을 따로 관리할 수 있도록 하기 위해 

  • Interceptor
  • Filter
  • AOP

를 사용한다. 

 

 

하나씩 알아보자.

Interceptor

 

인터셉터(Interceptor)는 handler를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 일종의 필터이다.

요청에 대해 컨트롤러의 핸들러로 도달하기 전에 낚아채서 개발자가 원하는 추가적인 작업을 한 후, 핸들러로 보내줄 수 있도록 해주는 것이다.

 

인터셉터를 활용해 클라이언트의 요청과 관련되, 전역적으로 처리해야하는 작업들을 할 수 있다.

이에 대한 대표적인 예로, 인증과 인가와 같은 요청 작업이 있어, 클라이언트로 넘어가기 전에 검사해야하는 부분이므로, 인터셉터가 처리하기에 적합하다.

 

 

스프링에서 제공하는 org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현하거나,

org.springframework.web.servlet.handler.HandlerInterceptorAdapter 추상클래스를 오버라이딩 하여 구현할 수 있다.

어떤 메서드를 가지고 있는지 알아보자.

 

preHandle()

 

  • 컨트롤러가 호출되기 전에 실행됨
  • 실행되어야할 '핸들러'에 대한 정보를 인자값으로 받기 때문에 '서블릿 필터'에 비해 세밀하게 로직을 구성할 수 있음
  • 리턴값이 boolean

 

postHandle()

 

  • 핸들러가 실행은 완료 되었지만 아직 view가 생성되기 이전에 호출
  • 비동기적 요청처리 시에는 처리되지 않음

 

afterCompletion()

 

  • 모든 view에서 최종 결과를 생성하는 일을 포함한 모든 작업이 완료된 후 실행됨
  • 비동기적 요청처리 시에는 처리되지 않음

 

Filter

 

클라이언트의 요청이 서버로 전달되면, 해당 요청은 Dispatcher Servlet이전에 등록된 Filter체인을 따라 순차적으로 가로채진다.

응답의 경우도, 전달되기 전에 등록된 Filter체인을 따라 가공된다.

 

doFilter() 메서드를 구현하여 처리하는데, ServletRequest와 ServletResponse를 파라미터로 받아서 다음 필터나 Dispatcher Servlet으로 전달한다. 

 

Interceptor와 Filter모두 요청을 가로채서 작업을 하는 것 같은데 차이가 무엇일까?

필터는 웹 컨테이너로 Dispatcher Servlet 앞에서 작동한다.

따라서

필터는 특정 요청과 컨트롤러에 관계없이 전역적으로 적용하면 좋고,

 

 

인터셉터는 요청 url을 처리할 수 있는 컨트롤러를 찾는 핸들러 호출 전 후에 작동한다.

따라서 

인터셉터는 요청과 관련된 추가적인 요구사항을 만족해야할때 적용하면 좋다. 

 

AOP

 

AOP(Aspect-Oriented Programming)은 프로그래밍 패러다임 중 하나로, 관심사의 분리를 위해 사용되는 기술이다.

핵심 비즈니스 로직과 관련 없는 부가적인 기능들을 모듈화해 코드의 중복을 줄이고 

유지보수성을 향상시키는 데에 주로 활용된다.

 

일반적으로 중복되는 코드 부분 (commit, rollback, log처리) 를 별도의 영역으로 분리하여 필요할 때마다 가져다 쓸 수 있게 객체화 하는 기술을 말한다.

 

우선 AOP와 관련된 용어를 먼저 정리해보자.

 

AOP 용어 정리

 

  • AOP 프록시
    • AOP 기능을 구현하기 위해 만든 프록시 객체
  • 조인 포인트 (Join Point)
    • 어드바이스가 적용될 수 있는 위치로, AOP를 적용할 수 있는 지점이다.
    • 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한된다.
  • 포인트 컷(Pointcut)
    • 조인 포인트 중에서 어드바이스를 어디에 적용할지, 적용하지 않을지 위치를 판단하는 (필터링) 기능이다.
  • 타겟 (target)
    • 어드바이스를 받는 객체, 포인트 컷으로 결정
  • 어드바이스 (Advice)
    • 실제로 수행될 코드
    • Around, Before, After와 같은 다양한 종류의 어드바이스가 있음
  • 에스펙트 (Aspect)
    • 어드바이스 + 포인트 컷 을 모듈화 한 것
    • 하나의 어드바이스만이 아닌 여러 어드바이스와 포인트 컷이 함께 존재할 수 있다.

 

 

후기

 

앞으로 하나씩 적용해보자.

 

참고

 

[웹 서비스 구조 정리]

[자바 서블릿 기본서]

[스프링 인터셉터(Interceptor)란?]

[spring 처리단계]