짧은 답

A 레코드는 “이 이름은 이 IPv4 주소에 있다”는 뜻입니다. AAAA는 같은 것을 IPv6로 말합니다. CNAME은 “이 이름은 사실 저 이름이니, 저쪽에 물어보라”는 뜻입니다.

주소를 다른 사람이 관리할 때는 CNAME을 씁니다. CDN, SaaS, 호스팅 플랫폼이 그렇습니다. 주소를 내가 관리할 때는 A/AAAA를 쓰고, 도메인의 루트(example.com)에는 CNAME을 쓸 수 없으므로 항상 A/AAAA를 씁니다.

대부분의 결정은 이것으로 끝납니다. 이 글의 나머지는 그 이유와, 짧은 답만 믿었다가 문제가 생기는 경우들입니다.

각 레코드가 실제로 하는 일

A 레코드는 한 번에 끝납니다. 이름을 물으면 주소가 나옵니다.

$ dig +noall +answer api.example.com A
api.example.com.        3600    IN      A       203.0.113.10

CNAME은 체인을 돌려줍니다.

$ dig +noall +answer shop.example.com A
shop.example.com.            3600  IN  CNAME  stores.platform.example.net.
stores.platform.example.net.  300  IN  A      198.51.100.7

A 레코드를 물었는데 답이 두 줄입니다. 재귀 리졸버(통신사 DNS, 1.1.1.1, 8.8.8.8 등)가 CNAME을 보고 대상 이름으로 조회를 다시 시작한 뒤, 체인 전체를 돌려준 것입니다. 브라우저가 따로 한 일은 없습니다. nslookup -type=CNAME shop.example.com으로도 “canonical name = stores.platform.example.net.”이라고 같은 내용을 볼 수 있습니다.

여기서 두 가지가 나오는데, CNAME 규칙 대부분이 이 두 가지로 설명됩니다.

CNAME은 주소뿐 아니라 모든 조회 타입을 넘깁니다. shop.example.com에 AAAA, TXT, MX를 물어도 리졸버는 stores.platform.example.net 쪽 답을 줍니다. 그래서 CNAME 하나로 IPv4와 IPv6가 함께 해결되고, 같은 이유로 CNAME은 다른 레코드와 같은 이름에 있을 수 없습니다(아래에서 설명합니다).

체인의 각 단계는 자기 TTL을 가집니다. 위 예에서 리졸버는 내 CNAME을 1시간, 플랫폼의 A 레코드는 5분만 캐시합니다. 플랫폼이 서버를 옮기면 내 존을 건드리지 않아도 5분 안에 방문자가 따라갑니다. 플랫폼이 IP 대신 CNAME을 요구하는 진짜 이유가 이것입니다. 주소 관리를 자기들 몫으로 두려는 것입니다.

무엇을 쓸지 고르는 기준

상황쓸 레코드이유
루트 도메인(example.com)A/AAAA, 또는 DNS 업체의 ALIAS·평탄화존 루트(apex)에는 CNAME 불가
CDN·SaaS·호스팅 플랫폼을 가리키는 서브도메인업체 문서에 적힌 그대로 CNAME업체가 IP를 바꿀 수 있고, 도메인 확인과 인증서 발급에 CNAME을 쓰는 경우가 많음
고정 IP를 가진 내 서버의 서브도메인A/AAAA한 단계 더 거칠 이유가 없음
루트와 같은 서버를 쓰는 www루트는 A/AAAA, www는 루트로 CNAME하거나 같은 A/AAAACNAME이면 IP가 바뀔 때 고칠 곳이 하나
MX나 TXT도 필요한 이름A/AAAACNAME은 이름을 공유할 수 없음
MX 레코드가 가리키는 메일 서버 이름A/AAAAMX·NS 대상은 별칭이면 안 됨(RFC 2181)

업체가 어떤 레코드를 만들라고 안내했다면 그대로 하세요. 그 업체의 엣지가 무엇을 기대하는지는 업체가 제일 잘 압니다. 정확한 CNAME이 걸려 있는지 확인한 뒤에야 인증서를 발급해 주는 곳도 있습니다.

루트 도메인에 CNAME을 쓸 수 없는 이유

RFC 1034(3.6.2절)는 어떤 이름에 CNAME이 있으면 그 이름에 다른 데이터가 있어서는 안 된다고 정합니다. RFC 2181이 이를 다시 확인했고, 나중에 DNSSEC이 자기 서명 레코드에 대해서만 예외를 두었습니다.

모든 존의 루트에는 SOA 레코드와 NS 레코드가 반드시 있습니다. 루트에는 이미 다른 데이터가 있으니, 거기에 CNAME을 두면 존 구조 자체와 모순됩니다. 실제 운영 중인 존이라면 루트에 MX, SPF용 TXT, 각종 소유 확인 TXT까지 있어서 상황이 더 나쁩니다.

BIND는 이런 존을 아예 읽어 들이지 않습니다. named-checkzone이 CNAME and other data 오류를 냅니다. 예전에는 이 조합을 그냥 받아 주는 DNS 관리 화면도 있었는데, 그러면 서버가 무엇을 돌려주느냐에 따라 동작이 달라집니다. 사람들이 알아차리는 증상은 대개 메일입니다. CNAME을 받은 리졸버는 그것을 따라가므로, 내 도메인의 MX를 물었는데 대상 쪽 MX가 돌아옵니다. 남의 메일 서버이거나 아예 없는 레코드입니다.

대신 쓸 수 있는 방법

ALIAS, ANAME, CNAME 평탄화. 많은 DNS 업체가 설정은 CNAME처럼 하지만 응답은 A/AAAA처럼 나가는 레코드를 제공합니다. 업체가 대상을 직접 조회해서 주소로 바꿔 돌려주는 방식입니다. Cloudflare는 이를 CNAME flattening이라 부르고 루트에서는 자동으로 적용합니다. 다른 업체는 ALIAS나 ANAME이라 부릅니다. Route 53의 alias 레코드도 같은 일을 하지만, AWS 리소스와 같은 호스팅 영역의 다른 레코드만 가리킬 수 있습니다.

이것들은 모두 표준 DNS 레코드 타입이 아닙니다. 그 업체 안에서만 존재합니다. DNS 업체를 옮길 때는 새 업체도 이 기능을 지원하는지 먼저 확인하세요. 지원하지 않으면 그 레코드는 옮겨지지 않습니다.

한 가지 맞바꾸는 것이 있습니다. 평탄화에서는 방문자의 리졸버가 아니라 DNS 업체의 서버가 대상을 조회합니다. DNS 질의가 어디서 왔는지를 보고 엣지 위치를 고르는 CDN이라면, 방문자 대신 DNS 업체의 위치를 기준으로 고를 수 있습니다. 실제로 영향이 있는지는 DNS 업체와 CDN 조합에 따라 다르므로, 엣지 배치가 중요하다면 양쪽에 문의하세요.

결과 확인: 루트에서 CNAME이 아니라 A/AAAA가 보여야 정상입니다.

$ dig +noall +answer example.com A
example.com.            300     IN      A       198.51.100.7

플랫폼이 공개한 주소 사용. 루트 도메인용 고정 IP를 문서로 공개하는 호스팅 플랫폼이 많습니다(예를 들어 GitHub Pages는 루트 도메인용 A·AAAA 레코드를 공개합니다). 적힌 그대로 쓰고, 사이트가 갑자기 연결되지 않으면 문서부터 다시 확인하세요.

루트를 www로 리다이렉트. www는 CNAME으로 플랫폼에 연결하고, 루트는 간단한 서버나 DNS·호스팅 업체의 리다이렉트 기능으로 https://www.example.com에 301을 보내게 합니다. 리다이렉트는 TLS 연결이 성공한 뒤에야 일어나므로, 루트에도 유효한 인증서가 따로 있어야 합니다.

“다른 레코드 금지” 규칙은 서브도메인에도 적용됩니다

이 규칙을 루트에서 처음 만나는 경우가 많지만, 서브도메인에서도 똑같이 걸립니다.

어떤 서비스가 docs.example.com에 TXT 레코드를 넣어 소유를 증명하라고 하는데, docs는 이미 문서 호스팅으로 가는 CNAME입니다. 관리 화면이 추가를 거부하거나, 추가는 되는데 확인이 영영 통과하지 않습니다. 그 이름에 TXT를 물으면 CNAME을 따라가기 때문입니다.

$ dig +noall +answer docs.example.com TXT
docs.example.com.                  3600  IN  CNAME  docs-site.hosting.example.net.
docs-site.hosting.example.net.      300  IN  TXT    "v=spf1 include:_spf.hosting.example.net ~all"

이 TXT는 호스팅 업체 것이지 내 것이 아닙니다. 많은 서비스가 바로 이 문제 때문에 _service-verify.docs.example.com 같은 별도 이름으로 확인하는 방법을 제공합니다. 있다면 그쪽을 쓰세요.

메일도 마찬가지입니다. support.example.com이 메일(MX)도 받고 플랫폼에 올린 웹사이트도 보여야 한다면 CNAME일 수 없습니다. 그 이름에는 플랫폼의 A/AAAA를 쓰거나, 메일은 다른 이름으로 받으세요.

관리 화면에서는 멀쩡해 보이는 실수

CNAME에 IP 주소 입력. 203.0.113.10을 CNAME 대상으로 그냥 받아 주는 관리 화면이 있습니다. 리졸버는 이름이 203.0.113.10.인 호스트를 찾고(관리 화면이 존 이름을 붙이면 203.0.113.10.example.com.), 아무것도 없으니 NXDOMAIN이 됩니다.

존 파일에서 끝 점 누락. BIND 문법에서 www IN CNAME app.platform.net처럼 끝에 점을 빼면 app.platform.net.example.com.이 됩니다. app.platform.net.으로 써야 합니다. 웹 관리 화면은 대개 알아서 처리하지만, 직접 편집하는 존 파일은 그렇지 않습니다.

MX나 NS가 CNAME을 가리킴. RFC 2181(10.3절)은 MX와 NS의 대상이 별칭이 아니라 주소 레코드를 직접 가져야 한다고 정합니다. 그래도 받아 주는 메일 서버가 많아서 오히려 눈에 띄지 않다가, 받아 주지 않는 발신자를 만나면 그때 드러납니다. 메일 서버 이름에는 A/AAAA를 주세요.

CNAME에서 A로 바꾸면서 AAAA를 잊음. CNAME일 때는 IPv6가 자동으로 따라왔습니다. A 레코드로 바꾸면 IPv6는 따로 관리해야 하는 레코드가 되고, 오래된 AAAA가 남아 있으면 IPv4 사용자는 새 서버로, IPv6 사용자는 예전 서버로 갑니다. 찾는 방법은 A/AAAA 점검 가이드에 있습니다.

대상이 사라진 CNAME(dangling CNAME). 플랫폼에서 앱, 스토리지 버킷, 랜딩 페이지를 지웠는데 promo.example.com은 여전히 그쪽을 가리키는 경우입니다. 어떤 플랫폼에서는 아무나 같은 이름으로 리소스를 만들어 내 서브도메인에 자기 콘텐츠를 올릴 수 있습니다. 리소스를 지울 때 DNS 레코드도 같은 작업에서 지우세요.

여러 업체를 거치는 긴 체인. 한두 단계는 흔하고, CDN은 내부적으로 몇 단계를 쓰기도 합니다. 하지만 단계마다 살아 있어야 하는 업체가 하나씩 늘고, 무언가 바뀔 때 기다려야 할 TTL도 하나씩 늘어납니다. 체인이 세 회사를 거친다면 정말 필요한지 따져 보세요.

실제 설정 확인하기

터미널에서:

dig +noall +answer www.example.com A
dig +noall +answer www.example.com AAAA
dig +short www.example.com CNAME

# 캐시를 모두 건너뛰고 권한 있는(authoritative) 서버에 직접 질의
dig @ns1.your-dns-provider.example www.example.com A +norecurse

DNS 조회는 한 이름의 모든 레코드 타입을 TTL과 함께 보여 줍니다. 그 이름이 CNAME인지, A/AAAA를 가졌는지, 혹은 (잘못되게) 둘 다 가진 것처럼 보이는지 한눈에 확인할 수 있습니다.

방금 A에서 CNAME으로, 또는 그 반대로 바꿨다면 리졸버들은 TTL이 끝날 때까지 예전 답을 들고 있습니다. 전파 확인으로 공용 리졸버들이 바뀌었는지 비교할 수 있고, 다음번에 이 대기 시간을 줄이는 방법은 TTL 가이드에 있습니다.

CNAME을 플랫폼에 연결했더니 인증서 이름 오류가 난다면, 대개 플랫폼 쪽에 내 도메인을 아직 등록하지 않은 것입니다. ERR_CERT_COMMON_NAME_INVALID 가이드의 4번 원인이고, SSL 점검으로 플랫폼이 내 이름에 실제로 어떤 인증서를 내보내는지 확인할 수 있습니다.

내 도메인에서 바로 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.