도커 웹개발, 로컬 환경을 바꾼 한 달 사용기

profile_image
작성자 컨테이너개발자 서해준
댓글 0건 조회 44회

새 프로젝트를 받을 때마다 Node.js 버전부터 데이터베이스 설치 상태까지 다시 맞추느라 지쳤습니다. 제 컴퓨터에서는 잘 실행되는데 동료의 컴퓨터에서는 오류가 나는 상황도 반복됐습니다. 그래서 한 달 동안 개인 웹개발 프로젝트와 소규모 협업에 도커(Docker)를 실제로 적용해 보았습니다.

처음에는 컨테이너, 이미지, 볼륨처럼 낯선 용어가 많아 오히려 일이 늘어난다고 느꼈습니다. 하지만 환경 구성을 코드로 남기기 시작하자 새 컴퓨터에서도 같은 개발 환경을 훨씬 빠르게 재현할 수 있었습니다. 이 글에서는 기능 설명만 나열하지 않고, 제가 직접 겪은 시행착오와 장단점, 초보자가 시간을 아낄 수 있는 사용 순서를 중심으로 풀어보겠습니다.

설치보다 어려웠던 첫 일주일의 시행착오

이미지와 컨테이너를 구분하니 흐름이 보였습니다

도커를 처음 실행했을 때 가장 혼란스러웠던 부분은 이미지와 컨테이너의 차이였습니다. 실제로 사용해 보니 이미지는 실행 환경을 만들기 위한 설계도이고, 컨테이너는 그 설계도로 시작한 실행 인스턴스라고 이해하는 편이 쉬웠습니다. 이미지를 수정하는 대신 Dockerfile을 고치고 다시 빌드해야 한다는 원칙을 받아들이자 작업 흐름이 단순해졌습니다.

첫 연습 대상으로는 복잡한 서비스를 고르지 않고 간단한 Node.js 웹 서버를 선택했습니다. 프로그래밍의 기본 개념이 아직 낯설다면 프로그래밍 용어 설명을 먼저 읽고, 소스 코드와 실행 환경이 서로 다른 층이라는 점을 잡아두는 것도 도움이 됩니다. 저는 애플리케이션 코드, 런타임, 외부 패키지가 어떤 관계인지 종이에 그린 뒤 Dockerfile을 작성하니 명령어를 무작정 외울 때보다 이해가 빨랐습니다.

  • Dockerfile: 어떤 운영체제 기반 이미지와 런타임, 패키지를 사용할지 기록합니다.
  • 이미지: Dockerfile을 빌드해 만든 실행 가능한 묶음이며 일반적으로 직접 수정하지 않습니다.
  • 컨테이너: 이미지를 바탕으로 실행되는 격리된 프로세스입니다. 중지하거나 지워도 원본 이미지는 남습니다.
  • 포트 연결: 컨테이너 내부 포트를 내 컴퓨터의 포트에 연결해야 브라우저에서 서버를 열 수 있습니다.
  • 볼륨: 데이터베이스 파일처럼 컨테이너를 삭제해도 유지해야 할 데이터를 외부에 보관합니다.

처음 만든 Dockerfile에서 시간을 잡아먹은 문제

제가 처음 작성한 Dockerfile은 소스 전체를 먼저 복사한 뒤 패키지를 설치하는 구조였습니다. 코드 한 줄만 바꿔도 의존성 설치 단계가 다시 실행되어 빌드가 지나치게 느렸습니다. package.json과 잠금 파일을 먼저 복사하고 패키지를 설치한 다음, 나머지 소스를 복사하도록 순서를 바꾸자 캐시가 활용되어 반복 빌드 시간이 눈에 띄게 줄었습니다.

또 하나의 실수는 node_modules와 로그 파일까지 빌드 컨텍스트에 포함한 것이었습니다. .dockerignore 파일을 작성하지 않으면 불필요한 파일 전송 때문에 빌드가 느려지고 이미지에 민감한 파일이 섞일 위험도 있습니다. 저는 .git, node_modules, 환경 변수 파일, 테스트 결과, 임시 로그를 제외한 뒤 이미지 크기와 빌드 시간을 함께 확인했습니다.

  1. 작은 웹 서버 하나를 준비하고 사용 중인 런타임 버전을 확인합니다.
  2. 프로젝트 루트에 Dockerfile과 .dockerignore를 만듭니다.
  3. 잠금 파일을 포함해 의존성 설치 단계가 매번 달라지지 않게 합니다.
  4. 이미지를 빌드한 뒤 포트를 연결해 컨테이너를 실행합니다.
  5. 브라우저 접속뿐 아니라 컨테이너 로그와 종료 코드도 확인합니다.
  6. 코드 한 줄을 수정해 두 번째 빌드가 실제로 빨라지는지 비교합니다.
사용 팁: 첫날부터 프론트엔드, 백엔드, 데이터베이스를 모두 컨테이너로 옮기지 않는 편이 좋았습니다. 웹 서버 하나를 빌드하고 로그를 읽는 데 익숙해진 뒤 서비스를 추가해야 오류가 어느 층에서 발생했는지 찾기 쉽습니다.

실제 웹개발 프로젝트에서 체감한 장점과 불편

팀 환경을 맞추는 시간은 줄고 파일 처리는 무거워졌습니다

한 달 동안 가장 크게 체감한 장점은 온보딩 속도였습니다. 이전에는 README에 런타임 버전, 데이터베이스 계정, 설치 명령을 길게 적어도 누군가 한 단계를 놓치곤 했습니다. 도커 컴포즈 파일을 추가한 뒤에는 환경 변수 예시 파일을 복사하고 정해진 명령을 실행하는 것으로 API 서버와 데이터베이스를 함께 시작할 수 있었습니다.

반면 모든 작업이 빨라진 것은 아닙니다. 운영체제와 프로젝트 구성에 따라 소스 폴더를 연결한 상태에서 많은 작은 파일을 읽을 때 응답이 느려졌고, 편집 후 자동 새로고침이 늦게 반응하기도 했습니다. 그래서 의존성 폴더까지 무조건 호스트와 공유하지 않고, 소스 코드만 필요한 범위로 연결하는 방식으로 조정했습니다. 도커가 느리다고 단정하기보다 어떤 디렉터리를 마운트했고 어떤 프로세스가 파일을 감시하는지 살피는 것이 중요했습니다.

직접 써본 항목좋았던 점불편했던 점제가 적용한 방법
런타임 관리프로젝트별 Node.js 버전을 분리하기 쉬움기반 이미지 선택이 낯설 수 있음버전 태그를 명시하고 팀에서 하나로 고정
데이터베이스로컬 설치 없이 빠르게 시작 가능볼륨을 지우면 개발 데이터도 사라질 수 있음삭제 명령 실행 전 볼륨 목록을 확인
팀 공유환경 차이로 생기는 오류가 감소컴포즈 설정을 함께 관리해야 함README에 실행·중지·초기화 명령을 구분
코드 반영볼륨 연결로 즉시 수정 가능파일 감시 속도가 느려질 수 있음공유 경로를 최소화하고 생성 파일은 제외
문제 추적서비스별 로그를 분리해 볼 수 있음호스트와 컨테이너 로그를 혼동하기 쉬움서비스 이름과 요청 시간을 함께 기록

도커 컴포즈는 편리했지만 설정을 숨겨주지는 않았습니다

백엔드와 PostgreSQL을 따로 실행할 때는 명령이 길어지고 네트워크 이름도 자주 잊었습니다. 컴포즈 파일에 서비스, 포트, 환경 변수, 볼륨을 기록하자 한 명령으로 두 서비스를 시작할 수 있어 편했습니다. 특히 데이터베이스가 준비되기 전에 API가 접속을 시도해 실패하는 문제를 발견하면서, 단순한 시작 순서와 실제 서비스 준비 상태가 다르다는 사실도 배웠습니다.

서비스 이름을 호스트명으로 사용할 수 있다는 점도 유용했습니다. 다만 컨테이너 안에서 localhost는 내 컴퓨터가 아니라 그 컨테이너 자신을 가리킵니다. API 컨테이너가 데이터베이스에 연결할 때 localhost를 입력해 접속 오류가 났고, 데이터베이스 서비스 이름으로 바꾼 뒤 해결했습니다. 코딩이라는 활동의 범위를 넓게 이해하려면 코딩의 개념과 의미도 참고할 만하지만, 실제 웹개발에서는 코드 작성뿐 아니라 네트워크와 실행 환경을 설계하는 능력도 중요했습니다.

  • 서비스 이름을 명확하게 짓기: app, db처럼 역할이 바로 보이는 이름을 사용하면 로그 확인과 내부 통신이 편합니다.
  • 환경 변수 분리하기: 비밀번호를 컴포즈 파일에 직접 적지 말고 로컬 전용 환경 파일과 배포용 비밀 관리 수단을 구분합니다.
  • 헬스 체크 활용하기: 프로세스가 실행됐다는 사실뿐 아니라 요청을 받을 준비가 되었는지 검사합니다.
  • 볼륨 이름 지정하기: 어떤 프로젝트의 데이터인지 알 수 있도록 이름을 정하면 실수로 삭제할 가능성이 줄어듭니다.
  • 재시작만 반복하지 않기: 문제가 발생하면 서비스 로그, 포트 충돌, 환경 변수, 네트워크 순서로 원인을 좁혀갑니다.
컨테이너에서 접속 오류가 나면 코드부터 고치기 전에 세 가지를 확인했습니다. 프로세스가 실행 중인지, 올바른 포트에서 대기하는지, 상대 서비스 이름으로 통신하는지를 순서대로 보면 불필요한 수정이 줄어듭니다.

오늘 30분으로 만드는 재현 가능한 개발 환경

작은 프로젝트 하나만 컨테이너로 옮겨보세요

도커를 계속 사용할지 판단하려면 강의를 오래 보는 것보다 이미 실행되는 작은 프로젝트를 한 번 옮겨보는 편이 빠릅니다. 저는 할 일 목록 API를 대상으로 삼아 기존 로컬 실행 시간과 컨테이너 실행 시간을 비교했습니다. 첫 설정에는 시간이 들었지만 Dockerfile과 실행 명령을 저장해 두자 프로젝트를 며칠 쉬었다 다시 열어도 환경을 기억해낼 필요가 없었습니다.

여기서 목표는 완벽한 운영 배포 이미지가 아닙니다. 다른 폴더에 프로젝트를 새로 복제해도 같은 명령으로 실행되는 상태를 만드는 것이 첫 기준입니다. 코드를 작성하는 과정과 실행 절차를 함께 기록한다는 관점은 프로그래밍 관련 개념을 확장해 이해하는 데도 도움이 됩니다.

  1. 0~5분: 현재 프로젝트의 실행 명령, 런타임 버전, 노출 포트를 메모합니다.
  2. 5~12분: 가벼운 공식 기반 이미지를 고르고 작업 폴더와 의존성 설치 명령을 Dockerfile에 작성합니다.
  3. 12~17분: node_modules, .git, 환경 변수 파일, 로그를 .dockerignore에 추가합니다.
  4. 17~22분: 이미지를 빌드하고 포트를 연결해 실행한 뒤 브라우저 또는 API 도구로 응답을 확인합니다.
  5. 22~26분: 컨테이너 로그를 열어 요청 기록과 오류가 정상적으로 보이는지 확인합니다.
  6. 26~30분: 컨테이너를 삭제하고 같은 이미지로 다시 실행해 결과가 동일한지 시험합니다.

한 달 사용 후에도 지키는 운영 습관

익숙해진 뒤에는 이미지 태그를 무조건 latest로 두지 않고 프로젝트가 기대하는 버전을 명시했습니다. 기반 이미지가 바뀌면 어제까지 되던 빌드가 오늘 달라질 수 있기 때문입니다. 또한 개발용 이미지에 디버깅 도구를 넣는 것은 편리하지만 운영 이미지까지 그대로 사용하면 용량과 공격 표면이 커질 수 있어, 개발 단계와 실행 단계를 나누는 멀티 스테이지 빌드도 시험했습니다.

라이선스와 비용도 설치 전에 확인할 부분입니다. 개인 학습, 회사 규모, 상업적 사용 조건에 따라 데스크톱 제품의 이용 범위가 달라질 수 있으므로 오래된 블로그의 가격표를 그대로 믿지 않고 공식 약관을 확인하는 습관이 필요합니다. 도커 엔진, 데스크톱 앱, 이미지 저장소는 서로 역할과 과금 기준이 다를 수 있다는 점도 구분해야 합니다.

  • 비밀 정보 검사: 이미지 빌드 전에 .env, 인증서, 클라우드 키가 복사되지 않는지 확인합니다.
  • 고정된 의존성 사용: 잠금 파일을 이미지에 포함하고 빌드 과정에서 임의 업데이트가 발생하지 않게 합니다.
  • 불필요한 권한 줄이기: 가능하면 애플리케이션을 루트 사용자가 아닌 별도 사용자로 실행합니다.
  • 이미지 크기 관찰: 패키지 캐시와 빌드 산출물이 최종 이미지에 남지 않도록 단계를 분리합니다.
  • README 갱신: 실행 명령뿐 아니라 중지, 로그 확인, 데이터 초기화 방법을 적습니다.
  • 삭제 전 대상 확인: 볼륨 제거 명령은 편리하지만 복구가 어려우므로 프로젝트 이름과 백업 여부를 먼저 봅니다.

지금 바로 터미널을 열고 자주 쓰는 웹개발 프로젝트의 런타임 버전, 실행 명령, 포트 번호 세 가지를 README에 적어보세요. 그다음 그 세 줄을 Dockerfile로 옮겨 이미지를 한 번 빌드하고, 기존 방식과 동일한 화면이나 API 응답이 나오는지 확인하면 됩니다. 오늘의 행동은 도커 기능을 전부 배우는 것이 아니라, 내 컴퓨터 밖에서도 반복 가능한 첫 실행 환경 하나를 만드는 것입니다.

도커 웹개발, 로컬 환경을 바꾼 한 달 사용기

댓글목록

등록된 댓글이 없습니다.