무던하게

RESTful API를 설계할 때, 클라이언트가 서버로부터 의미 있는 응답을 받을 수 있도록 설계하는 것은 매우 중요합니다. 스프링 부트는 ResponseEntity를 활용하여 HTTP 응답을 유연하고 직관적으로 처리할 수 있도록 지원하며, 기본적으로 JSON 형식의 응답을 제공하여 클라이언트와 효율적으로 데이터를 주고받습니다.


1. ResponseEntity를 통한 응답 처리

ResponseEntity는 HTTP 응답의 상태 코드, 헤더, 본문(body)을 설정할 수 있는 클래스입니다. 이를 활용하면 API 응답을 더욱 명확하게 설계할 수 있습니다.

주요 기능

  1. HTTP 상태 코드 설정: 성공, 실패 여부를 명확히 전달.
  2. 응답 헤더 추가: 필요한 메타데이터를 포함.
  3. 응답 본문 설정: 클라이언트에게 반환할 데이터 포함.

1.1 기본 사용 예제

아래 예제는 사용자 정보를 조회한 뒤, 결과에 따라 적절한 HTTP 상태 코드를 반환합니다.

@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
    User user = userService.findUserById(id);
    if (user == null) {
        // 404 상태 코드와 빈 응답 반환
        return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    }
    // 200 상태 코드와 사용자 데이터 반환
    return ResponseEntity.ok(user);
}

1.2 상태 코드와 본문 설정

ResponseEntity를 활용하면 상태 코드와 본문을 유연하게 설정할 수 있습니다.

예제: 리소스 생성

@PostMapping("/users")
public ResponseEntity<String> createUser(@RequestBody User user) {
    userService.saveUser(user);
    // 201 상태 코드와 성공 메시지 반환
    return ResponseEntity.status(HttpStatus.CREATED)
                         .body("User created successfully!");
}

예제: 리소스 삭제

@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
    boolean deleted = userService.deleteUser(id);
    if (!deleted) {
        // 404 상태 코드 반환
        return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    }
    // 204 상태 코드 반환 (본문 없음)
    return ResponseEntity.noContent().build();
}

1.3 헤더 포함 응답

ResponseEntity를 사용하면 응답 헤더를 쉽게 추가할 수 있습니다.

예제: 응답에 커스텀 헤더 추가

@GetMapping("/users/{id}")
public ResponseEntity<User> getUserWithHeader(@PathVariable Long id) {
    User user = userService.findUserById(id);
    if (user == null) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    }
    // 커스텀 헤더 추가
    return ResponseEntity.ok()
                         .header("X-Custom-Header", "CustomHeaderValue")
                         .body(user);
}

2. JSON 응답 처리

스프링 부트는 Jackson 라이브러리를 통해 객체를 자동으로 JSON으로 변환합니다. 따라서 @RestController 또는 @ResponseBody를 사용하면 추가 작업 없이도 JSON 응답을 쉽게 생성할 수 있습니다.


2.1 기본 JSON 응답

예제: 객체를 JSON으로 반환

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    return userService.findUserById(id);
}
  • 스프링 부트는 자동으로 User 객체를 JSON으로 변환하여 클라이언트에 반환합니다.

2.2 JSON 필드 제어

@JsonIgnore 또는 @JsonProperty를 사용하면 JSON 응답의 필드를 세밀하게 제어할 수 있습니다.

  • JSON 관련 어노테이션(@JsonIgnore, @JsonProperty, @JsonInclude)은 과거에 많이 사용되었지만, 현재 실무에서는 DTO를 사용하여 응답 데이터를 명확히 관리하고 보안을 강화하는 방식이 훨씬 더 보편적이고 권장되는 방식입니다.

예제: 필드 숨기기

public class User {
    private Long id;
    private String name;

    @JsonIgnore
    private String password;

    // Getter, Setter
}
  • password 필드는 JSON 응답에서 제외됩니다.

예제: 필드 이름 변경

public class User {
    private Long id;

    @JsonProperty("full_name")
    private String name;

    // Getter, Setter
}
  • name 필드는 JSON에서 full_name으로 표시됩니다.

2.3 커스터마이징 JSON 출력

스프링에서 ObjectMapper를 커스터마이징하여 JSON 응답의 형식을 제어할 수 있습니다.

예제: 전역 설정

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // null 값 제외
        return mapper;
    }
}

3. ResponseEntity와 JSON의 결합

ResponseEntity와 JSON 응답을 함께 활용하면 상태 코드, 헤더, JSON 데이터를 조합한 응답을 생성할 수 있습니다.

예제: JSON 데이터와 상태 코드 반환

@GetMapping("/users/{id}")
public ResponseEntity<Map<String, Object>> getUserAsMap(@PathVariable Long id) {
    User user = userService.findUserById(id);
    if (user == null) {
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("message", "User not found");
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
    }

    Map<String, Object> successResponse = new HashMap<>();
    successResponse.put("id", user.getId());
    successResponse.put("name", user.getName());
    return ResponseEntity.ok(successResponse);
}

4. 응답 처리 모범 사례

  1. 적절한 상태 코드 사용: 요청의 결과를 명확히 표현하기 위해 올바른 HTTP 상태 코드를 반환하세요.
  2. JSON 응답 일관성 유지: 클라이언트가 예측 가능한 구조의 데이터를 받을 수 있도록 통일된 JSON 형식을 유지하세요.
  3. 에러 메시지 포함: 에러 상황에서는 상태 코드와 함께 명확한 에러 메시지를 JSON으로 제공하세요.

예제: 에러 처리 공통화

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<Map<String, Object>> handleUserNotFound(UserNotFoundException ex) {
        Map<String, Object> response = new HashMap<>();
        response.put("error", "User Not Found");
        response.put("message", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }
}

결론

  • ResponseEntity는 상태 코드, 헤더, 본문을 유연하게 처리하여 응답의 가독성과 신뢰성을 높입니다.
  • 스프링 부트의 JSON 처리 기능을 활용하면 최소한의 설정으로 직관적인 API를 설계할 수 있습니다.
profile

무던하게

@moodone

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!