๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Springboot

[Spring] Spring MVC: HandlerMapping

by ๋Œ€๋ณต2 2025. 5. 31.

์„œ๋ก 

Spring MVC๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๊ณผ์ • ์ค‘, DispatcherServlet์ด ์ปจํŠธ๋กค๋Ÿฌ์— ์ ‘๊ทผํ•˜๋Š” ๊ณผ์ •์„ ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

 


HandlerMapping

์‹๋‹น์œผ๋กœ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณด์ž. ์†๋‹˜์€ ๋“ค์–ด์™€์„œ ๋‹ค์–‘ํ•œ ์ฃผ๋ฌธ์„ ํ•˜๊ณ  ํ•ด๋‹น ์ฃผ๋ฌธ์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

  • ์ฃผ๋ฌธ ์ ‘์ˆ˜์› (HandlerMapping)
  • ์ „๋ฌธ ์š”๋ฆฌ์‚ฌ (์ปจํŠธ๋กค๋Ÿฌ/ํ•ธ๋“ค๋Ÿฌ)

HandlerMapping (์ฃผ๋ฌธ ์ ‘์ˆ˜์›)

  • ์—ญํ• : "์–ด๋–ค ์†๋‹˜(์š”์ฒญ)์˜ ์ฃผ๋ฌธ(URL)์ด ์–ด๋–ค ์š”๋ฆฌ์‚ฌ(์ปจํŠธ๋กค๋Ÿฌ)์—๊ฒŒ ๊ฐ€์•ผ ํ• ์ง€"๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ  ์—ฐ๊ฒฐํ•ด ์ค€๋‹ค.
  • ์„ค๋ช…: "์Šคํ…Œ์ดํฌ" ์ฃผ๋ฌธ์€ '์Šคํ…Œ์ดํฌ ์ „๋ฌธ ์š”๋ฆฌ์‚ฌ'์—๊ฒŒ, "ํŒŒ์Šคํƒ€" ์ฃผ๋ฌธ์€ 'ํŒŒ์Šคํƒ€ ์ „๋ฌธ ์š”๋ฆฌ์‚ฌ'์—๊ฒŒ ์ „๋‹ฌํ•ด์•ผ ํ•จ์„ ์ฃผ๋ฌธ ์ ‘์ˆ˜์›(HandlerMapping) ์ด ํŒŒ์•…ํ•˜๊ณ  ์—ฐ๊ฒฐํ•œ๋‹ค.

==> HandlerMapping์€ ๋“ค์–ด์˜ค๋Š” ์›น ์š”์ฒญ(URL)์„ ๋ณด๊ณ , ์ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ(Controller)๋ฅผ ์ฐพ์•„์ค€๋‹ค.

 

// ์ด ๋งคํ•‘์€ @Controller ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค์™€ @RequestMapping ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์Šค์บ”ํ•˜์—ฌ
// ํŠน์ • URL ์š”์ฒญ์ด ์™”์„ ๋•Œ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ์˜ ์–ด๋–ค ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ• ์ง€ ๊ฒฐ์ •ํ•œ๋‹ค

@Controller
public class OrderController {

    @GetMapping("/order/steak") // ์ด URL ์š”์ฒญ์ด ์˜ค๋ฉด
    public String orderSteak() { // ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  HandlerMapping์ด ํŒŒ์•…ํ•œ๋‹ค
        return "steakOrderPage";
    }

    @GetMapping("/order/pasta") // ์ด URL ์š”์ฒญ์ด ์˜ค๋ฉด
    public String orderPasta() { // ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  HandlerMapping์ด ํŒŒ์•…ํ•œ๋‹ค
        return "pastaOrderPage";
    }
}
// ์œ„ ์ฝ”๋“œ์—์„œ /order/steak๋ผ๋Š” ์š”์ฒญ์ด ์˜ค๋ฉด 
// OrderController์˜ orderSteak() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค๊ณ  HandlerMapping์ด ์•Œ๋ ค์ค€๋‹ค

HandlerMapping์˜ ๋™์ž‘ ์›๋ฆฌ

DispatcherServlet์€ ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด, HandlerMapping์—๊ฒŒ ์š”์ฒญ์„ ์œ„์ž„ํ•˜์—ฌ ํ•ด๋‹น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์„ ๊ฐ€์žฅ ๋จผ์ € ์‹œ์ž‘ํ•œ๋‹ค.

 

1. HanlerMapping ์กฐํšŒ

DispatcherServlet์€ ์„ค์ •๋œ HandlerMapping ๋นˆ๋“ค์„ ์ˆœํšŒํ•˜๋ฉฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  Handler๋ฅผ ์ฐพ์•„๋‹ฌ๋ผ๊ณ  ์š”์ฒญ. ์šฐ์„ ์ˆœ์œ„(Order)์— ๋”ฐ๋ผ ์กฐํšŒ

 

2. Handler ๊ฒ€์ƒ‰

๊ฐ HandlerMapping ๊ตฌํ˜„์ฒด๋Š” ์ž์‹ ๋งŒ์˜ ๋งคํ•‘ ์ „๋žต์— ๋”ฐ๋ผ ์š”์ฒญ URL๊ณผ ๋งคํ•‘๋˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ๋Š”๋‹ค.

ex: @RequestMapping ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ HandlerMapping์€ ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ์— ๋ถ™์€ @RequestMapping ์ •๋ณด๋ฅผ ๋ถ„์„ ํ›„ ๋งคํ•‘

 

3. HandlerExecutionChain ๋ฐ˜ํ™˜

HandlerMapping์ด ์š”์ฒญ์— ํ•ด๋‹นํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ์œผ๋ฉด, ๋‹จ์ˆœํžˆ ํ•ธ๋“ค๋Ÿฌ ๊ฐ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ HandlerExecutionChain ๊ฐ์ฒด ๋ฐ˜ํ™˜ -> HandlerExecutionChain์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ๋‹ค.

  • Handler ๊ฐ์ฒด: ์‹ค์ œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฉ”์„œ๋“œ (๋˜๋Š” ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ์‹ธ๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด)
  • Interceptor ๋ชฉ๋ก: ํ•ด๋‹น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „์—, ํ›„์—, ๊ทธ๋ฆฌ๊ณ  ์™„๋ฃŒ ํ›„์— ์‹คํ–‰๋  HandlerInterceptor(์ธํ„ฐ์…‰ํ„ฐ)๋“ค์˜ ๋ชฉ๋ก

 

4. HandlerAdapter ์„ ํƒ (by DispatcherServlet)

DispatcherServlet์€ HandlerExecutionChain์—์„œ ์–ป์€ Handler ๊ฐ์ฒด๋ฅผ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•  HandlerAdapter๋ฅผ ์ฐพ๋Š”๋‹ค. Handler๋Š” ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, HandlerAdapter๋Š” ํ•ธ๋“ค๋Ÿฌ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋ฐฉ์‹์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

5. Handler ์‹คํ–‰

์„ ํƒ๋œ HandlerAdapter๊ฐ€ HandlerExecutionChain์— ์žˆ๋Š” Handler ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌ


์ฃผ์š” HandlerMapping ๊ตฌํ˜„์ฒด

  • ์Šคํ”„๋ง์€ ๋‹ค์–‘ํ•œ HandlerMapping ๊ตฌํ˜„์ฒด๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ๊ฐœ๋ฐœ์ž๋Š” ํ•„์š”์— ๋”ฐ๋ผ ์ด๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉ
  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•˜์ง€๋งŒ, ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” HandlerMapping๋“ค์„ ์ž๋™์œผ๋กœ ๋“ฑ๋ก

 

RequestMappingHandlerMapping

  • ํŠน์ง•: ์Šคํ”„๋ง 3.1๋ถ€ํ„ฐ ๋„์ž…๋œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ HandlerMapping์œผ๋กœ, @Controller ๋ฐ @RestController ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค์™€ @RequestMapping, @GetMapping, @PostMapping ๋“ฑ ์š”์ฒญ ๋งคํ•‘ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์Šค์บ”ํ•˜์—ฌ ๋งคํ•‘ ์ •๋ณด๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.
  • ๋™์ž‘: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ์ ์— ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ ํด๋ž˜์Šค๋ฅผ ์Šค์บ”ํ•˜์—ฌ @RequestMapping ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋“ค์„ ์ฐพ๊ณ , ํ•ด๋‹น ๋ฉ”์„œ๋“œ์™€ ๋งคํ•‘๋  URL ํŒจํ„ด ์ •๋ณด๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์บ์‹ฑ, ์ดํ›„ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์บ์‹ฑ๋œ ์ •๋ณด์—์„œ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ์šฐ์„ ์ˆœ์œ„: ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„

BeanNameUrlHandlerMapping

  • ํŠน์ง•: ๋นˆ์˜ ์ด๋ฆ„์„ URL๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘. ์ฆ‰, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ๋นˆ์˜ ์ด๋ฆ„๊ณผ ์š”์ฒญ URL์ด ์ผ์น˜ํ•˜๋ฉด ํ•ด๋‹น ๋นˆ์„ ํ•ธ๋“ค๋Ÿฌ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์˜ˆ์‹œ: <bean name="/home.do" class="com.example.controller.HomeController"/> ์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด '/home.do' ์š”์ฒญ์€ 'HomeController' ๋นˆ์œผ๋กœ ๋งคํ•‘
  • ์‚ฌ์šฉ ๋นˆ๋„: @RequestMapping ๋“ฑ์žฅ ์ดํ›„๋กœ๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉ X

SimpleUrlHandlerMapping

  • ํŠน์ง•: ๋ช…์‹œ์ ์œผ๋กœ URL๊ณผ ํ•ธ๋“ค๋Ÿฌ ๋นˆ์„ ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ์‹. ์„ค์ • ํŒŒ์ผ์— ์ง์ ‘ URL๊ณผ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋งต ํ˜•ํƒœ๋กœ ์ •์˜
  • ์˜ˆ์‹œ: 
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/hello.do">myHelloController</prop>
            <prop key="/bye.do">myByeController</prop>
        </props>
    </property>
</bean>
  • ์‚ฌ์šฉ ๋นˆ๋„: @RequestMapping ๋“ฑ์žฅ ์ดํ›„๋กœ๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉ X

RouterFunctionMapping (Spring WebFlux)

  • ํŠน์ง•: ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์Šคํƒ€์ผ๋กœ ๋ผ์šฐํŒ…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” HandlerMapping. @Controller ๊ธฐ๋ฐ˜์˜ ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐฉ์‹ ๋Œ€์‹  RouterFunction์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜์— ๋งคํ•‘ํ•œ๋‹ค.
  • ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ณณ: Spring WebFlux์—์„œ ๋งŽ์ด ์‚ฌ์šฉ. Spring MVC์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์˜ˆ์‹œ:
@Configuration
public class RouterConfig {
    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        return route(GET("/hello"), handler::hello)
                .andRoute(GET("/bye"), handler::bye);
    }
}

HandlerMapping์˜ ์šฐ์„ ์ˆœ์œ„ (Order)

์—ฌ๋Ÿฌ ๊ฐœ์˜ HandlerMapping์ด ๋“ฑ๋ก๋˜์–ด ์žˆ์„ ๋•Œ, DispatcherServlet์€ Ordered ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ @Order ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •์˜๋œ ์šฐ์„ ์ˆœ์œ„(Order)์— ๋”ฐ๋ผ HandlerMapping์„ ์ˆœํšŒํ•œ๋‹ค.(๋‚ฎ์€ ์ˆซ์ž๊ฐ€ ๋†’์€ ์šฐ์„ ์ˆœ์œ„)

  • ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„: Ordered.HIGHEST_PRECEDENCE (Integer.MIN_VALUE)
  • ๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„: Ordered.LOWEST_PRECEDENCE (Integer.MAX_VALUE)

DispatcherServlet์€ ์ดˆ๊ธฐํ™”๋  ๋•Œ(initHandlerMappings(ApplicationContext context)) ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ๋ชจ๋“  HandlerMapping ํƒ€์ž…์˜ ๋นˆ๋“ค์„ ์ฐพ์€ ๋’ค ์ •๋ ฌํ•˜์—ฌ ๋‚ด๋ถ€์ ์œผ๋กœ List ํ˜•ํƒœ๋กœ ์œ ์ง€

  1. ๋ชจ๋“  HandlerMapping ๋นˆ ์กฐํšŒ
  2. AnnotationAwareOrderComparator๋ฅผ ํ†ตํ•œ ์ •๋ ฌ
    • Ordered ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋นˆ์˜ getOrder() ๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜ ๊ฐ’
    • @Order ์–ด๋…ธํ…Œ์ด์…˜์— ์ง€์ •๋œ ๊ฐ’
  3. ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ ์œ ์ง€ ํ›„ ์š”์ฒญ ์‹œ ์ˆœ์ฐจ์  ์กฐํšŒ
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) { // ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ ์ˆœ์ฐจ ์กฐํšŒ
            // ๊ฐ HandlerMapping์—๊ฒŒ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์š”์ฒญ
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                // ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ์œผ๋ฉด ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋‹ค์Œ HandlerMapping์€ ์กฐํšŒํ•˜์ง€ ์•Š์Œ
                return handler;
            }
        }
    }
    return null; // ๋ชจ๋“  HandlerMapping์ด ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ์ง€ ๋ชปํ•จ
}

 

Default Order

  • RequestMappingHandlerMapping: ๊ธฐ๋ณธ์ ์œผ๋กœ 0 (๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„)
  • SimpleUrlHandlerMapping: Integer.MAX_VALUE - 1 ๋˜๋Š” ๊ทธ์— ์ค€ํ•˜๋Š” ๋งค์šฐ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„
  • BeanNameUrlHandlerMapping, ControllerClassNameHandlerMapping: ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋“ฑ๋ก๋˜์ง€ ์•Š๊ฑฐ๋‚˜, ๋“ฑ๋ก๋˜๋”๋ผ๋„ ๋งค์šฐ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„

๋ช…์‹œ์  Order

๊ฐœ๋ฐœ์ž๋Š” Order๊ฐ’์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public SimpleUrlHandlerMapping customSimpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        Properties urlMappings = new Properties();
        urlMappings.put("/customUrl", "myCustomController");
        mapping.setMappings(urlMappings);
        mapping.setOrder(1); // RequestMappingHandlerMapping (order=0) ๋ณด๋‹ค ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„
        return mapping;
    }

    @Bean
    public MyCustomController myCustomController() {
        return new MyCustomController();
    }
}