본문 바로가기

Backend/Spring | SpringBoot

[Spring] MessageSource를 이용한 Exception처리

반응형

보통 MessageSource는 국제화(i18n)를 목적으로 사용한다.

하지만 이를 이용하면 예외처리가 좀 더 간편해진다.

 

build.gradle에 의존성 추가

implementation 'net.rakugakibox.util:yaml-resource-bundle:1.1'

 

MessageConfiguration 파일 생성

import net.rakugakibox.util.YamlResourceBundle;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

@Configuration
public class MessageConfiguration implements WebMvcConfigurer {

    @Bean // 세션에 지역설정. default는 KOREAN = 'ko'
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.KOREAN);
        return slr;
    }

    @Bean // 지역설정을 변경하는 인터셉터. 요청시 파라미터에 lang 정보를 지정하면 언어가 변경됨.
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }

    @Override // 인터셉터를 시스템 레지스트리에 등록
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }

    @Bean // yml 파일을 참조하는 MessageSource 선언
    public MessageSource messageSource(
            @Value("${spring.messages.basename}") String basename,
            @Value("${spring.messages.encoding}") String encoding
    ) {
        YamlMessageSource ms = new YamlMessageSource();
        ms.setBasename(basename);
        ms.setDefaultEncoding(encoding);
        ms.setAlwaysUseMessageFormat(true);
        ms.setUseCodeAsDefaultMessage(true);
        ms.setFallbackToSystemLocale(true);
        return ms;
    }

    // locale 정보에 따라 다른 yml 파일을 읽도록 처리
    private static class YamlMessageSource extends ResourceBundleMessageSource {
        @Override
        protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
            return ResourceBundle.getBundle(basename, locale, YamlResourceBundle.Control.INSTANCE);
        }
    }
}

 

application.yml에 i18n경로 및 인코딩 정보 추가

spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/test
    driver-class-name: org.h2.Driver
    username: sa
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    properties.hibernate.hbm2ddl.auto: update
    showSql: true
  messages:
    basename: i18n/exception
    encoding: UTF-8

 

다국어 처리 message yml파일 작성

path : resources\i18n\exception_en.yml          resources\i18n\exception_ko.yml

메시지 예시

# exception_en.yml
unKnown:
  code: "-9999"
  msg: "An unknown error has occurred."
userNotFound:
  code: "-1000"
  msg: "This member not exist"
  
# exception_ko.yml
unKnown:
  code: "-9999"
  msg: "알수 없는 오류가 발생하였습니다."
userNotFound:
  code: "-1000"
  msg: "존재하지 않는 회원입니다."

 

ResponseService의 getFailResult메소드에서 code, msg 사용

public CommonResult getFailResult(int code, String msg) {
    CommonResult result = new CommonResult();
    result.setSuccess(false);
    result.setCode(code);
    result.setMsg(msg);
    return result;
}

 

ExceptionAdvice의 에러 메시지를 messageSource로 변경

@RequiredArgsConstructor
@RestControllerAdvice
public class ExceptionAdvice {

    private final ResponseService responseService;

    private final MessageSource messageSource;

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResult defaultException(HttpServletRequest request, Exception e) {
        // 예외 처리의 메시지를 MessageSource에서 가져오도록 수정
        return responseService.getFailResult(Integer.valueOf(getMessage("unKnown.code")), getMessage("unKnown.msg"));
    }

    @ExceptionHandler(CUserNotFoundException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResult userNotFoundException(HttpServletRequest request, CUserNotFoundException e) {
        // 예외 처리의 메시지를 MessageSource에서 가져오도록 수정
        return responseService.getFailResult(Integer.valueOf(getMessage("userNotFound.code")), getMessage("userNotFound.msg"));
    }

    // code정보에 해당하는 메시지 조회
    private String getMessage(String code) {
        return getMessage(code, null);
    }
    // code정보, 추가 argument로 현재 locale에 맞는 메시지 조회
    private String getMessage(String code, Object[] args) {
        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
    }
}

 

+ ) 바로 띄울 때

<!-- ... -->
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!-- ... -->
<spring:message code="unKnown.msg" />

 

 

++ ) yml파일에 상위 level추가

test:
  unKnown:
    code: "-9999"
    msg: "알수 없는 오류가 발생하였습니다."

 

호출

<spring:message code="test.unKnown.msg" />

 

반응형

'Backend > Spring | SpringBoot' 카테고리의 다른 글

[Spring Security] Spring Security 적용  (0) 2024.03.15
[Spring] @RequestBody  (0) 2024.03.14
[Spring] redirect경로  (0) 2024.03.13
[Spring Security] CSRF 토큰  (0) 2024.03.13
[JPA] Auto Increase가 안될 때  (0) 2024.03.11