2025. 8. 8. 11:32ㆍFrameWork/SpringBoot
DispatcherServlet으로 전환
우리는 FrontController라는 서블릿을 만들었지만 서블릿 컨테이너를 관리하거나 다루는 작업을 하지 않도록 Servlet Containerless한 개발을 하고 싶은데 애플리케이션의 로직과 연결되어 있는 것들이 서블릿 코드안에 존재하게 된다.
이러한 코드는 먼저 맵핑에 대한 문제이다.
웹 요청으로 이걸 처리할 Controller와 메서드가 어떤것인지를 연결시켜주는 작업인데 이걸 코드 상에서 보자면
이렇게 요청 URL와 요청 타입(GET 방식)을 통해서 어떤 클래스의 빈을 사용해서 어떤 함수를 사용할지를 코드로 서블릿 내부에 작성되어 있는 것을 볼 수 있다.
두번째는 요청의 파라미터값을 추출해서 메서드에 파라미터로 넘겨주는 작업에 대한 문제이다.
보면 getParam의 파라미터가 name이라는 파라미터이고 이걸 코드로 지정해줬어야만 정상적으로 실행 된다.
이러한 작업을 파라미터 바인딩이라고 하는데 이런 부분들이 서블릿 내부에서 작동을 해줘야만 한다는 것이다.
이런 작업들은 어쨋든 필요할 수 밖에 없는 작업이라 FrontController에 작성해뒀는데 이런 코드들을 모두 서블릿과 관련된 코드에 담아둘 수는 없을 노릇이다.
이런 부분에서 가장 유명한 서블릿인 DispatcherServlet이라는 것을 사용할 것이다.
이 서블릿 등록 하는 부분에 있는 것을 모두 날리고 다른 서블릿을 이제 여기에 추가해보도록 하자.
이 dispatcherServlet은 우리가 이전에 서블릿으로 위에서 구현했던 저런 것들을 사용하기 편하게 도구로 개발을 해뒀다.
그래서 이걸 그냥 꺼내다 쓰면 된다.
이름은 dispatcherServlet으로 하고
dispatcherServlet의 인스턴스를 만들어주고
우리가 만들었던 서블릿에서는 스프링 컨테이너한테 빈을 달라고 요구하고 URI 정보를 이용해서 맵핑도 하는 방법을 사용했었는데 서블릿이니까 URI 정보를 분석하는건 알아서 잘 할거고 스프링 컨테이너와 커뮤니케이션을 어떻게 하지? 라고 생각할 수 있다.
그래서 이 오브젝트를 생성할때 앞에서 만들었던 ApplicationContext를 전달해주면 된다.
근데 에러가 나는것을 볼 수 있다.
이건 dispatcherServlet은 ApplicationContext 중에서 Web환경에서 사용하도록 더 발전시킨 웹 애플리케이션 컨텍스트 타입을 전달해줘야한다.
그래서 코드로 작업을 할때는 그냥 GenericApplicationContext가 아니라 GenericWebApplicationContext를 써줘야 한다.
이러면 에러가 사라진것을 볼 수 있다.
이렇게 dispatcherServlet에 ApplicationContext를 넘겨서 dispatcherServlet이 맵핑을 하다가 작업을 위임할, 요청을 dispatch할 오브젝트를 찾아야하는데 그때 사용할 서블릿 컨테이너를 전달을 해줬다
이렇게 하면 충분할까?
위에선 우리가 웹 요청이 "/hello"로 들어온다면 어디 오브젝트를 사용해서 어떤 작업을 수행하라 라는 기준이 될 내용을 전혀 전달하지 않았기 때문에 이로써 무슨 작업을 진행하기는 힘들다
이런 정보를 어떤식으로든 dispatcherServlet에게 전달을 해줘야하는데 방법은 다양하다.
스프링의 초기에는 이러한 정보들을 XML에 어떤 URL로 들어오면 어떤 bean이 처리할지를 모두 명시하기도 했었다.
그 외에도 여러 방법들이 있는데 가장 각광을 받았고 개발자들이 좋아했던 방법은 맵핑 정보를 서블릿에 직접 작성하는 대신에 요청을 처리할 컨트롤러 클래스 안에 맵핑정보를 대신 넣어주는 방법이였다.
어노테이션 매핑 정보 사용
이전에 우리가 작성을 했었는데 메서드의 상단에 @를 붙이고 GetMapping을 넣어준 후에 중괄호 내부에 문자열로 맵핑될 URL을 넣어주었었던 방식이다.
이 안에는 /hello라는 URL로 Get요청이 온다면 해당 컨트롤러의 메서드가 처리를 해주겠다고 맵핑을 해주는 어노테이션이다.
그러면 이렇게 하고 끝나는가?
컨트롤러 이름위에 @RestController라는 것을 넣어줬었던것이 있었다.
근데 이건 사실은 dispatcherServlet과는 직접적인 관련은 없고 뒤에 스프링 컨테이너를 사용하는 또다른 방법을 사용할때 필요한 것이기에 안넣어도 된다.
그럼 @GetMapping을 넣으면 dispatcherServlet은 어떤일을 하는 것일까?
dispatcherServlet은 스프링 컨테이너인 ApplicationContext를 생성자로 받았었는데 즉 이걸 가지고 dispatcherServlet이 무슨 작업을 해주냐면 bean을 모두 찾아서 그 중에 웹 요청을 처리할 수 있는 맵핑 정보(GetMapping, RequestMapping 등..)를 가지고 있는 클래스를 찾아서 해당 컨트롤러가 웹 요청을 처리할 수 있도록 만들어진 웹 컨트롤러 이구나 라고 인식하고 그 안에 요청 정보들을 추출한다("/hello"를 get요청으로 보내면 getParam() 메서드를 사용한다라는 정보).
그리고 맵핑에 사용할 맵핑 테이블을 하나 만들어 두고 그 이후에 웹 요청이 들어오면 그걸 참고해서 담당할 bean오브젝트와 메서드를 확인하는 것이다.
그런데 사실
이런식으로만 되어 있으면 dispatcherServlet이 찾지 못한다.
bean이 등록된게 지금은 두개밖에 없지만 나중에 큰 프로젝트가 되면 수천개가 될 수 있는데 이걸 매번 찾아서 사용한다는 것은 매우 번거로운 일이기 때문이다
그래서 그 위에 클래스 레벨에 @RequestMapping이라는 어노테이션을 붙여줘야 한다
dispatcherServlet이 맵핑 정보를 만들때 기본적으로 클래스 레벨에 있는 정보를 참고한 후에 메서드 레벨에 붙어 있는 맵핑정보를 거기에 추가를 한다.
예를 들어서
이렇게 되어 있다면 먼저 /new에 /hello를 붙여 /new/hello로 getParam이라는 메서드의 맵핑 정보가 설정된다.
또
이렇게 메서드에 URL정보를 넣지 않으면 먼저 /hello라는 요청으로 들어온건 HelloController로 들어오고 그중 Get요청에 한해서만 getParam()이 처리해준다라는 맵핑이 완성된다.
이제 서버를 다시 띄우고 한번 확인해보면
에러가 난다
뭐가 문제일까?
사실 맵핑은 성공적으로 된 상태로 실제 getParam까지는 아마 요청이 들어왔을 것이다.
그리고 getSayHi()도 호출하고 return을 했을 것이다.
그런데 해당 컨트롤러 메서드가 String을 return하면 이걸 dispatcherServlet이 처리하는 여러가지 방법이 있다.
그중에 가장 기본은 String으로 return된 문자열을 보고 View라는 html template을 찾아서 View를 return해줘라 라고 할텐데 이걸 못찾으니까 우리가 원하는 작업을 수행할 View가 없으니 404 에러, 못찾겠다라고 알리는 것이다.
그럼 view이름을 return하는게 아니라 String값을 그대로 web응답의 body에 넣어서 그대로 전달하게 하는 방식으로 동작하게 하려면 메서드에 @ResponseBody를 넣어주면 된다.
## 여기서 나는 안됐다... 스프링 버전 차이로 보이는데...조금 더 찾아보긴하겟다만 방법이 없어보인다.
나는 그냥 @Controller 어노테이션을 추가해서 실행했다
그럼 이전에 우리가 처음에 했을때는 저런 @ResponseBody같은걸 안썼었는데 그때는 그럼 어떻게 사용이 됐던 것일까 ?
간단하게 말하자면 앞에 사용했던 @RestController라는 어노테이션이 붙으면 내부에 모든 메서드에 특별하게 지정하지 않은 한 @ResponseBody가 붙어 있는 것으로 인식한다.
실제 이건 생략이 된다는거지 정보가 아예 필요가 없다는 말이 아니다.
우리는 dispatcherServlet이 어떤 정보를 필요로 하고 어떤 경우엔 어떤 정보를 생략해도 되는지에 대해서 다 정확히 알고 있어야만 나중에 Spring MVC에 등장하는 내용인데 Spring MVC는 이런 관례들을 많이 알고 우리가 필요한 코드만 정확하게 가져다 사용할 수 있어야 한다.
그래서 코드를 보면 이 응답값은 FrontController역할을 하는 dispatcherServlet이 어떤식으로 해석하겠구나를 떠올릴 수 있어야한다.
보면 dispatcherServlet하나 추가하는걸로 코드가 엄청 간결해졌다.
지금 우리 코드를 보면
스프링 컨테이너를 생성하고
서블릿 컨테이너를 코드로 실행을 하면서 서블릿을 등록하는 부분으로 두가지의 분리된 작업이 진행되고 있는데 다음에는 이 두가지를 통합하는 작업을 진행해보자.
그래서 스프링 컨테이너가 어떤 라이프 사이클을 가지고 움직이는지를 확인하고 서블릿 컨테이너를 만드는 작업을 다른 방식으로 바꿔보도록 해보자.
'FrameWork > SpringBoot' 카테고리의 다른 글
토비의 스프링 부트 - 이해와 원리 : 섹션5. 독립 실행형 스프링 애플리케이션 - Dependency Injection & 의존성 오브젝트 DI 적용 (1) | 2025.08.07 |
---|---|
Gradle (1) | 2025.08.06 |
토비의 스프링 부트 - 이해와 원리 : 섹션5. 독립 실행형 스프링 애플리케이션 - 스프링 컨테이너 사용, 의존 오브젝트 추가 (0) | 2024.09.01 |
윈도우(WSL)로 SDKMan 설치하기 (0) | 2024.02.06 |