[๊ตฌํ] Spring์์ ์๋ฌ๋ฅผ ๋ด๋ 3๊ฐ์ง ๋ฐฉ๋ฒ
ํ๋ก์ ํธ์์ ์๋ฌ๋ฅผ ๋ผ ๋ ๋ด๊ฐ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ด ์ธ ๊ฐ์ง์ด๋ค.
- ResponseEntity ์ฌ์ฉ
- Custom ์์ธ ํด๋์ค ๋ง๋ค๊ธฐ
- ControllerAdvice ์ฌ์ฉํ๊ธฐ
๊ฐ์ธ์ ์ผ๋ก ์์ฃผ ๊ฐ๋จํ ํ๋ก์ ํธ๊ฐ ์๋๋ผ๋ฉด 3๋ฒ์ ์ถ์ฒํ๋ค.
ํ๋์ฉ ๋ด๋ณด์๋ฉด,
1. ResponseEntity ์ฌ์ฉ
์ค๋ช ํ ๊ฒ ์์ ์ ๋๋ก ๋งค์ฐ ๊ฐ๋จํ ๊ฒ์ด ์ฅ์ ์ด๋ค.
๋จ์ ์ ์ฝ๋๊ฐ ์ง์ ๋ถํด์ง๋ค๋ ์ ์ด๋ค.
new ResponseEntity<>({๋ณด๋ผ ๋ฐ์ดํฐ}, HttpStatus.{status});
์ด ํ ์ค๋ง ๋ฆฌํดํ๋ฉด ๋๋ค.
ํ์ง๋ง ์์ธ์ฒ๋ฆฌํ ๊ฒ ์ฌ๋ฌ๊ฐ๊ฐ ๋๋ฉด ๋๋ฌด ๋ณต์กํด์ง๋ค.
@PostMapping("/users/login")
public ResponseEntity<HashMap> socialLogin(@RequestBody UserDto.LoginDto loginDto) {
HashMap<String, Object> responseMap = new HashMap<>();
User user = userService.signIn(loginDto.getSocialType(), loginDto.getSocialToken());
if (loginDto.getSocialType().equals("kakao")) {
if (user == null) {
responseMap.put("status", 404);
responseMap.put("message", "ํ์ ์ ๋ณด ์์");
return new ResponseEntity<>(responseMap, HttpStatus.NOT_FOUND);
}
responseMap.put("status", 200);
responseMap.put("message", "๋ก๊ทธ์ธ ์ฑ๊ณต");
responseMap.put("data", new HashMap<String, String>() {{
put("token", jwtTokenProvider.createToken(user.getSocialId(), user.getRoles()));
}});
return new ResponseEntity<>(responseMap, HttpStatus.OK);
} else {
responseMap.put("status", 401);
responseMap.put("message", "์์
ํ์
์ค๋ฅ");
return new ResponseEntity<>(responseMap, HttpStatus.NOT_FOUND);
}
responseMap.put("status", 404);
responseMap.put("message", "ํ์ ์ ๋ณด ์์");
return new ResponseEntity<>(responseMap, HttpStatus.NOT_FOUND);
}
์ ๊ฒฝ์ฐ ์๋ต์ ์๋ ๋ชจ์์ผ๋ก ๋๊ฐ๋ค.
{
"message": "ํ์ ์ ๋ณด ์์",
"status": 404
}
ResponseEntity๋ฅผ ์ ๋ถ Service ๋จ๊ณ์์ return ํ๊ณ , ๊ทธ๋ฅผ ๊ทธ๋๋ก Controller์์ ๋ฐํํ๋ ๋ฐฉ๋ฒ๋ ์๊ธด ํ์ง๋ง,
์ฝ๋ ๊ฐ๋ ์ฑ์ด ๋ณ๋ก์ผ ๊ฒ ๊ฐ๋ค.
๊ฐ๋จํ๊ฒ ์๋ฌ๋ฅผ ๋ด์ผํ ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค.
2. Custom ์์ธ ํด๋์ค ๋ง๋ค๊ธฐ
์ญ์ ๋งค์ฐ ๊ฐ๋จํ๋ค.
ํ์ง๋ง ๋ชจ๋ ์๋ฌ์ ๋ํด ์ ๋ถ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค๋ ๋จ์ ์ด ์๋ค.
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "ํด๋น id์ ํด๋นํ๋ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.")
public class NotFoundIdException extends RuntimeException{ }
์ด๋ฐ ํด๋์ค๋ฅผ ํ๋ ๋ง๋ค์ด์ ํ์ํ ๋ Throw ํด์ฃผ๋ฉด ๋๋ค.
{
"timestamp": "2023-04-02T13:13:11.287+00:00",
"status": 400,
"error": "Bad Request",
"trace": "{trace}",
"message": "์๋ชป๋ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๊ฐ ์
๋๋ค.",
"path": "/api/dresses/search"
}
์ด๋ฐ ํ์์ ๋ฐ์ดํฐ๊ฐ ์ ์ก๋๋ค.
3. ControllerAdvice ์ฌ์ฉํ๊ธฐ
๊ฐ์ฅ ์ถ์ฒํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๊น๋ํ๊ณ , ๋ง์ ์๋ฌ๋ฅผ ๋ง์ ํด๋์ค๋ฅผ ๋ง๋ค์ง ์๊ณ ๋ ํด๊ฒฐํ ์ ์๋ค.
@RequiredArgsConstructor
@Getter
public enum ErrorResponseStatus {
BAD_REQUEST_WRONG_TAG_EXCEPTION(HttpStatus.BAD_REQUEST, "์๋ชป๋ ํ๊ทธ"),
UNAUTHORIZED_WRONG_SOCIAL_TYPE(HttpStatus.UNAUTHORIZED, "์๋ชป๋ ์์
ํ์
"),
UNAUTHORIZED_INVALID_SOCIAL_TOKEN(HttpStatus.UNAUTHORIZED, "์๋ชป๋ ์์
ํ ํฐ"),
NOT_FOUND_USER_EXCEPTION(HttpStatus.NOT_FOUND, "ํ์ ์ ๋ณด ์์"),
NOT_FOUND_PASSWORD_EXCEPTION(HttpStatus.NOT_FOUND, "๋น๋ฐ๋ฒํธ ๋ถ์ผ์น"),
CONFLICT_DUPLICATED_NICKNAME(HttpStatus.CONFLICT, "์ค๋ณต๋ ๋๋ค์");
private final HttpStatus code;
private final String message;
}
์ด๋ฐ enum ํ์ผ์ ํ๋ ์์ฑํ๊ณ ,
์ด enum ํ์ผ๋ก ์์ฑ๋ ์ ์๋ Exception ํ์ผ์ ์์ฑํ๋ค.
@Getter
@AllArgsConstructor
public class CustomException extends RuntimeException {
private final ErrorResponseStatus errorResponseStatus;
}
๋ค์ ์ Exception ํ์ผ์ handleํ๋ ExceptionHandler๋ฅผ ๋ง๋ค์ด์ค๋ค.
@RestControllerAdvice ๋๋ @ControllerAdvice๋ฅผ ๋ถ์ฌ์ผ Spring์ด ์ธ์ํด์ ์๋ฌ๋ฅผ ๋ฐ์์์ผ์ค๋ค.
@RestControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler{
@org.springframework.web.bind.annotation.ExceptionHandler(value = CustomException.class)
protected ResponseEntity<ResponseTemplate> handleCustomException(CustomException e) {
return ResponseTemplate.toResponseEntity(e.getResponseCode());
}
}
ResponseTemplete์ toResponseEntity ๋ฉ์๋๋ฅผ ๋ฆฌํดํ๋ฉด์ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ฐ,
toResponseEntity์์๋ ErrorResponseStatus์ status์ ๋ด์ฉ์ ๋ฐ์์ ResponseEntity์ ์ง์ด ๋ฃ์ด์ ์ 1๋ฒ๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์๋ฌ๋ฅผ ๋ฐ์์ํค๊ณ ์๋ค.
@Builder
@AllArgsConstructor
public class ResponseTemplate {
public int status;
public String message;
private final LocalDateTime timestamp = LocalDateTime.now();
public static ResponseEntity<ResponseTemplate> toResponseEntity(ErrorResponseStatus errorResponseStatus) {
return ResponseEntity
.status(errorResponseStatus.getHttpStatus())
.body(ResponseTemplate.builder()
.status(errorResponseStatus.getHttpStatus().value())
.message(errorResponseStatus.getMessage())
.build()
);
}
}
์ 4๊ฐ์ ํด๋์ค๋ง ๋ง๋ค์ด์ฃผ๋ฉด ์ ๋ง์ ์๋ฌ๋ฅผ ์ฝ๋๋ฅผ ์ฌํ์ฉํ์ฌ ๋ฐ์์ํฌ ์ ์์ด ๊ฐ์ฅ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
1, 2๋ฒ๋ณด๋ค๋ ์กฐ๊ธ ๋ณต์กํ๊ฒ ๋๊ปด์ง ์ ์๋ค๋ ๋จ์ ์ ์์ผ๋ ์ํฉ์ ๋ฐ๋ผ ํ์ํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.