문제 상황
MessageSource를 이용해서 messages_en.properties에 있는 값을 반환하는데 ‘ 작은따옴표 문자가 정상적으로 출력되지 않는 문제가 있었습니다.
상황 1
- 일반 문자열에 ‘(작은따옴표)를 사용하면 원하는 값을 정상적으로 출력
상황 2
- 일반 문자열 + 템플릿 문자를 사용한 경우 ‘(작은따옴표)가 정상적으로 출력되지 않았습니다.
MessageSource 인터페이스를 뜯어보면서 MessageFormat가 연관이 있는 것을 확인하고 검색을 해보니 자바 공식문서에 아래와 같은 글이 있었습니다.
Within a String, a pair of single quotes can be used to quote any arbitrary characters except single quotes.
→ 하나의 ‘’ 쌍은 문자열을 인용하는데 사용되어진다. (’’은 제외된 채 출력된다.)
For example, pattern string "'{0}'" represents string "{0}", not a FormatElement. A single quote itself must be represented by doubled single quotes '' throughout a String → 작은따옴표 그 자체를 사용하려면 ‘’ 2번 감싸야한다.
위와 같은 설명을 통해 ‘’ 작은따옴표를 출력하는 방법은 이해가 됐다, 하지만 MessageFormat을 어느 상황에서 어떻게 처리되는지 정확하게 파악하고자 하나씩 뜯어보았다.
문제 해결
a_message 경우는 일반 문자열만 있는 경우이고 b_message는 템플릿 문자를 가진 메시지라고 생각하면 됩니다.
a_message=You've already' logged in. Please use the current password.
b_message=Please download '테스트 Test' App on the Google Play Store or the App Store. **** {0} ****
Spring의 MessageSource가. properties 파일에서 메시지를 처리할 때, 작은따옴표는 escape 문자로 사용됩니다. 이는 MessageFormat을 따르는 방식인데, 작은따옴표가 메시지에서 변수나 포맷을 표시하기 위한 리터럴 문자의 시작과 끝을 나타내는 역할을 합니다.
Spring에서 작은따옴표의 역할:
- Escape 문자: 작은따옴표는 MessageFormat에서 escape 문자로 사용됩니다. 예를 들어, 변수나 포맷에 영향을 주지 않도록 특수 문자를 출력해야 할 때, 작은따옴표로 해당 부분을 감쌉니다.
- 템플릿 문자를 사용하면서 ‘’를 통해 문자열을 감싸는 경우 작은따옴표를 한 번 더 감싸서 처리한다.
- 리터럴 처리: 작은따옴표로 감싼 텍스트는 리터럴로 처리됩니다. 즉, 작은따옴표로 감싸지 않으면 일반적으로 메시지 내부에서 의미 있는 포맷으로 해석되지만, 작은따옴표로 감싸면 그 내용을 그대로 출력합니다.
- 작음 따옴표를 감싸면 템플릿 문자는 리터럴 자체로 인식되어 처리됩니다. (템플릿 문자가 무시됨)
'테스트 Test' 같은 경우는 ‘ ‘ 안에 있는 글자가 리터럴 처리되어 작은따옴표 없이 출력됩니다.
이를 해결하기 위해서 이스케이프 \ 가 아닌 ‘를 감싸서 사용해야 합니다. (’’테스트 Test’’)
배운 점
모르는 것을 생겼을 때 공식문서를 먼저 보는 습관을 조금씩 들이고 있는데, 공식문서를 보니 자세한 설명과 근거 있는 자료로 궁금증을 해결하는데 가장 좋은 문서인 거 같다. 또한 공식문서로 이해가 되지 않으면 번역기를 사용하거나 영어로 된 문서를 찾아보는데 영어로된 문서에 친숙해져야 함을 또 한 번 느끼는 시간이었다.
- 사용하는 메서드와 관련된 정보를 알 수 있다.
→ MessageSourceResolvable와 MessageFormat과 연관이 있음을 알 수 있었고 하나씩 찾아보았다.
@Override
public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
String msg = getMessageInternal(code, args, locale);
if (msg != null) {
return msg;
}
String fallback = getDefaultMessage(code);
if (fallback != null) {
return fallback;
}
throw new NoSuchMessageException(code, locale);
}
@Nullable
protected String getMessageInternal(@Nullable String code, @Nullable Object[] args, @Nullable Locale locale) {
if (code == null) {
return null;
}
if (locale == null) {
locale = Locale.getDefault();
}
Object[] argsToUse = args;
if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
// Optimized resolution: no arguments to apply,
// therefore no MessageFormat needs to be involved.
// Note that the default implementation still uses MessageFormat;
// this can be overridden in specific subclasses.
String message = resolveCodeWithoutArguments(code, locale);
if (message != null) {
return message;
}
}
else {
// Resolve arguments eagerly, for the case where the message
// is defined in a parent MessageSource but resolvable arguments
// are defined in the child MessageSource.
argsToUse = resolveArguments(args, locale);
MessageFormat messageFormat = resolveCode(code, locale);
if (messageFormat != null) {
synchronized (messageFormat) {
return messageFormat.format(argsToUse);
}
}
}
// Check locale-independent common messages for the given message code.
Properties commonMessages = getCommonMessages();
if (commonMessages != null) {
String commonMessage = commonMessages.getProperty(code);
if (commonMessage != null) {
return formatMessage(commonMessage, args, locale);
}
}
// Not found -> check parent, if any.
return getMessageFromParent(code, argsToUse, locale);
}
resolveArguments, resolveCodeWithoutArguments 메서드를 보면 템플릿 문자 여부에 따라 다르게 처리하고 있음을 알 수 있다.
Arguments가 있는 경우 인자를 먼저 처리하고, resolveCode()를 통해 처리할 MessageFormat를 생성하고 포맷팅 합니다.
'Spring Framework > Spring' 카테고리의 다른 글
[Spring] 스프링 FactoryBean 이해하기 - Custom Bean 생성방법 (2) | 2024.10.09 |
---|---|
[Spring] Spring Batch Tasklet 작업 단위 이해하기 - StepContribution, ChunkContext (0) | 2024.10.07 |
[Spring] Spring으로 HTML 파일 PDF 변환, 다운로드하기 - 한글 깨짐 문제, Thymeleaf, PDF 변환 해결 (0) | 2024.07.30 |
[Spring] 스프링 reactive-stream, 비동기 통신 이해하기 (0) | 2024.06.23 |
[Spring Cloud] Spring Config Server를 이용해 설정 파일 관리하기 (0) | 2024.04.01 |