프로젝트/기술 면접 복습 플랫폼
프로젝트에 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 덕분에 얻은 효과
- 배포 생산성↑: docker-compose up -d 한 줄이면 모든 서비스가 동작한다.
- 환경 이슈 해결: 자바 버전을 확인해야하는 수고로움이 없어진다.
- 이미지 용량↓: 멀티스테이지 빌드로 불필요한 파일을 제외시켰다.
- 운영 일관성 확보: 로컬-테스트-프로덕션이 완전 동일해졌다.
마무리
처음에는 생소했던 Docker였지만, 하나씩 설정하면서 확실히 서비스의 배포 체계가 탄탄해졌다는 느낌이 들었다.
코드 잘 짰으니 이젠 어디서든 잘 굴러가겠지 라는 안심이 드는 게 정말 크다.
다음엔 코드를 Push 하면 바로 빌드 → Docker 이미지 생성 → EC2 배포까지 이어지는 과정을 만드는 게 목표이다.