
Spring에서 컨트롤러(
@Controller)를 통해 API 응답을 반환하는 방식과 예외 처리는 크게 두 가지 방식으로 나눌수 있다.
API 응답을 처리할 때 성공/실패 여부를 전달하는 방법 두가지
HTTP 상태코드는 클라이언트가 요청한 작업이 성공했는지, 실패했는지를 나타내는 표준 코드
HTTP 상태코드의 종류
자주 사용되는 HTTP 상태코드
| 상태코드 | 의미 | 설명 |
|---|---|---|
| 200 OK | 요청 성공 | 정상적으로 요청을 처리하고 응답을 반환 |
| 201 Created | 리소스 생성 성공 | 새로운 자원이 성공적으로 생성됨 (POST 요청 시) |
| 400 Bad Request | 잘못된 요청 | 클라이언트의 요청이 잘못됨 (유효성 검증 실패) |
| 401 Unauthorized | 인증 실패 | 로그인 정보 부족 등으로 접근 불가 |
| 403 Forbidden | 권한 없음 | 인증은 했지만, 해당 작업을 수행할 권한이 없음 |
| 404 Not Found | 리소스 없음 | 요청한 리소스를 찾을 수 없음 |
| 500 Internal Server Error | 서버 오류 | 서버에서 예기치 못한 오류 발생 |
예제
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findUserById(id);
if (user == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); // 404 응답
}
return ResponseEntity.ok(user); // 200 응답
}
내부적으로 사용할 에러 코드가 필요할 경우, 자체적으로 정의한 상태 코드로 사용 가능
커스텀 상태코드 예시
| 상태코드 | 의미 | 설명 |
|---------|------|------|
| A01 | 타입 에러 | 요청한 데이터 타입이 맞지 않음 |
| A02 | 입력 에러 | 필수 입력 값이 누락됨 |
| B01 | 외부 서버 통신 오류 | 외부 API 호출 중 오류 발생 |
커스텀 상태코드의 특징
예제
@GetMapping("/users/{id}")
public ResponseEntity<CustomResponse> getUser(@PathVariable Long id) {
User user = userService.findUserById(id);
if (user == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new CustomResponse("A02", "사용자를 찾을 수 없습니다."));
}
return ResponseEntity.ok(new CustomResponse("A00", "요청 성공", user));
}
ResponseEntity<T>는 Spring에서 제공하는 HTTP 응답을 감싸는 클래스ResponseEntity<T> 사용하면 좋은 경우
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findUserById(id);
return ResponseEntity.ok(user); // HTTP 200 OK
}
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser); // HTTP 201 Created
}
@GetMapping("/users")
public ResponseEntity<String> getUser(@RequestParam(required = false) String name) {
if (name == null || name.isEmpty()) {
return ResponseEntity.badRequest().body("이름을 입력하세요!"); // HTTP 400 Bad Request
}
return ResponseEntity.ok("Hello, " + name);
}
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.findUserById(id)
.map(ResponseEntity::ok) // HTTP 200 OK
.orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); // HTTP 404 Not Found
}
@GetMapping("/error")
public ResponseEntity<String> throwError() {
throw new RuntimeException("서버 오류 발생!");
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 오류: " + e.getMessage());
}
Spring에서는 Optional을 활용하면 Null 처리와 예외 처리를 좀 더 쉽게 할 수 있음.
Optional 활용 예제
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));
return ResponseEntity.ok(user);
}
이렇게 하면 id에 해당하는 사용자가 없을 때, 자동으로 RuntimeException이 발생하며 500 Internal Server Error를 반환
Optional + orElseThrow 활용
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok) // 값이 있으면 200 OK 응답
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다.")); // 404 응답
}
ResponseEntity<T>를 활용하면, HTTP 상태코드를 포함하여 응답을 유연하게 반환할 수 있음Optional.ofNullable().orElseThrow()를 사용하면, Null 예외 처리를 깔끔하게 할 수 있음