DNS의 원리 (2) - DNS 질의와 해석 과정 - soo:bak
작성일 :
이름에서 주소까지
DNS의 원리 (1)에서 DNS의 계층 구조와 레코드 타입을 살펴보았습니다.
이제 실제로 이름이 어떻게 IP 주소로 변환되는지 알아봅니다.
브라우저에 www.example.com을 입력하면 무슨 일이 일어날까요?
답을 얻기까지 눈에 보이지 않는 여러 단계를 거칩니다.
DNS 질의의 세 구성 요소
Stub Resolver
도메인 이름을 IP 주소로 변환하는 소프트웨어를 통칭하여 리졸버(Resolver)라고 합니다.
가벼운 라이브러리부터 독립적인 DNS 서버까지, 형태는 다양합니다.
Stub Resolver(스텁 리졸버)는 그중 가장 가벼운 형태로, 운영체제에 내장되어 있습니다.
역할은 세 가지입니다.
- 애플리케이션으로부터 DNS 질의 요청을 받음
- 설정된 DNS 서버에 질의 전송
- 응답을 애플리케이션에 전달
이때 질의를 넘겨받는 DNS 서버가 Recursive Resolver입니다.
Recursive Resolver
Recursive Resolver(재귀 리졸버)는 Stub Resolver로부터 질의를 받아 IP 주소를 찾아주는 DNS 서버입니다. 단, 도메인 정보를 자체적으로 갖고 있지는 않습니다.
도메인은 Zone 단위로 서로 다른 서버가 나누어 관리합니다. www.example.com의 IP를 찾으려면 .com 담당 서버, example.com 담당 서버를 차례로 질의해야 하는데, Recursive Resolver가 이 과정을 수행합니다.
한 번 찾아낸 DNS 질의 결과를 저장해 두고, 같은 질의가 다시 들어오면 저장된 값을 바로 반환합니다. DNS 조회 결과를 캐싱하는 리졸버이므로 Caching Resolver라고도 합니다.
네트워크 설정에 입력하는 8.8.8.8, 1.1.1.1 등은 Recursive Resolver의 주소입니다.
Authoritative Server
Authoritative Server(권한 있는 서버)는 특정 Zone의 데이터를 직접 관리하는 서버입니다.
앞서 Recursive Resolver가 차례로 질의하는 .com 담당 서버, example.com 담당 서버가 모두 Authoritative Server입니다.
DNS의 원리 (1)에서 살펴본 Zone File과 레코드를 보관하며, Recursive Resolver는 이 서버에서 최종 IP 주소를 얻습니다.
1
2
3
4
5
6
구성 요소 관계도
┌─────────┐ 질의 ┌─────────────────┐ 질의 ┌──────────────┐
│ Stub │ ──────▶ │ Recursive │ ──────▶ │ Authoritative│
│ Resolver│ ◀────── │ Resolver │ ◀────── │ Server │
└─────────┘ 응답 └─────────────────┘ 응답 └──────────────┘
세 구성 요소의 역할은 정해져 있지만, 이들 사이의 질의 방식은 서로 다릅니다.
재귀적 질의 vs 반복적 질의
Stub Resolver → Recursive Resolver 구간은 재귀적 질의, Recursive Resolver → Authoritative Server 구간은 반복적 질의를 사용합니다.
재귀적 질의(Recursive Query)
Stub Resolver는 Recursive Resolver에 질의를 보내고 최종 IP 주소를 응답받습니다.
1
2
3
4
5
6
7
8
Stub Resolver Recursive Resolver
│ │
│ www.example.com의 IP 주소는? │
│ ────────────────────────────────▶│
│ │
│ 93.184.216.34 │
│◀──────────────────────────────── │
│ │
Stub Resolver가 보는 것은 이것이 전부입니다. Recursive Resolver가 그 사이에 어떤 서버를 몇 번 거쳤는지는 보이지 않습니다.
이처럼 질의를 받은 쪽이 최종 결과까지 알아서 찾아오는 방식을 재귀적 질의라고 합니다.
반복적 질의(Iterative Query)
Recursive Resolver가 Root Server에 www.example.com의 IP 주소를 질의합니다.
1
2
3
4
5
6
7
8
Recursive Resolver Root Server
│ │
│ www.example.com의 IP는? │
│ ───────────────────────────▶ │
│ │
│ .com TLD 서버 주소 안내 │
│◀─────────────────────────────│
│ │
IP 주소가 아닌, .com을 담당하는 TLD 서버 주소를 안내받습니다. Recursive Resolver는 안내받은 서버에 같은 질문을 다시 보냅니다.
1
2
3
4
5
6
7
8
Recursive Resolver .com TLD Server
│ │
│ www.example.com의 IP는? │
│ ───────────────────────────▶ │
│ │
│ example.com 네임서버 주소 안내│
│◀─────────────────────────────│
│ │
이번에도 IP 주소 대신 example.com을 담당하는 서버 주소를 안내받습니다.
이처럼 Recursive Resolver가 안내를 따라 직접 다음 서버에 질의를 반복하는 방식을 반복적 질의라고 합니다.
실제 조회 과정
앞서 본 두 방식이 하나의 조회에서 함께 동작합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
브라우저
│ ①
Stub Resolver
│ ② 재귀적 질의
Recursive Resolver
│
├── ③ ──▶ Root Server
│ ◀── ④ .com TLD 서버 주소 안내
│
├── ⑤ ──▶ .com TLD Server
│ ◀── ⑥ example.com NS 주소 안내
│
├── ⑦ ──▶ example.com 권한 서버
│ ◀── ⑧ 93.184.216.34
│
Recursive Resolver
│ ⑨
Stub Resolver
│ ⑩
브라우저
- ①~② Stub Resolver가 Recursive Resolver에 재귀적 질의를 보냅니다.
- ③~⑧ Recursive Resolver가 Root → TLD → 권한 서버를 차례로 반복적 질의하여 IP 주소를 얻습니다.
- ⑨~⑩ 최종 IP 주소가 Stub Resolver를 거쳐 브라우저에 전달됩니다.
캐시가 없다면 도메인 하나를 조회할 때마다 4번의 왕복이 필요합니다.
매번 루트 서버부터 조회하면 불필요한 부하와 지연이 발생합니다.
캐싱과 TTL
DNS 질의 결과는 여러 지점에서 캐싱됩니다. 질의는 가장 가까운 캐시부터 확인하고, 결과가 없으면(캐시 미스) 다음 단계로 넘어갑니다.
1
2
3
4
5
6
7
8
9
10
브라우저 캐시
│ 캐시 미스
▼
OS 캐시 (Stub Resolver)
│ 캐시 미스
▼
Recursive Resolver 캐시
│ 캐시 미스
▼
Authoritative Server 조회
브라우저와 OS(Stub Resolver)는 개별 기기의 캐시이고, Recursive Resolver는 네트워크 내 여러 사용자가 공유하는 캐시입니다. 공유 캐시이므로 다른 사용자가 이미 조회한 결과가 남아 있을 가능성이 높고, .com이나 .net 같은 TLD 서버 주소는 거의 항상 캐시에 남아 있어 Root 서버를 거칠 필요가 없습니다.
TTL (Time To Live)
캐시에 저장된 정보가 영원히 유효할 수는 없습니다. 원본 레코드가 변경되면 캐시된 정보는 틀린 정보가 되기 때문입니다.
이를 방지하기 위해 모든 DNS 레코드에는 TTL(Time To Live)이 포함되어 있습니다. 해당 레코드를 캐시에 보관할 수 있는 시간을 초 단위로 지정하며, 브라우저, OS, Recursive Resolver 모두 이 값을 따릅니다.
1
2
3
example.com. 3600 IN A 93.184.216.34
│
└── TTL: 3600초 (1시간)
이 레코드를 캐시한 곳은 1시간 동안 보관하며, 이후에는 폐기하고 필요할 때 다시 조회합니다.
TTL이 짧으면 변경사항이 빠르게 반영되지만 질의 횟수가 늘어나고, 길면 질의 횟수는 줄지만 변경사항 반영이 느려집니다.
네거티브 캐싱
존재하지 않는 도메인 asdfgh123.example.com을 조회하면 Authoritative Server가 “없음(NXDOMAIN)”을 응답합니다. 네거티브 캐싱(Negative Caching)은 이 “없음” 응답도 캐싱하는 것입니다. 같은 질의가 반복되면 캐시에서 바로 “없음”을 반환하여 불필요한 조회를 줄입니다.
일반 레코드는 자체 TTL이 캐시 유효 시간이지만, “없음” 응답에는 반환할 레코드가 없습니다.
대신 네거티브 캐시 TTL은 DNS의 원리 (1)에서 소개한 SOA 레코드에서 결정됩니다. SOA 레코드 자체의 TTL과 MINIMUM 필드 중 작은 값을 사용합니다(RFC 2308).
1
2
3
4
5
example.com. 86400 IN SOA ns1.example.com. admin.example.com. (
│ ...
│ 3600 ) ; MINIMUM
│ │
└─── 둘 중 작은 값 = 네거티브 캐시 TTL ──┘
이 예시에서 SOA 레코드의 TTL은 86400초(24시간), MINIMUM 필드는 3600초(1시간)이므로 네거티브 캐시 TTL은 3600초입니다.
RFC 2308은 네거티브 캐시 TTL로 1~3시간을 권장합니다.
DNS 메시지 구조
지금까지 질의가 어떤 순서로 이루어지는지 살펴봤습니다. 이제 질의와 응답이 어떤 형태로 전달되는지 알아봅니다.
프로토콜 기본
DNS는 기본적으로 UDP 포트 53을 사용합니다. DNS 질의와 응답은 대부분 하나의 패킷에 담길 만큼 작고, 소켓과 전송 계층 (1)에서 설명했듯이 UDP는 연결 설정 없이 패킷 왕복 한 번으로 통신이 완료되기 때문입니다.
다만 UDP를 사용할 수 없는 경우에는 TCP 포트 53을 사용합니다.
- 응답이 512바이트(RFC 1035가 정한 UDP DNS 메시지의 최대 크기)를 초과할 때
- Zone Transfer(Zone 데이터 전체를 다른 서버로 복제하는 작업)처럼 대량의 데이터를 전송할 때
- DNSSEC(DNS 보안 확장)으로 응답이 커질 때
메시지 형식
DNS 메시지는 항상 다섯 개 섹션으로 구성됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────┐
│ Header │
│ (ID, Flags, 각 섹션의 레코드 수) │
├─────────────────────────────────────────────┤
│ Question │
│ (질의할 도메인 이름, 타입, 클래스) │
├─────────────────────────────────────────────┤
│ Answer │
│ (질의에 대한 응답 레코드) │
├─────────────────────────────────────────────┤
│ Authority │
│ (해당 도메인의 NS 레코드) │
├─────────────────────────────────────────────┤
│ Additional │
│ (NS의 IP 주소 등 부가 정보) │
└─────────────────────────────────────────────┘
질의와 응답 모두 이 구조를 그대로 사용합니다. 질의 메시지에서는 Header와 Question만 내용이 있고 나머지 세 섹션은 비어 있습니다. 응답 메시지에서는 Answer, Authority, Additional에 조회 결과가 채워집니다.
Header 섹션
Header는 모든 DNS 메시지의 처음 12바이트를 차지합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Header (12바이트)
┌──────────────────────┐
│ ID │ 2바이트
├──────────────────────┤
│ Flags │ 2바이트
│ (QR, AA, TC, RD, │
│ RA, RCODE 등) │
├──────────────────────┤
│ QDCOUNT │ 2바이트
├──────────────────────┤
│ ANCOUNT │ 2바이트
├──────────────────────┤
│ NSCOUNT │ 2바이트
├──────────────────────┤
│ ARCOUNT │ 2바이트
└──────────────────────┘
ID는 16비트 식별자입니다. 질의를 보낸 쪽이 설정하고 응답에 그대로 포함되어 질의-응답 쌍을 식별합니다.
Flags:
| 필드 | 설명 |
|---|---|
| QR | 0=질의, 1=응답 |
| AA | Authoritative Answer. Authoritative Server가 직접 응답했는지 여부 |
| TC | Truncated. 응답이 잘려 TCP로 재시도 필요 |
| RD | Recursion Desired. Stub Resolver가 Recursive Resolver에 재귀 질의를 요청할 때 1로 설정 |
| RA | Recursion Available. Recursive Resolver가 재귀 질의를 지원하는지 여부 |
| RCODE | 응답 코드. 아래 표 참조 |
QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT는 각각 Question, Answer, Authority, Additional 섹션의 레코드 수입니다.
RCODE의 주요 값:
| 값 | 이름 | 의미 |
|---|---|---|
| 0 | NOERROR | 성공 |
| 1 | FORMERR | 형식 오류 |
| 2 | SERVFAIL | 서버 실패 |
| 3 | NXDOMAIN | 도메인 없음 |
| 4 | NOTIMP | 미구현 |
| 5 | REFUSED | 거부됨 |
Question 섹션
1
2
3
4
5
6
7
8
9
10
11
Question 섹션
┌──────────────────────────┐
│ QNAME │ 가변 길이
│ (질의할 도메인 이름) │
├──────────────────────────┤
│ QTYPE │ 2바이트
│ (레코드 타입: A, AAAA 등)│
├──────────────────────────┤
│ QCLASS │ 2바이트
│ (거의 항상 IN=Internet) │
└──────────────────────────┘
QNAME은 도메인 이름을 점(.) 기준으로 나눈 각 부분(레이블) 앞에 길이 바이트를 붙이고, 마지막을 0으로 끝내는 방식으로 인코딩됩니다.
1
2
3
4
5
6
7
www.example.com
인코딩:
03 77 77 77 → 3(길이) w w w
07 65 78 61 6D 70 6C 65 → 7(길이) e x a m p l e
03 63 6F 6D → 3(길이) c o m
00 → 종료
Answer, Authority, Additional 섹션
세 섹션 모두 같은 Resource Record(RR) 형식입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Resource Record (RR)
┌───────────────────────────────┐
│ NAME │ 가변 길이
│ (도메인 이름) │
├───────────────────────────────┤
│ TYPE │ 2바이트
│ (A, AAAA, CNAME, NS 등) │
├───────────────────────────────┤
│ CLASS │ 2바이트
│ (거의 항상 IN=Internet) │
├───────────────────────────────┤
│ TTL │ 4바이트
│ (캐시 유효 시간, 초 단위) │
├───────────────────────────────┤
│ RDLENGTH │ 2바이트
│ (RDATA의 길이) │
├───────────────────────────────┤
│ RDATA │ 가변 길이
│ (실제 데이터) │
└───────────────────────────────┘
예를 들어 A 레코드의 RDATA는 4바이트 IPv4 주소이고, TTL은 앞서 살펴본 캐시 유효 시간입니다.
메시지 압축
www.example.com을 질의한 응답에는 같은 도메인 이름이 여러 섹션에 등장합니다.
Question과 Answer에 www.example.com이, Authority와 Additional에 ns1.example.com이 각각 반복됩니다.
DNS는 이 반복을 줄이기 위해 이름 압축(Name Compression)을 사용합니다.
1
2
3
4
5
Question 섹션:
www.example.com ← 원본 (17바이트)
Answer 섹션:
[포인터 → Question의 www.example.com] ← 2바이트
같은 이름이 두 번째로 등장할 때 17바이트를 다시 쓰는 대신, 메시지 내에서 이미 등장한 위치를 가리키는 2바이트 포인터로 대체합니다.
Glue Record
반복적 질의에서 .com TLD 서버는 example.com의 네임서버 이름을 안내합니다.
그런데 네임서버 이름이 ns1.example.com처럼 자신의 도메인 아래에 있으면 문제가 생깁니다.
ns1.example.com의 IP를 알려면 example.com의 네임서버에 질의해야 하는데, 그 네임서버가 바로 ns1.example.com이기 때문입니다.
.com 서버는 이 순환을 끊기 위해 네임서버 이름과 함께 IP 주소도 응답에 포함합니다.
1
2
3
4
5
6
7
8
9
.com 서버의 응답
Authority 섹션:
example.com NS ns1.example.com
example.com NS ns2.example.com
Additional 섹션:
ns1.example.com A 93.184.216.1 ← Glue Record
ns2.example.com A 93.184.216.2 ← Glue Record
Recursive Resolver는 네임서버 이름과 IP를 한 번에 받아 바로 질의할 수 있습니다.
이처럼 순환 참조를 끊기 위해 Additional 섹션에 제공되는 A 레코드를 Glue Record라고 합니다.
로컬 DNS 설정
Stub Resolver가 질의를 보낼 Recursive Resolver의 주소는 운영체제 설정 파일에 지정되어 있습니다.
/etc/resolv.conf
Linux/macOS에서 Stub Resolver가 참조하는 DNS 설정 파일입니다.
1
2
3
4
# /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
search example.com
nameserver는 질의를 보낼 Recursive Resolver의 주소입니다. 여러 줄을 지정하면 첫 번째 서버에서 응답이 없을 때 다음 서버로 넘어갑니다.
search는 도메인 이름에 점(.)이 없을 때 자동으로 붙일 도메인입니다.
예를 들어 위 설정에서 server라는 이름을 조회하면, Stub Resolver가 search 도메인을 붙여 server.example.com으로 질의합니다.
회사 내부 네트워크처럼 같은 도메인 아래의 호스트에 자주 접근할 때 유용합니다.
/etc/hosts
DNS의 원리 (1)에서 다루었던 HOSTS.TXT처럼 호스트 이름과 IP 주소를 직접 매핑하되, 각 기기에서 개별 관리하는 파일입니다.
1
2
3
# /etc/hosts
127.0.0.1 localhost
192.168.1.100 myserver.local
Stub Resolver는 DNS 질의를 보내기 전에 이 파일을 먼저 확인합니다.
myserver.local을 조회하면 192.168.1.100이 바로 반환되어 Recursive Resolver까지 질의가 가지 않습니다.
개발 환경에서 도메인을 원하는 IP로 직접 지정할 때 유용합니다. 예를 들어 팀에서 사용하는 테스트 서버(192.168.1.100)에 myserver.local이라는 이름을 등록해 두면, IP 주소를 외울 필요 없이 이름으로 접근할 수 있습니다.
마무리
글의 처음에서 시작한 질문, 브라우저에 www.example.com을 입력하면 무슨 일이 일어나는가를 요약하면 다음과 같습니다.
1
2
3
4
5
6
7
8
www.example.com 조회 과정
1. 브라우저 → OS 캐시 확인
2. Stub Resolver → Recursive Resolver (재귀적 질의)
3. Recursive Resolver 캐시 확인
4. 캐시 미스 시: Root → TLD → Authoritative (반복적 질의)
5. 결과를 각 캐시에 저장
6. IP 주소를 브라우저에 전달
각 구성 요소의 역할이 명확합니다. Stub Resolver는 질의만 전달하고, Recursive Resolver는 캐싱과 탐색을 담당하며, Authoritative Server는 자신이 관리하는 Zone의 공식 답만 제공합니다. 이 분업 구조가 전 세계 도메인을 처리하는 분산 시스템의 기반입니다.
다음 편에서는 이 질의 과정의 보안 문제와 DNSSEC, DoH/DoT 같은 보안 확장을 살펴봅니다.
시리즈
- DNS의 원리 (1) - DNS의 탄생과 계층 구조
- DNS의 원리 (2) - DNS 질의와 해석 과정 (현재 글)
- DNS의 원리 (3) - DNS 보안과 현대적 발전
관련 글