맥에서 포트 3000이 이미 사용 중일 때: 프로세스 찾아서 종료하기
2026년 10월 13일 업데이트
맥에서 포트 3000을 비우려면 lsof -nP -iTCP:3000 -sTCP:LISTEN으로 어떤 프로세스가 리스닝 중인지 확인하고, kill <PID>로 그 프로세스를 종료하면 돼요. kill -9는 일반 시그널을 무시할 때만 쓰세요. 범인은 대개 예전 세션에서 켜 둔 채 잊어버린 개발 서버(Node, Vite, Next.js, Rails, Python)예요.
명령어를 입력하는 대신 리스닝 중인 포트를 목록으로 보고 싶다면, 무료 메뉴 막대 앱 WTMemory가 포트와 그 포트를 쓰는 프로세스를 함께 보여 줘요. 자세한 내용은 글 마지막에 있어요. 아래 터미널 방법은 모든 맥에서 동작해요.
"EADDRINUSE: address already in use :::3000"은 무슨 뜻인가요?
하나의 TCP 포트와 주소에서는 한 번에 하나의 프로세스만 리스닝할 수 있어요. Node가 Error: listen EADDRINUSE: address already in use :::3000을 출력하거나 Rails가 Address already in use - bind(2) for "127.0.0.1" port 3000이라고 하면, 이미 누군가 그 포트를 잡고 있어서 운영체제가 바인딩을 거부한 거예요. 새로 띄운 서버에는 문제가 없어요. 예전 리스너를 찾아 종료하거나 다른 포트로 시작하면 돼요.
맥에서 포트 3000을 쓰는 프로세스는 어떻게 찾나요?
macOS에는 네트워크 소켓까지 보여 주는 lsof(list open files)가 기본으로 들어 있어요.
lsof -nP -iTCP:3000 -sTCP:LISTEN
출력 예시:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 48213 you 23u IPv6 0x... 0t0 TCP *:3000 (LISTEN)
-iTCP:3000은 포트 3000의 TCP 소켓만 보여 줘요.-sTCP:LISTEN은 접속한 클라이언트는 빼고 리스닝 소켓만 보여 줘요.-n과-P는 DNS와 포트 이름 조회를 건너뛰어서 lsof가 훨씬 빨라져요.
종료할 대상은 PID 열이에요. COMMAND는 9글자에서 잘리기 때문에 프로젝트 경로를 포함한 전체 명령줄을 보려면 ps -p 48213 -o pid,ppid,etime,command를 실행하세요.
맥에서 리스닝 중인 TCP 포트 전체를 한 번에 보려면:
lsof -nP -iTCP -sTCP:LISTEN
lsof는 내가 조회할 권한이 있는 프로세스만 보여 줘요. 포트가 사용 중인데 lsof에 아무것도 안 나온다면 리스너가 다른 사용자나 root 소유라는 뜻이니 sudo를 붙여 다시 실행하세요.
포트 3000을 쓰는 프로세스는 어떻게 종료하나요?
kill 48213
lsof 명령을 다시 실행해 보세요. 아무것도 안 나오면 포트가 빈 거예요. 한 줄로 쓰면 이렇게요.
lsof -ti tcp:3000 -sTCP:LISTEN | xargs kill
-t는 PID만 출력하고, xargs가 이를 kill에 넘겨요. macOS의 BSD xargs는 입력이 없으면 명령을 실행하지 않기 때문에 포트가 이미 비어 있어도 문제없어요.
-sTCP:LISTEN 필터는 꼭 남겨 두세요. 흔히 돌아다니는 lsof -ti :3000 | xargs kill은 포트 3000에 연결된 모든 클라이언트까지 잡아내는데, 보통 localhost:3000을 열어 둔 브라우저 탭도 여기에 포함돼요. 필터 없이 실행하면 서버와 함께 Chrome이나 Safari까지 종료될 수 있어요.
kill과 kill -9, 뭘 써야 하나요?
일반 kill은 정중하게 종료를 요청하는 SIGTERM을 보내요. Node, Vite, Next.js, Rails 등 대부분의 서버는 이를 받아 소켓을 닫고 로그를 flush하고 자식 프로세스를 멈춰요. kill -9는 커널이 즉시 강제하는 SIGKILL을 보내요. 프로세스가 정리할 기회를 얻지 못하므로:
- 자식 프로세스(워커, 감시 도구, 번들러)가 고아 프로세스로 남아 다른 포트를 계속 잡고 있을 수 있어요.
- 락 파일, PID 파일, 임시 파일이 남고, 이걸 지우기 전까지 시작을 거부하는 도구도 있어요.
- PostgreSQL이나 MySQL 같은 데이터베이스는 다음 시작 때 크래시 복구를 거쳐야 해요. 이런 건
brew services stop postgresql@16처럼 전용 도구로 멈추세요.
kill을 보내고 2~3초 기다린 뒤 lsof로 확인하고, 그래도 안 될 때만 kill -9로 넘어가세요.
터미널을 닫았는데 서버가 왜 계속 돌고 있나요?
터미널 창을 닫으면 보통 셸과 그 작업들에 hangup 신호가 가지만, 이를 버티고 살아남는 경우가 많아요.
- Ctrl+C 대신 Ctrl+Z를 누른 경우. Ctrl+Z는 서버를 일시 정지할 뿐이고, 정지된 프로세스도 포트는 계속 잡고 있어요.
jobs를 실행한 다음fg로 불러와 Ctrl+C를 누르세요. - 고아가 된 자식 프로세스.
npm run dev는 셸을 띄우고, 그 셸이node를 띄워요. 부모가 갑자기 죽으면 자식은launchd(PID 1)에 입양되어 계속 서비스할 수 있어요. - 에디터 터미널과 태스크. VS Code, Cursor, JetBrains IDE는 창이 닫히거나 크래시가 나도 통합 터미널과 백그라운드 태스크를 살려 둘 수 있어요.
- tmux, screen,
nohup, 또는&와disown. 애초에 터미널보다 오래 살아남도록 만든 도구예요. - 프로세스 관리자.
brew services(launchd), pm2, nodemon, Docker 재시작 정책은 종료된 프로세스를 새 PID로 바로 되살려요.
남아 있는 개발 서버를 이름으로 찾으려면?
오래된 서버가 여러 개라면 부모 PID와 실행 시간까지 함께 이름으로 나열해 보세요.
ps -axo pid,ppid,etime,command | grep -E 'node|vite|next|webpack|rails|python' | grep -v grep
PPID가 1이면 고아가 되어 launchd에 입양된 프로세스예요. ETIME이 크다면(며칠, 또는 열린 터미널이 없는데 몇 시간) 그것도 힌트예요. pgrep -fl vite를 쓰면 더 짧은 목록이 나와요. pkill -f "next dev"로 패턴에 맞는 프로세스를 종료할 수도 있지만, -f는 전체 명령줄 어디든 매칭하므로 먼저 pgrep -fl "next dev"로 무엇이 걸리는지 확인하세요.
Docker가 포트를 잡고 있다면?
lsof에 Docker 프로세스(예: com.docker.backend)가 보인다면 컨테이너가 그 포트를 publish하고 있는 거예요. Docker 프로세스를 종료하면 모든 컨테이너가 멈추니 그러지 마세요. 해당 컨테이너를 찾아서 멈추세요.
docker ps --filter "publish=3000"
docker stop <container>
맥에서 자주 쓰는 개발용 포트
| 포트 | 주로 쓰는 곳 |
|---|---|
| 3000 | Next.js, Create React App, Nuxt, Rails, 다수의 Express 앱 |
| 4200 | Angular CLI |
| 5000, 7000 | AirPlay 수신 모드(ControlCenter), Flask 기본값 |
| 5173, 4173 | Vite 개발 서버, Vite preview |
| 8000 | Django, python3 -m http.server |
| 8080 | Tomcat, Spring Boot, 각종 프록시 |
| 8888 | Jupyter |
| 5432, 3306, 6379, 27017 | PostgreSQL, MySQL, Redis, MongoDB |
| 11434 | Ollama |
어느 포트든 같은 lsof 명령에서 3000만 해당 포트 번호로 바꾸면 돼요.
macOS에서 포트 5000이나 7000이 사용 중인 이유는?
macOS Monterey부터 AirPlay 수신 모드가 포트 5000과 7000에서 리스닝하고, lsof에는 소유자가 ControlCenter로 표시돼요. 다른 건 아무것도 안 띄운 맥에서 새 Flask 앱이 "Address already in use"로 실패하는 이유가 바로 이거예요. ControlCenter는 macOS가 다시 실행하니 종료하지 마세요. 대신 다음 중 하나를 하세요.
- 시스템 설정 > 일반 > AirDrop 및 Handoff에서 AirPlay 수신 모드를 끄거나(Monterey: 시스템 환경설정 > 공유),
- 앱을 다른 포트로 실행하세요. 예:
flask run --port 5001
다음에 포트 충돌을 피하려면?
- 서버는 Ctrl+Z가 아니라 Ctrl+C로 멈추고, 터미널을 닫기 전에
jobs를 확인하세요. - 프로젝트 두 개를 동시에 띄워야 하면 다른 포트를 지정하세요:
PORT=3001 npm start,next dev -p 3001,vite --port 5174. - Vite는 자동으로 다음 빈 포트로 옮겨 가요. 오래된 서버를 알아챌 수 있도록 차라리 실패하길 원한다면
--strictPort를 넘기세요.
메뉴 막대에서 포트 사용 프로세스 찾기
WTMemory는 Apple Silicon과 Intel 맥의 macOS 12 이상에서 돌아가는 네이티브 메뉴 막대 앱이에요. 메뉴에서 Find What's Using a Port(포트 사용 중인 프로세스 찾기)를 고르면 TCP 리스닝 엔드포인트와 각각을 쓰는 프로세스를 볼 수 있고, Running Apps(실행 중인 앱)를 열면 개발 프로세스마다 PID, 실행 파일 위치, 메모리, CPU를 볼 수 있어요. 조회는 무료예요.
프로세스 종료는 Pro 기능이고, 실행 전에 직접 검토하고 확인해요. 리스너를 종료하면 포트 하나만이 아니라 프로세스 전체가 멈춰요. WTMemory는 프로세스에 '버려진 것'이나 '종료해도 안전함' 같은 딱지를 붙이지 않아요. 판단은 여러분이 해요. 잠금 컨트롤로 보호한 앱은 절대 종료되지 않아요. Pro는 $9 USD 일회성 결제로 맥 3대까지 쓸 수 있고, 7일 체험판이 있어요.
자주 묻는 질문
포트 3000을 쓰는 프로세스를 명령 하나로 종료하려면?
lsof -ti tcp:3000 -sTCP:LISTEN | xargs kill을 실행하세요. -sTCP:LISTEN 필터가 중요해요. 이게 없으면 lsof가 브라우저처럼 포트 3000에 접속한 클라이언트까지 함께 반환해서 그것들까지 종료하게 돼요.
kill -9는 쓰면 안 되나요?
최후의 수단이에요. SIGKILL은 프로세스를 즉시 끝내기 때문에 쓰기 내용을 디스크에 반영하거나 락 파일을 지우거나 자식 프로세스를 멈출 기회가 없어요. 먼저 일반 kill을 보내고 몇 초 기다려 보세요.
종료했는데 프로세스가 다시 살아나는 이유는?
뭔가가 다시 실행하고 있어서예요. launchd를 통한 Homebrew services, pm2, nodemon, 파일 감시 도구, 재시작 정책이 걸린 Docker 컨테이너 등이요. brew services stop이나 docker stop처럼 해당 도구로 멈추세요.
포트를 누가 쓰는지 보려면 sudo가 필요한가요?
다른 사용자나 root가 소유한 프로세스일 때만 필요해요. lsof에 아무것도 안 나오는데 포트가 여전히 사용 중이라면 같은 명령을 sudo로 실행하세요.
아무것도 안 띄웠는데 맥에서 포트 5000이 사용 중인 이유는?
macOS Monterey부터 AirPlay 수신 모드가 ControlCenter 프로세스를 통해 포트 5000과 7000에서 리스닝해요. 시스템 설정에서 AirPlay 수신 모드를 끄거나 앱을 다른 포트로 실행하세요.