A와 AAAA 레코드가 하는 일
A 레코드는 “이 이름은 이 IPv4 주소에 있다”는 뜻입니다. AAAA 레코드는 같은 내용을 IPv6로 말합니다. 누군가 www.example.com을 열면 리졸버가 두 가지를 모두 묻고, 클라이언트는 돌아온 주소로 연결합니다.
구조는 이게 전부입니다. 문제는 대부분 레코드 내용과 실제 상태 사이의 세 가지 틈에서 생깁니다.
- 클라이언트가 찾는 곳에 레코드가 없거나, 아무도 조회하지 않는 존에 들어 있음
- 레코드가 사이트가 없는 주소를 가리킴(예전 서버, 주차 페이지, 사설 IP)
- AAAA 레코드는 있는데 서버가 IPv6로 실제로 응답하지 않음
마지막 경우가 가장 까다롭습니다. 브라우저가 이 문제를 가려 주기 때문입니다.
레코드가 실제로 무엇인지 확인하기
타입을 하나씩 명시해서 물어보세요. dig에 타입을 주지 않으면 A만 묻습니다. “IPv6는 문제없다”는 결론의 상당수가 AAAA를 한 번도 조회하지 않은 데서 나옵니다.
dig +short A example.com
dig +short AAAA example.com
dig +short A www.example.com
dig +short AAAA www.example.com
루트 도메인(example.com)과 www는 레코드가 따로인 별개의 이름입니다. 둘 다, 그리고 실제로 쓰는 서브도메인을 모두 확인하세요.
이름이 CNAME이면 +short는 연결 고리를 먼저 출력하고 마지막에 주소를 보여 줍니다.
$ dig +short A www.example.com
example.netlify.app.
203.0.113.40
전체 출력에는 +short보다 많은 정보가 있습니다. 특히 TTL과 상태 줄입니다.
$ dig A example.com
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41312
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
example.com. 3600 IN A 203.0.113.10
3600은 조회한 리졸버에 남은 캐시 시간(초)입니다. DNS 조회는 A, AAAA를 비롯한 주요 타입을 우리 네트워크 밖에서 한 화면에 보여 주므로, 내 리졸버가 예전 응답을 들고 있을 수 있을 때 유용합니다.
‘A 레코드 없음’: NXDOMAIN인가 NODATA인가
브라우저에서는 똑같아 보이지만 뜻이 다릅니다.
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, ...
NXDOMAIN은 이름 자체가 없다는 뜻입니다. 만든 적이 없거나, 오타이거나, 도메인 전체가 위임되지 않은 경우입니다(만료, hold, 네임서버 고장).
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, ...
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; AUTHORITY SECTION:
example.com. 900 IN SOA ns1.dnshost.example. hostmaster.example.com. ...
NOERROR인데 응답이 0개(흔히 NODATA라고 부릅니다)면 이름은 있지만 그 타입의 레코드가 없다는 뜻입니다. 이름에 AAAA만 있거나, MX·TXT만 있거나, DNS 호스팅에 루트 도메인은 있는데 A 레코드를 아무도 추가하지 않은 경우가 대표적입니다.
도메인 전체가 NXDOMAIN이라면 먼저 RDAP 조회로 등록 상태를 보세요. 만료됐거나 clientHold가 걸린 도메인은 TLD 존에서 빠지고, 이때는 레코드를 아무리 추가해도 해결되지 않습니다.
권한 네임서버에 직접 물어보기
무엇이든 바꾸기 전에, 레코드가 원본에 없는 것인지 캐시에만 없는 것인지 가려야 합니다.
dig +short NS example.com
dig @ns1.dnshost.example A www.example.com +norec
+norec는 재귀 없이 권한 서버가 가진 데이터만 물어봅니다. 결과는 셋 중 하나입니다.
- 권한 서버에는 올바른 레코드가 있고 내 리졸버에는 없음. 캐시 문제입니다. 예전 TTL이 끝나기를 기다리세요. 레코드를 만들기 전에 그 이름을 조회했다면 리졸버는 ‘없음’이라는 응답도 캐시합니다. 기간은 그 존의 negative TTL(SOA 레코드의 TTL과 SOA 마지막 필드 중 작은 값)까지입니다. “방금 추가했는데 계속 없다고 나온다”가 한동안 이어지는 이유입니다.
- 권한 서버에도 없음. 레코드가 저장되지 않았거나 엉뚱한 이름으로 저장됐습니다. 흔한 실수는 존 이름을 자동으로 붙여 주는 관리 화면에
www.example.com을 통째로 입력해www.example.com.example.com을 만드는 것입니다. - NS 레코드가 내가 수정한 곳이 아닌 다른 곳을 가리킴. 등록대행자의 DNS에 레코드를 추가했는데 도메인은 Cloudflare로 위임돼 있거나, 그 반대인 경우입니다. 엉뚱한 곳에서 한 수정은 아무에게도 보이지 않습니다. 네임서버 불일치를 참고하세요.
주소가 틀림: 레코드는 있는데 엉뚱한 곳을 가리킴
DNS의 IP와 서버의 실제 IP를 비교하세요. VPS라면 ip -4 addr, ip -6 addr로 로컬 주소를 볼 수 있지만, NAT나 클라우드 로드밸런서 뒤에 있으면 공인 주소가 다릅니다. 이때는 클라우드 콘솔에 표시된 주소를 기준으로 삼으세요.
흔히 보는 잘못된 값은 이렇습니다.
서버 이전 후 남은 예전 서버 주소. A 레코드가 여전히 이전 호스팅의 IP입니다. 방문자는 예전 사이트를 보거나, 예전 호스팅에 인증서가 더는 없다면 인증서 오류를 봅니다.
주차 페이지나 기본 주소. 많은 등록대행자와 호스팅이 도메인을 등록할 때 기본 A·AAAA 레코드를 만들어 둡니다. A 레코드는 내 서버 IP로 바꾸고 AAAA는 등록대행자의 주차 페이지를 가리키는 채로 둡니다. IPv4 방문자는 내 사이트를, IPv6 방문자는 “이 도메인은 주차 중”을 봅니다.
사설 주소. 공개 존에 10.x.x.x, 172.16~31.x.x, 192.168.x.x, fd00::/8이 들어간 경우입니다. 사무실 안에서는 되고 밖에서는 안 됩니다. 안팎에서 다른 응답이 필요하면 split-horizon DNS를 쓰고, 사설 주소는 내부 존에만 두세요.
잊고 있던 CDN이나 프록시. Cloudflare 프록시를 켜면 공개 DNS에는 내 주소가 아니라 Cloudflare 주소가 나옵니다. 정상이며, 원 서버 IP는 Cloudflare 대시보드에 있습니다.
AAAA는 있는데 IPv6가 깨진 경우
흔하면서도 알아채기 어려워 따로 다룹니다.
이름에 A와 AAAA가 모두 있으면 IPv6가 되는 클라이언트는 대체로 IPv6부터 시도합니다. 요즘 브라우저는 Happy Eyeballs(RFC 8305)를 씁니다. IPv6 연결이 짧은 시간(1초도 안 되는 지연) 안에 성공하지 않으면 IPv4 연결을 병렬로 시작하고 먼저 붙는 쪽을 씁니다. 그래서 브라우저에서는 죽은 AAAA가 알아채기 어려운 지연 정도로만 나타납니다. 직접 볼 일이 없습니다.
다른 클라이언트는 이렇게 너그럽지 않습니다. 주소 목록을 하나씩 돌면서 주소마다 연결 시간 초과를 다 기다리는 클라이언트는, 죽은 IPv6 주소에서 몇 초씩 기다린 뒤에야 IPv4를 시도하거나 아예 포기합니다. 많은 스크립트와 라이브러리(예를 들어 Python requests, wget), 모니터링 에이전트, 다른 회사 인프라에서 오는 웹훅이 여기에 해당합니다. Let’s Encrypt의 HTTP-01 검증은 AAAA가 있으면 IPv6를 우선하고, IPv6 주소가 다른 서버의 내용으로 응답하면 IPv4로 다시 시도하지 않고 검증에 실패합니다.
그래서 증상이 이상하게 나타납니다. “크롬에서는 되는데 연동에서만 시간 초과”, “인증서 갱신이 계속 실패”, “업타임 모니터가 오락가락”.
확인하는 법
curl로 프로토콜을 하나씩 강제해 봅니다.
curl -4 -sS -o /dev/null -w '%{http_code} %{remote_ip}\n' https://www.example.com/
curl -6 -sS -o /dev/null -w '%{http_code} %{remote_ip}\n' https://www.example.com/
-4는 200인데 -6이 멈추거나, 거부되거나, 다른 상태 코드를 내면 AAAA가 깨진 것입니다.
함정이 하나 있습니다. IPv6가 되는 컴퓨터에서 실행해야 합니다. IPv6가 없는 네트워크에서는 curl -6이 모든 사이트에 대해 “Network is unreachable”이나 “Couldn’t connect”로 실패하므로, 내 사이트에 대해 아무것도 알려 주지 않습니다. curl -6 -sI https://www.google.com/로 먼저 확인해 보고, 이것도 실패하면 IPv6가 되는 VPS에서 테스트하세요.
흔한 원인과 해결
웹 서버가 IPv6에서 듣고 있지 않음. nginx에서 listen 80;, listen 443 ssl;은 IPv4에만 바인드합니다. 줄을 따로 추가해야 합니다.
listen 443 ssl;
listen [::]:443 ssl;
ss -ltnp | grep -E ':(80|443)\b'로 무엇이 듣고 있는지 보세요. 0.0.0.0:443만이 아니라 [::]:443(또는 *:443)이 보여야 합니다. nginx -T | grep listen은 설정 전체의 listen 지시어를 보여 주므로, IPv4 줄만 들어간 가상 호스트를 찾을 수 있습니다.
방화벽이 IPv4만 허용함. iptables와 ip6tables는 규칙이 따로입니다. 한쪽에서 443을 열어도 다른 쪽은 열리지 않습니다. nftables에서도 ip family 테이블은 IPv4만, inet은 둘 다 다룹니다. 클라우드 방화벽도 같습니다. AWS 보안 그룹의 0.0.0.0/0 규칙은 ::/0을 포함하지 않습니다.
AAAA가 내 것이 아닌 호스트를 가리킴. 위의 주차 페이지 경우이거나, 이전 후 남은 예전 서버의 IPv6입니다. AAAA 값과 서버의 ip -6 addr를 비교하세요.
서버에 IPv6 주소는 있는데 경로가 없음. 게이트웨이를 설정해야 동작하는 IPv6 주소를 주는 업체도 있고, 요금제에 따라 아예 안 되는 곳도 있습니다. 서버에서 ping -6 2001:4860:4860::8888이 실패하면 서버 자체에 IPv6가 없는 것입니다.
당장 IPv6를 살릴 수 없다면 AAAA 레코드를 지우세요. IPv4로만 열리는 사이트는 문제없습니다. 아무 데도 닿지 않는 AAAA가 있는 사이트는 AAAA가 없는 것보다 나쁩니다.
A 레코드가 여러 개인 경우
한 이름에 A 레코드가 여러 개면 평범한 DNS 라운드 로빈입니다. 리졸버는 전부를, 대개 순서를 바꿔 가며 돌려주고, 클라이언트는 그중 하나를 고릅니다. 상태 확인은 없습니다. 뒤의 서버가 살아 있든 아니든 DNS는 주소를 계속 내줍니다.
브라우저는 연결이 아예 실패하면 다음 주소를 시도하므로, 죽은 서버는 완전한 장애보다 느려짐으로 보일 때가 많습니다. 다른 클라이언트는 그냥 실패할 수도 있습니다.
실수로 생기는 흔한 경우는 이렇습니다. 서버를 옮기면서 예전 IP를 바꾸지 않고 새 서버 IP를 추가합니다. 그러면 일부 방문자는 새 서버로, 일부는 예전 서버로 가고, 수정 사항이 “가끔만” 보이며, 세션이 한쪽 서버에만 있어 로그인이 풀립니다. 주소를 전부 확인하세요.
dig +short A www.example.com
모르는 주소가 보이면 그 주소를 직접 테스트합니다.
curl -sI --resolve www.example.com:443:198.51.100.7 https://www.example.com/
--resolve는 curl이 실제 호스트명을 보내면서 그 IP로 연결하게 하므로, 그 주소로 간 방문자가 보는 것을 그대로 볼 수 있습니다. 남은 레코드를 지우고, 그 TTL이 지난 뒤에 예전 서버를 끄세요.
일부러 여러 서버를 두는 거라면, 라운드 로빈은 똑같은 서버들에 부하를 나누는 데는 쓸 만하지만 장애 조치(failover)는 아닙니다. 장애 조치가 필요하면 로드밸런서나 상태 확인을 하는 DNS 서비스가 필요합니다.
레코드를 바꿀 때: 예전 값의 수명은 TTL이 정함
예전 응답을 캐시한 리졸버는 TTL이 끝날 때까지 그 값을 씁니다. TTL이 86400인 레코드는 아무리 빨리 수정해도 모든 곳에 반영되기까지 최대 하루가 걸릴 수 있습니다.
계획된 이전이라면:
- 하루 이상 전(최소한 예전 TTL만큼 전)에 TTL을 300으로 낮춥니다.
- A와 AAAA 값을 함께 바꿉니다. 한쪽을 빠뜨리면 위의 IPv6 주차 페이지 문제가 생깁니다.
- 전파 확인으로 공용 리졸버들이 모두 같은 값을 낼 때까지 봅니다.
- 그때까지 예전 서버를 켜 두고, 끝나면 TTL을 다시 올립니다.
값별 근거는 TTL 설정 가이드에, “왜 일부 사람은 아직 예전 사이트를 보나”는 DNS 전파에 정리돼 있습니다.
방문자인데 나만 사이트가 안 열린다면
다른 사람에게는 열린다면, 예전 응답이 내 컴퓨터나 네트워크에 남아 있을 가능성이 큽니다.
- 내 리졸버와 공용 리졸버를 비교합니다.
nslookup www.example.com과nslookup www.example.com 1.1.1.1. - hosts 파일(
/etc/hosts,C:\Windows\System32\drivers\etc\hosts)에 예전 항목이 있는지 봅니다. - 캐시를 비웁니다. Windows는
ipconfig /flushdns, macOS는sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder. 크롬은chrome://net-internals/#dns에 따로 캐시를 둡니다.
브라우저 오류로 범위를 좁힐 수 있습니다. ERR_NAME_NOT_RESOLVED와 DNS_PROBE_FINISHED_NXDOMAIN은 이름 조회 실패이고, 조회는 됐는데 시간 초과가 나면 DNS가 아니라 연결 문제입니다.
함께 보기
- CNAME과 A 레코드: 언제 별칭을 쓰는지, 루트 도메인에 CNAME을 둘 수 없는 이유.
- 네임서버 불일치: 내가 한 수정이 세상이 묻는 서버에 닿지 않을 때.
내 도메인에서 바로 확인
무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.