파이썬에서 Address already in use가 나오면 먼저 해당 포트에 실제로 LISTEN 중인 소켓이 있는지 확인해야 합니다. 리스너가 있으면 중복 실행이나 다른 프로세스 점유를 먼저 정리하고, 리스너가 없으면 이전 연결의 TIME_WAIT과 주소 계열, 종료 방식 등을 구분해 본 뒤 포트 변경이나 재사용 옵션을 선택합니다. 무작정 프로세스를 종료하거나 SO_REUSEADDR을 켜는 것이 공통 해결책은 아닙니다.
이 오류가 의미하는 것
서버 소켓이 bind()로 IP 주소와 포트를 연결하려 할 때 이미 사용할 수 없는 주소라면 바인딩이 실패합니다. Python 공식 문서에는 OSError: [Errno 98] Address already in use가 바인딩 실패의 예로 나옵니다. Windows에서는 같은 계열의 Winsock 오류 코드가 10048로 표시됩니다.
다만 메시지만으로 원인을 하나로 단정할 수는 없습니다. 현재 실행 중인 서버가 포트를 듣고 있을 수도 있고, 이전 실행이 완전히 정리되지 않았을 수도 있으며, 방금 닫힌 TCP 연결의 TIME_WAIT이 재바인딩을 방해할 수도 있습니다.
1. 포트를 실제로 듣고 있는지 먼저 확인하기
첫 단계는 프로세스를 죽이는 것이 아니라 리스너를 확인하는 것입니다. Linux에서는 다음처럼 해당 포트의 LISTEN 소켓만 조회할 수 있습니다.
ss -ltnp 'sport = :5000'
예시 포트는 5000입니다. 실제로 바인딩하려던 포트로 바꿔 확인하세요. 권한이나 환경에 따라 프로세스 정보가 보이지 않을 수 있습니다. 다른 조회 방법으로 lsof를 사용해 TCP 리스너를 확인할 수도 있습니다.
lsof -nP -iTCP:5000 -sTCP:LISTEN
출력에 PID와 프로그램이 보이면 포트를 점유한 프로세스가 있는 것입니다. 내가 띄운 이전 서버인지, 다른 서비스인지부터 확인해야 합니다. 다른 서비스라면 이름만 비슷하다는 이유로 종료하지 말고 실행 목적과 PID를 확인한 뒤 포트를 바꾸는 편이 안전합니다.
위 명령은 Linux 기준입니다. 운영체제별 포트 조회 도구와 출력 형식은 다르므로 사용하는 환경에 맞춰 확인하세요.
2. 리스너가 있다면 중복 실행부터 의심하기
자동화 콜백 서버나 개발 서버를 다시 실행할 때 흔한 경우는 이전 프로세스가 아직 살아 있는 상태에서 같은 명령을 한 번 더 실행하는 것입니다. 이때 확인할 순서는 다음과 같습니다.
- 리스너의 PID와 실행 명령을 확인합니다.
- 그 프로세스가 현재 실행해야 하는 서버인지, 별도 서비스인지 구분합니다.
- 내 프로세스라면 애플리케이션의 정상 종료 절차로 닫습니다.
- 정상 종료 후에도 남아 있을 때만 프로세스 상태와 로그를 확인해 강제 종료를 검토합니다.
- 다시 실행하기 전에 포트가 비었는지 재확인합니다.
강제 종료부터 하면 원인을 가릴 수 있고, 다른 자동화나 개발 도구의 연결을 끊을 수 있습니다. 특히 공용 개발 머신이나 여러 작업이 함께 도는 환경에서는 PID만 보고 종료하지 않는 것이 좋습니다.
3. LISTEN이 없으면 TIME_WAIT과 주소 조건을 구분하기
LISTEN 소켓이 보이지 않는다고 해서 포트가 언제나 즉시 재사용되는 것은 아닙니다. ss는 TCP 상태를 listening과 time-wait처럼 구분해 보여 주며, -o 옵션으로 TIME_WAIT 타이머 정보를 확인할 수 있습니다.
ss -tan -o state time-wait 'sport = :5000'
TIME_WAIT은 이전 TCP 연결을 정리하는 과정의 상태입니다. 이 상태가 보이는 것만으로 바인딩 실패의 원인이 확인된 것은 아닙니다. 바인딩하려는 로컬 주소와 포트가 해당 소켓과 관련 있는지 확인한 뒤 기다리거나 서버 소켓의 재사용 정책을 검토합니다. 반대로 LISTEN이 확인되면 단순히 기다릴 문제가 아니라 현재 프로세스와 중복 실행 여부를 먼저 판단해야 합니다.
또한 서버가 127.0.0.1에 바인딩되었는지, 0.0.0.0에 바인딩되었는지처럼 IP 주소와 주소 계열이 어떻게 설정되었는지도 확인해야 합니다. IPv4와 IPv6 설정이 섞인 경우에는 같은 포트 숫자만 보고 충돌 여부를 단정하기 어렵습니다.
4. SO_REUSEADDR은 언제 검토할까
Python 공식 socket 문서에서 SO_REUSEADDR은 TIME_WAIT 상태의 로컬 소켓을 자연 만료 전에 재사용하는 용도의 옵션으로 설명됩니다. 따라서 서버를 정상적으로 종료한 뒤 개발 환경에서 같은 주소를 빠르게 다시 바인딩해야 하는 상황에는 검토할 수 있습니다.
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("127.0.0.1", 5000))
server.listen()
# 요청을 처리하는 코드
이 옵션은 이미 실행 중인 리스너나 잘못된 중복 실행을 해결하는 만능 스위치가 아닙니다. 운영체제별 동작 차이와 주소 공유 정책도 있으므로, 포트 점유 프로세스를 확인하지 않은 채 추가하는 것은 피해야 합니다. 특히 Windows에서는 공유 바인딩과 관련된 옵션의 동작을 별도로 확인해야 합니다.
소켓은 명시적으로 close()하거나 with 문으로 관리하는 것이 좋습니다. 코드에서 예외가 발생해도 정리되도록 수명 범위를 분명히 해 두면 다음 실행에서 원인을 좁히기 쉬워집니다.
상황별로 어떤 조치를 선택할까
LISTEN과 PID가 확인되고 내 프로세스가 중복 실행 중이면: 중복 인스턴스를 정상 종료하고 한 번만 다시 실행합니다.LISTEN과 PID가 확인되지만 다른 서비스라면: 그 서비스를 끊지 말고 애플리케이션 포트를 바꿀지 운영 구성을 조정할지 결정합니다.LISTEN은 없고 관련된TIME_WAIT이 보이면: 연결 정리 시간을 고려하고, 필요한 경우SO_REUSEADDR의 범위와 운영체제 동작을 확인합니다.- 아무 상태도 맞지 않으면: 바인딩 주소, IPv4·IPv6 설정, 실행 중인 별도 인스턴스와 실제 로그를 다시 확인합니다.
핵심은 오류 문구를 보고 바로 kill이나 포트 변경을 실행하는 것이 아니라, LISTEN인지 TIME_WAIT인지와 그 소켓을 만든 주체를 분리하는 데 있습니다. 이 순서를 지키면 자동화 콜백 서버를 재시작할 때 다른 서비스까지 끊는 실수를 줄이고, 재사용 옵션이 필요한 경우에도 적용 범위를 좁힐 수 있습니다.
출처
- Python
socket공식 문서 —bind(),SO_REUSEADDR, 소켓 종료와TIME_WAIT관련 설명 - Microsoft Learn Winsock 오류 코드 — Windows 오류 코드 10048
- Linux
ss(8)문서 — 리스너·TCP 상태·TIME_WAIT 타이머 조회 - Linux
lsof(8)문서 — 프로세스와 네트워크 소켓 조회