- [spring] REST 구현 (@RestController를 사용하는 이유/ResponseEntity를 사용하는 이유/글로벌 예외 핸들러 정의하기)2024년 11월 12일 09시 50분 01초에 업로드 된 글입니다.작성자: @kimyu0218
@Controller 대신 @RestController를 사용하는 이유
웹 앱을 만드는 방식에는 크게 두 가지가 있다.
- 백엔드가 완성된 뷰를 제공하는 앱
- 프론트엔드-백엔드 분리 방식의 앱
프론트엔드-백엔드 분리 방식의 경우, 백엔드는 원시 데이터만 제공한다. 따라서 스프링 MVC 다이어그램에서 뷰 리졸버가 더 이상 필요하지 않다.
스프링은 컨트롤러 클래스를 컨텍스트에 빈으로 등록하기 위해 `@Controller` 어노테이션을 사용한다. 하지만 HTTP 응답을 전달하기 위해서는 `@ResponseBody` 어노테이션이 필요하다. 이를 통해 컨트롤러가 뷰 이름이 아닌 HTTP 응답 데이터를 반환한다는 것을 디스패처 서블릿에게 알릴 수 있다.
@RequestMapping("/member") @Controller // 컨트롤러를 의미하는 스테레오 타입 어노테이션 public class MemberController { @GetMapping @ResponseBody // 뷰 이름이 아닌 HTTP 응답을 바로 반환한다 public List<Member> findMembers() { ... } }
하지만 메서드마다 `@ResponseBody` 어노테이션을 붙이는 것은 번거로운 일이다. 이를 해결하기 위해 스프링은 `@Controller`와 `@ResponseBody`를 결합한 `@RestController`를 제공한다.
@RequestMapping("/member") @RestController // HTTP 응답을 반환하는 컨트롤러 public class MemberController { @GetMapping public List<Member> findMembers() { ... } }
위 코드와 같이 더 이상 `@ResponseBody`를 반복하지 않아도 된다.
DTO 대신 ResponseEntity를 반환하는 이유
위의 코드처럼 컨트롤러는 DTO 객체를 반환할 수 있다. 스프링은 기본적으로 `200 OK` 응답을 반환한다.
하지만 DTO 대신 `ResponseEntity`를 반환하면 세밀한 제어가 가능하다. `ResponseEntity`는 HTTP 응답의 상태 코드, 헤더, 본문을 제어할 수 있는 기능을 제공한다.
@RequestMapping("/member") @RestController public class MemberController { @GetMapping public ResponseEntity<List<Member>> findMembers() { ... return ResponseEntity .status(HttpStatus.OK) .header("[NAME]", "[VALUE]") .body(result); } }
@RestControllerAdvice로 글로벌 예외 핸들러 정의하기
`MemberController`의 `findMembers` 메서드에서 403이나 500 같은 예외가 발생할 수 있다면 `try ~ catch`문으로 HTTP 상태별로 다른 HTTP 응답을 보내야 한다. 하지만 이는 컨트롤러 로직이 복잡해진다는 단점이 있다.
컨트롤러 로직이 길어지는 것을 방지하기 위해 `@RestControllerAdvice`와 `@ExceptionHandler`를 활용할 수 있다.
- `@RestControllerAdvice` : `@ControllerAdvice`와 `@ResponseEntity` 어노테이션을 결합한 것으로, 컨트롤러의 메서드를 가로채 예외 처리 메서드를 구현할 수 있다.
- `@ExceptionHandler` : 예외를 처리하는 메서드 앞에 붙이는 어노테이션으로, `value` 속성에 해당 메서드가 처리할 수 있는 예외 클래스를 설정한다.
@RestControllerAdvice // AOP를 통해 RestController의 메서드를 가로챈다 public class GlobalExceptionHandler { // BadRequestException 클래스를 예외 처리한다 @ExceptionHandler({BadRequestException.class}) public ResponseEntity<ErrorResponse> handleBadRequestException(final BadRequestException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage())); } ... }
이처럼 어플리케이션에서 발생할 수 있는 모든 예외를 핸들링하면 클라이언트에게 시스템 내부 정보가 노출되는 것을 사전에 예방할 수 있다.
🔗 GlobalExceptionHandler 소스코드
참고자료
'backend > 프레임워크' 카테고리의 다른 글
[spring] 스프링 부트 시작하기 (스프링/스프링 부트/@SpringBootApplication/ApplicationContext) (1) 2024.12.19 [spring] JPA/Hibernate, Spring Data JPA, 영속성 컨텍스트 (0) 2024.12.16 [spring] 스프링 이벤트 (ApplicationEvent와 ApplicationListener/@EventListener와 @TransactionalEventListener/비동기 이벤트) (1) 2024.11.15 [spring] 스프링 AOP (0) 2024.11.10 [spring] 스프링 컨텍스트 (1) 2024.11.10 다음글이 없습니다.이전글이 없습니다.댓글