spring 동작 원리

2024. 3. 11. 19:35카테고리 없음

스프링 동작 원리를 찾아보면 이를 설명하는 다양한 그림들이 있다.

 

디스패처 서블릿

HTTP 요청을 처리하는 프론트 컨트롤러 역할을 맡고 뷰 리졸버 , 핸들러 매핑, 핸들러 어뎁터 등 여러 웹 MVC 구성요소와 상호작용한다.

디스패처 서블릿은 여러 필터 체인과 인터셉터를 가질 수 있다.

디스패처 서블릿을 설정한다면 어떠한 구현체를 사용할 것인지, 특정 URL 매핑만 인식한다던지 설정할 수 있다.

 

핸들러 매핑

 

[Spring MVC] Dispatcher Servlet, 핸들러 매핑, 핸들러 어댑터

Spring MVC 구조를 학습하게 되었습니다. 스프링 MVC는 아래와 같은 흐름으로 진행됩니다. 처음엔 그림만 보면 이해하기 어렵다고 느낄텐데요. 차근차근 따라가면 흐름은 그렇게 어렵지 않습니다. D

ksabs.tistory.com

 

[Hong]Spring에서 Handler

스프링 MVC 형식으로 코드를 수정하던 중에 핸들러의 개념에 대해서 잘 모르는 것 같아서 찾아보고 정리해보고자 합니다..핸들러.. 그냥 직역하면 '처리하는 취급자' 이런느낌입니다. 스프링에서

velog.io

url 요청에 대해 어떠한 핸들러(컨트롤러)를 사용할지 설정할 수 있다. bean 이름 혹은 어노테이션 기반의 핸들러를 찾는다.

해당 요청을 적절하게 처리할 수 있는 핸들러를 찾고 이를 디스패처 서블릿에게 알려준다. 디스패처 서블릿은 이러한 핸들러를 실행할 수 있는 어뎁터를 찾는다.

 

핸들러 어댑터

다양하게 정의된 핸들러가 있다. 서로 다른 인터페이스를 상속한 핸들러가 존재할 수 있고 , 어노테이션 기반으로 작성된 핸들러가 존재할 수 있는데 각 핸들러 유형에 따라 적절한 실행 방법을 적용하는 역할을 한다.

 

핸들러

어노테이션 기반으로 매핑하는 핸들러와 controller 인터페이스를 상속받은 핸들러, httpRequsetHandler 인터페이스를 상속받은 핸들러등 다양한 형태의 핸들러가 존재한다.

 

1. controller 인터페이스를 상속받은 핸들러

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LegacyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 요청 처리 로직을 여기에 구현합니다.
        String name = request.getParameter("name");
        String message = "Hello, " + (name != null ? name : "Guest") + "!";
        
        // ModelAndView를 사용하여 결과를 반환합니다.
        ModelAndView modelAndView = new ModelAndView("legacyView");
        modelAndView.addObject("message", message);
        return modelAndView;
    }
}

위의 예제는 controller 인터페이스를 상속 받은 핸들러이다. 

 

[src] > [main] > [webapp] > [WEB-INF] > web.xml  에 applicationContext.xml의 path가 설정되어있는데

applicationContext.xml에 아래의 코드를 추가하여 특정 요청에 대해 해당 핸들러가 동작하도록 설정할 수 있다.

<bean name="/legacy" class="com.example.LegacyController"/>

 

또한 AppConfig를 통해 설정할 수 있다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {

    @Bean
    public LegacyController legacyController() {
        return new LegacyController();
    }

    @Bean
    public SimpleUrlHandlerMapping handlerMapping() {
        SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
        Map<String, Object> urlMap = new HashMap<>();
        urlMap.put("/lagacy", legacyController());
        handlerMapping.setUrlMap(urlMap);
        return handlerMapping;
    }
}

핸들러를 빈에 등록하고 handlerMapping을 통해 URL과 핸들러를 매핑한다.

 

 

2. httpRequsetHandler 를 상속받은 핸들러

Controller 인터페이스를 구현한 핸들러와 설정방식은 비슷하지만 반환형에 대해 큰 차이가 있다.

두 핸들러 모두 handleRequest(HttpServletRequset request, HttpServletReponse response) 를 오버라이드하여 요청을 처리한다.

하지만 Controller같은 경우 ModelAndView를 반환하는 반면 HttpRequestHandler는 반환형이 void이다.

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.HttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyHttpRequestHandler implements HttpRequestHandler {

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 요청 처리 로직을 여기에 구현합니다.
        String name = request.getParameter("name");
        String message = "Hello, " + (name != null ? name : "Guest") + "!";
        
        // 응답을 생성합니다.
        response.setContentType("text/plain");
        response.getWriter().write(message);
    }
}

해당 코드에서 response.getWriter().write(message) 부분에서 클라이언트에게 응답을 보낸다.

 

즉 두 핸들러의 차이는 Controller 인터페이스를 구현한 핸들러는 ModelAndView를 반환하여 스프링의 뷰 리졸버에 의해 뷰가 렌더링되는 방식으로 동작하는 반면, HttpRequestHandler 인터페이스를 구현한 핸들러는 직접적으로 HTTP 응답을 생성하여 반환하는 것에 있다.

 

HttpServletResponse같은 경우 extends 하고있는 ServletResponse 인터페이스에 getWriter가 정의되어있고 서블릿 컨테이너(톰켓)가 해당 인터페이스의 구현체를 제공한다.

 

HttpServletRequset, HttpServletResponse는 어디에서 생성되는 것일까?

핸들러 매핑을 통해 httpRequestHandler를 상속받은 핸들러를 써야하는걸 알았다면 디스패처 서블릿이 HttpServletRequset, HttpServletResponse를 생성하게 된다. 

 

3. 에러 컨트롤러

//Custom error
public class CustomValidationException extends RuntimeException {
    public CustomValidationException(String message) {
        super(message);
    }
}

// 유저 컨트롤러
@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserDTO userDTO, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            // 유효성 검사에 실패한 경우 에러 처리
            throw new ValidationException("Invalid user data");
        }
        
        userService.registerUser(userDTO);
        return ResponseEntity.ok("User registered successfully");
    }
}

// 에러 컨트롤러
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<String> handleValidationException(ValidationException ex) {
        // 유효성 검사 예외 처리 로직
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
    }
}

 

에러 컨트롤러는 프로그램 전역에서 발생한 에러(메모리 부족, 파일 포맷 오류, 데이터 베이스 연결 문제 , 외부 서비스 호출 실패 등)를 유형에 따라 전역적으로 처리하는 로직을 만들 수 있다.

 

도메인 로직을 수행하고 있는 특정 컨트롤러에서 에러를 감지하여 해당 컨트롤러에 에러를 핸들링하는 코드를 작성하여 에러를 처리하기도 한다. 이 경우 에러 핸들러에 이 부분을 위임할 수 있다(위의 예시의 경우 유저 컨트롤러에서 로직을 작성하고 에러컨트롤러를 선언하지 않을 수도 있다.). 어느부분에서 예외가 발생했는지 반환하거나, 로깅을 수행한다거나 , 외부 서비스(AWS SNS 등)를 호출하는 로직 등을 추가할 수 있다. 또 로직이나 외부서비스 같은 경우는 AOP를 이용해 중복을 줄일 수 있다.

 

커스텀 에러같은 경우 표준화된 예외 클래스 외 명확한 의미 전달을 위해 사용한다. 상황에 따라 적절한 로직을 적용할 수 있도록 도와줄 수 있다.

 

위의 예제의 경우 유효성 검증 에러를 에러 컨트롤러에게 위임하는데 에러를 발생시키는 것은 많은 비용을 발생시킨다. 이러한 코드는 권장되는 형태는 아니고 에러 컨트롤러는 전역적인 예기치않은 오류를 처리하는데 사용하고, 유효성 검증 같은 경우는 해당 컨트롤러에서 처리하는게 비용 측면에서 적절하다. 

 

4. 핸들러 인터셉터

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 컨트롤러 호출 전에 실행되는 코드
        return true; // true를 반환하면 핸들러 실행, false를 반환하면 핸들러 실행 취소
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // 컨트롤러 호출 후, 뷰 렌더링 전에 실행되는 코드
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) throws Exception {
        // 뷰 렌더링 후 실행되는 코드 (예외가 발생한 경우에도 실행됨)
    }
}

 

 

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/specificControllerPath/**"); // 특정 컨트롤러에 대한 패턴을 지정하여 인터셉터를 적용
    }
}

 

핸들러 인터셉터 같은 경우 핸들러 매핑 후 핸들러 어뎁터가 선택된 이후 동작한다.

핸들러의 실행 전(preHandle), 실행 후(postHandle), 요청 처리 완료 후(afterCompletion)에 각각의 함수가 수행된다.

 

대표적인 수행 예시

preHandle: 권한 및 인증을 체크, 시작 시점 로직 및 로깅

postHandle: 데이터 추가(날짜), 캐시 설정

afterCompletion: 리소스 해제, 완료 시점 로깅, 정보 수집(요청 수, 처리시간 응답 코드 등 수)

 

postHandle같은 경우 예외가 발생한 경우는 실행되지않고 afterCompletion의 경우 예외 여부와 관계없이 실행된다.

 

config를 통해 어느 범위에 컨트롤러에 대해 동작할건지 지정할 수 있고 프로젝트 구조에서는 따로 interceptor 디렉토리를 만들어 관리한다.

 

 

 

[Spring] 필터(Filter) vs 인터셉터(Interceptor) 차이 및 용도 - (1)

Spring은 공통적으로 여러 작업을 처리함으로써 중복된 코드를 제거할 수 있도록 많은 기능들을 지원하고 있다. 이번에는 그 중에서 필터(Filter) vs 인터셉터(Interceptor)의 차이에 대해 알아보고자

mangkyu.tistory.com

 

서블릿 컨테이너? 스프링 컨테이너?

 

톰캣은 서블릿 컨테이너이며, 생성된 서블릿 컨테이너는 여러 서블릿 인스턴스를 가지고 있습니다. 서블릿 인스턴스는 멀티 스레드 환경에서 동작합니다.

 

각각의 요청 마다 하나의 스레드가 담당합니다.  스레드를 생성할때 마다 메모리를 할당받아야 하는 비용이 발생합니다. 따라서 미리 정해진 개수의 스레드를 서블릿 컨테이너가 생성하고 이를 관리합니다(스레드 풀). 각 스레드는 서블릿 인스턴스를 실행합니다.

 

요청을 처리하는데 데이터가 필요합니다. 스레드가 할당받은 메모리에 이러한 데이터를 세팅해야합니다.

각 스레드는 해당 요청을 처리하는 데 필요한 데이터(요청 파라미터, 세션 정보, 헤더 등)를 서블릿 컨테이너의 메모리에 저장합니다. 이러한 데이터를 바탕으로 디스패처 서블릿을 실행하여 요청을 적절한 핸들러(컨트롤러)에 매핑, 처리합니다.

 

서블릿 컨테이너는 클라이언트의 요청에 대한 URL 패턴을 기반으로 서블릿 인스턴스를 활성화하고 요청을 전달하는데, 디스패처 서블릿은 이러한 서블릿 중 하나입니다. 하지만 디스패처 서블릿은 일반적인 서블릿과는 다르게 스프링 프레임워크에 의해 제공되고 구성됩니다.

생성하고 제공하는 주체는 스프링이지만 저장되는 메모리와 사용하는 주체는 서블릿 컨테이너입니다.

 

 스프링 컨테이너는 각 빈을 관리합니다. 핸들러, 핸들러 매핑, 뷰 리졸버 등의 객체는 스프링 컨테이너에 의해 생성되고 관리됩니다. 이러한 빈들은 주로 상태를 갖지 않는 객체입니다. 각 쓰레드에 저장된 데이터는 스프링 컨테이너가 관리하는 빈들을 통해 처리되며, 이러한 빈들은 동시에 접근하여 읽기가 가능합니다. 빈과 서블릿 인스턴스 모두 일반적으로 싱글톤으로 관리됩니다. 이러한 원리로 스프링은 다중 처리가 동시에 가능합니다.

 

스프링부트에선 application.properties 파일을 통해 내장 톰켓을 설정을 변경할 수 있습니다.

server.port=8081
server.servlet.context-path=/myapp
server.servlet.session.timeout=30m
server.tomcat.connector.max-threads=200
server.tomcat.connector.accept-count=100

세션 시간과 최대 스레드 수 등 다양한 설정을 할 수 있다.

 

 

 

ServletContainer와 SpringContainer는 무엇이 다른가?

Controller 1개는 어떻게 수십 만개의 요청을 처리하는가

sigridjin.medium.com

 

[Spring] Controller의 종류와 다양한 전략

1. Controller의 종류 1) Servlet - javax.servlet.Servlet을 상속한 클래스를 컨트롤러로 사용한다. @Component("/home") // bean 이름을 이용한 핸들러 매핑 방식 이용 public class HomeServlet extends HttpServlet { protected void

kimcoder.tistory.com

 

 

HttpServletRequest, HttpServletResponse에 대한 이해

WAS가 웹브라우져로부터 Servlet요청을 받으면 요청을 받을 때 전달 받은 정보를 HttpServletRequest객체를 생성하여 저장 웹브라우져에게 응답을 돌려줄 HttpServletResponse객체를 생성(빈 객체) 생성된 Http

zester7.tistory.com

 

 

[JSP] 서블릿(Servlet)이란?

1. Servlet(서블릿) 서블릿을 한 줄로 정의하자면 아래와 같습니다. 클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술 간단히 말해서,

mangkyu.tistory.com

 

 

메세지 컨버터는 어떻게 결정되는가?

 

스프링 HTTP 메시지 컨버터

HTTP 메시지 컨버터 뷰 템플릿으로 HTML을 생성해서 응답하지 않고, HTTP API처럼 JSON 데이터를 HTTP 바디에서 직접 읽거나 쓰는 경우 HTTP 메시지 컨버터를 사용하면 편리하다. HTTP 메시지 컨버터란? 요

dev-monkey-dugi.tistory.com

 

 

resolver는 어떤 역할을 하는가

 

Resolver란 무엇인가?

HandlerMethodArgumentResolverArgument ResolverSpring에서 Resolver의 동작은 아래와 같은 과정으로 이루어진다.Client Request 요청Dispatcher Servlet에서 해당 요청 처리Client Request에 대

velog.io

어떤 메세지 컨버터를 선택할지 핸들러 어댑터가 content-type을 통해 결정하는데 어떤 content-type이 있는가?

 

HTTP: Content-Type 에 대해 알아보자 (application/json, application/x-www-form-urlencoded, multipart/form-data)

웹 개발을 하게 되면 HTTP에 대해 조금 알아두는 것이 좋습니다. 그중에서 Content-Type에 대한 이해가 부족한 것 같아 정리를 해보았습니다. Content-Type Content-Type은 api 연동시에 보내는 자원을 명시하

jw910911.tistory.com

form 태그는 어떠한 content-type을 지원할까?

 

x-www-form-urlencoded 타입이란 (multipart/form-data와의 차이점)

x-www-form-urlencoded와 multipart/form-data 차이점 //기존에 설정된 springdoc consumes default-consumes-media-type: multipart/form-data //변경된 설정 default-consumes-media-type: application/x-www-form-urlencoded 프로젝트 작업 중 동

wildeveloperetrain.tistory.com

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

Object Mapper 커스텀

 

알고보면 만만한 Jackson Custom Serialization

API 서버를 만들다보면 어떤 객체를 JSON으로 만들때, 특정 필드만 제외하거나 특정 필드의 이름을 바꿔야 하는 일이 생길 수 있다. 그 객체를 JSON으로 만들 때 특정 필드의 이름을 항상 바꾸려면

homoefficio.github.io

 

 

Jackson ObjectMapper 정리

개요 Java 개발자라면 Jackson에서 제공하는 ObjectMapper와 자주 마주치게 됩니다. Java 클래스 내용을 확인하거나 내용물의 Parsing 과정에 필요한 커스터마이징이 존재하기 때문입니다. 물론 중요한 기

interconnection.tistory.com

spring 동작 원리

 

[Spring MVC] Spring의 동작 원리

Spring 이란? 경량 컨테이너로서 자바 객체를 직접 관리한다. 각각의 객체 생성, 소멸과 같은 라이프 사이클을 관리하며 스프링으로부터 필요한 객체를 얻어올 수 있다. 스프링은 Plain Old Java Object

yenbook.tistory.com

인터셉터 ? 필터?

 

Spring Interceptor, 제대로 이해하기

Interceptor는 컨트롤러에 들어오는 요청 HttpRequest와 컨트롤러가 응답하는 HttpResponse를 가로채는 역할을 합니다. 스프링의 Intercepter의 이해와 사용법을 익히는 것이 본 포스팅의 목표입니다. ---------

gngsn.tistory.com

핸들러 어뎁터? 핸들러 매핑?

 

[Spring MVC] Dispatcher Servlet, 핸들러 매핑, 핸들러 어댑터

Spring MVC 구조를 학습하게 되었습니다. 스프링 MVC는 아래와 같은 흐름으로 진행됩니다. 처음엔 그림만 보면 이해하기 어렵다고 느낄텐데요. 차근차근 따라가면 흐름은 그렇게 어렵지 않습니다. D

ksabs.tistory.com