Study/Docker

[Docker]로컬 개발 환경 세팅(vscode, IntelliJ 그리고 devcontainer.json) (3)

seomj 2026. 2. 4. 20:00

서론

포트 충돌을 이전에 해결하지 못했다.현재까지 진행한 내용은 내가 생각했던 개발 환경 세팅과는 다른 방향이었다. 

이에 대해 고민하며 지금까지의 접근법을 정리하고자 한다. 


지금까지의 상황

내가 구현하고 싶었던 환경

  • 컨테이너를 통한 개발 환경 통일
  • 컨테이너 내부에서 개발 진행 후 빌드 및 디버깅

 

1차로 구현한 환경 (https://seomj74.tistory.com/390)

  • 컨테이너를 단순히 실행 도구(Runtime)로만 사용
  • docker-compose up 한 번에 모든 서비스가 다 동작
  • Front
    • 이미지 빌드에서 Node.js 실행 환경만 갖춘 상태
    • /app/node_modules 익명 볼륨을 생성
      • 운영체제 간의 라이브러리 충돌 방지(호스트와 컨테이너)
    • 컨테이너가 생성될 때마다 npm install을 체크 (docker-compose command)
      • 컨테이너가 생성될 때마다 package.json을 읽어 라이브러리를 설치
      • 기동 속도 저하
  • Back
    • 이미지 빌드에서 ubuntu 위에 Java만 설치하여 순수 JDK 환경 상태
      • Ubuntu 이미지 사용 (ubuntu:22.04)
        • 자바를 실행하기에는 불필요한 패키지가 많아 무거움
    • 호스트 캐시( ~/.gradle:/root/.gradle )를 공유
      • 초기 구동이 빠름
      • 파일 잠금(Lock) 충돌이나 권한 문제  발생
    • ./gradlew 파일을 실행
      • /app 폴더에 있는 코드를 컴파일하고 빌드하여 Spring Boot 앱 실행
  • 문제
    • JDK는 컨테이너에, 캐시와 코드는 호스트에 있는 것을 사용
      • 개발 환경 통일과 거리가 멈

 

2차로 구현한 환경 (https://seomj74.tistory.com/391)

  • docker-compose up을 진행하지만 실행은 선택적
  • WSL 2 기반 환경 통일
    • WSL 2 내부에 IDE의 핵심 엔진 설치
    • IntelliJ와 WebStorm에서 WSL 2 엔진과 통신하며 진행
  • Front
    • 이미지 빌드 시 npm install 진행
      • 소스 코드가 변해도 package.json이 수정되지 않으면 속도가 빠름
      • 수정 시 컨테이너 내 npm install 혹은 이미지 재빌드 필요
    • 기존에 생성되던 익명 볼륨을 기명 볼륨으로 바꾸어 재사용성을 높임
    • 호스트와 컨테이너 분리 
      • 호스트의 node_modules가 컨테이너를 덮어 쓰지 않음으로써 컨테이너 내 빌드 안정성 확보
  • Back
    • 이미지 빌드
      • 경량화 이미지 사용 (eclipse-temurin:21-jdk-alpine)
      • gradlew, build.gradle 등 설정 파일만 먼저 복사하여 빌드 도구 캐싱
        • build.gradle이 변경되면 ./gradlew build 혹은 이미지 재빌드 필요
      • Gradle 실행 엔진 다운로드
    • profiles를 통해 선택적 실행
      • DB, Redis 등 필요한 도구만 띄워두고 로컬 개발 진행 후 컨테이너를 띄워 테스트
        • 리소스 관리 효율적 
    • 빌드 결과물과 캐시 격리를 통해 컴파일 충돌 방지
  • 문제
    • package.json이나 build.gradle이 변경될 때마다 이미지를 다시 빌드하거나 컨테이너 내부에서 수동 설치해야 함

 

정리

1차와 2차의 경우 내가 구현하고자 하는 방향과 많이 다르다는 것을 알 수 있다.

본인의 경우, 컨테이너가 개발 환경 역할만 하길 바랬기에 컨테이너를 띄우고 내부에 접속해서 빌드, 디버깅이 가능한 환경을 바랬다. Java, OS, 의존성 버전 등을 맞춰야 추후 "내 컴퓨터에선 되는데?"를 방지할 수 있을 것이라 생각했다.

 

하지만, 이는 앱 자체를 실행하는 환경에 가깝다.

프론트의 경우에는 HMR(Hot Module Replacement)로 인해 빌드없이 소스 코드 수정만으로 웹 사이트에서 즉시 확인이 가능했다.

백엔드의 경우에는 수정 시 마다 빌드가 필요했다. 컨테이너가 실행 중인 상황이며 이미 포트를 점유하고 있기에 개발자가 내부에서 수동 빌드 및 실행을 하려고 하면 포트 충돌이 발생했다.

 

개선

이를 해결하기 위해 다시 고민해보기 시작했다. 알아보던 와중 Dev Container에서 devcontainer.json이라는 게 존재한다는 것을 발견했다.

devcontainer.json

정의된 도구와 런타임 스택에 대한 개발 컨테이너를 구성하는 데 필요한 모든 메타데이터와 설정 값을 가진다.

 

더 자세한 항목들이 궁금하면 아래 링크를 참고하길 바란다.

https://containers.dev/implementors/json_reference/

 

Dev Container metadata reference

The devcontainer.json file contains any needed metadata and settings required to configure a development container for a given well-defined tool and runtime stack. It can be used by tools and services that support the dev container spec to create a develop

containers.dev

 

Dockerfile은 OS와 런타임을 어떻게 구성할지를 정의하는 파일로, 설계도와 같은 역할 

docker-compose는 서비스 간의 네트워크와 데이터 저장소를 연결하는 역할

devcontainer.json은 IDE가 개발자를 위해 수행할 동작을 정의

 

적용

3차로 구현할 환경

  • 실행하지 않고 컨테이너만 유지하여 개발 환경만 제공 
  • 빌드를 도커에서 진행하지 않고 개발자가 필요할 때 IDE에서 직접 진행
  • Front
    • 이미지 빌드
      • npm install을 진행하여 frontend_node_modules 볼륨에 저장
      • bash와 git 등 개발에 필요한 도구들을 미리 설치
      • 대기 상태 유지
    • devcontainer.json을 통해 VS Code나 WebStorm/IntelliJ같은 IDE가 컨테이너 내부의 서비스에 직접 접
    • 패키지를 추가해야 하면 IDE 터미널에서 npm install 실행
    • npm run dev를 통해 개발자아 원할 때 앱 실행 
  • Back
    • 이미지 빌드
      • eclipse-temurin:21-jdk를 사용하여 git, curl, procps 등을 설치
      • ENV GRADLE_USER_HOME=/home/gradle_cache와 기명 볼륨(gradle_cache)을 연결
    • postCreateCommand를 통해 컨테이너가 생성되자마자 실행 권한 부여와 Gradle 엔진 확인
    • IntelliJ가 컨테이너 내부로 접속하여 소스 코드를 인덱싱하고 직접 명령

 

코드

Front devcontainer.json

{
  "name": "Frontend Dev Environment",
  "dockerComposeFile": "../../docker-compose.yml",
  "service": "frontend",
  "workspaceFolder": "/app"
  "forwardPorts": [5173]
}

Back devcontainer.json

{
  "name": "Backend Dev Environment",
  "dockerComposeFile": "../../docker-compose.yml",
  "service": "backend",
  "workspaceFolder": "/app",

  "context": "../../",

  "customizations": {
    "jetbrains": {
      "backend": "IntelliJ"
    }
  },

  "postCreateCommand": "chmod +x gradlew && ./gradlew --version",

  "forwardPorts": [8080, 5005, 3306],
  "remoteUser": "root"
}

docker-compose.yaml

services:
  backend:
    build:
      context: ../../
      dockerfile: ./Infra/local_dev/backend.Dockerfile
    volumes:
      - ../../Backend:/app
      - /app/build
      - /app/.gradle
      - gradle_cache:/home/gradle_cache
    ports:
      - "8080:8080"
      - "5005:5005"
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/testdb
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=1234
      - SPRING_DATA_REDIS_HOST=redis
    depends_on:
      - mysql
      - redis
  
  frontend:
    build:
      context: ../../
      dockerfile: ./Infra/local_dev/frontend.Dockerfile
    volumes:
      - ../../Frontend:/app
      - frontend_node_modules:/app/node_modules
    ports:
      - "5173:5173"

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=1234
      - MYSQL_DATABASE=testdb
    volumes:
      - mysql_data:/var/lib/mysql

  redis:
    image: redis:7.2-alpine
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"

volumes:
  mysql_data:
  frontend_node_modules:
  redis_data:
  gradle_cache:

Front Dockerfile

FROM node:20-alpine
WORKDIR /app

RUN apk add --no-cache bash git

COPY Frontend/package*.json ./
RUN npm install

COPY Frontend/ .

CMD ["tail", "-f", "/dev/null"]

Back Dockerfile

FROM eclipse-temurin:21-jdk

WORKDIR /app

RUN apt-get update && apt-get install -y \
    git \
    bash \
    curl \
    procps \
    && rm -rf /var/lib/apt/lists/*

ENV GRADLE_USER_HOME=/home/gradle_cache

COPY Backend/gradlew .
COPY Backend/gradle gradle
COPY Backend/build.gradle Backend/settings.gradle ./

RUN chmod +x gradlew
RUN ./gradlew --version --no-daemon

COPY Backend/ .

CMD ["tail", "-f", "/dev/null"]

 

VS Code

VS Code에서 .devcontainer 디렉터리가 있는 위치를 오픈한다.

참고로 .devcontainer가 오픈한 폴더 바로 아래에 있지 않으면 못 찾으니 유의하도록!

 

dev container 플러그인을 설치해야 한다. 이는 설치되어 있다는 가정하에 설명한다. 설치하지 않았다면 플러그인에서 검색해서 그냥 설치해주면 끝이다. (간단)

 

그러면 오른쪽 하단에 Reopen in Container라고 뜬다. 이걸 클릭하자. 

 

혹은 Ctrl + Shift + P로 접근해서 Rebuild and Reopen in Container를 선택하자.

 

그러면 아래와 같이 devcontainer.json을 기준으로 어떤 것을 오픈할 것인지 묻는다.

 

Front

Front 컨테이너로 접속해서 실행하면 된다.

npm run dev -- --host 0.0.0.0

 

Back

필요한 플러그인을 설치하고 Run하면 된다.

 

WebStorm & IntelliJ

참고로 WebStorm과 IntelliJ는 계속 시도했으나 접속이 안된다. Ultimate 버전에서만 제공하는 기능으로 알고 있다.

https://www.jetbrains.com/help/idea/connect-to-devcontainer.html 

 

Dev Container overview | IntelliJ IDEA

 

www.jetbrains.com

 

devcontainer.json에서 우클릭 후 Dev Containers에서 실행하면 된다. 아마 Ultimate 버전을 사용 중이라면 무리없이 접근이 되지 않을까 싶다.

 

 

 

참고

https://benggri.tistory.com/137