
설정 파일 하나 때문에 멈춰버린 빌드 작업
분명히 로컬 환경에서는 잘 돌아가던 프로젝트인데, docker-compose up –build 명령어를 입력하자마자 붉은색 에러 메시지가 화면을 가득 채울 때가 있어요. “failed to solve: rpc error: code = Unknown desc = failed to compute cache key: not found” 같은 메시지를 마주하면 머릿속이 하얘지곤 해요. 파일 경로는 맞는 것 같은데 왜 도커는 Dockerfile을 찾지 못한다고 하는 걸까요?
이런 문제는 대부분 빌드 컨텍스트(Build Context)에 대한 이해가 부족해서 발생해요. 도커 컴포즈는 단순히 파일을 실행하는 도구가 아니라, 지정된 폴더를 통째로 도커 엔진에 전달하여 이미지를 만드는 복잡한 과정을 거쳐요. 이 과정에서 폴더의 범위를 잘못 지정하거나, 경로를 한 단계만 잘못 적어도 전체 빌드 프로세스는 완전히 멈춰버려요.
단순히 이미지를 내려받는(pull) 방식이 아니라, 내가 만든 소스코드를 직접 이미지로 만들어야 하는 개발자라면 반드시 넘어야 할 산이에요. 이 고비를 넘기지 못하면 컨테이너 운영 단계에서 매번 설정 파일을 수정하며 시간을 낭비하게 돼요.
이 글을 끝까지 읽고 나면, 더 이상 빌드 경로 때문에 씨름하지 않아도 돼요. 다음과 같은 핵심 내용을 완벽하게 익힐 수 있어요.
- 컴포즈 build 옵션의 핵심 개념과 동작 원리
- 실무에서 바로 복사해서 쓰는 상황별 설정 예제
- 빌드 인자(args)를 활용한 유연한 이미지 생성법
- 자주 발생하는 경로 오류와 해결 노하우
성공적인 빌드를 위한 사전 체크리스트
무작정 코드를 짜고 빌드를 시도하기 전에, 우리가 다루는 용어와 환경을 명확히 정리할 필요가 있어요. 컴포즈 파일에 build 키워드를 적기 전, 반드시 이해해야 하는 세 가지 기둥이 있어요.
반드시 알아야 할 핵심 용어
첫 번째는 빌드 컨텍스트예요. 이건 도커 엔진이 이미지를 만들기 위해 참조할 수 있는 ‘파일들의 범위’를 말해요. 만약 컨텍스트를 현재 폴더(.)로 잡았다면, 도커는 그 폴더 안에 있는 모든 파일을 읽으려고 시도해요.
두 번째는 Dockerfile이에요. 이미지를 어떻게 만들지 적어둔 레시피라고 생각하면 돼요. 빌드 컨텍스트 안에 이 레시피가 반드시 들어있어야 해요.
세 번째는 빌드 인자(Build Args)예요. 이미지를 만들 때 외부에서 값을 전달해주는 통로예요. 예를 들어, 개발용 이미지와 운영용 이미지를 하나의 Dockerfile로 만들고 싶을 때 이 인자를 사용해요.
빌드 컨텍스트를 너무 넓게 잡으면(예: 프로젝트 루트 전체) 빌드 속도가 매우 느려질 수 있어요. 불필요한 파일이 도커 엔진으로 전송되지 않도록 .dockerignore 파일을 사용하는 습관을 들여야 해요.
상황별 설정 선택 기준
어떤 방식으로 서비스를 실행할지 결정하기 위해 아래 표를 참고해 보세요. 여러분의 프로젝트 상황에 맞는 방식을 선택하는 것이 첫걸음이에요.
| 설정 방식 | 주요 용도 | 장점 | 단점 |
|---|---|---|---|
| image 사용 | 이미 빌드된 공용 이미지 사용 | 가장 빠르고 간편함 | 커스텀 설정 불가 |
| 기본 build | 로컬 소스 기반 이미지 생성 | 내 소스를 즉시 반영 | 빌드 시간 소요 |
| build + args | 환경별 맞춤 이미지 생성 | 재사용성이 매우 높음 | 설정이 복잡해짐 |
대부분의 개발 환경에서는 build 옵션을 사용하여 소스코드를 이미지로 변환하는 과정이 필수적이에요. 이제 본격적으로 구체적인 예제를 통해 실력을 키워볼까요?
실무에 바로 적용하는 단계별 build 설정 예제
이론은 충분히 이해했어요. 이제 실제 docker-compose.yml 파일에 어떤 내용을 적어야 하는지, 단계별로 하나씩 살펴보며 실전 감각을 익혀볼게요.
STEP 1. 가장 기본적인 최소 구성 예제
프로젝트 구조가 매우 단순할 때 사용하는 방식이에요. Dockerfile과 docker-compose.yml이 같은 폴더에 있는 경우죠.
services:
web:
build: .
위 설정은 현재 디렉토리(.)를 빌드 컨텍스트로 사용하고, 그 안에 있는 기본 이름인 Dockerfile을 찾아 빌드를 시작하라는 뜻이에요. 가장 깔끔하지만, 프로젝트가 커지면 관리하기가 어려워져요.
STEP 2. Dockerfile 경로를 별도로 지정하기
실무에서는 서비스별로 폴더를 나누어 관리하는 경우가 많아요. 예를 들어, 백엔드 코드가 ./backend 폴더 안에 있다면 어떻게 설정해야 할까요? 단순히 경로만 적으면 안 되고, context와 dockerfile을 명확히 구분해줘야 해요.
services:
api:
build:
context: ./backend
dockerfile: Dockerfile.dev
여기서 주의할 점이 있어요. context는 도커가 파일을 찾을 ‘시작점’이에요. 만약 context를 ./backend로 잡았다면, dockerfile의 경로는 그 안에서의 상대 경로여야 해요. 즉, ./backend/Dockerfile.dev를 의미하게 되는 거죠. 만약 경로를 꼬아버리면 앞서 말한 ‘file not found’ 에러를 만나게 돼요.
STEP 3. 빌드 인자(args)를 활용한 유연한 환경 구축
개발 환경(dev)과 운영 환경(prod)에서 서로 다른 라이브러리 버전을 사용해야 한다면 어떻게 할까요? 매번 Dockerfile을 새로 쓰는 건 너무 비효율적이에요. 이때 사용하는 것이 바로 args 옵션이에요.
services:
app:
build:
context: .
args:
APP_VERSION: 1.2.0
NODE_ENV: production
이렇게 전달된 값은 Dockerfile 내에서 ARG APP_VERSION 명령어를 통해 사용할 수 있어요. 하나의 레시피로 다양한 환경의 요리를 만들어내는 아주 스마트한 방법이에요.
STEP 4. 여러 서비스를 가진 복합 프로젝트 관리
실제 프로젝트는 프론트엔드, 백엔드, 데이터베이스가 얽혀 있는 구조죠. 각 서비스의 빌드 방식을 다르게 설정하는 통합 예제를 볼게요.
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
backend:
build:
context: ./server
dockerfile: Dockerfile.prod
args:
DB_HOST: db_container
db:
image: postgres:15-alpine
위 예제에서 보듯이, 직접 빌드해야 하는 서비스는 build를 사용하고, 이미 완성된 이미지가 있는 외부 라이브러리는 image를 사용하여 자원을 효율적으로 배분할 수 있어요.
STEP 5. 빌드 성능 최적화와 캐시 관리
매번 빌드할 때마다 모든 과정을 처음부터 다시 한다면 시간이 너무 많이 걸려요. 도커는 변경되지 않은 단계는 cache를 사용해 건너뛰지만, 가끔 캐시 때문에 옛날 코드가 반영되는 문제가 생기기도 해요.
소스코드가 변경되었는데도 반영이 안 된다면, 빌드 캐시가 꼬인 것일 수 있어요. 이럴 땐 docker-compose build –no-cache 명령어로 캐시를 완전히 무시하고 새로 빌드하는 것이 가장 확실한 해결책이에요.
또한, target 옵션을 사용하면 Dockerfile 내의 multi-stage build 단계 중 특정 단계까지만 빌드하도록 지정할 수 있어, 개발 단계에서는 가벼운 이미지를 사용하는 데 큰 도움이 돼요.
자주 하는 실수와 해결법
실무에서 제가 직접 겪고, 동료들이 가장 많이 물어보는 실수들을 모아봤어요. 이 리스트만 알고 있어도 디버깅 시간이 절반으로 줄어들 거예요.
- ❌ 컨텍스트 경로 오류 → 왜 발생하는가: context 경로를 기준으로 dockerfile 경로를 적지 않아서 발생해요. ✅ 해결법: context 폴더로 먼저 이동한 뒤, 그 안에서 파일이 있는지 확인하는 느낌으로 경로를 작성하세요.
- ❌ 빌드 인자(args) 미전달 → 왜 발생하는가: Dockerfile에는 ARG가 정의되어 있지만, 컴포즈 파일에 args 섹션을 누락했을 때 발생해요. ✅ 해결법: 컴포즈 파일의 build: args: 항목을 반드시 확인하세요.
- ❌ 대소문자 구분 실수 → 왜 발생하는가: 리눅스 기반 도커 엔진은 Dockerfile과 dockerfile을 다르게 인식해요. ✅ 해결법: 파일명의 대소문자를 정확히 일치시키세요.
- ❌ .dockerignore 누락 → 왜 발생하는가: node_modules 같은 무거운 폴더가 컨텍스트에 포함되어 빌드가 멈추거나 느려져요. ✅ 해결법: 불필요한 파일은 반드시 .dockerignore에 등록하세요.
- ❌ 권한 문제 → 왜 발생하는가: 빌드 중 파일을 생성할 때 권한이 없어 실패하는 경우예요. ✅ 해결법: Dockerfile 내에서 USER 명령어를 적절히 사용하거나 권한 설정을 확인하세요.
자주 묻는 질문
Q. 캐시를 사용하지 않고 강제로 다시 빌드하려면 어떻게 하나요?
명령어 뒤에 –no-cache 옵션을 붙여주면 돼요. docker-compose build –no-cache라고 입력하면 됩니다.
Q. 빌드 컨텍스트를 프로젝트 루트가 아닌 다른 곳으로 잡을 수 있나요?
네, 당연해요. context: 값에 원하는 상대 경로 혹은 절대 경로를 적어주면 도커는 그 폴더를 기준으로 모든 파일을 읽기 시작해요.
Q. 하나의 Dockerfile로 여러 환경을 만드는 게 정말 효율적인가요?
그럼요. ARG와 multi-stage build를 조합하면, 하나의 파일로 개발용, 테스트용, 운영용 이미지를 모두 제어할 수 있어 관리가 매우 편해져요.
Q. 컴포즈 파일에서 빌드한 이미지를 나중에 다시 쓰려면 어떻게 하나요?
build 옵션과 함께 image 옵션을 같이 적어주세요. 그러면 빌드가 완료된 후 해당 이름으로 로컬 이미지 저장소에 등록되어 나중에 docker images로 확인할 수 있어요.
마치며: 안정적인 컨테이너 운영을 위하여
지금까지 컴포즈 build 옵션의 다양한 설정법과 실무 예제를 살펴보았어요. 처음에는 복잡해 보이지만, 컨텍스트(어디서)와 파일 경로(무엇을), 그리고 인자(어떻게)라는 세 가지만 기억하면 금방 익숙해질 수 있어요.
- 빌드 컨텍스트는 도커 엔진에 전달되는 파일의 범위예요.
- Dockerfile 경로 지정 시 컨텍스트 기준의 상대 경로를 사용하세요.
- 환경별 차이는 build: args 옵션으로 해결하세요.
- 빌드 속도를 위해 .dockerignore를 반드시 활용하세요.
- 캐시 문제 발생 시 –no-cache 옵션을 사용해 보세요.
오늘 배운 내용을 바탕으로 여러분의 docker-compose.yml 파일을 한 번 점검해 보는 건 어떨까요? 작은 설정 하나가 전체 배포 파이프라인의 안정성을 결정한답니다.
🚀 오늘 바로 할 일: 현재 진행 중인 프로젝트의 Dockerfile 경로가 컴포즈 파일의 컨텍스트와 일치하는지 확인해 보세요.
📅 이번 주 목표: 서비스별로 Dockerfile을 분리하고, args를 사용하여 개발/운영 환경을 구분해 보세요.
실무에서 바로 사용할 수 있는 설정 예제 파일이 필요하다면, 본문의 예제를 프로젝트 구조에 맞게 수정해서 바로 적용해 보세요. 더 깊이 있는 컨테이너 운영이 궁금하다면 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드 글을 함께 읽어보시길 추천해요.