서브도메인 발행을 DB 한 줄로 — 와일드카드 DNS의 함정

와일드카드 DNS와 호스트 라우팅을 미리 깔면 새 서브도메인은 DB 한 줄로 열린다. 단 DNS와 TLS 와일드카드는 규칙이 달라, 그 경계를 모르면 사고가 난다.
English  ·  日本語  ·  한국어
← 목록으로
박
박지훈
2026년 09월 09일 수정 2026.10.17 읽기 15분 조회 12

새 서브도메인 하나를 열자고 DNS 레코드를 만들고, 인증서를 받고, 서버 블록을 추가하고 재시작한다. 프로젝트가 수백 개면 이 일을 수백 번 한다. 그런데 이 중 대부분은 한 번만 해두면 되는 일이다. 와일드카드 DNS와 호스트 라우팅을 미리 깔아두면, 새 서브도메인을 여는 일은 데이터베이스에 행 하나 추가하는 것으로 끝난다.

여기까지는 깔끔하다. 진짜 이야기는 그다음이다. "와일드카드 DNS"와 "와일드카드 인증서(TLS)"는 이름만 같을 뿐 적용 규칙이 다르다. 이 둘을 같은 규칙이라고 믿는 순간, "DNS는 분명 응답하는데 인증서 때문에 안 열리는" 사고가 난다. 그래서 이 글의 중심 질문은 "몇 단계까지 되느냐"가 아니라 "언제 안 되느냐"다.

정문 하나, 안내데스크 하나

건물 주소(도메인)는 하나지만, 그 안에 방(서브도메인)은 얼마든지 만들 수 있다. 방을 하나 틀 때마다 외벽을 뚫지는 않는다. 방문객은 모두 정문 하나로 들어오고, 로비의 안내데스크가 "어느 방 찾으세요?"를 듣고 호실을 알려준다. 이 그림만 머리에 깔아두면 뒤의 내용이 전부 여기에 들어온다.

  • 정문 = 와일드카드 DNS. 모든 방문객을 받는 단일 진입점.
  • 안내데스크 = 호스트 기반 라우팅. 이름을 듣고 방을 찾아준다.
  • 명부 = 라우팅 테이블(DB).

방을 하나 더 여는 일은 배관 공사가 아니라, 명부에 이름 한 줄 적는 것이다.

한 번만 까는 바탕: 와일드카드 DNS

DNS에 *.ontle.kr 형태의 와일드카드 레코드를 등록해둔다. 여기서 존(zone)은 한 도메인의 DNS 레코드를 담는 묶음이고, 레이블(label)은 도메인을 점으로 끊은 각 토막(report-2026·ontle·kr)이다. 존에 따로 정의되지 않은 이름으로 질의가 들어오면, 이 와일드카드가 대신 응답을 만들어 보낸다. report-2026.ontle.kr이든 dashboard-abc.ontle.kr이든, 미리 등록하지 않아도 같은 목적지로 나간다.

; 와일드카드 A 레코드 (직접 IP로)
*.ontle.kr.    3600   IN   A       203.0.113.10

; 또는 CDN/터널을 경유하면 CNAME
*.ontle.kr.    3600   IN   CNAME   edge.example-cdn.net.

흔히 "와일드카드는 바로 아래 한 단계만 커버한다"고들 하는데, 그건 DNS의 규칙이 아니다. 어느 계층의 규칙인지는 뒤에서 따로 짚는다. 여기서는 바탕을 한 번 깔았다는 것까지만 기억하면 된다.

요청을 받는 순간: 진입점이 이름표를 읽는다

모든 요청은 하나의 진입점으로 들어온다. 이 진입점은 리버스 프록시(들어오는 요청을 제일 앞에서 받아 뒤 서버로 넘기는 중계소)나 CDN 엣지, 또는 아웃바운드 터널이다. 진입점은 HTTP 요청에 실린 Host 헤더 — 브라우저가 주소창에 넣은 도메인 이름 — 를 읽는다. TLS를 쓰면 SNI(접속하려는 이름을 미리 알려주는 값)가 같은 역할을 한다. 어느 문으로 들어왔는지는 IP가 아니라 이름표로 갈린다.

GET / HTTP/1.1
Host: report-2026.ontle.kr

발행: 명부에 한 줄

진입점 뒤의 애플리케이션은 Host 값을 받아 "이 이름이 등록돼 있나?"를 조회한다. 있으면 연결된 콘텐츠를, 없으면 404나 안내 페이지를 보여준다. 명부는 개념적으로 이렇게 생겼다.

서브도메인 콘텐츠 유형 소유자 상태
report-2026 분석 대시보드 프로젝트 A 발행됨
jihoon-blog 개인 블로그 사용자 B 발행됨
team-x 팀 랜딩 팀 X 임시중지

새 서브도메인을 여는 일이 "DNS 추가 → 인증서 발급 → 서버 설정 → 재시작"에서 이 표에 행 하나 추가로 줄어든다. DNS와 인증서는 이미 준비돼 있으니, 남는 건 데이터베이스 트랜잭션 하나뿐이다. DNS 전파를 기다릴 이유도 없다. 와일드카드 레코드는 오래전에 퍼져 끝난 상태고, 바뀌는 건 애플리케이션 안의 명부뿐이라 보통 수 초 안에 반영된다.

서빙: 뷰어에 갇히지 않는 독립 웹사이트

고객에게 납품할 분석 대시보드를 report-2026이라는 이름으로 발행했다고 하자. report-2026.ontle.kr에 접속하면 온틀의 헤더나 내비게이션이 감싸는 게 아니라, 그 대시보드의 원본 HTML이 최상위 문서로 그대로 응답된다.

차이는 체감으로 드러난다. 이 주소를 카카오톡에 보내면 온틀 로고가 아니라 그 대시보드의 제목·설명·썸네일이 뜬다. 구글 검색에도 "온틀"이 아니라 그 사이트가 직접 색인된다. 명함처럼 건네도, 검색엔진에 잡혀도 온전히 고객 것으로 보인다. 이것이 온틀에서 산출물을 발행했을 때 이름.ontle.kr이 뷰어에 갇히지 않고 독립 웹사이트로 보이는 이유다.

두 와일드카드는 다른 규칙이다

여기서부터가 내가 실무에서 사고를 가장 많이 본 지점이다. 두 "와일드카드"는 이름만 같고 적용 범위 규칙이 다르다. 뭉개는 순간 "DNS는 응답하는데 인증서 오류로 안 열리는" 상황을 만난다.

  • DNS 와일드카드(*.ontle.kr 레코드): 레이블 개수로 제한되지 않는다. 가로막는 노드만 없으면 a.b.ontle.kr처럼 여러 단계 아래 이름에도 응답을 만들어낸다. 단, 이미 존재하는 이름에는 적용되지 않는다(근거 RFC 4592).
  • TLS 와일드카드 인증서(*.ontle.kr): 가장 왼쪽 레이블 한 개만 매칭한다. report-2026.ontle.kr은 덮지만 a.b.ontle.kr이나 ontle.kr(맨 위 이름 그 자체)은 덮지 못한다. 이 "한 단계" 제한이 RFC 6125 §6.4.3과 CA/Browser Forum Baseline Requirements가 정한 규칙이고, 후속 표준 RFC 9525도 그대로 유지한다.

그래서 진짜 질문은 "언제 안 되느냐"다. 이름.ontle.kr처럼 한 단계짜리만 발행하는 한, 와일드카드 인증서 한 장이면 충분하다 — 온틀의 기본 발행 형태다. 하지만 팀명.고객사.ontle.kr처럼 두 단계 깊이로 내려가는 이름을 열고 싶다면, DNS는 (중간 노드가 없으면) 응답하더라도 TLS 인증서는 *.ontle.kr 한 장으로 안 덮인다. 그 깊이를 쓰려면 *.고객사.ontle.kr 인증서를 따로 발급하거나, 인증서의 SAN(한 인증서가 커버하는 이름 목록)에 그 이름을 직접 넣어야 한다. "DNS가 응답한다"와 "TLS가 신뢰된다"는 별개의 층위다.

[심화] 와일드카드가 안 통하는 한 곳 — empty non-terminal

안내데스크 비유를 다시 가져오자. 명부에 이름이 없어도, 그 아래 세부 호실에 이미 입주자가 있으면 안내데스크는 그 구역을 "빈 이름"으로 보지 않는다. DNS도 똑같다. 이름이 존재하는 경로는 둘이다 — (1) 그 이름에 직접 레코드가 붙어 있거나, (2) 자기 레코드는 없어도 그 아래에 레코드를 가진 하위 이름이 있는 경우. 두 번째가 empty non-terminal(빈 비단말, ENT)이다. 와일드카드는 "정의되지 않은 이름"만 받아주므로, 이렇게 존재하는 이름은 건드리지 않는다.

문제없는 경우 — DNS는 *.ontle.kr 그대로 두고 발행은 DB 명부에만 한다. 새 DNS 레코드가 안 생기니 ENT도 안 생긴다. x.team.ontle.kr을 발행해도 team.ontle.kr은 와일드카드 응답을 그대로 유지한다.

함정 — x.team.ontle.kr에 실제 DNS 레코드를 직접 박고 상위 team.ontle.kr엔 아무것도 두지 않으면, team.ontle.kr이 ENT가 된다. 이제 와일드카드가 team.ontle.kr을 대신 받지 않는다. 조회하면 NXDOMAIN("이름 자체가 없음")이 아니라 NODATA("이름은 있는데 그 타입의 레코드가 없음")가 돌아오고, 결국 안 열린다.

요약하면, DB에만 행을 추가하는 한 ENT는 안 생긴다. 깊은 이름에 개별 DNS 레코드를 박기 시작할 때만 조심하면 된다(RFC 4592).

운영에서 미끄러지는 자리들

구조가 단순할수록 운영 규칙이 느슨하면 사고가 커진다. 서브도메인을 대량으로 발급한다면 아래는 기본으로 깔고 가는 게 좋다.

예약어가 뚫리면 무슨 일이 생기나

www, api, admin, mail, blog, app, cdn, status 같은 이름을 사용자가 임의로 가져가면, 사용자 콘텐츠가 시스템 관리 페이지처럼 보이거나 반대로 실제 시스템 서비스가 밀려난다. 처음부터 예약 목록에 넣어 막고, 입력값은 대소문자·공백을 정규화해서 비교한다.

RESERVED_SUBDOMAINS = frozenset({
    "www", "api", "admin", "mail", "ftp", "blog",
    "app", "cdn", "status", "docs", "help", "support",
})

def is_reserved(name: str) -> bool:
    normalized = name.strip().lower()
    return normalized in RESERVED_SUBDOMAINS

같은 이름 동시 요청, 경쟁 상태를 막으려면

이름 배정은 애플리케이션 로직의 "있는지 확인 후 생성"에 맡기면 안 된다. 같은 이름을 동시에 요청한 두 트랜잭션이 둘 다 "없다"고 판단해 둘 다 끼워 넣으면, 한 이름에 두 산출물이 걸린다. 정규화한 이름 컬럼에 데이터베이스 UNIQUE 제약을 걸고, 중복 삽입 시 DB가 내는 오류를 애플리케이션에서 "이미 사용 중"으로 매핑하는 게 안전하다.

한 계정이 수천 개를 찍어낼 때

제한 없이 발급하면 스팸성 대량 생성과 자원 고갈에 그대로 노출된다. 요금제나 계정 등급별로 "동시 발행 가능한 서브도메인 수"에 상한을 두고, 초과 시 명확히 안내하며 막는다.

캐시는 어떻게 배신하는가

CDN이나 엣지 캐시를 쓰면, 새로 발행한 이름이 아직 캐시에 없어 첫 요청이 느리거나, 반대로 회수한 이름이 캐시에 남아 옛 콘텐츠를 계속 보여준다. 발행·회수 이벤트가 나면 그 이름의 캐시 무효화(purge)를 라우팅 테이블 갱신과 한 트랜잭션처럼 묶어두는 게 좋다.

끝은 DB가 아니라 브라우저다

로그엔 성공이 찍혔는데 고객은 404를 본다. 라우팅 테이블에 행이 들어갔다고 그 주소가 실제로 열린다는 보장은 없다. 흔한 원인은 이렇다 — 캐시가 안 갱신됐거나, 라우팅 조건에 오타가 있거나, (개별 DNS 레코드를 박았다면) 앞서 본 ENT 때문에 DNS 단계에서 이미 막혔거나, 인증서가 그 깊이를 못 덮거나.

그래서 서브도메인 발행은 "DB에 기록 성공"에서 끝나면 안 된다. 실제 URL에 HTTP 200이 뜨는지, 브라우저로 열어 화면이 진짜 나오는지 눈으로 확인하는 데까지가 한 세트다. 로그와 DB 카운트는 "시도했다"는 증거이지 "됐다"는 증거가 아니다.

"라우팅 테이블에 report-2026.ontle.kr을 추가했습니다." — 여기까지는 시도다. 그 주소에 들어갔을 때 화면이 뜨는지는 아직 아무도 모른다.

"라우팅 테이블에 추가하고, curl -sI https://report-2026.ontle.kr로 HTTP 200과 TLS 정상을 확인했고, 브라우저로 열어 대시보드가 렌더링되는 것까지 봤습니다." — 이제야 됐다고 말할 수 있다.

온틀로 시작하기

산출물을 만들 때마다 DNS·인증서·배포 서버를 다시 만질 필요는 없다. 온틀은 이 구조 위에서, 만든 결과물을 이름.ontle.kr이라는 독립 웹사이트로 그 자리에서 발행한다. 직접 해보고 싶다면 https://ontle.kr 에서 시작할 수 있다.

FAQ

와일드카드 인증서 한 장을 모든 서브도메인이 공유하는데, 보안상 괜찮나요?

`*.ontle.kr` 인증서 한 장이 1단계 이름 전부를 덮는 건 편의의 핵심이자 대가입니다. 모든 서브도메인이 같은 인증서와 개인키에 묶이므로, 그 키가 유출되면 영향 범위가 전체로 번집니다. 그래서 키 보관(접근 권한 최소화)과 주기적 교체를 운영 기본으로 챙겨야 합니다. 특정 서브도메인을 더 강하게 보호하고 싶다면 그 이름에 전용 인증서를 추가로 발급해, 그 서버에서는 전용 인증서만 제시하도록 할 수 있습니다. 다만 전용 인증서를 발급한다고 기존 `*.ontle.kr` 와일드카드가 그 이름에 대해 유효성을 잃는 것은 아닙니다 — 와일드카드 인증서는 여전히 그 이름에도 유효하므로, 와일드카드 개인키가 유출되면 그 이름을 사칭하는 위험은 그대로 남습니다. 즉 전용 인증서 발급만으로 그 이름이 와일드카드의 적용 범위에서 빠지거나 "완전히 격리"되지는 않습니다(참고: RFC 9525 §7.1).

와일드카드라 아무 이름이나 DNS가 응답하는데, 미등록 이름이 오·남용되지는 않나요?

DNS가 응답한다고 콘텐츠가 있는 건 아닙니다. 등록되지 않은 이름으로 들어오면 애플리케이션이 명부에 없다고 판단해 404나 안내 페이지로 돌려보냅니다. 여기에 예약어 목록과 계정당 발행 수 상한을 더하면, "아무 이름이나 받아주는" 입구가 실제 남용으로 이어지는 걸 앞단에서 막을 수 있습니다.

이미 서브도메인별로 따로 세팅해 둔 서비스를 이 구조로 옮길 수 있나요?

DNS는 이미 `*.ontle.kr` 와일드카드가 받고 있으니 새 이름은 명부(DB)에 추가하는 것으로 흡수됩니다. 다만 과거에 개별 DNS 레코드를 직접 박아둔 이름이 있다면, 그걸 정리할 때 상위 이름이 ENT가 되지 않는지부터 확인해야 합니다. 그 이름 자체를 계속 서빙해야 한다면 명시적 레코드를 유지하거나, 라우팅을 명부로 옮기면서 불필요한 개별 레코드를 걷어내는 순서로 진행하세요.

참고자료


글 · 박지훈