국제화와 지역화

위키백과 내용 - https://ko.wikipedia.org/wiki/%EA%B5%AD%EC%A0%9C%ED%99%94%EC%99%80_%EC%A7%80%EC%97%AD%ED%99%94

국제화와 현지화는 출판물이나 하드웨어 또는 소프트웨어 등의 제품을 언어 및 문화권 등이 다른 여러 환경에 대해 사용할 수 있도록 지원하는 것을 의미한다. 이때 국제화는 제품 자체가 여러 환경을 지원할 수 있도록 제품을 설계하는 것을 의미하며, 현지화는 제품을 각 환경에 대해 지원하는 것을 의미한다.

 

최자의 보물창고 블로그 내용(신빙성은 적을 수 있음) - https://jwchoi85.tistory.com/tag/internationalization

컴퓨터에서 국제화와 현지화 라는것은, 다른 언어와 지역적인 차이들을 소프트웨어에 적용시킨다는 것을 말합니다. 국제화라는 것은 여러가지 다른 언어와 지역적 차이들을 기술적인 변경 없이 소프트웨어에 적용할 수 있도록 설계하는 과정을 말합니다. 현지화 라는 것은 국제화가 된 소프트웨어에 특정 지역이나 언어에 대해서 지역특색의 컴포넌트를 추가한다거나 번역을해서 적용시키는 것을 말합니다

 

 

LocaleResolver 종류

 

  • AceeptHeaderLocaleResolver (default)
    • 브라우저의 언어값으로 사용하는 것
    • HTTP 'Accept-Language' header값을 사용하는것
    • setLocale()메소르드를 지원하지 않음 사용하면 UnsupportedOperationException 발생함.
    • Locale aceeptHeaderLocale = new HttpServletRequest().getLocale();

  • CookieLocaleResolver
    • 쿠키 기반의 로케일 정도를 사용하는 것
    • <bean id="localeResolver"
          class="org.springframework.web.servlet.i18n.CookieLocaleResolver" >
          <property name="cookieName" value="clientlanguage"/>
          <property name="cookieMaxAge" value="100000"/>
          <property name="cookiePath" value="web/cookie"/>
      </bean>

    • 속성 설명

      프로퍼티 

      설 명  

       cookieName

       사용할 쿠키 이름

       cookieDomain

       쿠키 도메인

       cookiePath

       쿠키 경로, 기본값은 "/"이다.

       cookieMaxAge

       쿠키 유효 시간 (second), -1로 지정하면 브라우저를 닫을 때 쿠키를 지우겠다는 것

       cookieSecure

       보안 쿠키 여부, 기본값은 false 이다.

    • 만약 저장된 쿠키값이 없어서 특정값으로 세팅하고자 한다면 defaultLocale 파라미터값을 세팅하면 된다.

    • SessionLocaleResolver나 CookieLocaleResolver에 defaultLocale이 없다면 브라우저의 언어설정(AceeptHeaderLocaleResolver)을 따르게 된다.
    • CookieLocaleResolver에 보면 몇가지 추가 세팅할 수 있는 값이 있음
      • languageTagCompliant - Specify whether this resolver's cookies should be compliant with BCP 47 language tags instead of Java's legacy locale specification format. The default is falseNote: This mode requires JDK 7 or higher. Set this flag to true for BCP 47 compliance on JDK 7+ only.  
      • 위키백과의 내용과 같이 ko-KR과 같이 '_'가 아닌 '-'를 붙여서 표현하는 방식으로 쿠키값을 심을지 여부값 이다. true값이 되면 'ko-KR', 'en-US'와 같이 '-'가 들어간 형태로 cookieValue값이 세팅된다. springframework 내부적으로는 'ko_KR' 형식으로 사용하고 쿠키값(cookieValue)만 변경됨.
        • languageTagCompliant 값에 따른 parsing하는 코드
          • (isLanguageTagCompliant() ? Locale.forLanguageTag(locale) : StringUtils.parseLocaleString(locale))
      • Cookie 관련 정보(CookieGenerator class를 상속받아 cookie관련 설정값을 추가로 세팅 할 수 있음)
        • HttpOnly (CookieGenerator.cookieHttpOnly에 있는 값을 상속받음.) - javascript에서 document.cookie로 cookie값을 조작할 수 없도록 하고 웹서버에서만 cookie값을 조작할 수 있도록 할지 여부 값, 참고 링크 https://nsinc.tistory.com/121
        • secure (CookieGenerator.cookieSecure 에 있는 값을 상속받음) - HTTPS가 아닌 통신에서는 쿠키를 전송하지 않을지 여부 값. 참고 링크 https://nsinc.tistory.com/121
  • SessionLocaleResolver
    • 세션 기반의 로케일 정보를 사용하는 것
    • request가 가지고 있는 session에 있는 값을 가지고 오는 것
      <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

    • 설정 예

    • <beans:bean id="localeResolver"

          class="org.springframework.web.servlet.i18n.SessionLocaleResolver">

          <beans:property name="defaultLocale" value="ko" />

      </beans:bean>

    • SessionLocaleResolver나 CookieLocaleResolver에 defaultLocale이 없다면 브라우저의 언어설정을 따르게 된다.

  • FixedLocaleResolver
    • 웹 요청과 상관없이 defaultLocale 속성값을 사용는 것
    • FixedLocaleResolver는 setLocale() 메소드를 지원하지 않음 호출하면  UnsupportedOperationException이 발생함.

Locale 변경시

  • LocaleResolver를 이용한 Locale 변경
    • LocaleResolver를 빈으로 등록했다면, 컨트롤러에서 LocaleResolver를 이용해서 Locale을 변경할 수 있게 된다. 예를 들어 다음과 같이 LocaleResolver를 설정했다고 하자.

    • <bean class="com.xxx.test.controller.LocaleChangeController">

              <property name="localeResolver" ref="localeResolver" />

      </bean>

      <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

       

      이 경우, 컨트롤러 클래스는 다음과 같이 LocaleResolver의 setLocale() 메서드를 호출해서 클라이언트의 웹 요청을 위한 Locale을 변경할 수 있다.

    • import org.springframework.web.servlet.LocaleResolver;

      @Controller

      public class LocaleChangeController {

          private LocaleResolver localeResolver;

          @Inject

          public void setLocaleResolver(LocaleResolver localeResolver) {

              this.localeResolver = localeResolver;

          }

       

          @RequestMapping("/changeLocale")

          public String change(@RequestParam("language") String language, HttpServletRequest request, HttpServletResponse response) {

              // Locale locale = (isLanguageTagCompliant() ? Locale.forLanguageTag(locale) : StringUtils.parseLocaleString(locale))  // <-- CookieLocaleResolver를 사용 하는 경우

              Locale locale = new Locale(language);   // <-- cookie가 아닌 다른 LocaleResolver를 사용하는 경우

       

              // LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request); // <-- DI를 받지 않고 현재 request에서 LocaleResolver를 가져올 수 도 있음.

       

              localeResolver.setLocale(request, response, locale);

              return "redirect:/index.jsp";

          }

      }

      LocaleResolver를 이용해서 Locale을 변경하면, 이후 요청에 대해서는 지정한 Locale을 이용해서 메시지 등을 로딩하게 된다.

      ResolverContextUtils는 현재 request에서 LocaleResolver를 가져올 수 있음.

  • LocaleChangeInterceptor를 사용하는 경우
    • org.springframework.web.servlet.i18n.LocaleChangeInterceptor는 url를 통해서 언어를 변경할 경우 사용 아래와 같은 설정 상태면 
    • <mvc:interceptors>

          <beans:bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">

          <beans:property name="paramName" value="lang" />

          </beans:bean>

      </mvc:interceptors>

 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/LocaleResolver.html

Posted by 파이팅야
,

.net HttpClient를 사용해서 외부 http API 호출하고 있는데

'Unable to read data from the transport connection: 현재 연결은 원격 호스트에 의해 강제로 끊겼습니다' exception이 발생하는 경우 


https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10 문서의 내용보면  

'keep alive(persistence connection)를 하지 않을려면 Connection : Close를 명시적으로 붙여줘야 한다'는 내용이 있음

해당 내용을 보고 .net쪽 HttpClient library가 'Connection'header값이 없으면 keep alive로 동작하게 한것 같음 만약 아래코드가 없으면 keep-alive로 동작함

RequestHeaders headers = new RequestHeaders();

headers.Add("Connection", "close");

예전에도 비슷한 상황이 있었는데... 자꾸 까먹어 적어 놔야겠다. ㅋ

Posted by 파이팅야
,

MSSQL을 사용할 때
인덱스가 걸려있는 VARCHAR 컬럼데이터를 조회시 NVARCHAR로 데이터를 넘길경우
인덱스가 걸리지 않아 성능에 문제가 발생하는 사례가 존재한다.

(쿼리분석기에서는 빠른데 미들웨어에서 호출 시 느린경우)


 하여 보통은 jtds 드라이버 설정시
 sendStringParametersAsUnicode=false
 값을 주어 유니코드 처리하지 않게 함으로서 VARCHAR로 처리 되도록 하여 해결을 하고 있다.

 근데 문제는
 VARCHAR 와 NVARCHAR 가 섞여있을 경우에는 어떻게 해야 하는가?

 사실 이문제는 아래와 같이 풀면 해결이 될거라고 생각했다.

 1) 드라이버에서는 유니코드를 사용하지 않음 처리.
     - sendStringParametersAsUnicode=false
 2) mybatis mapper 에 jdbcType 을 부여
    - #{column, jdbcType=NVARCHAR}

 혹은 1)번을 부여하지 않고( default 는 sendStringParametersAsUnicode=true 이다. )
 #{column, jdbcType=NVARCHAR} 와 #{column, jdbcType=VARCHAR} 를 명시해주면 되지 않을까?

 라고 생각했었다.

 하지만, 결론은 안된다는거...

 그래서 jtds 드라이버 소스코드를 살펴 보았다.

 역시...NVARCHAR 사용을 위한 setNString() 에서 아래와 같은 코드로 구현이 되어있었고,

  [ JtdsCallableStatement.java ]

  public void setNString(String parameterName, String value)
            throws SQLException {
        // TODO Auto-generated method stub
        throw new AbstractMethodError();
    }

  public void setNString(int parameterIndex, String value)
            throws SQLException {
        // TODO Auto-generated method stub
        throw new AbstractMethodError();
    }

 실제 TdsData.java  ( getNativeType 메소드 ) , Support.java ( getJdbcTypeName 메소드 )내에서 지원가능한 jdbcType 검사를 하는데  여기에도 NVARCHAR 가 누락이 되어있다.

그럼 어떻게 NVARCHAR 를 지원하고 있었던걸까?

결론은 이렇다.

jtds 에서는 모든 NVARCHAR 든 VARCHAR 든 VARCHAR 로 인식하고 드라이버에 설정된 유니코드 사용여부에 따라 VARCHAR를 변환하고 있었던 것이다.

'모' 아니면 '도' 로 말이다.

그렇다면 이부분을 어떻게 해결할 수 있을까?

답은 간단하다.

setNString 구간에 setString 을 참조하여 내용을 체워주고,

JtdsPreparedStatement.java ]
protected void setParameter(int parameterIndex, Object x, int targetSqlType, int scale, int length)

메소드에 NVARCHAR 타입일 경우 pi.isUnicode = true 로 부여하면,

mybatis에서 NVARCHAR 로 jdbcType 을 부여한 것에 대해서 처리가 가능하다.

이때, mybatis 부분도 수정이 필요한데 이유는 이렇다.

mybatis 의 NVARCHAR 를 처리하는 NStringTypeHandler 라는 클래스가 존재하는데

이 클래스에서 파라미터 바인딩시 setString 이 호출되게 되어있다.

이걸 setNString 을 호출하게 변경하면, 정상처리가 되게 되는 것이다.


이번 기회에 jtds 내에서 어떤 일이 일어나는지 알게된 점이 좋았던거 같다.

그리고 왜 NVARCHAR 를 지원하지 않게 되어있는지...그리고 mybatis에서도 왜 NStringTypeHandler 에서도

setNString 이 아니라 setString 으로 되어있었는지에 대해서는 좀더 고민을 해봐야 할듯하다.

이유가 없지는 않을테니 말이다.

여하튼 이렇게 VARCHAR 와 NVARCHAR 을 Mix 에서 사용하는 곳에서는
이런 부분도 고려가 되어야 할듯하다.




출처 : http://ymseok.blogspot.kr/2013/07/jtds-mssql-nvarchar.html

Posted by 파이팅야
,