개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

기초개념 › Spring

컨트롤러

어떤 클래스를 컨트롤러라고 부르는가

컨트롤러는 class로 선언한 자바 클래스입니다. 일반 클래스와 다른 곳은 두 군데입니다. 클래스 위의 @RestController는 스프링이 이 클래스의 객체를 만들어 두고 웹 요청을 처리하는 클래스로 다룬다는 표시이고, 메서드 위의 @GetMapping("/hello")는 브라우저가 /hello라는 주소로 보낸 GET 요청을 이 메서드에 맡긴다는 등록입니다. 컨트롤러는 서블릿이 아닙니다. 요청은 스프링 부트가 톰캣에 등록해 둔 서블릿 하나(DispatcherServlet)가 받아서 컨트롤러의 메서드를 불러 줍니다. @RestController와 @GetMapping을 하나씩 지워 가며 같은 요청을 보내, 스프링이 객체를 만들고 메서드를 불러 주는지를 봅니다.

실행으로 대조 · 스프링 부트 4.1.1에서 여덟 가지 조합을 실제 컴포넌트 스캔으로 띄우고 요청을 보내 대조

이 실습에 나오는 개념

컨트롤러 클래스
컨트롤러 클래스는 스프링 MVC에서 웹 요청을 처리하는 메서드를 가진 자바 클래스다. 서블릿과 달리 특정 클래스를 상속하거나 인터페이스를 구현하지 않아도 되고, 클래스 위에 @Controller나 @RestController를 붙여 컨트롤러라고 표시한다. 스프링이 클래스 경로를 훑어(컴포넌트 스캔) 표시가 붙은 클래스를 찾아 객체를 만들어 두며, 개발자가 new로 만들지 않는다. 요청은 DispatcherServlet이 받아, 메서드 위에 적힌 주소(@GetMapping 등)를 보고 알맞은 컨트롤러의 메서드를 불러 준다. 주소는 메서드마다 적으므로(클래스 위에는 공통 앞부분만 적을 수 있다) 클래스 하나에 주소 여러 개를 둘 수 있다.
@RestController
@RestController는 클래스를 컨트롤러로 표시하는 애너테이션이다. @Controller와 @ResponseBody를 합친 것으로, 이 클래스의 요청 처리 메서드가 돌려주는 값이 뷰를 거치지 않고 곧바로 응답 본문에 쓰인다. @Controller는 @Component를 특수화한 것이어서 스프링이 컴포넌트 스캔으로 이 클래스를 찾아 객체로 등록한다. 이 애너테이션이 없으면 스프링이 그 클래스를 컨트롤러로 찾지 못하므로, 메서드에 @GetMapping을 붙여도 요청을 받지 못한다.
@GetMapping
@GetMapping은 HTTP GET 요청을 특정 메서드에 맡기는 애너테이션이다. 괄호 안에 주소(예: "/hello")를 적으며, @RequestMapping(method = RequestMethod.GET)의 줄임 형태로 만들어진 애너테이션이다. 스프링은 컨트롤러로 찾은 클래스의 메서드에서 이 애너테이션을 읽어 주소와 메서드를 짝지은 주소 표를 만든다. 같은 방식의 줄임 형태로 @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping이 있다.
DispatcherServlet과 프론트 컨트롤러
Spring MVC에서 모든 HTTP 요청을 가장 먼저 받는 서블릿이다. 요청을 처리할 컨트롤러를 찾아 호출하고 그 결과를 응답으로 만드는 전체 흐름을 조율한다. 이렇게 하나의 진입점이 모든 요청을 받아 공통 처리를 한 뒤 각 담당에게 넘기는 구조를 프론트 컨트롤러 패턴이라 한다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. 서버가 뜹니다. 스프링 부트가 톰캣을 띄우고, 톰캣에 DispatcherServlet이라는 서블릿 하나를 등록합니다. 이 서블릿은 우리가 쓴 것이 아니라 스프링이 제공하는 것이며, 모든 주소("/")의 요청을 맡습니다.
  2. 스프링이 프로젝트의 클래스를 훑어 @RestController가 붙은 클래스를 찾습니다. HelloController 클래스에 붙어 있으므로, 스프링이 이 클래스의 객체를 만들어 둡니다. 이 객체는 첫 요청이 오기 전, 서버가 뜰 때 이미 만들어집니다.
  3. 스프링이 HelloController 클래스의 메서드에서 @GetMapping이 붙은 것을 찾아, 거기 적힌 주소와 메서드를 스프링의 주소 표에 적습니다.
  4. 브라우저 주소창에 localhost:8080/hello 주소를 입력하면, 브라우저가 서버에 GET /hello 요청을 보냅니다.
  5. 톰캣이 요청을 받습니다. 톰캣에 등록된 서블릿은 DispatcherServlet 하나이고 모든 주소를 맡고 있으므로, 톰캣이 요청을 DispatcherServlet에 넘깁니다. 우리가 쓴 클래스에는 아직 아무것도 오지 않았습니다.
  6. DispatcherServlet이 스프링의 주소 표에서 /hello 주소를 찾습니다. HelloController.hello 메서드가 적혀 있습니다.
  7. DispatcherServlet이 찾아낸 메서드를 부릅니다. 이 메서드가 속한 객체는 서버가 뜰 때 스프링이 만들어 둔 것입니다. 우리가 쓴 코드에는 new HelloController()라는 줄도, hello() 메서드를 부르는 줄도 없습니다. 이 메서드가 돌려주는 글은 "안녕하세요"입니다.
  8. 브라우저가 200 응답을 받습니다. 메서드가 돌려준 글이 응답 본문이 되어, 본문은 "안녕하세요"입니다.

실행 결과

코드

// 표시: 웹 요청을 처리하는 클래스. 스프링이 객체를 만들어 둔다
@RestController
public class HelloController {
    // 등록: 이 주소로 온 GET 요청을 이 메서드에 맡긴다
    @GetMapping("/hello")
    public String hello() {
        return "안녕하세요";
    }
}

이 모형의 전제

스프링 부트 4.1.1(스프링 프레임워크 7.0.9, 내장 톰캣 11)을 JDK 21에서 실행한 결과입니다. 브라우저는 주소마다 GET 요청을 한 번씩 차례로 보낸다고 가정했습니다. 주소의 localhost:8080은 내 컴퓨터에서 스프링 부트를 기본 포트로 띄웠을 때의 예입니다. 여덟 가지 조합을 모두, 화면의 코드와 같은 소스(객체와 실행 횟수를 세는 줄만 더함)를 패키지 app으로 컴파일하고 @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan이 붙은 시작 클래스와 함께 자바 프로세스를 새로 띄워 확인했습니다. 컨트롤러를 손으로 등록하지 않고 스프링이 클래스를 훑어 찾게 했으며, 요청은 서버가 뜬 뒤 실제 HTTP로 보냈습니다. 대조한 것은 스프링이 만든 컨트롤러 객체의 수(서버가 뜬 직후와 요청 뒤), 주소 표(RequestMappingHandlerMapping의 getHandlerMethods 가운데 이 클래스의 것), 주소마다의 상태 코드와 본문, 메서드 실행 횟수입니다. 톰캣에 등록된 서블릿이 DispatcherServlet 하나이고 "/" 주소를 맡는다는 것, 컨트롤러 클래스가 Servlet 타입이 아니라는 것, 404 응답에서 메서드가 실행되지 않았다는 것도 실행으로 확인했습니다. 문서가 정한 것은 스프링 프레임워크 문서 Web MVC의 DispatcherServlet 절(DispatcherServlet이 모든 요청을 받는 서블릿 하나이고 실제 일은 위임 구성 요소가 한다는 것, 스프링 부트에서는 서블릿 선언이 스프링 설정에서 찾아져 컨테이너에 등록된다는 것), Annotated Controllers의 Declaration 절(컨트롤러 객체가 클래스 경로 스캔으로 찾아지고 @RestController가 @Controller와 @ResponseBody를 합친 것이라는 것), Mapping Requests 절(@GetMapping 같은 단축 애너테이션)과 @RestController, @Controller, @GetMapping의 API 문서입니다. 객체가 서버가 뜰 때 만들어지고 주소 표도 그때 만들어진다는 것과, 주소가 없을 때 404가 나가는 것은 문서가 정한 것이 아니라 이 버전의 기본 설정에서 관찰한 동작입니다. 화면의 코드는 package와 import 줄을 줄였고, 컨트롤러가 시작 클래스와 같은 패키지(또는 그 아래)에 있다고 가정했습니다. 주소 등록은 @GetMapping만 다뤘고 @RequestMapping, @PostMapping이나 @Controller는 다루지 않았습니다. 스프링 부트의 DispatcherServlet이 서버가 뜰 때가 아니라 첫 요청이 올 때 초기화되는 것은 관찰했지만 이 실습의 단계에는 넣지 않았고, "세부 동작 실습: MVC 요청 흐름"에서 다룹니다.

앞 실습: 서블릿 · 다음 실습: 설정 파일

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요