본문 바로가기
Computer Science

개발자가 알아야 하는 Unicode, UTF-8의 개념

by DuncanKim 2023. 7. 2.
728x90

개발자가 알아야 하는 Unicode, UTF-8의 개념

 

 

개발을 하다 보면 유니코드와 UTF-8이라는 용어를 들어봤을 것이다. 두 가지 개념은 텍스트 데이터의 표현과 처리에 있어서 중요한 역할을 하고 있다. 이번 글에서는 유니코드와 UTF-8의 개념과 활용에 대해서 알아볼 것이다.

 

 

1. 인코딩(Encoding), 디코딩(Decoding)이란 무엇인가?

 

유니코드와 UTF-8을 알기 전에, 먼저 알아야 하는 개념이 있다. 인코딩과 디코딩이다.

 

인코딩(Encoding)은 데이터를 다른 형식으로 변환하는 과정을 말한다. 인코딩은 일련의 규칙을 적용하여 데이터를 해당 형식으로 표현하는 방식인 것이다. 주로 문자열을 바이트로 변환하는 과정을 의미한다.

let str = "Hello, world!" // 변환할 문자열
if let data = str.data(using: .utf8) {
    let bytes = [UInt8](data)
    print("바이트 배열: \(bytes)")
} else {
    print("인코딩 실패")
}

예를 들어 이런 식으로 바이트 배열로 변환하는 것으로 이해해 볼 수 있다. 이렇게 우리가 알아들을 수 있는 텍스트나 이미지, 오디오, 비디오 등을 컴퓨터가 알아듣고, 처리하여 화면으로 보여주게 끔 하기 위해 필요한 과정이 Encoding인 것이다. 인코딩은 다양한 형식과 규칙을 가지고 있으며, 각 형식은 특정 데이터 표현 방식을 정의한다.

 

 

한편, 디코딩(Decoding)은 인코딩된인코딩 된 데이터를 원래의 형식으로 변환하는 과정을 말한다. 디코딩은 인코딩 된 데이터를 해석하여 원래의 데이터 형식으로 복원하는 작업이다. 이는 데이터를 전송하거나 저장하는 동안 데이터의 무결성과 일관성을 유지하기 위해 중요한 단계다.

let bytes: [UInt8] = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] // 디코딩할 바이트 배열
if let str = String(bytes: bytes, encoding: .utf8) {
    print("디코딩 결과: \(str)")
} else {
    print("디코딩 실패")
}

예를 들어 이런 식으로 바이트 배열을 String 값으로 변환하는 것으로 이해해볼 수 있다. 컴퓨터가 알아듣는 바이트 배열을 문자열 또는 이미지, 오디오, 비디오 등으로 변환시키는 것을 Decoding인 것이다. 

 

인코딩과 디코딩은 서로 반대되는 작업으로 데이터의 형식 변환을 위해 함께 사용된다. 예를 들어, 텍스트 문자열을 UTF-8 인코딩으로 변환하여 저장한 후, 필요한 시점에서 UTF-8로 인코딩 된 데이터를 디코딩하여 원래의 텍스트 문자열을 복원할 수 있는 것이다.

 

다시 말해, 인코딩은 데이터를 다른 형식으로 변환하는 과정이며, 디코딩은 인코딩된 데이터를 다시 원래의 형식으로 변환하는 과정이다. 이러한 인코딩과 디코딩 과정은 데이터의 형식 변환이 필요한 다양한 상황에서 사용된다.

 

 

2. 유니코드(Unicode)란 무엇인가?

 

컴퓨터가 처음 나왔을 때, 영어권에서 먼저 만들었기에 영어와 몇 가지 특수문자만 활용이 되었다. 알고리즘 문제를 풀다 보면 ASCII 코드를 활용해서 영어 대문자, 소문자를 60번대 언저리에서 90번 때 즈음까지 쓰는 것을 생각해 보면 이해를 할 수 있을 것이다.

 

그렇지만, 시간이 지나 여러 국가에서도 컴퓨터를 사용하고, 자신들의 국가의 언어를 표시하고 싶어졌을 것이다. 그래서 1바이트 안에 임의대로 영어 대신에 자국 문자를 할당하여 사용하게 된다. 이때는 문제가 없었다. 왜냐하면 다른 국가와 컴퓨터 안에서 교류를 하는 일이 없었기 때문...

 

 

하지만, 인터넷이 발전하고 다른 국가의 홈페이지를 들어갔더니 '궰긹똻묺' 등의 요상한 글자를 마주하는 일이 생겼다. 각 나라마다의 기준으로 문자를 할당하여 사용했기에 호환이 되지 않았던 것이다. 그래서 국제적으로 전 세계 모든 언어를 표시할 수 있는 표준 코드를 만들기로 해서 나온 것이 Unicode이다.

 

유니코드는 전 세계의 문자들을 일관되게 표현하기 위한 국제 표준이다. 문자에 고유한 코드 포인트를 할당하여 표현한다. 각 코드 포인트는 0부터 10FFFF 사이의 16진수 값으로 표현되며, 모든 문자에 대해 고유한 코드 포인트를 가지고 있다. 글자와 코드가 1:1로 매핑되어있는 표준화된 테이블이라고 생각하면 쉽다.

 

https://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_%EC%98%81%EC%97%AD

 

유니코드 영역 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 유니코드에는 연속된 코드 포인트의 집합인 영역(block, 블록)들이 있다. 각 영역의 이름은 유일하며, 그 범위는 다른 영역과 겹치지 않는다. 영역의 크기는 16의

ko.wikipedia.org

이렇게 유니코드는 정의되어 있다.

 

유니코드는 우리가 흔히 사용하는 문자뿐만 아니라 이모지, 특수 문자, 여러 언어의 문자 등 다양한 문자를 표현할 수 있도록 해준다. 이러한 특성 때문에 개발할 때 유니코드를 올바르게 이해하고 처리하는 것이 중요하다.

 

 

3. UTF-8은 무엇인가?

 

UTF-8은 유니코드를 인코딩 하는 방법이다. 유니코드를 인코딩 시키는 다른 방법으로는 EUC-KR, CP949, UTF-16 등이 있다. 인코딩 시키는 방법이 다른 것이다.

 

UTF-8은 가변 길이 인코딩 방식으로, ASCII 문자는 1바이트로 표현하고, 유니코드의 다른 문자들은 2바이트부터 최대 4바이트까지 사용하여 표현한다. 쉽게 말하면, 글자마다 byte 길이가 다르다는 것이다. ‘a’는 1 byte이고 ‘가'는 3 byte이다. 가변을 구분하기 위해 첫 바이트에 표식을 넣었는데 2 byte는 110으로 시작하고 3바이트는 1110으로 시작한다. 나머지 바이트는 10으로 시작한다. 하나의 법처럼 이렇게 만들어 놓은 것이라 110인 이유, 1110인 이유는 따로 없다.

 

UTF-8은 8bit를 사용하여 1개의 Index를 표현한다.

 

 

4. 이 개념들이 활용되는 예시

 

1) 다국어 지원

 

다국어 지원은 프로그램이 여러 언어를 지원하며 사용자에게 언어 선택 옵션을 제공해야 할 때 유용하다. 예를 들어, 언어 선택 메뉴를 통해 사용자가 원하는 언어로 프로그램을 실행할 수 있도록 할 수 있다. 이뿐만 아니라 프로그램 내에서 다국어 문자열을 표시하고, 언어에 맞는 번역을 제공하는 기능을 구현할 수도 있다. 유니코드와 UTF-8을 사용하면 모든 언어의 문자를 일관되게 표현할 수 있으므로 사용자는 자신의 언어로 프로그램을 사용할 수 있게 된다.

// 언어 선택에 따른 다국어 지원
struct ContentView: View {
    @State private var selectedLanguage: String = "English"
    
    var body: some View {
        VStack {
            Text("Select Language:")
            Picker("Language", selection: $selectedLanguage) {
                Text("English").tag("English")
                Text("한국어").tag("Korean")
                // 다른 언어 옵션들 추가
            }
            .pickerStyle(SegmentedPickerStyle())
            
            Text(LocalizedStringKey("Hello, World!")) // 다국어 문자열 표시
                .font(.title)
                .padding()
        }
    }
}

이렇게 언어 선택에 따른 다국어 지원을 하기 위해서 고려를 해야 하기도 한다.

 

2) 데이터 저장 및 전송

 

텍스트 데이터를 파일에 저장하거나 네트워크를 통해 전송해야 할 때 유니코드와 UTF-8을 사용하여 데이터의 일관된 표현과 안정성을 보장할 수 있다. 예를 들어, 사용자가 입력한 텍스트를 파일로 저장하고 다시 불러올 때, 유니코드와 UTF-8을 사용하여 데이터를 인코딩하여 저장하고 디코딩하여 불러올 수 있다. 또한, 데이터베이스에 저장된 텍스트 데이터를 다른 시스템으로 전송해야 할 때도 유니코드와 UTF-8을 사용하여 데이터의 일관성을 유지할 수 있다. 이를 통해 데이터의 손실 없이 다양한 시스템 간에 텍스트 데이터를 주고받을 수 있다.

// 텍스트 데이터를 파일에 저장 및 불러오기
func saveTextToFile(text: String, filePath: String) {
    do {
        try text.write(toFile: filePath, atomically: true, encoding: .utf8)
        print("Text saved successfully.")
    } catch {
        print("Error saving text:", error.localizedDescription)
    }
}

func loadTextFromFile(filePath: String) -> String? {
    do {
        let text = try String(contentsOfFile: filePath, encoding: .utf8)
        return text
    } catch {
        print("Error loading text:", error.localizedDescription)
        return nil
    }
}

 


3) 문자열 처리


문자열을 다룰 때 유니코드와 UTF-8을 이해하면 텍스트 데이터를 올바르게 처리할 수 있다. 예를 들어, 문자열의 길이를 계산해야 할 때 유니코드를 고려하여 각 문자의 길이를 정확하게 계산할 수 있다. 또한, 다른 언어의 문자열을 비교할 때도 유니코드와 UTF-8을 적절히 처리하여 올바른 비교 결과를 얻을 수 있다. 또한, 문자열 내에서 특정 문자를 검색하거나 대체할 때도 유니코드와 UTF-8을 이해하면 정확한 작업을 수행할 수 있다.

// 한글 검색 시에 유니코드와 UTF-8 활용
let sentence = "한글 검색 예시입니다."
let searchWord = "한글"
let utf16SearchWord = searchWord.utf16

if let range = sentence.range(of: utf16SearchWord) {
    let startIndex = sentence.utf16.distance(from: sentence.utf16.startIndex, to: range.lowerBound)
    let endIndex = sentence.utf16.distance(from: sentence.utf16.startIndex, to: range.upperBound)
    let searchResult = sentence[startIndex..<endIndex]
    
    print("검색 결과: \(searchResult)")
} else {
    print("검색어를 찾을 수 없습니다.")
}

한글 검색에 유니코드와 UTF-8을 활용하여 문자열 처리를 수행하는 방법이다. 유니코드와 UTF-8은 문자열의 일관성과 안정성을 보장하기 위해 중요한 역할을 한다. 다른 문자열 처리 작업에서도 유니코드와 UTF-8을 활용하여 문자열을 올바르게 처리할 수 있다.

728x90

댓글