UTF-8 정리

WebPrograming관련 : 2007.03.16 18:29

UTF-8 개발에 관한 정리입니다.

1. 모든 문서는 UTF-8 인코딩으로 저장되어야 합니다.

에디트 플러스의 경우 도구 -> 기본설정 -> 파일 부분에서 새 파일 형식을

UTF-8 로 해놓음으로써 새파일 작성시 UTF-8을 기본으로 작성할수 있고,

이미 다른 인코딩 타입에서 작성된 문서인 경우 내용을 모조리 Ctrl+C로 복사후

문서 -> 인코딩 변경 로드에서 UTF-8로 변경후 다시 붙여넣기 하면 됩니다.


이클립스의 경우 Package Explorer 에서 프로젝트에서 우측 버튼을 누른 후

Properties->Info->Text file encoding->Other 을 UTF-8 로 잡아주면 됩니다.

( 기존 다른 인코딩 타입에서 작성된 문서 내부 한글은 모조리 깨지게 됨 )


* ascii 로 작성된 자바스크립트 파일을 utf 문서에서 불러와 수행시킬 경우

자바스크립트 에러 나는 문제도 있더군요.(한글 주석이 영향을 줬을수도...)


2. jsp 파일 상단에는 다음과 같은 방식으로 UTF-8 설정합니다.

<%@ page contentType = "text/html;charset=utf-8" %>


3. 서블릿은 다음과 같은 방식으로 UTF-8을 설정합니다.

request.setCharacterEncoding("utf-8")


4. 자바스크립트에서 encodeURIComponent 처리 및 톰캣 server.xml 의 설정 변경

위 1,2,3번의 방식으로 하면 post 방식의 데이터는 잘 받지만

get 방식의 데이터는 한글이 깨집니다.

이와 같은 경우 자바스크립트의 encodeURIComponent 함수와

server.xml 을 이용하여 처리하면 됩니다.


4.1 server.xml

톰캣 폴더의 conf 폴더에는 server.xml 파일이 존재합니다.

에디터로 열어보면


예)

    <Connector port="8080" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />


값이 약간 틀릴수 있지만 초기 셋팅 값이 보통 저러하고,

Connector 은 초기에 2개가 있는데 8080 포트 부분을 수정하면 됩니다.

수정하는 방법은

URIEncoding="UTF-8" 을 추가하면 됩니다.


예)

    <Connector port="8080" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="utf-8" />


위와 같이 했다고 해결되는 것은 아닙니다. get 방식으로 데이터를 전송하면

자바 파일에서 확인 했을 경우 물음표 값만 넘어옵니다.


get 방식으로 보낼 경우 자바스크립트로 변환을 해줍니다.

예를 들어 "한글"을 자바스크립트에서

<SCRIPT>alert( encodeURIComponent("한글") )</SCRIPT>

처럼 encodeURIComponent 함수로 변환하게 되면 %ED%95%9C%EA%B8%80 으로

변환됩니다. 이 값을 주소에 "한글" 대신에 넣게 되면 자바에서 알아서 잘~ 받습니다 ^^


5. HttpURLConnection 을 통하여 UTF-8 로 된 URL 을 읽을 경우.

다른 부분은 동일하고, 

BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

로 변경합니다.


6. jsp -> bean 데이터 전송은 문제 없지만 jsp -> jsp 에서 한글이 깨진다면

받는 부분에서

 String test = request.getParameter("test ");

 test = new String(test .getBytes("8859_1"), "UTF-8");

처럼 변환해서 저장합니다.


ps. 참고 사이트

http://cafe.naver.com/phpinfo.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=434





utf-8 인코딩을 이용한 한글 url 처리
[ 조회수: 1989 ]
 

개요

  • utf-8 은 유니코드를 위한 가변길이 문자 인코딩 방식중 하나로 켄 톰프슨과 롭 파이크가 만들었다.
  • 유니코드 한문자를 표시하기 위해 1byte~4byte 까지 가변적으로 표현한다.
  • U+0000부터 U+007F 범위에 있는 아스키 문자들은 UTF-8에서 1바이트만으로 표시된다. 4바이트로 표현되는 문자는 모두 기본 다중언어 평면(BMP) 바깥의 유니코드 문자이며, 거의 사용되지 않는다.

구조

  • U+007F(0~127) 까지의 문자는 7비트 아스키 문자와 동일하게 표시된다.
  • U+007F 이후의 문자는 4byte 까지의 비트 패턴으로 표시되며 7비트 아스키 문자와 혼동되지 않게 하기 위해 모든 바이트의 최상위 비트는 1 이다.
코드 범위 UTF-16BE 표현 UTF-8 표현 설명
000000 ~00007F 00000000 0xxxxxxx 0xxxxxxx 아스키와 동일한 범위
000080 ~0007FF 00000xxx xxxxxxxx 110xxxxx 10xxxxxx 첫바이트는 110 으로 시작하고, 나머지 바이트는 10으로 시작함
000800 ~00FFFF xxxxxxxx xxxxxxxx 1110xxxx 10xxxxxx 10xxxxxx 첫바이트는 1110으로 시작하고 , 나머지는 10으로 시작함
010000 ~10FFFF 110110yy yyxxxxxx 110111xx xxxxxxxx 11110zzz 10zzxxxx 10xxxxxx 10xxxxxx UTF-16 "surrogate pair" 영역(yyyy=zzzzz -1)


utf-8 인코딩 예제

'위'(한글) --> U+C7404(유니코드)
U+0800부터 U+FFFF 사이의 영역에 있으므로, 표에 따라 1110xxxx 10xxxxxx 10xxxxxx 형식으로 인코딩
C704(16진수) --> 1100-0111-0000-0100(2진수) --> 11101100 10011100 10000100(utf-8 인코딩)
결과적으로 이 문자는 3바이트로 인코딩 되면 16진수로 표기하면 EC 9C 84가 된다.

(첫 128 문자는 1바이트로 표시되고, 그 다음 1920 문자 (판독 기호가 붙은 라틴 문자, 그리스 문자, 키릴 문자, 콥트 문자, 아르메니아 문자,
히브리 문자, 아랍 문자)는 2바이트로 표시되며, 나머지 문자들 중 BMP 안에 들어 있는 것은 3바이트, 아닌 것은 4바이트로 표시된다.)

관련자료

utf-8 체크

    1. 입력된 문자열이 utf-8로 인코딩 되었는지 하기 위해서는 utf-8 의 인코딩 패턴을 조사한다. 즉 바이트 배열의 패턴이 utf-8 인코딩 패턴(1110xxxx 10xxxxxx 10xxxxxx)과 맞는지를 비교한다.
    2. 000080 ~0007FF 범위의 110xxxxx 10xxxxxx 패턴 일 경우엔 중복되는 코드값으로 인해 utf-8 인코딩인지 아닌지 체크하는것은 사실상 불가능하다.
      110xxxxx 10xxxxxx 패턴의 utf-8 판독

      110xxxxx 10xxxxxx 패턴의 코드일 경우 utf-8로 인코딩 되었는지 아닌지를 확인하는것은 사실상 불가능하다.
      하지만 utf-8로 인코딩 되면 아스키 영역은 1byte 한글은 3byte 내지는 4byte를 사용하므로 단순하게 생각하여 2 바이트 110xxxxx 10xxxxxx 패턴의 경우는 utf-8 인코딩이 아님을 유추할수 있다.
      판독 기호가 붙은 라틴 문자, 그리스 문자, 키릴 문자, 콥트 문자, 아르메니아 문자, 히브리 문자, 아랍 문자 등이 입력되면 판독 오류가 발생하겠지만 이는 무시해도 좋을듯 하다.

    3. Null 값의 경우 자바에서는 변형된 utf-8 인코딩 방식에 따라 1byte가 아닌 2byte(11000000 10000000)로 표기하므로 110xxxxx 10xxxxxx 의 경우 null 인지 비교해야 정확한 판독이 가능하지만 입력된 값의 인코딩여부를 판독하므로 상황에 따라서는 무시해도 괜찮을듯 하다.
    4. CJK 2바이트+한글 2바이트 가 '1110xxxx 10xxxxxx 10xxxxxx 0xxxxxxx' 와 같은 패턴으로 utf-8 3바이트 와 아스키1바이트 조합과 유사할 경우 앞자리 3바이트를 유니코드 형태로 역치환 해서 000800 ~00FFFF 범위가 맞는지 체크한다.

utf-8 인코딩 체크 예제

utf-8 인코딩 판독 예제

1. '위'.getBytes("ISO-8859-1") : -20 , -100, -124
2. unsigned byte = 236, 156, 132
3. 16진수 : EC , 9C, 84
4. 2진수 : 11101100 10011100 10000100
5. urf-8 패턴제거: 00001100 00011100 00000100
6. 유니코드 타입으로 치환 : 00001100<<12 + 0011100<<6 + 00000100 = 11000000 00000100
7. 16진수 : 000800 <= 00E704 =< 00FFFF

package util;

import java.io.UnsupportedEncodingException;

/** 
 *  <pre>
 *  작성자 : 이종희 (qola@naver.com)
 *  JAlbum.com (http://jalbum.net/download.jsp?ref=list) 소스 참고함.   
 *  <b>특정 문자열이 utf-8 인코딩인지 아닌지 체크한다.</b>
 * 
 *  utf-8 인코딩 패턴인  110xxxxx 10xxxxxx , 1110xxxx 10xxxxxx 10xxxxxx 인지 비교한다.
 *  2바이트 (110xxxxx 10xxxxxx) 패턴의 유니 코드 중복으로 인해 100% 검증할수 없지만 
 *  한글의 경우 3byte 로 표기 되므로  2바이트 패턴일 경우 utf-8 인코딩이 아닌것으로 간주한다.
 *  
 *  따라서 000080 ~0007FF 영역의 라틴 문자, 그리스 문자, 키릴 문자, 콥트 문자, 
 *  아르메니아 문자, 히브리 문자, 아랍 문자 등은 utf-8 인코딩을 비교할수 없다. 
 *  
 *  수정된 utf-8의 의한 null값 \u0000 은 11000000 10000000 로 표기되지만 무시하기로 한다.
 *  
 *  </pre>
 */

public class UTFUtil {
	   
	
	public static boolean isUTF8(String str) throws Exception{
		byte[] bytes=str.getBytes("ISO-8859-1");
		return isUTF8(bytes,0,bytes.length);
	}
	   
	public static boolean isUTF8(byte[] buf, int offset, int length) {

	       boolean yesItIs = false;
	       for (int i=offset; i<offset+length; i++) {
	          if ((buf[i] & 0xC0) == 0xC0) { // 11xxxxxx 패턴 인지 체크 
	             int nBytes;
	             for (nBytes=2; nBytes<8; nBytes++) {
	                int mask = 1 << (7-nBytes);
	                if ((buf[i] & mask) == 0) break;
	             }
	            
                      //CJK영역이나 아스키 영역의 경우 110xxxxx 10xxxxxx 패턴으로 올수 없다.
	             if(nBytes==2) return false;
	             
	             // Check that the following bytes begin with 0b10xxxxxx
	             for (int j=1; j<nBytes; j++) {
	                if (i+j >= length || (buf[i+j] & 0xC0) != 0x80) return false;
	             }
                
                if(nBytes==3){
                	// 유니코드 형태로 역치환 해서 0x0800 ~ 0xFFFF 사이의 영역인지 체크한다. 
                    char c = (char) (((buf[i] & 0x0f) << 12) + ((buf[i+1] & 0x3F) << 6) + (buf[i+2] & 0x3F));
                    if(!(c >= 0x0800 && c <= 0xFFFF)){
                        return false;
                    }	                	
                }
	                
	             yesItIs = true;
	          }
	       }
	       return yesItIs;
	 }
	    
}


 



한글 URL 사용시 문제 발생 케이스

  • IE에는 유니코드 지원을 위해 기본적으로 URL을 UTF-8 로 전송해 주지만 옵션 사항이므로 사용자 마다 환경이 다를수 있다.
  • utf-8로 다시 보내는 기능을 제공하지 않는 브라우저도 존재한다.
  • RSS 리더와 같은 별도의 클라이언트를 사용할경우 서버인코딩, 클라이언트, 브라우저 인코딩 이 모두 일치하여야만 한다.

해결책

  • REQUEST 요청시 URL이 UTF-8 인코딩 방식인지 아닌지를 체크하여 UTF-8 방식이면 "ISO-8859-1 --> UTF-8" 형태로 인코딩 변환을 해주고 그렇지 않으면 "ISO-8859-1 --> MS949" 로 인코딩 변환을 시켜준다.

구현 예제

  • APACHE MOD_REWRITE
    • 아래 예와 같이 MOD_REWRITE를 이용해 특정 패턴의 URL을 REWRITE 시킨다.
      RewriteEngine   on
          # // http://tag.naver.com/tag2/한글 --> http://tag.naver.com/tag/index.jsp?tag=한글
          RewriteRule ^/tag2/(.+)$  /tag/index.jsp?tag=$1 [PT]
  • JSP 샘플
    • isUTF8() 체크 메소드를 이용해 파라메터의 인코딩을 각각 처리해준다.
      <%@page language="java" contentType="text/html;charset=utf-8"%>
      <%@page import="java.net.URLEncoder, util.*" %>
      
      <html>
      <head>
      	<meta http-equiv="Content-type" content="text/html; charset=utf-8">
      </head>
      
      <body>
        index.jsp... <br>
        
        <%
          String uri=request.getRequestURL().toString();
        	String tag=request.getParameter("tag");
        	byte[] tagBytes=tag.getBytes("ISO-8859-1");
        	
        	out.println("byte[]===");
        		for(int i=0;i<tagBytes.length;i++){
        			out.println(tagBytes[i]+",");
        		}
        	out.println("<br>");  
        	
        	boolean isUTF8=UTFUtil.isUTF8(tag);
        	
        	if(isUTF8) {
        		out.println(util.Enco.toUtf(tag));
        		out.println("<br>");
        		out.println(isUTF8);
        		
        	}else{ 
        		out.println(new String(tag.getBytes("ISO-8859-1"),"ms949"));
        		out.println("<br>");
        		out.println(isUTF8);
        	}
        %>
        
        <a href="http://tag_test.com/tag2/한글">http://tag_test.com/tag2/한글</a><br>
        <a href="http://tag_test.com/tag2/코리아">http://tag_test.com/tag2/코리아</a><br>
        <a href="http://tag_test.com/tag2/위">http://tag_test.com/tag2/위</a>
      </body>
      </html>

Posted by Real_G