포트와 localhost
💡 학습 가이드:
npm run dev를 실행하면 터미널에http://localhost:5173이 나옵니다.localhost가 무엇인지,5173은 무엇을 의미하는지 생각해 본 적이 있나요? 왜 가끔EADDRINUSE오류가 발생할까요? 이 장에서는 일상 개발에서 매일 보지만 깊이 파고들지 않았던 개념들을 한 번에 정리합니다.
시작하기 전에 두 가지 "기본 벽돌"을 먼저 보충하는 것을 권장합니다:
- 네트워크 기초: IP 주소와 HTTP의 개념이 잘 이해되지 않는다면 컴퓨터 기초 - 네트워크 통신을 먼저 보세요.
- 터미널 기초: 터미널 명령줄에 익숙하지 않다면 명령어와 Shell 스크립트를 먼저 보세요.
0. 서론: 매일 보는 localhost:5173은 대체 무엇인가?
$ npm run dev
> vite
准备就绪...GET /index.html),然后把对应的文件内容返回给你。모든 개발자의 일상에는 이 출력이 빠지지 않습니다:
➜ Local: http://localhost:5173/하지만 이 짧은 한 줄에 몇 가지 핵심 개념이 숨겨져 있습니다:
- http:// → 통신 프로토콜 (어떤 언어로 대화할 것인가)
- localhost → 목적 주소 (누구를 찾을 것인가)
- :5173 → 포트 번호 (찾은 후 어느 문을 두드릴 것인가)
이 세 가지를 이해하면 개발 환경 네트워크 문제의 90%를 이해할 수 있습니다. 이제 하나씩 분석해 봅시다.
1. 포트란 무엇인가? (IP는 건물, 포트는 호실 번호)
1.1 직관적 비유
서버를 한 건물이라고 상상해 보세요:
- IP 주소(예:
192.168.1.100)는 건물의 주소 — "어느 건물로 갈 것인가"를 알려줍니다. - 포트 번호(예:
:80)는 건물 내 호실 번호 — "어느 방으로 들어갈 것인가"를 알려줍니다.
한 건물에는 레스토랑(80호), 카페(443호), 사무실(22호)이 동시에 있을 수 있습니다. 마찬가지로 한 컴퓨터에서 웹 서버, 데이터베이스, SSH 서비스가 각각 다른 포트를 차지하며 동시에 실행될 수 있습니다.
👇 직접 해보기: 아래의 "방 문패"를 클릭하여 다른 포트에 연결을 시도해 보세요. 포트가 "열려 있을 때"(프로그램이 수신 중)와 "닫혀 있을 때" 각각 어떤 일이 발생하는지 관찰하세요.
1.2 포트 번호의 범위
포트 번호는 0–65535 사이의 정수입니다 (총 65536개). 이 많은 포트는 세 가지 구간으로 나뉩니다:
| 구간 | 범위 | 용도 | 예시 |
|---|---|---|---|
| 시스템 포트 | 0 – 1023 | 표준 프로토콜용 예약, 일반 사용자가 임의로 사용 불가 | 80 (HTTP), 443 (HTTPS), 22 (SSH) |
| 등록 포트 | 1024 – 49151 | 일반적인 애플리케이션 등록용 | 3306 (MySQL), 5432 (PostgreSQL), 6379 (Redis) |
| 동적 포트 | 49152 – 65535 | 운영 체제가 임시 할당 | 브라우저가 요청을 보낼 때 시스템이 임의의 소스 포트 할당 |
개발 서버가 3000, 5173, 8080을 좋아하는 이유는? 이들이 모두 "등록 포트" 범위에 있어 관리자 권한 없이도 수신 가능하고, 시스템 서비스와 충돌하기 어렵기 때문입니다.
1.3 개발 중 자주 나오는 포트 번호 빠른 참조
👇 직접 해보기: 포트 번호나 서비스명을 검색하고, 아무 행이나 클릭하면 사용 예시가 펼쳐집니다.
80HTTP网页访问(未加密)安全443HTTPS网页访问(加密)安全22SSH安全远程登录注意21FTP文件传输敏感3306MySQLMySQL 数据库敏感5432PostgreSQLPostgreSQL 数据库敏感27017MongoDBMongoDB 数据库敏感6379RedisRedis 缓存敏感3000Node/ReactNode.js / React 开发服务器安全5173ViteVite 开发服务器安全8080通用 HTTPHTTP 备用端口 / 代理安全8000Django/PythonDjango / Python HTTP 服务安全5000FlaskFlask 开发服务器安全4200AngularAngular 开发服务器安全53DNS域名解析注意25SMTP邮件发送注意2. localhost란 무엇인가? (스스로를 찾기)
2.1 "루프백"의 핵심 개념
localhost는 특수한 도메인 이름으로, 항상 자신의 컴퓨터를 가리킵니다.
브라우저에 http://localhost:3000을 입력하면 다음이 발생합니다:
- 브라우저가 운영 체제에 묻습니다: "
localhost의 IP가 무엇인가요?" - 운영 체제가 바로 답합니다: "
127.0.0.1" (DNS 조회 없음) - 데이터 패킷이
127.0.0.1로 발송되지만, 실제로 기기를 떠나지 않습니다 - 운영 체제가 "루프백 인터페이스(loopback interface)"를 통해 데이터 패킷을 되돌려 보냅니다
- 3000 포트에서 수신 중인 프로그램이 요청을 받아 응답을 반환합니다
전체 과정이 네트워크 케이블, 라우터, 인터넷 연결을 거치지 않습니다.
👇 직접 해보기: "요청 보내기"를 클릭하여 데이터 패킷의 전체 여정을 관찰하세요. 그런 다음 아래의 "가면 카드"를 클릭하여 localhost의 다양한 표기법과 차이를 알아보세요.
localhost→ 127.0.0.1127.0.0.1→ 127.0.0.1::1→ ::10.0.0.0→ 0.0.0.0/etc/hosts 文件里的映射。浏览器看到 localhost 时,直接解析为 127.0.0.1,不会去问 DNS 服务器。 2.2 localhost vs 127.0.0.1 vs 0.0.0.0
이 세 가지 개념은 자주 혼동되지만 의미가 완전히 다릅니다:
| 표기 | 의미 | 누가 접근할 수 있는가 |
|---|---|---|
localhost / 127.0.0.1 | 루프백 주소, 자기 자신만 | 본인 컴퓨터만 |
0.0.0.0 | 모든 네트워크 인터페이스 수신 | 본인 + LAN 내 다른 기기 |
192.168.x.x | LAN IP | LAN 내 기기 |
실제 시나리오:
# 자신만 접근 가능 (안전, 개발에 적합)
npm run dev -- --host localhost
# 휴대폰도 접근 가능 (모바일 디버깅에 적합)
npm run dev -- --host 0.0.0.0많은 프레임워크(Vite, Next.js 등)는 기본적으로
localhost를 수신하므로, 같은 WiFi에 연결된 휴대폰으로도 접근할 수 없습니다. 휴대폰으로 디버깅하고 싶다면--host매개변수를 추가하세요.
3. 포트 충돌: 가장 흔한 개발 환경 문제
3.1 왜 충돌이 발생하나요?
하나의 포트를 동시에 하나의 프로그램만 수신할 수 있습니다. 한 방에 한 가구만 살 수 있는 것과 같습니다.
같은 포트에서 두 번째 서비스를 시작하려고 하면 이 전형적인 오류를 보게 됩니다:
Error: listen EADDRINUSE :::3000사람의 말로 번역하면: "3000호에 이미 누가 살고 있어서 들어갈 수 없습니다!"
흔한 충돌 시나리오:
- 이전 개발 서버가 완전히 종료되지 않고 백그라운드에서 계속 실행 중
- 서로 다른 두 프로젝트가 같은 기본 포트 사용
- 어떤 시스템 서비스가 이미 원하는 포트를 차지
👇 직접 해보기: 아래 시뮬레이터에서 서비스를 여러 번 시작해 보세요. 포트 충돌 시 "직접 시작"과 "스마트 시작"의 다른 처리 방식을 비교해 보세요.
:5173🟢 RunningEADDRINUSE 错误,说明这个端口已经被占了。要么杀掉旧进程,要么换个端口。 3.2 문제 해결 및 해결
포트 충돌을 겪었을 때 해결 절차는 매우 일정합니다:
macOS / Linux:
# 첫 번째: 누가 3000 포트를 차지하고 있는지 확인
lsof -i :3000
# 두 번째: PID를 확인한 후 강제 종료
kill -9 <PID>Windows:
# 첫 번째: 누가 3000 포트를 차지하고 있는지 확인
netstat -ano | findstr :3000
# 두 번째: 프로세스 종료
taskkill /PID <PID> /F많은 현대 프레임워크(Vite, Create React App 등)는 포트 충돌 시 "다른 포트로 변경할까요?"라고 자동으로 묻습니다. 하지만 기본 원리를 이해하면 프레임워크가 도와주지 못하는 까다로운 문제를 더 빠르게 해결할 수 있습니다.
4. 개발 중 "동일 출처 정책"과 CORS
4.1 "출처"란 무엇인가?
브라우저에는 동일 출처 정책(Same-Origin Policy)이라는 보안 메커니즘이 있습니다: 프로토콜, 도메인, 포트 세 가지가 모두 동일해야 "동일 출처"로 인정됩니다.
| 주소 A | 주소 B | 동일 출처 여부 | 이유 |
|---|---|---|---|
http://localhost:5173 | http://localhost:5173/about | ✅ 동일 출처 | 프로토콜, 도메인, 포트 모두 같음 |
http://localhost:5173 | http://localhost:3000 | ❌ 다른 출처 | 포트 다름 (5173 vs 3000) |
http://localhost:5173 | https://localhost:5173 | ❌ 다른 출처 | 프로토콜 다름 (http vs https) |
4.2 왜 프론트/백엔드 분리는 반드시 CORS를 만나게 되나요?
프로젝트 아키텍처가 다음과 같을 때:
프론트엔드 (Vite) → http://localhost:5173
백엔드 (Express) → http://localhost:3000프론트엔드 페이지가 :5173에서 로드된 후 fetch('/api/users')로 :3000의 API를 요청하면 — 포트가 달라 크로스 오리진 제한이 발동합니다!
두 가지 일반적인 해결책:
해결책 1: 백엔드에 CORS 설정
// Express 백엔드
app.use(cors({ origin: 'http://localhost:5173' }))해결책 2: 프론트엔드에 프록시 설정 (권장)
// vite.config.js
export default {
server: {
proxy: {
'/api': 'http://localhost:3000'
}
}
}프록시의 원리: Vite 개발 서버가 대신 요청을 "전달"해 줍니다. 브라우저는 :5173과 통신한다고 생각하지만(동일 출처), 실제로는 Vite가 배후에서 :3000에 요청을 전달합니다.
5. 실전 문제 해결: 세 가지 가장 흔한 문제
👇 직접 해보기: 겪어본 문제를 선택하고 단계에 따라 함께 해결해 보세요. 각 단계에서 "실행"을 클릭하면 출력을 확인할 수 있습니다.
lsof -i :30006. 용어 대조표
| 영문 용어 | 한국어 대조 | 설명 |
|---|---|---|
| Port | 포트 | 0–65535 사이의 숫자로, 같은 기기에서 서로 다른 네트워크 서비스를 구분. 각 서비스가 하나의 포트를 "수신"하며 클라이언트 연결을 대기 |
| localhost | 로컬 호스트 | 특수한 도메인 이름으로, 항상 자기 자신(127.0.0.1)을 가리킴. 네트워크 연결 없이 자기 기기에서 실행 중인 서비스에 접근할 때 사용 |
| Loopback Interface | 루프백 인터페이스 | 운영 체제의 가상 네트워크 인터페이스. 127.0.0.1로 향하는 데이터 패킷은 기기를 떠나지 않고 이 인터페이스를 통해 "되돌아"옴 |
| EADDRINUSE | 주소 이미 사용 중 | Node.js / 운영 체제가 보고하는 오류로, 수신하려는 포트가 이미 다른 프로그램에 의해 사용되고 있음을 나타냄 |
| CORS | 크로스 오리진 리소스 공유 | 브라우저 보안 메커니즘. 프론트엔드 페이지가 다른 출처(프로토콜/도메인/포트가 다름)의 API를 요청할 때 백엔드의 명시적 허가가 필요 |
| Same-Origin Policy | 동일 출처 정책 | 브라우저 보안의 초석: 같은 프로토콜, 같은 도메인, 같은 포트의 요청만 자유롭게 통신하도록 허용 |
| Proxy | 프록시 | 개발 환경에서 프록시 서버가 브라우저를 대신해 백엔드에 요청을 전달하여 브라우저의 동일 출처 제한을 우회 |
| 0.0.0.0 | 모든 인터페이스 | 서비스가 0.0.0.0을 수신할 때, 모든 네트워크 인터페이스(자기 자신, LAN 등)의 연결을 수락함을 의미 |
| Well-known Ports | 잘 알려진 포트 | 0–1023 포트의 총칭, HTTP (80), HTTPS (443), SSH (22) 등 표준 프로토콜용으로 예약됨 |
| PID | 프로세스 ID | 운영 체제가 각 실행 중인 프로그램에 할당하는 고유 번호, 프로세스 관리 및 종료에 사용 |
| lsof | 열린 파일 나열 | macOS/Linux 명령어, 특정 포트를 차지한 프로세스를 확인하는 데 사용 (lsof -i :포트번호) |
| HMR | 핫 모듈 교체 | 개발 서버의 기능: 코드 수정 후 브라우저가 자동으로 업데이트되어 수동 새로고침 불필요.底层에서 WebSocket을 통해 브라우저에 알림 |
요약
포트와 localhost는 개발 환경에서 가장 기본적이고 빈번하게 사용되는 개념입니다:
- 포트 = 한 기기에서 서로 다른 서비스를 구분하는 "호실 번호" (0–65535)
- localhost = "스스로를 찾는" 특수 주소 (127.0.0.1), 데이터가 기기를 떠나지 않음
- 포트 충돌의 본질은 "하나의 호실에 하나의 간판만 걸 수 있음"
- CORS의 본질은 "포트가 다르면 = 출처가 다름", CORS 또는 프록시로 해결 필요
이 네 문장을 기억하면 개발 환경에서 만나는 대부분의 네트워크 문제를 빠르게 원인을 파악할 수 있습니다.