2009. 12. 15. 15:59

출처 : http://www.oracle.com/technology/global/kr/index.html

개발 준비

제대로 된 다국어 유틸리티를 만들기 위해서는 다음과 같은 준비물이 필요하다.
  • JDK 1.4.2
  • Oracle GDK library(orai18n.jar)
  • Oracle JDeveloper
  • 기타 라이브러리
  • Oracle GDK(Globalization Development Kit)

     첫 회에서 미리 언급한 바가 있지만, 오라클은 무려 140개 이상의 로케일(Locale)을 지원한다. 이는 오라클 기반으로 만들어진 애플리케이션은 140가지 이상의 다른 환경을 동시에 지원할 수 있다는 의미가 된다. 오라클을 설치하면 이미 각각의 로케일에 대한 정보(날짜, 숫자 형식, 타임존, 캐릭터셋, 통화 기호 등)가 모두 설치되게 된다. 여러분들이 NLS_LANG 변수값을 설정할 때 빈번하게 사용하는 "AMERICAN_AMERICA"나 "KOREAN_KOREA"는 이 많은 로케일 중의 두 가지일 뿐이다.

     문제는 이 많은 로케일 정보를 어떻게 열람하고 사용할 수 있는가 말이다.  로케일 정보를 단지 "열람"하는 것은 예전부터 오라클 데이터베이스에 포함되어 있던 "Oracle Locale Builder"로 가능하다. 이 툴은 로케일 정보를 열람하는 것 뿐 아니라, 새로운 로케일을 만들어낼 수 있는 강력한 툴이지만, 새로운 로케일을 만들어내는 것보다는 필요할 때마다 애플리케이션이 필요한 로케일 정보를 잘 사용할 수 있도록 해 주는 것이 더 필요하다.

    그래서, 오라클은 10g부터 GDK(Globalization Development Kit)을 포함하고 있다. 이는 자바 기반의 애플리케이션이 로케일 정보를 각 클라이언트의 상황에 맞게 실행시에 제공할 수 있도록 프로그래밍할 수 있게 해 준다. OTN에서 따로 다운로드받아서 사용할 수 있으며, 사용을 위해 꼭 데이터베이스가 필요한 것은 아니다. 10g 데이터베이스가 가지고 있는 NLS 정보를 모두 가지고 있으므로, 웹 기반의 애플리케이션이라면 데이터베이스 없이 오라클의 NLS 정보를 이용할 수 있는 것이다.

    오라클 10g 데이터베이스가 설치되어 있다면, 다름 jar 파일들을 CLASSPATH에 포함시키기만 하면 된다. GDK의 Java API 레퍼런스는 위의 OTN 링크에서 역시 찾을 수 있다.
  • $ORACLE_HOME/jlib/orai18n.jar
  • $ORACLE_HOME/jlib/orai18n-lcsd.jar
  • 기타 라이브러리

    오라클 JDeveloper에 포함되어 있는 ordhttp11.jar는 파일 업로드 처리에 유용한 클래스들을 포함하고 있다. 그리고 데이터베이스를 사용할 경우 JDBC 라이브러리도 필수다.
  • $ORACLE_HOME/ord/jlib/ordhttp11.jar
  • $ORACLE_HOME/jdbc/lib/classes12.jar
  • 다국어 유틸리티 시작

    Oracle GDK가 없어도 준비할 수 있는 유틸리티로 워밍업하기

    다국어 - 유니코드 변환

    학교에서 교수님이 다음과 같은 화면을 보여주면서 같은 기능을 하는 스크립트를 개발하라고 하면 어떻겠는가? 아마 개발자 여러분들은 쉽게 할 수 있을 것이다. 하나의 JSP와 몇 개의 Javascript만 있으면 이런 페이지는 쉽게 만들어낼 수 있다.  세 가지 입출력 함수의 기능은 다음과 같다.
  • 다국어('한')를 입력받아 유니코드 이스케이프된 문자('\uC624')나 HTML 페이지에서 사용할 수 있는 엔티티 형식('오')으로 변환한다
  • 유니코드 이스케이프된 문자를 다시 역으로 다국어로 출력한다
  • 엔티티 형식을 다시 다국어로 출력한다


  • <그림 : 다국어 - 유니코드 변환 페이지> * 오늘의 깜짝 지식: 甲骨文은 오라클이 중국에서 사용하는 회사명이다.

    이 중 유니코드 이스케이프의 경우 JDK에 포함된 native2ascii라는 유틸리티로도 같은 결과를 얻을 수 있다. 위의 그림은 native2ascii의 간단한 사용 방법 또한 보여주고 있으며, 그 입력과 출력은 이 유틸리티와 동일하다. 다음은 첫 번 째 함수의 Javascript 소스 코드로 보는 바와 같이 매우 간단하다. 핵심이 되는 함수는 붉은 색으로 표시를 했으며, 나머지는 입출력 형식을 맞추는 코드가 되겠다.

    function to_native2ascii()
    {
    var s = document.native2ascii.nativeStr.value;
    var len = s.length;
    var i;
    var c;
    var charval;
    var result = "";
    var resultHTML = "";
    for(i=0; i < len; i++)
    {
    c = s.charCodeAt(i);
    charval = s.charAt(i);
    if(c < 128)
    {
    result = result + charval;
    switch(charval)
    {
    case '&':
    resultHTML = resultHTML + "&amp;";
    break;
    case '<':
    resultHTML = resultHTML + "&lt;";
    break;
    case '>':
    resultHTML = resultHTML + "&gt;";
    break;
    default:
    resultHTML = resultHTML + charval;
    }
    }
    else
    {
    result = result + "\\u"+c.toString(16).toUpperCase();
    resultHTML = resultHTML + "&#" + c + ";";
    }
    }
    document.native2ascii.OutputStr.value = result;
    document.native2ascii.OutputStrHTML.value = resultHTML;
    }
  • 오라클 데이터베이스와의 입출력시 사용되어야 하는 NLS정보는 GDK의 로케일 정보와 일치한다. 가령 오라클 형식의 한국 날짜 기본 형식은 'RR/MM/DD'('05/09/20')이다. 하지만, JDK에서는 'YY. MM. DD.'('05. 09. 20.')을 사용한다. 우리가 데이터베이스로 하여금 특별히 날짜 형식을 지정해 주지 않은 채 날짜 정보를 삽입하고자 한다면 오라클 로케일 형식에 맞추어 전달해야 하는 것이다.

    SQL> insert into datetable values('05/06/01');

    1 개의 행이 만들어졌습니다.

    SQL> insert into datetable values('05. 06. 01.');
    insert into datetable values('05. 06. 01.')
                                 *
    1행에 오류:
    ORA-01830: 날짜 형식의 지정에 불필요한 데이터가 포함되어 있습니다.

    SQL> select next_day(sysdate,'Tuesday') from dual;
    select next_day(sysdate,'Tuesday') from dual
                            *
    1행에 오류:
    ORA-01846: 지정한 요일이 부적합합니다

    SQL> select next_day(sysdate,'화요일') from dual;

    NEXT_DAY
    --------
    05/09/13
  • 참고로 오라클의 경우에도 일반적인 자바 기반 애플리케이션은 단순히 JDK에서 제공하는 로케일 정보를 사용하는 경우가 많다.  사실 날짜 형식을 제외하고는 서로 값의 차이가 뚜렷하게 나는 경우는 별로 없다.

  • 그렇다면, 역함수를 구현하는 것도 그리 어려운 일이 될 수가 없다. string.charCodeAt(i)의 역함수인 "String.fromCharCode()" 사용하면 된다. 본 편에 들어가기 전에 이런 간단한 소스를 소개한 것은 한 가지 강조하고픈 점이 있기 때문이다. 개발자는 생산성이 높아야 하고, 그 생산성은 JDeveloper와 같은 통합 툴 같은 것만 가지고는 해결되지 않는다. 늘 자신의 생산성을 향상시킬 무기를 가지고 있어야 한다. 이런 간단한 유틸리티가 얼마나 본인의 생산성에 도움이 될 것인지는 수치로 측정할 수는 없지만,  확실한 것은 한국어가 영어 알파벳을 사용하는 언어가 아닌 이상, 여러분들은 언젠가는 웹 기반의 한국어 또는 다국어 애플리케이션을 작성하며 "아쉬운 순간"을 맞이하게 될 것이라는 점이다. 전투를 위한 여러분의 진지를 구축한다고 생각하자.

    로케일 정보를 한 눈에 볼 수 있는 유틸리티 작성

    오라클이 제공하는 로케일 정보를 Java 애플리케이션에서 열람하고 사용할 수 있는 방법을 알기 위해서, Oracle Locale Builder와 같이 로케일을 선택하면 해당 로케일에 대한 정보를 보여주는 JSP 기반의 웹 페이지를 만들어보도록 하자. 단순히 Locale Builder와 같은 내용을 보여주면 재미가 없고 효용도 떨어지니 JDK에서 제공하는 로케일 정보를 함께 보여주는 것이 더 유용할 것 같다. 같은 로케일에 대해서도 오라클의 로케일 정보와 자바의 로케일 정보가 완전히 일치하지는 않는다.  이를 함께 비교하는 것도 재미있으며 때로는 뜻하지 않게 의문을 풀어주기도 한다.
    이제부터 만들어볼 로케일 열람 유틸리티는 단순히 하나의 JSP 스크립트이다. 하지만 길이가 약간 길기 때문에 여기서는 각각의 기능별로 쪼개어 살펴보고 마지막에 전체 스크립트를 다운로드받아서 각자 실행해 보는 형식으로 진행하겠다.



    그림에서 보는 바와 같이 오라클이 제공하는 모든 로케일을 리스팅하고 그 중 하나를 선택하면, 해당 로케일 구성(언어 - 영역)에 따라 각종 NLS 정보를 보여주는 페이지가 되겠다. 리스팅된 로케일 구성은 실제 NLS_LANG값에 사용될 수 있는 "언어"와 "영역"의 구성이다.

    헤더

    다국어를 출력해야 하므로 UTF-8 기반의 JSP 페이지를 생성해야 하는 것은 당연하다. 그리고, Oracle GDK의 다양한 클래스들의 도움과 JDK의 유틸리티를 사용하기 위해 여러 패키지명을 등록한다.

    <%@ page contentType="text/html;charset=utf-8"%>
    <%@ page import="oracle.i18n.servlet.*,
    oracle.i18n.servlet.localesource.*,
    oracle.i18n.util.*,oracle.i18n.text.*,
    java.util.Locale,java.util.*,java.text.*"%>

    모든 오라클의 로케일 구성을 나열하기

      사용자에게 오라클이 지원하는 모든 로케일 정보를 나열하고 여기서 원하는 로케일을 선택하도록 하기 위한 코드이다. 위의 그림에 있는 선택 상자가 이에 해당된다.

    String[] allLocales = OraLocaleInfo.getAvailableLocale();
    ....
    <select name="localeChoice" onChange="localeForm.submit();">
    <%
    for(int i=0; i < allLocales.length; i++)
    {
    if(allLocales[i].equals(lstr))
    {
    %>
    <option value="<%=allLocales[i]%>" selected><%=allLocales[i]%></option>
    <%
    }
    else
    {
    %>
    <option value="<%=allLocales[i]%>"><%=allLocales[i]%></option>
    <%
    }
    }
    %>
    </select>

    로케일 인스턴스 획득

    사용자가 원하는 로케일을 선택했을 경우, 일단 이에 해당하는 로케일 객체를 얻어내야 한다. 이 로케일 객체는 해당 로케일에 대한 모든 정보를 포함하고 있는 것으로 사실상 우리가 만드는 스크립트의 핵심 코드이며, 이후의 모든 메소드 호출은 로케일 정보를 단순히 사용하고 화면에 표현함에 다름 아니다.

        String lstr = request.getParameter("localeChoice");    // KOREAN_KOREA를 선택했다면
    ..
    String lang = null;
    String terr = null;
    if(lstr != null)
    {
    int _idx;
    _idx = lstr.indexOf('_');
    lang = lstr.substring(0,_idx); // KOREAN
    terr = lstr.substring(_idx+1); // KOREA
    oraLocaleInfo = OraLocaleInfo.getInstance(lang,terr);
    javaLocale = oraLocaleInfo.getLocale();
    }
    else
    {
    // 만일 사용자의 선택이 없는 경우, 사용자 브라우저의 로케일을 사용하기로 한다
    javaLocale = request.getLocale();
    oraLocaleInfo = OraLocaleInfo.getInstance(javaLocale);
    javaLocale = oraLocaleInfo.getLocale();
    lstr = oraLocaleInfo.getLanguage()+"_"+oraLocaleInfo.getTerritory();
    }
    /* 영문명과 각 국가의 고유 표기명을 병기하기 위해 다음과 같은 객체를 미리 생성해 두자. 예를 들어
    국가명의 경우 영문으로 표기되는 이름도 있고, 각 국가의 언어를 사용하여 표기하는 국가명도 있다 */
    OraDisplayLocaleInfo oraUSDisplayLocaleInfo =
    OraDisplayLocaleInfo.getInstance(Locale.US);
    OraDisplayLocaleInfo oraDisplayLocaleInfo =
    OraDisplayLocaleInfo.getInstance(javaLocale);
    만일 사용자가 아직 로케일을 선택하지 않은 경우, 사용자 브라우저에서 설정한 언어 정보를 바탕으로 로케일을 획득하기로 한다. 이 정보는 인터넷 익스플로러의 경우 "도구 - 인터넷 옵션 - 언어"를 차례대로 선택하여 설정할 수 있다. 그리고 넷스케이프의 경우 "Edit - Preference - Navigator - Languages"를 따라가며 선택할 수 있다.


    <인터넷 익스플로러에서 언어 설정하기>



    <넷스케이프에서 언어 설정하기>


    언어 및 국가(영역)명 정보

    각 사용자별로 위치한 나라의 이름이나, 사용자가 원하는 언어의 이름을 애플리케이션에 하드 코드로 저장하지 않고, GDK나 JDK로부터 직접 사용할 수 있다. 언어명의 경우 "Korean"과 같은 한국어의 영문 Display명 뿐 아니라, "한국어"(Native Display Name), "ko"(ISO 639표준: 2 char), "kor"(ISO 639-2 표준: 3 char)과 같이 여러 가지 포맷으로 획득할 수 있다. 국가명 역시 "Korea"뿐 아니라 "한국"(오라클 Native Display Name), "대한민국"(Java Native Display Name), "KR"(ISO3166) 등 여러 가지 포맷으로 얻어낼 수 있다.

    Oracle GDK

    오라클 GDK의 API를 통해 오라클 고유의 언어명과 국가명을 획득할 수 있다. 언어명은 특히 오라클의 번역된 메시지 리소스를 저장하는 파일명에 이용되고 있다. $ORACLE_HOME/rdbms/mesg 디렉토리에는 원 파일 명에 언어명이 붙어 있는 메시지 파일들이 많이 저장되어 있다.
    2004-03-08  오후 02:28             5,632 dbvus.msb
    2004-03-08 오후 02:27 5,632 dbvar.msb (Arabic)
    2004-03-08 오후 02:27 6,144 dbvca.msb (Catalan)
    2004-03-08 오후 02:27 6,144 dbvcs.msb (Czech)
    2004-03-08 오후 02:27 6,144 dbvd.msb (German)
    2004-03-08 오후 02:27 5,632 dbvdk.msb (Danish)
    2004-03-08 오후 02:27 6,144 dbve.msb (Spanish)
    2004-03-08 오후 02:27 6,144 dbvel.msb (Greek)
    2004-03-08 오후 02:27 6,144 dbvf.msb (French)
    2004-03-08 오후 02:27 6,144 dbvhu.msb (Hungraian)
    2004-03-08 오후 02:27 6,144 dbvi.msb (Italian)
    2004-03-08 오후 02:28 5,632 dbviw.msb (Hebrew)
    2004-03-08 오후 02:27 5,632 dbvja.msb (Japanese)
    2004-03-08 오후 02:28 5,632 dbvko.msb (Korean)
    2004-03-08 오후 02:28 5,632 dbvn.msb (Norwegian)
    ....
    // 언어명 정보
    <td width="48%">
    <TABLE BORDER=0>
    <TR><TD>Oracle</TD><TD><%=LocaleMapper.getOraLanguage(javaLocale)%></TD></TR>
    <TR><TD>Oracle Short Name</TD><TD><%=oraLocaleInfo.getShortLanguage()%></TD></TR>
    <TR><TD>Display Name</TD><TD><%=oraUSDisplayLocaleInfo.getDisplayLanguage(javaLocale)%></TD></TR>
    <TR><TD>Display Name(Native)</TD><TD><%=oraDisplayLocaleInfo.getDisplayLanguage(javaLocale)%></TD></TR>
    </TABLE>
    </td>
    ...
    // 영역 이름 정보
    <td width="48%">
    <%
    String oraTerr = oraLocaleInfo.getTerritory();
    %>
    <TABLE BORDER=0>
    <TR>
    <TD>Oracle Territory Name</TD><TD><%=oraTerr%></TD>
    </TR>
    <TD>Display Country</TD><TD><%=oraUSDisplayLocaleInfo.getDisplayCountry(javaLocale)%></TD>
    </TR>
    <TD>Display Country(Native)</TD><TD><%=oraDisplayLocaleInfo.getDisplayCountry(javaLocale)%></TD>
    </TR>
    <TD>Display Territory</TD><TD><%=oraUSDisplayLocaleInfo.getDisplayTerritory(oraTerr)%></TD>
    </TR>
    <TD>Display Territory(Native)</TD><TD><%=oraDisplayLocaleInfo.getDisplayTerritory(oraTerr)%></TD>
    </TR>
    </TABLE>
    </td>

    번역의 묘는 다음 마지막 연재에서 느낄 수 있으니 기대하시라. 오라클은 소프트웨어 업체 중 가장 진보적인 번역 시스템을 갖추고 있다.

    Java

    Java의 경우 ISO 표준명에 따른 언어 및 국가 기호를 따르고 있다. 오라클도 자바 클래스 리소스(java.utill.ResourceBundle 객체)나 프로퍼피 파일(.properties)의 경우 자바 애플리케이션에 의해 이용되므로, 리소스 명에 붙은 언어 및 국가명을 ISO 표준 기반으로 사용하고 있다. 앞서 얻어낸 Java Locale 객체를 기반으로 다음과 같이 언어 및 국가명을 얻어낼 수 있다.

    //언어명 정보:
    <TABLE BORDER=0>
    <TR><TD>ISO639(2-char)</TD><TD><%=javaLocale.getLanguage()%></TD></TR>
    <TR><TD>ISO639-2(3-char)</TD><TD><%=javaLocale.getISO3Language()%></TD></TR>
    <TR><TD>Display Name</TD><TD><%=javaLocale.getDisplayLanguage(Locale.US)%></TD></TR>
    <TR><TD>Display Name(Native)</TD><TD><%=javaLocale.getDisplayLanguage(javaLocale)%></TD></TR>
    </TABLE>

    //영역(국가)명 정보:
    <TABLE BORDER=0>
    <TR><TD>Java Territory</TD><TD><%=LocaleMapper.getJavaTerrFromOraTerr(oraTerr)%></TD></TR>
    <TR><TD>ISO 3166(2-char)</TD><TD><%=javaLocale.getCountry()%></TD></TR>
    <TR><TD>ISO 3166(3-char)</TD><TD><%=javaLocale.getISO3Country()%></TD></TR>
    <TR><TD>Display Country</TD><TD><%=javaLocale.getDisplayCountry(Locale.US)%></TD></TR>
    <TR><TD>Display Country(Native)</TD><TD><%=javaLocale.getDisplayCountry(javaLocale)%></TD></TR>
    </TABLE>
  • OraDateFormat.getDefaultDateFormatPattern(OraDateFormat.DEFAULT,oraLocaleInfo) : 한국의 경우 "RR/MM/DD" 반환

  • 다음은 오라클 로케일 정보(좌측)과 자바 로케일 정보(우측) 호출 결과를 보여준다.



    날짜 및 달력 정보

    Oracle GDK

    여러분들은 java.text.DateFormat 객체를 이용하여 날짜를 스트링으로 변경하는 애플리케이션 코드를 작성해 보았을 것이다. java.text.SimpleDateFormat 객체를 이용하면 로케일에 관계없이 원하는 날짜 형식을 출력할 수 있고, java.text.DateFormat 객체로부터는 직접 로케일을 입력하여 원하는 로케일의 기본 날짜 형식을 얻어낼 수 있다. 그런데 같은 로케일이라고 해도 오라클 데이터베이스를 위한 날짜 형식은 이런 JDK의 날짜 형식과 일치하지 않는다. 나라별로 꼭 정확히 같은 형식의 날짜 형식을 표준으로 정해 놓았다고 볼 수도 없기 때문이다. 예를 들어 2005년 9월 20일을 "2005-9-20"이라고 표기하든, "2005/9/20"이라고 표기하든 연월일의 순서만 맞으면 우리 나라에서 사용되는 데 별 지장이 없다.

     오라클 소프트웨어를 위한 날짜 정보는 오라클의 소프트웨어에 포함이 되어 있으며, 한국어의 경우 일반적인 오라클의 표기법에 "연월일"의 순서를 한국에 맞게 조합한 결과라고 볼 수 있겠다. SQLPLUS같은 클라이언트에서 질의문을 생성할 때 사용할 날짜 포맷은 Oracle GDK에 의존하는 것이 좋다. Oracle GDK는 java.text.DateFormat 객체와 유사한 형식을 가진 OraDateFormat이라는 객체를 제공하며, 그 사용방법은 거의 동일하다. 한 가지 더 편리한 점은 TO_DATE와 같은 함수에서 사용할 수 있는 날짜 형식 또한 쉽게 얻을 수 있다는 것이다.

    // 날짜, 날짜시간, 시간 표기 정보

    OraDateFormat oradefaultDateFormat = OraDateFormat.getDateInstance(OraDateFormat.DEFAULT,oraLocaleInfo);
    OraDateFormat orasimpleDateFormat = OraDateFormat.getDateInstance(OraDateFormat.SHORT,oraLocaleInfo);
    OraDateFormat oralongDateFormat = OraDateFormat.getDateInstance(OraDateFormat.LONG,oraLocaleInfo);
    OraDateFormat oradefaultDateTimeFormat = OraDateFormat.getDateTimeInstance(OraDateFormat.DEFAULT,oraLocaleInfo);
    OraDateFormat orashortDateTimeFormat = OraDateFormat.getDateTimeInstance(OraDateFormat.SHORT,oraLocaleInfo);
    OraDateFormat oralongDateTimeFormat = OraDateFormat.getDateTimeInstance(OraDateFormat.LONG,oraLocaleInfo);
    OraDateFormat orashortTimeFormat = OraDateFormat.getTimeInstance(oraLocaleInfo);
    ...
    <TD colspan=2><B>[Date Format]</B></td>
    </TR>
    <TR>
    <TD>Default</TD>
    <TD><%=OraDateFormat.getDefaultDateFormatPattern(OraDateFormat.DEFAULT,oraLocaleInfo)%><BR>
    <B><%=oradefaultDateFormat.format(testDate)%></B></TD>
    ...

    <TR>
    <TD colspan=2><B>[Date/Time Format]</B></td>
    </TR>
    <TR>
    <TD>Default</TD>
    <TD><%=OraDateFormat.getDefaultDateTimeFormatPattern(OraDateFormat.DEFAULT,oraLocaleInfo)%><BR>
    <B><%=oradefaultDateTimeFormat.format(testDate)%></B></TD>
    </TD>
    </TR>
    ....
    <TD colspan=2><B>[Time Format]</B></td>
    </TR>
    <TR>
    <TD>Default</TD>
    <TD><%=OraDateFormat.getDefaultTimeFormatPattern(oraLocaleInfo)%><BR>
    <B><%=orashortTimeFormat.format(testDate)%></B></TD>
    </TD>
    </TR>


    // 년/월/일/요일 등 날짜 표기에 사용되는 단위 및 기호 정보
    <%
    OraDateFormatSymbols dateSymbols = new OraDateFormatSymbols(oraLocaleInfo);
    %>
    <TABLE BORDER=0>
    <TR>
    <TD>Months(From Jan.)</TD>
    <TD>
    <% // 월 이름
    String[] months = dateSymbols.getMonths(); //1월, 2월, 3월 , January, February,..
    for(int i=0; i < months.length; i++)
    {
    .....
    <% // 짧은 월 이름
    String[] shortMonths = dateSymbols.getShortMonths(); // 한국어는 동일, Jan, Feb
    for(int i=0; i < shortMonths.length; i++)
    {
    ...
    <% // 요일명
    String[] weekDays = dateSymbols.getWeekdays(); // 일요일, 월요일,
    for(int i=0; i < weekDays.length; i++)
    {
    ..
    <% // 짧은 요일명
    String[] shortWeeks = dateSymbols.getShortWeekdays(); // 일, 월, 화
    for(int i=0; i < shortWeeks.length; i++)
    {
    ..
    <% // AM/PM 표기 방법
    String[] ampmStrs = dateSymbols.getAmPmStrings(); // 오전, 오후
    for(int i=0; i < ampmStrs.length; i++)
    {
    ..
    <% // 연대 표기 방법
    String[] eras = dateSymbols.getEras(); // 기원전, 서기
    ..

    // 한 주일의 첫 번 째 요일
    int orafirstDayOfWeek = oraLocaleInfo.getStartDayOfTheWeek(); // 한국은 일요일, 프랑스는 월요일
    switch(orafirstDayOfWeek)
    {
    case 0:
    out.println("Sunday");
    break;

    Java

    JDK에서 날짜 포맷을 얻어내는 방법은 비교적 잘 알려져 있다. 앞서 Oracle GDK를 설명하면서 언급한 java.text.DateFormat 객체를 사용하면 해당 로케일에 맞는 날짜 포맷을 쉽게 얻어낼 수 있다.  여기서는 구체적인 소스 코드를 소개하겠다. 아마, 이번 회에 나온 소스 코드들 중 실제 애플리케이션에서 가장 빈번하게 사용되는 코드일 것이다.

    <%
    DateFormat simpleDateFormat = DateFormat.getDateInstance(DateFormat.SHORT,javaLocale);
    DateFormat mediumDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM,javaLocale);
    DateFormat longDateFormat = DateFormat.getDateInstance(DateFormat.LONG,javaLocale);
    DateFormat fullDateFormat = DateFormat.getDateInstance(DateFormat.FULL,javaLocale);

    DateFormat shortDateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,javaLocale);
    DateFormat mediumDateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM,javaLocale);
    DateFormat longDateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG,javaLocale);
    DateFormat fullDateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,javaLocale);

    DateFormat shortTimeFormat = DateFormat.getTimeInstance(DateFormat.SHORT,javaLocale);
    DateFormat mediumTimeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM,javaLocale);
    DateFormat longTimeFormat = DateFormat.getTimeInstance(DateFormat.LONG,javaLocale);
    DateFormat fullTimeFormat = DateFormat.getTimeInstance(DateFormat.FULL,javaLocale);

    %>
    ...
    <TABLE BORDER=0>
    <TR><TD colspan=2><B>[Date Format]</B></TD></TR>
    <TR><TD>Short</TD><TD><%=simpleDateFormat.format(testDate)%></TD></TR>
    <TR><TD>Medium</TD><TD><%=mediumDateFormat.format(testDate)%></TD></TR>
    <TR><TD>Long</TD><TD><%=longDateFormat.format(testDate)%></TD></TR>
    <TR><TD>Full</TD><TD><%=fullDateFormat.format(testDate)%></TD></TR>
    </TABLE>
    <BR>
    ...

    <% // 날짜 표기에 필요한 각종 기호들을 포함한 객체
    DateFormatSymbols jdateSymbols = new DateFormatSymbols(javaLocale);
    %>
    <TR>
    <TD>Months(From Jan.)</TD>
    <TD>
    <% // "월" 이름
    months = jdateSymbols.getMonths();
    for(int i=0; i < months.length; i++)
    {
    ...
    <% // 짧은 "월" 이름
    shortMonths = jdateSymbols.getShortMonths();
    for(int i=0; i < shortMonths.length; i++)
    {
    ..
    <% // 짧은 요일 이름
    shortWeeks = jdateSymbols.getShortWeekdays();
    ...
    <% // 오전 오후를 표기하는 기호
    ampmStrs = jdateSymbols.getAmPmStrings();

    <% // 기원전, 후를 표기하는 기호
    eras = jdateSymbols.getEras();

    <% // 달력 객체 획득
    Calendar localCalendar = Calendar.getInstance(javaLocale);
    %>
    <% // 한 주의 첫 번 째 요일
    int firstDayOfWeek = localCalendar.getFirstDayOfWeek();
    switch(firstDayOfWeek)
    {
    case Calendar.SUNDAY:
    out.println("Sunday");
    break;

    ..
    <TD>Minumum day of the first week of a month</TD>
    <TD><B><%=localCalendar.getMinimalDaysInFirstWeek()%></B></TD>


    이상과 같이 가볍게 객체들의 메소드를 호출함으로써 해당 로케일의 날짜 및 달력 정보를 아주 쉽게 얻어낼 수 있다. 다음은 위의 메소드를 호출한 결과로 왼쪽은 Oracle GDK의 API를 사용한 결과이고, 오른쪽은 JDK를 이용한 결과이다. 오라클 데이터베이스에서는 특별히 TO_DATE를 이용하여 날짜 포맷을 지정해 주지 않으려면 왼쪽의 Oracle GDK가 보여주는 기본 날짜 형식을 사용해야 한다. 아래의 예는 프랑스어를 쓰는 프랑스 지역(FRENCH_FRANCE)의 날짜 포맷을 보여주고 있다.


    <FRENCH_FRANCE: 프랑스의 날짜 및 시간 표기 정보>

    따라서, 해당 로케일 정보를 가진 클라이언트에서 날짜를 입력하려면 TO_DATE를 사용하여 정확히 포맷을 제시하든지 아니면 기본 형식(DD/MM/RR)을 사용해야 한다. 다음의 예를 살펴보자. NLS_LANG을 아래와 같이 설정하여 프랑스어를 사용하는 프랑스 영역으로 설정하자. 두 번 째 SQL 문장은 프랑스에 맞는 기본 날짜 형식을 TO_DATE  없이 입력한 것으로, 문제없이 입력되는 것을 확인할 수 있다.
    C:\Documents and Settings\jwryoo>set NLS_LANG=FRENCH_FRANCE.WE8ISO8859P1
    SQL> insert into dtest values('2005/09/20');
    insert into dtest values('2005/09/20')
    *
    ERREUR α la ligne 1 :
    ORA-01861: le littΘral ne concorde pas avec le format chaεne de caractΦres


    SQL> insert into dtest values('20/09/05');

    1 ligne crΘΘe.

    SQL> insert into dtest values(TO_DATE('2005/09/20','RRRR-MM-DD'));

    1 ligne crΘΘe.

    시간대(Timezone) 정보

    Oracle GDK

    오라클 GDK의 시간대 정보를 얻어내는 API는 특히 JDK와 비교했을 때 간략하다. JDK의 경우 해당 시스템의 기본 시간대 정보를 얻어내는 방법은 쉽지만, 특정 로케일의 시간대 정보를 얻어내는 것은 그 시간대를 상징하는 문자열 이름(Asia/Seoul)을 알아야 하므로 불편하다.

    Timezone 객체를 얻어내고 사용할 때 주의해야 할 것은 해당 시간대가 DST(Daylight Saving Time)을 사용하고 있는지에 대한 여부를 확인하는 것이다. 그리고 영역이 아주 넓은 경우 한 로케일에 속하는 시간대가 매우 많을 수 있다. 예를 들어 AMERICAN_AMERICA의 경우 무려 8개의 Timezone 객체를 얻어낼 수 있으며, 이 중 "Pacific/Honolulu"를 제외한 나머지는 모두 DST를 사용한다.

    <%      
    TimeZone[] localTimezones = oraLocaleInfo.getLocalTimeZones();
    for(int i=0; i < localTimezones.length; i++)
    {
    %>
    <TABLE>
    <TR><TD colspan=2><B>[<%=localTimezones[i].getID()%>]</B></TD></TR>
    <TR><TD>Display Name: </TD><TD><%=oraDisplayLocaleInfo.getDisplayTimeZone(localTimezones[i])%></TD></TR>
    <%
    if(localTimezones[i].useDaylightTime())
    {
    %>
    <TR><TD colspan="2" style="text-decoration: underline;">Standard Time</TD><TR>
    <%
    }
    %>
    <TR><TD>Short Name</TD><TD><%=localTimezones[i].getDisplayName(false,TimeZone.SHORT,javaLocale)%></TD></TR>
    <TR><TD>Long Name(English)</TD><TD><%=localTimezones[i].getDisplayName(false,TimeZone.LONG,Locale.US)%></TD></TR>
    <TR><TD>Long Name(Native)</TD><TD><%=localTimezones[i].getDisplayName(false,TimeZone.LONG,javaLocale)%></TD></TR>
    <TR><TD colspan=2>&nbsp;</TD></TR>
    <%
    if(localTimezones[i].useDaylightTime())
    {
    %>
    <TR><TD colspan="2" style="text-decoration: underline;">Daylight Saving Time</TD><TR>
    <TR><TD>Short Name</TD><TD><%=localTimezones[i].getDisplayName(true,TimeZone.SHORT,javaLocale)%></TD></TR>
    <TR><TD>Long Name(English)</TD><TD><%=localTimezones[i].getDisplayName(true,TimeZone.LONG,Locale.US)%></TD></TR>
    <TR><TD>Long Name(Native)</TD><TD><%=localTimezones[i].getDisplayName(true,TimeZone.LONG,javaLocale)%></TD></TR>
    <%
    }
    %>
    </TABLE>

    다음의 예는 아일랜드의 시간대 정보를 나타낸다. 영국과 같이 Greenwich 표준 시간대임을 알 수 있다. 유럽과 미국은 DST의 시작 및 종료 날짜가 조금씩 다르므로 Timezone.inDaylightTime(Date date)"과 같은 메소드를 통해 확인하는 것이 필요하다. JDK의 경우 따로 Timezone 객체를 사용하는 예를 보여줄 필요는 없을 것 같다. 위의 오라클 GDK의 예제가 반환하는 Timezone 객체는 JDK의 java.util.Timezone 객체로 결국 차이점은 해당 로케일에 속한 Timezone 객체를 얼마나 쉽게 얻어내는가 뿐이다.


    <ENGLISH_IRELAND : 아일랜드의 시간대 정보>

    통화 기호 및 숫자 표기 정보

    Oracle GDK

    Oracle GDK는 특별히 Currency 객체를 가지고 있지 않지만, JDK와 동등한 결과를 보여주는 데 문제가 없다. 그리고, 부가적으로 두 가지 이상의 통화가 사용되고 있는 경우까지 다룰 수 있다. 개인적으로는 시간대와 같이 java.util.Currency 객체들을 반환하는 것이 더 좋다고 생각이 되긴 하지만, 현재 상태로도 통화 기호라든지 통화명을 뽑아 쓰는 데에는 문제없다.

    // 통화 정보 획득

    <STRONG>Currency</STRONG>
    </td>
    <td width="48%">
    <%
    String oraCurrencySym = oraLocaleInfo.getCurrencySymbol();
    %>
    <TABLE border=0>
    <TR><TD>Currency Symbol</TD><TD><B><%=oraCurrencySym%></B></TD></TR>
    <TR><TD>Display Currency</TD><TD><B><%=oraDisplayLocaleInfo.getDisplayCurrency(oraCurrencySym)%></B></TD></TR>
    <TR><TD>Currencies</TD><TD><B>
    <%
    String[] currencies = oraLocaleInfo.getLocalCurrencies();
    for(int i=0; i < currencies.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=currencies[i]%>
    <%
    }
    %>
    </B></TD></TR>
    <TR><TD>Default Currency Pattern</TD><TD><B><%=OraNumberFormat.getDefaultCurrencyFormatPattern(oraLocaleInfo)%></B></TD></TR>
    <TR><TD>Currency Example</TD><TD>
    <%
    OraNumberFormat oracurrencyFormat = OraNumberFormat.getCurrencyInstance(oraLocaleInfo);
    %>
    <B>
    <%=oracurrencyFormat.format(123456789L)%><BR>
    <%=oracurrencyFormat.format(123456789.1234D)%><BR>
    <%=oracurrencyFormat.format(-123456789.1234D)%>
    </B>
    </TD></TR>
    </TABLE>
    </td>

    // 숫자 형식 정보

    <TR><TD>Default Number Pattern</TD><TD><B><%=OraNumberFormat.getDefaultNumberFormatPattern(oraLocaleInfo)%></B></TD></TR>
    <TR><TD>Number Example</TD><TD><B>
    <%
    OraNumberFormat oraNumberFormat = OraNumberFormat.getNumberInstance(oraLocaleInfo);
    %>
    <%=oraNumberFormat.format(123456789L)%><BR>
    <%=oraNumberFormat.format(123456789.1234D)%><BR>
    <%=oraNumberFormat.format(-123456789L)%><BR>

    <%
    OraDecimalFormatSymbols oraDecSymbols = new OraDecimalFormatSymbols(oraLocaleInfo);
    %>
    </B>
    </TD></TR>
    <TR><TD>Decimal Separator</TD><TD><B><%=oraDecSymbols.getDecimalSeparator()%></B></TD></TR>
    <TR><TD>Grouping Used</TD><TD><B><%=((oraNumberFormat.isGroupingUsed()==true)?"Yes":"No")%></B></TD></TR>
    <TR><TD>Grouping Separator</TD><TD><B><%=oraDecSymbols.getGroupingSeparator()%></B></TD></TR>
    </TABLE>

    Java

    Oracle GDK에 비해 DecimalFormatSymbols 객체가 아주 풍부한 내용을 담고 있음을 알 수 있다. 다국어 기반의 개발자라면 숫자를 표기할 때 "똑같은 소스 코드로 해당 나라의 형식에 맞게끔 출력될 수 있는" 프로그램을 만들 수 있어야 할 것이다. 한국어만을 기반으로 하는 것이 아니라, 중국, 일본, 태국 등 아시아, 그리고 유럽에 걸쳐 서비스를 하려면, 여기에 사용되는 클래스들은 항상 머릿속에 넣어두어야 한다.

    // 통화 기호 정보

    <%
    Currency javaCurr = Currency.getInstance(javaLocale);
    %>
    <TABLE border=0>
    <TR><TD>Currency Symbol</TD<TD><B><%= javaCurr.getSymbol()%></B></TD></TR>
    <TR><TD>Currency Name(ISO4217)</TD<TD><B><%= javaCurr.toString()%></B></TD></TR>
    <TR><TD>Currency Code</TD<TD><B><%= javaCurr.getCurrencyCode()%></B></TD></TR>
    <TR><TD>Currency Example</TD<TD><B>
    <%
    NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(javaLocale);
    %>
    <%=currencyFormat.format(123456789L)%><BR>
    <%=currencyFormat.format(-123456789L)%><BR>
    <%=currencyFormat.format(-123456789.55)%>

    // 숫자 형식 정보
            <TABLE BORDER=0>
    <TR><TD>Number Example</TD><TD>
    <B>
    <%
    NumberFormat numberFormat = NumberFormat.getNumberInstance(javaLocale);
    %>
    <%=numberFormat.format(123456789L)%><BR>
    <%=numberFormat.format(123456789.1234D)%><BR>
    <%=numberFormat.format(-123456789L)%>

    </TD>
    </TR>
    <TR><TD>Integer Example</TD><TD><B>
    <%
    NumberFormat intFormat = NumberFormat.getIntegerInstance(javaLocale);
    %>
    <%=intFormat.format(123456789L)%><BR>
    <%=intFormat.format(-123456789L)%>
    </B></TD></TR>
    <TR><TD>
    Percentage Example</TD><TD><B>
    <%
    NumberFormat pcFormat = NumberFormat.getPercentInstance(javaLocale);
    %>
    "0.4" = <%=pcFormat.format(0.4)%><BR>
    "0.9999" = <%=pcFormat.format(0.9999)%><BR>
    "-0.5" = <%=pcFormat.format(-0.5)%>
    </B></TD></TR>
    <%
    DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(javaLocale);
    %>
    <TR><TD>Decimal Separator</TD><TD><B><%=decSymbols.getDecimalSeparator()%></B></TD></TR>
    <TR><TD>Grouping Separator</TD><TD><B><%=decSymbols.getGroupingSeparator()%></B></TD></TR>
    <TR><TD>Zero Digit</TD><TD><B><%=decSymbols.getZeroDigit()%></B></TD></TR>
    <TR><TD>Percent</TD><TD><B><%=decSymbols.getPercent()%></B></TD></TR>
    <TR><TD>Mil-Percent</TD><TD><B><%=decSymbols.getPerMill()%></B></TD></TR>
    <TR><TD>NaN(Not A Number)</TD><TD><%=decSymbols.getNaN()%></TD></TR>
    <TR><TD>Infinity</TD><TD><B><%=decSymbols.getInfinity()%></B></TD></TR>
    <TR><TD>Minus Sign</TD><TD><B><%=decSymbols.getMinusSign()%></B></TD></TR>
    <TR><TD>Pattern Separator</TD><TD><B><%=decSymbols.getPatternSeparator()%></B></TD></TR>
    <TR><TD>Decimal Separator</TD><TD><B><%=decSymbols.getDecimalSeparator()%></B></TD></TR>
    <TR><TD>Monetary Decimal Separator</TD><TD><B><%=decSymbols.getMonetaryDecimalSeparator()%></B></TD></TR>
    </TABLE>

    다음은 독일어를 사용하는 독일 지역(GERMAN_GERMANY)의 환율 및 숫자 표기 정보를 나타낸 것이다. 우리와는 정 반대로 소수점 표기에 콤마(,)를, 천 단위로 숫자를 그룹핑할 때 마침표(.)를 사용하고 있는 것을 볼 수 있다. 때에 따라서는 정말 엉뚱하게 숫자의 값이 전달될 수 있으므로 특히 조심해야 한다는 것을 알려주는 좋은 예이다.


    <GERMAN_GERMANY: 독일의 환율 및 숫자 표기 정보>

    캐릭터셋 정보

    Oracle GDK

    Oracle GDK는 해당 로케일의 언어를 저장할 수 있는 오라클 데이터베이스의 캐릭터셋을 쉽게 알아낼 수 있는 방법을 각 운영체제 별로 제공한다. 그렇다고 UNIX 운영체제에 WINDOWS 코드페이지 기반의 캐릭터셋을 설치할 수 없다는 것은 아니다. 한국어의 경우 UNIX 캐릭터셋으로 KO16KSC5601과 AL32UTF8이 출력되지만, 그렇다고 UNIX 운영체제에 KO16MSWIN949 캐릭터셋의 데이터베이스 인스턴스를 생성할 수 없는 것은 아니다.

            <B>ALL:</B><BR>
    <%
    // 모든 캐릭터셋
    String[] allCharsets = oraLocaleInfo.getLocalCharacterSets();
    for(int i=0; i < allCharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=allCharsets[i]%>
    <%
    }
    %>
    <BR><BR>
    <B>UNIX:</B><BR>
    <%
    // UNIX 운영체제
    String[] unixCharsets = oraLocaleInfo.getLocalCharacterSets(LocaleMapper.UNIX);
    for(int i=0; i < unixCharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=unixCharsets[i]%>
    <%
    }
    %>
    <BR><BR>
    <B>WINDOWS:</B><BR>
    <%
    // Windows 운영체제
    String[] winCharsets = oraLocaleInfo.getLocalCharacterSets(LocaleMapper.WINDOWS);
    for(int i=0; i < winCharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=winCharsets[i]%>
    <%
    }
    %>
  • export NLS_LANG=KOREAN_KOREA.KO16MSWIN949 (O)
  • export NLS_LANG=MS949 (X)
  • Reader r = new InputStreamReader(new FileInputStream("file.txt"),"KO16MSWIN949"); (X)
  • Reader r = new InputStreamReader(new FileInputStream("file.txt"),"MS949"); (O)
  • Java

    일반적인 다국어 지원 애플리케이션에서는 오라클 캐릭터셋보다 IANA 표준 캐릭터셋 이름이나 자바 캐릭터셋 이름들이 더 빈번히 사용될 것이다. KO16MSWIN949라는 캐릭터셋명은 오로지 데이터베이스 인스턴스를 생성할 때나, NLS_LANG 변수값을 지정할 때 사용될 뿐, 다른 애플리케이션에서는 MS949라는 자바 캐릭터셋 명을 사용해야 한다.
    아래의 소스 코드는 자바 로케일 객체를 기반으로 IANA 또는 자바 캐릭터셋 이름들을 출력하는 코드이다. Java 란에 출력이 되지만, 소스 코드를 들여다보면 Oracle GDK를 이용했음을 알 수 있다. Oracle GDK는 이렇듯 오라클의 로케일 정보와 JDK의 로케일 정보에 대해 훌륭한 매핑을 제공하고 있다.

          <B>[IANA Standard Charsets]</B><BR>
    <B>UNIX:</B><BR>
    <%
    String[] unixIANAcharsets = LocaleMapper.getIANACharSetFromLocale(LocaleMapper.UNIX,javaLocale);
    for(int i=0; i < unixIANAcharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    String fontUrl = "/tools/locale/fonttest.jsp?charset="+unixIANAcharsets[i]+"&lang="+javaLocale.getLanguage()+"&cont="+javaLocale.getCountry();
    %>
    <!--<a href="<%=fontUrl%>" target=_blank><%=unixIANAcharsets[i]%></a>-->
    <%=unixIANAcharsets[i]%>
    <%
    }
    %>
    <BR><BR>
    <B>WINDOWS:</B><BR>
    <%
    String[] winIANAcharsets = LocaleMapper.getIANACharSetFromLocale(LocaleMapper.WINDOWS,javaLocale);
    for(int i=0; i < winIANAcharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=winIANAcharsets[i]%>
    <%
    }
    %>
    <BR><BR>
    <B>EMAIL_UNIX:</B><BR>
    <%
    String[] unixEmailIANAcharsets = LocaleMapper.getIANACharSetFromLocale(LocaleMapper.EMAIL_UNIX,javaLocale);
    for(int i=0; i < unixEmailIANAcharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=unixEmailIANAcharsets[i]%>
    <%
    }
    %>
    <BR><BR>
    <B>EMAIL_WINDOWS:</B><BR>
    <%
    String[] winEmailIANAcharsets = LocaleMapper.getIANACharSetFromLocale(LocaleMapper.EMAIL_WINDOWS,javaLocale);
    for(int i=0; i < winEmailIANAcharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=winEmailIANAcharsets[i]%>
    <%
    }
    %>

    <BR><BR>
    <B>[Java Charsets]</B><BR>
    <B>JAVA_UNIX:</B><BR>
    <%
    for(int i=0; i < unixCharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=LocaleMapper.getJavaCharacterSet(LocaleMapper.ORACLE,unixCharsets[i])%>
    <%
    }
    %>

    <BR><BR>
    <B>JAVA_WINDOWS:</B><BR>
    <%
    for(int i=0; i < winCharsets.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=LocaleMapper.getJavaCharacterSet(LocaleMapper.ORACLE,winCharsets[i])%>
    <%
    }
    %>

    다음 그림은 위의 소스 코드의 실행 결과로, 일본어를 사용하는 일본에 대한 캐릭터셋 정보를 나타내 주고 있다.


    <JAPANESE_JAPAN:  일본에 대한 캐릭터셋 정보>

    기타 정보

    Oracle GDK

    기타 Oracle GDK에서는 해당 언어를 좌측에서 우측으로 표기할 지, 우측에서 좌측으로 표기할 지를 판별할 수 있는 메소드와 함께 SQL 문장에서 사용할 수 있는 정렬 방식의 이름을 제공해 준다.

    // 널리 알려진 대로 Arabic의 경우 Right To Left 표기 방식이다.
    <%
    int wDir = oraLocaleInfo.getWritingDirection();
    %>
    <%=((wDir==0)?"LEFT to RIGHT":"RIGHT to LEFT")%>

    // 정렬 방식
    <%
    String[] sorts = oraLocaleInfo.getLocalLinguisticSorts();
    for(int i=0; i < sorts.length; i++)
    {
    if(i > 0)
    out.println(",");
    %>
    <%=sorts[i]%>
    <%
    }
    %>

    다음은 KOREAN_KOREA 로케일에 대해 실행한 결과로, 한글은 물론 좌측에서 우측으로 표기하는 것이 맞다. 그리고 한글 정렬을 위해서는 "KOREAN_M"을 사용할 수 있는데, 이에 대해서는 연재 첫 회를 참고하기 바란다. 하지만, 기본적으로는 BINARY 정렬이 된다는 것을 이 API로는 알기 어렵다. 그리고 로케일에 무관하게 "UNICODE_BINARY" 정렬 방식을 사용할 수도 있다.


    <한국어의 쓰기 순서와 정렬 방식>
  • 바이트 코드와 실제 글자 매핑 : UTF8의 " 236 152 164 235 157 188 237 129 180" == EUC-KR "191 192 182 243 197 172" =="오라클"
  • 해당 글자가 특정 캐릭터셋에서 지원되는지 여부를 확인하는 툴 : oracle.i18n.util.OraSQLUtil를 이용하여 간단히 구현할 수 있다.
  • 캐릭터셋에 지원되지 않는 글자를 삽입하기 위해 유니코드 이스케이프로 변환하는 유틸리티 : racle.i18n.util.OraSQLUtil.escapeUNISTR로 간단히 구현할 수 있다. "오라클" == UNISTR('\c624\b77c\d074')
  • 파일 인코딩을 변환하는 유틸리티(현재 WINDOWS949로 인코딩된 일반 한글 텍스트 파일을 UTF8로 변환하기)

  • 정리하며

    기타 있으면 유용한 유틸리티

    이 외에도 다음과 같은 유틸리티를 마련하면 매우 유용하다

    전체 소스

    자, 그럼 이제 전체 소스를 다운로드 받아 본인의 JSP 엔진에 설치해 보는 과정을 해 보는 것은 어떨까? 한 번 설치해 두면 두고두고 써먹을 수 있는 것 중 하나가 바로 이 NLS 정보 열람 유틸리티이다.

    실행하기 위해 필요한 라이브러리는 이미 "개발 준비" 섹션에 소개한 바 있다. 단순히 설치로만 그칠 게 아니라, 이리저리 변경해 보고 Oracle GDK와 JDK의 레퍼런스 문서를 보며 사용되지 않은 API를 추가해 보는 것도 재미와 가치를 더할 것이다.

    GDK는 독보적 존재

    Java 기반의 다국어 지원 애플리케이션 개발 킷이라는 것은 어떻게 보면 참 독보적인 존재이다. 공개 소프트웨어 개발 포럼에서나 개발될 것 같은 라이브러리인데, 상업용 소프트웨어 개발 회사가 리소스를 투자해 개발하고 배포하며 자사의 대표적인 소프트웨어에도 포함시키고 있다는 것은 대형 소프트웨어 업체가 가진 매력이자 힘이라고 볼 수 있다. 첫 회에 언급했듯 "한국어 버전", "중국어 버전"의 오라클 소프트웨어는 존재하지 않는다. 오로지 "지구 버전"의 오라클 소프트웨어가 있을 뿐이다. 글로벌 시대를 부정할 수는 없는 법, 갈 수록 GDK와 같은 라이브러리는 보이지 않는 곳에서 힘을 발휘할 것이다. 오라클 데이터베이스 없이도 즐겁게 사용할 수 있는 오라클의 로케일 정보 라이브러리, GDK를 이용하여 "즐프"하시기를 바라며 이만 마치겠다.


    Posted by 옹니미