← 블로그 목록

Developer Knowledge

개발자가 알아야 할 지식: DNS TTL과 캐시, 배포는 왜 바로 바뀌지 않을까

도메인 레코드를 바꿨는데 일부 사용자에게만 예전 서버가 보이는 이유를 DNS 캐시와 TTL 관점에서 설명하고, 배포·마이그레이션·장애 대응에서 안전하게 다루는 방법을 정리한다.

Paul Mockapetris / IETF
  • 개발자가 알아야 할 지식
  • Software Engineering
  • DNS
  • Networking
  • Operations

왜 개발자가 알아야 하나

도메인 설정은 종종 “인프라 담당자가 한 번 만지는 것”처럼 취급된다. 하지만 실제 서비스 운영에서는 애플리케이션 개발자도 DNS의 영향을 계속 받는다. 새 서버로 트래픽을 옮기고, CDN을 붙이고, API 엔드포인트를 교체하고, 장애 시 우회 도메인을 열고, SaaS 연동을 위해 TXT 레코드를 추가한다. 이때 가장 자주 나오는 말이 “DNS 전파가 아직 안 됐다”다. 문제는 이 표현이 너무 뭉뚱그려져 있어서, 무엇을 기다리는지, 얼마나 기다려야 하는지, 지금 사용자가 왜 서로 다른 결과를 보는지 설명하기 어렵다는 점이다.

DNS는 중앙 서버 하나가 전 세계의 모든 질문에 실시간으로 답하는 구조가 아니다. RFC 1034가 설명하듯 DNS는 계층적 이름 공간과 분산 데이터베이스를 기반으로 하고, 성능을 위해 로컬 캐싱을 적극적으로 사용한다. 브라우저, 운영체제, 사내 DNS 리졸버, 통신사 리졸버, 퍼블릭 리졸버, CDN, 권한 있는 네임서버가 각자 역할을 나눠 가진다. 어떤 레코드를 조회하면 그 결과는 여러 계층에 잠시 저장될 수 있고, 그 “잠시”를 결정하는 대표적인 값이 TTL(Time To Live)이다.

TTL을 모르면 배포 계획이 운에 기대게 된다. 예를 들어 api.example.com의 A 레코드를 새 로드 밸런서로 바꿨다고 해보자. 설정 화면에서는 변경이 완료됐고, 내 노트북에서 dig를 해보면 새 IP가 보인다. 그런데 일부 고객은 여전히 이전 IP로 접속한다. 모바일 앱은 괜찮은데 사내망 사용자만 실패한다. 장애 대응 중 레코드를 되돌렸는데 30분 동안 에러가 계속된다. 이런 현상은 대개 “DNS가 이상하다”가 아니라, 캐시와 TTL을 고려하지 않은 운영 절차가 드러난 것이다.

개발자가 DNS TTL을 이해해야 하는 이유는 단순히 네트워크 지식이 멋있어서가 아니다. 외부 의존성이 있는 현대 서비스에서는 이름 해석이 곧 가용성의 일부다. API 클라이언트가 DNS 결과를 얼마나 오래 캐시하는지, 커넥션 풀은 IP 변경을 언제 반영하는지, Kubernetes나 서비스 메시의 내부 DNS는 어떤 TTL을 쓰는지, CDN 앞단에서 CNAME을 어떻게 처리하는지에 따라 배포와 장애의 모양이 달라진다. “도메인만 바꾸면 된다”는 말 뒤에는 실제로 여러 캐시가 순차적으로 만료되어야 한다는 현실이 숨어 있다.

핵심 개념

DNS 조회는 보통 사용자의 애플리케이션이 직접 루트 네임서버부터 끝까지 찾아가는 방식으로 일어나지 않는다. 애플리케이션은 운영체제의 resolver 또는 런타임의 DNS 라이브러리에 이름 해석을 요청하고, 그 요청은 재귀 리졸버(recursive resolver)로 전달된다. 재귀 리졸버는 필요한 경우 루트, TLD, 권한 있는 네임서버(authoritative nameserver)를 따라가며 답을 얻고, 결과를 캐시에 저장한다. 다음에 같은 질문이 오면 권한 있는 서버까지 다시 가지 않고 캐시된 답을 돌려줄 수 있다.

TTL은 DNS 리소스 레코드가 캐시에 머물 수 있는 시간이다. 권한 있는 네임서버가 “이 A 레코드는 300초 동안 유효하다고 봐도 된다”라고 말하면, 리졸버는 일반적으로 그 시간 동안 결과를 재사용할 수 있다. TTL이 남아 있는 동안 권한 있는 서버의 원본 값을 바꿔도 이미 캐시한 리졸버는 즉시 새 값을 보지 않을 수 있다. 그래서 DNS 변경은 “전 세계에 새 값을 밀어 넣는(push) 작업”이 아니라 “기존 캐시가 만료되면 새 값을 다시 가져가는(pull) 과정”에 가깝다.

여기서 중요한 세부사항은 TTL이 하나의 장소에만 적용되지 않는다는 점이다. 브라우저와 운영체제는 자체 DNS 캐시를 둘 수 있고, JVM 같은 런타임은 별도의 캐시 정책을 가질 수 있으며, 기업 네트워크나 통신사 리졸버는 중간에서 결과를 보관할 수 있다. 일부 환경은 표준 TTL을 그대로 따르지 않거나 최소·최대 TTL을 강제로 적용하기도 한다. 따라서 “TTL을 60초로 낮췄으니 1분 뒤에는 모두 바뀐다”라고 단정하면 위험하다. 대개는 빨라지지만, 모든 클라이언트와 중간 리졸버가 정확히 같은 방식으로 움직인다는 보장은 없다.

DNS에는 긍정 응답만 캐시되는 것도 아니다. 존재하지 않는 이름에 대한 응답, 즉 NXDOMAIN 같은 부정 응답도 캐시될 수 있다. RFC 2308은 negative caching을 다루며, 존재하지 않는 레코드에 대한 지식을 저장하는 것이 응답 시간을 줄이고 네임서버와 네트워크 부하를 낮춘다고 설명한다. 실무적으로는 아직 만들지 않은 서브도메인을 미리 조회했거나, 검증용 TXT 레코드를 잘못 넣었다가 고쳤을 때도 “없다”는 결과가 한동안 남을 수 있다는 뜻이다.

레코드 종류도 구분해야 한다. A와 AAAA는 이름을 IPv4·IPv6 주소로 연결한다. CNAME은 한 이름을 다른 이름의 별칭으로 만든다. MX는 메일 교환 서버, TXT는 도메인 소유권 검증이나 SPF·DKIM 같은 정책에 많이 쓰인다. CNAME을 쓰면 최종 IP를 얻기까지 여러 이름을 따라가야 하며, 각 단계의 TTL과 캐시 상태가 영향을 준다. CDN이나 클라우드 로드 밸런서 앞에서는 CNAME 대상의 내부 변경이 사용자에게 투명하게 처리되기도 하지만, 별칭 체인이 길어질수록 문제를 추적할 때 확인해야 할 지점도 늘어난다.

TTL은 낮을수록 항상 좋은 값이 아니다. 짧은 TTL은 변경을 빠르게 반영하는 데 유리하지만, 캐시 효율을 낮추고 권한 있는 네임서버와 리졸버에 더 많은 조회를 만든다. 반대로 긴 TTL은 조회 비용과 지연을 줄이고 안정적인 이름에는 좋지만, 장애 대응이나 마이그레이션 때 오래된 값이 오래 남는다. 결국 TTL은 “변경 민첩성”과 “캐시 효율·안정성” 사이의 운영상 트레이드오프다. 자주 바뀔 수 있는 레코드와 거의 바뀌지 않는 레코드를 같은 기준으로 두면 어느 쪽이든 손해를 본다.

작은 예시 또는 체크리스트

다음은 api.example.com을 기존 로드 밸런서에서 새 로드 밸런서로 옮기는 전형적인 절차다.

  • 현재 레코드와 TTL을 확인한다. 예: api.example.com A 203.0.113.10 TTL 3600.
  • 변경 예정 최소 하루 전, 가능하면 기존 TTL보다 충분히 이른 시점에 TTL을 낮춘다. 예: 3600초에서 300초로 변경한다.
  • 낮춘 TTL이 기존 캐시에서 만료될 시간을 기다린다. 기존 TTL이 3600초였다면 최소 1시간은 지나야 대부분의 리졸버가 새 TTL을 보게 된다.
  • 새 로드 밸런서가 준비됐는지 인증서, 헬스 체크, 방화벽, 로그, 백엔드 연결을 먼저 검증한다.
  • A/CNAME 레코드를 새 대상으로 바꾼다. 이후 여러 리졸버에서 조회 결과를 확인하되, 즉시 모든 곳이 같아야 한다고 기대하지 않는다.
  • 이전 서버는 바로 끄지 않는다. 최소한 예전 TTL과 연결 유지 시간을 고려해 한동안 양쪽을 모두 정상 처리하게 둔다.
  • 안정화 후 TTL을 장기 운영에 맞는 값으로 다시 조정한다. 60초 같은 낮은 TTL을 영구 설정으로 방치하지 않는다.

이 절차의 핵심은 “변경 직전에 TTL을 낮추면 된다”가 아니라는 점이다. 기존에 이미 1시간 TTL로 캐시된 리졸버는 그 1시간이 끝날 때까지 낮아진 TTL 자체를 모를 수 있다. TTL 낮추기는 실제 변경보다 먼저 해야 효과가 있다. 이 순서를 놓치면 설정은 맞는데 트래픽이 천천히 섞이는 시간을 피할 수 없다.

장애 대응에서는 조금 다른 기준이 필요하다. 이미 높은 TTL로 잘못된 IP가 퍼졌다면 DNS만 고쳐서는 즉시 회복되지 않을 수 있다. 이때는 이전 IP 쪽에 임시 프록시를 세우거나, 로드 밸런서 설정을 복구하거나, 클라이언트 재시도와 커넥션 풀 갱신을 유도하는 방법을 함께 고려해야 한다. DNS는 강력한 라우팅 도구지만, 초 단위 페일오버 장치로만 기대하면 실망하기 쉽다.

실무에서 자주 생기는 오해

  • “DNS 전파가 안 됐다”는 표현은 원인을 가린다. 실제로는 권한 있는 네임서버 갱신이 안 된 것인지, 재귀 리졸버 캐시가 남은 것인지, 운영체제 캐시인지, 애플리케이션 런타임 캐시인지, CDN 설정인지 나눠 봐야 한다.

  • “내 컴퓨터에서 새 IP가 보이니 배포가 끝났다”는 충분한 검증이 아니다. 내 환경이 쓰는 리졸버는 이미 캐시가 만료됐을 수 있지만, 다른 지역·통신사·기업망의 리졸버는 다를 수 있다. dig @1.1.1.1, dig @8.8.8.8, 사내 리졸버, 권한 있는 네임서버 직접 조회를 구분해 보는 습관이 좋다.

  • “TTL을 0 또는 매우 낮게 두면 안전하다”도 반만 맞다. 낮은 TTL은 긴급 변경에는 도움이 되지만, 조회 부하와 지연을 늘리고 일부 리졸버가 최소 TTL을 적용하면 기대만큼 빠르지 않을 수 있다. 안정적인 도메인은 적절히 긴 TTL이 더 낫다.

  • “CNAME은 그냥 별명이라 비용이 없다”는 착각도 흔하다. CNAME 자체는 편리하지만 최종 주소를 얻기까지 추가 조회와 캐시 계층이 생긴다. 루트 도메인(apex)에는 일반적인 CNAME을 둘 수 없는 제약도 있어, DNS 사업자의 ALIAS/ANAME 같은 비표준 기능을 쓰는 경우 동작 방식을 따로 이해해야 한다.

  • “DNS를 바꾸면 기존 연결도 새 서버로 간다”는 틀렸다. DNS는 새 연결이 이름을 해석할 때 영향을 준다. 이미 열린 TCP 연결, HTTP keep-alive, gRPC 채널, 데이터베이스 커넥션 풀은 DNS 변경만으로 즉시 끊기지 않는다. 클라이언트가 언제 다시 해석하는지도 별도 문제다.

  • “없는 도메인을 만들었으니 바로 조회돼야 한다”도 항상 맞지 않다. 앞서 잘못 조회되어 NXDOMAIN이 캐시됐다면 부정 캐시 TTL 동안 계속 없다고 보일 수 있다. 도메인 검증 작업에서는 레코드를 먼저 만들고, 값과 네임서버를 확인한 뒤 검증 버튼을 누르는 편이 안전하다.

오늘 바로 적용해보기

가장 먼저 할 일은 운영 중인 주요 도메인의 TTL을 목록화하는 것이다. 사용자 트래픽 진입점, API, 관리자 도구, 웹훅 수신 주소, 이메일 관련 레코드, 도메인 검증용 TXT를 구분해 현재 TTL과 변경 책임자를 적어두자. 자주 바뀌는 레코드와 거의 바뀌지 않는 레코드가 같은 TTL을 쓰고 있다면 이유를 확인한다. 이유가 없다면 운영 목적에 맞게 조정할 수 있다.

두 번째는 DNS 변경 절차에 “TTL 사전 조정”과 “이전 대상 유지 기간”을 명시하는 것이다. 마이그레이션 티켓에 “레코드 변경” 한 줄만 쓰지 말고, 기존 TTL 확인, TTL 낮추는 시각, 실제 변경 시각, 검증할 리졸버, 롤백 방법, 이전 인프라 종료 시각을 넣자. 특히 이전 서버를 끄는 시각은 레코드 변경 직후가 아니라 캐시와 연결 수명을 고려한 뒤여야 한다.

세 번째는 애플리케이션의 DNS 캐시 정책을 확인하는 것이다. 사용하는 언어 런타임, HTTP 클라이언트, 서비스 디스커버리 라이브러리가 DNS를 언제 다시 조회하는지 문서로 남겨두면 장애 때 큰 차이가 난다. 긴 수명의 프로세스가 시작 시점에만 DNS를 조회한다면 TTL을 낮춰도 의미가 줄어든다. 커넥션 풀 재생성, 프로세스 재시작, 클라이언트 설정 변경이 필요한지 미리 알아두자.

네 번째는 모니터링을 IP가 아니라 이름과 경로 관점으로도 보는 것이다. “새 IP에 요청이 온다”뿐 아니라 “옛 IP에도 아직 요청이 오는가”, “어느 지역이나 ASN에서 오래된 값이 남는가”, “NXDOMAIN이나 SERVFAIL이 늘었는가”를 확인하면 DNS 변경 중 문제를 빨리 발견할 수 있다. 장애 상황에서는 dig +trace, 권한 있는 네임서버 직접 질의, 여러 퍼블릭 리졸버 비교가 기본 도구가 된다.

마지막으로 DNS를 만능 페일오버로 설계하지 말자. 빠른 장애 전환이 필요하다면 로드 밸런서, Anycast, 헬스 체크 기반 라우팅, CDN, 클라이언트 재시도 정책 같은 계층과 함께 설계해야 한다. DNS는 이름을 안정적으로 찾게 해주는 기반이고, TTL은 그 기반의 캐시 전략이다. 초 단위 복구를 DNS 레코드 변경 하나에만 맡기면 캐시라는 설계 원리와 싸우게 된다.

더 알아보기

오늘의 takeaway

DNS 변경은 값을 밀어 넣는 버튼이 아니라, 여러 캐시가 TTL에 따라 새 사실을 다시 배워가는 과정이다.