diff --git a/CLAUDE.md b/CLAUDE.md index a2de483..15c72aa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,7 +12,7 @@ SAM 프로젝트의 기술적 개요 문서입니다. 이 문서를 참조하면 **핵심 요약**: - **회사**: 주일/경동 (블라인드/스크린 제조업체) - **프로젝트**: SAM (Smart Automation Management) - 차세대 ERP/MES 통합 시스템 -- **기술 스택**: Laravel 11 + HTMX + Tailwind CSS + MySQL 8.0 +- **기술 스택**: Laravel 12 + HTMX + Tailwind CSS + MySQL 8.0 (PHP 8.4) - **아키텍처**: Multi-tenant (tenant_id 기반 데이터 격리) - **레거시**: 5130.co.kr (PHP 기반) → SAM으로 마이그레이션 중 @@ -120,6 +120,10 @@ fix: [auth] 로그인 시 세션 만료 오류 수정 **각 폴더는 독립적인 Git 저장소입니다. 해당 폴더에서 git 명령을 실행해야 합니다.** +> **서버 경로 참고**: +> - 개발/운영 서버 모두 `/home/webservice/` 하위에 동일한 구조로 배치 +> - 서버: `/home/webservice/api`, `/home/webservice/mng`, `/home/webservice/react`, `/home/webservice/sales` + --- ## 서버 접근 정책 (3단계 분류) @@ -191,24 +195,33 @@ Claude가 **절대 직접 실행하지 않으며**, 사용자에게 명령어를 ### 서버 접속 정보 -| 서버 | 호스트 | 계정 | -|------|--------|------| -| 운영 서버 | `114.203.209.83` | `pro` | +| 서버 | 호스트 | 계정 | 역할 | +|------|--------|------|------| +| 개발 서버 | `114.203.209.83` | `pro`, `hskwon` | 개발/스테이징 + Jenkins CI/CD + Gitea | +| 운영 서버 | (신규, 미확정) | 별도 계정 | 정식 서비스 | -### 배포 흐름 +> **참고**: Jenkins(`114.203.209.83:8080`)와 Gitea(`114.203.209.83:3000`)는 개발 서버에서 운영한다. + +### 배포 흐름 (Jenkins CI/CD) ``` -Claude 역할 사용자/팀장 역할 -┌───────────────────────┐ ┌─────────────────┐ -│ 코드 작성/수정 │ │ │ -│ git add / git commit │ │ │ -│ │──push──→ │ git push │ -│ 서버 진단 (Level 1) │ │ │ -│ 서버 변경 (Level 2) │──확인──→ │ 승인 │ -│ 위험 작업 (Level 3) │──안내──→ │ 직접 실행 │ -└───────────────────────┘ └─────────────────┘ +Claude 역할 Jenkins (자동) 운영 서버 +┌───────────────────┐ ┌──────────────────┐ ┌──────────────┐ +│ 코드 작성/수정 │ │ │ │ │ +│ git add / commit │ │ │ │ │ +│ │─push──→ │ Gitea Webhook │ │ │ +│ │(사용자) │ → Jenkins 빌드 │ │ │ +│ │ │ → Lint/Test │ │ │ +│ │ │ → SSH Deploy ────│──→ │ git pull │ +│ │ │ │ │ composer │ +│ 서버 진단 (L1) │ │ │ │ migrate │ +│ 서버 변경 (L2) │─확인──→ │ │ │ │ +│ 위험 작업 (L3) │─안내──→ │ │ │ (팀장 직접) │ +└───────────────────┘ └──────────────────┘ └──────────────┘ ``` +> **브랜치 전략**: `develop` → 개발 서버 (자동 배포), `main`/`master` → 운영 서버 (PR 머지 + 팀장 승인) + ### 체크리스트 (서버 작업 시) - [ ] Level 1 (읽기): 사용자 요청 시 즉시 실행 @@ -220,39 +233,43 @@ Claude 역할 사용자/팀장 역할 ## React 빌드/배포 정책 (필수 규칙) -> **경고: React(Next.js) 빌드는 반드시 로컬에서 실행합니다. 서버에서 빌드 절대 금지!** +> **경고: React(Next.js) 빌드를 운영 서버에서 직접 실행하지 않는다!** ### 배경 -서버 스펙(2코어, 3.8GB RAM, Swap 없음)으로는 Next.js 빌드 시 메모리 부족으로 20분 이상 소요되거나 실패한다. -로컬(WSL)에서 빌드 후 결과물만 서버에 배포한다. +개발 서버(2코어, 3.8GB RAM + Swap 4GB)에서 Jenkins가 React 빌드를 수행한다. +Jenkins 빌드 실패 시 로컬(WSL)에서 빌드 후 결과물을 서버에 배포한다(fallback). ### 금지 사항 ``` -❌ 서버에서 npm run build 실행 금지 +❌ 운영 서버에서 npm run build 실행 금지 ❌ 서버 SSH 접속 후 빌드 명령 실행 금지 ❌ Claude가 직접 npm run build 실행 금지 (로컬 포함) ``` -### 빌드/배포 방법 +### 빌드/배포 방법 (Jenkins 자동화) ``` -Claude 역할 사용자/팀장 역할 -┌─────────────────┐ ┌─────────────────┐ -│ 코드 작성/수정 │ │ │ -│ git commit │──push──→ │ git pull │ -│ │ │ npm run build │ -│ │ │ 서비스 재시작 │ -└─────────────────┘ └─────────────────┘ +Claude 역할 Jenkins (자동) 운영 서버 +┌─────────────────┐ ┌──────────────────┐ ┌──────────────┐ +│ 코드 작성/수정 │ │ Checkout │ │ │ +│ git commit │─push──→ │ Install + Lint │ │ │ +│ │(사용자) │ Build (Next.js) │ │ │ +│ │ │ Package (tar.gz) │──scp→ │ 압축 해제 │ +│ │ │ │ │ node 재시작 │ +└─────────────────┘ └──────────────────┘ └──────────────┘ ``` +> **Fallback**: Jenkins 빌드 실패(OOM) 시 로컬에서 `react/deploy.sh`로 수동 배포 + ### 빌드가 필요한 상황 사용자에게 다음과 같이 안내한다: ``` -React 코드가 변경되었습니다. git push 후 서버에서 배포해주세요. +React 코드가 변경되었습니다. git push 후 Jenkins가 자동 배포합니다. +(Jenkins 실패 시 로컬에서 deploy.sh로 수동 배포해주세요.) ``` --- @@ -291,12 +308,26 @@ React 코드가 변경되었습니다. git push 후 서버에서 배포해주세 # 로컬: 마이그레이션은 반드시 API 컨테이너에서 실행 docker exec sam-api-1 php artisan migrate -# 서버: Docker 없음, 직접 실행 +# 개발 서버: Docker 없음, 직접 실행 cd /home/webservice/api && php artisan migrate +# 운영 서버: --force 플래그 필수 (production 환경) +cd /home/webservice/api && php artisan migrate --force + # MNG에서 마이그레이션 실행 금지 (로컬/서버 모두) ``` +### DB 환경 분리 + +| 환경 | DB명 | 호스트 | 용도 | +|------|------|--------|------| +| 로컬 (Docker) | `samdb` | `sam-mysql-1:3306` | 개발/테스트 | +| 개발 서버 | `samdb` | `localhost` | 스테이징 | +| 운영 서버 | `sam_prod` | `localhost` | 정식 서비스 | +| 통계 DB | `sam_stat` | 동일 서버 | StatMonitorService 전용 | + +> **참고**: `sam_stat`은 API/MNG 모두 `config/database.php`의 별도 connection으로 접속한다. + ### 이유 - MNG: 프론트엔드/관리자 화면 담당 (컨트롤러, 뷰, 라우트) @@ -336,7 +367,8 @@ cd /home/webservice/api && php artisan migrate ``` 메뉴를 추가하려면 아래 명령을 서버에서 실행해 주세요: -ssh sam-server "cd /home/webservice/mng && php artisan tinker --execute=\" +# 개발 서버 +ssh pro@114.203.209.83 "cd /home/webservice/mng && php artisan tinker --execute=\" App\\Models\\Commons\\Menu::create([ 'tenant_id' => 1, 'parent_id' => <부모ID>, @@ -347,6 +379,9 @@ App\\Models\\Commons\\Menu::create([ 'is_active' => true, ]); \"" + +# 운영 서버 (동일 경로, 서버 주소만 변경) +ssh <운영계정>@<운영서버IP> "cd /home/webservice/mng && php artisan tinker --execute=\"...동일...\"" ``` ### 체크리스트 (메뉴 변경 요청 시) @@ -360,17 +395,20 @@ App\\Models\\Commons\\Menu::create([ ## 실행 환경 (필수 인지) -> **중요: 로컬과 서버의 환경이 다릅니다!** +> **중요: 로컬 / 개발 서버 / 운영 서버의 환경이 다릅니다!** -### 환경 비교 +### 환경 비교 (3-Tier) -| 항목 | 로컬 (WSL) | 서버 (운영) | -|------|-----------|------------| -| **구성 방식** | Docker 컨테이너 | 네이티브 설치 (Docker 없음) | -| **PHP/Laravel** | 컨테이너 내부 | 서버에 직접 설치 | -| **MySQL** | 컨테이너 (sam-mysql-1) | 서버에 직접 설치 | -| **Nginx** | 컨테이너 (sam-nginx-1) | 서버에 직접 설치 | -| **명령 실행** | `docker exec` 필요 | 직접 실행 | +| 항목 | 로컬 (WSL) | 개발 서버 | 운영 서버 | +|------|-----------|----------|----------| +| **구성 방식** | Docker 컨테이너 | Bare-metal (네이티브) | Bare-metal (네이티브) | +| **PHP** | 컨테이너 내부 (8.4) | 직접 설치 (8.4) | 직접 설치 (8.4) | +| **MySQL** | 컨테이너 (sam-mysql-1) | 직접 설치 (8.0) | 직접 설치 (8.0) | +| **Nginx** | 컨테이너 (sam-nginx-1) | 직접 설치 | 직접 설치 | +| **명령 실행** | `docker exec` 필요 | 직접 실행 | 직접 실행 | +| **서버 IP** | localhost | `114.203.209.83` | (신규, 미확정) | +| **추가 서비스** | — | Jenkins, Gitea | — | +| **DB** | `samdb` | `samdb` | `sam_prod` | > **배경**: 서버는 Docker가 무거워서 PHP, Nginx, MySQL 등을 네이티브로 설치하여 운영한다. @@ -388,27 +426,50 @@ PHP, Laravel, Node.js 등이 **Docker 컨테이너 안에** 설치되어 있다. └── sam-nginx-1 ← Nginx 웹서버 ``` -### 서버 환경 (네이티브) +### 서버 환경 (Bare-metal — 개발/운영 동일 구조) -서버에는 Docker가 없다. PHP, Nginx, MySQL이 직접 설치되어 있다. +서버에는 Docker가 없다. PHP 8.4, Nginx, MySQL 8.0이 직접 설치되어 있다. ``` -운영 서버 (114.203.209.83) -├── Nginx ← 웹서버 (직접 설치) -├── PHP-FPM ← PHP 처리 (직접 설치) -├── MySQL ← DB (직접 설치) -├── /home/webservice/mng ← MNG 앱 -└── /home/webservice/api ← API 앱 +개발 서버 (114.203.209.83) 운영 서버 (신규) +├── Nginx ├── Nginx +├── PHP-FPM (3 pools) ├── PHP-FPM (3 pools) +│ ├── api.sock │ ├── api.sock +│ ├── mng.sock │ ├── mng.sock +│ └── sales.sock │ └── sales.sock +├── MySQL 8.0 (samdb) ├── MySQL 8.0 (sam_prod) +├── Supervisor ├── Supervisor +│ ├── sam-api-worker (x1) │ ├── sam-api-worker (x1) +│ ├── sam-mng-worker (x2) │ ├── sam-mng-worker (x2) +│ └── sam-api-scheduler │ └── sam-api-scheduler +├── Node.js (React SSR :3000) ├── Node.js (React SSR :3000) +├── Jenkins (:8080) │ +├── Gitea (:3000) │ +├── /home/webservice/api ├── /home/webservice/api +├── /home/webservice/mng ├── /home/webservice/mng +├── /home/webservice/react ├── /home/webservice/react +└── /home/webservice/sales └── /home/webservice/sales ``` -### 명령어 비교 (로컬 vs 서버) +### 도메인 매핑 -| 작업 | 로컬 (Docker) | 서버 (네이티브) | -|------|--------------|----------------| +| 서비스 | 로컬 (Docker) | 개발 서버 | 운영 서버 | +|--------|--------------|----------|----------| +| React (사용자) | `dev.sam.kr` | `dev.codebridge-x.com` | `codebridge-x.com` | +| API | `api.sam.kr` | `api.dev.codebridge-x.com` | `api.codebridge-x.com` | +| MNG (관리자) | `mng.sam.kr` | `mng.dev.codebridge-x.com` | `mng.codebridge-x.com` | +| Sales | `sales.sam.kr` | `sales.dev.codebridge-x.com` | `sales.codebridge-x.com` | +| 5130 (레거시) | `5130.sam.kr` | — | — | + +### 명령어 비교 (로컬 vs 개발 vs 운영) + +| 작업 | 로컬 (Docker) | 개발/운영 서버 (네이티브) | +|------|--------------|-------------------------| | artisan 실행 | `docker exec sam-api-1 php artisan <명령>` | `cd /home/webservice/api && php artisan <명령>` | | composer 실행 | `docker exec sam-api-1 composer install` | `cd /home/webservice/api && composer install` | -| 마이그레이션 | `docker exec sam-api-1 php artisan migrate` | `cd /home/webservice/api && php artisan migrate` | +| 마이그레이션 | `docker exec sam-api-1 php artisan migrate` | 개발: `php artisan migrate` / 운영: `php artisan migrate --force` | | 캐시 클리어 | `docker exec sam-mng-1 php artisan cache:clear` | `cd /home/webservice/mng && php artisan cache:clear` | +| Queue 재시작 | — | `sudo supervisorctl restart sam-api-worker:*` | ### 로컬 Docker 명령어 패턴 @@ -434,6 +495,7 @@ docker exec sam-mng-1 php artisan cache:clear - [ ] **로컬**: `php artisan` → `docker exec sam-mng-1 php artisan` 또는 `sam-api-1` 사용 - [ ] **로컬**: `composer` → `docker exec sam-mng-1 composer` 또는 `sam-api-1` 사용 - [ ] **서버**: `php artisan`, `composer` 직접 실행 (Docker 없음) +- [ ] **운영 서버 마이그레이션**: `--force` 플래그 필수 - [ ] **마이그레이션은 반드시 API에서 실행** (로컬: `docker exec sam-api-1`, 서버: 직접) --- @@ -442,6 +504,22 @@ docker exec sam-mng-1 php artisan cache:clear > **중요: 코드를 pull 받은 후 반드시 필요한 명령을 실행하세요!** +### 브랜치 전략 + +| 브랜치 | 배포 대상 | 트리거 | 승인 | +|--------|----------|--------|------| +| `feature/*` | — | — | — | +| `develop` | 개발 서버 (`dev.codebridge-x.com`) | Push 시 자동 배포 | 불필요 | +| `main`/`master` | 운영 서버 (`codebridge-x.com`) | PR 머지 시 Jenkins 배포 | 팀장 승인 필수 | + +``` +feature/* ──→ develop ──→ main/master + (push) (PR merge) + ↓ ↓ + 개발 서버 운영 서버 + (자동 배포) (Jenkins CI/CD) +``` + ### 로컬 환경 (Docker) 업데이트 ```bash @@ -464,38 +542,64 @@ docker exec sam-api-1 php artisan config:clear docker exec sam-mng-1 php artisan config:clear ``` -### 서버 환경 업데이트 (네이티브 — Docker 없음) +### 개발 서버 업데이트 (자동) -> 서버에는 Docker가 없다. PHP, Composer 등이 직접 설치되어 있으므로 명령어를 그대로 실행한다. +> `develop` 브랜치에 Push 시 Gitea Webhook → Jenkins가 자동으로 배포한다. +> 수동 배포가 필요한 경우: ```bash # API 프로젝트 cd /home/webservice/api -git pull +git pull origin develop composer install php artisan migrate php artisan config:clear # MNG 프로젝트 (마이그레이션 없음) cd /home/webservice/mng -git pull +git pull origin develop composer install php artisan config:clear ``` +### 운영 서버 배포 (Jenkins 자동화) + +> `main`/`master` 브랜치에 PR 머지 시 Jenkins가 자동으로 배포한다. +> 수동 배포는 **비상 절차**로만 사용한다. + +```bash +# 비상 수동 배포 (Jenkins 장애 시에만) +# API 프로젝트 +cd /home/webservice/api +git pull origin main +composer install --no-dev --optimize-autoloader +php artisan migrate --force +php artisan config:clear && php artisan cache:clear && php artisan route:cache && php artisan view:cache +sudo supervisorctl restart sam-api-worker:* + +# MNG 프로젝트 +cd /home/webservice/mng +git pull origin master +composer install --no-dev --optimize-autoloader +php artisan config:clear && php artisan cache:clear && php artisan view:cache +sudo supervisorctl restart sam-mng-worker:* +``` + ### 요약 표 -| 작업 | 로컬 (Docker) | 서버 (네이티브) | -|------|--------------|----------------| -| git pull | WSL에서 직접 | 서버에서 직접 | -| composer install | `docker exec sam-api-1 composer install` | `composer install` (직접) | -| migrate | `docker exec sam-api-1 php artisan migrate` | `php artisan migrate` (직접) | -| config:clear | `docker exec sam-api-1 php artisan config:clear` | `php artisan config:clear` (직접) | +| 작업 | 로컬 (Docker) | 개발 서버 | 운영 서버 | +|------|--------------|----------|----------| +| 배포 방식 | 수동 | Jenkins 자동 (develop push) | Jenkins 자동 (main/master PR) | +| git pull | WSL에서 직접 | Jenkins 자동 | Jenkins 자동 | +| composer install | `docker exec sam-api-1 composer install` | Jenkins 자동 | `--no-dev --optimize-autoloader` | +| migrate | `docker exec sam-api-1 php artisan migrate` | Jenkins 자동 | `--force` 플래그 포함 | +| config:clear | `docker exec sam-api-1 php artisan config:clear` | Jenkins 자동 | `route:cache` + `view:cache` 포함 | ### 체크리스트 (pull 후) - [ ] API: `git pull` → `composer install` → `php artisan migrate` → `config:clear` - [ ] MNG: `git pull` → `composer install` → `config:clear` (마이그레이션 없음) +- [ ] 운영 배포: `main`/`master`에 PR 머지 → Jenkins 자동 처리 (수동 금지) ---