HTTP와 Web Application에 대한 내용을 살펴보았으니, 이를 Spring Boot에서 어떻게 활용하고 구현하는지를 확인해 봅시다.
Spring MVC
Spring 프레임워크는 다양한 구성품으로 이뤄져 있습니다. 그 중 하나는 일반적인 의미의 Web Application을 만드는 Spring Web, 또는 Spring MVC Artifact 입니다. Web Application을 만들때는 이 Dependency를 꼭 포함시키도록 합니다.
여기서 Spring MVC의 MVC는 어플리케이션을 만드는 설계 패턴을 일컫는 말입니다. Model - View - Controller의 약자로서, 어플리케이션 개발에 있어 세 부분으로 나눠서 개발을 하여 코드의 역할을 분담하는 설계입니다.
그림에서 살펴볼 수 있듯, 사용자가 실제로 조작하게 되는 부분은 Controller로서, 이곳이 인터페이스의 역할을 합니다. 이 Controller는 이후 실제 서비스라고 볼 수 있는 데이터인 Model을 조작하게 됩니다. Model은 다시 사용자가 확인하는 데이터의 표현, View를 갱신합니다. 사용자는 이 View를 확인하고 다시 Controller를 통해 의사를 전달합니다.
웹 어플리케이션에서 MVC의 대부분은 서버 측에서 관리됩니다. Web Browser에서는 View에서 전달받은 HTML을 통해 Controller에 Hyperlink나 Form-Data를 전송합니다. Model은 웹 서버에서 흔히 데이터베이스와 그와 상호 작용하는 부분으로 구성됩니다. Controller가 Model의 조작을 완료하면 갱신된 View를 브라우저로 전송합니다.
이를 Spring Framework에서 확인하면 위와 같은 그림으로 구성되어 있습니다. View Resolver가 View의 역할을 하고, 거기서 돌려줄 HTML Template은 저희가 구성하게 됩니다. Controller를 통해 사용자 요청, 정확히는 HTTP 요청의 Path를 구성하게 되며, 데이터베이스를 구성하고, Mybatis 또는 JPA를 이용하여 사용함으로서 Model을 구성합니다. 한번의 사용자 요청에 대하여 처리되는 순서는 위의 그림과 같습니다.
Controller와 RequestMapping
요청 경로 구성하기
위에서 이야기했듯, Controller에서 사용자의 요청을 Path 단위로 분류하게 됩니다. 그리고 지난 챕터에서 이야기한 IoC Container는 저희가 정의한 Java class 중 일부를 Controller로 인식하고 관리하게 됩니다. 이때 사용하는 것이 Controller Annotation 입니다.
임의로 정의한 Java class에 @Controller 를 붙이면, 해당 class는 Bean 객체로서 인식됩니다. 즉, Spring Boot가 실행되면서 해당 class의 객체를 생성, IoC Container에서 관리하게 됩니다. 특히 그 중 Controller 는 사용자의 요청을 분류하기 위한 Controller 객체로 인식하여, 내부의 RequestMapping 을 확인하고 구성합니다.
@RequestMapping 어노테이션은 @Controller class 내부에 존재하는 함수에 추가하여, 위 Spring Application 구조의 Handler Mapping으로 그 정보를 전달합니다. Handler Mapping은 사용자의 HTTP 요청의 경로를 확인, @RequestMapping 이 붙은 함수 중 어떤 함수로 요청을 전달할지를 결정하게 됩니다.
즉 위의 @RequestMapping 의 인자로 전달한 "/profile" 이라는 문자열은, 이 서버로 도달한 HTTP 요청 중 /profile 이라는 경로로 보내진 요청을, 이 함수에서 처리해 달라는 의미입니다.
@RequestMapping 의 경우, @Controller 가 붙은 class에도 추가할 수 있습니다. 이 경우 해당 @Controller 의 모든 @RequestMapping 의 상위 경로에 추가됩니다.
위의 @RequestMapping 과, 내부 함수의 @RequestMapping("/profile") 과 합쳐지면, 최종 경로는 /view/profile 이 됩니다.
HTTP 요청의 Method는 GET, POST, PUT, DELETE 등 여러가지가 존재합니다. 이는 위에서 살펴본 HTTP의 요청의 Request Line에 포함되어 있는데, 이러한 Method를 기준으로 @RequestMapping 외의 어노테이션이 존재합니다. 위의 그림에서 보이는 @GetMapping 을 비롯하여, @PostMapping, @PutMapping 등이 존재합니다.
참고
단순 URL의 Path만 전달하는 것이 아닌, 부수적인 정보도 포함시킬 수 있습니다. 대표적으로 produces 와 conumses 인자를 전달할 수 있는데, consumes 는 요청으로 받을 수 있는 Media Type, produces 는 응답으로 생성되는 Media Type을 설정하기 위한 인자입니다. 이때 문자열의 형태로 Media Type을 정의할 수도 있지만, org.springframework.http.MediaType 에 정의된 상수를 사용하는 것도 좋은 방법입니다.
응답 결정
위에서도 잠시 살펴보았지만, 사용자에게 돌아가는 응답은 두갈래로 나뉘어서 처리됩니다. 이중 단순한 데이터의 요청은 즉시 사용자에게 응답의 형태로 돌아가지만, 페이지를 요구하는 요청은 View Resolver에 거쳐서 전달되게 됩니다.
@Controller 와 @RequestMapping 을 사용할 때, String 을 응답의 형태로 구성하게 될 경우, 이 응답은 Spring Framework의 View Resolver로 전달되게 됩니다. 저희는 지금 "profile.html" 이라는 응답을 전달하게 되는데, 이때 저희 Application 및 자원의 구성에 따라 profile.html 을 찾아서 응답으로 보내게 됩니다.
이때 어떤 HTML을 사용하게 되는지는, 잠시 뒤 살펴볼 설정에 따라 결정되게 됩니다. 위의 그림은 그 후보들입니다.
반드시 HTML을 응답으로 전달해야 되는 것은 아닙니다. 이미 위에서 살펴 보았듯이, produces 인자를 전달하면 응답의 Media Type을 결정할 수 있다고 하였습니다. 이때 @ResponseBody 어노테이션을 Return Type (지금 같은 경우 SamplePayload )에 붙여주게 되면, 해당 Return Type의 데이터를 JSON과 같은 Media Type으로 변환하여 응답을 구성합니다.
또는 ResponseEntity 객체를 사용할 수 있습니다. 이 ResponseEntity 는 HTTP 응답을 추상화한 객체로서, HTTP 응답의 전체 혹은 일부를 구성할 수 있습니다.
Controller와 RestController
기본적으로 위에서 살펴본 @Controller 를 사용하게 되면, 요청을 위한 경로와 응답을 구성할 수 없는 형태는 없습니다. 하지만 client와 server를 분리하게 되면, 서버 측에서는 최대한 데이터의 처리를 하기 위해, Controller 를 통해 View를 전달하는 기능들과 데이터를 주고받는 기능들을 class 단위에서 분리하기도 합니다.
이때, 이 “데이터 상호작용을 위한 @Controller class”를 위한 편의 어노테이션이 @RestController 어노테이션 입니다.
간단하게 생각하면 @RestController 는 내부의 모든 함수의 Return Type에 @ResponseBody 가 추가되는 @Controller 라고 생각할 수 있습니다. 즉 @RestController 내부의 @RequestMapping 함수들은 ResponseEntity 나 @ResponseBody 어노테이션을 사용하지 않아도, 데이터 응답, 즉 JSON과 같은 데이터를 전송할것이라고 기대할 수 있습니다.
Spring Framework는 내부적으로 처리할 수 있는 데이터의 형식이 매우 다양합니다. 위의 예시에서는 응답으로 byte[] 를 반환하고 있습니다. 어떠한 형태의 응답이든 데이터의 일종이고, MediaType.IMAGE_PNG_VALUE 와 함깨 Client에게 이 데이터가 이미지 임을 전달하는 부분입니다. 이미지의 처리를 Spring이 직접 관여하지는 않습니다만, 어쨋든 byte[] 데이터가 HTTP의 응답에 기록할 부분임을 알고 있기 때문에 정상적으로 작동하여 이미지를 보여줄 수 있습니다.