@RestController 애너테이션
@RestController = @Controller + @ResponseBody
@Controller는 요청에 대해 HTML View 템플릿을 반환하는데 사용된다.
RESTful한 API 개발에선 JSON, XML, 파일 등 실제 데이터 자체를 반환해야하므로 @RestController가 사용된다.
- @RestController는 클래스 단위에 적용되며, 내부 모든 메서드는 @ResponseBody의 효과를 가진다.
- ViewResolver를 거치지 않고 HttpMessageConverter를 통해 응답 데이터로 직렬화된다.
- 기본 출력 형식은 application/json이며 Accept 헤더로 명시할 수 있다.
@RestController
public class UserController {
@GetMapping("/hello")
public String hello() {
return "Hello JSON!"; // View 형태가 아닌 HTTP 본문에 그대로 출력된다
}
}
@Controller는 View 템플릿이며 사용할 때 @ResponseBody가 필요하다. RESTful API에 적합하지 않다.
@RestController는 HTTP 본문 데이터에 응답하며 JSON으로 자동 반환된다. RESTful API에 적합하다.
디렉터리 구조 예
src/main/java/com/example/controller/view/PageController.java - > @Controller 사용함 (Thymeleaf 등..)
src/main/java/com/example/controller/api/UserRestController.java - > @RestController 사용함 (JSON API)
ResponseEntity를 통한 응답 제어
구조, 목적
ResponseEntity<T>는 HTTP 응답의 전체 구조(상태코드, 헤더, 바디)를 개발자가 직접 제어할 수 있도록 도와주는
스프링 클래스이다.
<T> : 응답 본문에 포함할 객체(JSON으로 직렬화됨)
상태코드 설정 : .ok(), status(HttpStatus.CREATED) 등등..
커스텀 헤더 설정 : .header("key","value") 등..
@RestController
public class UserController {
private final UserService userService;
@PostMapping("/api/users")
public ResponseEntity<UserDto> create(@ResponseBody CreateUserDto dto) {
UserDto saved = userService.create(dto);
URI location = URI.create("/api/users/" + saved.getId());
return ResponseEntity.created(location) // 201 상태코드 + location 헤더
.header("request-ID", UUID.randomUUID().toString())
.body(saved); // body는 응답 본문임
}
}
응답코드 예시 (과제에서는 .ok()만 쓴다)
| 생성 성공 | 201 Created | Location 헤더가 필수임 |
| 삭제 성공 | 204 No Content | 본문 없이 성공함(void) |
| 조건 실패 | 422 Unprocessable Entity | 비즈니스 로직 위반, 사용자 문제 |
| 서버 오류 | 500 Internal Server Error | 예외 발생, 개발자쪽 문제 |
아래는 에러처리에 활용
@GetMapping("/users/{id}")
public ResponseEntity<?> find(@PathVariable Long id) {
return userService.findById(id) // id를 기반으로 유저를 찾는데
.map(ResponseEntity::ok) // ResponseEntity타입을 ok로 반환
.orElse(ResponseEntity.status(HttpStatus.NOT_FOUND) // 할수없다면 예외발생
.body(Map.of("error","사용자를 찾을 수 없음")));
}
* 성공 응답은 200, 201, 204로 구분하고 실패응답은 400, 401, 403, 404, 422등 정확하게 설계해야한다.
* Swagger 문서에서도 상태코드별 응답형식을 명시하면 클라이언트 개발자와 협업을 했을 때 충돌이 줄어든다.
* ResponseEntity<Object>는 왠만하면 피하고 제네릭 타입을 명시해야한다. 타입 안정성과 직렬화 성능 향상에 유리하다.
HTTP 메세지 컨버터(MessageConverter)의 동작방식
메세지 컨버터 : Spring에서 요청 본문을 자바 객체로 변환하거나, 자바 객체를 응답 본문으로 직렬화할 때 사용하는 컴포넌트
요청흐름
HTTP JSON 요청 - > HttpMessageConverter - > @RequestBody - > Java 객체
응답흐름
Java 객체 - > @ResponseBody or ResponseEntity - > HttpMessageConverter - > HTTP 응답 JSON
Spring 내장 컨버터(자세하게 알 필요까진 없다)
| 클래스 | 기능 | 미디어 타입 |
| MappingJackson2HttpMessageConverter | JSON - > 객체 | application/json |
| StringHttpMessageConverter | 문자열 변환 | text/plain |
| ByteArrayHttpMessageConverter | 바이너리 파일 처리 | application/octet-stream |
| FormHttpMessageConverter | application/x-www-form-urlencoded | 폼 데이터 처리 |
* spring-boot-starter-web을 사용하면 Jackson 기반 JSON 컨버터가 자동으로 설정된다.
Content Negotiation(콘텐츠 협상)
클라이언트의 Accept 헤더에 따라 반환 형식을 결정한다.
Accept: application/json - > JSON 응답
Accept: application/xml - > XML 응답
@PostMapping(value = "/data",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public DataDto getData() {
return new DataDto();
}
produces 안에 있는 MediaType이 중요한것 같은데.. 아니면 value값에 따라 반환 형식이 결정되는건가?
* XML 컨버터 사용시 jackson-dataformat-xml을 추가해줘야한다.
커스텀 컨버터 : 어려우니 다음 시간에
* 파일 업로드는 MultipartHttpServletRequest, @RequestPart와 multipart/form-data 컨버터의 조합으로 한다고 한다.
아~
자주 쓰는 핵심 애너테이션인데 어렵네요잉
'Spring Boot' 카테고리의 다른 글
| Entity 설계 (7) | 2025.07.22 |
|---|---|
| RESTful 구현 기본 : Controller에서 요청 처리 (1) | 2025.07.18 |
| REST : 제약조건 (0) | 2025.07.10 |
| REST : 탄생배경과 개념 (7) | 2025.07.10 |
| MVC : Spring Boot 시작하기 (0) | 2025.07.06 |