텍스트 데이터와 문자 인코딩: 컴퓨터 저장 원리와 실무 활용법

컴퓨터가 데이터를 저장하는 기본 원리는 이진수(0과 1) 입니다. 컴퓨터 메모리는 수많은 전구처럼 작동하는 트랜지스터로 구성되어 있으며, 이 트랜지스터는 전류가 흐르거나 흐르지 않는 두 가지 상태를 표현합니다. 이와 같이, 모든 데이터는 결국 0과 1의 조합으로 저장됩니다.


1. 문자 인코딩과 디코딩

  • 문자 인코딩 (Encoding):
    문자를 특정한 숫자(바이트)로 변환하는 과정입니다.
    예시: A → 65, B → 66

  • 문자 디코딩 (Decoding):
    인코딩된 숫자(바이트)를 원래의 문자로 변환하는 과정입니다.

프로그래밍 언어에서 문자열을 바이트로 변환할 때는 항상 문자 집합(Charset) 을 명시해야 합니다. 예를 들어, 자바에서는 String.getBytes(Charset charset) 메소드를 사용하여 인코딩할 수 있습니다.


2. 아스키(ASCII) 코드와 UTF-8

아스키(ASCII) 문자 집합

  • 정의:
    아스키 코드는 128개의 문자(0~127)를 표현할 수 있는 인코딩 방식입니다.
  • 용도:
    주로 영어 알파벳, 숫자, 기본 구두점 등을 다룹니다.

UTF-8 인코딩

  • 특징:
    • 가변 길이 인코딩: 1바이트부터 최대 4바이트까지 사용하여 문자를 표현합니다.
    • 아스키와 호환: 아스키 범위(0~127)의 문자는 UTF-8에서도 동일한 바이트 값을 사용합니다.
  • 효율성:
    • 저장 공간 절약: 대부분의 서양 문자와 ASCii 문자는 1바이트만 사용합니다.
    • 네트워크 효율성: 영문 텍스트를 전송할 때 UTF-8은 UTF-16보다 최대 2배 더 효율적입니다.
      참고로 웹상의 문서 중 80% 이상이 영문이므로, UTF-8의 효율성은 매우 중요합니다.

3. 실무에서의 문자 인코딩 활용

문자 집합 선택

문자열을 바이트로 변환할 때는 반드시 사용할 문자 집합(예: UTF-8, EUC-KR, ISO-8859-1)을 지정해야 합니다. 잘못된 문자 집합을 사용할 경우, 특히 한글과 같이 다국어 문자의 경우 문자 깨짐(garbled text) 문제가 발생할 수 있습니다.

한글 깨짐 문제의 주요 원인

  1. EUC-KR과 UTF-8의 호환성 문제:
    UTF-8로 인코딩한 한글을 EUC-KR(ms949)로 디코딩하거나, 반대로 EUC-KR로 인코딩한 한글을 UTF-8로 디코딩할 때 문제가 발생합니다.

  2. ISO-8859-1과의 혼용:
    EUC-KR이나 UTF-8로 인코딩한 한글을 ISO-8859-1로 디코딩하면, 지원하지 않는 문자로 인해 깨짐 현상이 발생합니다.

예시

package charset;

import java.nio.charset.Charset;
import java.util.Arrays;

import static java.nio.charset.StandardCharsets.*;

public class EncodingMain2 {

    private static final Charset EUC_KR = Charset.forName("EUC-KR");
    private static final Charset MS_949 = Charset.forName("MS949");


    public static void main(String[] args) {
        System.out.println("== 영문 ASCII 인코딩 ==");
        test("A", US_ASCII, US_ASCII);
        test("A", US_ASCII, ISO_8859_1); // ASCII 확장(LATIN-1)
        test("A", US_ASCII, EUC_KR); // ASCII 포함
        test("A", US_ASCII, MS_949); // ASCII 포함
        test("A", US_ASCII, UTF_8); // ASCII 포함
        test("A", US_ASCII, UTF_16BE); // UTF_16 디코딩 실패


        System.out.println("== 한글 인코딩 - 기본 ==");
        test("가", US_ASCII, US_ASCII); // X
        test("가", ISO_8859_1, ISO_8859_1); // X
        test("가", EUC_KR, EUC_KR);
        test("가", MS_949, MS_949);
        test("가", UTF_8, UTF_8);
        test("가", UTF_16BE, UTF_16BE);


        System.out.println("== 한글 인코딩 - 복잡한 문자 ==");
        test("뷁", EUC_KR, EUC_KR); // X
        test("뷁", MS_949, MS_949);
        test("뷁", UTF_8, UTF_8);
        test("뷁", UTF_16BE, UTF_16BE);


        System.out.println("== 한글 인코딩 - 디코딩이 다른 경우 ==");
        test("가", EUC_KR, MS_949);
        test("뷁", MS_949, EUC_KR); // 인코딩 가능, 디코딩 X
        test("가", EUC_KR, UTF_8); // X
        test("가", MS_949, UTF_8); // X
        test("가", UTF_8, MS_949); // X


        System.out.println("== 영문 인코딩 - 디코딩이 다른 경우 ==");
        test("A", EUC_KR, UTF_8);
        test("A", MS_949, UTF_8);
        test("A", UTF_8, MS_949);
        test("A", UTF_8, UTF_16BE); // X
    }



    private static void test(String text, Charset encodingCharset, Charset decodingCharset){
        byte[] encoded = text.getBytes(encodingCharset);
        String decoded = new String(encoded, decodingCharset);
        System.out.printf("%s -> [%s] 인코딩 -> %s %sbyte -> [%s] 디코딩 -> %s\n",
                text, encodingCharset, Arrays.toString(encoded),
                encoded.length,
                decodingCharset, decoded);


    }


}
profile
배움을 추구하는 개발자

0개의 댓글