Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

rudu_std

ResponseEntity 본문

Spring & Spring Boot

ResponseEntity

Ru_Du 2024. 8. 25. 15:59

ResponseEntity

ResponseEntity는 HTTP 응답을 나타내는 Spring Framework의 클래스이다. 이 클래스는 요청에 대한 응답의 HttpHeader, HttpBody 및 Status Code를 포함하여 클라이언트에게 전달할 수 있는 다양한 기능을 제공한다.

ResponseEntity는 @RestController와 함께 사용되어 JSON 또는 XML과 같은 RESTful 서비스를 제공하는데 이상적이다.

 

왜 ResponseEntity를 사용하는가?

  1. HTTP 상태 코드 제어: ResponseEntity를 사용하면 응답에 대한 HTTP 상태 코드를 명시적으로 지정할 수 있습니다. 이는 클라이언트에게 정확한 상태 정보를 제공하는 데 도움이 됩니다.
  2. 응답 본문 및 헤더 제어: ResponseEntity를 통해 응답 본문과 헤더를 세밀하게 제어할 수 있습니다.
  3. 유연성: ResponseEntity를 사용하면 일반적인 객체 또는 커스텀 클래스를 응답으로 반환할 수 있으며, Spring은 자동으로 해당 객체를 적절한 형식으로 변환합니다.

주요 특징 및 기능

Response Status Code 설정: ResponseEntity 객체를 생성할 때, HTTP Status Code를 명시적으로 제공할 수 있다. 이는 API의 Response를 보다 명확하게 표현할 수 있게 해주며, 클라이언트에게 유용한 정보를 제공한다.

Response Header 조작: Response에 필요한 HTTP Header를 추가하거나 변경할 수 있다. 이를 통해 캐싱 정책, 내용 유형, CORS Header 설정 등을 세밀하게 조정할 수 있다.

Response Body 관리: ResponseEntity는 Response Body에 직접 객체를 설정할 수 있으며, Spring의 메시지 컨버터를 통해 객체를 JSON이나 XML 등의 형식으로 자동 변환할 수 있다.

 

자주 사용되는 HTTP 상태 코드 및 코드

상태 코드(Status Code)로는 100번대부터 500번대까지 매우 다양하게 있습니다.
여기서는 주로 사용되는 상태 코드를 알아보고 자세한 내용은 아래 링크에서 확인할 수 있습니다.

  • HttpStatus.OK : 200 OK
  • HttpStatus.CREATED : 201 Created
  • HttpStatus.NO_CONTENT : 204 No Content
  • HttpStatus.BAD_REQUEST : 400 Bad Request
  • HttpStatus.UNAUTHORIZED : 401 Unauthorized
  • HttpStatus.FORBIDDEN : 403 Forbidden
  • HttpStatus.NOT_FOUND : 404 Not Found
  • HttpStatus.INTERNAL_SERVER_ERROR : 500 Internal Server Error
return ResponseEntity.ok(data); // 200 OK
return ResponseEntity.status(HttpStatus.CREATED).body(data); // 201 Created

->> more Status Code

 

응답 본문 설정

응답 본문은 ResponseEntity의 body() 메서드를 통해 설정된다. 이 본문은 JSON, XML, 텍스트 등 다양한 형식으로 클라이언트에게 전송된다.

return ResponseEntity.ok("Hello, World!"); // 응답 본문으로 문자열 반환
return ResponseEntity.ok(new MyDto()); // DTO 객체를 JSON으로 변환하여 반환

응답 헤더 설정

응답 헤더는 ResponseEntity의 headers() 메서드를 통해 설정할 수 있다. 이를 통해 클라이언트에게 추가적인 메타데이터를 전송할 수 있다.

// 헤더와 바디를 동시에
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "value");
return ResponseEntity.ok().headers(headers).body(data);

// 헤더만
HttpHeaders headers = new HttpHeaders();
headers.set("Custom-Header", "value");

return ResponseEntity.ok().headers(headers).build();
///////////////////////////////////////////////////////
// body(data)를 호출하면 그 즉시 ResponseEntity 객체가 만들어져 반환되므로 
// build()를 추가로 호출할 필요가 없다.

다양한 상태 코드 반환

return ResponseEntity.noContent().build(); // 204 No Content
return ResponseEntity.badRequest().body("Invalid request"); // 400 Bad Request

ResponseEntity는 특정 상태 코드에 맞는 편리한 메서드를 제공하며

필요에 따라 다양한 상태 코드를 쉽게 반환할 수 있다.

 

예시 코드

@GetMapping("/example")
public ResponseEntity<String> example() {
    String message = "Hello, this is an example.";
    
    HttpHeaders headers = new HttpHeaders();
    headers.add("Custom-Header", "CustomValue");
    
    return ResponseEntity.ok()
                         .headers(headers)
                         .body(message);
}

 


Controller 와 RestController 의 비교

// Controller 
@GetMapping("/")
public User getUser() {
    User user = userService.getUser();
    return user;
}
// 일반적으로 Controller에서 객체를 Return 하는 경우 HTTP 응답을 제어할 수가 없다

///////////////////////////////////////////

// RestController 
@GetMapping("/")
public ResponseEntity<User> getUser() {
	User user = userService.getUser();
	return ResponseEntity.ok(user);
}
// 성공을 의미하는 OK(200 code)와 함께 user 객체를 Return 하는 코드

 

ResponseEntity는 생성자 보다는 빌더 패턴 사용

생성자 패턴

return new ResponseEntity(body, headers, HttpStatus.valueOf(200));

 

빌더 패턴

return ResponseEntity.ok()
	.headers(headers)
	.body(body);

ResponseEntity.ok()는 정적 팩토리 메서드 이다.

그리고 뒷부분을 메소드 체이닝으로 연결한 빌더 패턴을 사용하는 것이 의미가 더 직관적이고 유지보수에 좋다.

[정적 팩토리 메서드란 : https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/]

 

ResponseEntity의 Body 타입 명시

아래와 같이 메소드를 작성하면 ResponseEntity의 타입을 작성하지 않은 것이라 Object(모든 자바의 부모 클래스)를 Return 받는다.

 

아래의 코드는 아래의 코드와 기능적으로 의미가 같다.

public ResponseEntity getUser() {}

public ResponseEntity<Object> getUser() {}

ResponseEntity의 타입은 명시하지 않으면 Object 타입을 Return 해준다.

다만 대부분의 상황에서는 유지보수를 위해 타입을 명시해 주는 것이 직관적이므로 좋다.

 

타입이 특정되지 않는다면 Return 타입은 Object 대신 와일드카드 사용

반환 타입이 명확하지 않아도 Return 된다

public ResponseEntity<Object> getUsers() {
    List<User> users = userService.getUsers();
    return ResponseEntity.ok(users);
}
/////////////////////////////////////////////////
public ResponseEntity<?> getUsers() {
    List<User> users = userService.getUsers();
    return ResponseEntity.ok(users);
}

보통 타입을 여러개 받고 싶은 경우 아래와 같이 Object나 와일드카드를 사용할 수 있다.

이러한 방법은 개발 구성원들끼리 공유될 경우 개발 생산성을 높일 수 있기 때문에 좋은 선택이 될 수 있다.

둘 모두 사용 가능하지만 Return 할 때 객체의 타입이 명확하지 않을 때는 Object를 사용하는 것보다 와일드카드를 사용하는 것이 좋다.

정해지지 않은 타입을 반환한다는 점에서는 Object나 와일드카드나 같은 기능을 하지만 와일드카드를 사용하면 반환할 수 있는 객체의 타입이 명확하지 않아도 사용이 가능하다.

 

@GetMapping("/users")
public ResponseEntity<?> getUsers() {
    List<?> users = userService.getUsers();
    return ResponseEntity.ok(users);
}

예시

 

[와일드 카드와 오브젝트의 차이]

더보기

 

  • 와일드카드 ?: 어떤 타입이든 허용하되, 그 타입이 무엇인지는 모를 때 사용한다. 예를 들어, List<?>는 List<String>, List<Integer> 등 어떤 타입의 리스트든 참조할 수 있지만, 타입이 확정되지 않기 때문에 리스트에 null 이외의 객체를 추가할 수 없다.
  • Object 타입: 모든 타입의 객체를 허용하되, 그 타입이 반드시 Object여야 한다. 예를 들어, List<Object>는 모든 객체를 담을 수 있지만, List<String>이나 List<Integer>와는 호환되지 않는다.

 

 

와일드카드, Object vs 사용자 객체

와일드카드나 Object를 이용해서 API를 만든다면 해당 API를 사용하는 사용자는 불필요한 형변환 작업을 해줘야 하는 단점이 존재한다.

 

아래는 (List<User>)로 불필요한 형변환을 하는 예제.

public ResponseEntity<Object> getUsers() {
    List<User> users = userService.getUsers();
    return ResponseEntity.ok(users);
}
///////////////////////////////////////////
ResponseEntity<Object> response = myController.getUsers();
Object body = response.getBody();

// 형변환 필요
List<User> users = (List<User>) body;

 

여기서 response.getBody()는 Object 타입을 반환하므로, 사용자는 List<User>로 형변환을 해야 한다.

만약 잘못된 형변환을 한다면, 런타임에 ClassCastException이 발생할 수 있다.

 

제네릭을 사용하여 형변환 문제 해결

제네릭을 사용하면 이 문제를 피할 수 있다.

예를 들어, ResponseEntity<List<User>>를 사용하면 API 사용자가 형변환을 할 필요가 없다.

public ResponseEntity<List<User>> getUsers() {
    List<User> users = userService.getUsers();
    return ResponseEntity.ok(users);
}
/////////////////////////////////////////////////
ResponseEntity<List<User>> response = myController.getUsers();
List<User> users = response.getBody(); // 형변환 불필요

 

 

설명

더보기
public ResponseEntity<List<User>> getUsers() {
    List<User> users = userService.getUsers();
    return ResponseEntity.ok(users);
}

 

  • 이 메서드는 ResponseEntity<List<User>> 타입의 객체를 반환.
  • 내부적으로 userService.getUsers()를 호출해서 List<User> 객체를 가져온다.
  • 그 다음, ResponseEntity.ok(users)를 통해 List<User> 객체를 HTTP 응답의 본문(body)에 담아서 클라이언트에게 반환한다.
ResponseEntity<List<User>> response = myController.getUsers();
List<User> users = response.getBody(); // 형변환 불필요

 

  • ResponseEntity<List<User>> response = myController.getUsers();에서는 myController.getUsers() 메서드를 호출하여 반환된 ResponseEntity<List<User>> 객체를 response 변수에 저장한다.
  • response 객체는 ResponseEntity의 인스턴스이며, 이 객체는 HTTP 응답 상태 코드, 헤더, 본문(body)을 가지고 있다.
  • **response.getBody()**는 ResponseEntity 객체의 본문(body) 부분을 가져오는 메서드. 이 경우, getBody()는 List<User> 타입의 데이터를 반환한다.
  • 따라서, **List<User> users = response.getBody();**는 response 객체의 본문에 들어 있는 List<User> 데이터를 users 변수에 담는 코드이다.

결론

  • myController.getUsers()가 반환한 ResponseEntity 객체의 본문(body)에는 List<User>가 들어 있다.
  • response.getBody()를 호출하면 그 본문에 담긴 List<User>가 반환된다.
  • 이 값을 List<User> users 변수에 할당함으로써, users 변수는 이제 List<User> 타입의 데이터를 참조하게 된다.

API 설계 측면에서는 타입 파라미터를 이용하는 것보다 명시적으로 사용자 객체를 지정해 주는 것이 더 좋다.

'Spring & Spring Boot' 카테고리의 다른 글

WebRTC [화상 랜덤 채팅 구현 시 맞닥뜨린 문제]  (0) 2024.10.10
Spring - Pageable  (0) 2024.09.02
@RestController, @Controller  (0) 2024.08.20
REST API란 무엇인가?  (0) 2024.08.20
@RequestParam과 @PathVariable  (0) 2024.08.20