컨트롤러 레이어는 비즈니스 로직 처리보다는 HTTP 요청/응답을 담당하는 레이어이다.
HTTP는 GET/POST/PUT/DELETE/OPTIONS 등과 같은 메서드와 URI를 이용해 서버에 HTTP 요청을 보낼 수 있다.
그렇다면 서버는 이 요청을 받은 후 처리를 어떻게 할까?
[HTTP 요청]
GET /test HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Content-Length: 17
{
"id": 123
}
- 클라이언트가 서버 localhost:8080에 HTTP GET 메서드를 이용해 test라는 리소스를 요청함
- 서버는 자기 주소를 제외한 /{리소스}부분과 어떤 HTTP 메서드를 사용했는지 알아야 함
그 이후 해당 리소스의 HTTP 메서드에 연결된 메서드를 실행해야 함
스프링 부트 스타터 웹(spring-boot-starter-web)
스프링 부트 스타터 웹(spring-boot-starter-web)의 어노테이션을 이용하면 이 연결을 쉽게 할 수 있다.
스프링 부트 스타터 웹은 스프링 부트 프로젝트 설정 시 함께 다운받았던 라이브러리이다.
[build.gradle]
implementation 'org.springframework.boot:spring-boot-starter-web'
이제부터 할 연결 작업의 어노테이션은 모두 스프링 부트 스타터 웹 패키지에서 제공하는 것이다.
TestController
[TestController.java]
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1") // 리소스
public class TestController {
@GetMapping("/healthcheck")
public String testController() {
return "Healthcheck OK!";
}
}
- 우리는 REST API를 구현하므로 @RestController 어노테이션을 이용해 이 컨트롤러가 RestController 임을 명시함
- RestController를 이용하면 HTTP와 관련된 코드 및 요청/응답 매핑을 스프링이 알아서 해줌
* 경로 맨 앞에 / 붙이는 거랑 / 안붙이는 거랑 큰 차이 없음 !
- Client
GET http://localhost:8080/api/v1/healthcheck - Server
@RestController HTTP 요청 처리
@RequestMapping("/api/v1") 리소스 경로 지정
@GetMapping("/healthcheck") HTTP GET 메서드 처리, 리소스 경로 지정
@GetMapping(value = "healthcheck") 해도 됨
리소스 경로는 @RequestMapping 또는 @GetMapping에 지정하면 된다. 둘다 해도 되고, 둘 중 하나에 해도 됨!
실행 결과
./gradlew bootRun 실행하면 애플리케이션이 실행되어 위의 사진같이 나온다.
매개변수 넘겨받는 방법
/test가 아닌 /test/{id}로 PathVariable 이나 /test?id=123처럼 요청 매개변수를 받아야 한다면 어떻게 할까?
매개변수 넘겨받는 방법은 2가지가 있다.
- @PathVariable
- @RequestParam
@PathVariable
- /{id}와 같이 URI의 경로로 넘어오는 값을 변수로 받을 수 있음
import org.apringframework.web.bind.annotation.PathVariable;
@GetMapping("/{id}")
public String testControllerWithPathVariables(@PathVariable(required = false) int id) {
return "Hello World! ID " + id;
}
- Client
GET http://localhost:8080/test/123 - Server
@RestController HTTP 요청 처리
@RequestMapping("test") 리소스 경로 지정
@GetMapping("/{id}") HTTP GET 메서드 처리, 변수 id에 매핑
- /{id} 경로로 들어오는 임의의 숫자 또는 문자를 변수 id에 매핑하라는 뜻
- 컨트롤러의 id 변수에 123이 들어간다.
@RequestParam
import org.apringframework.web.bind.annotation.RequestParam;
@GetMapping("/testRequestParam")
public String testControllerRequestParam(@RequestParam(required = false) int id) {
return "Hello World! ID " + id;
}
- ?id={id}와 같이 요청 매개변수로 넘어오는 값을 변수로 받을 수 있음
- Client
GET http://localhost:8080/test/testRequestParam?id=123 - Server
@RestController HTTP 요청 처리
@RequestMapping("test") 리소스 경로 지정
@GetMapping("/testRequestParam") HTTP GET 메서드 처리, 리소스 경로 지정
testControllerRequestParam(@RequestParam(required = false) int id)- @RequestParam으로 들어온 임의의 숫자 또는 문자를 변수 id에 매핑
- 컨트롤러의 id 변수에 123이 들어간다.
@RequestBody
- 보통 반환하고자 하는 리소스가 복잡할 때 사용한다.
- 예를 들어 String이나 int 같은 기본 자료형이 아니라 오브젝트처럼 복잡한 자료형을 통째로 요청에 보내고 싶은 경우이다.
[RequestBody 실습을 위한 TestRequestBodyDTO]
package com.example.demo.dto;
import lombok.Data;
@Data
public class TestRequestBodyDTO {
private int id;
private String message;
}
package com.example.demo.controller;
import com.example.demo.dto.ResponseDTO;
import com.example.demo.dto.TestRequestBodyDTO;
import org.springframwork.http.ResponseEntity;
import org.springframwork.web.bind.annotation.GetMapping;
import org.springframwork.web.bind.annotation.PathVariable;
import org.springframwork.web.bind.annotation.RequestBody;
import org.springframwork.web.bind.annotation.RequestMapping;
import org.springframwork.web.bind.annotation.RequestParam;
import org.springframwork.web.bind.annotation.RequestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("/testRequestBody")
public String testControllerRequestBody(@RequestBody TestRequestBodyDTO testRequestBodyDTO) {
return "Hello! ID " + testRequestBodyDTO.getId() + " Message:" + testRequestBodyDTO.getMessage();
}
}
- TestRequestBodyDTO를 요청 바디로 받는 testControllerRequestBody() 메서드이다.
- Client
GET http://localhost:8080/test/testRequestParam {'id`: 123, 'message`: `Hello!`} - Server
@RestController HTTP 요청 처리
@RequestMapping("test") 리소스 경로 지정
@GetMapping("/testRequestParam") HTTP GET 메서드 처리, 리소스 경로 지정
testControllerRequestBody(@RequestBody TestRequestBodyDTO testRequestBodyDTO) - JSON 형태의 String인 {'id`: 123, 'message`: `Hello!`} 를 TestRequestBodyDTO로 변환 후 전달
* JSON의 내부는 구조가 TestRequestBodyDTO와 같아야 함.
[JSON 형태의 TestRequestBody DTO]
{
"id" : 123,
"message" : "Hello!"
}
@ResponseBody
- 문자열보다 복잡한 오브젝트를 리턴하려면 어떻게 할까 ?
- 요청을 통해 오브젝트를 가져올 수 있는데 응답으로 오브젝트를 리턴할 수 있다.
- 간단하게 그냥 오브젝트를 리턴하면 된다. 이런 간단함의 비밀은 @RestController 어노테이션에 있다.
@Controller
@ResponseBody
public @interface RestController {
...
}
- @Controller
- "이 클래스는 웹 요청을 처리하는 컨트롤러이다."를 스프링에게 알려줌
- 스프링은 이 정보를 바탕으로 해당 클래스의 오브젝트를 만들고 다른 오브젝트와의 의존성을 연결함
- @ResponseBody
- HTTP 응답의 바디(Body)로 변환하여 클라이언트에게 직접 전송하도록 하는 어노테이션
- 이 어노테이션이 붙은 메서드의 반환 값은 JSON이나 XML 등으로 변환되어 HTTP 응답으로 전송됨
- @RestController
- @Controller와 @ResponseBody의 조합
- 이 어노테이션을 사용하면 클래스의 모든 메서드에 @ResponseBody가 자동으로 적용
- 별도로 @ResponseBody를 붙일 필요 없이, 메서드의 반환 값이 HTTP 응답의 바디로 전송
[ResponseDTO를 반환하는 컨트롤러 메서드]
@GetMapping("/testResponseBody")
public ResponseDTO<String> testControllerResponseBody() {
List<String> list = new ArrayList<>();
list.add("Hello!");
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
return response;
}
[localhost:8080/test/testResponseBody HTTP 응답]
{
"error": null,
"data": "Hello!"
}
- 컴파일 후 localhost:8080/test/testResponseBody를 실행시키면 다음과 같은 JSON이 리턴된다.
- 우리가 작성할 컨트롤러는 모두 ResponseEntity를 반환할 예정이다.
[ResponseEntity 를 반환하는 컨트롤러 메서드]
@GetMapping("/testResponseEntity"]
public ResponseEntity<?> testControllerResponseEntity() {
List<String> list = new ArrayList<>();
list.add("Hello! And you got 400!");
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
// http status를 400으로 설정
return ResponseEntity.badRequest().body(response);
}
- 정상적으로 응답을 반환한다면 badRequest()가 아닌 ok() 메서드를 사용해야 함
ResponseEntity 리턴 VS ResponseDTO 리턴 비교
- 공통점: 리턴된 Body에는 아무 차이가 없고 모두 오브젝트를 리턴함
- 차이점: 전자는 헤더와 HTTP Status를 조작할 수 있음 → badRequest() or ok()
'개발' 카테고리의 다른 글
[React.js, 스프링부트, AWS로 배우는 웹 개발] 2장 - 퍼시스턴스 레이어 (0) | 2024.07.22 |
---|---|
[React.js, 스프링부트, AWS로 배우는 웹 개발] 2장 - 서비스 레이어 (0) | 2024.07.22 |
[React.js, 스프링부트, AWS로 배우는 웹 개발] 2장 - 레이어드 아키텍처 패턴, REST API 스타일 (0) | 2024.07.20 |
[React.js, 스프링부트, AWS로 배우는 웹 개발] 2장 - 스프링부트 실행 (0) | 2024.07.20 |
[React.js, 스프링부트, AWS로 배우는 웹 개발] 2장 - 스프링부트 설정 (0) | 2024.07.19 |