From 9d19d8283e45c80d37ffd75d57ee48cb3357ea40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EB=8C=80=EC=9D=BC?= Date: Wed, 17 Jun 2026 11:31:10 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=90=EC=82=B0=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=8F=84=EC=BB=A4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 10 + .env | 6 - Dockerfile.backend | 12 + Dockerfile.frontend | 12 + doc_readme.md | 729 +++++++++++++++++++++++++++++++++++ doc_readme2.md | 730 ++++++++++++++++++++++++++++++++++++ docker-compose.yaml | 48 +++ docker/mysql/init/README.md | 16 + docker_task_plan.md | 330 ++++++++++++++++ server.js | 47 ++- start_docker_wsl.bat | 10 + start_docker_wsl.ps1 | 107 ++++++ start_server.ps1 | 65 ++++ stop_docker_wsl.bat | 4 + stop_docker_wsl.ps1 | 13 + vite.config.ts | 6 +- 16 files changed, 2129 insertions(+), 16 deletions(-) create mode 100644 .dockerignore delete mode 100644 .env create mode 100644 Dockerfile.backend create mode 100644 Dockerfile.frontend create mode 100644 doc_readme.md create mode 100644 doc_readme2.md create mode 100644 docker-compose.yaml create mode 100644 docker/mysql/init/README.md create mode 100644 docker_task_plan.md create mode 100644 start_docker_wsl.bat create mode 100644 start_docker_wsl.ps1 create mode 100644 stop_docker_wsl.bat create mode 100644 stop_docker_wsl.ps1 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..12df7a5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +dist +build +.git +.gitignore +.env +npm-debug.log +uploads +*.xlsx +*.log diff --git a/.env b/.env deleted file mode 100644 index bd7a495..0000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -DB_HOST=172.16.8.151 -DB_PORT=3306 -DB_USER=itam_admin -DB_PASS=itam1234 -DB_NAME=itam -PORT=3000 \ No newline at end of file diff --git a/Dockerfile.backend b/Dockerfile.backend new file mode 100644 index 0000000..65f9dac --- /dev/null +++ b/Dockerfile.backend @@ -0,0 +1,12 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "run", "server"] \ No newline at end of file diff --git a/Dockerfile.frontend b/Dockerfile.frontend new file mode 100644 index 0000000..525a1ff --- /dev/null +++ b/Dockerfile.frontend @@ -0,0 +1,12 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +EXPOSE 8080 + +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/doc_readme.md b/doc_readme.md new file mode 100644 index 0000000..f9b7dbe --- /dev/null +++ b/doc_readme.md @@ -0,0 +1,729 @@ +# ITAM 도커라이징 실전 가이드 + +## 1. 문서 목적 + +이 문서는 Gitea에 올라가 있는 현재 저장소를 기준으로, 개발 PC에 WSL2와 Ubuntu만 설치되어 있는 상태에서 지금의 Docker 실행 구조를 재현하는 방법을 처음부터 끝까지 설명하는 실전 가이드다. + +이 문서는 아래 상황을 가정한다. + +1. 소스 코드는 아직 로컬에 없거나, Gitea에서 막 받아올 예정이다. +2. Windows에는 WSL2와 Ubuntu는 설치되어 있다. +3. 그 외 Docker 관련 세팅은 아직 안 되어 있을 수 있다. +4. 최종 목표는 현재 저장소 기준 `frontend + backend + external DB` 구조를 Docker로 재현하는 것이다. + +이 문서의 목적은 아래 네 가지다. + +1. 현재 시스템 구조와 Docker 구조를 먼저 이해하게 한다. +2. 기존 파일 중 무엇이 새로 추가되었고 무엇이 수정되었는지 정리한다. +3. 각 단계별로 정확히 어디에서 명령을 실행해야 하는지 명시한다. +4. Gitea 소스만 받은 상태에서 지금과 같은 Docker 실행 상태까지 도달하게 한다. + +--- + +## 2. 현재 시스템 구조 개요 + +## 2.1 애플리케이션 원래 구조 + +현재 저장소의 본래 실행 구조는 다음과 같다. + +1. 프런트엔드: Vite 기반 TypeScript 앱 +2. 백엔드: Express 기반 Node.js API 서버 +3. 데이터베이스: 외부 MySQL 서버 + +즉, 원래부터 MySQL이 Docker 안에 들어 있던 구조가 아니다. + +프런트와 백엔드는 각각 별도 프로세스로 실행되며, 프런트는 `/api` 상대 경로로 백엔드 API를 호출한다. + +--- + +## 2.2 현재 Docker 구조 + +현재 최종 Docker 구조는 아래와 같다. + +1. `frontend` 컨테이너 +2. `backend` 컨테이너 +3. 외부 MySQL DB + +즉, 지금은 내부 `db` 컨테이너가 없고, 내부 `db-bootstrap` 컨테이너도 없다. + +현재 구조를 문장으로 풀면 다음과 같다. + +1. 브라우저는 `http://localhost:8080`으로 `frontend` 컨테이너에 접속한다. +2. `frontend`는 `/api` 요청을 `backend:3000`으로 프록시한다. +3. `backend`는 `.env`에 적힌 외부 DB 정보로 외부 MySQL에 직접 접속한다. +4. 조회 결과 JSON을 프런트가 받아 화면에 렌더링한다. + +간단한 흐름은 아래와 같다. + +```text +Browser + -> frontend container :8080 + -> Vite proxy (/api) + -> backend container :3000 + -> external MySQL (.env) +``` + +--- + +## 2.3 왜 이 구조가 맞는가 + +현재 구조가 적절한 이유는 다음과 같다. + +1. 원래 시스템도 외부 MySQL을 쓰는 구조였다. +2. 지금 목표는 운영형 단일 배포가 아니라 현재 개발형 구조를 Docker로 재현하는 것이다. +3. 프런트는 Vite dev server 기반이라 운영형 nginx 정적 배포 구조로 억지로 바꾸는 것보다, 현 구조를 유지하는 편이 안전하다. +4. 실무 표준 관점에서도 앱 컨테이너는 무상태로 유지하고, DB는 외부 인프라를 사용하는 구성이 더 일반적이다. + +--- + +## 3. 이번 도커라이징에서 추가되거나 수정된 파일 정리 + +아래 파일들은 이번 Docker 재현 구조를 위해 새로 추가되었거나 수정된 핵심 파일이다. + +## 3.1 새로 추가된 파일 + +1. `Dockerfile.frontend` +2. `Dockerfile.backend` +3. `.dockerignore` +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. `docker_task_plan.md` +11. `doc_readme2.md` + +--- + +## 3.2 기존 파일 중 수정된 핵심 파일 + +1. `server.js` +2. `vite.config.ts` +3. `doc_readme.md` + +--- + +## 3.3 각 파일의 역할 + +### `Dockerfile.frontend` + +역할: + +1. 프런트 Vite 개발 서버 이미지를 만든다. +2. 컨테이너 내부에서 `npm run dev -- --host 0.0.0.0`를 실행한다. + +### `Dockerfile.backend` + +역할: + +1. 백엔드 Express 서버 이미지를 만든다. +2. 컨테이너 내부에서 `npm run server`를 실행한다. + +### `.dockerignore` + +역할: + +1. `node_modules`, `build`, `.git`, `.env`, `uploads` 같은 불필요한 파일을 Docker build context에서 제외한다. + +### `docker-compose.yaml` + +역할: + +1. `frontend`, `backend` 두 컨테이너를 동시에 띄운다. +2. `backend`는 `.env`의 외부 DB를 사용한다. +3. `frontend`는 `backend:3000`으로 프록시한다. + +### `start_docker_wsl.ps1` + +역할: + +1. Windows 경로를 WSL 경로로 안전하게 바꾼다. +2. WSL 내부 Docker를 사용해 `docker compose up --build -d`를 실행한다. +3. 한글 경로와 공백 경로에서도 안정적으로 실행되게 한다. + +### `stop_docker_wsl.ps1` + +역할: + +1. 같은 방식으로 WSL 내부에서 `docker compose down`을 실행한다. + +### `start_docker_wsl.bat`, `stop_docker_wsl.bat` + +역할: + +1. PowerShell 스크립트를 쉽게 실행하는 래퍼 역할을 한다. + +### `server.js` + +중요 수정 사항: + +1. `dotenv.config({ override: true })`가 아니라 `dotenv.config()`를 사용한다. + +이유: + +1. Compose나 실행 환경이 주는 환경변수를 `.env`가 덮어써 버리면 안 된다. +2. 외부 DB 정보와 포트 설정 등 실행 환경 우선 구조를 유지해야 한다. + +### `vite.config.ts` + +중요 수정 사항: + +1. 프록시 타깃을 고정 `localhost:3000`이 아니라 환경변수 기반으로 받도록 바꿨다. + +현재 구조: + +```ts +const proxyTarget = process.env.VITE_DEV_PROXY_TARGET || 'http://localhost:3000'; +``` + +이유: + +1. 로컬에서 직접 프런트를 띄울 때는 `localhost:3000`이 맞다. +2. Docker 안에서는 `frontend` 컨테이너에서 보는 `localhost`가 백엔드가 아니므로 `backend:3000`을 써야 한다. + +--- + +## 4. 현재 `docker-compose.yaml` 기준 실제 동작 구조 + +현재 `docker-compose.yaml`은 아래 구조다. + +### `backend` + +1. `Dockerfile.backend`로 이미지를 빌드한다. +2. `.env`를 읽는다. +3. DB 관련 변수는 `${DB_HOST}`, `${DB_PORT}`, `${DB_USER}`, `${DB_PASS}`, `${DB_NAME}`를 그대로 사용한다. +4. 포트 `3000:3000`으로 노출한다. +5. `uploads`, `map_config.json`을 마운트한다. + +### `frontend` + +1. `Dockerfile.frontend`로 이미지를 빌드한다. +2. `VITE_DEV_PROXY_TARGET: http://backend:3000` 환경변수를 사용한다. +3. 포트 `8080:8080`으로 노출한다. +4. 브라우저의 `/api` 요청을 `backend`로 프록시한다. + +즉, 현재 Compose는 DB를 띄우지 않고 앱 두 개만 띄운다. + +--- + +## 5. 사전 준비 사항 + +이 섹션은 Gitea에서 코드를 받기 전 또는 받은 직후에 확인해야 한다. + +## 5.1 가정하는 기본 상태 + +이미 설치되어 있다고 가정하는 것: + +1. Windows +2. WSL2 +3. Ubuntu 배포판 + +아직 없을 수 있는 것: + +1. Docker Desktop 또는 WSL 내부 Docker 사용 환경 +2. Git 클라이언트 +3. 프로젝트 `.env` + +--- + +## 5.2 권장 Docker 실행 방식 + +현재 저장소 구조상 가장 권장하는 방식은 다음이다. + +1. Windows에 Docker Desktop 설치 +2. Docker Desktop에서 WSL2 통합 활성화 +3. Ubuntu WSL 내부에서 `docker` 명령을 사용할 수 있게 한다. + +이유: + +1. 현재 `start_docker_wsl.ps1`가 WSL 내부의 `docker`를 호출하는 구조다. +2. 실제 검증도 WSL 내부 Docker 기준으로 이루어졌다. + +--- + +## 5.3 외부 DB 정보 준비 + +현재 구조는 외부 MySQL을 사용하므로 `.env` 파일이 반드시 필요하다. + +최소한 아래 값이 필요하다. + +```env +DB_HOST=<외부 MySQL 호스트> +DB_PORT=3306 +DB_USER=<외부 MySQL 계정> +DB_PASS=<외부 MySQL 비밀번호> +DB_NAME=itam +``` + +필요 시 추가 환경변수는 현재 백엔드 코드 기준으로 함께 넣을 수 있다. + +--- + +## 6. Gitea에서 소스 받기 + +## 6.1 작업 실행 위치 + +이 단계는 **Windows PowerShell** 또는 **Windows 터미널의 PowerShell**에서 수행한다. + +실행 위치 이유: + +1. 이후 `start_docker_wsl.ps1`도 Windows PowerShell에서 실행하는 것이 가장 자연스럽다. +2. 로컬 작업 폴더를 Windows 경로 기준으로 준비할 수 있다. + +--- + +## 6.2 소스 클론 + +예시: + +```powershell +git clone +cd <클론된 저장소 경로> +``` + +현재 프로젝트처럼 한글 경로를 사용할 수도 있지만, 가능하면 너무 복잡한 경로는 피하는 것이 좋다. + +현재 실제 프로젝트 경로 예시는 아래였다. + +```text +c:\Users\user\Desktop\안건 파일\itam +``` + +이 경로도 현재 스크립트로는 동작 가능하다. + +--- + +## 7. Docker 환경 준비 + +## 7.1 작업 실행 위치 + +이 단계는 **Windows PowerShell**과 **WSL Ubuntu 터미널**을 둘 다 사용한다. + +1. 설치 확인은 Windows PowerShell에서 시작 +2. 실제 Docker 동작 확인은 WSL Ubuntu에서 수행 + +--- + +## 7.2 Docker Desktop 설치 여부 확인 + +**실행 위치: Windows PowerShell** + +```powershell +docker version +``` + +만약 여기서 바로 안 잡혀도 현재 프로젝트는 WSL 내부 Docker를 쓰므로, 다음 단계로 넘어가 WSL 내부 확인을 한다. + +--- + +## 7.3 WSL 내부 Docker 확인 + +**실행 위치: Windows PowerShell** + +```powershell +wsl -l -v +wsl sh -lc "docker --version" +``` + +정상 기대 결과: + +1. Ubuntu가 Running 상태 +2. `docker --version`이 정상 출력 + +만약 `docker --version`이 실패하면, Docker Desktop 설치 및 WSL 통합을 먼저 완료해야 한다. + +--- + +## 8. `.env` 파일 준비 + +## 8.1 작업 실행 위치 + +이 단계는 **Windows PowerShell**, **VS Code**, 또는 아무 텍스트 편집기**에서 수행한다. + +즉, 프로젝트 루트에 `.env` 파일을 만드는 작업이다. + +--- + +## 8.2 `.env` 작성 + +프로젝트 루트에 `.env`를 만든다. + +예시: + +```env +DB_HOST=your-external-db-host +DB_PORT=3306 +DB_USER=your-db-user +DB_PASS=your-db-password +DB_NAME=itam +``` + +주의: + +1. 현재 Compose는 내부 DB를 만들지 않는다. +2. 따라서 이 값이 곧 실제 운영/개발 외부 DB 연결 정보다. +3. 이 정보가 틀리면 `backend`는 기동해도 API에서 DB 오류가 난다. + +--- + +## 9. 현재 Docker 파일이 어떻게 동작하는지 이해하기 + +## 9.1 `Dockerfile.frontend` + +**확인 위치: 프로젝트 루트 / VS Code** + +현재 내용 핵심: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +EXPOSE 8080 +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] +``` + +의미: + +1. Node 20 Alpine 기반 +2. 의존성 설치 후 전체 소스 복사 +3. Vite 개발 서버 실행 + +--- + +## 9.2 `Dockerfile.backend` + +**확인 위치: 프로젝트 루트 / VS Code** + +현재 내용 핵심: + +```dockerfile +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +EXPOSE 3000 +CMD ["npm", "run", "server"] +``` + +의미: + +1. Node 20 Alpine 기반 +2. Express 서버 실행 + +--- + +## 9.3 `vite.config.ts` + +**확인 위치: 프로젝트 루트 / VS Code** + +현재 핵심: + +```ts +const proxyTarget = process.env.VITE_DEV_PROXY_TARGET || 'http://localhost:3000'; +``` + +그리고 `/api`, `/uploads`가 모두 `proxyTarget`으로 프록시된다. + +의미: + +1. 로컬 실행 시 기본값은 `localhost:3000` +2. Docker 실행 시 Compose가 `http://backend:3000`을 주입 + +이 수정이 있어야 Docker 안에서도 화면에 데이터가 표시된다. + +--- + +## 10. Docker Compose 기동 + +## 10.1 작업 실행 위치 + +이 단계는 반드시 **Windows PowerShell**에서 수행하는 것을 권장한다. + +이유: + +1. `start_docker_wsl.ps1`가 Windows 경로를 받아 WSL 경로로 바꾸는 구조다. +2. 한글/공백 경로에서 가장 안전하다. + +--- + +## 10.2 권장 기동 방법 + +**실행 위치: 프로젝트 루트의 Windows PowerShell** + +```powershell +.\start_docker_wsl.ps1 +``` + +또는 + +```powershell +.\start_docker_wsl.bat +``` + +이 스크립트는 내부적으로 아래를 수행한다. + +1. PowerShell 출력 인코딩을 UTF-8로 설정 +2. 현재 Windows 경로를 WSL 경로로 변환 +3. WSL 동작 확인 +4. WSL 내부 Docker 동작 확인 +5. `docker compose up --build -d` 수행 + +--- + +## 10.3 직접 기동이 필요할 때 + +**실행 위치: WSL Ubuntu 터미널** + +직접 실행 예시는 아래와 같다. + +```bash +cd /mnt/c/Users/user/Desktop/안건\ 파일/itam +docker compose up --build -d +``` + +하지만 현재 프로젝트는 한글 경로 이슈가 있었기 때문에, 특별한 이유가 없으면 `start_docker_wsl.ps1`를 우선 사용한다. + +--- + +## 11. 컨테이너 기동 후 검증 + +## 11.1 컨테이너 상태 확인 + +**실행 위치: Windows PowerShell** + +```powershell +wsl sh -lc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep itam" +``` + +정상 기대 상태: + +1. `itam-backend` -> `Up` +2. `itam-frontend` -> `Up` + +현재는 `itam-db`, `itam-db-bootstrap`가 없어야 정상이다. + +--- + +## 11.2 백엔드 API 확인 + +**실행 위치: Windows PowerShell** + +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +``` + +정상 기대값: + +1. `200` + +이 검사는 `backend`가 외부 DB에 정상 연결됐는지 보는 가장 직접적인 검사다. + +--- + +## 11.3 프런트 경유 API 확인 + +**실행 위치: Windows PowerShell** + +```powershell +Invoke-WebRequest -Uri http://localhost:8080/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +``` + +정상 기대값: + +1. `200` + +이 검사는 프런트 프록시가 정상인지 확인한다. + +예전에 화면에 데이터가 안 보였던 것은 외부 DB 자체가 아니라, 이 프록시 경로가 잘못돼 있었기 때문이다. + +--- + +## 11.4 브라우저 화면 확인 + +**실행 위치: 브라우저** + +```text +http://localhost:8080 +``` + +확인 포인트: + +1. 화면이 열리는지 +2. 목록/대시보드/테이블 데이터가 비어 있지 않은지 +3. 모달 진입 시 데이터가 정상적으로 보이는지 + +--- + +## 12. 지금 데이터가 표시되는 원리 + +현재는 내부 DB로 데이터를 옮겨 담지 않는다. + +현재 실제 동작 원리는 다음과 같다. + +1. 브라우저가 `frontend`에 접속한다. +2. 프런트가 `/api/...`로 요청한다. +3. Vite 프록시가 `backend:3000`으로 요청을 넘긴다. +4. `backend`가 `.env`의 외부 MySQL에 직접 접속한다. +5. 조회 결과 JSON을 프런트가 받아 화면에 렌더링한다. + +즉, 현재는 아래 구조다. + +```text +Browser -> frontend -> backend -> external MySQL +``` + +예전 외부 DB 구조에서 화면에 데이터가 안 보였던 이유는 외부 DB 때문이 아니라, 프런트 컨테이너가 `localhost:3000`을 잘못 바라보고 있었기 때문이다. + +지금은 `VITE_DEV_PROXY_TARGET: http://backend:3000`으로 수정되어 있기 때문에 정상 표시된다. + +--- + +## 13. 자주 헷갈리는 포인트 + +## 13.1 현재는 내부 DB 컨테이너가 없다 + +현재 `docker-compose.yaml`에는 아래가 없다. + +1. `db` 서비스 +2. `db-bootstrap` 서비스 +3. `itam_mysql_data` 볼륨 + +즉, DB는 Docker 스택 밖에 있다. + +--- + +## 13.2 현재는 `.env`가 곧 실제 DB 연결 정보다 + +현재 `backend`는 아래처럼 Compose에서 그대로 받는다. + +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}` + +즉, `.env`를 틀리게 적으면 화면도 데이터가 안 뜬다. + +--- + +## 13.3 `server.js`는 여전히 중요하게 수정된 상태다 + +현재 `server.js`는 `dotenv.config()`를 사용한다. + +이 구조는 이후 Compose나 실행 환경에서 변수를 주입할 때, 애플리케이션이 그 값을 받아들일 수 있게 하기 위해 유지해야 한다. + +--- + +## 14. 스택 중지 방법 + +## 14.1 작업 실행 위치 + +**Windows PowerShell / 프로젝트 루트** + +--- + +## 14.2 권장 종료 명령 + +```powershell +.\stop_docker_wsl.ps1 +``` + +또는 + +```powershell +.\stop_docker_wsl.bat +``` + +이 스크립트는 내부적으로 WSL 경로 변환 후 `docker compose down`을 수행한다. + +--- + +## 15. 장애 발생 시 점검 순서 + +## 15.1 `frontend` 화면은 뜨는데 데이터가 없을 때 + +**실행 위치: Windows PowerShell** + +먼저 아래 두 API를 분리해서 본다. + +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +Invoke-WebRequest -Uri http://localhost:8080/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +``` + +판단 기준: + +1. `3000`은 200이고 `8080`만 실패 -> 프런트 프록시 문제 +2. 둘 다 실패 -> 백엔드 또는 외부 DB 연결 문제 + +--- + +## 15.2 백엔드가 외부 DB에 연결되지 않을 때 + +**실행 위치: Windows PowerShell** + +```powershell +wsl sh -lc "docker logs --tail=200 itam-backend" +``` + +점검 항목: + +1. `.env`의 DB 정보가 정확한지 +2. 외부 DB 서버 접근이 가능한지 +3. 계정/비밀번호가 맞는지 +4. 방화벽 또는 네트워크 이슈가 없는지 + +--- + +## 15.3 프런트 프록시가 의심될 때 + +**확인 위치: `vite.config.ts`, `docker-compose.yaml`** + +다음 두 설정이 유지되는지 확인한다. + +`vite.config.ts` + +```ts +const proxyTarget = process.env.VITE_DEV_PROXY_TARGET || 'http://localhost:3000'; +``` + +`docker-compose.yaml` + +```yaml +VITE_DEV_PROXY_TARGET: http://backend:3000 +``` + +이 둘 중 하나라도 바뀌면 Docker 안에서 화면 데이터가 다시 안 보일 수 있다. + +--- + +## 16. 현재 기준 재현 절차 요약 + +가장 짧게 정리하면 아래 순서다. + +1. Gitea에서 소스를 클론한다. +2. Windows PowerShell에서 프로젝트 루트로 이동한다. +3. `.env`에 외부 MySQL 정보를 작성한다. +4. Docker Desktop + WSL 통합 또는 WSL 내부 Docker 사용 가능 상태를 만든다. +5. `start_docker_wsl.ps1`를 실행한다. +6. `http://localhost:3000/api/assets/master`가 200인지 확인한다. +7. `http://localhost:8080/api/assets/master`가 200인지 확인한다. +8. 브라우저에서 `http://localhost:8080`을 열어 실제 데이터 표시를 확인한다. + +--- + +## 17. 현재 최종 결론 + +현재 저장소의 도커라이징 구조는 실무 표준에 맞는 `무상태 앱 컨테이너 + 외부 DB` 구조다. + +현재 핵심은 아래 세 가지다. + +1. `backend`는 외부 MySQL에 직접 연결한다. +2. `frontend`는 `backend:3000`으로 API 프록시한다. +3. WSL 경로 변환 스크립트를 통해 Windows 한글 경로에서도 안정적으로 실행한다. + +즉, 이 문서대로 진행하면 Gitea 소스만 받은 상태에서 지금과 같은 Docker 실행 구조를 재현할 수 있다. diff --git a/doc_readme2.md b/doc_readme2.md new file mode 100644 index 0000000..5b35781 --- /dev/null +++ b/doc_readme2.md @@ -0,0 +1,730 @@ +# 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회성 복제 수행 후 종료되는 도우미 컨테이너 + +논리 흐름은 다음과 같다. + +```text +브라우저 -> 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` + +프런트 컨테이너 이미지 정의다. + +```dockerfile +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` + +백엔드 컨테이너 이미지 정의다. + +```dockerfile +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. `frontend`는 `backend` 서비스명으로 프록시한다. + +### 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` + +현재 프록시는 환경변수 기반으로 동작한다. + +```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`으로 동작하며, `backend`와 `db-bootstrap`은 이 상태를 기다린다. + +### 5.2 `db-bootstrap` 서비스 + +역할: + +1. 외부 원본 DB에서 내부 `db`로 초기 데이터 복제 +2. 1회성 작업 후 종료 + +핵심 포인트: + +1. `.env`를 읽어 외부 DB 접속 정보를 가져온다. +2. 내부 `db`에 `asset_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_on`은 `db` healthy와 `db-bootstrap` 성공 종료를 모두 기다린다. + +즉, 백엔드는 DB bootstrap이 끝난 뒤 시작한다. + +### 5.4 `frontend` 서비스 + +역할: + +1. Vite dev server 제공 +2. 브라우저 요청 `/api`, `/uploads`를 `backend`로 프록시 + +핵심 포인트: + +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 내부에서 동작 가능 + +권장 확인 명령: + +```powershell +wsl -l -v +wsl sh -lc "docker --version" +``` + +### 6.2 `.env` 파일 + +현재 최종 구조는 "첫 기동 시 외부 DB에서 내부 DB로 bootstrap" 하는 방식이므로 `.env`가 반드시 필요하다. + +최소한 다음 값은 외부 원본 DB를 가리켜야 한다. + +```env +DB_HOST= +DB_PORT=3306 +DB_USER= +DB_PASS= +DB_NAME=itam +``` + +주의: + +1. `.env`는 `db-bootstrap`이 외부 원본 DB에 접속할 때 사용한다. +2. `backend`는 최종적으로 내부 `db` 컨테이너를 쓰므로, 런타임에서는 Compose `environment`가 우선한다. + +### 6.3 한글 경로 주의 + +현재 프로젝트 경로는 한글과 공백을 포함한다. + +```text +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: + +```powershell +.\start_docker_wsl.ps1 +``` + +방법 B: + +```powershell +.\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 컨테이너 상태 확인 + +```powershell +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 직접 확인 + +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +``` + +정상 기대값: + +1. `200` + +### 8.3 프런트 경유 API 확인 + +```powershell +Invoke-WebRequest -Uri http://localhost:8080/api/assets/master -UseBasicParsing | Select-Object -ExpandProperty StatusCode +``` + +정상 기대값: + +1. `200` + +### 8.4 데이터가 실제로 들어왔는지 확인 + +```powershell +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 브라우저 화면 확인 + +브라우저에서 아래 주소를 연다. + +```text +http://localhost:8080 +``` + +목록/대시보드 데이터가 보이면 화면까지 정상 연결된 것이다. + +--- + +## 9. 재기동 절차 + +코드만 수정됐고 DB는 유지하고 싶다면 다음처럼 하면 된다. + +### 9.1 스택 종료 + +```powershell +.\stop_docker_wsl.ps1 +``` + +또는 + +```powershell +.\stop_docker_wsl.bat +``` + +### 9.2 스택 재기동 + +```powershell +.\start_docker_wsl.ps1 +``` + +이 경우 `itam_mysql_data` 볼륨이 유지되므로, `db-bootstrap`은 내부 DB에 `asset_core`가 이미 있음을 감지하고 빠르게 종료한다. + +--- + +## 10. DB를 완전히 다시 초기화하는 절차 + +외부 원본 DB에서 다시 처음부터 내부 DB를 복제하고 싶다면, MySQL 볼륨을 제거해야 한다. + +### 10.1 스택 중지 + +```powershell +.\stop_docker_wsl.ps1 +``` + +### 10.2 MySQL 데이터 볼륨 삭제 + +```powershell +wsl sh -lc "docker volume rm -f itam_itam_mysql_data" +``` + +### 10.3 다시 시작 + +```powershell +.\start_docker_wsl.ps1 +``` + +이때 `db-bootstrap`이 외부 DB에서 내부 DB로 전체를 다시 복제한다. + +--- + +## 11. 현재 구조에서 꼭 알아야 할 설계 포인트 + +### 11.1 `server.js`의 `dotenv.config()` 변경 이유 + +백엔드가 내부 DB로 붙게 하려면 Compose가 준 환경변수가 `.env`보다 우선해야 한다. + +만약 아래처럼 `override: true`를 쓰면 안 된다. + +```js +dotenv.config({ override: true }); +``` + +이렇게 되면 내부 `db`가 아니라 `.env`의 외부 DB로 다시 붙을 수 있다. + +현재는 아래가 맞다. + +```js +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이 성공했는지 확인할 때 + +```powershell +wsl sh -lc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep itam" +``` + +여기서 `itam-db-bootstrap`이 `Exited (0)`인지 본다. + +### 12.3 내부 DB에 실제 데이터가 있는지 확인할 때 + +```powershell +wsl sh -lc "docker exec itam-db mysql -uitam_admin -pitam1234 -D itam -e 'SHOW TABLES'" +``` + +### 12.4 백엔드 로그 확인 + +```powershell +wsl sh -lc "docker logs --tail=200 itam-backend" +``` + +### 12.5 DB 로그 확인 + +```powershell +wsl sh -lc "docker logs --tail=200 itam-db" +``` + +### 12.6 프런트 로그 확인 + +```powershell +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. `frontend`가 `backend`를 프록시해 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-bootstrap`가 `Exited (0)`인지 확인한다. +5. `http://localhost:3000/api/assets/master`와 `http://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 구조는 역사적으로 한 번 사용했던 임시 해결책으로만 남아 있다. \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..6156596 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,48 @@ +services: + backend: + build: + context: . + dockerfile: Dockerfile.backend + container_name: itam-backend + working_dir: /app + env_file: + - .env + environment: + DB_HOST: ${DB_HOST} + DB_PORT: ${DB_PORT} + DB_USER: ${DB_USER} + DB_PASS: ${DB_PASS} + DB_NAME: ${DB_NAME} + PORT: 3000 + ports: + - "3000:3000" + volumes: + - ./:/app + - backend_node_modules:/app/node_modules + - ./uploads:/app/uploads + - ./map_config.json:/app/map_config.json + command: npm run server + restart: unless-stopped + + frontend: + build: + context: . + dockerfile: Dockerfile.frontend + container_name: itam-frontend + working_dir: /app + depends_on: + - backend + environment: + CHOKIDAR_USEPOLLING: "true" + VITE_DEV_PROXY_TARGET: http://backend:3000 + ports: + - "8080:8080" + volumes: + - ./:/app + - frontend_node_modules:/app/node_modules + command: npm run dev -- --host 0.0.0.0 + restart: unless-stopped + +volumes: + backend_node_modules: + frontend_node_modules: \ No newline at end of file diff --git a/docker/mysql/init/README.md b/docker/mysql/init/README.md new file mode 100644 index 0000000..fbe7b40 --- /dev/null +++ b/docker/mysql/init/README.md @@ -0,0 +1,16 @@ +# MySQL init directory + +This directory is kept as a legacy hook for file-based MySQL initialization. + +Current production path in this repository is not file-based import. +The live Docker flow uses the `db-bootstrap` service in `docker-compose.yaml` to stream data from the external source DB into the internal `db` container. + +Use this directory only if you intentionally switch back to `docker-entrypoint-initdb.d` style initialization. + +If you do that, typical naming would be: + +- `01_schema.sql` +- `02_seed.sql` +- or a single `01_itam_dump.sql` + +Remember that files in this directory are executed automatically by the MySQL container only on the first initialization of the data volume. \ No newline at end of file diff --git a/docker_task_plan.md b/docker_task_plan.md new file mode 100644 index 0000000..ce2d78d --- /dev/null +++ b/docker_task_plan.md @@ -0,0 +1,330 @@ +# ITAM 도커라이징 작업 태스크 정리 + +## 1. 문서 목적 + +이 문서는 ITAM 자산관리 시스템의 도커라이징 작업을 실제 실행 단위로 쪼개서 정리한 태스크 문서다. + +이 문서의 목표는 아래와 같다. + +1. 내일까지 보여줄 시연 범위를 기준으로 우선순위를 정한다. +2. 시연용 작업과 운영형 전환 작업을 분리한다. +3. 개발 담당자가 바로 실행할 수 있는 체크리스트를 제공한다. + +관련 배경과 구조 분석은 [doc_readme.md](c:/Users/user/Desktop/안건%20파일/itam/doc_readme.md) 문서를 기준으로 한다. + +현재 구현/검증 상태: + +- `Dockerfile.frontend` 생성 완료 +- `Dockerfile.backend` 생성 완료 +- `docker-compose.yaml` 생성 완료 +- `.dockerignore` 생성 완료 +- WSL2 Ubuntu에서 `docker compose up --build -d` 검증 완료 +- frontend 8080 응답 확인 완료 +- backend `/api/assets/master` 응답 확인 완료 +- 현재 DB는 external MySQL 기준이며, DB 컨테이너 추가 작업은 다음 단계로 남아 있음 + +## 2. 이번 작업의 최우선 목표 + +이번 도커라이징의 1차 목표는 "운영 배포 완료"가 아니라 아래 상태를 재현하는 것이다. + +1. frontend 컨테이너가 정상 기동한다. +2. backend 컨테이너가 정상 기동한다. +3. backend가 기존 외부 MySQL 또는 MySQL 컨테이너에 정상 연결된다. +4. 브라우저에서 화면이 열린다. +5. 핵심 API 호출이 정상 동작한다. +6. 업로드 저장 경로가 유지된다. +7. 필요 시 DB까지 함께 포함된 재현 가능한 스택을 제공한다. + +## 3. 작업 범위 구분 + +### 3.1 이번 시연 범위에 포함 + +- Dockerfile.frontend 초안 작성 +- Dockerfile.backend 초안 작성 +- docker-compose.yaml 작성 +- `.dockerignore` 작성 +- MySQL 컨테이너 추가 설계 +- 초기 SQL dump 또는 init SQL 적재 방식 정의 +- `uploads` 볼륨 처리 +- `map_config.json` 영속성 처리 방식 반영 +- 컨테이너 기동 및 접속 확인 +- 핵심 API 및 화면 확인 + +### 3.2 이번 시연 범위에서 제외 + +- DB 전체 마이그레이션 자동화 +- nginx 기반 운영 배포 구조 +- 단일 이미지 운영 구조 전환 +- CI/CD 연계 + +## 4. 선행 확인 태스크 + +아래 태스크는 실제 Docker 파일 작성 전에 먼저 확인해야 한다. + +### Task 1. 외부 MySQL 접근 가능 여부 확인 + +- 목적: 컨테이너에서 외부 DB 접속이 가능한지 확인 +- 확인 항목: + - DB_HOST 접근 가능 여부 + - DB_PORT 3306 접속 가능 여부 + - 계정 권한 정상 여부 +- 완료 기준: + - backend 컨테이너 기준 DB 연결 에러가 발생하지 않음 + +### Task 2. 기준 스키마 상태 확인 + +- 목적: 현재 앱이 요구하는 테이블 구조가 실제 DB와 맞는지 확인 +- 확인 항목: + - `asset_core` + - `asset_spec` + - `asset_location` + - `asset_remote` + - `asset_history` + - `hardware_components_master` + - `job_spec_standards` +- 완료 기준: + - `/api/assets/master` 호출 시 쿼리 에러가 발생하지 않음 + +### Task 3. 파일 영속성 대상 확인 + +- 목적: 컨테이너 재시작 이후에도 유지되어야 할 파일/폴더 식별 +- 대상: + - `uploads` + - `map_config.json` +- 완료 기준: + - 볼륨 설계 대상이 명확하게 문서화됨 + +### Task 4. DB 기준 데이터 소스 확정 + +- 목적: MySQL 컨테이너 최초 기동 시 어떤 데이터로 초기화할지 결정 +- 선택지: + - 기존 사내 DB에서 추출한 SQL dump 사용 + - 정리된 스키마 SQL + seed SQL 사용 + - 수동 import 절차 사용 +- 완료 기준: + - `docker/mysql/init` 기준 적재 전략 또는 수동 복원 절차가 확정됨 + +## 5. 시연용 도커라이징 태스크 + +### Task 5. 프런트 Dockerfile 작성 + +- 목적: Vite 개발 서버를 컨테이너에서 구동 +- 작업 내용: + - Node 20 계열 이미지 사용 + - `package*.json` 복사 후 `npm install` + - 8080 포트 노출 + - `npm run dev -- --host 0.0.0.0` 실행 +- 산출물: + - `Dockerfile.frontend` +- 완료 기준: + - 컨테이너에서 8080 포트가 정상 listen 상태가 됨 + +### Task 6. 백엔드 Dockerfile 작성 + +- 목적: Express API 서버를 컨테이너에서 구동 +- 작업 내용: + - Node 20 계열 이미지 사용 + - `package*.json` 복사 후 `npm install` + - 3000 포트 노출 + - `npm run server` 실행 +- 산출물: + - `Dockerfile.backend` +- 완료 기준: + - 컨테이너에서 3000 포트가 정상 listen 상태가 됨 + +### Task 7. MySQL Docker 구성 추가 + +- 목적: DB까지 포함한 재현 가능한 스택 구성 +- 작업 내용: + - `mysql:8.0` 서비스 정의 + - `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD` 설정 + - utf8mb4 문자셋 옵션 반영 + - MySQL 데이터 volume 연결 + - 초기 SQL 적재용 `docker/mysql/init` 디렉터리 설계 +- 산출물: + - `docker-compose.yaml` 내 `db` 서비스 또는 별도 DB compose 확장안 +- 완료 기준: + - MySQL 컨테이너가 정상 기동하고 3306 포트에서 응답 가능 + +### Task 8. backend DB 연결 전환 + +- 목적: backend가 external MySQL 대신 DB 컨테이너를 바라보도록 변경 +- 작업 내용: + - `DB_HOST`를 `db`로 전환 + - 필요 시 `.env.docker` 또는 compose 내부 환경변수 사용 + - backend `depends_on`에 db 추가 +- 산출물: + - DB 컨테이너용 backend 환경 정의 +- 완료 기준: + - backend 로그에서 DB 연결 성공 확인 + +### Task 9. docker-compose.yaml 확장 + +- 목적: frontend/backend를 함께 기동 +- 작업 내용: + - frontend 서비스 정의 + - backend 서비스 정의 + - db 서비스 정의 + - 포트 매핑 추가 + - `.env` 또는 docker 전용 환경변수 연결 + - MySQL 데이터 볼륨 연결 + - `uploads` 볼륨 연결 + - `map_config.json` 처리 방식 반영 +- 산출물: + - `docker-compose.yaml` +- 완료 기준: + - `docker compose up --build` 한 번으로 세 서비스가 모두 올라옴 + +### Task 10. `.dockerignore` 작성 + +- 목적: 불필요한 빌드 컨텍스트 제외 +- 제외 권장 항목: + - `node_modules` + - `dist` + - `build` + - `.git` + - `uploads` + - `*.xlsx` +- 산출물: + - `.dockerignore` +- 완료 기준: + - 이미지 빌드 컨텍스트가 과도하게 커지지 않음 + +## 6. 시연 검증 태스크 + +### Task 11. WSL 컨테이너 기동 검증 + +- 실행 명령: + +```bash +powershell -ExecutionPolicy Bypass -File .\start_docker_wsl.ps1 +``` + +- 확인 항목: + - frontend 로그 에러 여부 + - backend 로그 에러 여부 + - db 로그 에러 여부 + - backend와 db 연결 성공 여부 +- 완료 기준: + - 세 컨테이너 모두 종료 없이 유지됨 + +### Task 12. 웹 접속 검증 + +- 확인 항목: + - `http://localhost:8080` 접속 가능 여부 + - 첫 화면 로딩 여부 + - 콘솔 에러 여부 +- 완료 기준: + - 브라우저에서 초기 화면이 정상 표시됨 + +### Task 13. API 검증 + +- 확인 항목: + - `http://localhost:3000/api/assets/master` + - 프런트에서 `/api/assets/master` 호출 정상 여부 +- 완료 기준: + - 200 응답 또는 정상 데이터 응답 확인 + +### Task 14. DB 초기 데이터 검증 + +- 확인 항목: + - MySQL 컨테이너 내부에 목표 DB가 생성되었는지 + - 기준 테이블이 존재하는지 + - 샘플 데이터 또는 실데이터가 적재되었는지 +- 완료 기준: + - backend가 기대하는 최소 테이블과 데이터가 실제로 조회됨 + +### Task 15. 업로드/파일 저장 검증 + +- 확인 항목: + - `/api/upload` 호출 정상 여부 + - 업로드 파일이 `uploads`에 실제 저장되는지 + - `map_config.json` 수정 내용이 유지되는지 +- 완료 기준: + - 컨테이너 재시작 후에도 저장 데이터가 유지됨 + +## 7. 시연 후 후속 태스크 + +### Task 16. 운영형 프런트 배포 구조 전환 + +- 목표: Vite dev server 대신 정적 빌드 기반 구조로 전환 +- 후보: + - nginx 정적 서빙 + - Express 정적 서빙 + +### Task 17. DB 초기화/마이그레이션 전략 통합 + +- 목표: 기준 스키마와 실행 순서를 단일 정책으로 통일 +- 필요 작업: + - 기준 스키마 선정 + - 초기화 스크립트 확정 + - 마이그레이션 순서 정의 + +### Task 18. `.env.example` 및 배포 환경 분리 + +- 목표: 민감정보를 저장소에서 분리하고 배포별 설정 체계화 + +### Task 19. 운영 볼륨 및 백업 전략 정리 + +- 목표: 업로드 파일과 설정 파일, MySQL 데이터의 장기 보존 정책 정리 + +### Task 20. DB 백업/복원 절차 문서화 + +- 목표: 컨테이너 DB를 기준으로 dump/restore 절차를 문서화 + +## 8. 우선순위 정리 + +### P0: 내일까지 반드시 필요한 작업 + +1. Task 1. 외부 MySQL 접근 가능 여부 확인 +2. Task 2. 기준 스키마 상태 확인 +3. Task 4. DB 기준 데이터 소스 확정 +4. Task 7. MySQL Docker 구성 추가 +5. Task 8. backend DB 연결 전환 +6. Task 9. docker-compose.yaml 확장 +7. Task 11. WSL 컨테이너 기동 검증 +8. Task 12. 웹 접속 검증 +9. Task 13. API 검증 +10. Task 14. DB 초기 데이터 검증 + +### P1: 시연 안정화를 위해 권장되는 작업 + +1. Task 3. 파일 영속성 대상 확인 +2. Task 10. `.dockerignore` 작성 +3. Task 15. 업로드/파일 저장 검증 + +### P2: 시연 이후 진행할 작업 + +1. Task 16. 운영형 프런트 배포 구조 전환 +2. Task 17. DB 초기화/마이그레이션 전략 통합 +3. Task 18. `.env.example` 및 배포 환경 분리 +4. Task 19. 운영 볼륨 및 백업 전략 정리 +5. Task 20. DB 백업/복원 절차 문서화 + +## 9. 개발자용 최종 작업 순서 제안 + +개발 담당자에게는 아래 순서로 진행하라고 전달하면 된다. + +1. 외부 DB 연결 가능 여부부터 확인 +2. 현재 DB 스키마가 앱 요구사항과 맞는지 확인 +3. DB 기준 dump 또는 init SQL 확보 +4. MySQL 컨테이너 구성 추가 +5. backend의 DB 연결 대상을 `db`로 전환 +6. WSL에서 `docker compose config` 확인 +7. WSL에서 컨테이너 기동 테스트 +8. 웹 접속 및 API 확인 +9. 업로드 및 파일 영속성 확인 +10. 시연 완료 후 운영형 구조로 분리 작업 진행 + +## 10. 완료 판단 기준 + +이번 도커라이징 1차 작업은 아래 조건을 만족하면 완료로 본다. + +1. `docker compose up --build`로 프런트, 백엔드, DB가 모두 기동한다. +2. 브라우저에서 8080 화면이 열린다. +3. `/api/assets/master`가 정상 응답한다. +4. backend가 DB 컨테이너와 정상 연결된다. +5. DB 초기 테이블과 데이터가 기대 상태로 적재된다. +6. `uploads`, `map_config.json`, MySQL 데이터가 재시작 후에도 유지된다. + +이 문서는 실제 구현 작업의 체크리스트로 사용한다. \ No newline at end of file diff --git a/server.js b/server.js index 18a5d95..118ed52 100644 --- a/server.js +++ b/server.js @@ -4,7 +4,22 @@ import cors from 'cors'; import dotenv from 'dotenv'; import fs from 'fs'; -dotenv.config({ override: true }); +dotenv.config(); + +const dbConfig = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: parseInt(process.env.DB_PORT || '3306') +}; + +const getDbConnectionSummary = () => ({ + host: dbConfig.host || '(missing)', + port: dbConfig.port, + user: dbConfig.user || '(missing)', + database: dbConfig.database || '(missing)' +}); const app = express(); app.use(cors()); @@ -18,11 +33,11 @@ if (!fs.existsSync('uploads')) { // MySQL Pool Configuration const pool = mysql.createPool({ - host: process.env.DB_HOST, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: process.env.DB_NAME, - port: parseInt(process.env.DB_PORT || '3306'), + host: dbConfig.host, + user: dbConfig.user, + password: dbConfig.password, + database: dbConfig.database, + port: dbConfig.port, waitForConnections: true, connectionLimit: 10, queueLimit: 0 @@ -48,7 +63,15 @@ const pool = mysql.createPool({ `); console.log('✅ job_spec_standards table verification completed.'); } catch (err) { - console.error('❌ Failed to verify/create job_spec_standards table:', err); + console.error('❌ Failed to verify/create job_spec_standards table:', { + db: getDbConnectionSummary(), + code: err.code, + errno: err.errno, + syscall: err.syscall, + address: err.address, + port: err.port, + message: err.message + }); } finally { if (connection) connection.release(); } @@ -56,7 +79,15 @@ const pool = mysql.createPool({ // Error Handler const handleError = (res, err, label) => { - console.error(`❌ [${label}] Error:`, err); + console.error(`❌ [${label}] Error:`, { + db: getDbConnectionSummary(), + code: err.code, + errno: err.errno, + syscall: err.syscall, + address: err.address, + port: err.port, + message: err.message + }); res.status(500).json({ error: err.message }); }; diff --git a/start_docker_wsl.bat b/start_docker_wsl.bat new file mode 100644 index 0000000..36ee26e --- /dev/null +++ b/start_docker_wsl.bat @@ -0,0 +1,10 @@ +@echo off +chcp 65001 >nul +cd /d "%~dp0" +powershell -ExecutionPolicy Bypass -File "%~dp0start_docker_wsl.ps1" +if errorlevel 1 ( + echo. + echo [ERROR] start_docker_wsl.ps1 failed. + pause + exit /b %errorlevel% +) \ No newline at end of file diff --git a/start_docker_wsl.ps1 b/start_docker_wsl.ps1 new file mode 100644 index 0000000..66e4975 --- /dev/null +++ b/start_docker_wsl.ps1 @@ -0,0 +1,107 @@ +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +$projectWindowsPath = $PSScriptRoot +$wslProjectPath = (wsl wslpath $projectWindowsPath).Trim() +$envFilePath = Join-Path $PSScriptRoot '.env' + +function Get-EnvValue { + param( + [string]$FilePath, + [string]$Key + ) + + if (-not (Test-Path $FilePath)) { + return $null + } + + $line = Get-Content $FilePath | Where-Object { $_ -match "^$Key=" } | Select-Object -First 1 + if (-not $line) { + return $null + } + + return ($line -split '=', 2)[1].Trim() +} + +function Test-TcpPortFast { + param( + [string]$HostName, + [int]$Port, + [int]$TimeoutMs = 3000 + ) + + $client = New-Object System.Net.Sockets.TcpClient + try { + $asyncResult = $client.BeginConnect($HostName, $Port, $null, $null) + if (-not $asyncResult.AsyncWaitHandle.WaitOne($TimeoutMs, $false)) { + $client.Close() + return $false + } + + $client.EndConnect($asyncResult) + $client.Close() + return $true + } + catch { + $client.Close() + return $false + } +} + +Write-Host "============================================" -ForegroundColor Cyan +Write-Host " HM ITAM WSL Docker Start" -ForegroundColor Cyan +Write-Host "============================================" -ForegroundColor Cyan +Write-Host "" + +Write-Host "[INFO] Checking WSL..." +wsl -l -v +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] WSL is not available." -ForegroundColor Red + exit 1 +} + +Write-Host "[INFO] Checking Docker in WSL..." +wsl sh -lc "docker --version" +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Docker is not available inside WSL." -ForegroundColor Red + exit 1 +} + +$dbHost = Get-EnvValue -FilePath $envFilePath -Key 'DB_HOST' +$dbPort = Get-EnvValue -FilePath $envFilePath -Key 'DB_PORT' + +if (-not $dbPort) { + $dbPort = '3306' +} + +if (-not $dbHost) { + Write-Host "[WARN] .env is missing DB_HOST. Containers will still start, but backend DB calls will fail until DB settings are fixed." -ForegroundColor Yellow +} + +if ($dbHost) { + Write-Host "[INFO] Checking external DB reachability..." + $dbReachable = Test-TcpPortFast -HostName $dbHost -Port ([int]$dbPort) + if (-not $dbReachable) { + Write-Host "[WARN] External DB is unreachable: $dbHost`:$dbPort" -ForegroundColor Yellow + Write-Host "[HINT] Containers will still start. Check VPN/private network connection, firewall rules, DB host/port in .env, or whether the DB server is running." -ForegroundColor Yellow + } +} + +Write-Host "[INFO] Starting ITAM containers in WSL..." +wsl sh -lc "cd '$wslProjectPath' && docker compose up --build -d --remove-orphans" +if ($LASTEXITCODE -ne 0) { + Write-Host "[WARN] Build-based startup failed. Retrying with cached images/containers..." -ForegroundColor Yellow + wsl sh -lc "cd '$wslProjectPath' && docker compose up -d --remove-orphans" + if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Failed to start containers." -ForegroundColor Red + exit 1 + } +} + +Write-Host "" +Write-Host "============================================" -ForegroundColor Green +Write-Host " [OK] WSL Docker stack started." -ForegroundColor Green +Write-Host " [INFO] Frontend: http://localhost:8080" +Write-Host " [INFO] Backend : http://localhost:3000/api/assets/master" +Write-Host "============================================" -ForegroundColor Green + +Start-Process "http://localhost:8080" \ No newline at end of file diff --git a/start_server.ps1 b/start_server.ps1 index ef6e687..917b976 100644 --- a/start_server.ps1 +++ b/start_server.ps1 @@ -1,6 +1,49 @@ # HM ITAM Server Start Script [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +function Get-EnvValue { + param( + [string]$FilePath, + [string]$Key + ) + + if (-not (Test-Path $FilePath)) { + return $null + } + + $line = Get-Content $FilePath | Where-Object { $_ -match "^$Key=" } | Select-Object -First 1 + if (-not $line) { + return $null + } + + return ($line -split '=', 2)[1].Trim() +} + +function Test-TcpPortFast { + param( + [string]$HostName, + [int]$Port, + [int]$TimeoutMs = 3000 + ) + + $client = New-Object System.Net.Sockets.TcpClient + try { + $asyncResult = $client.BeginConnect($HostName, $Port, $null, $null) + if (-not $asyncResult.AsyncWaitHandle.WaitOne($TimeoutMs, $false)) { + $client.Close() + return $false + } + + $client.EndConnect($asyncResult) + $client.Close() + return $true + } + catch { + $client.Close() + return $false + } +} + Write-Host "============================================" -ForegroundColor Cyan Write-Host " HM ITAM System Start" -ForegroundColor Cyan Write-Host "============================================" -ForegroundColor Cyan @@ -21,6 +64,13 @@ if (-not (Test-Path "node_modules")) { Write-Host "[INFO] Checking ports..." $backendPort = 3000 $frontendPort = 8080 +$envFilePath = Join-Path $PSScriptRoot '.env' +$dbHost = Get-EnvValue -FilePath $envFilePath -Key 'DB_HOST' +$dbPort = Get-EnvValue -FilePath $envFilePath -Key 'DB_PORT' + +if (-not $dbPort) { + $dbPort = '3306' +} if (Get-NetTCPConnection -LocalPort $backendPort -ErrorAction SilentlyContinue) { Write-Host "[WARNING] Port $backendPort [Backend] is already in use." -ForegroundColor Yellow @@ -30,6 +80,21 @@ if (Get-NetTCPConnection -LocalPort $frontendPort -ErrorAction SilentlyContinue) Write-Host "[WARNING] Port $frontendPort [Frontend] is already in use." -ForegroundColor Yellow } +if (-not $dbHost) { + Write-Host "[WARNING] .env is missing DB_HOST. Backend and frontend will still start, but DB calls will fail until DB settings are fixed." -ForegroundColor Yellow +} +else { + Write-Host "[INFO] Checking external DB reachability..." + $dbReachable = Test-TcpPortFast -HostName $dbHost -Port ([int]$dbPort) + if ($dbReachable) { + Write-Host "[INFO] External DB reachable: $dbHost`:$dbPort" + } + else { + Write-Host "[WARNING] External DB is unreachable: $dbHost`:$dbPort" -ForegroundColor Yellow + Write-Host "[WARNING] Backend and frontend will still start, but DB-backed screens and APIs may fail." -ForegroundColor Yellow + } +} + Write-Host "" Write-Host "[INFO] Starting Backend [Port: 3000]..." Start-Process cmd -ArgumentList "/k npm run server" diff --git a/stop_docker_wsl.bat b/stop_docker_wsl.bat new file mode 100644 index 0000000..b9b3955 --- /dev/null +++ b/stop_docker_wsl.bat @@ -0,0 +1,4 @@ +@echo off +chcp 65001 >nul +cd /d "%~dp0" +powershell -ExecutionPolicy Bypass -File "%~dp0stop_docker_wsl.ps1" \ No newline at end of file diff --git a/stop_docker_wsl.ps1 b/stop_docker_wsl.ps1 new file mode 100644 index 0000000..bfd7656 --- /dev/null +++ b/stop_docker_wsl.ps1 @@ -0,0 +1,13 @@ +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +$projectWindowsPath = $PSScriptRoot +$wslProjectPath = (wsl wslpath $projectWindowsPath).Trim() + +Write-Host "[INFO] Stopping ITAM WSL Docker stack..." +wsl sh -lc "cd '$wslProjectPath' && docker compose down --remove-orphans" +if ($LASTEXITCODE -ne 0) { + Write-Host "[ERROR] Failed to stop containers." -ForegroundColor Red + exit 1 +} + +Write-Host "[OK] WSL Docker stack stopped." -ForegroundColor Green \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 56b1c1c..ba7f424 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,16 +1,18 @@ import { defineConfig } from 'vite'; +const proxyTarget = process.env.VITE_DEV_PROXY_TARGET || 'http://localhost:3000'; + export default defineConfig({ server: { port: 8080, host: true, // Listen on all local IPs proxy: { '/api': { - target: 'http://localhost:3000', + target: proxyTarget, changeOrigin: true, }, '/uploads': { - target: 'http://localhost:3000', + target: proxyTarget, changeOrigin: true, } }