DNS
DNS(Domain Name System)는 사람이 읽을 수 있는 도메인 이름을 IP 주소로 변환하는 인터넷의 전화번호부입니다. 계층적 구조로 이루어진 분산 데이터베이스로, 전 세계 수십억 건의 질의를 처리합니다.
핵심 개념
재귀 질의 흐름
브라우저가 example.com을 입력하면 로컬 DNS 리졸버에 질의합니다. 캐시에 없으면 루트 DNS → .com TLD DNS → example.com 권한 DNS 순서로 재귀 질의합니다. 최종적으로 IP 주소를 받아 브라우저에 반환하고, 결과를 TTL 동안 캐시합니다.
핵심 개념
DNS 계층 구조
DNS는 역트리 구조입니다. 루트(.)가 최상위이고, .com·.org·.kr 같은 TLD(Top Level Domain), 그 아래 example.com 같은 2차 도메인, www·mail 같은 서브도메인 순서입니다. 각 계층의 네임서버가 자신의 영역(Zone)에 대한 정보를 관리합니다.
핵심 개념
DNS 레코드 종류
A: 도메인 → IPv4 주소. AAAA: 도메인 → IPv6 주소. CNAME: 도메인 → 다른 도메인(별칭). MX: 메일 서버 지정. NS: 네임서버 지정. TXT: 임의 텍스트(SPF·DKIM 등). PTR: IP → 도메인(역방향 조회). SOA: 존의 권한 시작 정보.
핵심 개념
TTL과 캐싱
TTL(Time To Live)은 DNS 레코드의 캐시 유효 시간(초)입니다. TTL=300이면 5분간 캐시에 저장됩니다. 짧은 TTL은 빠른 변경 반영을 가능하게 하지만 DNS 서버 부하를 증가시킵니다. 브라우저·OS·로컬 DNS 리졸버 등 여러 단계에서 캐싱이 발생합니다.
핵심 개념
재귀 vs 반복 질의
재귀 질의(Recursive): 클라이언트가 로컬 리졸버에 요청하면, 리졸버가 최종 답을 찾아 반환합니다. 클라이언트는 한 번만 요청합니다. 반복 질의(Iterative): 각 DNS 서버가 다음에 물어볼 서버 주소를 알려주고, 클라이언트가 직접 순서대로 질의합니다. 실제로는 두 방식이 혼합됩니다.
핵심 개념
DNS 보안 (DNSSEC)
기본 DNS는 UDP 53번 포트를 사용하며 암호화가 없어 스푸핑·캐시 포이즈닝에 취약합니다. DNSSEC은 디지털 서명으로 응답의 출처와 무결성을 검증합니다(내용을 암호화하지는 않습니다). DoH(DNS over HTTPS)와 DoT(DNS over TLS)는 DNS 질의 자체를 암호화해 프라이버시를 보호합니다(암호화되는 구간은 클라이언트와 리졸버 사이입니다).
DNS가 필요한 이유
사람은 이름을 기억하고 네트워크는 숫자 주소로 통신합니다. DNS는 도메인 이름을 IP 주소 같은 정보로 바꿔 주는, 전 세계에 분산된 계층형 데이터베이스입니다. 이름과 주소가 분리되어 있어서 서버의 주소를 바꿔도 이름은 그대로 쓸 수 있습니다. 기본 설계는 RFC 1034와 RFC 1035에 정의되어 있습니다.
숫자로 확인하기
자주 하는 오해
면접에서 이렇게 물어요
면접 질문
브라우저에 주소를 입력하면 DNS는 어떻게 동작하나요?
브라우저와 OS 캐시를 먼저 보고, 없으면 설정된 리졸버에 질의합니다. 리졸버는 캐시가 없으면 루트, TLD, 권한 네임서버 순으로 물어 IP를 얻어 돌려주고 TTL 동안 저장합니다.
면접 질문
재귀 질의와 반복 질의의 차이는 무엇인가요?
재귀는 클라이언트가 리졸버에 한 번 묻고 최종 답을 받는 방식이고, 반복은 질의를 받은 서버가 다음에 물을 서버를 알려 주고 질의자가 직접 따라가는 방식입니다. 실제로는 클라이언트와 리졸버 사이는 재귀, 리졸버와 권한 서버 사이는 반복으로 섞여 있습니다.
면접 질문
CNAME과 A 레코드의 차이는 무엇인가요?
A는 이름을 IPv4 주소로 직접 연결하고, CNAME은 이름을 다른 이름의 별칭으로 만듭니다. CNAME 대상의 최종 주소는 다시 조회해야 합니다.
면접 질문
DNS 캐시 포이즈닝은 어떻게 막나요?
응답이 요청과 맞는지 확인하도록 질의마다 무작위 트랜잭션 ID와 소스 포트를 쓰고, DNSSEC 서명을 검증하며, DoT나 DoH로 질의 경로를 보호합니다.
실제 시스템에서는
dig로 레코드 종류와 TTL을 확인하고 +trace 옵션으로 루트부터의 위임 과정을 따라가 볼 수 있습니다.
InetAddress는 조회 결과를 캐시하고 기간은 java.security의 networkaddress.cache.ttl 보안 속성으로 조절한다고 문서가 설명합니다. DNS 레코드를 바꿨는데 애플리케이션이 이전 주소를 계속 쓴다면 이 캐시를 의심해 볼 수 있습니다.
함께 보면 좋은 토픽
참고 자료
Java — DNS 조회 및 레코드 분석
import java.net.*; import javax.naming.*; import javax.naming.directory.*; public class DnsLookup { public static void main(String[] args) throws Exception { // ── 기본 A 레코드 조회 (InetAddress) ───────────────── // OS의 /etc/resolv.conf에 설정된 DNS 리졸버 사용 InetAddress[] addrs = InetAddress.getAllByName("example.com"); for (InetAddress addr : addrs) { System.out.println("A: " + addr.getHostAddress()); // 93.184.216.34 } // 역방향 조회 (PTR 레코드) InetAddress ip = InetAddress.getByName("8.8.8.8"); System.out.println("PTR: " + ip.getHostName()); // dns.google // ── JNDI로 다양한 DNS 레코드 조회 ───────────────────── var env = new java.util.Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory"); env.put(Context.PROVIDER_URL, "dns://8.8.8.8"); // Google DNS 직접 지정 DirContext ctx = new InitialDirContext(env); // MX 레코드 조회 (메일 서버) Attributes mx = ctx.getAttributes("gmail.com", new String[]{"MX"}); NamingEnumeration<?> mxVals = mx.get("MX").getAll(); while (mxVals.hasMore()) { System.out.println("MX: " + mxVals.next()); // "10 alt1.gmail-smtp-in.l.google.com." } // TXT 레코드 조회 (SPF, DKIM 등) Attributes txt = ctx.getAttributes("example.com", new String[]{"TXT"}); System.out.println("TXT: " + txt.get("TXT")); // NS 레코드 조회 (권한 네임서버) Attributes ns = ctx.getAttributes("example.com", new String[]{"NS"}); System.out.println("NS: " + ns.get("NS")); // "a.iana-servers.net., b.iana-servers.net." ctx.close(); } }
InetAddress.getAllByName()은 JVM이 OS의 DNS 리졸버를 통해 A 레코드를 조회합니다.
결과는 JVM 내부에 캐시되며, networkaddress.cache.ttl 시스템 프로퍼티로 TTL을 제어합니다.
JNDI DnsContextFactory를 사용하면 A 레코드 외에 MX·NS·TXT·SOA 등 다양한 레코드를 조회할 수 있습니다.
DNS 서버 주소를 직접 지정해 특정 리졸버로 우회할 수도 있습니다.
MX 레코드의 숫자 접두사는 우선순위(낮을수록 높음)입니다. 메일 서버는 우선순위가 낮은 MX부터 시도합니다.
Spring 환경에서는 JVM DNS 캐시가 영구 캐싱되어 문제가 될 수 있습니다.
networkaddress.cache.ttl=60으로 설정하면 60초마다 재조회합니다.
Python — DNS 질의 패킷 직접 분석 (dnspython)
import dns.resolver import dns.message import dns.query import dns.rdatatype # ── 기본 A 레코드 조회 ────────────────────────────────── resolver = dns.resolver.Resolver() resolver.nameservers = ['8.8.8.8', '1.1.1.1'] # Google / Cloudflare DNS answers = resolver.resolve('example.com', 'A') for rdata in answers: print(f"A: {rdata.address}") # 93.184.216.34 print(f"TTL: {answers.ttl}초") # 3600 # ── 재귀 질의 직접 추적 (iterative) ───────────────────── def trace_dns(domain: str): # 루트 서버부터 시작 nameservers = ['198.41.0.4'] # a.root-servers.net for level in ['루트', 'TLD', '권한']: qname = dns.name.from_text(domain) req = dns.message.make_query(qname, dns.rdatatype.A) try: resp = dns.query.udp(req, nameservers[0], timeout=3) except Exception as e: print(f"{level} 질의 실패: {e}"); break # 최종 답변이 있으면 종료 if resp.answer: print(f"{level} → 최종 답변: {resp.answer[0]}") break # 위임 정보(NS)로 다음 서버 추출 next_ns = [] for rrset in resp.additional: if rrset.rdtype == dns.rdatatype.A: next_ns.append(str(rrset[0])) print(f"{level} → 다음 NS: {next_ns[:2]}") if next_ns: nameservers = next_ns trace_dns('example.com') # ── 캐시 확인 (로컬 시스템) ───────────────────────────── # macOS: sudo dscacheutil -cachedump -entries Host # Linux: systemd-resolve --statistics # Windows: ipconfig /displaydns
dnspython은 DNS 프로토콜을 직접 다루는 라이브러리입니다. 특정 DNS 서버를 지정해 질의하거나, UDP 패킷을 직접 구성해 전송할 수 있습니다.
trace_dns 함수는 브라우저 없이 DNS 재귀 질의 과정을 직접 추적합니다. 루트→TLD→권한 서버를 단계별로 질의하며 위임 체인을 따라갑니다.
resp.additional 섹션에는 위임된 NS 서버의 IP(글루 레코드)가 포함됩니다. 이를 통해 닭과 달걀 문제를 해결합니다.
실제 운영 환경에서는 시스템 DNS 캐시를 주기적으로 플러시해야 도메인 변경이 즉시 반영됩니다.
TTL 만료 전에는 캐시된 이전 IP로 연결될 수 있습니다.
Q1
DNS 조회 과정에서 가장 먼저 확인하는 곳은?
Q2
DNS 조회 과정에서 가장 먼저 확인하는 곳은?
Q3
재귀적 DNS 질의에서 TLD(Top-Level Domain) 네임서버의 역할은?
Q4
재귀적 DNS 질의에서 TLD(Top-Level Domain) 네임서버의 역할은?
Q5
DNS가 도메인 이름을 IP 주소로 변환해주는 이유는?
Q6
DNS가 도메인 이름을 IP 주소로 변환해주는 이유는?