도커 컴포즈로 로컬 환경 구성하시는 거, 정말 많은 개발자들이 겪는 초기 허들 중 하나예요.
저도 처음에는 '왜 저 포트가 안 열릴까?', '네트워크 드라이버가 뭘 하는 거지?' 하면서 엄청 헤맸던 기억이 납니다.
특히 DB랑 API 서버랑 엮을 때면 뭔가 마법처럼 돌아가야 할 것 같은 압박감에 더 막막하더라고요.
질문자님이 딱 핵심을 짚으셨어요.
네트워크 설정이 가장 까다로운 부분입니다.
'이거만은 꼭 점검해야 한다'는 핵심 체크포인트 위주로, 그리고 초보자가 가장 헷갈리는 부분 위주로 정리해서 말씀드릴게요.
너무 깊은 이론보다는, 일단 '돌아가게 만드는' 실전 팁 위주로 봐주시면 좋을 것 같습니다.
--- ###
1.
가장 먼저 확인해야 할 '기본 원칙' (가장 중요) 일단 가장 기본이 되는 원칙부터 잡으셔야 합니다.
이게 90%의 문제 해결의 시작점이에요.
1.
서비스 이름으로 통신하세요 (Host 대신 서비스명 사용) 이게 제일 많이 실수하는 부분입니다.
예를 들어, docker-compose.yml 파일에 db라는 서비스를 띄우고, API 서버가 이 DB에 접속해야 한다고 가정해 볼게요.
절대 호스트 머신의 IP 주소나 localhost를 사용하시면 안 됩니다.
왜냐하면, 컨테이너 내부에서 localhost는 '자기 자신'을 의미해요.
API 서버 컨테이너 입장에서 localhost는 API 서버 자신이고, DB 컨테이너는 완전히 다른 네트워크 공간에 존재하거든요.
올바른 방법: 서비스 이름(Service Name)을 이용한 내부 DNS 이름으로 접근해야 합니다.
docker-compose.yml 파일에서 서비스 이름을 지정했다면, 해당 이름이 곧 컨테이너 간의 호스트 이름(Hostname)이 됩니다.
예시: API 서버 코드에서 DB 연결 주소를 http://db:5432/ 처럼 작성해야 합니다.
(여기서 db는 docker-compose.yml의 서비스 이름, 5432는 DB 포트) 2.
포트 매핑은 '외부'와 '내부'를 분리해서 생각하세요. 이게 헷갈리는 지점이에요.
ports: 섹션 (Host -> Container): 이건 '외부'에서 컨테이너로 접속할 때만 필요합니다.
(예: 로컬 PC에서 브라우저로 접근) * ports: - "8080:80" 이면, 내 PC의 8080 포트로 들어온 트래픽을 컨테이너 내부의 80 포트로 보내준다는 의미입니다.
- 서비스 간 통신 (Container -> Container): 이건
ports: 설정과는 전혀 상관이 없습니다. * 서비스 A가 서비스 B에 접근할 때는, 포트 매핑을 건드리지 않아도 내부 네트워크 이름으로 통신이 가능합니다.
- DB가 5432 포트로 열려있다면, API 서버는 그냥
db:5432로 접근하면 됩니다.
실전 체크포인트: 만약 A가 B에 접근할 때 포트 번호만 지정하고 서비스 이름을 안 썼다면, 99% 확률로 'Connection Refused'나 'Host Not Found' 오류가 납니다.
--- ###
️ 2.
단계별 점검 리스트 (Troubleshooting Checklist) 문제가 생겼을 때 순서대로 체크해 보시면 시간 절약이 됩니다.
Step 1: docker-compose.yml 파일 구조 점검 * 모든 서비스가 정의되어 있는지?
(DB, API, Redis 등) * 각 서비스마다 networks: 부분이 명시적으로 연결되어 있는지?
(기본적으로 같은 네트워크에 묶이지만, 명시하는 게 안전합니다.) * 환경 변수(environment: 또는 .env 파일)에 비밀번호나 포트 같은 것이 누락되지 않았는지?
(가장 흔한 실수 중 하나입니다.
로컬 테스트라 해도 환경 변수는 필수입니다.) Step 2: 컨테이너 상태 확인 * docker-compose ps 를 실행해서 모든 서비스가 Up 상태인지 확인하세요.
- 만약 특정 서비스가
Exited 상태라면, docker-compose logs [서비스명] 으로 로그를 찍어봐야 합니다.
(DB가 시작 안 되는 경우, 권한 문제일 때가 많습니다.) Step 3: 네트워크 연결 테스트 (가장 확실한 방법) * ping 테스트: API 서버 컨테이너 내부로 들어가서 DB 서비스 이름으로 ping을 날려보세요.
docker exec -it [api_container_id] ping db * 이게 성공하면, 네트워크 이름 자체는 통하는 겁니다.
- Telnet/nc 테스트 (포트 확인): ping이 성공해도 포트가 막혀있을 수 있습니다.
docker exec -it [api_container_id] nc -zv db 5432 (nc는 netcat, 없다면 apk add iputils 같은 걸로 설치해야 할 수 있음) * 이게 성공하면, 해당 컨테이너에서 해당 포트로 접근할 수 있다는 의미입니다.
--- ###
3.
심화/추가 팁 및 주의사항 이론적인 부분이라 조금 복잡할 수 있지만, 알아두시면 나중에 정말 큰 도움이 됩니다.
A.
외부 접근과 내부 통신 분리 (매우 중요) 만약 나중에 프론트엔드 서버(Nginx나 React 같은 것)를 띄워서 로컬 브라우저에서 접근하게 만들 계획이라면, 반드시 프론트엔드 서비스만 ports:를 통해 외부 포트를 열어주셔야 합니다.
예: ```yaml services: frontend: build: ./frontend ports: - "3000:3000" # <-- 오직 이거만 외부 포트 지정 api: # ports: <-- 여기에 포트 지정하면 안 됨 (다른 서비스가 덮어쓸 수 있음) # ...
네트워크 드라이버 이해하기 (개념만 이해)** 도커 컴포즈가 기본적으로 `bridge` 네트워크를 사용합니다.
이 브릿지 네트워크 안에서, 서비스 이름 기반의 DNS 이름 해석 기능이 마법처럼 동작하는 겁니다.
그래서 그냥 '서비스 이름'으로 통신하는 것이 가장 안정적이고 간편합니다.
(만약 이걸 우회해서 IP 주소로 통신하려고 시도하면, 도커가 내부적으로 IP 주소를 할당/변경하기 때문에 계속 깨지게 됩니다.) **C.
캐시나 Redis 같은 Key-Value 스토어 사용 시 주의점** Redis 같은 경우에도 마찬가지로, `redis`라는 서비스 이름으로 접속하고 포트(기본 6379)를 명시하는 게 좋습니다.
만약 코드에서 Redis 연결 시 기본 포트를 오버라이딩하거나, 다른 이름의 포트를 쓰게 된다면, 해당 포트가 `docker-compose.yml`에서 정의된 서비스의 포트와 일치하는지 재확인해주세요.
--- ### 📝 요약하자면...
1.
**통신 주소 = 서비스 이름** (절대 `localhost`나 `127.0.0.1` 금지) 2.
**외부 접근 포트 = `ports:`** (이건 로컬 PC ↔ 컨테이너 간 통신) 3.
**내부 통신 포트 = 그냥 포트 번호만 사용** (서비스 이름:포트) 4.
**문제 발생 시 = `docker exec` + `ping` 또는 `nc`로 서비스 이름으로 테스트** 이 세 가지만 확실히 구분해서 접근하시면, 네트워크 때문에 막히는 경우는 90% 이상 해결될 겁니다.
시간 되실 때 위 체크리스트 순서대로 테스트해보시는 걸 추천드립니다.
궁금한 거 있으면 언제든지 다시 물어봐 주세요!