인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

dktmvktm님의 프로필 이미지
dktmvktm

작성한 질문수

자바 스프링부트 활용 웹개발 실무용

E05 ControllerAdvice 사용과 예외처리 다국어 활용

강사님 ControllerAdvice 사용과 예외처리 다국어 활용 이 강의는 좀 어렵게 느껴집니다.

작성

·

346

1

노트 정리를 하면서 강의르 수강중인데  메세지 응답  설정 부분의 흐름이 잘 이해가 안가는데요 코드의 의미나 실행흐름이 이해가 안가는 부분들이 많은데 한줄 한줄 혹은 실행 흐름을 파악하려면 어떻게 해야 되나요?  이부분 강의가 체감 난이도가 제일 높은것 같습니다.

http://www.skilnote-for-java.co.kr/wm/myshortcut/spring-mybatis-vue/20

답변 1

0

송자바코딩님의 프로필 이미지
송자바코딩
지식공유자

안녕하세요. dktmvktm 님

해당 강의는 Spring에서 제공하는 여러가지 기능등을 조합하여서 커스텀으로 예외관련 메세지를 프로퍼티로 관리해서 사용하는 강의 입니다.

 

먼저 다국어 프로퍼티를 사용하기 위한 

	@Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
source.setBasename("classpath:/messages/message");
source.setDefaultEncoding("UTF-8");
source.setCacheSeconds(60);
source.setDefaultLocale(Locale.KOREAN);
source.setUseCodeAsDefaultMessage(true);
return source;
}

위 메세지소스를 빈으로 등록합니다.

 

그다음 예외처리 클래스를 자바에서 기본으로 제공하는 예외 클래스를 사용해도 되지만,

커스텀으로 만들어서 사용이 가능하다는 거를 보여주기 위해

 

package kr.co.songjava.configuration.exception;
import kr.co.songjava.configuration.http.BaseResponseCode;
public abstract class AbstractBaseException extends RuntimeException {

private static final long serialVersionUID = 8342235231880246631L;

protected BaseResponseCode responseCode;
protected Object[] args;

public AbstractBaseException() {
}

public AbstractBaseException(BaseResponseCode responseCode) {
this.responseCode = responseCode;
}

public BaseResponseCode getResponseCode() {
return responseCode;
}

public Object[] getArgs() {
return args;
}

}

추상 클래스를 하나 만듭니다.

 

추상 클래스를 만든 이유는 다른 클래스를 만들 때 해당 클래스를 상속받아서 구현하면

메세지 포맷(json 데이터) 을 동일하게 또는 확장성포함  사용이 가능하기에 만들었습니다.

 

package kr.co.songjava.configuration.exception;
import kr.co.songjava.configuration.http.BaseResponseCode;

public class BaseException extends AbstractBaseException {

private static final long serialVersionUID = 8342235231880246631L;

public BaseException() {
}

public BaseException(BaseResponseCode responseCode) {
this.responseCode = responseCode;
}
public BaseException(BaseResponseCode responseCode, String[] args) {
this.responseCode = responseCode;
this.args = args;
}

}

 

BaseException은 AbstractBaseException을 상속받아 구현하였고,

프로젝트에서 기본예외클래스를 사용하기 위한 클래스 입니다.

이외에 필요하면 다른이름으로 만들어서 사용해서 됩니다.

 

package kr.co.songjava.configuration.web.bind.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import kr.co.songjava.configuration.exception.BaseException;
import kr.co.songjava.configuration.http.BaseResponse;
@ControllerAdvice
public class BaseControllerAdvice {
@Autowired
private MessageSource messageSource;
@ExceptionHandler(value = { BaseException.class })
@ResponseStatus(HttpStatus.OK)
@ResponseBody
private BaseResponse<?> handleBaseException(BaseException e, WebRequest request) {
      return new BaseResponse<String>(e.getResponseCode(), messageSource.getMessage(e.getResponseCode().name(), e.getArgs(), null));
}

}

이제 spring 에서 제공하는 컨트롤러어드바이스를 활용하여 예외가 발생하는 케이스에 대하여 정의합니다.

 

ExceptionHanlder에는 BaseException을 지정하였죠?

 

컨트롤러 / 서비스 / 비즈니스 로직에서 예외가 발생할 때 BaseException 예외가 발생하면 해당 메소드(handleBaseException) 가 실행됩니다.

 

그리고 바로 응답값으로 BaseResponse클래스에 응답코드와 예외에서 발생한 에러코드를 메세지소스에 넣으면 프로퍼티에

정의한 메세지 내용이 반황되는값을 포함하여 리턴합니다.

 

 

package kr.co.songjava.configuration.http;
import lombok.Data;

@Data
public class BaseResponse<T> {
private BaseResponseCode code;
private String message;
private T data;

public BaseResponse(T data) {
this.code = BaseResponseCode.SUCCESS;
this.data = data;
}

public BaseResponse(BaseResponseCode code, String message) {
this.code = code;
this.message = message;
}

}

 

 

해당 클래스는 Ajax / APi 통신시 공통으로 사용할 클래스로 만든겁니다.

성공일경우는 code와 data만 사용하겠지만

예외가 발생할경우는 code와 message만 필요하겠지요?

보통 프로젝트를 개발하면 대부분 이정도 포맷만 만들어놔도 거이 다 사용이 가능합니다.

다른 케이스들은 생성자를 추가하거나 아니면 set을 통하여 사용해도 무방합니다.

 

 

 

 

package kr.co.songjava.configuration.http;


public enum BaseResponseCode {
SUCCESS, // 성공
ERROR, // 에러
DATA_IS_NULL,  // NULL
VALIDATE_REQUIRED, // 필수 체크
;
}

 

해당 ennum은 성공 / 실패 등에 대하여 에러 메세지 코드를 Enum으로 관리해서 사용하기 위함입니다.

하드코딩보다는 한곳에서 관리하는게 좋습니다.

이 enum에 정의한 코드들은 메세지 프로퍼티와 1:1 로 사용해야 됩니다.

 

SUCCESS = \uc815\uc0c1\uc801\uc73c\ub85c \ucc98\ub9ac\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
ERROR = \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.
DATA_IS_NULL = \uc694\uccad\ud558\uc2e0 {0} \ub370\uc774\ud130\ub294 Null \uc785\ub2c8\ub2e4.
VALIDATE_REQUIRED = {0}({1}) \ud544\ub4dc\ub294 \ud544\uc218\ub85c \uc785\ub825\ud558\uc154\uc57c \ud569\ub2c8\ub2e4.

 

위 프로퍼티는 enum에서 사용하는 코드의 메세지를 정의.

 

 

	@PutMapping
@ApiOperation(value = "등록 / 수정 처리", notes = "신규 게시물 저장 및 기존 게시물 업데이트가 가능합니다.")
@ApiImplicitParams({
@ApiImplicitParam(name = "boardSeq", value = "게시물 번호", example = "1"),
@ApiImplicitParam(name = "title", value = "제목", example = "spring"),
@ApiImplicitParam(name = "contents", value = "내용", example = "spring 강좌"),
})
public BaseResponse<Integer> save(BoardParameter parameter) {

// 제목 필수 체크
if (StringUtils.isEmpty(parameter.getTitle())) {
throw new BaseException(BaseResponseCode.VALIDATE_REQUIRED, new String[] { "title", "제목" });
}
// 내용 필수 체크
if (StringUtils.isEmpty(parameter.getContents())) {
throw new BaseException(BaseResponseCode.VALIDATE_REQUIRED, new String[] { "contents", "내용" });
}


boardService.save(parameter);
return new BaseResponse<Integer>(parameter.getBoardSeq());
}

해당 메소드의 기능은 등록수정 요청이 왔을때

 

간단하게 if를 사용하여 조건에 안맞으면 BaseException 예외를 발생시킵니다.

 

그럼 초반에 세팅했던 컨트롤러어드바이스에 설정한 메소드에 정의한 예외 클래스와 일치하므로 클라이언트에 응답값을 원하는 포맷으로 만들어서 리턴이 가능합니다.

 

해당 영상은 spring에서 제공하는 기능을 커스텀으로 사용하는 방법입니다.

 

요즘에는 spring-validation이 있어서 위에 처럼 조건문을 사용안해도 form 검증을 쉽게 검증이 가능합니다.

그러나 모든 요청이 form값이 아니고 간단한 1개 2개 3개 만 받는경우도 있어서

이런 방법도 있다라는거를  사용법을 알려드린거에요.

 

위에 설명이 이해에 도움이되시길 바랍니다~^^

 

 

 

 

 

 

dktmvktm님의 프로필 이미지
dktmvktm

작성한 질문수

질문하기