SpringBoot를 활용한 Rest

이 게시물에서는 Spring에서 메시지가 처리되는 방법과 MessageSource를 사용하여 예외 메시지의 우선 순위를 지정하는 방법을 살펴보겠습니다.

Spring은 여러 언어를 처리하기 위해 i18n 설정을 지원합니다.

i18n은 국제화의 약자입니다(I + 중간에 남은 문자 수 + n).

이 설정을 사용하면 한국어의 “Hello”를 영어의 “Hello”로 표시할 수 있습니다. 이 방법을 사용하여 예외적인 경우 메시지 처리 방법을 변경합니다.


yml에 메시지 속성을 쓰기 위한 라이브러리 추가

기본 Spring 구성에서 실제 다국어 메시지를 저장하는 파일은 “message_ko.properties”, “message_en.properties” 형식으로 .properties 파일에 저장됩니다. 그러나 여기서는 구성 파일인 application.yml과 마찬가지로 yml의 이점을 사용하여 메시지 파일을 저장합니다.

이렇게 하려면 다음 라이브러리를 추가해야 합니다.

akkinoc/yaml-resource-bundle: YAML 형식용 Java ResourceBundle. (github.com)

GitHub – akkinoc/yaml-resource-bundle: YAML 형식용 Java ResourceBundle.

YAML 형식용 Java ResourceBundle. GitHub에서 계정을 생성하여 akkinoc/yaml-resource-bundle 개발에 기여하세요.

github.com

build.gradle 파일의 종속성에 다음을 추가합니다.

// message properties를 yml로 작성하기 위한 라이브러리 추가
implementation("net.rakugakibox.util:yaml-resource-bundle:1.1")

MessageConfiguration 파일 생성

org.ziaho.ziahorestapi.config에서 MessageConfiguration을 만듭니다.

Spring에서 제공하는 LocaleChangeInterceptor를 사용하는 lang이라는 요청 파라미터가 있으면 해당 값을 읽어 locale 정보를 변경한다.

아래에서 SessionLocaleResolver는 기본적으로 Session에서 로케일 정보를 읽고 저장하는데 사용되지만 아래와 같은 다른 리졸버도 있으므로 상황에 따라 적절한 리졸버를 설정하여 사용할 수 있다.

org.springframework.web.servlet.i18n(스프링 프레임워크 6.0.6 API)

org.springframework.web.servlet.i18n(스프링 프레임워크 6.0.6 API)

애플리케이션 컨텍스트 인프라와 Spring Web MVC 프레임워크의 핵심 인터페이스 및 클래스를 통합하는 서블릿을 제공합니다.

docs.spring.io

  • AbstractLocaleContextResolver
  • AbstractLocaleResolver
  • AcceptHeaderLocaleResover
  • CookieLocaleResolver
  • FixedLocaleResolver
  • SessionLocaleResolver
package org.ziaho.ziahorestapi.config;

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 파일을 읽도록 처리 (ko, en)
    private static class YamlMessageSource extends ResourceBundleMessageSource {
        @Override
        protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
            return ResourceBundle.getBundle(basename, locale, YamlResourceBundle.Control.INSTANCE);
        }
    }
}

위와 같이 소스를 작성하면 YamlMessageSource를 찾을 수 없다는 오류가 발생한다.

구현(“net.rakugakibox.util:yaml-resource-bundle:1.1″)

이(가) 사용 중이지만 라이브러리가 업데이트되어 사용할 수 없는 것 같습니다.

따라서 우리는 springframework 기반 속성을 사용하는 연습을 할 것입니다.

오류 해결 소스

Springboot로 Rest API 빌드(7) MessageSource(tistory.com)로 예외 처리

Springboot로 REST API 빌드(7) MessageSource로 예외 처리

시작하기 전에 변경 사항이 있습니다! (2022-07-18 개정) https://github.com/akkinoc/yaml-resource-bundle/issues/103 버그: ‘dev.akkinoc.util.YamlResourceBundle’에 대한 라이브러리를 찾을 수 없음 Issue #103 akkinoc/yaml-resource – 번들 버그 설명 Gradle G 사용

pepega.tistory.com


변경된 MessageConfiguration 파일

package org.ziaho.ziahorestapi.config;

import java.util.Locale;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

@Configuration
public class MessageConfiguration {

    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
        sessionLocaleResolver.setDefaultLocale(Locale.KOREA);
        return sessionLocaleResolver;
    }
}

application.yml i18n 경로 추가

spring:
  messages:
    basename: i18n/exception

다국어 처리 메시지 속성 파일 생성

리소스 아래에 i18n 디렉터리를 생성하고 exception_en.properties 및 exception.properties를 생성한 후 다음을 입력합니다.

모든 메시지 값은 문자열로 정의되어야 합니다.


# exception_en.properties

unKnown.code = -9999
unKnown.message = An unknown error has occurred.

userNotFound.code = -1000
userNotFound.message = This member not exist.
# exception.properties

unKnown.code = -9999
unKnown.message = 알 수 없는 오류가 발생하였습니다.

userNotFound.code = -1000
userNotFound.message = 존재하지 않는 회원입니다.

코드 및 메시지를 수신하도록 ResponseService의 getFailResult 메소드 수정

// 실패 결과만 처리하는 메소드
public CommonResult getFailResult(int code, String message) {
    CommonResult result = new CommonResult();
    result.setSuccess(false);
    result.setCode(code);
    result.setMsg(message);
    return result;
}

ExceptionAdvice 오류 메시지를 messageSource의 내용으로 바꿉니다.

package org.ziaho.ziahorestapi.advice;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.ziaho.ziahorestapi.advice.exception.CUserNotFoundException;
import org.ziaho.ziahorestapi.model.response.CommonResult;
import org.ziaho.ziahorestapi.service.ResponseService;

@RequiredArgsConstructor
@RestControllerAdvice // 예외 발생 시, Json의 형태로 결과를 반환하기 위함
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) {
        return responseService.getFailResult(Integer.parseInt(getMessage("unKnown.code")),
                getMessage("unKnown.message")
        );
    }

    @ExceptionHandler(CUserNotFoundException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResult userNotFoundException(HttpServletRequest request, CUserNotFoundException e) {
        return responseService.getFailResult(
                Integer.parseInt(getMessage("userNotFound.code")),
                getMessage("userNotFound.message")
        );
    }

    // 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());
    }

}

결과 확인


회원번호 999번으로 검색을 해보니 해당하는 데이터가 없어서 설정코드와 메시지에 에러코드가 발생했습니다.

영어 오류 메시지를 확인하기 위해 UserController에서 긴 정보를 가져오도록 수정합니다.

@Operation(summary = "회원 단건 조회", description = "msrl로 회원을 조회한다.")
@GetMapping(value = "/user/{msrl}")
public SingleResult<User> findUserById(@Parameter(name = "회원번호", required = true) @PathVariable long msrl,
                                       @Parameter(name = "언어") @RequestParam(value = "lang") String lang) throws Exception {
    // 결과 데이터가 단일건인 경우 getSingleResult를 이용하여 결과를 출력
    return responseService.getSingleResult(userJpaRepo.findById(msrl).orElseThrow(CUserNotFoundException::new));


쿼리 문자열로 lang=en을 전달했지만 일치하지 않습니다,,,,,

찾아보니 MessageConfiguration 페이지에서 전달된 매개변수와 일치하는 속성 파일을 찾기 위해 기본 설정을 지정해야 하는 것 같지만 변경하려고 해도 여전히 일치하지 않습니다.

더 많은 탐색 및 편집



원천

SpringBoot2를 사용하여 Rest API 빌드(7) – MessageSource를 사용한 예외 처리(daddyprogrammer.org)

SpringBoot2로 REST API 구축(7) – MessageSource로 예외 처리

이 시간 동안 Spring에서 메시지를 처리하는 방법과 MessageSource를 사용하여 예외 보고를 구동하는 방법을 배웁니다. Spring은 여러 언어를 처리하기 위해 i18n 설정을 지원합니다.

www.daddyprogrammer.org

기능/메시지 소스(github.com)의 codej99/SpringRestApi

GitHub – codej99/SpringRestApi: SpringBoot2, SpringSecurity, JWT, Stateless Restful API

SpringBoot2, SpringSecurity, JWT, Stateless Restful API – GitHub – codej99/SpringRestApi: SpringBoot2, SpringSecurity, JWT, Stateless Restful API

github.com