231207W
여느날과 같이 오늘도 강의와 함께 아주 RestFull~ 한 API 작성 공부를 하는 도중, 어느 순간 ResponseEntity라는 녀석을 소개받았다.
이후로 한결같이 대면하며 즐거운 나날(ㅎ)을 보내는 도중 문득 이 녀석에 대해 궁금해졌다.
사실 공부하는 도중에 알게 된 녀석이기에 분명 설명도 들었을 터인데, 일단 쓰고, 또 일단 쓰고 …가 반복되다보니 잊어버린 듯 하다. 덩달아 이전에 쓰던 녀석도 가물가물해져 이번 기회에 알아보기로 했다.
일단 ResponseEntity를 알기 전에 API를 작성할 땐 이러했다.
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto,
@AuthenticationPrincipal
UserDetailsImpl userDetails) {
return productService.createProduct(requestDto, userDetails.getUser());
}
접근자 후에 ResponseDto Class를 바로 작성, 매개인자들을 받아주고 그대로 Service 단으로 보내줘, 거기서 수행된 로직을 가져와 반환하게 된다.
이를 ResponseEntity를 사용하면 이렇게 된다.
@PostMapping("/products")
public ResponseEntity<ProductResponseDto> createProduct(
@RequestBody ProductRequestDto requestDto,
@AuthenticationPrincipal
UserDetailsImpl userDetails) {
ProductResponseDto responseDto = productService.createProduct(requestDto,
userDetails.getUser());
return ResponseEntity.status(201).body(responseDto);
}
눈에 띄는 변화만 보자면 우선, 먼저 작성했던 ProductResponseDto 클래스가 ResponseEntity 클래스로 감싸졌?고, ⇒ 다시 생각해보니 그냥 감싸졌다가 아니라, 응답할 DTO를 넣어 준다.
Service단의 Method를 그대로 반환 했던 부분이 로직 내에서 수행 된 후, 다시 ResponseEntity를 반환하고 있다.
사실 바뀐 점 이외에는 아직 잘 모르겠으니 우선 ResponseEntity란 녀석이 누구인지 부터 알아보자.
우선 Request, Response 두 형태의 Entity로 존재하고, HttpEntity 클래스를 상속받아 구현한 클래스인데, 또한 Spring Framework에서 대표하는 HTTP응답 클래스라고 한다.
ResponseEntity는 사용자의 HttpRequest에 대한 응답 데이터를 포함하기에 HttpStatus, HttpHeaders, HttpBody를 포함한다고 한다.
그리고 ‘Spring MVC 상에서 응답할 때 사용 시, 이를 필요에 따라 응답 상태, 헤더, 그리고 바디를 꾸며줄 수 있다'고 하는데 한 번 살펴보자.
위 코드 마지막에 쓰여진 이 부분이다.
return ResponseEntity.status(201).body(responseDto);
.status() 내부에 로직대로 잘 수행이 되었을 시 띄워줄 상태번호를 지정해줄 수 있다. 번호를 직접 지정해주지 않아도 아래와 같이 다양하게 작성 해줄 수 있다.
.ok() //=상태코드 200
.badRequest() //=상태코드 400
그리고 @RequestBody로 요청을 받았다면 body에 responseDto를 담아서 반환해주게 된다.
뿐만 아니라 예외처리를 해주었다면 아래와 같이 작성 할 수도있다.
return ResponseEntity.badRequest()
.body(new CommonResponseDto(e.getMessage(), HttpStatus.BAD_REQUEST.value()));
위와는 다르게 @RequestBody로 요청을 받지 않았을 시엔 아래와 같이 반환한다.
return ResponseEntity.ok(responseData);
이 정도 까지 보면 자유롭게 Custom 할 수 있는게 큰 장점 이라고 느껴진다.
다음으로 사용하게 되는 상황과 방법을 네 가지로 들어주었는데 이는 다음과 같다.
1. Body를 사용해 성공적인 응답 반환을 할 때
javaCopy code
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/data")
public ResponseEntity<String> getData() {
String responseData = "Hello, World!";
return ResponseEntity.ok(responseData);
}
}
2. Error 응답을 반환 할 때
javaCopy code
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/error")
public ResponseEntity<String> getError() {
String errorMessage = "Resource not found";
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorMessage);
}
}
3. Custom화 한 헤더를 반환 할 때
javaCopy code
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/custom")
public ResponseEntity<String> getCustom() {
String responseData = "Custom Response";
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "CustomValue");
return ResponseEntity.ok().headers(headers).body(responseData);
}
}
4.예외처리를 핸들링 할 때 (feat-custom exception)
javaCopy code
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal Server Error");
}
}
추가적으로 지금까지는 그냥 쓰다보니 아무렇지 않게 사용해오고 있었는데, 이를 또 두 가지 패턴으로 사용할 수 있다더라.
바로 생성자 패턴과 빌더 패턴인데, 둘 중에 빌더 패턴을 사용하는 걸 권장하고 이유는 다음과 같았다.
- ResponseEntity.ok() 이는 정적 팩토리 메서드 이기 때문… 이라는데 아직 이해를 못했다.
정적 팩토리 메서드(Static Factory Method)는 개발자가 구성한 Static Method를 통해 간접적으로 생성자를 호출하는 객체를 생성하는 디자인 패턴… 역시 이해못했다. 이것도 따로 다뤄봐야겠다.
다행히도 두 번째 이유는 좀 더 직관적이라 이해가 됬는데, 바로 메서드 체이닝으로 연결한 빌더 패턴을 사용하는게 직관적이고 역시 유지보수에 좋기 때문이라고 한다.
역시 또지보수.
내가 개발공부를 시작하게 되면서 “이렇게 하면 좋다.” 라고 들은 것들의 이유들을 많이도 들어봤지만 결국 하나로 종결된다는 걸 깨닳았는데, 바로 유지보수였다.
스읍. 갑자기 왠지 모르게 짠한 마음이 들었는데, ‘조금이라도 더 일찍 퇴근하고 싶다..!’ 라는 개발자들의 마음의 소리가 들리는 것만 같았다.
그리고 나도 언젠가 똑같은 생각을 하게 될지도 모르겠다.
생각보다 글이 길어져 놀란 것에 비해, 가려운데가 아주 살짝만 긁어진 느낌이다.
나도 모르게 뭔가 엄청난 반전을 기대하고 있었나본데, 아무래도 의문점을 너무 오래 품고 있어서 가 아닐까 하는 생각이 들었다.
좀 더 자주 긁어줘서 해소를 해줘야겠다.
떡밥통
- 정적 팩토리 메서드(Static Factory Method)
- 메서드 체이닝
- 빌더 패턴
'문법 > JAVA' 카테고리의 다른 글
가막눈 쑤준 (1) | 2023.11.16 |
---|---|
저 객체가 그 객체요? ??? (0) | 2023.10.26 |
클래스와 아이들 (0) | 2023.10.23 |
상속 (0) | 2023.10.21 |
스레드와 스레드, 그리고 스레드. (1) | 2023.10.19 |