프로젝트/기술 면접 복습 플랫폼

프로젝트에 Docker를 도입한 이유

yoon4360 2025. 5. 5. 17:12

그런 얘기를 많이 들어봤다.

 

프로젝트 초기에는 로컬에서 Spring Boot 서버 띄우고, Next.js로 프론트 돌리고 처음엔 잘 됐었다.
그런데 팀원이 늘고, 배포를 하려다 보니 시스템 환경마다 오류가 발생했다.


클래식한 그 문제 , “내 PC에선 잘 되는데요?”

이 문제를 팀프로젝트에서 겪을 것을 대비해 Docker로 모든 환경을 통일해보고자 했다.


 



왜 Docker를 도입?

Docker를 도입하면서 기대했던 가장 큰 변화는 어디서든 한 번 설정으로 동일한 환경을 재현할 수 있다는 점이다.
Docker를 사용하면 EC2에도 Docker만 설치하면 되므로, 배포 과정도 간소화된다.

문제 상황: Docker 없는 환경의 고통

  • 로컬마다 자바, npm, node, OS가 미묘하게 달라서 오류가 발생한다.
  • 프론트, 백엔드, Qdrant, Nginx를 일정 순서로 따로 실행해야 하는 번거로움이 있다.
  • EC2 배포 시에는 OS부터 설치하고, 각 버전 세팅까지 수작업해야 한다.
  • 변경이 생기면 팀원마다 일일이 설정을 다시 해야 한다.

Docker 도입 후 기대되는 변화

  • 어디서든 한 번 설정 → 같은 환경을 재현 할 수 있다.
  • EC2에 Docker만 설치하면 끝 → 배포도 쉽다.
  • docker-compose로 여러 서비스를 한꺼번에 제어 가능하다.
  • 프론트, 백, DB, 프록시가 하나의 시스템처럼 작동한다.

 


 

프로젝트에서 Docker가 맡은 역할

여러 서비스를 하나의 시스템처럼 관리할 수 있는 docker-compose 덕분에
프론트엔드, 백엔드, DB, 프록시가 한꺼번에 구동되어 개발 속도와 효율이 크게 향상되는 것을 기대할 수 있었다.

컨테이너 Docker에서의 역햘
backend (Spring Boot) Gradle로 JAR 빌드 후 실행
frontend (Next.js) 정적 빌드 후 Node로 실행
qdrant 벡터 DB (도커 허브 이미지 사용)
nginx 리버스 프록시로 API/프론트 라우팅

 


 

Docker 설정 코드

Backend - Dockerfile

# 1단계: 빌드용
FROM gradle:8.5.0-jdk17 AS build
WORKDIR /app
COPY --chown=gradle:gradle . .
RUN gradle clean build -x test --console=plain --no-daemon

# 2단계: 실행용
FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY --from=build /app/build/libs/server-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
  • 멀티 스테이지 빌드: 이미지 크기를 줄이기 위해 빌드/실행을 분리했다.
  • -x test: 테스트 제외로 빌드 속도 향상시켰다.
  • --no-daemon: Docker 환경에선 Gradle 데몬 비활성화했다.

Frontend - Dockerfile

FROM node:18
WORKDIR /app
COPY .env .env
COPY . .
RUN npm install --omit=dev
RUN npm run build
EXPOSE 3000
CMD ["npx","next","start"]

 

  • .env 파일 복사 → NEXT_PUBLIC_API_URL 환경변수를 주입했다.
  • --omit=dev: 배포 이미지에서 불필요한 dev 패키지를 제외했다.
  • npx next start: 빌드된 정적 앱을 실행한다.

 

docker-compose.yml

version: "3.8"

services:
  backend:
    build:
      context: ./server
      dockerfile: Dockerfile
    container_name: backend
    expose:
      - "8080"
    env_file:
      - .env
    depends_on:
      - qdrant

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: frontend
    expose:
      - "3000"
    env_file:
      - ./frontend/.env
    depends_on:
      - backend

  qdrant:
    image: qdrant/qdrant
    container_name: qdrant
    ports:
      - "6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage

  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/cert:/etc/nginx/cert
    depends_on:
      - frontend
      - backend

volumes:
  qdrant_data:
  • depends_on: 의존 관계를 정의해서 실행 순서를 보장했다.
  • env_file: 민감한 변수는 .env로 관리했다.
  • Qdrant는 공식 이미지 사용 + 볼륨 설정으로 데이터 보존했다.
  • Nginx는 리버스 프록시 + HTTPS 대비 cert 폴더 미리 구성했다.

 


 

Docker 덕분에 얻은 효과

  1. 배포 생산성↑: docker-compose up -d 한 줄이면 모든 서비스가 동작한다.
  2. 환경 이슈 해결: 자바 버전을 확인해야하는 수고로움이 없어진다.
  3. 이미지 용량↓: 멀티스테이지 빌드로 불필요한 파일을 제외시켰다.
  4. 운영 일관성 확보: 로컬-테스트-프로덕션이 완전 동일해졌다.

 


 

마무리

처음에는 생소했던 Docker였지만, 하나씩 설정하면서 확실히 서비스의 배포 체계가 탄탄해졌다는 느낌이 들었다.
코드 잘 짰으니 이젠 어디서든 잘 굴러가겠지 라는 안심이 드는 게 정말 크다.

 

다음엔 코드를 Push 하면 바로 빌드 → Docker 이미지 생성 → EC2 배포까지 이어지는 과정을 만드는 게 목표이다.