업무를 하다가 고객사의 웹 애플리케이션에서 일부 데이터가 영어로 접속했을 때 다국어 처리가 안 된다는 요청이 있었습니다. 음.. 이번에도 스마트팜 프로젝트이군요. 제가 개발한 프로젝트는 아니었지만 이렇게 자주 요청을 해오니 이제는 없던 애정이 생길 정도입니다.
일부 단어가 영어로 변환이 안 된다고 합니다. 다국어 처리 기능이 전체가 아닌 일부만 변환이 안된다고 한다면? 생각보다 간단한 문제일 수 있겠네요.
그래도 구체적인 현상을 파악해야 정확환 원인을 찾아갈 수 있겠죠. 문제가 발생한 부분을 살펴보았습니다.
UI를 살펴보니 이미지를 분석하는 기능입니다. 이미지를 분석 후, 결과를 화면에 출력하는데, 출력된 결과가 다국어 처리가 안 되고 한글로 나옵니다.
그래서 현상을 정리해보면,
이미지 분석한 결과에 다국어 처리가 문제가 있네요. 포인트를 찾았습니다.
구조를 살펴보니 이 프로젝트는 이미지를 분석하는 서버와 웹 애플리케이션과 분리되어 있습니다.
화면에서 요청이 들어오면 자바에서 API 통신으로 요청을 하고 있습니다. API에서 요청 후 다국어 변환을 하지 않고 바로 클라이언트로 리턴하는 것으로 추정합니다.
포인트를 잡았으니 이제 소스를 분석해보겠습니다. 문제가 발생했던 jsp 페이지부터 살펴보았습니다.
<div class=" text-left">
<ul>
<li><spring:message code="crop.grwh.lt" /> : <span id="grwh_lt_min"></span> ~ <span id="grwh_lt_max"></span></li>
<li><spring:message code="crop.lef.lt" /> : <span id="lef_lt_min"></span> ~ <span id="lef_lt_max"></span></li>
<li><spring:message code="crop.lef.ara" /> : <span id="lef_ara_min"></span> ~ <span id="lef_ara_max"></span></li>
<li><spring:message code="crop.lef.yld" /> : <span id="lef_yld_min"></span> ~ <span id="lef_yld_max"></span></li>
<li><spring:message code="crop.stem.thck" /> : <span id="stem_thck_min"></span> ~ <span id="stem_thck_max"></span></li>
<li><spring:message code="crop.fclu.hg" /> : <span id="fclu_hg_min"></span> ~ <span id="fclu_hg_max"></span></li>
<li><spring:message code="crop.flan.grup" /> : <span id="flan_grup_min"></span> ~ <span id="flan_grup_max"></span></li>
<li><spring:message code="crop.frtst.grup" /> : <span id="frtst_grup_min"></span> ~ <span id="frtst_grup_max"></span></li>
<li><spring:message code="crop.hvst.grup" /> : <span id="hvst_grup_min"></span> ~ <span id="hvst_grup_max"></span></li>
<li><spring:message code="crop.frut.yld" /> : <span id="frut_yld_min"></span> ~ <span id="frut_yld_max"></span></li>
</ul>
</div>
보통 위와 같은 코드로 다국어 처리가 적용되어 있습니다. JSP의 정적인 값들은 위와 같이 쉽게 설정할 수 있습니다.
그전에 다국어 처리에 대한 MessageSource 설정을 Spring에 해야 합니다. 자세한 설명은 타 블로그에 쉽게 설명이 잘 되어 있으니 궁금하신 분들은 참고해 주시기 바랍니다.
https://kim-jong-hyun.tistory.com/26
[Spring] - MessageSource로 메세지 및 다국어 관리하기
이번장에는 Spring에서 제공해주는 MessageSource에 대해 알아보자. 웹개발을 하면서 화면단에 alert함수를 이용해 클라이언트에게 특정메세지를 보여줘야 할때가 많다. 이때 java에서 메세지값을 하드
kim-jong-hyun.tistory.com
다시 돌아와서, 문제가 발생한 부분은 데이터 리턴 값입니다. Ajax 통신을 해서 리턴하는 값을 화면에 출력하기 때문에 위의 코드로는 해결할 수 없습니다. 음, 어떻게 해야 할지 고민이 됩니다.
이미지 분석 서버에 참여한 직장동료에게 문의해봤습니다. 만약 이미지 분석 서버에서 리턴하는 데이터의 종류가 많다면 기존에 구현된 방식인 properties로 처리하는 것도 한계가 있습니다. 이럴 경우, DB의 테이블로 데이터를 관리해야 합니다.
다행히 어떠한 이미지를 삽입하여 요청을 해도, 이미지 분석 결과 데이터가 예측 가능한 수준으로 데이터 종류와 개수가 한정되어 있다고 합니다. 이 정도라면 properties로 처리하면 될 것 같습니다.
저는 데이터 개수가 많지 않기 때문에 properties로 처리하기로 했습니다.
번역해야 할 총 11개의 데이터도 찾아냈고 영문명, 한글명 모두 리스트업이 완료되었습니다. 그렇다면 다국어 변환 처리를 어디에서 해야 할까요?
소스를 살펴보니, 프런트 엔드에서 처리한다면 이미 다국어 처리 설정이 적용되어 있어서 현재 Locale 상태를 체크를 하지 않아도 됩니다. for문으로 결과 값을 변환해주면 될 것 같습니다.
백엔드에서 처리해야 한다면 다국어 처리 설정을 위해 bean을 가져와야 합니다. 그리고 Locale 비교하는 로직도 추가되겠네요. jsp에서는 자동으로 Locale에 따라 변환되지만, 자바에서는 그렇지 않습니다. 음.. 백엔드에서는 코딩 수가 조금 늘어나겠네요.
사실, 어디에서 구현하든 같은 결과를 얻을 수 있겠지만, 데이터 변환이니 이왕이면 백엔드에서 하기로 했습니다.
포인트도 잡았고 변환한 데이터 리스트도 확보했으니 구현을 시작해보겠습니다.
먼저 bean으로 등록된 아이디를 확인해 줍니다.
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
bean 아이디가 xml에 "messageSource"로 등록되어 있네요. 일단 기억해 둡시다.
그리고 properties 파일에 변환할 값들을 입력해줍니다.
farmstatus.diagnosis.result.septoria=Septoria leaf spot
farmstatus.diagnosis.result.canker=Bacterial canker
farmstatus.diagnosis.result.leafmold=Leaf mold
...(이하 생략)
위와 같은 형식으로 message_en.properties 파일과 message_ko.properties 파일에 각각 영어와 한글 데이터를 입력했습니다.
MVC 패턴이라 ServiceImpl파일이 존재합니다. 그래서 ServiceImpl 파일에서 다국어 처리 메서드를 구현하기로 했습니다. 아까 bean이 등록되어 있는 것을 확인했으니 해당 bean을 불러와야겠죠.
@Autowired
ReloadableResourceBundleMessageSource messageSource;
Spring의 @Autowird 어노테이션을 활용하여 Bean을 찾아 주입해 줍니다.
현재 Locale 값이 필요합니다. 그래서 Controller에서 request 객체에서 locale을 추출해 ServiceImpl로 넘겨줬습니다.
public Map<String, Object> diagnosis(HttpServletRequest request, ...중략){
Object localeValue = session.getAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME);
if(localeValue == null) {
locale = "ko";
} else {
locale = localeValue.toString();
}
return farmStatusService.diagnosis(map, locale);
}
그리고 ServiceImpl에 메서드를 구현했습니다.
private String checkNChangeLocaleDiagnosisKeywords(String textValue, String sessionLocale){
String[] checkTextList = {
"farmstatus.diagnosis.result.none",
"farmstatus.diagnosis.result.fail",
"farmstatus.diagnosis.result.septoria",
"farmstatus.diagnosis.result.canker",
"farmstatus.diagnosis.result.leafmold",
"farmstatus.diagnosis.result.grayleafspot",
"farmstatus.diagnosis.result.tocv",
"farmstatus.diagnosis.result.curlvirus",
"farmstatus.diagnosis.result.mildew",
"farmstatus.diagnosis.result.cabbageworm",
"farmstatus.diagnosis.result.liriomyza"
};
String krCode = ""+Locale.KOREA;
krCode = krCode.split("_")[0];
String usCode = ""+Locale.US;
usCode = usCode.split("_")[0];
if(sessionLocale.equals(usCode)) {
for(int i=0;i<checkTextList.length;i++) {
String keyName = checkTextList[i];
String korText = messageSource.getMessage(keyName, null, Locale.KOREA);
if(textValue.indexOf(korText) > -1) {
String usText = messageSource.getMessage(keyName, null, Locale.US);
textValue = textValue.replaceAll(korText, usText);
break;
}
}
}
return textValue;
}
위 메서드는 한글이 완전히 일치하면 영어로 해당 키워드 전체를 변환해주는 기능을 합니다. 변환해야 할 데이터의 개수가 11개 정도이기 때문에 String 배열로 선언해줬습니다.
Locale을 체크하는 로직에서 "Locale.KOREA"와 같은 값은 "import java.util.Locale;"으로 Locale 객체를 가져오면 됩니다.
아쉬운 점은 ' farmstatus.diagnosis.result.* ' 와 같은 방법으로 해당 조건에 키 값을 가져올 수 있으면 더 간편해질 텐데 방법을 찾지 못했습니다. properties 값 전체를 가져올 수는 있기는 하지만 properties에 등록된 데이터 크기에 비해 변환해야 할 데이터는 소수이기 때문에 비효율적으로 보여 포기했습니다.
ReloadableResourceBundleMessageSource에서 사용 가능한 메서드 리스트를 알고 싶으신 분은 아래 공식 사이트를 참고해 주시기 바랍니다.
ReloadableResourceBundleMessageSource (Spring Framework 6.0.1 API)
refreshProperties Refresh the PropertiesHolder for the given bundle filename. The holder can be null if not cached before, or a timed-out cache entry (potentially getting re-validated against the current last-modified timestamp). Parameters: filename - the
docs.spring.io
메서드를 만들었으니 적용을 해야겠죠. API를 호출한 후, 결과 값을 리턴하는 라인을 변경해 줍니다.
// AS-IS
rtnMap.put("prob", rtnObj.get("prob").toString());
// TO-BE
String prob = (String) rtnObj.get("prob").toString();
rtnMap.put("prob", checkNChangeLocaleDiagnosisKeywords(prob, locale));
기존 로직은 값만 리턴하면 되기 때문에 형 변환까지는 신경 쓰지 않아도 되지만, 수정한 버전에서는 형 변환을 해주어야 합니다.
구현이 완료되었으니 이제 테스트를 해봅시다. 계속 값을 확인하면서 구현했기 때문에 큰 문제는 없을 것 같지만
다국어 변환이 안되었던 시나리오대로 처음부터 재현해봤습니다.
11가지 케이스를 전부 테스트했습니다. 전부 깔끔하게 결과가 출력되네요.
이제 고객에게 완료되었다고 하고 운영서버에 반영해야겠습니다.
다국어 처리는 보통 jsp에서 <spring:message code="키값" /> 태그로 구현합니다. 하지만 백엔드에서도 구현해야 하는 경우가 생길 수 있습니다. 각자의 개발 환경이 다르니 완전히 동일할 수는 없겠지만 개발 환경이 Spring이라면 위 방법을 참조해서 구현하면 됩니다.
정리하면 아래와 같습니다.
네 이상입니다. 저의 경험이 도움이 되셨길 바랍니다.
[개발일지] nodejs로 API 서버 만들기 PART. 2 (0) | 2022.10.28 |
---|---|
[개발일지] nodejs로 API 서버 만들기 PART. 1 (0) | 2022.10.14 |
[개발일지] 화면 기능 개발하기 (1) | 2022.10.05 |
[개발일지] 오류를 해결하기 위한 과정 PART 2. (2) | 2022.09.29 |
[개발일지] 오류를 해결하기 위한 과정 PART 1. (0) | 2022.09.28 |
댓글 영역