EADDRINUSE, 포트 이미 사용 중, 프로세스 종료 | 3000번이 안 죽으면 뭐부터 볼까?
EADDRINUSE address already in use는 코드가 틀린 게 아니라 쓰려는 포트를 이미 다른 프로세스가 쥐고 있어서 서버가 바인딩을 못 하는 것이다. lsof -i :3000·netstat -ano로 PID를 찾고, kill·kill -9·taskkill /F·npx kill-port로 그 하나만 종료한다. nodemon·pm2·도커가 자꾸 되살리는 경우, TIME_WAIT, PORT 환경변수와 next dev -p로 포트 옮기기, Ctrl+C 정상 종료 예방까지. 개발자, Next.js, 노드, 백엔드, 터미널 기준. 2026년 8월 Node.js·마이크로소프트 문서.
EADDRINUSE는 코드가 틀린 게 아니라, 쓰려는 포트를 이미 다른 프로세스가 붙잡고 있어서 서버가 그 자리에 못 앉는 상황입니다. 그러니 코드를 고칠 게 아니라 포트를 쥔 프로세스를 찾아 정리하는 게 먼저예요.
개발 서버를 껐다가 다시 켰을 뿐인데 Error: listen EADDRINUSE: address already in use :::3000이 뜨는 일, 한 번쯤 겪으셨을 겁니다. 터미널을 강제로 닫았거나, 서버가 깔끔히 안 죽었거나, 다른 창에서 같은 포트를 이미 쓰고 있을 때 흔히 나오죠.
아래에서는 이 에러가 왜 나는지, 어떤 프로세스가 포트를 잡고 있는지 찾는 법, 맥·리눅스와 윈도우에서 그 프로세스를 종료하는 명령, 죽여도 자꾸 되살아날 때 볼 자리, 그리고 Next.js 같은 개발 서버에서 실전 순서까지 짚어 봅니다. 로컬에서 혼자 여러 프로젝트를 띄워 두고 개발하시는 분이라면 자주 만나는 상황이죠. 같은 빨간 줄 앞에서 뭐부터 눌러야 할지 막막하셨다면 도움이 되실 거예요. 근거는 Node.js 공식 문서와 마이크로소프트 taskkill 문서에 뒀습니다.
EADDRINUSE는 왜 뜨나
EADDRINUSE는 "주소가 이미 사용 중"이라는 뜻입니다. 서버가 특정 포트에서 요청을 받으려고 listen을 부르는 순간, 운영체제가 그 포트는 이미 다른 프로세스에 배정돼 있다며 거절한 것이죠. Node.js 문서도 이 에러를 서버가 이미 점유된 주소에 바인딩하려 할 때 나는 시스템 에러로 설명합니다.
가장 흔한 경우는 같은 서버가 두 번 켜진 상황입니다. 이전에 띄운 개발 서버가 완전히 안 죽었는데 새로 npm run dev를 돌리면, 앞 프로세스가 여전히 3000번을 쥐고 있어서 뒤 프로세스가 자리를 못 잡습니다. 터미널을 강제로 닫거나 노트북이 절전에서 깨어난 뒤에 자주 생기죠.
덜 흔하지만 헷갈리는 경우도 있습니다. 도커 컨테이너나 다른 도구가 몰래 같은 포트를 열어 두었거나, 앞서 죽인 프로세스가 소켓을 TIME_WAIT 상태로 잠깐 붙들고 있는 경우예요. 그래서 "분명히 껐는데 왜 안 되지" 싶을 때는 코드가 아니라 그 포트를 누가 쥐고 있는지부터 확인해야 합니다.
먼저 읽을 것 | 에러 메시지에는 항상 막힌 포트 번호가 :::3000처럼 함께 적혀 있습니다. :::는 IPv6, 0.0.0.0:3000은 IPv4 표기예요. 어떤 포트가 문제인지 그 숫자부터 확인하면 다음 단계가 빨라집니다.
포트를 이미 쥔 프로세스가 있으면 새 서버는 그 자리에 못 앉는다
어떤 프로세스가 포트를 잡고 있는지 찾기
포트를 쥔 프로세스를 먼저 찾아야 안전하게 종료할 수 있습니다. 무턱대고 노드 프로세스를 전부 죽이면 다른 프로젝트 서버까지 함께 꺼질 수 있으니, 문제의 포트 하나만 겨냥하는 편이 낫습니다.
맥과 리눅스에서는 lsof가 가장 빠릅니다. lsof -i :3000을 치면 그 포트를 여는 프로세스의 이름과 PID(프로세스 번호)가 나옵니다. PID만 바로 뽑고 싶다면 lsof -ti :3000을 쓰면 됩니다. 윈도우에서는 netstat -ano | findstr :3000으로 확인하는데, 줄 끝에 붙는 숫자가 PID입니다.
운영체제
확인 명령
보는 값
맥 · 리눅스
lsof -i :3000
프로세스 이름과 PID
맥 · 리눅스 (PID만)
lsof -ti :3000
PID 숫자만
윈도우
netstat -ano | findstr :3000
줄 끝 숫자가 PID
윈도우 (파워셸)
Get-NetTCPConnection -LocalPort 3000
OwningProcess가 PID
PID를 확인했다면 그게 정말 내 개발 서버가 맞는지 이름을 한 번 봐 두면 좋습니다. 엉뚱한 시스템 프로세스를 종료하면 다른 문제가 생길 수 있으니까요.
맥 · 리눅스에서 포트 3000 확인
# 포트 3000을 쓰는 프로세스 보기
lsof -i :3000
# 예시 출력
# COMMAND PID USER FD TYPE NODE NAME
# node 48213 lee 23u IPv6 TCP *:3000 (LISTEN)
# PID 숫자만 뽑기
lsof -ti :3000
포트를 쥔 프로세스를 종료하는 방법
PID를 알면 종료는 한 줄입니다. 맥·리눅스에서는 kill PID로 정상 종료를 먼저 시도하고, 그래도 안 죽으면 kill -9 PID로 강제 종료합니다. -9는 프로세스가 뒷정리할 틈 없이 곧바로 끊는 신호라, 웬만하면 먼저 일반 kill을 써 보는 편이 안전합니다.
한 줄로 찾고 죽이려면 kill $(lsof -ti :3000)처럼 묶어 씁니다. 윈도우에서는 netstat으로 PID를 확인한 뒤 taskkill /PID 2660 /F로 종료하는데, 마이크로소프트 문서 기준 /F는 강제 종료, /PID는 번호로 대상을 지정하는 옵션입니다.
명령을 외우기 번거롭다면 npx kill-port 3000 한 줄로 끝낼 수도 있습니다. npm의 kill-port 패키지는 안에서 결국 같은 lsof·kill(윈도우는 netstat·taskkill)을 대신 불러 주는 도구라, 운영체제를 안 가리고 같은 명령으로 포트를 비웁니다.
방법
명령
비고
맥·리눅스 정상 종료
kill PID
뒷정리 후 종료
맥·리눅스 강제 종료
kill -9 PID
즉시 끊음, 최후 수단
윈도우
taskkill /PID 2660 /F
관리자 권한 필요할 수 있음
운영체제 공통
npx kill-port 3000
패키지가 대신 처리
포트를 비우는 한 줄 명령
# 맥 · 리눅스 — 포트 3000을 쥔 프로세스 종료
kill $(lsof -ti :3000)
# 안 죽으면 강제 종료
kill -9 $(lsof -ti :3000)
# 윈도우 (cmd) — PID 확인 후 종료
# netstat -ano | findstr :3000
taskkill /PID 2660 /F
# 운영체제 공통 — 한 줄
npx kill-port 3000
PID를 겨냥해 하나만 죽이면 다른 프로젝트 서버는 건드리지 않는다
죽여도 자꾸 되살아날 때 볼 자리
분명히 종료했는데 포트가 여전히 막혀 있다면 몇 가지를 의심해 볼 수 있습니다. 첫째는 자동 재시작 도구입니다. nodemon이나 pm2, 도커 컴포즈의 restart 설정처럼 프로세스가 죽으면 곧바로 다시 띄우는 감시자가 있으면, 죽여도 새 프로세스가 같은 포트를 다시 잡습니다. 이때는 그 감시자부터 멈춰야 하죠.
둘째는 도커입니다. docker ps로 3000번을 매핑한 컨테이너가 떠 있다면 docker stop으로 컨테이너를 멈춰야 포트가 풀립니다. 호스트에서 kill을 해도 컨테이너 안 프로세스는 그대로일 수 있어서예요. 셋째는 방금 죽인 소켓이 잠깐 TIME_WAIT로 남는 경우인데, 이건 몇 초 기다리면 대개 풀립니다.
가장 빠른 우회는 포트를 바꿔 버리는 것입니다. 급할 때는 PORT=3001 npm run dev처럼 다른 번호로 띄우면 충돌 자체를 피할 수 있죠. 다만 이건 임시방편이라, 근본적으로는 원래 3000번을 쥔 프로세스를 정리하는 편이 깔끔합니다.
[ ] nodemon·pm2·도커 restart 같은 자동 재시작이 켜져 있는지 확인했다
[ ] docker ps로 같은 포트를 매핑한 컨테이너가 없는지 봤다
[ ] 방금 죽였으면 몇 초 기다린 뒤 다시 시도했다
[ ] 급하면 PORT 환경변수로 다른 포트에 띄웠다
[ ] 정확한 PID 하나만 겨냥했는지 이름으로 확인했다
node.exe 전체 종료는 신중히 | 윈도우에서 taskkill /F /IM node.exe는 모든 노드 프로세스를 한꺼번에 끕니다. 다른 프로젝트 서버나 편집기의 노드 프로세스까지 함께 죽으니, 포트 하나만 문제일 땐 PID를 지정해 종료하세요.
Next.js·개발 서버에서 실전 순서
에러가 떴을 때 순서를 정해 두면 헤매지 않습니다. 먼저 메시지에서 막힌 포트 번호를 읽고, lsof -i :3000(윈도우는 netstat)으로 누가 쥐고 있는지 확인합니다. 그게 내 이전 개발 서버가 맞으면 PID를 겨냥해 종료하고, 그다음 다시 서버를 띄우면 됩니다.
Next.js는 다행히 이 상황을 부드럽게 처리합니다. 기본 3000번이 막혀 있으면 자동으로 3001번처럼 빈 포트를 찾아 그쪽에 띄우고 안내해 주죠. 특정 포트를 고정하고 싶으면 next dev -p 3001이나 PORT=3001 next dev로 지정할 수 있습니다. 이 방식은 Next.js CLI 문서에 정리돼 있습니다.
반복해서 겪는다면 예방이 낫습니다. 개발 서버를 끌 때 터미널 창을 강제로 닫지 말고 Ctrl+C로 정상 종료하는 습관이 프로세스를 깔끔히 정리해 줍니다. 저처럼 한국에서 여러 사이트를 한 노트북으로 돌리는 1인 개발자라면 프로젝트마다 3000, 3001, 3002처럼 포트를 미리 나눠 두는 것도 충돌을 크게 줄여 줍니다. 환경변수나 셸 설정이 얽혀 헷갈릴 때는 direnv로 폴더 환경 나누는 글이나 워크트리로 프로젝트를 분리하는 글도 함께 보시면 됩니다.
아닙니다. 쓰려는 포트를 이미 다른 프로세스가 쥐고 있어서 나는 에러입니다. 코드보다 그 포트를 누가 붙잡고 있는지부터 확인해야 합니다. 대부분 이전에 띄운 서버가 완전히 안 죽은 경우예요.
어떤 프로세스가 포트를 쓰는지 어떻게 찾나요?
맥·리눅스는 lsof -i :3000, 윈도우는 netstat -ano | findstr :3000으로 확인합니다. 출력에 나오는 PID가 그 포트를 쥔 프로세스 번호이고, 이 번호로 종료 대상을 지정합니다.
kill과 kill -9는 뭐가 다른가요?
kill은 프로세스에 종료 신호를 보내 뒷정리 후 끝나게 하고, kill -9는 그 틈 없이 즉시 끊습니다. 먼저 일반 kill을 써 보고, 그래도 안 죽을 때만 -9를 쓰는 편이 안전합니다.
종료했는데도 포트가 계속 막혀요.
nodemon·pm2·도커 restart처럼 죽으면 다시 띄우는 감시자가 있는지 보세요. docker ps로 같은 포트를 매핑한 컨테이너도 확인해야 합니다. 방금 죽인 소켓이 잠깐 남는 경우면 몇 초 기다리면 풀립니다.
npx kill-port는 안전한가요?
지정한 포트를 쥔 프로세스만 종료하므로 taskkill /IM node.exe처럼 전부 죽이는 방식보다 안전합니다. 내부적으로 lsof·kill이나 netstat·taskkill을 대신 부르는 도구라, 운영체제를 안 가리고 같은 명령으로 씁니다.
그냥 다른 포트로 띄우면 안 되나요?
급할 때는 PORT=3001 npm run dev나 next dev -p 3001로 충돌을 피할 수 있습니다. 다만 원래 포트를 쥔 프로세스는 그대로 남으니, 반복되면 근본적으로 그 프로세스를 정리하는 편이 깔끔합니다.
EADDRINUSE는 결국 쓰려는 포트를 누가 이미 쥐고 있는지의 문제입니다. 메시지에서 포트 번호를 읽고, lsof나 netstat으로 PID를 찾아 그 하나만 종료하면 대부분 풀립니다. 자동 재시작이나 도커가 얽혔는지 살피고, 급할 땐 포트를 옮기면 되죠. 관련 글: direnv 폴더 환경, 깃 워크트리, npm ERESOLVE.
EADDRINUSE포트 사용 중프로세스 종료lsofnetstatkilltaskkillkill-portNext.js노드개발자터미널