3000 포트가 이미 사용 중일 때, proc로 실행 주체 찾기

3000 포트를 쓰는 프로세스를 찾았는데 이름이 node 하나뿐이다. 터미널 여러 개에 프로젝트를 띄워 둔 상태라면, 어느 서버를 꺼야 할지 어떻게 알아낼까?

3000 포트가 이미 사용 중일 때, proc로 실행 주체 찾기
세 줄 요약
  • proc on :3000은 포트 점유자를 찾고, proc why :3000은 이를 실행한 부모 프로세스까지 추적한다.
  • macOS·Linux에서 stop은 종료 요청 후 기다리다가 강제 종료할 수 있다. kill은 즉시 강제 종료하며, free는 포트 해제 여부도 검사한다.
  • 여러 Node 프로젝트가 떠 있다면 이름만으로 종료하지 말고 경로와 대상을 확인한다. --dry-run은 실제 종료 없이 대상만 보여 준다.

포트 점유자 다음에는 실행한 부모를 본다

개발 서버를 켰는데 EADDRINUSE나 ‘Port 3000 is already in use’가 나오면, 같은 포트를 누가 쓰고 있는지부터 찾아야 한다. proc CLI는 포트·프로세스 번호·이름·경로로 대상을 찾는 도구다. macOS·Linux·Windows를 지원한다. 아래 종료 신호 설명은 macOS와 Linux 기준이다.

Homebrew를 쓰는 맥에서는 공식 안내의 brew install yazeed/proc/proc로 설치할 수 있다. 설치한 뒤에는 읽기 전용 명령 두 개로 시작한다.

proc on :3000
proc why :3000

on은 포트를 사용하는 프로세스를 찾는다. why는 그 프로세스가 어떤 부모를 거쳐 실행됐는지 보여 준다. 공식 명령 설명에 따르면 각 단계에 실행 시간도 붙으므로, 오래전에 열어 둔 tmux 세션 같은 부모를 알아보는 데 도움이 된다.

프로세스 이름이 node라는 사실만으로 지금 작업 중인 서버인지 판단하기는 어렵다. 실행 경로와 부모를 함께 보면 다른 프로젝트의 서버인지, 다른 터미널에서 남겨 둔 작업인지 좁힐 수 있다. 두 조회 명령은 프로세스를 종료하지 않는다.

콜론이 붙으면 포트, 숫자만 쓰면 PID다

proc에서 :3000은 3000번 포트이고 3000은 프로세스 ID 3000이다. 콜론 하나가 대상의 의미를 바꾼다. node처럼 이름을 주면 해당 이름의 프로세스들이 대상이 되므로 여러 프로젝트를 동시에 실행한 상태에서는 범위를 더 좁혀야 한다.

proc by node --in .
proc stop node --in . --dry-run

첫 명령은 현재 작업 디렉터리로 범위를 제한해 Node 프로세스를 찾는다. 두 번째는 같은 범위에서 종료할 대상을 미리 보여 준다. 실제로 종료하려면 출력에 나타난 프로세스와 경로를 확인한 뒤 --dry-run을 뺀 명령을 실행한다.

--in은 작업 디렉터리 필터다. 프로세스의 권한을 제한하는 격리 기능은 아니다. 디렉터리 조건에 맞지 않는 프로세스는 빠질 수 있으므로, 포트 조회 결과와 경로를 함께 보고 대상을 정하는 것이 좋다.

stop도 시간이 지나면 강제 종료한다

세 명령은 모두 실행 중인 프로그램에 영향을 줄 수 있지만, 기다리는 방식과 마지막 확인이 다르다. 특히 stop을 ‘강제 종료를 절대 하지 않는 명령’으로 이해하면 안 된다.

명령종료 방식뒤이어 하는 확인
proc stop :3000SIGTERM으로 종료를 요청하고, 기본 10초를 넘기면 강제 종료한다.대상 프로세스의 종료를 기다린다.
proc kill :3000SIGKILL로 즉시 강제 종료한다.포트 해제 검사까지 맡는 명령은 아니다.
proc free :3000먼저 종료를 요청하고, 기다린 뒤 남아 있으면 강제 종료한다.해당 포트가 비었는지 반복 검사한다.

stop의 대기 시간은 --timeout으로 바꾼다. 예를 들어 proc stop :3000 --timeout 20은 종료 요청 뒤 기다리는 시간을 20초로 지정한다. 시간이 충분하면 프로그램이 정리 작업을 마칠 기회를 얻지만, 그 안에 끝나지 않으면 강제 종료 단계로 넘어간다.

free의 구현은 종료 신호를 보낸 뒤 남은 프로세스를 정리하고 포트 점유 여부를 다시 검사한다. 포트만 대상으로 받으며 --wait으로 대기 시간을 지정할 수 있다. 이름이 ‘free’라고 해서 정상 종료만 시도하는 것은 아니다.

일반 대화형 실행에는 확인 절차가 있고, --dry-run으로 변경 없이 대상을 볼 수 있다. 포트를 비우기 전에는 proc free :3000 --dry-run처럼 확인하는 편이 적절하다. 미리 보기 뒤 다른 프로세스가 포트를 차지할 수도 있으므로 출력은 그 시점의 상태로 읽어야 한다.

다시 실행되는 서버는 부모 프로그램에서 멈춰야 할 수 있다

종료 직후 같은 포트가 다시 차면 proc why :3000으로 현재 점유자의 부모를 다시 본다. 서버를 관리하는 프로그램이 자식을 재실행하는 구성이라면, 자식 프로세스만 반복해서 종료해도 문제가 돌아올 수 있다. 이 경우에는 해당 개발 도구나 서비스 관리자의 중지 기능으로 관리 대상 자체를 멈춰야 한다.

free가 성공해도 미래의 재점유까지 막아 주지는 않는다. 그 명령이 검사하는 것은 종료 후 포트가 비었는지다. ‘이 포트를 다시 열지 말라’는 예약이나 차단 기능과는 다르다.

스크립트나 AI 에이전트에서 읽을 때는 조회 결과를 JSON으로 받을 수 있다.

proc on :3000 --json
proc why :3000 --json

종료 코드는 0이 성공, 1이 오류, 2가 대상 미발견, 3이 권한 거부, 4가 잘못된 입력이다. 한편 변경 명령에 붙이는 --json은 단순한 표시 옵션으로만 취급하면 안 된다. 현재 free 구현은 JSON 모드에서 대화형 확인을 건너뛴다. 자동화에서는 조회·미리 보기와 실제 종료를 분리하고, 포트 점유자의 경로와 실행 주체를 확인한 뒤 변경해야 한다.