수업 시작에 앞서...


데이터를 처리하기 위해서 인코딩 방식에 대해서 익숙해 질 필요가 있다.

인코딩과 디코딩에 간단하게 말하자면 다음과 같다. 인코딩과 디코딩은 서로 반대되는 처리이다.
인코딩(encoding) : 부호화. 한 문자를 쪼개서 코드화. 한글 처리에 주의해야한다.

디코딩(decoding) : 복호화. 1과 0의 코드들를 합쳐서 문자화.

 

아래에서 설명하지만, 자바는 UTF-16을 사용하고 웹에서는 UTF-8을 사용하기 때문에 경우에 따라 CharacterSet을 변환해야 하는 상황이 발생할 수 있다.

 

UTF-8 형식으로 JSP 를 작성할 때에는 파일도 UTF-8 형식으로 저장해야 한다.

 

 

 

캐릭터 셋(CharacterSet)


사람이 사용하는 문자를 컴퓨터가 이해할 수 있도록 문자에 각 코드값을 부여한 것이다.

 

유니코드(unicode)

국제 표준으로 제정된 2바이트계의 만국 공통의 국제 문자부호 체계(UCS : Universal Code System)를 말한다.

애플컴퓨터, IBM, 마이크로소프트 등이 컨소시엄으로 설립한 유니코드(Unicode)가 1990년에 첫 버전을 발표하였고, ISO/IEC JTC1 에서 1995년 9월 국제표준으로 제정하였다. 공식 명칭은 ISO/IEC 10646-1(Universal Multiple-Octet Coded Character Set)이다.

 

데이터의 교환을 원활하게 하기 위해 문자 1개에 부여되는 값을 16비트로 통일하였다.

코드의 1문자 당 영어는 7비트, 비영어는 8비트, 한글이나 일본어는 16비트의 값을 가지게 되는데 이들을 모두 16비트로 통일한 것이다.

 

UTF-8 과 UTF-16

UTF-8 과 UTF-16 의 차이를 한 마디로 말하면 문자를 표현할 때의 단위가 다르다는 것이다.

UTF-8 은 8비트 가변장 멀티바이트에서 문자를 표현하고 UTF-16 은 16비트 가변장 멀티바이트에서 문자를 표현한다.

 

① UTF-8 (8Bit UCS Transformation Format)

웹에서 기본적으로 사용하는 코드. UCS-2로 정의되는 문자 집합을 이용하여 기술된 문자열을 바이트 열로 변환하는 방식의 1개 1문자를 1~6바이트 가변장 멀티바이트로 변환한다.

 

② UTF-16 (16Bit UCS Transformation Format)

자바의 기본 코드. 자바에서는 문자 하나를 저장하기위한 바이트 수는 영문자이든, 한글문자이든 구분없이 2바이트(16비트)를 차지하게 된다.
UCS-2 (Universal Multiple-Octet Coded Character-2)로 정의되는 문자 집합을 이용하여 기술된 문자열에 UCS-4의 일부 문자를 채워넣기 위한 인코딩 방식. UTF-8과 병용할 수 있다. UCS-2로 이용할 수 있는 문자수를 대폭 늘릴 수 있다.

※ 자바 단에서는 UTF-16을 사용하고 웹에서는 UTF-8을 사용하기 때문에 경우에 따라 변환해야 하는 상황이 발생할 수 있다.

 

문자 코드의 종류


KSC5601

한글 완성형 표준(한글 2,350자 표현).

한국공업표준 정보처리분야(C)의 5601번 표준안.

 

KSC5636

영문자에 대한 표준.

한국공업표준 정보처리분야(C)의 5636번 표준안. 기존 ASCII Code 에서 역슬래시(\)를 원(₩) 표시로 대체.

 

EUC-KR (MS949)

Bell-Laboratories 에서 유닉스 상에서 영문자 이외의 문자를 지원하기 위해 제안한 확장 유니코드(Extend UNIX Code) 중 한글 인코딩 방식.

영문은 KSC5636으로 처리하고, 한글은 KSC5601로 처리. 즉, EUC-KR(MS949) = KSC5636 + KSC5601

 

UTF-8

영문과 숫자는 1바이트, 한글은 3바이트로 표현.

(웹에서 많이 사용. UTF-8 형식으로 JSP 를 작성할 때에는 파일도 UTF-8 형식으로 저장해야 한다.)

 

UTF-16

자바 기본 문자 코드. 영문과 숫자는 1바이트, 한글은 2바이트로 표현.

 

ISO-8859-1

서유럽 언어 표기에 필요한 US-ASCII 에 없는 94개의 글자의 순차적 나열.

 



CharacterSet 예제 코드  -  CharacterSet 의 변환 메소드, 예외 처리 맛보기 관찰


예제 코드에 앞서...

이번 코드에서는 예외의 처리에 대해서 다루고 있다. 포스팅 자바 19 (2020.08.28) 에서 예외처리에 대해 자세히 다룰 예정이지만 당장 아래의 코드를 관찰하는데 필요한 정보를 간단히 살펴보자.

 

* 예외(Exception) 관련 사항

 - UnsupportedEncodingException 예외  :  지원되지 않는 문자 인코딩에 대해 발생하는 예외이다. 종종 사용해왔던 IOException 과 마찬가지로 『import java.io.UnsupportedEncodingException;』 구문을 작성해준다.

 - 예외 던지기 (throws)  :  IOException 예외를 사용해오면서, 지금까지는 메소드 내에서 예외(Exception)가 발생하면 메소드 밖으로 던져줘왔다.(메소드 선언부에 『throws IOException』 을 명시 → 메소드 내에서 IOException 예외가 발생하면 메소드 실행을 중단하고 예외를 메소드 밖으로 -해당 메소드를 호출한 곳으로- 던진다. 실행 중이던 메소드는 강제종료)

 - 예외 처리 (try-catch)  :  예외의 발생이 예상되는 영역을 try{ } 블럭으로 감싸서 예외 발생을 감시한다. 예외가 발생하면 처리할 내용을 catch{ } 블럭에 작성하여 예외를 처리한다. 이렇게 예외를 처리하면 메소드 밖으로 예외를 던질(throws) 필요가 없다.

 

※ character set 변환 관련 메소드

메소드 설명
static String                              
System.getProperty(String key) 
 System 클래스의 getProperty(String key) 메소드는
 현재 사용하고 있는 운영체제의 시스템 속성(property) 정보 중 인자로 전달받은 key 에 해당하는 값을 얻어온다.

 ex) System.getProperty("file.encoding");    // MS949
      - 사용하고 있는 운영체제가 어떤 문자 코드로 file을 인코딩하는지 확인한다.
byte[] getBytes()   String 클래스의 getBytes() 메소드는
 메소드를 호출한 String 객체의 문자열을 byte 배열로 반환한다.
 단, 매개변수가 없는 getBytes() 는 현재 시스템의 default character set 을 기반으로 한다.

 ex) "김소소".getBytes().length;    // == 6    // MS949 기반의 한글은 2 byte
byte[] getBytes(String charset  String 클래스의 getBytes() 메소드의 오버로딩으로, charset 종류 이름을 String 으로 전달하면 해당 문자 코드 기반의 byte 배열을 반환한다.

 ex) "김소소".getBytes("utf-8").length;    // == 9    // UTF-8 기반의 한글은 3 byte
String(byte[] bytes, String charset)   String 클래스의 생성자 중 하나로,
 첫 번째 매개변수인 byte 배열을 String 으로 생성하되 두 번째 매개변수의 인코딩 방식으로 지정해준다.

 ex) byte[] buf = "김소소".getBytes();    // MS949 기반의 byte 배열
     String str = new String(buf, "utf-8");
     // MS949 기반 byte 배열 → UTF-8 기반으로 문자열 생성

★ byte 배열(EUC-KR) 은 String(EUC-KR) 으로, byte 배열(UTF-8) 은 String(UTF-8) 로 변환해야 한다.

    간혹 byte 배열(EUC-KR) 을 String(UTF-8) 로 변환하려는 시도를 하게 되는데 이렇게 코딩하면 문자열이 깨지고 복구할 수 없게 된다.

    ex)  String convert = new String(name.getBytes("euc-kr"), "utf-8");
   위 구문에 의해 바이트 배열이 깨져서 EUC-KR, UTF-8 등 어떠한 바이트 배열로 읽어와도 문자가 깨져있는 것을 확인할 수 있다. 이미 깨져서 생성된 String 객체의 byte 배열은 어떤 방식으로도 복구가 불가능하다.

/*=====================================
  ■■■ 자바의 주요(중요)클래스 ■■■
  - 문자열 관련 클래스
  - 캐릭터 셋(CharacterSet)
=====================================*/

import java.io.UnsupportedEncodingException;

public class Test146
{
	// try-catch로 예외를 처리하므로 throws 할 필요 없다.
	public static void main(String[] args) // throws UnsupportedEncodingException
	{
		byte[] buf;
		String name = new String("김소소");

		// ○ System.getProperty("file.encoding")
		//	 사용하고 있는 운영체제가 어떤 문자 코드로 file을 인코딩하는지 확인한다.
		System.out.println("디폴트 캐릭터셋 : " + System.getProperty("file.encoding"));
		//--==>> 디폴트 캐릭터셋 : MS949	// (EUC-KR)


		// ○ String객체.getBytes()
		//  : String 문자열을 바이트 배열로 반환 (디폴트 캐릭터 셋 기반) 
		buf = name.getBytes();	// String → byte[]
        
		System.out.println("Length : " + buf.length);	// byte[] 길이 출력
		//--==>> Length : 6
		//	 EUC-KR(MS949) 기반의 한글 "김소소" 은 6 byte 이다. → 한글 2 byte

		// "김소소" 을 담은 default character set 기반의 byte[] 을 한 byte 씩 출력
		for( byte b : buf)
			System.out.print("[" + b + "] ");
		System.out.println();
		//--==>> [-79] [-24] [-68] [-46] [-68] [-46]

	// ↑ "김소소" 문자열을 default character set 기반으로 문자 코드 관찰
	////////////////////////////////////////////////////////////////////////////////////
	// ↓ "김소소" 문자열을 UTF-8 기반으로 문자 코드 관찰 

		try	// 예외를 throws 하지 않고 try-catch 방식으로 처리한다. (throws 작성 Ⅹ)
		{			
			// ○ String객체.getBytes("utf-8")
			//   String 문자열을 바이트 배열로 반환 (UTF-8 기반)
			buf = name.getBytes("utf-8");
			// 시스템에서 만약 "utf-8"이라는 인코딩를 지원하지 않는다면 예외가 발생할 수 있다.
			// java.io.UnsupportedEncodingException;	

			System.out.println("Length : " + buf.length);	// byte[] 길이 출력
			//--==>> Length : 9
			//	 UTF-8 기반의 한글 "김소소" 은 9 byte 이다. → 한글 3 byte
        
        
			// "김소소" 을 담은 UTF-8 기반의 byte[] 을 한 byte 씩 출력
			for(byte b : buf)
				System.out.print("[" + b + "] ");
			System.out.println();
			//--==>> [-22] [-71] [-128] [-20] [-122] [-116] [-20] [-122] [-116]
            

	////////////////////////////////////////////////////////////////////////////////////
	// ↓ UTF-8 바이트 배열(buf) → UTF-8 문자열로 조립

			// ○ String(byte[] bytes, Charset charset) 생성자
			//   문자열을 생성하면서 인코딩 방식을 UTF-8 로 지정해준다.
			//new String(buf, "utf-8");
            
			System.out.println("Value1 : " + new String(buf, "utf-8"));
			//--==>> Value1 : 김소소	// 정상적인 문자열

			System.out.println();

	////////////////////////////////////////////////////////////////////////////////////
	// ↓ UTF-8 바이트 배열(buf) → EUC-KR(MS949) 문자열로 조립 (잘못된 변환이다.)

			// ○ String(byte[] bytes) 생성자
			//   시스템이 사용하고 있는 default character set 방식으로 문자열을 생성한다.
			//new String(buf);	// == new String(buf, MS949);	// 현재 MS949 기반
            
			System.out.println("Value2 : " + new String(buf));
			//--==>> Value2 : 源??냼?냼	// 비정상적인 문자열
			// ★ 현재 default charset 이 MS949 이므로
			//	 UTF-8 기반의 byte 배열을 String 으로 변환하려면
			//	 정상적인 인코딩을 위해 String 생성자에 "utf-8" 을 전달해야 한다.

			System.out.println();
          
	////////////////////////////////////////////////////////////////////////////////////
	// ↓ EUC-KR 문자열 → EUC-KR 바이트 배열 → UTF-8 문자열로 조립 (잘못된 변환)

			String convert = new String(name.getBytes("euc-kr"), "utf-8");
			
			System.out.println("Value3 : " + convert);
			//--==>> Value3 : ????	// 비정상적인 문자열


			// 비정상적인 문자열 → EUC-KR 기반 byte 배열로 반환
			buf = convert.getBytes();
			System.out.println("Length : " + buf.length);
			//--==>> Length : 4	// 출처를 알 수 없는 바이트 크기를 반환하고 있다.


			// 비정상적인 문자열 → UTF-8 기반 byte 배열로 반환
			buf = convert.getBytes("utf-8");
			System.out.println("Length : " + buf.length);
			//--==>> Length : 11	// 출처를 알 수 없는 바이트 크기를 반환하고 있다.

			/*
			※ check~!!!
			   EUC-KR(MS949) 을 UTF-8 로 변환하겠다는 의도로 위와 같이 코딩을 해버리면
			   (→ 『String convert = new String(name.getBytes("euc-kr"), "utf-8");』)
			   위 구문에 의해 바이트 배열이 깨져서 EUC-KR, UTF-8 등 
				어떠한 바이트 배열로 읽어와도 문자가 깨져있는 것을 확인할 수 있다.

			   이미 깨져서 생성된 String 객체의 byte 배열은 어떤 방식으로도 복구가 불가능하다.
               
			★ byte 배열(EUC-KR) 은 String(EUC-KR) 으로,
			   String(UTF-8) → byte 배열(UTF-8) 로 변환해야 한다.
			*/

		}	// try 블럭 end
		// try 에서 UnsupportedEncodingException 발생하면 잡아내어(catch) 예외를 처리한다.
		catch (UnsupportedEncodingException e)
		{
			System.out.println(e.toString());
			// 예외에 대한 출력을 하는 것으로 예외를 처리해버리겠다는 의미.
		}
	}	// main() end
}
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기