EAI_AGAIN은 getaddrinfo가 이름을 잠깐 풀지 못할 때 노드가 내는 일시 실패입니다. 레지스트리 주소부터 dns.lookup으로 확인하고, 한 번 재시도한 뒤에도 남으면 리졸버와 VPN을 봅니다. 레코드가 없는 오류, 연결 거절, 체크섬 불일치와 줄을 가릅니다. npm, DNS, 도커, 한국 1인 개발. 2026년 9월 노드 문서.
EAI_AGAIN은 이름 서버가 잠깐 답을 못 해서 getaddrinfo가 다시 시도하라는 코드를 돌려준 상태입니다. 호스트 철자가 틀린 줄과 자리가 다릅니다.
카페 망이나 회사 VPN, 도커 안의 127.0.0.11이 호스트 리졸버를 못 따라가면 npm install이 이 코드로 멈춥니다. 같은 명령을 1분 뒤에 하면 풀리는 경우가 많습니다. 포트에 붙은 프로세스를 죽이거나 패키지 폴더를 지우는 순서는 이 줄에 안 맞습니다.
레지스트리 호스트를 노드로 한 번 더 풀어 보고, 호스트는 되는데 컨테이너만 실패하면 리졸버를 맞추세요. 노드 공통 오류 표는 이 코드를 일시적인 이름 해석 실패로 적습니다. 표는 Node.js 오류 문서에 있습니다.
EAI_AGAIN은 다시 하라는 뜻이다
리눅스 getaddrinfo에서 EAI_AGAIN은 이름 서버가 일시 실패를 알렸다는 뜻입니다. 나중에 다시 부르라는 신호예요.
노드로 오면 error.code가 EAI_AGAIN이고 syscall은 getaddrinfo입니다. 호스트 이름은 hostname 칸에 그대로 있습니다. 그 문자열이 레지스트리인지, 디비 호스트인지, 사내 깃인지부터 읽으세요.
한 번 실패했다고 그 도메인이 없는 것은 아닙니다. 해석기, 망, VPN이 답을 못 받은 순간입니다. 계속 같은 코드면 리졸버 설정이 그 순간이 아니라 경로 자체입니다.
로그에서 먼저 읽을 칸 | code, syscall, hostname 세 칸입니다. syscall이 connect면 이름은 이미 풀린 뒤의 이야기입니다.
getaddrinfo가 다시 하라 하면, 포트 점유보다 리졸버를 먼저 본다
이름 없음, 거절, 시간 초과와 가르기
설치가 멈춘 로그만 보면 다섯 코드가 한 문장처럼 보입니다. syscall이 갈라 줍니다.
코드
syscall
의미
먼저
EAI_AGAIN
getaddrinfo
일시 실패, 다시 시도
리졸버, 한 번 재시도
ENOTFOUND
getaddrinfo
그 이름의 레코드가 없음
호스트 철자
ECONNREFUSED
connect
이름은 풀렸고 포트가 거절
서비스와 포트
ETIMEDOUT
connect
이름은 풀렸는데 응답이 없음
경로, 방화벽
EADDRINUSE
listen
내 프로세스가 포트를 못 염
점유 PID
ENOTFOUND는 이름을 물어봤더니 없다는 답입니다. 철자와 레지스트리 URL을 고칩니다. EAI_AGAIN은 물어본 질문이 아직 답을 못 받은 상태입니다. 포트가 이미 쓰인 줄은 EADDRINUSE 글이고, listen이라 getaddrinfo와 syscall이 다릅니다.
같은 호스트를 한 번 더 풀어 보기
npm이 멈췄으면 레지스트리 호스트만 따로 풀어 보세요. 기본 공개 레지스트리는 registry.npmjs.org입니다.
레지스트리 호스트만 해석
npm config get registry
node -e "require('dns').lookup('registry.npmjs.org', (e, a) => { console.log(e && e.code, a) })"
맥은 scutil --dns로 지금 쓰는 이름 서버를 봅니다. 리눅스 호스트는 getent hosts registry.npmjs.org로 시스템 해석과 노드 해석을 나란히 둡니다. 시스템만 되고 노드만 EAI_AGAIN이면 노드 프로세스의 망 네임스페이스가 다릅니다. 컨테이너 안인 경우가 많습니다.
두 번째 lookup이 주소로 끝나면, 설치 명령만 한 번 더 실행합니다. 세 번 연속 같은 코드면 캐시를 비우는 단계로 가지 말고 리졸버를 고정하세요. 패키지 버전 목록 문제는 ETARGET 글이고, 파일을 받은 뒤 해시가 틀린 문제는 EINTEGRITY 글입니다. 둘 다 이름이 풀린 다음의 줄입니다.
[ ] 오류의 hostname을 적었다
[ ] npm config get registry와 그 호스트가 같은지 봤다
[ ] 노드 lookup을 두 번 호출해 일시 실패인지 봤다
[ ] 시스템 해석과 노드 해석이 갈리는지 봤다
호스트는 풀리고 컨테이너만 실패하면 리졸버가 다른 것이다
도커 안만 실패할 때
컴포즈 네트워크의 컨테이너는 보통 127.0.0.11 내장 해석기를 봅니다. 그 해석기가 호스트 DNS에 질문을 전달하다 답을 못 받으면 앱은 EAI_AGAIN을 받습니다. 노트북 터미널의 lookup은 성공인데 docker compose exec 안만 실패하면 이 경로입니다.
회사 내부 레지스트리를 쓸 때 공개 해석기 1.1.1.1로 바꾸면 사설 이름이 더 안 풀립니다. 먼저 호스트가 쓰는 이름 서버를 컨테이너에 그대로 넘기세요. 공개 레지스트리만 쓰는데 호스트 해석기가 불안정할 때만 아래처럼 공개 해석기를 서비스에 한정합니다.
그 서비스만 이름 서버를 고정
services:
app:
dns:
- 1.1.1.1
- 8.8.8.8
이 블록은 그 서비스의 해석만 바꿉니다. 사내 깃 호스트가 이 앱과 같은 컨테이너에 있으면 공개 해석기 한 줄이 사내 이름까지 막습니다. 그때는 호스트와 같은 사내 해석기 주소를 적습니다. 도커 문서의 dns 키는 이 재정의용입니다.
재시도를 코드 어디에 둘지
설치 명령의 EAI_AGAIN은 명령 한 번 재실행으로 충분한 경우가 많습니다. 앱이 외부 API 호스트를 부팅 때마다 풀어야 하면, 그 호출만 짧게 두 번 다시 부릅니다. 간격을 200밀리초, 600밀리초처럼 벌리세요.
ENOTFOUND까지 같은 재시도에 넣으면 없는 이름을 몇 초 동안 두드립니다. 코드가 EAI_AGAIN일 때만 다시 부르세요. connect 단계의 ETIMEDOUT은 이름은 풀린 뒤라, DNS 재시도와 타임아웃 상한은 다른 칸입니다.
상황
동작
하지 말 일
한 번만 EAI_AGAIN
같은 명령을 한 번 더
node_modules 삭제
호스트는 되고 컨테이너만
그 서비스의 dns를 호스트와 맞춤
사내 이름에 공개 해석기만
세 번 연속
리졸버, VPN, 레지스트리 URL
락파일 폐기
ENOTFOUND로 바뀜
호스트 철자
같은 재시도 루프에 방치
한국 카페 망에서 공개 npm이 저녁마다 이 코드를 내면, 사내 레지스트리 미러가 있을 때는 npm config get registry가 그 미러를 가리키는지 확인하세요. 미러 호스트의 해석 실패와 공개 레지스트리의 해석 실패는 고치는 주소가 다릅니다.
일시 실패만 두 번 다시 부르기
const dns = require('dns').promises
async function lookupWithRetry(host) {
const waits = [0, 200, 600]
let last
for (const ms of waits) {
if (ms) await new Promise((r) => setTimeout(r, ms))
try {
return await dns.lookup(host)
} catch (err) {
last = err
if (err.code !== 'EAI_AGAIN') throw err
}
}
throw last
}
이 코드는 패키지 캐시 이전이 이름 해석입니다. npm cache clean은 해시나 메타 칸이고, getaddrinfo가 실패하면 캐시에 도착하기 전입니다. lookup 결과부터 보세요.
노트북은 되는데 컴포즈 안만 실패합니다.
컨테이너 해석기가 호스트와 다른 답을 받는 경로입니다. 그 서비스의 dns를 호스트가 쓰는 이름 서버와 맞추세요. 사내 이름이면 공개 해석기만 넣지 마세요.
ENOTFOUND와 어떻게 다릅니까?
ENOTFOUND는 그 이름에 레코드가 없다는 답입니다. EAI_AGAIN은 일시 실패라 다시 물으라는 답입니다. 철자를 고치기 전에 lookup을 한 번 더 해보세요.
레지스트리를 공개 주소로 바꿔도 같으면요?
바뀐 호스트의 lookup도 실패하면 레지스트리 상품 문제가 아니라 나가는 해석 경로입니다. VPN을 끊거나 다른 망에서 같은 lookup만 비교하세요.
앱 코드에서 몇 번까지 다시 부르나요?
부팅 때 필요한 호스트는 두세 번, 간격은 200밀리초와 600밀리초 정도가 충분합니다. ENOTFOUND와 ETIMEDOUT은 그 루프 밖으로 보내세요.
디비 호스트에서도 같은 코드가 납니다.
hostname 칸의 디비 주소를 lookup 하세요. 풀리면 53300이나 비밀번호 칸으로 넘어갑니다. 해석이 실패하는 동안에는 슬롯을 세도 서버에 도착하지 않습니다.
EAI_AGAIN은 이름 해석이 잠깐 실패한 코드입니다. hostname을 두 번 풀어 보고, 컨테이너만 실패하면 그 서비스의 리졸버를 맞추세요. 철자가 틀린 줄, 해시가 틀린 줄, 버전이 없는 줄은 이름이 풀린 다음에 갑니다. 관련 글: ETARGET, EINTEGRITY, EADDRINUSE.
회사 망에서는 내부 이름과 공개 이름이 다른 풀이 서버를 봅니다. 공개 해석기로 통째로 바꾸면 사내 패키지 주소가 하루 종일 안 풀릴 수 있습니다. 한 번 다시 묻는 것은 잠깐의 실패에 맞고, 같은 실패가 이어지면 해석기, 가상 사설망, 저장소 호스트를 차례로 봅니다. 받아 둔 파일을 지우거나 포트를 끄거나 잠금 파일을 지우는 일은 이름이 풀린 다음에 합니다. 서명 검증을 끄는 설정은 이 일시 실패의 해결이 아닙니다. 컨테이너만 실패하면 그 서비스에 적힌 해석기부터 보고, 호스트에서 풀리는 이름인지 먼저 적어둡니다. 다시 묻는 함수는 이 일시 코드일 때만 기다리고, 다른 코드는 그대로 올려 보내야 로그가 숨지 않습니다. 설치 주소가 잠깐 안 풀리는 동안 버전 목록을 의심하면 없는 배포를 고치려다 시간을 씁니다. 이름이 풀린 뒤에야 포트, 해시, 버전을 의심하세요. 같은 창에서 세 가지를 한꺼번에 고치면 어느 수정이 살렸는지 남지 않습니다. 일시 실패가 사라진 다음의 설치 로그를 따로 보관해 두면 다음 아침의 비교가 빨라집니다. 해석이 돌아온 시각과 설치가 다시 시작된 시각을 같이 적으면, 다음 실패가 같은 일시 코드인지 바로 가릅니다. 사내 이름만 실패하면 공개 해석기로 바꾸지 말고 그 이름 서버의 상태를 먼저 확인하세요.