과제를 진행하려 하다보니 개념에 대한 이해가 하나도 없어 뭐가 어떻게 동작되는 것인지, 뭐가 무엇인지 도저히 이해가 되지도 않고.. 시작조차 할 수 없었다 😭
그냥 따라 하려고 했지만 그렇게 했다간 뒤에 내용들 마저도 엉망진창 뒤죽박죽이 되어 버릴 것만 같아서 정리 한다.
동작방식을 그림으로 표현한 자료도 매우 다양했고 비슷했지만 서로 달라서 또 쉽지 않았던 것 같다 😵💫
과제에서 제일 먼저 막혔던 것은 핸들러 메서드가 무엇인가? 컨트롤러와 핸들러 메서드의 역할이었는데 찾아보다 보니 전체적인 작동방법에 대한 이해가 필요한 것 같아 다시 살펴보았다.
무엇보다도.. 설명에 대한 생략이 많아 더욱 혼돈이 왔던 것 같다..
Spring MVC 의 동작 방식과 구성요소
웹 계층에 서블릿 API를 기반으로 클라이언트의 요청을 처리하는 모듈이 있는데 이를 스프링 웹 MVC(spring-web-mvc) 또는 스프링 MVC라고 한다. 스프링 MVC는 컨트롤러에 요청을 전달하고 다양한 기능을 제공하는 중앙 서블릿을 중심으로 설계되어있다.
컨트롤러는 클라이언트의 요청을 직접적으로 전달받는 엔드포인트로 뷰와 모델 사이에서 상호작용하는 역할을 수행한다.
클라이언트로부터의 요청을 처리하고 결과를 모델 객체에 담아 뷰로 전달한다. 이 때 컨트롤러는 비즈니스 로직을 수행하여 필요한 데이터를 가공하고 가공된 데이터를 모델에 저장한다.
모델은 클라이언트에게 표시될 데이터를 가지고 있으며, 뷰는 모델의 데이터를 사용해 응답을 생성하게 된다.
요청 처리 방법
컨트롤러(Controller)
컨트롤러는 위에서도 얘기했듯 정말 많은 역할을 한다.
클라이언트의 요청을 처리하고, 데이터를 가공하거나 비즈니스 로직을 수행한 후 결과를 반환하는 역할을 담당한다. 클라이언트로부터 요청을 받으면 컨트롤러는 해당요청을 적절한 방법으로 라우팅하고 필요한 데이터를 가져와서 로직을 수행한다.
스프링 MVC에서 컨트롤러를 정의 하는 방법
어노테이션을 사용하여 클래스를 컨트롤러로 지정하고 특정 URL 패턴과 매핑을 설정한다. 이렇게 지정하면 해당 URL로 들어오는 요청을 컨트롤러에서 처리하게 된다.
@Controller
@RequesetMapping("/user")
핸들러 메서드(Handler Method)
핸들러 메서드는 @RequestMapping과 그 하위 어노테이션(@GetMapping, @PostMapping 등)이 붙은 메서드의 정보를 추상화한 객체이다. 핸들러 메서드는 실행 가능한 객체가 아니라 메서드를 실행하기 위해 필요한 참조 정보를 담고 있는 객체이다.➡️ 핸들러 메서드는 MVC 컨트롤러 클래스 내에 작성되며 특정 URL 패턴과 연결되어 해당 URL로의 요청을 처리한다.
디스패처 서블릿은 애플리케이션이 실행될 때 모든 컨트롤러 빈의 메서드를 살펴서 매핑 후보가 되는 메서드를 추출한 뒤, 이를 핸들러 메서드 형태로 저장해둔다. 스프링 MVC는 요청을 기반으로 저장된 메서드 중 조건에 맞는 핸들러 메서드를 참조하여 실행한다.
결론적으로 컨트롤러는 실제로 요청을 처리하는 역할을 하고, 디스패처 서블릿은 요청을 처리할 컨트롤러를 찾기 위해 핸들러 메서드를 사용한다. 핸들러의 구체적인 타입에 따라 해당 핸들러를 찾아주는 핸들러 메서드와 핸들러의 처리 결과를 그에 맞는 Model과 View로 변환해주는 🔗핸들러 어댑터(HandlerAdapter)[참고4]가 필요하다.
✅ 컨트롤러와 핸들러의 차이점?
여러 글을 찾아보다 보니 어디서는 컨트롤러, 어디서는 핸들러 그리고 어떤 글에서 컨트롤러와 핸들러의 역할에 대한 이야기가 있어서 나도 하루종일 뭐가 어떻게 다른 건데?! 하면서 머리를 쥐어짰던 것 같다. 여쭈어본 결과 컨테이너라는 큰 틀 안에 핸들러가 들어가 있는 개념이라고 하셨다. 기능을 크게 나누지 않는 것이 좋다고...!
클래스 안에 메서드가 있는 것 처럼 컨트롤러라는 클래스 안에 핸들러라는 메서드가 있는 것이라고 이해하면 쉽다고 하셨다.
1. 클라이언트의 요청을 디스패처 서블릿이 받는다.
✨ 디스패처 서블릿은 프론트 컨트롤러(Front Controller)로 클라이언트의 요청을 처리하기 전에 공통적인 작업을 수행하고 요청을 적절한 핸들러로 분배하여 처리하는 중앙 집중식 컨트롤러이다. 프론트 컨트롤러는 웹 애플리케이션의 구조를 단순화시키고 코드의 중복을 줄이며, 보안, 로깅, 인증, 권한 부여 등 공통 작업을 한 곳에서 처리할 수 있도록 도와주는 역할을 한다. 따라서, 애플리케이션의 유연성과 확장성을 향상시킬 수 있다.
2. 클라이언트 요청을 처리할 컨트롤러에 대한 검색을 HandlerMapping에 요청한다.
디스패처 서블릿은 요청을 처리할 핸들러를 찾고 객체의 메서드를 호출한다. 가장 먼저 어느 컨트롤러가 요청을 처리할 수 있는지를 식별해야 하는데, 해당 역할을 하는 것이 바로 HandlerMapping이다.
3. 클라이언트 요청과 매핑되는 핸들러 객체를 다시 리턴한다.
4. 실제로 클라이언트 요청을 처리할 핸들러 메서드를 찾기 위해 핸들러 어댑터 를 찾아서 전달한다.
디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 핸들러 어댑터를 통해 위임한다. 모든 컨트롤러가 디스패처 서블릿과 동일한 인터페이스를 가지고 있지 않을 수 있기 때문이다.(구현 방식이 다양하기 때문에)
✅ 핸들러 어댑터에서는 인터셉터, ArgumentResolver, ReturnValueHandler 등을 활용하여 요청 전/후에 공통적인 전/후처리 작업을 수행한다. 이는 코드의 재사용성과 유지보수성을 높이며, 다양한 기능을 확장할 수 있는 구조를 제공한다.
✅ 일반적으로 핸들러 어댑터는 컨트롤러의 메서드를 호출하기 위해 리플렉션(reflection)을 사용한다. 리플렉션은 실행 시점에 클래스의 구조를 분석하고, 메서드를 동적으로 호출할 수 있는 기능을 제공한다. 핸들러 어댑터는 컨트롤러의 클래스와 메서드 이름, 매개변수 등의 정보를 기반으로 리플렉션을 사용하여 핸들러 메서드를 호출한다.
5. 전달받은 컨트롤러 정보로 핸들러 메서드를 호출한다.
6. 핸들러 메서드는 비즈니스 로직을 처리하고 리턴 받은 모델 데이터를 핸들러 어댑터에게 전달한다.
7. 핸들러 어댑터는 전달받은 모델 데이터와 뷰 정보를 다시 디스패처 서블릿에게 전달한다.
8. 디스패처 서블릿은 전달받은 뷰 정보를 다시 ViewResolver에게 전달해서 뷰 검색을 요청한다.
9. ViewResolver는 뷰 정보에 해당하는 뷰 를 찾아서 다시 리턴해준다.
10. 디스패처 서블릿은 ViewResolver로부터 전달받은 뷰객체를 통해 모델 데이터를 넘겨주면서 클라이언트에게 전달할 응답 데이터 생성을 요청한다.
11. 뷰는 응답 데이터를 생성해 다시 디스패처 서블릿에게 전달한다.
12. 디스패처 서블릿은 뷰로부터 전달받은 응답 데이터를 최종적으로 전달한다.
참고 자료 📕
🔗 [Spring] 디스패처 서블릿(Dispatcher Servlert)이 HTTP 요청을 처리하는 방법과 핸들러 메소드(HandlerMethod)
🔗 [Spring MVC] 스프링 MVC란 무엇인가? - 스프링 MVC 구조 이해
🔗[Spring 3 - MVC] 컨트롤러의 종류와 핸들러 어댑터
진짜 역대급으로 많은 자료를 살펴보았다. 살펴 본 자료는 더 많았지만 게시글과는 관련 없는 것도 많아 넣지 않았다.
나는 컨트롤러와 핸들러 메서드에 너무 집중한 나머지 과제와 동떨어져 컨트롤러와 핸들러 메서드의 차이점이 무엇인지? 하루종일 고민만 했던 것 같다. (결국 알지는 못했지만..🫠)
이렇게 자료만 보고 글만 보다가는 과제를 해결하지 못 할 것 같아 과제를 먼저 풀었는데 생각보다 과제에서 요구하는 것은 단순했다.
@RestController에 대해 공부를 했더라면 어땠을까 하는 생각이 들기도 한다.
혹은 Controller 핸들러 메서드의 다양한 인수에 대한 사용이라던가 @RequestMapping 어너테이션에 대한 공부..
어쩌면 이틀동안 흐름과 컨트롤러, 핸들러 메서드에 대한 공부가 있었기에 이런 생각을 할 수 있는 것일지도 모른다.