API๋ฅผ ์์ฑํ ๋ ๋ฐ๋ก Exception Handler๋ฅผ ๋ง๋ค์ด์ฃผ์ง ์์ผ๋ฉด,
Runtime Error์ ๋ชจ๋ 500 Error๋ก Response๊ฐ ๊ฐ๊ฒ ๋๋ค.
FE๋ ์ด ์๋ฌ ์ฝ๋์์ ์๋ฌ์ ์์ธ์ ์ ์ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Custome Exception์ ์ถ๊ฐํ์ฌ FE์๊ฒ ๋ด์ฉ์ ์ ๋ฌํ๋๋ก ํด๋ณด์!
ErrorCode ๋ง๋ค๊ธฐ
๐ก ๋ค์ํ ์ํฉ์์ ์ฐ์ผ Error Code๋ฅผ ๋ง๋ ๋ค.
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@AllArgsConstructor
@Getter
public enum ErrorCode {
/* 400 BAD_REQUEST : ์๋ชป๋ ์์ฒญ */
/* 401 UNAUTHORIZED : ์ธ์ฆ๋์ง ์์ ์ฌ์ฉ์ */
INVALID_AUTH_TOKEN(HttpStatus.UNAUTHORIZED, "๊ถํ ์ ๋ณด๊ฐ ์๋ ํ ํฐ์
๋๋ค."),
/* 404 NOT_FOUND : Resource๋ฅผ ์ฐพ์ ์ ์์ */
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "ํด๋นํ๋ ์ ๋ณด์ ์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."),
/* 409 : CONFLICT : Resource์ ํ์ฌ ์ํ์ ์ถฉ๋. ๋ณดํต ์ค๋ณต๋ ๋ฐ์ดํฐ ์กด์ฌ */
DUPLICATE_RESOURCE(HttpStatus.CONFLICT, "๋ฐ์ดํฐ๊ฐ ์ด๋ฏธ ์กด์ฌํฉ๋๋ค."),
;
private final HttpStatus httpStatus;
private final String message;
}
- ์ํ๋ฅผ ๋ด์ HttpStatus์ ๋ฉ์ธ์ง๋ฅผ ๋ด์ String ์์ฑ์ ์ถ๊ฐ
- ์ด์ ๋ง๋ ๋ค์ํ Enum ํ์
์ Error Code๋ค์ ๋ง๋ค์ด์ฃผ์๋ค.
์ด๋, HttpStatus๋ฅผ ๋ฐ๋ฅด์ง ์๊ณ
USER_NOT_FOUND(901, "์ ์ ์์")
์ ๊ฐ์ด customํด์ ๋ง๋ค์ด๋ ๋๋ค.
๋ค๋ง ์ด๋ด ๊ฒฝ์ฐ์๋ HttpStatusํ์ ์ด ์๋ Integerํ์ ์ผ๋ก ๋ฐ๊ฟ์ผ ํ๋ค!
CustomException ์ถ๊ฐ
๐ก ErrorCode๋ฅผ ๋ด์ class๋ฅผ ๋ง๋ค์.
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class CustomException extends RuntimeException{
ErrorCode errorCode;
}
- RuntimeException์ ์์
- ErrorCode๋ง ์์ฑ์ผ๋ก ์ถ๊ฐ > ์ํ์ ๋ฉ์ธ์ง ์ ๋ณด ๋ชจ๋ ๋ด๊ฒจ์์ผ๋ฏ๋ก!
Custom Exception Handler ์ถ๊ฐ
๐ก ์ปจํธ๋กค๋ฌ ์ ์ญ์์ ๋ฐ์ํ๋ Custom์๋ฌ๋ฅผ ์ก์์ค Handler๋ฅผ ๋ง๋ค์
import com.ovcors.godlife.api.exception.CustomException;
import com.ovcors.godlife.api.exception.ErrorResponseEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(CustomException.class)
protected ResponseEntity<ErrorResponseEntity> handleCustomException(CustomException e) {
return ErrorResponseEntity.toResponseEntity(e.getErrorCode());
}
}
@ExceptionHandler(CustomException.class)
โ ๋ฐ์ํ CustomException ์์ธ๋ฅผ ์ก์์ ํ๋์ ๋ฉ์๋์์ ๊ณตํต ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค๋ค.
- Controller์๋ง ๋ฑ๋ก์ด ๊ฐ๋ฅํ๋ค. (Service์์ญ์ ๋!)
- ๋ฑ๋กํ Controller ์์ญ ์์์๋ง ์๋ํ๋ค.
@ExceptionHandler({ Exception1.class, Exception2.class})
์ด๋ฐ์์ผ๋ก ๋ ๊ฐ ์ด์ ๋ฑ๋ก๋ ๊ฐ๋ฅํ๋ค.
@ControllerAdvice + @ExceptionHandler
โ ๋ชจ๋ ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ํ๋ CustomException์ ์บ์นํ๋ค.
ErrorResponseEntity
โ Error๋ด์ฉ์ ๋ด์ Response Entity๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค.
@Data
@Builder
public class ErrorResponseEntity {
private int status;
private String code;
private String message;
public static ResponseEntity<ErrorResponseEntity> toResponseEntity(ErrorCode e){
return ResponseEntity
.status(e.getHttpStatus())
.body(ErrorResponseEntity.builder()
.status(e.getHttpStatus().value())
.code(e.name())
.message(e.getMessage())
.build()
);
}
}
- CustomException์ toResponseEntity๋ก ๋ณด๋ด๋ฉด, ErrorCode e์์ ๋ด์ฉ์ ๊ฐ์ง๊ณ Response๋ฅผ ๋ง๋ ๋ค.
์ฌ์ฉ
userRepository.findById({id}).orElseThrow(
()->new CustomeException(USER_NOT_FOUND)
);
๐ก userRepository์์ id์ ๋ง๋ ์ ์ ๊ฐ ์์ ๊ฒฝ์ฐ Exception์ด ๋ฐ์ํ์ฌ Response๋ก ์ ๋ฌ ๋ ๊ฒ์ด๋ค
์ด๋ฌํ ์ฒ๋ฆฌ๋ฅผ ํตํด, FE๋ ์๋ฌ์ ๋ํ status, code, message๋ฅผ ๋ณด๊ณ ์์ธ์ ์ ํํ ํ์ ํ ์ ์๋ค.
์ฐธ๊ณ ๋ธ๋ก๊ทธ
๐ https://bcp0109.tistory.com/303
๐ https://jeong-pro.tistory.com/195
'๐ป ๊ฐ๋ฐ ์ผ์ง > SpringBoot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SpringBoot] CustomJsonDeserializer ๊ตฌํํ๊ธฐ (String to List<Object>) (0) | 2023.01.12 |
---|---|
[SpringBoot] Kafka ์ฐ๋(docker) ๋ฐ Consumer ๊ตฌํํ๊ธฐ (0) | 2023.01.11 |
SpringBoot - Slack Bot ์ฐ๋ํ๊ธฐ (0) | 2022.04.05 |
[Error] org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected. (0) | 2022.03.27 |
[Test] SpringBoot Test - Service (Mockito) (0) | 2022.03.27 |