MMeshOps
Docker와 Docker Compose: 개발 환경을 하나로 통일하는 도구
14예상 12입문

실전 로컬 스택: Next.js + Go + DB + Redis 연결하기

실제 서비스 개발 환경과 동일하게 프론트엔드, 백엔드 API, 그리고 2개의 데이터베이스를 하나의 Docker Compose 파일로 조립합니다.

지금까지 배운 이미지, 컨테이너, 볼륨, 네트워크, Health Check 개념을 모두 합쳐서 실제 상용 서비스 구조와 유사한 로컬 개발 환경을 만들어 볼 차례입니다. 프론트엔드(Next.js), 백엔드 API(Go), 관계형 데이터베이스(PostgreSQL), 그리고 캐시 저장소(Redis)를 하나의 도커 컴포즈 파일로 매끄럽게 연결해 봅시다.

풀스택 아키텍처 밑그림 그리기

여러 컨테이너를 한 번에 띄우기 전에는 어떤 서비스들이 어떤 방식으로 통신할지 머릿속에, 혹은 종이 위에 그려보는 과정이 매우 중요합니다. 우리가 만들 개발 환경의 구조는 다음과 같습니다.

  1. 사용자 브라우저는 포트 매핑을 통해 Next.js 서버에 접속합니다.
  2. Next.js (프론트엔드)는 서버 사이드 작업이나 클라이언트 요청을 Go API 서버로 넘깁니다.
  3. Go API (백엔드)는 핵심 비즈니스 로직을 처리하며, 필요에 따라 DB와 Redis에 접근합니다.
  4. PostgreSQL (메인 DB)는 영구적인 사용자 데이터와 서비스 데이터를 저장합니다.
  5. Redis (캐시 DB)는 빠른 조회를 위한 임시 데이터나 세션 정보를 저장합니다.

서비스별 도커 환경 역할

Next.js
사용자 인터페이스 제공, 포트 3000번 개방
Go API
프론트엔드의 요청 처리, 포트 8080번 사용 (내부 통신 위주)
PostgreSQL
데이터 보존을 위한 볼륨(Volume) 마운트 필수
Redis
메모리 기반 빠른 캐싱 역할 수행

완성된 docker-compose.yml 분석

이제 모든 개념을 총망라한 `docker-compose.yml` 파일을 작성해 보겠습니다. 각 설정이 왜 들어갔는지 이전 에피소드의 기억을 더듬으며 살펴보세요.

최종 로컬 스택 docker-compose.yml

version: '3.8'

services:
  # 1. 프론트엔드 서비스
  web:
    image: node:18-alpine
    working_dir: /app
    volumes:
      - ./frontend:/app
    ports:
      - "3000:3000"
    command: npm run dev
    depends_on:
      api:
        condition: service_healthy

  # 2. 백엔드 서비스
  api:
    image: golang:1.21-alpine
    working_dir: /app
    volumes:
      - ./backend:/app
    ports:
      - "8080:8080"
    command: go run main.go
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3

  # 3. 메인 데이터베이스
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secretpassword
      POSTGRES_DB: myapp
    ports:
      - "5432:5432" # 로컬 DB 툴 접속용
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin -d myapp"]
      interval: 5s
      timeout: 5s
      retries: 5

  # 4. 캐시 데이터베이스
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:

아키텍처의 핵심 포인트 연결하기

작성된 코드를 바탕으로 주요 개념들이 어떻게 적용되었는지 확인해 보겠습니다.

1. 안전한 실행 순서 제어 `depends_on`과 `healthcheck`가 체인처럼 연결되어 있습니다. `db`가 준비(healthy)되어야 `api`가 실행을 시작합니다. `api`가 부팅되어 헬스체크용 엔드포인트(`/health`)가 응답할 준비(healthy)가 되어야 마침내 `web` 서비스가 시작됩니다. 이 체인 덕분에 프로세스 충돌 없는 완벽한 부팅이 보장됩니다.

2. 서비스 이름 기반 네트워크 통신 Go API 서비스의 `environment` 설정을 보면 `DB_HOST=db`, `REDIS_HOST=redis`로 환경 변수를 주입하고 있습니다. 내부 네트워크의 DNS가 작동하므로, 복잡한 IP 주소 대신 컨테이너 이름만으로 연결 설정을 끝냈습니다.

3. 데이터 영속성 (볼륨) 가장 하단에 `pgdata`라는 이름 있는 볼륨(Named Volume)을 선언했고, `db` 서비스 내부 경로인 `/var/lib/postgresql/data`에 마운트했습니다. 이제 컨테이너를 내리고(`down`) 다시 올려도 회원 정보나 게시글 데이터는 안전하게 유지됩니다.

4. 핫 리로딩을 위한 바인드 마운트 `web`과 `api` 서비스의 `volumes` 설정(`- ./frontend:/app`)은 로컬 컴퓨터의 코드를 컨테이너에 직통으로 연결합니다. 이제 로컬 에디터에서 코드를 수정하면, 컨테이너 내부에서 즉시 반영(Hot Reload)되어 개발 속도를 혁신적으로 높여줍니다.

로컬 스택 운영 규칙

  1. 1실행하기: docker-compose up -d (백그라운드 실행 권장)
  2. 2로그 보기: docker-compose logs -f web api (원하는 서비스만 집중 관찰)
  3. 3완전 초기화: docker-compose down -v (볼륨까지 삭제하여 데이터를 백지화할 때만 사용)

이제 개발에 집중하세요

이 컴포즈 파일 하나만 있으면, 팀에 새로 합류한 개발자는 복잡한 Node.js 버전 세팅이나 Go 컴파일러 설치, PostgreSQL 튜닝 없이 오직 명령어 한 줄(`docker-compose up -d`)만 치면 모든 개발 준비가 끝납니다.

이것이 바로 로컬 개발 환경에서 도커가 제공하는 가장 강력한 가치이며, 바이브코딩을 위한 견고한 토대입니다.

개념 퀴즈

퀴즈 답을 맞춰야 학습 완료가 됩니다.

위의 실전 docker-compose.yml 구조에서, 컨테이너가 처음부터 완전히 다시 시작될 때 가장 먼저 실행이 완료되고 통신 가능한 상태가 되어야 하는 서비스는 어느 것일까요?

3줄 요약

  1. 1도커 컴포즈를 사용하면 프론트, 백엔드, 다중 DB로 구성된 복잡한 아키텍처도 한 번에 구동할 수 있습니다.
  2. 2볼륨, 네트워크, 환경변수, 헬스체크 기능이 유기적으로 결합되어 충돌 없고 안정적인 로컬 환경을 보장합니다.
  3. 3명령어 한 줄로 팀원 모두가 완벽하게 동일한 개발 환경을 복제할 수 있게 됩니다.