
VirtualBox 환경에서 BIND를 이용해 Public/Private DNS 서버를 구축하고, Wireshark 패킷 분석을 통해 DNS 캐시, 재귀적/반복적 질의 등 이론적 지식이 실제 네트워크에서 어떻게 동작하는지 실증적으로 검증한 과정을 기록합니다.
DNS 탐구: 우리가 입력한 주소는 과연 어디서 ‘응답’ 오는 걸까? (feat. DNS 캐시와 Public & Private DNS의 협업)
Public DNS와 Private DNS의 우선순위 게임 - 호스트는 DNS 서버를 어떻게 선택할까
DNS라는 말을 처음 접한 건 컴퓨터 네트워크 수업을 처음 수강했을 때다. 브라우저 주소창에 자연스레 입력하던 google.com이라는 주소 뒤에 복잡한 IP가 숨어있다는 사실을 알게 되었을 때, 신기하고 궁금한 점이 많았던 기억이 난다. 그러다 이번에 DNS를 좀 더 깊게 탐구하게 되면서, 문득 이런 질문이 떠올랐다.
“우리가 입력한 웹사이트 주소, 과연 누가 ‘응답’해 주는 걸까?”
이 질문을 마음속에 품고, 이번엔 실제 환경을 구성해 Public DNS와 Private DNS가 공존할 때 DNS 서버의 우선순위가 어떻게 작동하는지를 확인해 보았다. 이 글은 그 과정을 담은 기록이자 나의 생각을 정리한 회고이다.
DNS, 도대체 왜 중요한 걸까?
DNS(Domain Name System)는 간단히 말해 “인터넷상의 전화번호부”와 같다. 우리는 웹 브라우저 주소창에 이해하기 쉬운 도메인 이름(naver.com, google.com)을 입력하지만, 실제 통신할 때는 IP 주소(223.130.195.95, 142.250.190.14)가 필요하다. 이 도메인 이름을 IP 주소로 변환하는 것이 바로 DNS의 역할이다.
DNS 시스템은 계층적으로 구성되어 있다. 최상단의 루트 DNS 서버부터 시작해, .com과 같은 TLD 서버, 그리고 특정 도메인의 IP 정보를 직접 가지고 있는 권한 DNS 서버까지 나뉘어 각자의 역할을 수행하고 있다.
인터넷이 존재하는 한, DNS는 피할 수 없는 필수 요소이며, DNS가 동작하지 않으면 인터넷 자체가 마비될 수도 있다는 점에서 DNS의 중요성은 굳이 강조하지 않아도 될 정도이다.
그렇다면 여기서 궁금증이 생긴다.
그렇다면 여기서 궁금증이 생긴다. 만약 Public DNS와 Private DNS가 함께 있을 때, 내 컴퓨터는 어떤 DNS 서버를 먼저 찾아갈까? 그리고 최종 응답은 과연 누가 하는 걸까?
실험실 구축: DNS의 여정을 재현하기 위한 무대 설정
이론적인 궁금증을 참지 못하고, 직접 VirtualBox 위에 작은 네트워크 세상을 만들었다. 이 실험의 주인공은 다음과 같다.

- Windows 10 VM (클라이언트,
10.0.2.15): 호기심 많은 사용자. 웹 브라우저를 통해 세상에 질문을 던진다. 이 친구가 아는 유일한 DNS 서버는 바로 아래의 ‘우분투 서버 1’이다. - 우분투 서버 1 (Public DNS 역할,
10.0.2.16): 네트워크의 문지기. 외부 세계(google.com등)로 나가는 길(Google DNS8.8.8.8로의 포워딩)과 내부(baboX.com)로 들어가는 길을 모두 알고 있다. 자체적으로 Apache 웹 서버를 운영한다. - 우분투 서버 2 (Private DNS 역할,
10.0.2.17): 내부망의 전문가.babo1.com,babo2.com과 같은 내부 도메인 정보(Zone File)를 독점적으로 관리하는 권위자다. 자체적으로 Nginx 웹 서버를 운영한다.
이 구조를 그림으로 나타내면 다음과 같다.
핵심 시나리오는 이렇다. 클라이언트(Win 10)의 모든 DNS 질의는 무조건 문지기인 ‘우분투 서버 1’에게 먼저 전달된다. 이 문지기는 자신이 모르는 내부 도메인에 대한 질문을 받으면, 전문가인 ‘우분투 서버 2’에게 슬쩍 물어보고 그 답을 전달해 주는 역할을 맡는다. 과연 이 흐름대로 동작할까?
가설과 검증: 패킷은 거짓말을 하지 않는다
이제 모든 준비가 끝났다. 가설을 세우고, 직접 패킷을 들여다보며 진실을 확인해 보자. 각 서버에서 tcpdump를 실행해 모든 통신을 감시하기 시작했다.
가설 1: 외부 도메인(www.google.com) 질의 - 문지기(서버1)가 알아서 처리해 줄 것이다.
클라이언트가 www.google.com에 접속하면, 클라이언트는 자신이 아는 유일한 DNS 서버인 10.0.2.16(서버 1)에게 물어볼 것이다. 서버 1은 구글의 주소를 모르므로, 자신이 바라보는 상위 DNS 서버(8.8.8.8)에게 물어본 뒤, 그 결과를 클라이언트에게 전달하고 자신도 그 결과를 캐싱할 것이다.
▶ 검증: Wireshark로 패킷을 열어보니 예상대로였다.
- [Client → 서버 1]: “DNS Standard query 0x… A
www.google.com” - [서버 1 → 8.8.8.8]: “DNS Standard query 0x… A
www.google.com” (클라이언트를 대신해 외부로 질의) - [8.8.8.8 → 서버 1]: “DNS Standard query response 0x… A
142.250.206.196” - [서버 1 → Client]: “DNS Standard query response 0x… A
142.250.206.196” (결과 전달) - [Client → 142.250.206.196]: “TCP SYN” (웹 접속 시작)
결론: 외부 도메인 질의에 대한 최종 응답자는 서버 1이었지만, 그 정보의 진짜 출처는 외부 DNS 서버(
8.8.8.8)였다. 서버 1은 충실한 ‘재귀적 해석기(Recursive Resolver)‘이자 ‘전달자(Forwarder)’ 역할을 수행했다.
가설 2: 내부 도메인(http://www.babo2.com) 질의 - 문지기(서버1)가 전문가(서버2)에게 물어볼 것이다.
클라이언트가 내부망의 전문가(서버 2)가 호스팅하는 www.babo2.com에 접속하려 한다. 이번에도 클라이언트는 문지기인 서버 1에게 물어볼 것이다. 서버 1의 BIND 설정에는 babo2.com zone에 대한 요청을 10.0.2.17(서버 2)로 전달하라는 forward 구문이 명시되어 있다. 따라서 서버 1은 서버 2에게 질의를 넘기고, 응답을 받아 클라이언트에게 전달할 것이다.
▶ 검증: 패킷의 흐름은 한 편의 위임 과정을 보여주었다.
- [Client → 서버 1]: “DNS Standard query 0x… A
www.babo2.com” - [서버 1 → 서버 2]: “DNS Standard query 0x… A
www.babo2.com” (내부 전문가에게 질의 전달) - [서버 2 → 서버 1]: “DNS Standard query response 0x… A
10.0.2.17” (권위 있는 응답) - [서버 1 → Client]: “DNS Standard query response 0x… A
10.0.2.17” (결과 전달) - [Client → 10.0.2.17]: “HTTP GET /” (Nginx 웹 서버 접속)
결론: 이번에도 클라이언트에게 직접 응답한 것은 서버 1이었지만,
www.babo2.com의 IP 주소(10.0.2.17)를 알려준 실질적인 **권위(Authority)**를 가진 응답자는 바로 서버 2였다. 서버 1은 똑똑한 중개자 역할을 완벽히 수행했다.
가설 3: 캐시의 마법 - 두 번째 질문은 즉시 응답할 것이다.
DNS의 핵심은 효율성이다. 그렇다면 방금 www.google.com의 주소를 물어봤던 서버 1에게 똑같은 질문을 다시 하면 어떻게 될까? 서버 1은 그 결과를 캐시(Cache)에 저장했을 것이므로, 더 이상 외부 DNS 서버에게 물어보지 않고 즉시 응답할 것이다.
▶ 검증:
클라이언트에서 다시 한번 nslookup www.google.com을 실행하고 패킷을 확인했다.
- [Client → 서버 1]: “DNS Standard query 0x… A
www.google.com” - [서버 1 → Client]: “DNS Standard query response 0x… A
142.250.206.196” (즉시 응답)
결론 :
서버 1과8.8.8.8사이의 통신은 감쪽같이 사라졌다. 서버 1의 BIND 캐시가 빛을 발하는 순간이었다. 이 캐시 정보는 TTL(Time To Live) 동안 유효하며, 이는 불필요한 네트워크 트래픽을 줄이고 응답 속도를 극적으로 향상시키는 DNS의 핵심 기능이다.
그래서 얻은 건 무엇인가?
이번 DNS 실습을 통해 얻은 가장 중요한 깨달음은, 눈에 보이지 않는 DNS 요청 처리의 흐름을 명확히 이해하는 것의 가치였다. 실제로 이번 환경을 구성하며 느꼈던 가장 큰 이점은 다음과 같다.
첫째, DNS 요청이 어디에서 어떻게 처리되는지 명확히 이해하게 되었다. 직접적인 응답자는 내 컴퓨터의 네트워크 설정에 명시된 DNS 서버(서버 1)였지만, 그 응답의 진정한 출처는 Public DNS일 수도, 내부의 Private DNS일 수도 있다는 다층적 구조를 파악했다.
둘째, DNS 캐시의 중요성을 두 눈으로 확인했다. 캐시 덕분에 반복적인 요청이 얼마나 빠르게 처리되고 네트워크 효율이 향상되는지 보며, 왜 캐시가 필수적인지 온몸으로 이해할 수 있었다.
셋째, DNS 문제가 발생할 때 실제 환경에서 디버깅하는 능력을 키울 수 있었다. 환경을 구성하면서 다양한 문제가 생겼지만, DNS 요청 흐름을 명확히 이해했기 때문에 패킷을 분석하며 비교적 쉽게 원인을 찾고 해결할 수 있었다.
앞으로 DNS를 마주할 때
사실 DNS는 많은 사용자들이 ‘자연스레 작동’하는 존재로 생각하지만, 그 속에서는 매우 세밀한 위임과 신뢰, 그리고 기억(캐시)의 네트워크가 움직이고 있다. 이번 실습을 통해 DNS의 이러한 내부 구조를 이해하고 나니, 이제는 DNS 문제가 발생하더라도 당황하지 않고 그 문제를 차분히 해결할 자신감이 생겼다.
아마 앞으로 DNS 문제를 마주할 때마다 이번 실습이 떠오를 것 같다. 그리고 그때마다 DNS의 흐름을 떠올리며 차근히 문제를 풀어낼 수 있기를 바란다. 이 글이 나중에 DNS를 다시 복습하거나 누군가 DNS 구조를 이해하는 데 작은 참고가 되었으면 하는 바람도 있다.
DNS라는 작지만 중요한 시스템을 깊게 이해하는 즐거움, 그 자체로 이번 탐구는 충분히 의미 있었다.
💬 댓글
GitHub 계정으로 로그인하여 댓글을 남겨보세요. GitHub 로그인
🔧 댓글 시스템 설정이 필요합니다
GitHub Discussions 기반 댓글 시스템을 활성화하려면:
GISCUS_SETUP_GUIDE.md파일의 안내를 따라 설정 완료Repository 관리자만 설정할 수 있습니다. 설정이 완료되면 모든 방문자가 댓글을 남길 수 있습니다.