
빌드 오류 때문에 밤새 본 적 있나요? 컴포즈 build 옵션의 핵심
분명히 로컬 환경에서는 코드가 잘 돌아가는데, 도커 컴포즈(Docker Compose)로 빌드하려고 하면 갑자기 파일을 못 찾겠다거나 빌드 속도가 너무 느려져서 답답했던 적이 있을 거예요. 특히 build 옵션을 처음 만졌을 때, 왜 어떤 건 되고 어떤 건 안 되는지 도무지 알 수 없어 막막한 기분이 들기도 해요.
이런 문제는 대부분 Dockerfile 자체의 잘못이라기보다는, 컴포즈 파일에서 정의한 빌드 설정이나 빌드 컨텍스트(Build Context)를 제대로 이해하지 못해서 발생해요. 컨텍스트가 너무 크면 빌드할 때마다 수 기가바이트(GB)의 데이터를 전송하느라 시간이 한없이 늘어지기도 하고, 경로 설정을 조금만 잘못해도 엔진은 명령을 수행할 수 없다고 외치죠.
지금 이 글을 읽고 계신 분들은 아마도 프로젝트를 컨테이너로 올리는 과정에서 예상치 못한 벽에 부딪힌 입문 개발자이거나, CI/CD 파이프라인을 구축하면서 빌드 최적화를 고민하는 분들일 거예요. 이 글을 끝까지 읽고 나면 빌드 옵션이 각각 어떤 역할을 하는지, 그리고 어떻게 하면 더 빠르고 정확하게 이미지를 만들 수 있는지 명확하게 알게 될 거예요.
이 글에서는 다음과 같은 내용을 아주 자세히 다뤄요.
- 빌드 컨텍스트의 개념과 경로 설정의 중요성
- build args를 활용한 동적 빌드 방법
- 빌드 속도를 획기적으로 높이는 .dockerignore 활용법
- 실무에서 자주 마주치는 빌드 오류와 해결책
빌드 시작 전 반드시 확인해야 할 체크리스트
본격적으로 컴포즈 build 옵션을 만지기 전에, 우리가 무엇을 준비해야 하는지 먼저 살펴봐야 해요. 무작정 명령어를 입력하기보다 기본 개념을 먼저 잡고 가는 것이 삽질을 줄이는 가장 빠른 길이에요.
꼭 알아야 할 핵심 용어
빌드 과정을 이해하려면 최소한 세 가지 용어는 머릿속에 넣어두어야 해요. 첫째는 빌드 컨텍스트예요. 이건 도커 엔진이 빌드를 수행하기 위해 ‘전송받는 폴더의 범위’를 뜻해요. 둘째는 Dockerfile이에요. 이미지를 어떻게 만들지 적어둔 레시피 같은 존재죠. 셋째는 Build Args예요. 빌드할 때만 잠깐 쓰고 버리는 일회용 변수라고 생각하면 쉬워요.
빌드할 때 사용하는 환경 변수(environment)와 빌드 인자(args)는 엄격히 구분돼요. environment는 컨테이너가 ‘실행될 때’ 사용하고, args는 이미지를 ‘만드는 과정’에서만 사용한다는 점을 잊지 마세요!
설정 방식에 따른 비교
도커 컴포즈 파일에서 이미지를 관리할 때, 단순히 만들어진 이미지를 가져올 것인지 아니면 직접 빌드할 것인지 결정해야 해요. 상황에 맞는 선택 기준을 아래 표로 정리해 보았어요.
| 구분 방식 | 주요 특징 | 추천 상황 |
|---|---|---|
| image 옵션만 사용 | Docker Hub 등에서 이미 만들어진 이미지를 내려받아요. | DB, Redis 같은 표준 소프트웨어를 쓸 때 |
| build 옵션 사용 | Dockerfile을 읽어 사용자의 코드가 담긴 이미지를 직접 만들어요. | 자체 개발한 애플리케이션을 배포할 때 |
| image + build 혼합 | 빌드 결과물에 특정 이름을 붙여서 저장해요. | 빌드한 이미지를 바로 다른 곳에서 재사용할 때 |
보통 개발 단계에서는 코드가 계속 바뀌기 때문에 build 옵션을 주로 사용하게 돼요. 하지만 운영 환경에서는 보안과 속도를 위해 미리 빌드된 이미지를 가져오는 방식을 선호한다는 점도 참고해 두면 좋아요.
실전! 컴포즈 빌드 옵션 완벽 마스터하기
이제 실제로 docker-compose.yml 파일을 어떻게 작성하고, 어떤 옵션을 써야 하는지 단계별로 알아볼게요. 이론만 아는 것보다 직접 코드를 보며 이해하는 것이 훨씬 빨라요.
STEP 1. 빌드 컨텍스트와 경로 설정하기
가장 먼저 해야 할 일은 빌드할 파일들이 어디에 있는지 알려주는 거예요. 컴포즈 파일에서 context라는 키를 사용하는데, 이게 바로 앞서 말한 빌드 컨텍스트예요. 보통은 .(현재 폴더)로 설정하지만, 폴더 구조가 복잡하다면 명확하게 지정해줘야 해요.
예를 들어, 프로젝트 루트에 `services`라는 폴더가 있고 그 안에 `api`라는 서비스용 코드가 있다면 아래와 같이 작성해요.
services:
api:
build:
context: ./services/api
dockerfile: Dockerfile.dev
여기서 dockerfile 옵션은 매우 유용해요. 기본적으로 도커는 컨텍스트 폴더 안에 이름이 Dockerfile인 파일을 찾지만, 개발용(Dockerfile.dev)과 운영용(Dockerfile.prod)을 따로 만들어 두고 싶을 때 이 옵션으로 갈아 끼울 수 있거든요.
STEP 2. Build Args로 동적 환경 만들기
이미지를 만들 때 특정 버전이나 설정값을 외부에서 넣어주고 싶을 때가 있죠? 이때 사용하는 것이 바로 args예요. 예를 들어, 특정 라이브러리 버전을 빌드 시점에 결정하고 싶다면 컴포즈 파일에 이렇게 적어주세요.
services:
web:
build:
context: .
args:
- APP_VERSION=1.2.0
- BUILD_ENV=development
이렇게 넘겨준 값은 Dockerfile 내부에서 ARG APP_VERSION과 같은 명령어를 통해 받아올 수 있어요. 이 방식을 쓰면 이미지 하나를 만들어 두고, 빌드 옵션만 바꿔서 여러 환경에 대응할 수 있어 매우 효율적이에요.
STEP 3. .dockerignore로 빌드 속도 올리기
빌드가 너무 느리다면 반드시 확인해야 할 포인트가 바로 .dockerignore 파일이에요. 도커는 빌드를 시작할 때 지정된 컨텍스트 폴더 전체를 도커 데몬으로 복사해요. 만약 프로젝트 폴더에 용량이 큰 node_modules나 .git 폴더, 혹은 거대한 데이터 파일이 들어있다면 빌드 한 번 할 때마다 이 파일들을 전부 전송하느라 엄청난 시간이 소요돼요.
이를 방지하기 위해 프로젝트 루트에 `.dockerignore` 파일을 만들고, 불필요한 항목을 적어줘야 해요. 다음과 같은 목록을 추천해요.
node_modules/(패키지 매니저가 생성한 대용량 폴더).git/(버전 관리 기록이 담긴 무거운 폴더)*.log(쌓여있는 로그 파일들)dist/또는build/(이전 빌드 결과물)
이렇게만 설정해도 빌드 시작 단계에서 발생하는 ‘Sending build context to Docker daemon’ 메시지의 진행 속도가 눈에 띄게 빨라지는 것을 경험할 수 있어요.
STEP 4. 캐시 활용과 무효화 전략
도커 빌드는 똑같은 명령어가 반복되면 이전 결과를 재사용하는 캐시(Cache) 메커니즘을 사용해요. 이건 엄청난 장점이지만, 때로는 독이 되기도 하죠. 코드를 수정했는데 반영이 안 된다면 도커가 예전 캐시를 그대로 쓰고 있는 거예요.
이럴 때는 두 가지 방법이 있어요. 첫째는 docker-compose build --no-cache 명령어를 사용하여 캐시를 완전히 무시하고 처음부터 다시 빌드하는 방법이에요. 둘째는 Dockerfile 내부에서 COPY 명령의 순서를 조정하는 거예요. 자주 바뀌지 않는 라이브러리 설치(예: npm install)를 코드 복사(COPY . .)보다 앞 순서에 배치하면, 코드가 바뀌어도 라이브러리 설치 단계는 캐시를 사용하므로 빌드 시간을 대폭 아낄 수 있어요.
실전 시나리오: 다중 서비스 빌드 예시
이제 배운 내용을 종합해서, 프론트엔드와 백엔드가 나누어진 프로젝트의 컴포즈 설정을 상상해 볼까요? 아래는 실무에서 흔히 볼 수 있는 구조예요.
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- NODE_ENV=production
ports:
- "3000:3000"
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
args:
- DB_HOST=db_service
environment:
- DEBUG=false
depends_on:
- db_service
db_service:
image: postgres:15-alpine
위 설정에서 프론트엔드는 개발 환경 변수를 넘겨주고, 백엔드는 별도의 운영용 Dockerfile을 사용하며, DB는 이미 만들어진 이미지를 가져다 쓰는 아주 깔끔한 구조를 보여주고 있어요. 각 서비스가 독립적인 컨텍스트를 가지기 때문에 서로의 불필요한 파일이 섞이지 않아 빌드도 아주 빠를 거예요.
자주 하는 실수와 해결법
빌드 과정에서 만나는 문제는 대부분 패턴이 정해져 있어요. 뻔한 오류를 피하기 위해 다음 사례들을 꼭 기억해 두세요.
- ❌ 실수:
context를 프로젝트 루트가 아닌 엉뚱한 하위 폴더로 설정함
→ 왜 발생하는가:Dockerfile이 있는 위치를 기준으로 생각하다 보니 정작 필요한 소스 코드가 포함된 상위 폴더를 놓치는 경우예요.
✅ 해결법:context는 프로젝트의 가장 뿌리가 되는 곳(보통 루트)으로 잡고,dockerfile옵션으로 상세 경로를 지정하세요. - ❌ 실수: 빌드 시 변경된 코드가 반영되지 않음
→ 왜 발생하는가: 도커의 레이어 캐시가 이전의 성공적인 빌드 상태를 그대로 유지하고 있기 때문이에요.
✅ 해결법:--no-cache옵션을 붙여 빌드하거나,Dockerfile내에서 파일을 복사하는 순서를 점검하세요. - ❌ 실수: 빌드 인자(args)가 컨테이너 안에서 안 보임
→ 왜 발생하는가:args는 빌드 중에만 살아있고, 실행 중에는 사라지기 때문이에요.
✅ 해결법: 실행 시점에 필요한 값은environment섹션에 따로 적어줘야 해요. - ❌ 실수: 빌드 속도가 말도 안 되게 느림
→ 왜 발생하는가: 불필요한 대용량 파일(예:node_modules)이 빌드 컨텍스트에 포함되어 전송되고 있어요.
✅ 해결법: .dockerignore 파일을 만들어 제외할 목록을 명확히 작성하세요. - ❌ 실수: 상대 경로 오류로 파일을 못 찾음
→ 왜 발생하는가:Dockerfile내부의COPY경로는context로 지정된 폴더를 기준으로 계산되기 때문이에요.
✅ 해결법:context폴더의 위치와COPY경로의 기준점을 항상 일치시키세요.
자주 묻는 질문
Q. docker-compose build와 docker-compose up –build의 차이가 뭔가요?
build 명령어는 오직 이미지를 만드는 작업만 수행해요. 반면 up --build는 이미지를 새로 빌드한 다음, 그 이미지를 바탕으로 컨테이너를 즉시 실행까지 해주는 아주 편리한 명령어예요. 개발할 때는 보통 up --build를 훨씬 많이 써요.
Q. Dockerfile 이름을 바꾸고 싶은데 어떻게 하나요?
docker-compose.yml의 build 섹션 아래에 dockerfile: 새로운이름이라고 적어주기만 하면 돼요. 이렇게 하면 하나의 폴더 안에서도 여러 개의 환경별 레시피를 관리할 수 있어요.
Q. 빌드할 때만 쓰고 버리는 변수를 어떻게 만드나요?
Dockerfile에 ARG 명령어를 사용하고, 컴포즈 파일의 build: args: 섹션에 값을 넣어주세요. 그러면 빌드 과정에서만 유효한 변수가 생성돼요.
Q. 빌드 결과물이 너무 커서 용량이 걱정돼요.
멀티 스테이지 빌드(Multi-stage build)를 검토해 보세요. 빌드할 때만 필요한 도구들은 첫 번째 스테이지에서 쓰고, 최종 이미지에는 실행에 꼭 필요한 파일만 두 번째 스테이지로 복사하는 방식이에요. 용량을 획기적으로 줄일 수 있어요.
Q. 컨텍스트 경로를 설정할 때 절대 경로를 써도 되나요?
가능은 하지만 권장하지 않아요. 팀원마다 컴퓨터 환경이 다르기 때문에, 반드시 프로젝트 내부를 기준으로 하는 상대 경로를 사용해야 어디서든 동일하게 동작해요.
더 나은 컨테이너 운영을 위한 첫걸음
오늘 우리는 컴포즈 빌드 옵션이 단순히 파일을 복사하는 과정을 넘어, 얼마나 전략적으로 다뤄야 하는지를 살펴봤어요. 빌드 컨텍스트를 잘 잡고, 필요한 인자만 전달하며, 불필요한 파일은 철저히 배제하는 것만으로도 여러분의 개발 환경은 훨씬 쾌적해질 거예요.
- 빌드 컨텍스트(context)는 소스 코드가 포함된 루트 폴더로 설정하세요.
- 특정 환경에 대응하려면
dockerfile옵션으로 레시피를 분리하세요. - 빌드 시점에 필요한 값은
args를, 실행 시점에 필요한 값은environment를 사용하세요. - 빌드 속도 향상을 위해
.dockerignore는 선택이 아닌 필수예요. - 캐시 문제로 코드가 반영 안 될 땐
--no-cache를 활용하세요.
오늘 배운 내용을 바탕으로 지금 바로 여러분의 docker-compose.yml 파일을 열어보세요. 혹시 너무 큰 폴더가 통째로 전송되고 있지는 않은지, 아니면 불필요하게 긴 빌드 시간을 견디고 있지는 않은지 확인해 볼 차례예요.
이번 주 실행 과제:
- 현재 프로젝트에
.dockerignore파일이 있는지 확인하고, 없다면 생성하기 args를 하나 추가해서Dockerfile에서 사용해 보기- 빌드 속도가 얼마나 개선되었는지 초시계로 측정해 보기
혹시 설정을 바꿨는데도 여전히 해결되지 않는 에러 메시지가 있다면, 주저하지 말고 댓글로 남겨 주세요. 여러분이 겪는 구체적인 상황을 정리해 주시면 함께 고민해서 답변을 달아 드릴게요!
다음 단계로 넘어가고 싶다면, 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드를 읽어보시는 것을 강력히 추천해요. 기본기가 탄탄하면 빌드 옵션은 훨씬 더 쉬워질 거예요.