Unicode

Unicode는 현대 컴퓨팅에서 가장 중요한 문자 인코딩 표준 중 하나이다.

Unicode는 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 국제 표준이다.
단순히 말해서, Unicode는 각 문자에 고유한 번호(코드 포인트)를 할당하는 표준화된 방식이다.

예를 들어, Unicode에서:

여기서 ‘U+‘는 이것이 Unicode 코드 포인트임을 나타내는 접두사이고, 그 뒤의 숫자는 16진수 값이다.

중요한 점은 Unicode는 문자와 숫자(코드 포인트) 간의 매핑만 정의할 뿐, 이 숫자들이 컴퓨터 메모리에 어떻게 저장되는지는 정의하지 않는다는 것이다. 후자는 UTF-8, UTF-16과 같은 인코딩 방식에 의해 결정된다.

Unicode가 필요했던 이유

Unicode 이전에는 각 언어나 국가, 운영 체제마다 다양한 인코딩 방식이 사용되었다:

이러한 다양한 인코딩으로 인해 발생하는 문제들:

  1. 호환성 문제: 한 인코딩으로 작성된 문서를 다른 인코딩을 사용하는 시스템에서 읽으면 글자가 깨짐
  2. 다국어 지원 제한: 한 문서에서 여러 언어를 함께 사용하기 어려움
  3. 개발 복잡성: 개발자가 여러 인코딩을 처리하는 코드 작성 필요
  4. 데이터 교환 문제: 서로 다른 시스템 간 텍스트 데이터 교환 시 변환 필요

이러한 문제들을 해결하기 위해 단일 표준의 필요성이 대두되었고, 그 결과 Unicode가 탄생했다.

Unicode의 역사적 발전

Unicode의 역사적 발전 과정은 다음과 같다:

  1. 1980년대 후반: Apple과 Xerox를 중심으로 단일 문자 집합 표준에 대한 논의 시작
  2. 1991년: Unicode 1.0 발표 (7,161개 문자)
  3. 1993년: Unicode와 ISO 10646(국제 표준화 기구의 유니버설 문자 집합) 통합 시작
  4. 1996년: Unicode 2.0 발표 (38,950개 문자), UTF-8 포함
  5. 2000년대 초반: 주요 운영 체제와 프로그래밍 언어가 Unicode 지원 시작
  6. 현재: Unicode 15.0(2022년 발표)은 149,186개의 문자를 포함하며, 계속 확장 중

Unicode는 컴퓨터가 모든 문자를 표현할 수 있게 하는 것 외에도, 국제화와 지역화(i18n 및 l10n)의 기반이 되어 소프트웨어가 전 세계적으로 사용될 수 있도록 돕고 있다.

Unicode의 기술적 구조

코드 포인트와 코드 스페이스

Unicode의 핵심 개념은 코드 포인트(Code Point) 이다.
코드 포인트는 0에서 0x10FFFF 사이의 정수 값으로, 특정 문자에 할당된다. 이는 총 1,114,112개(17 × 65,536)의 코드 포인트가 있음을 의미한다.

Unicode 코드 스페이스(Code Space) 는 다음과 같이 구성된다:

이 구조는 다양한 문자 집합을 체계적으로 조직하면서도, 필요에 따라 확장할 수 있는 유연성을 제공한다.

문자 블록과 분류

Unicode는 관련 문자들을 **블록(Block)**으로 그룹화한다.
예를 들어:

또한 Unicode는 문자를 여러 **범주(Category)**로 분류한다:

이러한 분류는 텍스트 처리 알고리즘에서 문자를 카테고리별로 처리할 수 있게 해준다.

조합 문자와 정규화

Unicode는 두 가지 방식으로 문자를 표현할 수 있다:

  1. 선조합(Precomposed): 하나의 코드 포인트로 표현
    • 예: ‘é’ (U+00E9)
  2. 조합(Decomposed): 기본 문자와 결합 문자의 시퀀스로 표현
    • 예: ‘é’ = ’e’ (U+0065) + ‘◌́’ (U+0301)

이로 인해 같은 문자가 서로 다른 코드 포인트 시퀀스로 표현될 수 있으며, 이는 문자열 비교나 검색에 문제를 일으킬 수 있다. 이 문제를 해결하기 위해 **Unicode 정규화(Normalization)**가 사용된다.

Unicode는 네 가지 정규화 형식을 정의한다:

예시:

1
2
NFC: 'é' (U+00E9) - 단일 코드 포인트
NFD: 'e' (U+0065) + '◌́' (U+0301) - 두 개의 코드 포인트

정규화는 문자열 비교, 정렬, 검색 등에서 일관성을 보장하기 위해 중요한다.

Unicode 인코딩 방식

Unicode는 문자와 코드 포인트 간의 매핑만 정의하고, 이 코드 포인트를 바이트로 변환하는 방법은 별도의 인코딩 방식으로 정의된다. 가장 널리 사용되는 Unicode 인코딩은 UTF-8, UTF-16, UTF-32이다.

UTF-8

UTF-8(Unicode Transformation Format 8-bit)은 현재 웹과 대부분의 시스템에서 가장 널리 사용되는 인코딩이다.

특징:

인코딩 규칙:

1
2
3
4
1바이트: 0xxxxxxx                     (ASCII 문자)
2바이트: 110xxxxx 10xxxxxx            (라틴어 확장, 키릴 문자 등)
3바이트: 1110xxxx 10xxxxxx 10xxxxxx   (한글, 한자, 히브리어 등 대부분의 문자)
4바이트: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (이모지, 희귀 문자 등)

예시:

UTF-8은 공간 효율성(영문 위주 텍스트에서), ASCII 호환성, 강건성 등의 이유로 현대 시스템에서 기본 인코딩으로 자리 잡았다.

UTF-16

UTF-16은 모든 Unicode 문자를 2바이트 또는 4바이트로 인코딩한다.
Windows 내부 문자열 처리, Java, JavaScript 등 많은 시스템에서 사용한다.

특징:

인코딩 규칙:

예시:

UTF-32

UTF-32는 모든 Unicode 문자를 고정된 4바이트로 인코딩한다.

특징:

예시:

UTF-32는 간단한 구현과 직접 인덱싱이 가능한 이점이 있지만, 메모리 사용량이 크기 때문에 일반적으로 내부 처리용으로만 제한적으로 사용된다.

인코딩 비교 및 선택 기준

각 인코딩 방식을 비교해보면:

특성UTF-8UTF-16UTF-32
바이트 수1-42 또는 44
ASCII 호환성
공간 효율성 (영문)높음중간낮음
공간 효율성 (아시아 언어)중간높음낮음
직접 인덱싱
엔디안 이슈
주요 사용처웹, 파일, 대부분의 시스템Windows, Java, JS 내부 처리일부 내부 처리

인코딩 선택 기준:

Unicode 관련 주요 문제와 해결책

이모지 및 특수 문자 처리

이모지와 같은 특수 문자는 다음과 같은 문제를 일으킬 수 있다:

  1. 길이 계산 오류: 일부 이모지는 여러 코드 포인트의 조합으로 표현
  2. 서로게이트 쌍 처리: UTF-16에서 BMP 외 문자는 2개의 코드 유닛으로 표현
  3. 이모지 수정자: 피부색 수정자, 성별 수정자 등

해결책:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// JavaScript 예시

// 문제: 단순한 length 속성은 코드 유닛 수를 반환
let family = "👨‍👩‍👧‍👦";
console.log(family.length);  // 11 (여러 코드 포인트의 조합)

// 해결 1: 스프레드 연산자 사용
console.log([...family].length);  // 1 (시각적 문자 수)

// 해결 2: 정규식으로 특수 문자 인식
let regex = /\p{Emoji}/gu;  // Unicode 속성 이스케이프 (ES2018+)
console.log(family.match(regex).length);

정규화와 문자열 비교

두 문자열이 시각적으로 동일해도 다른 코드 포인트 시퀀스로 표현될 수 있어, 비교 문제가 발생한다.

문제 상황:

1
2
3
4
5
6
// JavaScript 예시
let s1 = "\u00E9";          // é (선조합형)
let s2 = "e\u0301";         // é (조합형: e + ◌́)

console.log(s1 === s2);     // false (다른 코드 포인트 시퀀스)
console.log(s1, s2);        // 시각적으로 동일: "é é"

해결책:

1
2
3
4
5
6
7
// JavaScript 예시
// 정규화 후 비교
console.log(s1.normalize('NFC') === s2.normalize('NFC'));  // true
console.log(s1.normalize('NFD') === s2.normalize('NFD'));  // true

// 로케일 인식 비교
console.log(s1.localeCompare(s2, 'en', { sensitivity: 'base' }));  // 0 (동등)

데이터베이스와 저장소 이슈

데이터베이스에서 Unicode 처리 시 발생할 수 있는 문제:

  1. 문자셋 불일치: DB와 애플리케이션 간 문자셋 불일치
  2. 콜레이션 문제: 정렬과 비교에서 언어별 규칙 차이
  3. 저장 크기: 다양한 인코딩에 따른 저장 용량 차이

해결책:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- MySQL 예시

-- 1. 데이터베이스 생성 시 적절한 문자셋 지정
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 2. 테이블 생성 시 문자셋 지정
CREATE TABLE users (
    name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);

-- 3. 콜레이션 선택
-- utf8mb4_unicode_ci: 유니코드 표준 비교 규칙
-- utf8mb4_general_ci: 빠르지만 덜 정확한 비교
-- utf8mb4_bin: 바이너리 비교 (대소문자 구분)

주의사항:

폼과 URL 인코딩

웹 폼 데이터나 URL에서 Unicode 문자를 처리할 때 발생하는 문제:

  1. URL 인코딩: URL은 ASCII 문자만 허용
  2. 폼 데이터 인코딩: 폼 제출 시 인코딩 방식
  3. 파일명 인코딩: 업로드된 파일의 이름 처리

해결책:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// JavaScript 예시

// URL 인코딩
let originalUrl = "https://example.com/search?q=안녕하세요";
let encodedUrl = encodeURI(originalUrl);
console.log(encodedUrl);  // "https://example.com/search?q=%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94"

// URL 컴포넌트 인코딩 (쿼리 파라미터 값 등)
let searchTerm = "안녕하세요?";
let encodedTerm = encodeURIComponent(searchTerm);
console.log(encodedTerm);  // "%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94%3F"

// 폼 데이터 처리
let form = new FormData();
form.append("name", "홍길동");
// FormData 객체는 자동으로 적절한 인코딩 처리

// 파일명 인코딩 (다운로드 헤더)
function setDownloadHeader(filename) {
    // RFC 5987에 따른 인코딩
    let encodedFilename = encodeURIComponent(filename)
        .replace(/'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
    
    return `attachment; filename*=UTF-8''${encodedFilename}`;
}

// Content-Disposition: attachment; filename*=UTF-8''%ED%95%9C%EA%B8%80%20%ED%8C%8C%EC%9D%BC.pdf

Unicode의 고급 기능과 활용

Unicode는 단순히 문자를 나타내는 것 이상의 많은 기능을 제공한다.

양방향 텍스트(BiDi) 처리

히브리어, 아랍어 등의 오른쪽에서 왼쪽으로 쓰는 언어(RTL)와 왼쪽에서 오른쪽으로 쓰는 언어(LTR)가 혼합된 텍스트를 처리하는 방법:

1
2
3
4
5
<!-- HTML에서 방향 지정 -->
<p dir="rtl">هذا نص عربي مع English words.</p>

<!-- BiDi 제어 문자 사용 -->
<p>This is English text with &#x202B;מילים בעברית&#x202C; inside.</p>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// JavaScript에서 BiDi 처리
function applyBiDi(text) {
    // U+202A: LTR Override
    // U+202B: RTL Override
    // U+202C: Pop Directional Formatting
    
    // 아랍어/히브리어 감지
    const hasRTL = /[\u0590-\u05FF\u0600-\u06FF]/.test(text);
    
    if (hasRTL) {
        return '\u202B' + text + '\u202C';  // RTL 컨텍스트 적용
    }
    return text;
}

문자 특성과 속성 활용

Unicode는 각 문자에 대해 다양한 속성 정보를 제공한다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Python 예시
import unicodedata

# 문자 이름 조회
print(unicodedata.name('A'))       # 'LATIN CAPITAL LETTER A'
print(unicodedata.name('한'))      # 'HANGUL SYLLABLE HAN'
print(unicodedata.name('😊'))     # 'SMILING FACE WITH SMILING EYES'

# 문자 분류 확인
print(unicodedata.category('A'))   # 'Lu' (Letter, uppercase)
print(unicodedata.category('1'))   # 'Nd' (Number, decimal digit)
print(unicodedata.category(' '))   # 'Zs' (Separator, space)

# 숫자 값 가져오기
print(unicodedata.numeric('½'))    # 0.5
print(unicodedata.numeric('四'))   # 4.0 (한자 숫자)

# 문자 분해
print(unicodedata.decomposition('Å'))  # 'A 030A' (A + 윗 고리)

대소문자 변환과 제목 케이스

Unicode는 언어별 대소문자 변환 규칙을 정의한다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// JavaScript 예시
// 터키어의 특수 케이스: 'i' ↔ 'İ', 'ı' ↔ 'I'
let turkishText = "istanbul";
console.log(turkishText.toLocaleUpperCase('tr-TR'));  // "İSTANBUL"
console.log(turkishText.toUpperCase());               // "ISTANBUL"

// 독일어 날카로운 S (ß) 대문자 변환
let germanText = "straße";
console.log(germanText.toLocaleUpperCase('de-DE'));  // "STRASSE"

// 제목 케이스 함수 (첫 글자만 대문자)
function toTitleCase(text, locale = 'en-US') {
    return text.replace(/\b\w+/g, word => 
        word.charAt(0).toLocaleUpperCase(locale) + 
        word.slice(1).toLocaleLowerCase(locale)
    );
}

console.log(toTitleCase("the quick brown fox"));  // "The Quick Brown Fox"

문자 분류와 필터링

Unicode 속성을 사용하여 특정 유형의 문자를 식별하고 필터링하는 방법:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// JavaScript 예시 (ES2018 이상)
// Unicode 속성 이스케이프를 이용한 정규식

// 한글 문자 검사
const hangulRegex = /\p{Script=Hangul}/u;
console.log(hangulRegex.test("안녕"));  // true
console.log(hangulRegex.test("Hello"));  // false

// 이모지 검사
const emojiRegex = /\p{Emoji}/u;
console.log(emojiRegex.test("😊"));  // true

// 공백 문자 필터링
const text = "Hello\u00A0World\u2003!";  // 여러 유형의 공백 포함
const cleanText = text.replace(/\p{White_Space}/gu, " ");  // 표준 공백으로 변환

// 문자 유형별 필터링 (예: 문자만 유지)
const alphaOnly = text.replace(/[^\p{Letter}\p{Mark}]/gu, "");
console.log(alphaOnly);  // "HelloWorld"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Python 예시
import re
import unicodedata

# 문자 유형 필터링
def filter_by_category(text, categories):
    return "".join(c for c in text if unicodedata.category(c) in categories)

# 문자와 숫자만 유지
alphanumeric = filter_by_category("Hello, 123!", ['Lu', 'Ll', 'Nd'])
print(alphanumeric)  # "Hello123"

# 악센트 제거 (NFD 정규화 후 결합 표시 제거)
def remove_accents(text):
    nfd = unicodedata.normalize('NFD', text)
    return "".join([c for c in nfd if not unicodedata.combining(c)])

print(remove_accents("café"))  # "cafe"

실무 애플리케이션에서의 Unicode 모범 사례

웹 개발에서의 Unicode 처리

웹 애플리케이션에서 Unicode를 올바르게 처리하는 방법:

HTML 및 HTTP 헤더:

1
2
3
4
5
6
7
8
<!-- HTML에서 문자셋 선언 -->
<meta charset="utf-8">

<!-- 서버 측 응답 헤더 설정 (예: Express.js) -->
app.use((req, res, next) => {
    res.set('Content-Type', 'text/html; charset=utf-8');
    next();
});

폼 및 AJAX 처리:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 폼 설정
<form method="post" accept-charset="utf-8">
    <input name="user" value="홍길동">
</form>

// AJAX 요청
fetch('/api/users', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
    body: JSON.stringify({ name: '홍길동' })
});

데이터베이스 연결:

1
2
3
4
5
6
7
8
9
// Node.js + MySQL 예시
const mysql = require('mysql');
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'user',
    password: 'password',
    database: 'mydb',
    charset: 'utf8mb4'  // 중요: 모든 Unicode 문자 지원
});

다국어 지원(i18n) 구현

다국어 지원을 위한 모범 사례:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 리액트에서 i18next 사용 예시
import { useTranslation } from 'react-i18next';

function MyComponent() {
    const { t, i18n } = useTranslation();
    
    return (
        <div>
            <h1>{t('welcome')}</h1>
            <p>{t('hello', { name: '홍길동' })}</p>
            
            <button onClick={() => i18n.changeLanguage('ko')}>한국어</button>
            <button onClick={() => i18n.changeLanguage('en')}>English</button>
        </div>
    );
}

// 번역 파일 (ko.json)
{
    "welcome": "환영합니다",
    "hello": "안녕하세요, {{name}}님"
}

날짜, 시간, 숫자 형식화:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Intl 객체 사용 (ECMAScript Internationalization API)

// 날짜 형식화
const date = new Date();
const formatter = new Intl.DateTimeFormat('ko-KR', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long'
});
console.log(formatter.format(date));  // "2023년 5월 15일 월요일"

// 숫자 형식화
const number = 1234567.89;
console.log(new Intl.NumberFormat('ko-KR').format(number));  // "1,234,567.89"
console.log(new Intl.NumberFormat('de-DE').format(number));  // "1.234.567,89"

// 통화 형식화
console.log(new Intl.NumberFormat('ko-KR', {
    style: 'currency',
    currency: 'KRW'
}).format(number));  // "₩1,234,568"

파일 시스템 및 파일명 처리

파일 시스템에서 Unicode 문자를 처리할 때 고려해야 할 사항:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Node.js 예시
const fs = require('fs');
const path = require('path');

// 파일 읽기
fs.readFile('한글파일명.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});

// 파일 쓰기
fs.writeFile('새로운파일.txt', '유니코드 텍스트', 'utf8', (err) => {
    if (err) throw err;
    console.log('파일이 저장되었습니다.');
});

// 안전한 파일명 생성
function sanitizeFilename(filename) {
    // 파일 시스템 금지 문자 및 제어 문자 제거
    return filename
        .replace(/[\/\?<>\\:\*\|"]/g, '_')  // 금지된 문자
        .replace(/[\x00-\x1f\x80-\x9f]/g, '');  // 제어 문자
}

// 웹에서 다운로드 파일명 설정
function setDownloadHeader(res, filename) {
    const encodedFilename = encodeURIComponent(filename);
    res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodedFilename}`);
}

보안 고려사항

Unicode와 관련된 보안 이슈와 그 대응 방법:

문자 혼동 공격(Homograph Attack) 방지:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 유사 문자(예: 라틴 'a'와 키릴 'а') 방지
function detectHomographs(domain) {
    // 다른 스크립트의 문자 섞임 감지
    let scripts = new Set();
    
    for (const char of domain) {
        // 각 문자의 스크립트 속성 확인
        let script = getScriptOfChar(char);
        if (script !== 'Common' && script !== 'Inherited') {
            scripts.add(script);
        }
    }
    
    // 여러 스크립트가 섞여 있으면 의심
    return scripts.size > 1;
}

function getScriptOfChar(char) {
    // Unicode 스크립트 속성 확인 로직
    // 실제로는 라이브러리 사용 권장
    // ...
}

XSS 방지를 위한 이스케이핑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// XSS 방어를 위한 HTML 이스케이핑
function escapeHtml(text) {
    return text
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

// 의도적인 제어 문자 삽입 방지
function stripControlChars(text) {
    return text.replace(/[\u0000-\u001F\u007F-\u009F]/g, '');
}

정규화 일관성 유지:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 일관된 정규화로 우회 방지
function normalizeInput(input) {
    // NFC로 일관되게 정규화
    return input.normalize('NFC');
}

// 데이터베이스 쿼리 전 정규화
function prepareForQuery(text) {
    return normalizeInput(text.trim());
}

미래 전망 및 발전 방향

  1. Unicode는 2022년 9월에 15.0 버전까지 출시되었으며:

    • 149,186개의 문자 포함
    • 159개의 현대 및 역사적 문자 체계 지원
    • 이모지, 수학 기호, 악보 기호 등 다양한 특수 기호 포함
    • 대부분의 컴퓨팅 환경에서 기본 표준으로 자리잡음
  2. 향후 발전 방향
    Unicode의 발전 추세와 예상되는 미래 방향:

    1. 계속되는 문자 추가: 역사적 문자, 희귀 문자 체계의 지속적 추가
    2. 이모지 확장: 새로운 이모지의 정기적 추가 및 개선
    3. 알고리즘 개선: 문자 분류, 텍스트 세분화, 양방향 알고리즘 등 개선
    4. CLDR 확장: 더 많은 언어와 지역에 대한 로케일 데이터 추가
    5. 웹과 모바일 통합 강화: 웹 표준과의 긴밀한 통합 계속
    6. AI 및 자연어 처리 지원: 다국어 AI 시스템 개발을 위한 기반 제공
  3. 개발자 준비 사항
    미래 대비를 위한 개발자 준비:

    1. UTF-8 표준화: 모든 새 프로젝트에서 UTF-8을 기본 인코딩으로 사용
    2. 가변 길이 문자 대응: 이모지 및 결합 문자 시퀀스 올바르게 처리
    3. 국제화 기본 적용: 초기부터 i18n 고려한 설계
    4. 최신 API 활용: Intl 객체 등 최신 국제화 API 익히기
    5. 정규식 현대화: Unicode 속성 이스케이프 활용한 정교한 정규식 패턴 사용
    6. 지속적 학습: Unicode 표준 업데이트 주시 및 새로운 기능 학습

용어 정리

용어설명

참고 및 출처