TechFeedTechFeed
Backend

ENOTFOUND, getaddrinfo, DNS | 호스트를 못 찾겠다고 나오면?

ENOTFOUND는 코드가 틀린 게 아니라 그 호스트 이름을 DNS가 IP로 못 바꿔 소켓이 시작도 못 한 줄입니다. 오타난 도메인, 도커 서비스 이름, 서버리스 localhost. Node.js, Docker, 한국 1인 넥스트 기준. 2026년 9월 Node.js 에러·DNS 문서.

by

ENOTFOUND는 코드가 틀린 게 아니라, 그 호스트 이름에 해당하는 IP를 DNS가 못 돌려줘서 소켓이 시작도 못 한 줄입니다. 서버 창은 켜져 있고 URL도 맞아 보이는데 fetch나 Prisma만 실패하면 대개 여기예요.


오타난 도메인, 꺼진 도커 서비스 이름, 배포 환경에만 없는 내부 호스트가 흔합니다. ECONNREFUSED는 거절할 프로세스가 있을 때고, ENOTFOUND는 이름부터 못 푼 단계라 포트부터 죽이면 안 됩니다.


콘솔에 찍힌 hostname을 그대로 복사해 nslookup부터 치세요. 카카오 콜백만 프리뷰 도메인으로 바꿨는데 API 호스트를 안 바꾼 경우도 같은 줄이 납니다. 근거는 Node.js 에러 문서DNS 모듈에 있습니다.


ENOTFOUND는 왜 포트 에러와 다른가

DNS가 그 이름을 IP로 못 바꿔서입니다. 노드는 연결 전에 getaddrinfo로 호스트를 해석하고, 결과가 없으면 EAI_AGAIN이나 ENOTFOUND를 던집니다. 포트에 누가 앉아 있는지는 아직 볼 차례가 아닙니다.


메시지에 getaddrinfo ENOTFOUND api.internal.local처럼 이름이 붙습니다. 그 문자열이 오타인지, 도커 네트워크에만 있는 별칭인지, 배포 환경 변수가 비었는지가 갈리는 지점이에요. IP를 숫자로 적었는데도 이 줄이 나면, 그 숫자에 오타가 있거나 IPv6만 살아 있는 경우가 많습니다.


연결 거부와는 자리가 다릅니다. 거절은 그 주소에 프로세스가 없어서 커넥션이 튕긴 줄이고, 여긴 주소 자체를 못 만든 줄입니다. 같은 빨간 줄로 보여도 고치는 칸이 달라요. 거절 쪽은 ECONNREFUSED 글에서 다룹니다.


먼저 읽을 것 | 에러 한 줄에 실패한 호스트 이름이 그대로 있습니다. 추측으로 localhost를 재시작하기 전에 그 문자열을 복사해 보세요. 이름이 틀리면 포트를 비워도 같은 줄이 반복됩니다.


DNS가 호스트 이름을 IP로 바꾸지 못해 ENOTFOUND가 나는 네트워크 개념 이미지
이름 해석이 실패하면 소켓은 시작도 못 한다

콘솔의 hostname부터 복사하는 이유

실패한 이름을 그대로 조회해야 원인이 갈립니다. 환경 변수에 공백이 붙거나 https://를 호스트 칸에 넣으면, 노드가 그 문자열 전체를 이름으로 봅니다.


콘솔에 찍힌 이름흔한 원인먼저 할 일
api.example.com오타, 만료된 DNS, 오타난 서브도메인nslookup으로 레코드 확인
db도커 컴포즈 서비스 이름, 네트워크 미연결같은 네트워크인지 compose 확인
localhost서버리스에 localhost를 그대로 배포환경별 호스트 분리
https://api.example.comURL 전체를 호스트로 넣음프로토콜·경로를 분리
(빈 문자열)환경 변수 미주입버셀·액션 secrets 이름

표를 보면 다섯 번째 칸이 의외로 잦습니다. 로컬 .env에는 값이 있는데 프리뷰 환경 변수 이름이 API_HOSTNEXT_PUBLIC_API_HOST로 갈라져 있으면, 서버 코드는 빈 문자열을 호스트로 넣습니다. 폴더별 환경이 헷갈리면 direnv 글을 같이 보세요.


실패한 호스트를 그대로 조회
node -e "console.log(JSON.stringify(process.env.API_HOST))" # 앞뒤 공백·따옴표가 있으면 그 문자 그대로 이름이 된다 nslookup api.example.com dig api.example.com A +short # 도커 안이라면 컨테이너에서 docker compose exec web getent hosts db

로컬·도커·배포에서 갈리는 원인

같은 코드라도 이름이 통하는 범위가 다릅니다. 노트북의 /etc/hosts에만 있는 별칭은 깃허브 액션에 없습니다. 도커 서비스 이름 db는 컴포즈 네트워크 안에서만 풀립니다.


버셀 같은 서버리스에 DATABASE_URL=postgresql://user:pass@localhost:5432/app을 그대로 올리면, 그 함수 안의 localhost는 개발자 맥이 아닙니다. 함수가 떠 있는 격리 환경의 loopback이라 이름이 있어도 내 DB가 아닙니다. 연결 거부가 아니라 ENOTFOUND가 나는 경우는, 호스트를 postgres처럼 로컬 도커 이름으로 둔 채 배포한 때예요.


카카오·토스 웹훅 URL을 프리뷰 도메인으로 바꿨다면 API 베이스도 같이 바뀌었는지 보세요. 프런트만 새 도메인데 서버가 옛 내부 별칭을 부르면 브라우저가 아니라 서버 로그에 ENOTFOUND가 찍힙니다. 시간 한도로 끊기는 줄과 섞이면 FUNCTION_INVOCATION_TIMEOUT 글과 자리를 나누세요.


로컬 hosts, 도커 네트워크, 배포 DNS가 같은 호스트 이름을 다르게 해석하는 흐름 이미지
이름이 통하는 범위가 환경마다 다르다

/etc/hosts와 DNS 캐시를 의심할 때

로컬만 실패하면 hosts 파일과 캐시부터 봅니다. 예전에 막힌 도메인을 hosts에 묶어 두었거나, 회사 VPN이 사내 DNS만 물려 둔 채 끊기면 공용 이름이 안 풀립니다.


맥은 sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder로 캐시를 비웁니다. 리눅스는 resolvectl flush-caches나 nscd 재시작입니다. 그래도 안 되면 dig @8.8.8.8 도메인으로 공용 DNS와 시스템 DNS를 비교하세요. 공용은 되고 시스템만 실패하면 해석기 설정 문제입니다.


도커 데스크톱은 VM 안의 resolv.conf를 씁니다. 호스트에서 nslookup이 되어도 컨테이너 안은 실패할 수 있어요. docker compose exec로 같은 조회를 한 번 더 해야 합니다. IPv6 AAAA만 있고 A가 없는 호스트도 환경에 따라 ENOTFOUND로 보입니다.


  • [ ] 에러에 찍힌 호스트 문자열을 그대로 복사했다
  • [ ] JSON.stringify로 환경 변수 공백·프로토콜을 확인했다
  • [ ] nslookup 또는 dig로 A 레코드를 봤다
  • [ ] 도커면 컨테이너 안에서 getent hosts를 쳤다
  • [ ] 배포 환경에 localhost·컴포즈 이름이 없는지 봤다

URL 전체를 호스트로 넣지 않기 | new URL(process.env.API_URL).hostname처럼 프로토콜을 분리하세요. https://api.example.com/v1을 호스트 칸에 그대로 넣으면 그 긴 문자열이 이름이 됩니다.


고친 뒤 다시 확인하는 순서

이름부터 고치고 연결은 그다음입니다. 호스트가 풀리면 그때서야 포트와 TLS를 봅니다. 순서를 뒤집으면 포트 킬만 반복하게 됩니다.


실무 순서는 짧습니다. 콘솔 이름 복사, 환경 변수 공백 확인, 로컬 DNS 조회, 컨테이너 안 조회, 배포 환경 변수 이름 대조. 다섯 칸이면 대부분 끝납니다. 모듈을 못 찾는 줄과 동시에 보이면 서버 파일이 기동 중에 외부 이름을 읽다 죽은 겁니다. 그 줄은 Cannot find module 글과 별개로 지으세요.


고친 뒤에는 같은 명령으로 한 번 더 조회해 레코드가 보이는지 확인합니다. TTL이 긴 레코드를 바꿨으면 몇 분 기다릴 수도 있어요. 한국에서 넥스트와 도커를 한 노트북으로 돌리면, 컴포즈 이름을 배포 URL에 그대로 옮기는 실수가 가장 잦습니다.


nslookup으로 호스트를 확인한 뒤 환경 변수를 고치는 실전 순서 이미지
이름이 풀린 뒤에야 포트와 TLS를 본다

참고 자료


내부 연계: 연결 거부, 서버리스 시간 한도, 폴더별 환경 변수


인용한 동작은 2026년 9월 공개 문서 기준입니다.


자주 묻는 질문

브라우저에선 열리는데 서버만 ENOTFOUND입니다.

브라우저와 노드가 쓰는 DNS가 다릅니다. 서버 코드의 호스트 문자열, 도커 resolv.conf, 배포 환경 변수를 보세요. 프런트가 공용 CDN을 부르고 서버가 내부 별칭을 부르면 한쪽만 실패합니다.


ECONNREFUSED와 무엇이 다른가요?

거절은 IP까지 가서 그 포트에 수신자가 없을 때입니다. ENOTFOUND는 IP를 만들기 전에 이름이 안 풀린 때입니다. 포트 킬은 거절 쪽에 가깝고, 여기선 이름과 환경 변수부터 고칩니다.


도커 서비스 이름 db가 안 풀립니다.

앱 컨테이너와 DB가 같은 컴포즈 네트워크에 있어야 합니다. 호스트 머신에서 db를 ping해도 소용 없고, 앱 컨테이너 안에서 getent hosts db를 치세요. 네트워크 이름이나 profiles로 서비스가 빠져 있는 경우가 많습니다.


배포만 실패하고 로컬 nslookup은 됩니다.

배포 환경 변수가 로컬 도커 이름이나 localhost를 담고 있을 가능성이 큽니다. 프리뷰 secrets 이름을 로컬 .env와 한 글자씩 대조하세요. 값이 비어 있으면 빈 문자열이 호스트가 됩니다.


IP를 숫자로 넣었는데도 납니다.

숫자 오타, 대괄호 없는 IPv6, 포트가 호스트에 붙은 경우가 있습니다. 192.168.0.1:5432를 호스트 칸에 넣으면 그 전체가 이름이 됩니다. 호스트와 포트는 분리하세요.


가끔만 나고 곧 됩니다.

일시 실패는 EAI_AGAIN에 가깝습니다. 업스트림 DNS 지연이나 재시작 직후입니다. 재시도는 가능하지만, 항상 같은 이름이면 오타·환경 변수 칸을 먼저 지우고 재시도를 얹으세요.


ENOTFOUND는 그 이름을 IP로 못 바꿨다는 뜻입니다. 콘솔의 hostname을 복사하고, 공백과 프로토콜을 뗀 다음, 로컬과 컨테이너와 배포에서 각각 조회하세요. 관련 글: 연결 거부, 서버리스 시간 한도, 모듈을 찾을 수 없음.


ENOTFOUNDgetaddrinfoDNS호스트도커fetch넥스트노드백엔드개발자

함께 보면 좋은 문제 해결

EXPLORE / Backend

이어서 읽어보기

전체 토픽 둘러보기