Featured Post

AI 코딩할 때 Docker 컨테이너로 토큰 아끼고 삽질 줄인 경험담

Docker - 컨테이너 관련 이미지

요즘 나도 꽤 자주 AI 바이브 코딩을 하고 있어. ChatGPT나 Claude한테 “이 기능 하나 만들어줘”, “이 에러 좀 봐줘” 하면서 말이지. 처음엔 참 신기하고 편했는데, 며칠 제대로 써보니까 묘하게 찝찝한 순간이 오더라. 코드보다 환경 설명을 더 많이 하고 있는 느낌? “Python 버전은 이렇고, 패키지는 저렇고, DB는 PostgreSQL이고…” 이런 말을 매번 반복하다 보니, 아 이거 토큰도 아깝고 내 시간도 아깝다 싶었어. 그래서 예전에 현업에서 지겹도록 써왔던 Docker를 AI 코딩 흐름에 붙여봤는데, 생각보다 효과가 꽤 괜찮더라고. 오늘은 그 얘기를 좀 편하게 해보려고 해.

AI가 코드는 잘 짜주는데, 환경은 결국 내가 책임져야 하더라

솔직히 처음에는 이런 생각을 했어. “이제 AI가 코드도 짜주는데 Docker까지 굳이 챙겨야 하나?” 뭐랄까, 약간 귀찮았지. 그냥 프롬프트 잘 쓰고, 결과물 받아서 실행하면 되지 않나 싶었거든.

근데 현실은 조금 달랐어. 예를 들어 AI한테 “FastAPI로 간단한 사용자 API 만들어줘”라고 하면 코드는 꽤 그럴듯하게 줘. 그런데 막상 내 로컬에서 실행해보면 Python 버전이 안 맞거나, 패키지 충돌이 나거나, 예전에 깔아둔 라이브러리 때문에 이상한 에러가 나. 그러면 또 AI한테 물어보게 되지.

ImportError: cannot import name 'Something' from 'some_package'

이런 에러 하나 던져주면 AI가 또 설명을 해줘. 그런데 문제는 그다음이야. 내가 내 환경을 다시 설명해야 해. “내 Python은 3.10이고, FastAPI 버전은 이렇고, 가상환경은 이렇게 되어 있고…” 어휴, 쓰다 보면 내가 개발을 하는 건지 환경설정 자기소개서를 쓰는 건지 헷갈릴 때가 있어.

그때 문득 이런 생각이 들었어. “아, 그냥 컨테이너 안에 고정된 개발 환경을 하나 만들어두면 되잖아.” 예전 프로젝트에서 신규 입사자 온보딩할 때 Docker로 환경 통일했던 기억이 딱 떠오르더라고. 그걸 LLM 코딩 워크플로우에 붙이면, AI한테 매번 환경을 구구절절 설명할 필요가 없겠다는 생각이 들었어.

실제로 그렇게 바꾸고 나니까 꽤 달라졌어. Docker 컨테이너 하나 띄워놓고, AI가 만들어준 코드를 그 안에서 바로 돌려보고, 에러 메시지만 짧게 넘겨주면 되니까 불필요한 프롬프트 재시도가 확 줄었어. 이게 은근히 크다. 토큰도 아끼고, 마음도 덜 피곤해.

내가 제일 먼저 한 일은 개발용 기본 이미지를 만들어두는 거였어

내가 가장 먼저 손본 건 개발용 기본 Docker 이미지였어. 예전에는 프로젝트 시작할 때마다 AI한테 이런 식으로 말했거든.

Python 3.11 기반이고,
FastAPI를 쓰고,
SQLAlchemy도 필요하고,
PostgreSQL 드라이버도 설치해야 하고,
테스트는 pytest로 돌릴 거야.

한두 번은 괜찮아. 그런데 매번 이러면 좀 지쳐. 그리고 AI도 가끔 헷갈려. 어떤 날은 psycopg2를 쓰고, 어떤 날은 asyncpg를 쓰자고 하고, 또 어떤 날은 설치 명령어를 빼먹어. 사람이랑 같이 일해도 이런 동료 있으면 커피 한잔 마시면서 대화가 필요하지.

그래서 아예 내가 자주 쓰는 조합을 기본 이미지로 만들어뒀어.

FROM python:3.11-slim

RUN apt-get update && apt-get install -y git curl

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

WORKDIR /app

이렇게 해두면 AI한테 길게 설명할 필요가 없어져. 그냥 이렇게 말하면 돼.

내 개발 환경은 Python 3.11 기반 Docker 컨테이너야.
FastAPI와 SQLAlchemy는 이미 설치되어 있어.
users API에 페이지네이션 기능만 추가해줘.

이 정도면 충분하더라. 예전에는 환경 설명만 길게 붙였는데, 이제는 요구사항 자체에만 집중할 수 있어. AI도 훨씬 덜 헤매고, 나도 덜 짜증 나고. 사실 이런 게 생산성 아니겠어. 대단한 마법은 아닌데, 매일 쓰다 보면 차이가 꽤 쌓여.

방식 AI에게 설명해야 하는 내용 체감 차이
로컬 환경 직접 사용 Python 버전, 패키지 버전, OS 차이, 설치 상태를 계속 설명 에러 원인이 환경인지 코드인지 헷갈림
Docker 기본 이미지 사용 컨테이너 기준 환경이라는 것만 짧게 설명 프롬프트가 짧아지고 재현성이 좋아짐

Docker Compose를 쓰면 AI한테 DB 설명을 덜 하게 돼

혼자 간단한 스크립트 만들 때는 Docker 하나만 있어도 괜찮아. 그런데 실제 개발은 보통 그렇게 예쁘게 끝나지 않잖아. 백엔드가 있고, 데이터베이스가 있고, Redis도 있고, 가끔은 Queue나 검색엔진까지 붙어. 이때부터 AI한테 설명할 게 갑자기 많아져.

예전에는 이런 식으로 프롬프트를 썼어.

PostgreSQL은 localhost:5432에서 실행 중이고,
DB 이름은 appdb이고,
사용자는 appuser이고,
비밀번호는 apppass야.
Redis는 localhost:6379야.
이 환경에 맞춰서 코드를 작성해줘.

별거 아닌 것 같지만, 이런 문장을 매번 붙이는 게 은근히 귀찮아. 그리고 더 큰 문제는 AI가 가끔 localhost 기준으로 코드를 짜준다는 거야. Docker 컨테이너 안에서는 localhost가 내 노트북이 아니라 컨테이너 자기 자신인데 말이지. 이거 모르면 한참 헤맨다. 나도 예전에 주니어들한테 이 설명만 몇 번을 했는지 몰라.

그래서 나는 Docker Compose 파일에 서비스 이름을 딱 정해두고 써. 예를 들면 이런 식이야.

서비스 컨테이너 이름 역할 코드에서 접근하는 이름
app myapp FastAPI 앱 app
db postgres-db PostgreSQL db
cache redis-cache Redis cache

이렇게 정해두면 AI한테는 그냥 이렇게 말하면 돼.

docker-compose 환경에서 app, db, cache 서비스가 떠 있어.
FastAPI 앱에서 db와 cache를 사용하도록 코드를 작성해줘.

짧지? 그런데 충분해. 컨테이너끼리는 Compose 서비스 이름으로 통신하니까, AI가 이상하게 localhost를 남발할 확률도 줄어들어. 물론 완전히 사라지진 않아. AI도 가끔 고집이 있거든. 그래도 내가 매번 장문의 환경 설명을 붙이는 것보다는 훨씬 낫다.

내가 자주 쓰는 Compose 흐름은 대충 이런 느낌이야.

docker compose up -d
docker compose logs -f app
docker compose restart app

특히 볼륨 마운트를 걸어두면 로컬에서 파일을 고친 게 컨테이너에 바로 반영돼. AI가 “이 부분을 이렇게 바꿔보세요”라고 하면 파일 수정하고, 앱만 재시작하고, 바로 테스트. 이 리듬이 생기면 꽤 편해. 개발할 때 흐름 끊기는 게 제일 싫잖아. 나이 들수록 더 그래. 한 번 끊기면 커피 타러 가고, 메신저 보고, 정신 차리면 30분 지나 있거든.

Dockerfile 레이어 캐싱은 AI 코딩할 때 더 빛나더라

AI랑 코딩하다 보면 흐름이 중요해. 코드를 받고, 실행하고, 에러를 보고, 다시 물어보고. 이걸 빠르게 돌려야 감이 안 끊겨. 그런데 매번 Docker 이미지를 처음부터 빌드하면 속이 좀 답답해져. 별것도 아닌 코드 한 줄 바꿨는데 의존성 설치부터 다시 하면, 기다리는 동안 마음이 식어.

그래서 Dockerfile에서 의존성 설치와 소스 코드 복사를 분리해두는 게 좋아. 이건 진짜 오래된 팁인데, AI 코딩 시대에도 여전히 유효해.

FROM python:3.11-slim

COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt

COPY . /app

WORKDIR /app

이렇게 해두면 requirements.txt가 바뀌지 않는 한 패키지 설치 레이어는 캐시돼. AI가 대부분 바꾸는 건 소스 코드잖아. 그러면 COPY . /app 이후만 다시 처리하면 되니까 빌드가 훨씬 빨라져.

이 차이가 작아 보이는데, 하루 종일 쌓이면 꽤 커. 예전에 내가 간단한 API 서버를 만들면서 대충 시간을 재본 적이 있어.

상황 빌드 시간 느낌
의존성과 소스 코드를 한 번에 복사 약 40초~1분 기다리다가 집중이 자주 끊김
레이어 캐싱을 고려한 Dockerfile 대부분 2~5초 AI 피드백 루프를 계속 이어가기 좋음

이게 토큰 절약이랑 무슨 상관이냐고 물을 수도 있어. 나는 꽤 관련이 있다고 봐. 빌드와 실행이 빠르면 내가 직접 확인하는 횟수가 늘어나고, 막연하게 AI한테 “안 되는 것 같아요”라고 물어보는 일이 줄어들어. 대신 정확한 에러 로그를 짧게 던질 수 있지.

에러:
sqlalchemy.exc.OperationalError: could not translate host name "localhost" to address

docker-compose 환경이고 DB 서비스 이름은 db야.
DB 접속 설정만 수정해줘.

이렇게 짧고 구체적으로 물어보면 AI 답변도 훨씬 좋아져. 긴 프롬프트보다 정확한 프롬프트가 이긴다. 이건 내가 LLM 쓰면서 계속 느끼는 부분이야.

.dockerignore는 은근히 많이들 놓치는데, 안 쓰면 손해야

이건 좀 민망한 얘기인데, 나도 예전에 급하게 만들다가 .dockerignore를 빼먹은 적이 있어. 그랬더니 이미지에 node_modules, .git, 테스트 결과물, 캐시 파일까지 다 들어가더라. 이미지 크기가 1GB를 넘어갔어. 그걸 보고 잠깐 멍해졌지. “내가 지금 뭘 만든 거지?” 싶더라고.

이미지가 커지면 빌드도 느리고, 푸시도 느리고, CI도 느려져. 그리고 개발 리듬도 같이 느려져. AI 코딩할 때는 더 치명적이야. 빠르게 돌려보고 수정해야 하는데, 컨테이너 하나 뜨는 데 시간이 오래 걸리면 괜히 AI한테 엉뚱한 질문을 하게 되거든.

나는 보통 이런 항목은 거의 기본으로 넣어둬.

    • node_modules - 컨테이너 빌드 시 다시 설치하는 게 낫다
    • .git - 이미지 안에 Git 히스토리까지 넣을 이유가 거의 없다
    • __pycache__ - Python 캐시 파일은 불필요하다
    • .pytest_cache - 테스트 캐시도 이미지에는 굳이 필요 없다
    • .env - 이건 정말 조심해야 한다. 보안상 이미지에 들어가면 안 된다
    • dist, build - 프론트엔드 산출물은 상황에 따라 분리하는 편이 낫다

내가 자주 쓰는 .dockerignore는 이런 느낌이야.

.git
.gitignore
.env
node_modules
__pycache__
.pytest_cache
.mypy_cache
dist
build
*.log
.DS_Store

이거 하나 넣었을 뿐인데 이미지 크기가 확 줄어든 적이 많아. 1GB 넘던 이미지가 200MB 안쪽으로 내려온 적도 있었고. 빌드 속도가 빨라지니 테스트도 자주 하게 되고, 테스트를 자주 하니 AI한테 던지는 질문도 짧아져. 이상하게 연결돼 있지만, 실제로 그래.

내가 실제로 쓰는 AI + Docker 작업 흐름

조금 더 현실적으로 얘기해볼게. 내가 평소에 FastAPI 프로젝트를 만질 때는 대충 이런 흐름으로 가.

    • docker compose up -d로 PostgreSQL, Redis, 앱 컨테이너를 먼저 띄운다
    • AI에게 “현재 앱은 FastAPI고, 사용자 API는 /users에 있어. 페이지네이션 기능을 추가해줘. 파일은 /app/api/users.py야.” 정도로 짧게 말한다
    • AI가 준 코드를 해당 파일에 반영한다
    • docker compose restart app으로 앱 컨테이너만 재시작한다
    • curl이나 Postman으로 바로 확인한다
    • 에러가 나면 전체 설명 대신 에러 로그와 관련 설정만 AI에게 다시 준다

여기서 핵심은 이거야. 환경 셋업 얘기를 거의 하지 않는다. 이미 Docker가 환경을 고정해주고 있으니까. 나는 비즈니스 로직, 파일 위치, 원하는 동작만 말하면 돼.

예전 프롬프트는 이런 느낌이었어.

Python 3.11이고 FastAPI를 사용 중이야.
PostgreSQL은 로컬 5432 포트에서 실행 중이고,
SQLAlchemy를 쓰고 있고,
users 테이블에는 id, name, email 컬럼이 있어.
페이지네이션 기능을 추가해줘.
limit과 offset을 쓸 거고...

요즘은 이렇게 줄었어.

FastAPI의 /users API에 limit, offset 기반 페이지네이션을 추가해줘.
DB 접근 코드는 기존 SQLAlchemy 세션을 그대로 사용해.
파일은 /app/api/users.py야.

훨씬 낫지 않나. 물론 프로젝트 초반에는 AI한테 구조를 한 번 알려줘야 해. 하지만 한 번 정리해두면 그다음부터는 짧게 대화할 수 있어. 나는 이걸 “AI한테 매번 회사 소개하지 않기”라고 부르는데, 꽤 마음에 드는 표현이야.

프롬프트도 Docker 기준으로 짧게 템플릿화해두면 편하다

나는 자주 쓰는 프롬프트를 몇 개 메모장에 넣어두고 돌려써. 거창한 프롬프트 엔지니어링은 아니고, 그냥 일하면서 손에 익은 문장들이야.

상황 내가 자주 쓰는 문장
기능 추가 Docker Compose 환경이고 app, db, cache 서비스가 떠 있어. 기존 구조를 유지하면서 이 기능만 추가해줘.
에러 수정 아래 에러는 컨테이너 안에서 발생했어. 원인을 짚고, 수정할 파일과 코드만 알려줘.
DB 연결 문제 DB는 localhost가 아니라 docker-compose 서비스 이름 db로 접근해야 해. 이 기준으로 설정을 고쳐줘.
테스트 작성 컨테이너 안에서 pytest로 실행할 테스트 코드를 작성해줘. 외부 서비스는 mock 처리해줘.

이렇게 몇 문장만 정해둬도 AI 답변 품질이 꽤 안정돼. 나는 LLM을 쓸 때 “많이 설명하면 잘 알아듣겠지”보다 “헷갈릴 여지를 줄여야 잘 알아듣는다” 쪽에 더 가깝다고 봐. 사람도 그렇잖아. 회의 시간에 배경 설명만 20분 들으면 정작 뭘 해야 하는지 흐려질 때가 있거든.

이 방식이 특히 잘 맞는 사람들

내가 써보니 이 방식은 아무나 무조건 써야 한다기보다는, 이런 사람한테 특히 잘 맞아.

    • ChatGPT, Claude, Cursor 같은 AI 코딩 도구를 자주 쓰는 개발자
    • 로컬 환경 충돌 때문에 자주 시간을 뺏기는 사람
    • LLM API 비용이 은근히 신경 쓰이는 개인 개발자나 프리랜서
    • 팀원마다 개발 환경이 달라서 “제 컴퓨터에서는 되는데요”를 자주 듣는 팀
    • AI에게 환경 설명을 반복하는 게 슬슬 귀찮아진 사람

반대로 아주 작은 스크립트 하나 만들거나, 일회성으로 코드 조각만 받아볼 때는 Docker까지 챙기는 게 오히려 번거로울 수 있어. 나도 그런 경우엔 그냥 로컬에서 바로 돌려. 도구는 상황에 맞게 써야지, 도구에 끌려다니면 또 피곤해지거든.

그래도 백엔드 API, DB 연동, Redis, Queue처럼 여러 서비스가 붙는 순간부터는 Docker Compose를 깔아두는 쪽이 훨씬 마음 편했어. 특히 AI랑 같이 코딩할 때는 재현 가능한 환경이 생각보다 큰 무기가 되더라. AI가 틀려도 내가 바로 검증할 수 있고, 내가 설명을 덜 해도 되고, 에러 로그도 깔끔하게 모을 수 있으니까.

내가 느낀 총평은 이거야

AI 코딩에서 Docker는 단순한 배포 도구가 아니라, 토큰을 아끼는 작업 환경 정리 도구에 가깝다고 느꼈어. 예전에는 Docker를 “운영 환경과 개발 환경을 맞추기 위한 도구” 정도로 생각했는데, 요즘은 조금 다르게 봐. AI와 대화할 때 반복 설명을 줄여주는 장치이기도 하더라고.

기본 이미지를 만들어두고, Docker Compose로 서비스 이름을 고정하고, Dockerfile 레이어 캐싱을 챙기고, .dockerignore로 쓸데없는 파일을 빼는 것. 사실 하나하나는 대단한 기술이 아니야. 그런데 이걸 AI 코딩 흐름에 붙이면 체감이 꽤 커져. 프롬프트가 짧아지고, 테스트가 빨라지고, 에러 분석이 명확해져.

AI 코딩 어시스턴트를 자주 쓰는데 매번 환경 설명하느라 지친 개발자라면, 이 방식은 한번 해볼 만해. 특히 LLM API 비용을 직접 내고 있거나, 사이드 프로젝트를 여러 개 돌리는 사람이라면 더더욱. 나도 처음엔 “이걸 굳이?” 했다가 지금은 거의 기본 세팅처럼 쓰고 있어. 뭐랄까, 여행 갈 때 늘 챙기는 멀티 어댑터 같은 느낌이야. 없어도 어떻게든 되지만, 있으면 마음이 참 편한 그런 물건 말이지.

댓글