PC 브라우저에서는 웹페이지가 열리는데, 다른 Docker 컨테이너에서 같은 주소를 호출하면 연결이 거부될 때가 있습니다. 이때 먼저 확인할 것은 “요청을 보내는 프로그램이 어디에서 실행되는가”입니다. 호스트의 브라우저와 컨테이너 안의 프로그램은 localhost를 서로 다른 대상으로 해석합니다.
이번 글은 Docker 헬스체크와 시작 순서의 후속편입니다. 단일 Docker 호스트에서 Compose의 기본 bridge 네트워크를 사용하는 상황을 기준으로, 주소와 포트의 역할을 정리합니다. host 네트워크나 여러 서버에 걸친 네트워크는 설명 범위에서 제외합니다.
1. localhost는 요청을 보내는 곳을 기준으로 봅니다
호스트 PC에서 127.0.0.1에 접속하면 그 PC의 로컬 환경을 향합니다. 반면 일반적인 bridge 네트워크의 컨테이너 안에서 127.0.0.1을 사용하면 그 컨테이너 자신을 향합니다. 옆 컨테이너의 웹 서버나 DB를 자동으로 가리키지 않습니다.
가상 사례로 web과 client라는 두 서비스가 있다고 하겠습니다. client에서 localhost:80을 요청하면 client 내부의 80번 포트를 찾습니다. web의 80번 포트에 도달하려면 두 서비스가 공유하는 네트워크에서 web이라는 이름을 사용할 수 있는지 확인해야 합니다.
Compose는 기본 네트워크를 만들고 같은 네트워크의 서비스를 이름으로 찾도록 구성합니다. 컨테이너 간 통신에는 서비스 이름과 컨테이너 포트를 사용합니다. 이 동작과 컨테이너 교체 시의 연결 처리는 Docker Compose 네트워크 공식 문서에 설명되어 있습니다.

2. 8080:80에서 어느 포트를 써야 할까?
| 요청 위치 | 예시 주소 | 의미 |
| 호스트의 브라우저 | 127.0.0.1:8080 | 호스트에 연결한 포트를 사용 |
| 같은 네트워크의 client | web:80 | 서비스 이름과 내부 포트를 사용 |
| client 내부의 자기 자신 | 127.0.0.1:80 | client 자신의 80번 포트를 사용 |
포트 매핑의 왼쪽은 호스트 포트, 오른쪽은 컨테이너 포트입니다. web이 80번에서 요청을 받는 상태라면 호스트 포트를 8080에서 18080으로 바꿔도 내부 통신 주소는 web:80입니다. 같은 네트워크의 통신을 위해 모든 서비스의 포트를 호스트에 공개할 필요는 없습니다.
다음은 로컬에서 경로를 비교할 수 있는 작은 구성 예시입니다. 새 빈 폴더의 compose.yaml로 구분해서 살펴보세요. web은 nginx 기본 페이지를 제공하고, client는 요청 도구를 실행할 수 있도록 대기하는 역할입니다. 이 글에서는 실제 네트워크 실행 결과를 측정하지 않았습니다.
services:
web:
image: nginx:stable-alpine
ports:
- "127.0.0.1:8080:80"
client:
image: alpine:3.23
command: ["sleep", "infinity"]
127.0.0.1을 앞에 둔 것은 로컬 확인 용도를 표현하기 위해서입니다. 호스트 주소를 생략하면 기본적으로 모든 호스트 주소에 포트가 연결될 수 있습니다. 공개 범위와 Docker 버전에 따른 주의점은 공식 포트 매핑 안내를 확인하세요. 특히 28.0.0 이전 버전의 localhost 공개에는 같은 L2 네트워크에서 접근 가능한 예외가 문서에 명시되어 있습니다.
3. 같은 요청을 두 위치에서 비교하기
위 예시를 별도 로컬 시험 환경에서 시작한 뒤, 아래 두 경로를 비교할 수 있습니다. 첫 명령은 해당 폴더의 서비스들을 실제로 시작합니다. 호스트의 8080번이 이미 사용 중이면 다른 빈 포트로 바꿔야 합니다.
docker compose up -d
# 호스트에서 웹 서비스 확인
curl http://127.0.0.1:8080
# client 컨테이너에서 웹 서비스 확인
docker compose exec client wget -qO- http://web:80
# 실제 호스트 포트 매핑 확인
docker compose port web 80
여기서 기대하는 것은 두 요청이 같은 nginx 페이지에 도달하는 것입니다. 시작 직후 실패한다면 web이 준비됐는지 확인한 뒤 다시 비교합니다. 이름을 찾을 수 있다는 것과 서비스가 이미 요청을 처리할 준비를 마쳤다는 것은 다른 조건입니다.
또한 브라우저에서 실행되는 JavaScript가 web:80을 호출하는 경우는 위 client 사례와 다릅니다. JavaScript의 요청 출발점은 사용자의 브라우저이므로, 사용자에게 도달 가능한 공개 주소나 같은 출처의 API 경로가 필요합니다. 서버 내부에서 통하는 이름을 프런트엔드 설정에 그대로 넣지 않도록 구분해야 합니다.
4. 이름이 맞는데도 연결되지 않는다면
- 두 서비스가 실제로 같은 네트워크에 연결되어 있는지 확인합니다. 서로 다른 Compose 프로젝트는 기본 네트워크도 다를 수 있습니다.
- 접속 대상이 사용하는 내부 포트를 확인합니다. 호스트 포트를 내부 주소에 잘못 붙이지 않았는지 봅니다.
- 서버 프로그램이 컨테이너의 loopback에만 바인딩되어 있지 않은지 확인합니다. 다른 컨테이너가 접근하려면 적절한 네트워크 인터페이스에서 요청을 받아야 합니다.
- 프로세스의 준비 상태와 로그를 확인합니다. 이름 해석 실패, 연결 거부, HTTP 오류 응답을 구분해서 적습니다.
예를 들어 HTTP 404를 받았다면 적어도 어떤 HTTP 서버에서는 응답이 온 것입니다. 이름을 찾지 못하는 오류와 같은 방법으로 접근하기보다 요청 경로와 응답한 서버를 확인합니다. 연결 문제를 넓게 묶지 않고 증상별로 나누면 불필요한 포트 추가를 줄일 수 있습니다.
5. 주소를 바꾸기 전에 남길 메모
연결을 설명할 때는 “client 컨테이너 → web:80”, “호스트 브라우저 → 127.0.0.1:8080”처럼 출발점과 도착점을 한 줄로 적어 보세요. 여기에 사용한 Compose 프로젝트, 서비스 이름, 오류 문구를 붙이면 다음 확인 위치가 명확해집니다.
컨테이너 IP를 직접 적어 임시로 해결하기보다 서비스 이름을 기준으로 연결을 구성하고, 교체 후 재접속도 확인하는 편이 좋습니다. 애플리케이션의 연결 복구는 네트워크 이름을 정하는 작업과 함께 검토할 항목입니다.
자료 확인일: 2026년 9월 8일. 공식 문서와 가상 로컬 구성에 기반한 설명입니다. 예시의 네트워크 실행과 성능 측정은 수행하지 않았습니다.
'소프트웨어개발' 카테고리의 다른 글
| Docker Compose .env와 env_file 차이: 환경변수가 적용되지 않을 때 확인할 것 (0) | 2026.09.08 |
|---|---|
| Docker 컨테이너는 실행 중인데 서비스가 안 된다면? 헬스체크와 시작 순서 (0) | 2026.09.07 |
| CCTV 관제 화면에 로그인하면 영상도 보호될까? 접근 권한의 구조 (0) | 2026.09.07 |