Files
ITAM/doc_readme2.md

19 KiB

ITAM 도커라이징 최종 재현 가이드

1. 문서 목적

이 문서는 현재 Git 저장소에 올라간 파일만 가지고, 지금과 동일한 수준으로 ITAM 시스템을 도커라이징하고 실행하는 절차를 처음부터 끝까지 정리한 최종 가이드다.

이 문서만 읽어도 아래 목표를 달성할 수 있게 작성한다.

  1. 현재 저장소 구조를 이해한다.
  2. 왜 이렇게 도커라이징했는지 판단 근거를 안다.
  3. WSL2 기반으로 실제 스택을 기동한다.
  4. 외부 MySQL에서 내부 MySQL 컨테이너로 초기 데이터를 bootstrap 한다.
  5. 프런트 8080과 백엔드 3000이 모두 정상 동작하는지 검증한다.
  6. 재초기화, 재기동, 장애 확인까지 수행한다.

이 문서는 최종 성공 구조 기준이다. 실패 기록은 doc_readme.md를 본다.


2. 최종 목표 구조

현재 최종 구조는 아래 4개 서비스/역할로 나뉜다.

  1. frontend: Vite 개발 서버 컨테이너, 포트 8080
  2. backend: Express API 서버 컨테이너, 포트 3000
  3. db: MySQL 8 컨테이너, 포트 3306
  4. db-bootstrap: 외부 MySQL -> 내부 MySQL로 1회성 복제 수행 후 종료되는 도우미 컨테이너

논리 흐름은 다음과 같다.

브라우저 -> frontend:8080 -> Vite proxy -> backend:3000 -> db:3306
                                      \
                                       -> /uploads -> backend 정적 경로

초기 1회 기동 시
외부 MySQL(.env) -> db-bootstrap -> 내부 MySQL(db)

3. 왜 이 구조를 선택했는가

이 저장소는 처음부터 운영형 정적 배포 앱이 아니었다. 실제 구조는 다음과 같았다.

  1. 프런트는 Vite 개발 서버가 따로 돈다.
  2. 백엔드는 Express API가 따로 돈다.
  3. 프런트는 상대 경로 /api를 호출한다.
  4. 백엔드는 프런트의 dist를 서빙하지 않는다.

따라서 내일 바로 시연 가능한 수준까지 빠르게 안정화하려면 아래 전략이 가장 맞다.

  1. 프런트를 Vite dev server 그대로 컨테이너화한다.
  2. 백엔드를 별도 컨테이너로 유지한다.
  3. DB는 MySQL 8 컨테이너로 묶되, 초기 데이터는 외부 DB에서 복제한다.
  4. 프런트 프록시는 컨테이너 네트워크 서비스명 backend로 붙게 한다.

즉, 현재 구조는 "개발형 구조를 Docker로 재현한 시연/개발용 Compose"다.


4. 저장소 내 최종 관련 파일 목록

현재 도커라이징과 직접 관련된 핵심 파일은 아래와 같다.

  1. .dockerignore
  2. Dockerfile.frontend
  3. Dockerfile.backend
  4. docker-compose.yaml
  5. start_docker_wsl.ps1
  6. stop_docker_wsl.ps1
  7. start_docker_wsl.bat
  8. stop_docker_wsl.bat
  9. docker/mysql/init/README.md
  10. server.js
  11. vite.config.ts

각 파일 역할은 다음과 같다.

4.1 .dockerignore

Docker build context에서 제외할 파일을 정의한다.

주요 제외 대상은 다음과 같다.

  1. node_modules
  2. dist
  3. build
  4. .git
  5. .env
  6. uploads
  7. *.xlsx
  8. *.log

4.2 Dockerfile.frontend

프런트 컨테이너 이미지 정의다.

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 8080

CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

이 이미지는 Vite dev server를 컨테이너에서 띄우기 위한 것이다.

4.3 Dockerfile.backend

백엔드 컨테이너 이미지 정의다.

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 3000

CMD ["npm", "run", "server"]

4.4 docker-compose.yaml

전체 스택의 핵심 파일이다.

현재 최종 구성은 다음 논리를 가진다.

  1. db는 MySQL 8 내부 DB다.
  2. db-bootstrap은 외부 DB 데이터를 내부 DB로 1회 복제한다.
  3. backend는 내부 db에 붙는다.
  4. frontendbackend 서비스명으로 프록시한다.

4.5 start_docker_wsl.ps1

Windows에서 WSL 경유로 Docker Compose를 안전하게 기동하는 진입점이다.

핵심은 다음 두 가지다.

  1. 프로젝트 Windows 경로를 wslpath로 WSL 경로로 바꾼다.
  2. 그 경로로 이동한 뒤 docker compose up --build -d를 수행한다.

4.6 stop_docker_wsl.ps1

같은 방식으로 WSL 내부에서 docker compose down을 수행해 스택을 안전하게 내린다.

4.7 start_docker_wsl.bat, stop_docker_wsl.bat

더블클릭 또는 간단 실행용 래퍼다. 내부적으로 PowerShell 스크립트를 호출한다.

4.8 server.js

중요 포인트는 다음 두 가지다.

  1. dotenv.config();를 사용한다.
  2. dotenv.config({ override: true })를 사용하지 않는다.

이 차이로 Compose 환경변수 DB_HOST=db.env보다 우선하도록 보장한다.

4.9 vite.config.ts

현재 프록시는 환경변수 기반으로 동작한다.

const proxyTarget = process.env.VITE_DEV_PROXY_TARGET || 'http://localhost:3000';

로컬 PC에서 직접 Vite를 띄우면 기본값 http://localhost:3000을 쓴다. 컨테이너에서는 Compose가 http://backend:3000을 주입한다.


5. 현재 최종 docker-compose.yaml 구조 설명

아래는 실제 동작 관점에서 읽어야 할 핵심 내용이다.

5.1 db 서비스

역할:

  1. 내부 MySQL 데이터 저장소
  2. 앱이 최종적으로 붙는 DB

핵심 설정:

  1. 이미지: mysql:8.0
  2. DB 이름: itam
  3. 앱 계정: itam_admin
  4. 데이터 볼륨: itam_mysql_data
  5. healthcheck 사용

healthcheck는 mysqladmin ping으로 동작하며, backenddb-bootstrap은 이 상태를 기다린다.

5.2 db-bootstrap 서비스

역할:

  1. 외부 원본 DB에서 내부 db로 초기 데이터 복제
  2. 1회성 작업 후 종료

핵심 포인트:

  1. .env를 읽어 외부 DB 접속 정보를 가져온다.
  2. 내부 dbasset_core 테이블이 이미 존재하면 아무 것도 하지 않고 종료한다.
  3. 그렇지 않으면 mysqldump | mysql 파이프라인으로 복제한다.
  4. restart: "no" 이므로 정상 종료 후 반복 실행하지 않는다.

또한 source DB와 target DB 변수는 분리돼 있다.

  1. source: SOURCE_DB_*
  2. target: TARGET_DB_*

이 구조로 외부 원본 DB 자격증명과 내부 컨테이너 DB 자격증명이 섞이지 않는다.

5.3 backend 서비스

역할:

  1. Express API 제공
  2. 내부 db에 연결
  3. /uploads 정적 제공

핵심 포인트:

  1. env_file: .env를 유지하지만,
  2. Compose environment에서 DB_HOST=db, DB_PORT=3306, DB_USER=itam_admin, DB_PASS=itam1234, DB_NAME=itam를 다시 지정한다.
  3. depends_ondb healthy와 db-bootstrap 성공 종료를 모두 기다린다.

즉, 백엔드는 DB bootstrap이 끝난 뒤 시작한다.

5.4 frontend 서비스

역할:

  1. Vite dev server 제공
  2. 브라우저 요청 /api, /uploadsbackend로 프록시

핵심 포인트:

  1. VITE_DEV_PROXY_TARGET: http://backend:3000
  2. CHOKIDAR_USEPOLLING: "true"
  3. npm run dev -- --host 0.0.0.0

중요한 이유는 컨테이너 안의 localhost가 호스트의 localhost가 아니기 때문이다.


6. 사전 준비 조건

이 저장소를 지금처럼 기동하려면 다음 전제가 필요하다.

6.1 운영체제와 런타임

  1. Windows
  2. WSL2 Ubuntu 설치 및 실행 중
  3. Docker CLI가 WSL 내부에서 동작 가능

권장 확인 명령:

wsl -l -v
wsl sh -lc "docker --version"

6.2 .env 파일

현재 최종 구조는 "첫 기동 시 외부 DB에서 내부 DB로 bootstrap" 하는 방식이므로 .env가 반드시 필요하다.

최소한 다음 값은 외부 원본 DB를 가리켜야 한다.

DB_HOST=<external-mysql-host>
DB_PORT=3306
DB_USER=<external-db-user>
DB_PASS=<external-db-password>
DB_NAME=itam

주의:

  1. .envdb-bootstrap이 외부 원본 DB에 접속할 때 사용한다.
  2. backend는 최종적으로 내부 db 컨테이너를 쓰므로, 런타임에서는 Compose environment가 우선한다.

6.3 한글 경로 주의

현재 프로젝트 경로는 한글과 공백을 포함한다.

c:\Users\user\Desktop\안건 파일\itam

이 때문에 Docker 관련 명령은 수동으로 경로를 조립하지 말고, start_docker_wsl.ps1 / stop_docker_wsl.ps1을 우선 사용해야 한다.


7. 첫 기동 절차

이 절차는 "Git에서 소스를 받은 뒤 처음 올리는 경우" 기준이다.

7.1 저장소 준비

  1. 저장소를 받는다.
  2. .env가 올바른 외부 원본 DB를 가리키는지 확인한다.
  3. WSL이 켜져 있는지 확인한다.

7.2 권장 실행 방법

Windows PowerShell에서 프로젝트 루트로 이동한 뒤 아래 중 하나를 사용한다.

방법 A:

.\start_docker_wsl.ps1

방법 B:

.\start_docker_wsl.bat

7.3 내부 실행 순서

스크립트는 내부적으로 다음 순서로 동작한다.

  1. 현재 Windows 경로를 WSL 경로로 변환한다.
  2. WSL 동작 여부를 확인한다.
  3. WSL 내부 Docker 사용 가능 여부를 확인한다.
  4. docker compose up --build -d를 수행한다.

7.4 기대되는 컨테이너 순서

정상이라면 다음 순서로 올라온다.

  1. itam-db
  2. itam-db-bootstrap
  3. itam-backend
  4. itam-frontend

itam-db-bootstrap은 정상이라면 최종 상태가 Exited (0)이어야 한다.


8. 첫 기동 후 검증 절차

기동 후에는 반드시 아래 검증을 수행한다.

8.1 컨테이너 상태 확인

wsl sh -lc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep itam"

정상 기대 상태:

  1. itam-db -> Up ... (healthy)
  2. itam-db-bootstrap -> Exited (0)
  3. itam-backend -> Up
  4. itam-frontend -> Up

8.2 백엔드 API 직접 확인

Invoke-WebRequest -Uri http://localhost:3000/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode

정상 기대값:

  1. 200

8.3 프런트 경유 API 확인

Invoke-WebRequest -Uri http://localhost:8080/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode

정상 기대값:

  1. 200

8.4 데이터가 실제로 들어왔는지 확인

wsl sh -lc "docker exec itam-db mysql -uitam_admin -pitam1234 -D itam -e 'SHOW TABLES' | head -n 20"

정상이라면 아래와 같은 테이블들이 보여야 한다.

  1. asset_core
  2. asset_remote
  3. asset_spec
  4. asset_location
  5. asset_history
  6. asset_software_perpetual
  7. asset_software_subscription
  8. hardware_components_master
  9. job_spec_standards

8.5 브라우저 화면 확인

브라우저에서 아래 주소를 연다.

http://localhost:8080

목록/대시보드 데이터가 보이면 화면까지 정상 연결된 것이다.


9. 재기동 절차

코드만 수정됐고 DB는 유지하고 싶다면 다음처럼 하면 된다.

9.1 스택 종료

.\stop_docker_wsl.ps1

또는

.\stop_docker_wsl.bat

9.2 스택 재기동

.\start_docker_wsl.ps1

이 경우 itam_mysql_data 볼륨이 유지되므로, db-bootstrap은 내부 DB에 asset_core가 이미 있음을 감지하고 빠르게 종료한다.


10. DB를 완전히 다시 초기화하는 절차

외부 원본 DB에서 다시 처음부터 내부 DB를 복제하고 싶다면, MySQL 볼륨을 제거해야 한다.

10.1 스택 중지

.\stop_docker_wsl.ps1

10.2 MySQL 데이터 볼륨 삭제

wsl sh -lc "docker volume rm -f itam_itam_mysql_data"

10.3 다시 시작

.\start_docker_wsl.ps1

이때 db-bootstrap이 외부 DB에서 내부 DB로 전체를 다시 복제한다.


11. 현재 구조에서 꼭 알아야 할 설계 포인트

11.1 server.jsdotenv.config() 변경 이유

백엔드가 내부 DB로 붙게 하려면 Compose가 준 환경변수가 .env보다 우선해야 한다.

만약 아래처럼 override: true를 쓰면 안 된다.

dotenv.config({ override: true });

이렇게 되면 내부 db가 아니라 .env의 외부 DB로 다시 붙을 수 있다.

현재는 아래가 맞다.

dotenv.config();

11.2 왜 docker-entrypoint-initdb.d 기반 dump 파일을 안 쓰는가

처음에는 이 방식을 시도했지만, 실제 데이터의 긴 문자열/깨진 텍스트 때문에 import가 line 97에서 중단됐다.

그래서 현재는 더 안정적인 아래 방식을 쓴다.

  1. 외부 DB에서 mysqldump
  2. 파이프로 내부 db에 즉시 mysql import

즉, 파일 중간 생성물을 신뢰하지 않는 구조다.

11.3 왜 프런트 프록시 타깃을 환경변수화했는가

로컬 직접 실행과 컨테이너 실행의 네트워크 기준이 다르기 때문이다.

  1. 로컬 직접 실행: localhost:3000이 맞다.
  2. 컨테이너 내부 실행: backend:3000이 맞다.

그래서 vite.config.ts는 둘 다 수용할 수 있게 작성됐다.


12. 문제 발생 시 진단 순서

이 프로젝트에서는 문제를 아래 순서로 자르면 가장 빠르다.

12.1 브라우저 화면에 데이터가 없을 때

먼저 다음 둘을 분리해서 본다.

  1. http://localhost:3000/api/assets/master
  2. http://localhost:8080/api/assets/master

판단 기준:

  1. 3000은 200이고 8080만 실패면 프런트 프록시 문제다.
  2. 둘 다 실패면 백엔드 또는 DB 문제다.

12.2 DB bootstrap이 성공했는지 확인할 때

wsl sh -lc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep itam"

여기서 itam-db-bootstrapExited (0)인지 본다.

12.3 내부 DB에 실제 데이터가 있는지 확인할 때

wsl sh -lc "docker exec itam-db mysql -uitam_admin -pitam1234 -D itam -e 'SHOW TABLES'"

12.4 백엔드 로그 확인

wsl sh -lc "docker logs --tail=200 itam-backend"

12.5 DB 로그 확인

wsl sh -lc "docker logs --tail=200 itam-db"

12.6 프런트 로그 확인

wsl sh -lc "docker logs --tail=200 itam-frontend"

13. 자주 나올 수 있는 장애와 해석

13.1 docker 명령이 PowerShell에서 안 보임

의미:

  1. Windows 셸이 아니라 WSL에서 Docker를 쓰는 환경이다.

대응:

  1. start_docker_wsl.ps1 사용

13.2 asset_core 테이블 없음

의미:

  1. 내부 DB 초기화가 안 됐거나 bootstrap이 안 끝났다.

대응:

  1. db-bootstrap 상태 확인
  2. .env 외부 DB 접속 정보 확인
  3. 필요하면 볼륨 삭제 후 재초기화

13.3 3000 API는 되는데 화면은 비어 있음

의미:

  1. DB는 정상이고 프런트 프록시 또는 화면 렌더링 문제다.

대응:

  1. 8080/api/assets/master 상태 먼저 확인

13.4 db-bootstrap가 실패 종료함

의미 후보:

  1. .env 외부 DB 접속 정보 오류
  2. 외부 DB 네트워크 접근 불가
  3. 외부 계정 권한 문제

대응:

  1. docker logs itam-db-bootstrap 확인

14. 현재 최종 검증 완료 상태

이 저장소는 아래 상태까지 검증이 완료됐다.

  1. WSL2 Ubuntu에서 Docker 실행 가능
  2. start_docker_wsl.ps1로 전체 스택 기동 가능
  3. db 컨테이너 healthcheck 통과
  4. db-bootstrap가 외부 DB에서 내부 DB로 데이터 복제 후 Exited (0) 종료
  5. backend가 내부 db를 사용해 API 응답 가능
  6. frontendbackend를 프록시해 8080 기준 화면/API 동작 가능
  7. 내부 MySQL에 실데이터 적재 확인

즉, 현재 Git에 올라간 상태만으로도 WSL2와 외부 원본 DB 정보만 있으면 지금과 같은 수준의 Docker 실행 재현이 가능하다.


15. 현재 구조의 한계와 다음 단계

현재 구조는 충분히 시연 가능하고 개발 재현도 가능하지만, 다음은 아직 별도 작업이 필요하다.

  1. 운영형 정적 배포 구조 전환
  2. 외부 DB 없이도 완전 독립 실행 가능한 정식 dump/backup 체계
  3. .env.example 정리
  4. DB bootstrap 전용 계정/권한 최소화
  5. 장기적으로 map_config.json 파일 저장 정책 정리

하지만 "현재 저장소만으로 지금과 같은 Docker 실행 상태 재현"이라는 목표는 이미 충족한다.


16. 빠른 실행 요약

가장 짧게 요약하면 다음 순서다.

  1. .env에 외부 원본 MySQL 접속 정보를 넣는다.
  2. WSL2 Ubuntu와 WSL 내부 Docker가 살아 있는지 확인한다.
  3. start_docker_wsl.ps1를 실행한다.
  4. itam-db-bootstrapExited (0)인지 확인한다.
  5. http://localhost:3000/api/assets/masterhttp://localhost:8080/api/assets/master가 모두 200인지 확인한다.
  6. 브라우저에서 http://localhost:8080을 열어 데이터가 보이는지 확인한다.

이 순서대로 진행하면 현재 저장소 기준 Dockerized ITAM 시연 환경을 재현할 수 있다.


17. 2026-06-16 최신 정정

이 문서의 상단 본문은 한동안 사용했던 내부 db + db-bootstrap 구조를 기준으로 작성됐다. 하지만 오늘 기준 현재 저장소의 실제 docker-compose.yaml은 다시 무상태 앱 컨테이너 + 외부 DB 구조로 되돌아가 있다.

따라서 현재 시점의 정답 아키텍처는 아래다.

  1. backend 컨테이너
  2. frontend 컨테이너
  3. 외부 MySQL DB

현재는 더 이상 아래 항목이 없다.

  1. db 서비스 없음
  2. db-bootstrap 서비스 없음
  3. itam_mysql_data 볼륨 없음

17.1 현재 실제 docker-compose.yaml 기준 backend 동작

현재 backend는 .env의 외부 DB 접속 정보를 그대로 사용한다.

즉, 아래 환경변수 매핑이 현재 기준이다.

  1. DB_HOST: ${DB_HOST}
  2. DB_PORT: ${DB_PORT}
  3. DB_USER: ${DB_USER}
  4. DB_PASS: ${DB_PASS}
  5. DB_NAME: ${DB_NAME}

PORT: 3000만 Compose에서 고정한다.

17.2 현재 실제 기동 구조

현재 스택 기동 순서는 단순하다.

  1. backend 기동
  2. frontend 기동
  3. backend는 외부 DB에 직접 접속
  4. frontend는 http://backend:3000으로 프록시

즉, 현재는 DB 컨테이너 초기화 단계나 bootstrap 단계가 존재하지 않는다.

17.3 현재 기준 첫 실행 체크리스트

오늘 기준으로는 아래 순서가 맞다.

  1. .env에 외부 DB 접속 정보 입력
  2. start_docker_wsl.ps1 또는 start_docker_wsl.bat 실행
  3. http://localhost:3000/api/assets/master가 200인지 확인
  4. http://localhost:8080/api/assets/master가 200인지 확인
  5. 브라우저에서 http://localhost:8080 접속 후 데이터 표시 확인

17.4 이 문서에서 현재 유효한 부분과 과거 이력 부분

현재도 그대로 유효한 내용은 아래다.

  1. WSL2 기반 실행 방식
  2. start_docker_wsl.ps1 / stop_docker_wsl.ps1 사용 방식
  3. server.js에서 Compose 환경변수가 .env보다 우선되도록 dotenv.config()를 유지해야 한다는 점
  4. vite.config.ts에서 프록시 타깃을 환경변수화해야 한다는 점

현재는 과거 이력으로만 읽어야 하는 내용은 아래다.

  1. 내부 db 서비스 설명
  2. db-bootstrap 설명
  3. itam_mysql_data 볼륨 설명
  4. 내부 DB 재초기화 절차
  5. 내부 테이블 확인 절차

17.5 현재 최종 한 줄 요약

오늘 날짜 기준 현재 저장소의 실사용 Compose 구조는 frontend + backend + external DB이며, 이전의 내부 DB/bootstrap 구조는 역사적으로 한 번 사용했던 임시 해결책으로만 남아 있다.