ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot ๊ณตํ†ต Global Exception Handler
    Spring 2022. 10. 23. 17:46
    ๋ฐ˜์‘ํ˜•

     

    ๐ŸŽˆ Spring์—์„œ ์ „์—ญ์ ์œผ๋กœ Exception์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํฌ์ŠคํŒ…ํ•œ๋‹ค.

     

    ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•˜๊ฒŒ ์žˆ๋‹ค.


    1. ๋ฉ”์„œ๋“œ ๋‚ด ์˜ˆ์™ธ ์ƒํ™ฉ์„ ์˜ˆ์ธกํ•˜์—ฌ try-catch๋ฌธ ์‚ฌ์šฉ
    2. ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ (validation)
    3. Intercepter์—์„œ ์„  ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
    4. HandlerExceptionResolver
    5. ExceptionHandlerExceptionResolver
    6. DefaultHandlerExceptionResolver
    7. ResponseStatusExceptionResolver

     

    ์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•๋“ค ๋ง๊ณ ๋„ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•œ๋‹ค.
    ํ•˜์ง€๋งŒ ๊ฐ๊ฐ์˜ ์—๋Ÿฌ์— ์ผ์ผ์ด ์ฒ˜๋ฆฌํ•˜๋‹ค๋ณด๋ฉด,
    ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘์„ ํ•  ์ˆ˜ ์—†๊ณ , ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

    ๊ถ๊ทน์ ์œผ๋กœ, ๊ณตํ†ต์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ณตํ†ต์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ํšจ์œจ์ ์ธ ๋ฐฉ์•ˆ์ด๋‹ค.

     

    ๊ทธ ๋ฐฉ๋ฒ•์œผ๋กœ, @Controller์™€ @ControllerAdvice, @ExceptionHandler๋ฅผ
    ์ด์šฉํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋‹จ์—์„œ ์ „์—ญ์ ์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•œ๋‹ค.


    ๐Ÿ“Œ @ExceptionHandler

    • ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ @Controller, @RestController๊ฐ€ ์ ์šฉ๋œ Bean๋‚ด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋ฅผ ์žก์•„์„œ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ํ•œ๋‹ค.
    • ์•„๋ž˜์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์— ์–ด๋…ธํ…Œ์ด์…˜๊ณผ Exception Class๋ฅผ ์„ ์–ธํ•ด์ฃผ๋ฉด ํ•ด๋‹น Exception์ด ๋ฐœ์ƒํ–ˆ์„๋•Œ ์›ํ•˜๋Š” ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    • TestController์— ํ•ด๋‹นํ•˜๋Š” Bean์—์„œ NullPointerException์ด ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, @ExceptionHandler(NullPointerException.class)๊ฐ€ ์ ์šฉ๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๊ฒƒ์ด๋‹ค.
    @RestController
    public class TestController {
        ...
        ...
        @ExceptionHandler(NullPointerException.class)
        public String nullpointers(Exception e) {
            System.err.println(e.getClass());
            return "error message";
        }
    }

    ํ•˜์ง€๋งŒ, @ExceptionHandler๋ฅผ ๋“ฑ๋กํ•œ Controller์—๋งŒ ์ ์šฉ๋œ๋‹ค. ๋‹ค๋ฅธ Controller์—์„œ NullPointerException์ด ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.


    ๐Ÿ“Œ @ControllerAdvice

    @ExceptionHandler๊ฐ€ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๊ฒƒ์ด๋ผ๋ฉด, @ControllerAdvice๋Š” ๋ชจ๋“  @Controller ์ฆ‰, ์ „์—ญ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋ฅผ ์žก์•„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” annotation์ด๋‹ค.

    ์ด ์ค‘์—์„œ๋„, @RestControllerAdvice์™€ @ControllerAdvice 2๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ๋‹ค.
    @RestControllerAdvice๋Š” @ResponseBody๊ฐ€ ์ถ”๊ฐ€ ๋˜์–ด ์žˆ๋‹ค๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.
    ๋ณธ ํฌ์ŠคํŒ…์—์„œ๋Š” @ControllerAdvice๋ฅผ ์“ฐ๋˜, ํ•„์š”ํ•œ ๋ถ€๋ถ„์—๋งŒ @ResponseBody๋ฅผ ๋ถ™์—ฌ ์ค€๋‹ค.

     

    ์ด ๊ธ€์—์„œ ๋ณด์—ฌ์ฃผ๊ณ ์ž ํ•˜๋Š”๊ฒƒ๋“ค์€ ๋Œ€๋žต ์ด๋ ‡๋‹ค.

    ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋“ค์„ ์žก์•„์„œ HttpCode 500 ๊ฐ™์€ ์—๋Ÿฌ๋ฅผ ์‘๋‹ตํ•˜์ง€ ์•Š๊ณ  200์„ ์‘๋‹ตํ•˜๋˜,
    ์„ ์–ธํ•œ ์—๋Ÿฌ Code์™€ Message๋ฅผ ๋‚ด๋ ค์ฃผ๊ณ  ํ™”๋ฉด์—์„œ ๊ทธ์— ๋งž์ถฐ์„œ ๋Œ€์‘์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๊ณตํ†ต์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” Error๋“ค์— ๋Œ€ํ•ด ์ •์˜ํ•ด๋‘” Error Code์™€ Message๋ฅผ ์‘๋‹ตํ•œ๋‹ค.

    ServiceException์„ ์ •์˜ํ•ด์„œ ๊ฐ ์„œ๋น„์Šค์—์„œ ์›ํ•˜๋Š” Error๋ฅผ ๋˜์ ธ์ค„ ์ˆ˜ ์žˆ๋‹ค.


    ๐ŸŽˆ ์‹ค์Šต ์ „์— ํ•„์š”ํ•œ Class๋“ค์„ ์ƒ์„ฑ ํ•ด๋ณธ๋‹ค.


    ๐Ÿ“Œ ResultCode.class

    public enum ResultCode {
        SUCCESS(200, ResultMessage.SUCCESS),
        UNAUTHORIZED(401, ResultMessage.UNAUTHORIZED),
        NO_AUTH(403, ResultMessage.NO_AUTH),
        INTERNAL_ERROR(500, ResultMessage.INTERNAL_ERROR),
        ACCESS_NO_AUTH(1_000, ResultMessage.ACCESS_NO_AUTH),
        ACCESS_TOKEN_EXPIRED(1_001, ResultMessage.ACCESS_TOKEN_EXPIRED),
        REFRESH_TOKEN_EXPIRED(1_002, ResultMessage.REFRESH_TOKEN_EXPIRED),
        VALID_NOT_PHONE_NUM(1_007, ResultMessage.VALID_NOT_PHONE_NUM),
        VALID_NOT_PASSWORD(1_008, ResultMessage.VALID_NOT_PASSWORD),
        MEMBER_NOT_EXIST(1_012, ResultMessage.MEMBER_NOT_EXIST),
        LOGIN_REQUIRED(1_019, ResultMessage.LOGIN_REQUIRED),
        PARAM_NOT_VALID(2_000, ResultMessage.PARAM_NOT_VALID),
        ;
    
        private final int resultCode;
        private final String resultMessage;
    
        ResultCode(int resultCode, String resultMessage) {
            this.resultCode = resultCode;
            this.resultMessage = resultMessage;
        }
    
        public int getResultCode() {
            return resultCode;
        }
    
        public String getResultMessage() {
            return resultMessage;
        }
    
        public interface ResultMessage {
            String SUCCESS = "์™„๋ฃŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
            String UNAUTHORIZED = "์ธ์ฆ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค.";
            String NO_AUTH = "์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.";
            String ACCESS_NO_AUTH = "์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.";
            String ACCESS_TOKEN_EXPIRED = "Access Token์ด ๋งŒ๋ฃŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
            String REFRESH_TOKEN_EXPIRED = "Refresh Token์ด ๋งŒ๋ฃŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
            String VALID_NOT_PHONE_NUM = "๊ฐ€์ž…๋˜์ง€ ์•Š์€ ํ•ธ๋“œํฐ ๋ฒˆํ˜ธ ์ž…๋‹ˆ๋‹ค.";
            String VALID_NOT_PASSWORD = "์ž˜๋ชป๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋‹ˆ๋‹ค.";   
            String MEMBER_NOT_EXIST = "์กด์žฌํ•˜์ง€ ์•Š๋Š” ์‚ฌ์šฉ์ž ์ž…๋‹ˆ๋‹ค.";
            String LOGIN_REQUIRED = "๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.";
            String PARAM_NOT_VALID = "ํŒŒ๋ผ๋ฏธํ„ฐ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.";
            String INTERNAL_ERROR = "์‹œ์Šคํ…œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.";
        }
    }
    
    • Error Code์™€ Message๋ฅผ ์ •์˜ํ•œ Common Enum Class๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • ํ•„์š”ํ•œ Code์™€ Message๋“ค์„ ์ถ”๊ฐ€ ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

    ๐Ÿ“Œ BaseResDto.class

    • Error Code์™€ Message๋ฅผ ๋‹ด์„ Dto Class๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • ์ด Class๋Š” Error๊ฐ€ ๋ฐœ์ƒ์‹œ Returnํ•  Common Response Class์ด๋‹ค.
    @Getter @Setter
    public class BaseResDto {
        private int resultCode = ResultCode.SUCCESS.getResultCode();
        private String resultMessage = ResultCode.SUCCESS.getResultMessage();
    }

    ๐Ÿ“Œ ServiceException.class

    @RequiredArgsConstructor
    public class ServiceException extends Exception {
    
        private final int resultCode;
        private final String resultMessage;
    
        public ServiceException(@NonNull ResultCode resultCodeEnum) {
            this.resultCode = resultCodeEnum.getResultCode();
            this.resultMessage = resultCodeEnum.getResultMessage();
        }
    
        public ServiceException(@NonNull ResultCode resultCodeEnum, @NonNull Map<String, Object> params) {
            this.resultCode = resultCodeEnum.getResultCode();
            String messageTemplate = resultCodeEnum.getResultMessage();
    
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                messageTemplate = messageTemplate.replaceAll(String.format("\\$\\{%s\\}", entry.getKey()), String.valueOf(entry.getValue()));
            }
    
            this.resultMessage = messageTemplate;
        }
    
        public int getResultCode() {
            return resultCode;
        }
    
        public String getResultMessage() {
            return resultMessage;
        }
    }
    • Exception์„ ์ƒ์† ๋ฐ›์€ ๊ณตํ†ต Exception Class์ด๋‹ค.
    • ์›ํ•˜๋Š” ์„œ๋น„์Šค๋‹จ ๋กœ์ง์—์„œ ์ด Exception์„ throw ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
    • ๋‘ ๋ฒˆ์งธ ์ƒ์„ฑ์ž๋Š” ResultCode Enum Class์—์„œ ๋ฉ”์„ธ์ง€๋ฅผ ${field}์™€ ๊ฐ™์ด ์ •์˜ํ•˜์—ฌ
      ์“ธ ์ˆ˜ ์žˆ๋‹ค.
      Ex) ${field} ํ•„๋“œ๋Š” ํ•„์ˆ˜ ๊ฐ’์ž…๋‹ˆ๋‹ค.


    ๐Ÿ“Œ GlobalExceptionHandler.class

    ๐Ÿ’ก Points

    @ControllerAdvice, @RestController๋ฅผ ์„ ์–ธํ•˜๊ณ , ExceptionHandler๋ฅผ ์ •์˜ํ•œ Class

    @ExceptionHandler: Catchํ•  Exception์„ ์„ ์–ธ

    ExceptionHandler์— ์„ ์–ธํ•œ Class๋ฅผ Parameter๋กœ ๋ฐ›๋Š”๋‹ค.

    ์ด Handler Method๋“ค์˜ Return Class๋Š” ์œ„์—์„œ ์ •์˜ํ•œ BaseResDto๋ฅผ ์‚ฌ์šฉ
    -> Common Response Class๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•จ

     

    ๐Ÿ’ก ๋™์ž‘ ์ˆœ์„œ
    ControllerAdvice๊ฐ€ ExceptionHandler์— ์„ ์–ธํ•œ Exception ๋ฐœ์ƒ์‹œ ๊ฐ Method๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    @ResponseStatus์— HttpStatus.OK๋ฅผ ์„ ์–ธํ•ด์„œ HTTP Code 200์„ ์‘๋‹ตํ•˜๊ฒŒ ํ•˜๊ณ ,
    BaseResDto์— ์ „๋‹ฌ ๋ฐ›์€ ResultCode, ResultMessage๋ฅผ Setํ•ด์„œ Returnํ•˜๋Š” ํ˜•ํƒœ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ™”๋ฉด์—์„œ๋Š” 500 Error ๊ฐ€ ์•„๋‹ˆ๋ผ 200 Code๋ฅผ ์‘๋‹ต ๋ฐ›๊ณ , ๊ฐ ResultCode์— ๋”ฐ๋ผ ResultMessage๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๋Š” ๋“ฑ, ์‚ฌ์šฉ์ž ์ •์˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    @Slf4j
    @ControllerAdvice
    @RestController
    public class GlobalExceptionHandler {
    	private static final String field = "${field}";
    
    	1๏ธโƒฃ
        @ExceptionHandler(com.package.common.exception.ServiceException.class)
    	@ResponseStatus(HttpStatus.OK)
    	public BaseResDto exception(com.package.common.exception.ServiceException e) {
    		BaseResDto baseResDto = new BaseResDto();
    		baseResDto.setResultCode(e.getResultCode());
    		baseResDto.setResultMessage(e.getResultMessage());
    
    		log.error("[{}]ServiceException: code[{}], message[{}]", ContextUtil.reqInfo.get().getUuid(), baseResDto.getResultCode(), baseResDto.getResultMessage());
    
    		return baseResDto;
    	}
    
    	2๏ธโƒฃ
    	@ExceptionHandler(BindException.class)
    	@ResponseStatus(HttpStatus.OK)
    	public BaseResDto exception(BindException e) {
    		BaseResDto baseResDto = new BaseResDto();
    		FieldError fieldError = e.getBindingResult().getFieldError();
    
    		if (fieldError == null) {
    			baseResDto.setResultCode(ResultCode.INTERNAL_ERROR.getResultCode());
    			baseResDto.setResultMessage(ResultCode.INTERNAL_ERROR.getResultMessage());
    
    			log.error("[{}]Internal Exception: {}", ContextUtil.reqInfo.get().getUuid(), ExceptionUtils.getStackTrace(e));
    
    			return baseResDto;
    		}
    
    		String code = fieldError.getCode();
    
    		if ("NotNull".equals(code) || "NotEmpty".equals(code) || "NotBlank".equals(code)) {
    			baseResDto.setResultCode(ResultCode.VALID_NOT_NULL.getResultCode());
    			baseResDto.setResultMessage(ResultCode.VALID_NOT_NULL.getResultMessage().replace(field, fieldError.getField()));
    		} else if ("Pattern".equals(code)) {
    			baseResDto.setResultCode(ResultCode.VALID_NOT_REGEXP.getResultCode());
    			baseResDto.setResultMessage(ResultCode.VALID_NOT_REGEXP.getResultMessage().replace(field, fieldError.getField()));
    		} else if ("MaxByte".equals(code)) {
    			baseResDto.setResultCode(ResultCode.PARAM_NOT_VALID.getResultCode());
    			baseResDto.setResultMessage(String.format("%s ๊ฐ’์ด %dbyte ๋ณด๋‹ค ํฝ๋‹ˆ๋‹ค.", fieldError.getRejectedValue(), fieldError.getArguments()[1]));
    		} else {
    			baseResDto.setResultCode(ResultCode.PARAM_NOT_VALID.getResultCode());
    			baseResDto.setResultMessage(ResultCode.PARAM_NOT_VALID.getResultMessage());
    		}
    
    		log.error("[{}]ValidationException: message[{}]", ContextUtil.reqInfo.get().getUuid(), e.getMessage());
    
    		return baseResDto;
    	}
    
    	3๏ธโƒฃ
    	@ExceptionHandler(MissingServletRequestParameterException.class)
    	@ResponseStatus(HttpStatus.OK)
    	public BaseResDto exception(MissingServletRequestParameterException e) {
    		BaseResDto baseResDto = new BaseResDto();
    		baseResDto.setResultCode(ResultCode.VALID_NOT_NULL.getResultCode());
    		baseResDto.setResultMessage(ResultCode.VALID_NOT_NULL.getResultMessage().replace(field, e.getParameterName()));
    
    		log.error("[{}]ValidationException: message[{}]", ContextUtil.reqInfo.get().getUuid(), ExceptionUtils.getStackTrace(e));
    
    		return baseResDto;
    	}
    
    	4๏ธโƒฃ
    	@ExceptionHandler(Exception.class)
    	@ResponseStatus(HttpStatus.OK)
    	public BaseResDto exception(Exception e) {
    		BaseResDto baseResDto = new BaseResDto();
    		baseResDto.setResultCode(ResultCode.INTERNAL_ERROR.getResultCode());
    		baseResDto.setResultMessage(ResultCode.INTERNAL_ERROR.getResultMessage());
    
    		log.error("[{}]Internal Exception: {}", ContextUtil.reqInfo.get().getUuid(), ExceptionUtils.getStackTrace(e));
    
    		return baseResDto;
    	}
    }
    
    • 1๏ธโƒฃ: ์œ„์—์„œ ์ƒ์„ฑํ•œ ServiceException์„ ํ†ตํ•ด ์ „๋‹ฌ ๋ฐ›์€ ResultCode, ResultMessage๋ฅผ BaseResDto์— set ํ•ด์ค€ ๋’ค Return. (ํ•„์š”์‹œ ์ฝ”๋“œ์™€ ๊ฐ™์ด log ์ถœ๋ ฅ)
    • 2๏ธโƒฃ: BindException์€ ๋ณดํ†ต Parameter์— ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ ๋ฐ›๊ฑฐ๋‚˜, @NotNull @NotEmpty ๊ฐ™์€ ์กฐ๊ฑด์— ๊ฑธ๋ ธ์„๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. BindingResult ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด FieldError๋ฅผ ๊ฐ€์ ธ์˜จ ๋’ค ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” ResultCode์™€ ResultMessage๋ฅผ set ํ•ด์ค€๋‹ค.
    • 3๏ธโƒฃ, 4๏ธโƒฃ๋„ ๋™์ผํ•œ ํ˜•ํƒœ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์›ํ•˜๋Š” Exception์— ์‘๋‹ตํ•˜๊ณ  ์‹ถ์€ ResultCode์™€ Message๋ฅผ Return ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    ๐Ÿ“Œ ์˜ˆ์‹œ

    @Getter
    public class UserResDto extends BaseResDto {
    	private String userId;
        private String name;
        
        public UserResDto(User user) {
        	this.userId = user.getUserId();
            this.name = user.getName();
        }
    }

     

    @GetMapping("user-info/{userId}")
    public BaseResDto getUserInfo(@PathVariable("userId") String userId) {
            return userService.getUserInfo(userId);
    }

     

    public BaseResDto getUserInfo(String userId) {
         User user = userRepository.findById(userId)
           .orElseThrow(() -> new ServiceException(ResultCode.USER_NOT_EXIST));
         
         return new UserResDto(user);
    }
    • BaseResDto๋ฅผ ์ƒ์† ๋ฐ›์€ User์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Dto Class๋ฅผ ์ƒ์„ฑ ํ•œ๋‹ค.
    • ํ•ด๋‹น ์œ ์ €์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” API๋ฅผ ํ˜ธ์ถœ ํ–ˆ์„ ๋•Œ, UserService์˜ getUserInfoํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. JPA๋กœ User Entity๋ฅผ ์กฐํšŒํ•˜๋Š” ๋™์‹œ์— orElseThrow๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์œ ์ € ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„๋•Œ ServiceException์— ๋ฏธ๋ฆฌ ์ •์˜ ํ•ด๋†“์€ ResultCode๋ฅผ ๋‹ด์•„์„œ throwํ•œ๋‹ค.
    • ๊ทธ๋Ÿฌ๋ฉด ControllerAdvice Annotation์„ ์„ ์–ธํ•ด๋†“์€ GlobalExceptionHandler๊ฐ€
      @ExceptionHandler(ServiceException.class)๊ฐ€ ์„ ์–ธ ๋˜์–ด ์žˆ๋Š” Method๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    • ์ „๋‹ฌ ๋ฐ›์€ ResultCode.USER_NOT_EXIST์˜ ResultCode, ResultMessage๋ฅผ BaseResDto์— setํ•ด์„œ Returnํ•œ๋‹ค.

    ๐Ÿ“Œ ์„œ๋น„์Šค ํ•จ์ˆ˜์˜ Return Type์ด BaseResDto์ธ ์ด์œ ๋Š” UserResDto๊ฐ€ ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ  ์ƒ์†(Is a) ๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฐ์‹์œผ๋กœ Response Dto์— BaseResDto๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ๊ฐ API๋งˆ๋‹ค Return Class๋ฅผ BaseResDto๋กœ ์„ ์–ธ ํ•˜๋ฉด ๋œ๋‹ค.

    ๋ฐ˜์‘ํ˜•

    ๋Œ“๊ธ€

Designed by Tistory.