팀 빌드 환경 표준화 | 누구나 한 번에 빌드되는 환경을 유지하는 방법
이 글의 핵심
빌드 환경이 복잡해질수록 "내 컴퓨터에선 되는데 팀원 컴퓨터에서는 안 된다"는 문제가 반복됩니다. 이 글은 환경 설정을 코드로 고정하고, CI와 로컬 환경의 차이를 없애고, 시간이 지나며 생기는 환경 드리프트를 막는 실전 방법을 정리합니다.
들어가며
새로 합류한 팀원에게 “이 저장소를 클론하고 빌드해보세요”라고 했을 때, 그 사람이 첫 빌드에 성공하기까지 얼마나 걸리는지는 팀의 개발 생산성을 보여주는 의외로 정확한 지표입니다. 저는 예전에 참여했던 한 프로젝트에서 신규 입사자의 환경 세팅에 평균 이틀이 걸리는 걸 본 적이 있습니다. 원인을 뜯어보니 온보딩 문서에는 “Node 18 설치, Python 3.10 설치, 특정 시스템 라이브러리 설치”라고만 적혀 있었는데, 실제로 빌드가 되려면 그 문서에 없는 로컬 환경변수 몇 개와, 누군가 예전에 전역으로 깔아둔 CLI 도구가 필요했습니다. 즉 “문서화된 환경”과 “실제로 빌드가 되는 환경”이 서로 다른 것이었습니다.
이 글은 그런 문제를 근본적으로 없애기 위해, 빌드 환경을 문서가 아니라 코드로 고정하고, 시간이 지나면서 그 코드가 실제 상태와 어긋나지 않도록(드리프트되지 않도록) 유지하는 방법을 정리합니다. 특정 언어나 프레임워크에 종속된 이야기가 아니라, 어떤 스택이든 적용할 수 있는 원칙 위주로 씁니다. Nix처럼 더 근본적으로 재현성을 보장하는 도구를 도입하고 싶다면 Nix & NixOS 완벽 가이드를, Docker 이미지 자체의 빌드 속도와 크기를 최적화하고 싶다면 Docker 멀티스테이지 빌드 최적화를 함께 참고하면 좋습니다.
1. “환경”을 설명하는 문서 대신 실행 가능한 코드로 고정하기
가장 흔한 실패 패턴은 README에 설치 순서를 나열하는 것입니다. 이 방식의 근본적인 문제는 문서는 실행되지 않는다는 점입니다. 누군가 새로운 의존성을 추가했을 때 코드는 바로 동작하지만, README를 갱신하는 것은 별도의 수동 작업이라 잊히기 쉽습니다. 몇 달이 지나면 README와 실제 필요한 환경이 조용히 어긋나기 시작합니다.
이걸 막는 가장 확실한 방법은 “환경을 설명하는 글”을 “환경을 만드는 코드”로 바꾸는 것입니다.
# Dockerfile.dev — 이 파일 자체가 "환경 설명서"를 대체합니다
FROM node:20-bookworm
RUN apt-get update && apt-get install -y \
libpq-dev \
build-essential \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
이 Dockerfile이 곧 문서입니다. 새 시스템 라이브러리가 필요해지면 반드시 이 파일을 고쳐야 빌드가 통과하므로, README처럼 “고치는 걸 깜빡하는” 실패 모드 자체가 구조적으로 없어집니다.
2. devcontainer로 “코드 편집기까지 포함한” 환경 규격화
Dockerfile만으로는 빌드는 되지만, 에디터에서 자동완성이 안 되거나 디버거 연결이 안 되는 등 개발자 경험 문제가 남습니다. VS Code의 devcontainer 규격을 쓰면 컨테이너 이미지에 더해 확장 프로그램, 설정, 포트 포워딩까지 함께 고정할 수 있습니다.
{
"name": "my-project-dev",
"dockerFile": "../Dockerfile.dev",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
"settings": { "editor.formatOnSave": true }
}
},
"forwardPorts": [3000, 5432],
"postCreateCommand": "npm ci"
}
새 팀원은 저장소를 클론하고 “Reopen in Container”만 누르면, 확장 프로그램 설치와 의존성 설치까지 자동으로 끝난 상태에서 코드를 열어볼 수 있습니다. 이 단계를 도입한 팀에서 실제로 체감되는 변화는, 온보딩 문서에서 “환경 설정” 챕터 전체가 “이 저장소를 클론하고 컨테이너로 여세요” 한 줄로 줄어든다는 점입니다.
3. CI와 로컬 환경을 같은 이미지로 통일하기
devcontainer까지 갖췄다면, CI 파이프라인도 로컬 개발과 같은 Dockerfile로 빌드하도록 맞추는 것이 다음 단계입니다. CI 전용 이미지를 따로 관리하면 “로컬에서는 되는데 CI에서는 실패한다”는 정반대 방향의 문제가 새로 생깁니다.
# .github/workflows/ci.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build dev image
run: docker build -f Dockerfile.dev -t app-dev .
- name: Run tests inside the same image
run: docker run --rm app-dev npm test
이렇게 하면 “빌드 환경”이 하나의 정의(Dockerfile)만 갖게 되고, 로컬·CI·신규 팀원의 환경이 항상 동일한 소스에서 나옵니다. 실무에서 겪는 흔한 함정은, CI 캐시 최적화를 위해 CI 워크플로우 안에 별도의 apt-get install 스텝을 추가해두고 정작 Dockerfile은 갱신하지 않는 경우입니다. 이러면 CI는 통과하지만 로컬 devcontainer는 그 패키지가 빠진 채로 남아, 시간이 지나면 두 환경이 다시 갈라지기 시작합니다. 환경에 뭔가를 추가할 때는 반드시 Dockerfile 한 곳에만 추가한다는 원칙을 팀 규칙으로 명시해두는 것이 좋습니다.
4. 환경 드리프트를 자동으로 감지하기
컨테이너화를 마쳤다고 끝이 아닙니다. 시간이 지나면서 다음과 같은 방식으로 조용히 어긋나기 시작합니다.
- 베이스 이미지 태그(
node:20)가 가리키는 실제 이미지가 시간이 지나며 패치 버전이 바뀜 - lockfile 없이 설치된 시스템 패키지가 업스트림에서 버전이 올라가며 동작이 달라짐
- 팀원 각자의 로컬 캐시에는 예전 이미지가 남아있어서, Dockerfile이 실제로 깨졌는데도 아무도 눈치채지 못함
이를 막기 위해 캐시 없이 매일 새벽 처음부터 이미지를 다시 빌드하는 CI 잡을 별도로 두는 것을 권합니다.
# .github/workflows/nightly-build-check.yml
on:
schedule:
- cron: '0 18 * * *' # 매일 새벽 3시(KST)
jobs:
clean-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build without cache
run: docker build --no-cache -f Dockerfile.dev -t app-dev-nightly .
- name: Notify on failure
if: failure()
run: echo "빌드 환경이 깨졌습니다 — 알림 채널로 전송"
이 잡이 실패하면 “누군가 코드를 고쳐서 빌드가 깨진 것”이 아니라 “환경 자체가 시간이 지나며 조용히 깨진 것”이라는 신호입니다. 이런 종류의 실패는 평소 테스트로는 절대 잡히지 않기 때문에, 별도의 감시 장치 없이는 몇 주씩 방치되는 경우가 많습니다.
5. 레거시 프로젝트를 점진적으로 컨테이너화하는 순서
이미 오래된 프로젝트라 빌드 스크립트가 지저분하다면, 한 번에 전부 컨테이너화하려 하지 말고 다음 순서를 권합니다.
- 현재 로컬 환경에서 빌드에 필요한 모든 단계를 하나의 셸 스크립트로 나열
- 그 스크립트를 완전히 새로운 컴퓨터(또는 최소한의 컨테이너)에서 실행해보며 실패하는 지점을 확인
- 실패한 지점(암묵적으로 필요했던 전역 도구, 하드코딩된 절대 경로 등)을 하나씩 Dockerfile로 옮김
- 스크립트 전체가 컨테이너 안에서 실패 없이 통과할 때까지 반복
- 통과하면 그 Dockerfile을 devcontainer/CI에 그대로 연결
이 순서의 핵심은 “실패하는 지점을 실제로 확인한 뒤에만 고친다”는 것입니다. 처음부터 완벽한 Dockerfile을 추측해서 작성하면, 실제로는 존재하지 않는 문제를 미리 방어하느라 불필요하게 복잡해지는 경우가 많습니다.
정리
빌드 환경을 팀 전체가 신뢰할 수 있게 유지하는 핵심은 결국 세 가지입니다.
- 환경을 문서가 아니라 코드(Dockerfile/devcontainer)로 고정한다
- 로컬과 CI가 같은 정의에서 빌드되도록 통일한다
- 시간이 지나며 생기는 드리프트를 정기적인 클린 빌드로 자동 감지한다
이 세 가지를 갖추면 “내 컴퓨터에선 되는데요”라는 말이 팀 채팅에서 사라지고, 새 팀원의 첫 빌드 성공까지 걸리는 시간이 온보딩 문서의 품질이 아니라 “컨테이너를 실행할 수 있는가”라는 훨씬 단순한 질문으로 바뀝니다.