조회수: 16

getaddrinfo ENOTFOUND 해결

getaddrinfo ENOTFOUND는 Node가 호스트명을 못 푼 것. DNS 문제인지 오타·전체 URL 탓인지 3단계로 가립니다. 무료 즉시 진단으로 확인.

내 도메인에 이 문제가 있는지 지금 확인

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

Problem

Node가 Error: getaddrinfo ENOTFOUND <호스트명>을 던지고 요청은 프로세스 밖으로 나가지도 못합니다. 에러 객체에서 err.codeENOTFOUND, err.syscallgetaddrinfo, err.hostname은 실제로 풀려던 이름입니다 — 이 마지막 필드부터 읽으세요. 절반은 그 이름이 당신 생각과 다릅니다.

Symptoms

  • axios, fetch, http.request, pg, mongoose 호출이 getaddrinfo ENOTFOUND로 거부됩니다.
  • 같은 URL이 브라우저나 curl에선 멀쩡한데 Node에서만 실패합니다.
  • 로컬에선 되는데 Docker·CI·프로덕션에선 안 되거나, 그 반대입니다.

getaddrinfo ENOTFOUND가 실제로 뜻하는 것

Node는 소켓을 열기 전에 호스트명을 IP로 바꿔야 합니다. 그 일을 dns.lookup()이 하고, 이건 libuv 스레드풀 위에서 운영체제의 getaddrinfo(3)를 호출합니다 — 당신 셸이 쓰는 것과 같은 리졸버 경로이고, /etc/hosts/etc/nsswitch.conf를 존중합니다. http/https 위에 얹힌 거의 모든 것(fetch, axios, DB 드라이버)이 내부적으로 dns.lookup()을 씁니다. (다른 계열인 dns.resolve*()는 내장 c-ares 라이브러리로 DNS 서버와 직접 대화합니다. 실패 방식이 달라서, 이쪽으로 바꾸면 다른 질문을 던지느라 문제가 “고쳐진” 것처럼 보일 때가 있습니다.)

ENOTFOUND는 그 OS 리졸버가 이름에 대해 쓸 수 있는 주소를 못 줬다는 뜻입니다. 무슨 말을 하는지 보세요: 연결을 연 적도, 한 바이트 보낸 적도, 서버에서 타임아웃 난 적도 없습니다. 실패는 0단계 — 이름을 IP로 바꾸는 단계 — 에서 났고, 그래서 그 아래 모든 것(TCP, TLS, 당신 엔드포인트)은 아직 등장조차 안 했습니다. 용의자는 셋으로 좁혀집니다: 이름이 틀렸거나, 이름은 맞는데 이 코드가 도는 곳에서 안 풀리거나, 사실 ENOTFOUND가 아니라 간헐적 EAI_AGAIN을 잘못 보고 있거나.

Top 3 Causes

  1. 호스트명이 잘못됐거나 깨졌다 — 가장 흔하고, “DNS가 고장 났다”인 경우는 거의 없습니다. 순수 호스트가 와야 할 자리에 전체 URL이 들어갔거나(api.example.com이 아니라 https://api.example.com/v1), 미설정 환경변수가 문자열 undefined로 박혔거나(`${process.env.DB_HOST}` 인데 DB_HOST가 없음), .env 파일의 뒤 공백·개행·따옴표가 호스트명에 딸려 왔습니다. err.hostname이 정확히 뭐가 나갔는지 보여줍니다 — 거기 슬래시나 콜론, undefined가 있으면 DNS 그만 쳐다보세요.
  2. 이름은 진짜인데 여기선 안 풀린다 — 호스트명은 멀쩡하고, 환경이 문제입니다. Docker Compose 서비스명(db, redis)은 그 컨테이너 네트워크 위에서만 풀립니다. 내부·VPN 전용 호스트명은 사무실에선 되지만 클라우드 빌드에선 안 됩니다. /etc/hosts 항목이 내 노트북에만 있습니다. 단서: 코드는 그대로인데 한 곳에선 풀리고 다른 곳에선 ENOTFOUND가 납니다.
  3. 사실은 일시적 리졸버 실패(EAI_AGAIN을 착각) — 부하 상황, 컨테이너 부팅 순간, 불안정한 DNS 서버에서는 리졸버에 잠깐 못 닿습니다. 그건 ENOTFOUND가 아니라 EAI_AGAIN인데, 사람들이 둘을 한 통에 넣고 엉뚱한 처방을 집습니다. 에러의 코드를 읽으세요: 일시적 실패엔 재작성이 아니라 재시도가 필요합니다.

ENOTFOUND vs EAI_AGAIN — 처방을 가르는 갈림길

이 둘은 syscall이 같아 늘 헷갈리고, 헷갈리면 몇 시간을 태웁니다. ENOTFOUND는 확정 답입니다: 리졸버가 응답했고, 그 응답이 “이 이름엔 주소가 없다”였습니다. 영구적입니다 — 1밀리초 뒤든 한 시간 뒤든 같은 조회는 같은 식으로 실패하니, 재시도 루프는 더 빨리 실패할 뿐입니다. 고칠 곳은 당신 코드 아니면 DNS 레코드입니다.

EAI_AGAIN은 답의 부재입니다: 리졸버에 못 닿았거나 제때 응답이 없었습니다. 일시적입니다 — 올라오는 중인 네트워크, 딸꾹질하는 DNS 서버, 시작하며 제 DNS와 경주하는 컨테이너. 여기선 상한을 둔 백오프 재시도가 정확한 처방이고, 호스트명 처리를 아무리 고쳐도 소용없습니다. 뭘 바꾸기 전에 err.code를 찍어, 어느 문제인지부터 말하게 하세요.

Diagnose with DechoNet

  • DNS Check — Node가 보고한 그대로 err.hostname 값을 붙여넣으세요. DechoNet이 정상 A/AAAA 레코드를 주는데 코드는 여전히 ENOTFOUND면, 이름은 멀쩡하고 문제는 로컬입니다: 당신 리졸버, 컨테이너 네트워크, 또는 /etc/hosts 항목. 여기서도 레코드가 없으면, 그 이름은 공개 인터넷에서 진짜로 안 풀리는 것 — 버그는 Node가 아니라 호스트명이거나 없는 레코드입니다.
  • Propagation Check — 방금 레코드를 만들거나 바꿨다면 여러 리졸버에 한 번에 물어보세요. 일부는 답하고 일부는 안 하면 이름이 없는 게 아니라 전파 지연입니다 — 앱을 디버깅하지 말고 시간을 주세요.

Resolution Checklist

  • err.hostname을 찍어 글자 그대로 읽습니다 — 슬래시·콜론·undefined·뒤 공백이 있으면 버그는 DNS 위쪽입니다.
  • 전체 URL이면 먼저 파싱합니다: new URL(str).hostname이 이 API들이 원하는 순수 호스트를 줍니다.
  • 호스트명을 조립하는 모든 환경변수가 이 환경에 실제로 설정돼 있는지 확인합니다 — 내 노트북 말고.
  • err.hostname으로 외부 DNS Check를 돌립니다. 어디에도 레코드가 없으면 이름을 고치거나 레코드를 올리세요 — 앱 디버깅을 멈춥니다.
  • Docker/Compose라면 컨테이너가 서비스명이 사는 네트워크에 있는지, 그리고 localhost로 호스트에 닿으려는 건 아닌지 확인합니다.
  • err.code를 찍습니다. EAI_AGAIN이 한 번이라도 보이면 영구 실패로 취급하지 말고 상한을 둔 백오프 재시도를 넣습니다.
  • 두 번째 기기나 네트워크에서 이름을 풀어, 오래된 로컬 캐시나 VPN DNS를 배제합니다.

When to Escalate

  • 외부 DNS Check에서도 레코드가 없으면 고칠 몫은 도메인 소유자입니다: A/AAAA 레코드가 없거나 네임서버 위임이 틀렸고, 존재하지 않는 이름은 Node 쪽 코드로 아무리 해도 안 풀립니다.
  • 이름이 당신 플랫폼 안(관리형 컨테이너, 서버리스, 잠긴 VPC)에서만 빼고 다 풀리면, 그 네트워크의 DNS를 관리하는 쪽으로 넘기세요 — 제약은 당신 애플리케이션이 아니라 거기 리졸버나 이그레스 규칙입니다.

관련 도구

관련 가이드

가이드 공유

[Ad] Guide Detail Inline
← 전체 가이드 보기