서로게이트
고친 사람 github-actions[bot]
서로게이트는 무언가를 대신하는 것에 붙는 이름입니다. 웹에서는 원래 서버를 대신해 응답을 내주는 중간 서버를 이렇게 부릅니다. 문자 인코딩에서는 글자마다 매긴 번호 가운데, 이모지처럼 번호가 큰 글자를 둘로 쪼개 적으려고 비워 둔 번호를 가리킵니다. 데이터베이스에서는 업무와 상관없이 매긴 키를 이 이름으로 부릅니다.
쉽고 빠른 이해
무슨 일을 하나 — 영어 낱말의 뜻은 「대리」입니다. 무엇을 대신하는지는 분야마다 다릅니다.
- 웹 — 원래 서버 대신 응답하는 중간 서버
- 문자열 — 글자 하나를 두 칸에 나눠 적을 때 쓰는 번호. 칸은 문자열을 두 바이트씩 끊어 적는 단위입니다
- 데이터베이스 — 업무 데이터 대신 행을 가리키는 일련번호 키
왜 있나 — 웹의 서로게이트가 없으면 모든 요청이 원래 서버까지 가야 합니다. 서버는 바빠집니다. 먼 사용자는 오래 기다립니다. 문자열의 서로게이트가 없으면 두 바이트 칸 하나로 적는 방식은 이모지 같은 큰 번호의 글자를 아예 못 적습니다.
어떻게 도나
- 웹 — 중간 서버가 원래 서버의 응답을 저장해 두었다가 같은 요청에 바로 내줍니다
- 문자열 — 칸 하나에 안 들어가는 번호를 앞 조각과 뒤 조각으로 나눕니다
- 두 조각은 글자에 쓰지 않고 비워 둔 번호라서 보통 글자와 헷갈리지 않습니다
대가 — 웹에서는 원래 서버가 내용을 바꿔도 중간 서버가 한동안 옛 응답을 내줄 수 있습니다. 문자열에서는 글자 하나가 길이 2 로 세어집니다. 길이만 보고 문자열을 자르면 글자가 반으로 쪼개집니다.
어느 뜻인지 가르기 — 함께 나오는 낱말을 봅니다. 캐시·오리진과 같이 나오면 웹, 글자·이모지·쌍과 같이 나오면 문자열, 키·테이블과 같이 나오면 데이터베이스입니다.
상세
이 절은 세 분야의 뜻을 표로 가른 뒤 웹 캐싱, 유니코드, 데이터베이스의 뜻을 차례로 봅니다.
세 분야의 세 뜻
세 뜻은 영어 낱말 surrogate 의 「대리」라는 뜻만 같이 씁니다. 하는 일은 서로 이어지지 않습니다. 그래서 문장 앞뒤로 어느 분야 이야기인지부터 정해야 읽힙니다.
| 분야 | 서로게이트가 가리키는 것 | 무엇을 대신하나 |
|---|---|---|
| 웹 캐싱 | 원래 서버 쪽에 서서 대신 응답하는 중간 서버 | 콘텐츠의 원본을 가진 서버 |
| 유니코드 | 큰 번호를 둘로 쪼개 적으려고 비워 둔 번호 | 두 바이트 칸 하나에 안 들어가는 글자 |
| 데이터베이스 | 업무 의미 없이 매긴 일련번호 키 | 업무 데이터에서 골라 쓰던 키 |
웹 캐싱의 서로게이트
웹에서 콘텐츠의 원본을 가진 서버를 오리진 서버라고 부릅니다. 쇼핑몰이라면 상품 페이지를 실제로 만들어 내는 애플리케이션 서버가 오리진입니다.
클라이언트와 오리진 사이에는 중간 서버가 끼는 일이 흔합니다. 요청을 받아 대신 전달하는 중간 서버를 넓게 프록시라고 부릅니다. 프록시는 누구 편에 서느냐로 갈립니다.
포워드 프록시는 클라이언트 편입니다. 회사망 출구에서 직원들의 요청을 모아 바깥으로 내보내는 서버가 그 예입니다. 클라이언트 쪽에서 이 프록시를 쓰겠다고 설정합니다.
서로게이트는 반대편, 오리진 편에 섭니다. 오리진을 운영하는 쪽이 직접 두거나, 오리진을 대신해 응답할 권한을 위임받습니다. 오리진 바로 앞에 놓이기도 하고 사용자 가까운 망의 다른 지점에 놓이기도 합니다.
flowchart TD
subgraph 클라이언트편["클라이언트 편"]
U["클라이언트"] --> F["포워드 프록시"]
end
F --> N["인터넷"]
subgraph 오리진편["오리진 편"]
S["서로게이트"] --> O["오리진 서버"]
end
N --> S
두 중간 서버는 같은 길의 양 끝에 섭니다.
클라이언트가 보기에 서로게이트는 오리진과 구별되지 않습니다. 클라이언트는 그 주소를 오리진의 주소로 알고 요청을 보냅니다. 클라이언트는 중간 서버를 따로 설정하지 않습니다. 있는지도 모릅니다.
서로게이트는 응답을 대개 자기 캐시에서 내줍니다. 캐시는 한 번 받은 응답을 저장해 두고 같은 요청이 오면 다시 내주는 저장소입니다. 캐시에 응답이 있으면 요청은 오리진까지 가지 않습니다.
이렇게 해서 얻는 것은 둘입니다. 오리진이 받는 요청이 줄어듭니다. 서로게이트가 사용자 가까이 있으면 응답이 오가는 거리도 짧아집니다.
대가는 낡은 응답입니다. 오리진이 상품 가격을 바꿔도 서로게이트는 저장해 둔 옛 페이지를 한동안 내줍니다. 그래서 응답마다 저장해 둘 시간을 정합니다. 급할 때는 저장된 응답을 지우는 퍼지를 합니다.
서로게이트에 속하는 이름들
현장에서 쓰는 여러 이름이 이 뜻의 서로게이트에 속합니다. 그중 현장에 가장 굳은 이름은 리버스 프록시입니다.
리버스 프록시는 오리진 앞에 서서 요청을 받아 넘기는 서버입니다. 오리진 편에서 대신 응답하니 서로게이트의 한 종류입니다.
오리진 서버 가속기는 오리진 앞에서 응답을 캐시해 빠르게 내주는 장치를 부르던 옛 이름입니다. 이것도 서로게이트의 한 종류입니다.
CDN(Content Delivery Network, 콘텐츠 전송 네트워크)은 세계 여러 곳에 서버를 깔아 사용자 가까이에서 응답하는 서비스입니다. 이렇게 사용자 가까이 깔린 서버를 엣지 서버라고 부릅니다. 엣지 서버 하나하나가 오리진을 대신하는 서로게이트입니다.
서로게이트라는 말 자체는 지금 주로 CDN 설명과 웹 캐싱 문서에서 만납니다. 나머지 곳에서는 리버스 프록시나 엣지 서버라는 이름이 더 흔합니다.
유니코드의 서로게이트
유니코드는 세상의 글자마다 번호를 하나씩 매긴 표입니다. 이 번호를 코드 포인트라고 부릅니다.
코드 포인트는 U+ 뒤에 16진수로 적습니다. 영문 A 는 U+0041, 한글 가 는 U+AC00 입니다. 번호는 U+0000 부터 U+10FFFF 까지 있습니다.
번호를 실제 바이트로 적는 방법이 여럿입니다. UTF-16은 그중 하나로, 16비트 곧 두 바이트를 한 칸으로 씁니다. 이름은 유니코드 변환 형식 16비트(Unicode Transformation Format 16-bit)를 줄인 것입니다.
이 한 칸을 코드 유닛이라고 부릅니다. 코드 유닛 하나에 담을 수 있는 번호는 U+FFFF 까지입니다. 그 위의 번호에는 이모지와 옛 문자, 드물게 쓰는 한자가 있습니다. 이런 글자는 코드 유닛 둘을 짝지어 적어야 합니다.
짝을 지으려고 유니코드는 U+D800 부터 U+DFFF 까지 2,048 개 번호를 어떤 글자에도 주지 않고 비워 두었습니다. 이 번호들이 서로게이트 코드 포인트입니다.
비워 둔 범위는 앞뒤 반으로 나뉩니다. 앞쪽 1,024 개(U+D800~U+DBFF)는 상위 서로게이트입니다. 뒤쪽 1,024 개(U+DC00~U+DFFF)는 하위 서로게이트입니다.
상위 하나와 하위 하나를 차례로 놓은 짝이 서로게이트 쌍입니다. 우리말로는 대리 쌍이라고도 부릅니다. 쌍 하나가 U+FFFF 를 넘는 글자 하나를 나타냅니다.
큰 번호가 쌍이 되는 과정
쪼개는 방법은 정해져 있습니다. 먼저 번호에서 0x10000 을 뺍니다. 가장 큰 번호 U+10FFFF 에서 빼도 0xFFFFF 라서, 남는 수는 늘 20비트에 들어갑니다.
이 20비트를 위 10비트와 아래 10비트로 가릅니다. 위 10비트는 0xD800 에 더합니다. 아래 10비트는 0xDC00 에 더합니다.
웃는 얼굴 이모지 😀 는 U+1F600 입니다. 이 번호가 두 칸이 되는 길은 아래와 같습니다.
flowchart TD
A["U+1F600"] --> B["0x10000 을 뺀다 · 0x0F600 (20비트)"]
B --> C["위 10비트 · 0x3D"]
B --> D["아래 10비트 · 0x200"]
C --> E["0xD800 에 더한다 · D83D"]
D --> F["0xDC00 에 더한다 · DE00"]
그림의 0x0F600 은 남은 수를 20비트 폭에 맞춰 앞에 0 을 채운 값입니다. 16비트 0xF600 으로 읽고 위 10비트를 뽑으면 엉뚱한 값이 나옵니다.
결국 UTF-16 으로 적은 😀 는 D83D DE00 두 칸입니다. 앞 칸은 상위 서로게이트 범위에, 뒤 칸은 하위 서로게이트 범위에 들어 있습니다.
상위 범위와 하위 범위는 서로 안 겹칩니다. 보통 글자의 번호와도 안 겹칩니다. 덕분에 칸 하나만 보고도 그것이 보통 글자인지, 쌍의 앞 조각인지, 뒤 조각인지 압니다. 문자열 중간부터 읽기 시작해도 글자 경계를 다시 찾을 수 있습니다.
서로게이트는 글자가 아니다
서로게이트 번호는 UTF-16 이 쌍을 만들 때만 씁니다. 그 자체로는 아무 글자도 뜻하지 않습니다.
다른 인코딩은 쌍을 만들지 않습니다. UTF-8 은 글자 하나를 1~4 바이트로 적습니다. UTF-32 는 글자 하나를 4바이트 한 칸에 적습니다. 이 둘로 서로게이트 번호 하나를 적은 바이트열은 올바른 텍스트가 아닙니다.
UTF-16 안에서도 짝이 맞아야 합니다. 상위 뒤에 하위가 안 오거나 하위가 홀로 나오면 짝 없는 서로게이트입니다. 받는 쪽은 이를 오류로 다루거나 대체 문자 � 로 바꿔 보여 줍니다.
백엔드 코드에서 만나는 서로게이트
자바의 String 은 UTF-16 코드 유닛을 나열해 담습니다. char 하나가 코드 유닛 하나입니다. 그래서 length() 는 글자 수가 아니라 코드 유닛 수를 돌려줍니다.
String s = "😀";
s.length(); // 2
s.codePointCount(0, s.length()); // 1
char hi = s.charAt(0);
Integer.toHexString(hi); // "d83d"
Character.isHighSurrogate(hi); // true
사람 눈에는 글자 하나인데 length() 는 2 를 돌려줍니다. 글자 수가 필요하면 codePointCount 로 코드 포인트를 셉니다. 첫 char 는 상위 서로게이트 D83D 입니다.
이 차이가 장애로 이어지는 곳은 문자열 자르기입니다. 닉네임을 substring(0, 10) 처럼 길이로 자르면 쌍 한가운데가 잘릴 수 있습니다. 상위 서로게이트만 남은 문자열을 UTF-8 로 바꿔 데이터베이스에 넣으면 저장이 실패하거나 ?·� 가 들어갑니다.
JSON(JavaScript Object Notation) 문자열의 \u 이스케이프도 코드 유닛 하나를 16진수 네 자리로 적습니다. 그래서 😀 를 이스케이프로 적으면 \uD83D\uDE00 처럼 두 덩어리가 됩니다. 앞에서 본 D83D 와 DE00 두 칸이 그대로 적힌 것입니다. 두 덩어리를 따로 다루면 같은 문제가 납니다.
데이터베이스의 대리 키
데이터베이스에서는 서로게이트가 홀로 쓰이지 않고 서로게이트 키(surrogate key)로 나옵니다. 우리말로는 대리 키라고 부릅니다. 업무 의미가 없는 일련번호를 행마다 매겨 기본 키로 쓰는 방식입니다.
대리 키가 대신하는 것은 자연 키입니다. 자연 키는 이메일이나 사업자 번호처럼 업무 데이터 안에서 골라 쓰는 키입니다. 자연 키는 바뀌거나 겹칠 수 있습니다. 그래서 바뀌지 않는 번호를 따로 매겨 행을 가리킵니다.
어느 뜻인지 가르는 단서
문서에서 서로게이트를 만나면 함께 나오는 낱말을 봅니다. 대개 그것만으로 뜻이 갈립니다.
| 함께 나오는 말 | 뜻 |
|---|---|
| 오리진 · 캐시 · CDN · 프록시 · 엣지 · 퍼지 | 웹 캐싱의 중간 서버 |
쌍 · UTF-16 · U+D800 · 코드 유닛 · 이모지 · 문자열 길이 |
유니코드의 비워 둔 번호 |
| 키 · 테이블 · 기본 키 · 자연 키 · 일련번호 | 데이터베이스의 대리 키 |
「서로게이트 키」는 한 번 더 갈립니다. 테이블 설계 이야기라면 대리 키입니다. CDN 설명에 나오면 일부 CDN 이 캐시된 응답에 붙이는 태그를 가리킵니다. 같은 태그가 붙은 응답을 한꺼번에 퍼지할 때 씁니다.
관련 항목
웹 캐싱에서 서로게이트로 묶이는 중간 서버
리버스 프록시 · 오리진 서버 가속기 · 엣지 서버 · CDN · 게이트웨이 · 공유 캐시
웹 서로게이트가 대신하거나 맞세워지는 참여자
오리진 서버 · 프록시 · 클라이언트 · 사용자 에이전트 · 서버
웹 서로게이트가 캐시를 다루는 수단
캐싱 · Cache-Control · Surrogate-Control · TTL · 퍼지 · 서로게이트 키
유니코드 서로게이트를 이루는 단위
코드 포인트 · 코드 유닛 · 대리 쌍 · 상위 서로게이트 · 하위 서로게이트 · 보충 평면 · 기본 다국어 평면
유니코드 서로게이트를 쓰거나 금지하는 인코딩
UTF-16 · UTF-8 · UTF-32 · CESU-8 · 문자 인코딩 · Unicode
짝 없는 서로게이트가 일으키는 문자열 문제
짝 없는 서로게이트 · 대체 문자 · 모지바케 · 문자열 · JSON
데이터베이스에서 대리 키와 맞세워지는 키
다른 이름: 써로게이트 · 서러게이트