ELKISS BLOG

도메인은 어떻게 연결할까?

여러 갈래로 갈린 표지판을 올려다보는 아테나

프로필 사이트를 하나 만들어보고 싶었습니다. 링크 몇 개 있는 간단한 페이지인데, github.io 주소 말고 제 이름으로 된 주소를 쓰고 싶었습니다.

그러려면 도메인이 필요합니다. 사는 것까지는 알겠는데 산 다음에 그게 어떻게 제 페이지로 연결되는지를 몰랐습니다. A 레코드니 CNAME 이니 하는 말은 봤는데 뭘 골라야 하는지도 몰랐고요.

알아보면서 몇 가지가 예상과 달랐습니다. 순서대로 적습니다. 나중에 이 블로그를 같은 도메인 아래에 붙일 때는 DNS 가 한 줄로 끝났는데, 왜 그렇게 달랐는지는 마지막에 적겠습니다.

도메인은 사는 게 아니라 빌리는 것이었습니다#

먼저 도메인이 있어야 합니다. 그런데 결제하려고 보니 “산다” 는 말이 정확하지 않았습니다. 연 단위 사용권이고, 갱신하지 않으면 유예기간 뒤에 다른 사람이 등록할 수 있습니다. 자동 갱신을 켜두는 것이 첫 번째 할 일이었습니다.

어디서 살지를 보다가 알게 된 것이 있습니다.

첫해 가격은 기준이 아닙니다. 첫해만 싸게 받고 갱신에서 회수하는 곳이 흔합니다. 계속 쓰는 물건이라 실제로 내는 값은 갱신가입니다.

.me 는 몬테네그로 국가 도메인인데 등록 제한이 없고 영어로 “나” 를 뜻해서 개인 사이트에 관행적으로 쓰입니다. 대신 .com 보다 갱신비가 비쌉니다 — 2026년 7월에 본 값이 연 $16 대였습니다.

그리고 등록 후 60일은 다른 곳으로 옮길 수 없다고 보면 됩니다. ICANN 정책이 그 기간의 이전 요청을 파는 곳이 거부할 수 있게 열어뒀습니다.1

그리고 파는 곳(레지스트라)은 판매 창구일 뿐입니다. 어디서 사든 등록되는 원장은 같습니다. 비싼 곳에서 산다고 도메인이 좋아지지 않습니다. 차이는 가격과 UI, 딸려 오는 기능 정도입니다.

그러면 볼 것은 가격이 아니라 딸려 오는 것들입니다. WHOIS 프라이버시부터 확인합니다 — 등록자 정보는 원래 공개 조회 대상이라, 가려주지 않으면 이름과 주소와 전화번호가 열립니다.

Cloudflare 에서 샀습니다. 갱신가가 등록가와 같은 것도 좋았지만 정작 필요했던 것은 DNS 기능 하나였고, 그게 왜 필요했는지가 다음 이야기입니다.

apex 에는 CNAME 을 못 겁니다#

elkiss.me 처럼 앞에 아무것도 안 붙은 이름을 apex 라고 합니다.

GitHub Pages 는 <계정>.github.io 라는 이름으로 서빙합니다. 그러니 내 도메인을 그 이름으로 넘기면 되겠다 싶었습니다. CNAME 이 하는 일입니다.

그런데 apex 에는 안 됩니다.

CNAME 의 정의가 “이 이름에 대한 것은 전부 저쪽에서 가져와라” 이기 때문입니다. 필드 하나를 가리키는 게 아니라 이름 전체를 대체합니다. 그래서 CNAME 이 있는 이름에는 다른 레코드가 함께 있을 수 없습니다.2

그런데 도메인의 맨 위 이름에는 비워둘 수 없는 칸이 있습니다. “이 도메인은 어느 서버에 물어봐야 하는가”(NS)와 “이 도메인의 기본 정보”(SOA) 둘인데, 이게 없으면 도메인이 도메인 노릇을 못 합니다. 그래서 apex 에는 항상 이 둘이 들어 있습니다.

apex 에 CNAME 을 걸면  → 다른 레코드가 있으면 안 된다
그런데 apex 에는       → 비울 수 없는 칸이 있다
                       → 동시에 성립 불가

서브도메인에는 그 칸이 꼭 있어야 하는 건 아니라서 이 제약이 안 걸립니다. blog.elkiss.me 를 붙일 때 이 절이 통째로 해당 없었던 이유입니다.

설정은 CNAME 인데 응답은 A 로 나갑니다#

우회로가 있습니다. 다만 그리로 가기 전에, 정공법이 왜 모자란지부터 보는 게 빠릅니다.

apex 에 CNAME 을 못 건다면 A 레코드로 IP 를 직접 적으면 됩니다. GitHub 이 Pages 용 IP 를 문서에 공개해두고 있으니 그대로 넣으면 그날 사이트가 뜹니다.

185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153

되기는 하는데, 이 넷은 고정하겠다는 약속이 아닙니다. A 로 간다는 것은 이 값을 손으로 적어두고 GitHub 이 바꾸면 따라 고치겠다는 뜻입니다. 안 고치면 사이트가 죽습니다. 배포를 한 적도 설정을 건드린 적도 없는데 어느 날 안 열립니다.

ACNAME
가리키는 것IP다른 이름
대상이 바뀌면직접 고쳐야 한다자동으로 따라간다

Cloudflare 는 이 사이를 CNAME flattening 으로 메웁니다. 설정 화면에서는 apex 에 CNAME 을 거는 것처럼 보이지만, 질의가 들어오면 Cloudflare 가 대신 대상을 조회해서 나온 IP 를 A 로 응답합니다. 설정은 CNAME 이라 GitHub 이 IP 를 바꿔도 따라가고, 응답은 A 라 표준을 어기지 않습니다.

같은 기능을 다른 업체는 ALIAS 나 ANAME 이라고 부릅니다. 표준이 아니라 업체별 기능이라 부르는 말이 제각각이고, 없는 곳도 있습니다. 도메인을 살 때 DNS 기능 하나를 보고 골랐다고 한 게 이것입니다.

그래서 실제로 넣은 것은 두 줄입니다.

TypeNameTargetProxy
CNAME@<계정>.github.ioDNS only
CNAMEwww<계정>.github.ioDNS only

@ 는 apex 를 가리키는 표기입니다. www 는 없어도 사이트가 뜨지만 @ 는 필수입니다 — GitHub 이 검사하는 것은 커스텀 도메인으로 등록한 이름 하나이고, 그게 apex 이기 때문입니다. www 는 앞에 www. 를 붙여 치는 사람을 위한 줄입니다. 두 이름이 다 잡혀 있으면 GitHub 이 www 쪽을 apex 로 넘겨줍니다.

프록시(주황 구름)는 꺼야 합니다. 켜면 응답 IP 가 Cloudflare 것으로 바뀌어서 GitHub 의 도메인 검사가 통과하지 못합니다. 고른 이유가 된 기능과 켜면 안 되는 스위치가 같은 화면에 있습니다.

넣고 나서 바로 안 뜨더라도 기다릴 일이 아닙니다. 조회부터 해봐야 합니다.

nslookup elkiss.me 8.8.8.8

공용 리졸버를 지정해서 묻습니다. 그냥 nslookup elkiss.me 로 물으면 내 컴퓨터나 공유기가 들고 있는 캐시가 답할 수 있어서, 레코드를 이미 고쳤는데도 옛 답이 나옵니다. IP 가 나오면 된 것이고, IP 없이 No answer 나 SOA 만 돌아오면 그 이름은 있는데 레코드가 없는 것입니다.

“DNS 전파” 는 무언가 퍼져나가는 게 아니었습니다. 레코드마다 “이 답을 몇 초 동안 다시 써도 된다” 는 시간(TTL)이 붙습니다. 그동안 통신사나 회사가 돌리는 DNS 서버는 받아둔 답을 그대로 내줍니다. 바뀌었다고 알려주는 장치는 없습니다. 그래서 전파를 기다린다는 것은 그 시간이 세계 곳곳에서 각자 만료되기를 기다린다는 뜻입니다.

다만 아직 안 넣은 레코드는 아무리 기다려도 안 생깁니다.

기본 배포는 저장소 루트를 통째로 서빙합니다#

DNS 가 붙으면 Pages 를 켭니다. 여기서 방식이 둘입니다.

기본값인 Deploy from a branch 는 저장소 루트를 통째로 서빙합니다. 따로 올리는 것이 없습니다. 브랜치에 있는 파일이 그대로 주소를 갖습니다 — index.html 만이 아니라 전부.

이 블로그가 그 방식이었다면 작업 일지(docs/LOG.md)도 blog.elkiss.me 아래에서 열렸을 겁니다. public 저장소라 유출은 아닙니다. 어차피 GitHub 에서 볼 수 있는 파일입니다. 다만 작업 문서가 개인 도메인에서 서비스되고 검색엔진에 색인될 이유는 없습니다.

하나가 더 붙습니다. 이 방식은 Jekyll 을 항상 돌립니다. 쓰겠다고 한 적이 없는데 기본 동작이 그렇고, 끄려면 저장소에 .nojekyll 이라는 빈 파일을 둬야 합니다.

index.html 이 없으면 Jekyll 이 README.md 를 기본 테마로 렌더링해 인덱스로 세웁니다. 그래서 빈 줄 알았던 주소를 열었더니 README 가 떠 있었습니다. 응답 HTML 에 Jekyll SEO tag 주석이 남아 있어서 알았습니다.

위에서 내려다본 바닥. 액자와 붓이 놓여 있고 맨발 자국이 왔다가 되돌아 나갔다
Deploy from a branch커스텀 Actions
주소가 생기는 것저장소 루트 전부워크플로가 담은 것만
docs/도메인에서 열린다담기지 않는다
index.html 이 없으면Jekyll 이 README.md 를 그린다해당 없음
설정클릭 두 번워크플로 파일 하나

커스텀 Actions 로 옮겼습니다. 이쪽은 워크플로가 배포할 것만 골라 담아 올리니 docs/ 와 README.md 는 아예 안 실립니다.

CNAME 이 두 군데 나오는데 서로 다른 물건입니다#

기본 방식에서 Pages 에 커스텀 도메인을 저장하면 GitHub 이 저장소 루트에 CNAME 파일을 만듭니다. 그리고 그 커밋을 자기가 합니다.

커밋   f221818
작성자 elkiss87
제목   Create CNAME

제 이름으로 들어와 있습니다. 웹에서 일어난 일이라 그렇습니다. 만들지 않은 커밋이 제 히스토리에 제 이름으로 남았습니다. 로컬은 이걸 모르니 ahead 1, behind 1 로 갈라집니다. git pull --rebase 로 풉니다.

같은 낱말이 두 군데 나와서 헷갈렸는데, 둘은 층이 다른 물건이었습니다.

CNAME 레코드CNAME 파일
어디에DNS저장소 루트
하는 일이 이름을 저 이름으로 넘긴다이 저장소가 받을 도메인은 이것이다
없으면주소가 GitHub 을 못 찾는다기본 방식에서는 GitHub 이 어느 도메인인지 모른다

방향이 반대입니다. 레코드는 밖에서 안으로 오는 길이고, 파일은 안에서 “나는 이 이름으로 불린다” 고 말하는 쪽입니다. 둘 다 있어야 연결됩니다.

그런데 커스텀 Actions 로 옮기면 이 파일은 더 이상 읽히지 않습니다. 도메인은 저장소의 Pages 설정에 남고, GitHub 문서도 이 방식에서는 CNAME 파일이 무시되며 필요 없다고 적습니다.3 저도 빼먹으면 도메인이 풀린다고 적어두고 워크플로에 챙겨 담고 있었습니다. 안에서 말하는 자리가 파일에서 설정으로 옮겨갔을 뿐, 둘 다 있어야 하는 것은 그대로입니다.

마지막으로 GitHub 이 Let’s Encrypt 인증서를 발급하면 Enforce HTTPS 를 켭니다. 발급되는 동안은 체크박스가 눌리지 않습니다.

블로그는 서브도메인이라 한 줄로 끝났습니다#

나중에 이 블로그를 붙일 때는 위의 것 중 도메인과 DNS 쪽이 거의 다 필요 없었습니다.

blog.elkiss.me 는 서브도메인입니다. apex 가 아니니 CNAME 을 그냥 걸 수 있고, flattening 도 A 레코드도 필요 없습니다. Cloudflare 에 blog → <계정>.github.io 한 줄, DNS only. 그게 전부였습니다.

저장소 이름은 안 들어갑니다. DNS 가 가리키는 것은 계정이고, 그중 어느 저장소가 이 도메인을 받을지는 저장소의 Pages 설정이 정합니다 — 앞에서 본 두 방향 그대로입니다.

돈도 안 듭니다. 사는 것은 elkiss.me 하나뿐이고 그 아래는 무제한입니다.

elkiss.me          ← 구매 대상
├─ www.elkiss.me   ← 무료
├─ blog.elkiss.me  ← 무료
└─ 임의.elkiss.me  ← 무료

Pages 는 저장소 하나당 커스텀 도메인 하나라서, 블로그는 별도 저장소로 만들고 서브도메인이 그 저장소를 받았습니다.

돌아보면 elkiss.me 의 @ 줄과 블로그의 blog 줄은 이름 칸 하나만 다릅니다. 둘 다 <계정>.github.io 로 넘기는 CNAME 입니다. 그런데도 처음이 훨씬 길었던 것은, 그 한 줄이 apex 에서는 원래 안 되고 되게 해주는 곳을 골라야 했기 때문입니다.


  1. ICANN Transfer Policy 는 등록일로부터 60일 안에 들어온 이전 요청을 레지스트라가 거부할 수 있는 사유로 열거합니다. 금지가 아니라 거부를 허용하는 형태입니다. 레지스트라를 옮긴 직후에도 같은 60일이 붙습니다. 이것을 30일로 줄이고 의무로 바꾸는 개정안이 2025년 3월 GNSO 를 통과했지만, 이 글을 쓰는 시점에는 아직 시행 전입니다. ↩︎

  2. RFC 1034 와 RFC 2181 이 정하는 제약입니다. RFC 2181 §10.1 은 CNAME 옆에 같이 있어도 되는 것을 딱 셋만 열거하는데(전부 보안 확장용입니다), NS 와 SOA 는 그 목록에 없습니다. ↩︎

  3. GitHub 문서 Managing a custom domain for your GitHub Pages site 는 브랜치에서 배포할 때만 CNAME 파일을 커밋한다고 적습니다. 커스텀 Actions 워크플로로 배포하면 그 파일을 만들지 않고, 이미 있는 것도 무시하며 필요로 하지 않습니다. ↩︎