Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- Docker 컨테이너 구성 및 실행 방법 명시 - 해결된 주요 문제들 문서화: * 프론트엔드 API 연결 오류 (10080 포트 문제) * 백엔드 데이터베이스 연결 실패 (localhost vs postgres) * 환경별 설정 관리 개선 - 실행 전 체크리스트 제공으로 향후 헷갈림 방지
286 lines
9.3 KiB
Markdown
286 lines
9.3 KiB
Markdown
# 🏗️ TK-MP-Project Rules & Context
|
||
|
||
## 📋 **프로젝트 개요**
|
||
- **목적**: 배관 자재 BOM 관리 및 리비전 비교 시스템
|
||
- **주요 기능**: 파일 업로드, 자재 분류, 리비전 비교, 구매 관리, 엑셀 내보내기
|
||
|
||
## 🛠️ **기술 스택**
|
||
```
|
||
Frontend: React.js + Material-UI + Vite + React Router DOM + Nginx
|
||
Backend: FastAPI + SQLAlchemy + Python + Uvicorn
|
||
Database: PostgreSQL (운영 및 개발)
|
||
캐시: Redis
|
||
관리도구: pgAdmin4
|
||
컨테이너: Docker + Docker Compose
|
||
기타: Axios, XLSX (SheetJS), file-saver
|
||
```
|
||
|
||
## 📁 **프로젝트 구조**
|
||
```
|
||
TK-MP-Project/
|
||
├── frontend/src/
|
||
│ ├── pages/ # 페이지 컴포넌트
|
||
│ ├── components/ # 재사용 컴포넌트
|
||
│ ├── utils/ # 유틸리티 (엑셀 등)
|
||
│ └── api.js # API 통신
|
||
├── backend/app/
|
||
│ ├── routers/ # API 라우터
|
||
│ ├── services/ # 비즈니스 로직 (분류기 등)
|
||
│ ├── models.py # DB 모델
|
||
│ └── main.py # FastAPI 앱
|
||
└── database/ # DB 스키마/시드
|
||
```
|
||
|
||
## 🐳 **Docker 실행 환경**
|
||
### 컨테이너 구성
|
||
- **tk-mp-frontend**: React + Nginx (포트: 3000)
|
||
- **tk-mp-backend**: FastAPI + Uvicorn (포트: 8000)
|
||
- **tk-mp-postgres**: PostgreSQL (포트: 5432)
|
||
- **tk-mp-redis**: Redis (포트: 6379)
|
||
- **tk-mp-pgadmin**: pgAdmin4 (포트: 5050)
|
||
|
||
### 실행 방법
|
||
```bash
|
||
# 개발 환경
|
||
./scripts/dev.sh
|
||
# 또는
|
||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
|
||
|
||
# 프로덕션 환경
|
||
./scripts/prod.sh
|
||
# 또는
|
||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
||
|
||
# 기본 환경
|
||
docker-compose up -d
|
||
```
|
||
|
||
### 중요한 설정 사항
|
||
- **API URL 설정**:
|
||
- 개발환경: `http://localhost:8000` (직접 접근)
|
||
- 프로덕션: `/api` (nginx 프록시를 통한 상대경로)
|
||
- **데이터베이스 연결**: `postgres:5432` (컨테이너명 사용, localhost 아님!)
|
||
- **환경변수**: `VITE_API_URL`로 API URL 오버라이드 가능
|
||
|
||
## 🗄️ **핵심 데이터베이스 스키마**
|
||
```sql
|
||
-- 핵심 테이블들
|
||
jobs (job_no, job_name, client_name, ...)
|
||
files (id, job_no, revision, original_filename, ...)
|
||
materials (id, file_id, original_description, classified_category, quantity, ...)
|
||
pipe_details (material_id, length_mm, ...)
|
||
-- 기타: fitting_details, flange_details, bolt_details, gasket_details
|
||
```
|
||
|
||
## 🔧 **중요한 코딩 컨벤션 & 패턴**
|
||
|
||
### **1. 자재 분류 시스템**
|
||
```python
|
||
# 항상 이 순서로 분류기 호출
|
||
classification_result = classify_pipe("", description, main_nom, length_value)
|
||
# 결과: {"category": "PIPE", "confidence": 0.95, ...}
|
||
```
|
||
|
||
### **2. 파이프 길이 처리 규칙**
|
||
```javascript
|
||
// ❌ 절대 하지 말 것: 평균 길이 계산/표시
|
||
// ✅ 항상 할 것: 총 길이 기준 계산
|
||
const totalLength = quantity * unitLength; // 총 길이 = 수량 × 단위길이
|
||
```
|
||
|
||
### **3. 자재 해싱 규칙**
|
||
```python
|
||
# 자재 고유성 판단: description + size + material_grade
|
||
material_hash = hashlib.md5(f"{description}|{size_spec}|{material_grade}".encode()).hexdigest()
|
||
```
|
||
|
||
### **4. 리비전 비교 로직**
|
||
```python
|
||
# 이전 리비전 자동 탐지: 숫자 기반 비교
|
||
current_rev_num = int(current_revision.replace("Rev.", ""))
|
||
# Rev.0 → Rev.1 → Rev.2 순서
|
||
```
|
||
|
||
## 🐛 **자주 발생하는 이슈 & 해결법**
|
||
|
||
### **1. 파이프 길이 합산 문제**
|
||
```python
|
||
# ❌ 잘못된 SQL: GROUP BY에 pd.length_mm 포함
|
||
# ✅ 올바른 방법: Python에서 같은 파이프들 합치기
|
||
if material_hash in materials_dict:
|
||
existing["quantity"] += float(new_quantity)
|
||
existing["total_length"] += new_quantity * unit_length
|
||
```
|
||
|
||
### **2. 프론트엔드 변수 초기화**
|
||
```javascript
|
||
// ❌ 사용 전에 선언하지 않음
|
||
const summaryData = [..., consolidatedMaterials.length, ...];
|
||
const consolidatedMaterials = consolidateMaterials(materials); // 뒤에 선언
|
||
|
||
// ✅ 사용 전에 먼저 선언
|
||
const consolidatedMaterials = consolidateMaterials(materials);
|
||
const summaryData = [..., consolidatedMaterials.length, ...];
|
||
```
|
||
|
||
### **3. API 응답 처리**
|
||
```javascript
|
||
// ✅ 항상 Axios 응답 구조 확인
|
||
setComparisonResult(result.data || result); // response.data 우선
|
||
```
|
||
|
||
## 🎯 **UI/UX 가이드라인**
|
||
|
||
### **1. 자재 표시 규칙**
|
||
- **파이프**: "총 길이: 4,561mm" (평균단위 표시 금지)
|
||
- **기타 자재**: "수량: 24 EA"
|
||
- **변경사항**: "이전: 2,781mm → 현재: 4,561mm / 변화: +1,780mm"
|
||
|
||
### **2. 버튼 네이밍**
|
||
- "BOM 목록으로" (뒤로가기)
|
||
- "엑셀 내보내기"
|
||
- "상세 비교 보기"
|
||
|
||
### **3. 페이지 네비게이션**
|
||
```javascript
|
||
// BOM 관련 페이지들은 job_no 기준으로 이동
|
||
navigate(`/bom-status?job_no=${jobNo}`);
|
||
navigate(`/material-comparison?job_no=${jobNo}&revision=${revision}`);
|
||
```
|
||
|
||
## 🔄 **개발 워크플로우**
|
||
|
||
### **1. 서버 실행 명령어**
|
||
```bash
|
||
# 백엔드 실행 (터미널 1번) - TK-MP-Project 루트에서
|
||
source venv/bin/activate # 가상환경 활성화 (venv는 루트에 있음)
|
||
cd backend
|
||
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||
|
||
# 프론트엔드 실행 (터미널 2번) - TK-MP-Project 루트에서
|
||
cd frontend
|
||
npm run dev # npm start 아님!
|
||
```
|
||
|
||
**접속 주소:**
|
||
- 백엔드 API: http://localhost:8000
|
||
- API 문서: http://localhost:8000/docs
|
||
- 프론트엔드: http://localhost:5173
|
||
|
||
### **2. 백엔드 변경 시**
|
||
```bash
|
||
# 항상 가상환경에서 실행 (사용자 선호사항)
|
||
cd backend
|
||
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
### **3. 데이터베이스 스키마 변경 시**
|
||
```sql
|
||
-- scripts/ 폴더에 마이그레이션 SQL 파일 생성
|
||
-- 번호 순서: 01_, 02_, 03_...
|
||
```
|
||
|
||
### **4. 커밋 메시지**
|
||
```
|
||
한국어로 작성 (사용자 선호사항)
|
||
예: "파이프 길이 계산 및 엑셀 내보내기 버그 수정"
|
||
```
|
||
|
||
## ⚠️ **절대 하지 말아야 할 것들**
|
||
|
||
1. **파이프 "평균단위" 표시** - 사용자가 혼란스러워함
|
||
2. **하드코딩된 길이 값** - 실제 데이터베이스 값 사용
|
||
3. **영어 커밋 메시지** - 사용자가 한국어 선호
|
||
4. **SQL에서 과도한 GROUP BY** - 같은 자재 분리됨
|
||
5. **비율 기반 길이 계산** - 실제 총길이 사용해야 함
|
||
|
||
## 💰 **구매 수량 계산 규칙**
|
||
|
||
### **1. 파이프 (PIPE)**
|
||
```javascript
|
||
// 6,000mm 단위 판매 + 절단여유분 2mm/조각
|
||
const cutLength = originalLength + 2; // 절단 여유분
|
||
const pipeCount = Math.ceil(cutLength / 6000); // 올림 처리
|
||
```
|
||
|
||
### **2. 피팅/계기/밸브 (FITTING/INSTRUMENT/VALVE)**
|
||
```javascript
|
||
// BOM 수량 그대로
|
||
const purchaseQuantity = bomQuantity;
|
||
```
|
||
|
||
### **3. 볼트/너트 (BOLT)**
|
||
```javascript
|
||
// +5% 후 4의 배수로 올림
|
||
const withMargin = bomQuantity * 1.05;
|
||
const purchaseQuantity = Math.ceil(withMargin / 4) * 4;
|
||
// 예: 150 → 157.5 → 160 SETS
|
||
```
|
||
|
||
### **4. 가스켓 (GASKET)**
|
||
```javascript
|
||
// 5의 배수로 올림
|
||
const purchaseQuantity = Math.ceil(bomQuantity / 5) * 5;
|
||
// 예: 7 → 10 EA
|
||
```
|
||
|
||
## 🎯 **현재 진행 상황**
|
||
- ✅ 자재 업로드 및 분류 시스템
|
||
- ✅ 리비전 비교 기능
|
||
- ✅ 파이프 길이 합산 로직 수정
|
||
- ✅ 엑셀 내보내기 기능
|
||
- 🚧 구매 수량 계산 시스템 (진행 중)
|
||
|
||
## 📚 **추가 참고사항**
|
||
- 사용자는 가상환경에서 Python 실행을 선호
|
||
- 백엔드 서버는 자동 재시작되므로 수동 재시작 불필요
|
||
- 작업 상태는 'in-progress'와 'complete'를 명확히 표시
|
||
|
||
---
|
||
## 🚨 **Docker 실행 관련 트러블슈팅**
|
||
|
||
### 해결된 주요 문제들 (2025.08.01)
|
||
|
||
1. **프론트엔드 API 연결 오류**
|
||
- **문제**: 빌드된 프론트엔드가 10080 포트로 API 요청
|
||
- **원인**: 환경변수 설정 누락으로 하드코딩된 포트 사용
|
||
- **해결**:
|
||
```bash
|
||
# Dockerfile에서 빌드 시 환경변수 주입
|
||
ARG VITE_API_URL=http://localhost:8000
|
||
ENV VITE_API_URL=$VITE_API_URL
|
||
|
||
# docker-compose.yml에서 환경변수 설정
|
||
environment:
|
||
- VITE_API_URL=${VITE_API_URL:-/api}
|
||
```
|
||
|
||
2. **백엔드 데이터베이스 연결 실패**
|
||
- **문제**: `psycopg2.OperationalError` - localhost:5432 연결 거부
|
||
- **원인**: 백엔드가 localhost로 DB 접근 시도 (Docker 컨테이너 내에서는 불가)
|
||
- **해결**:
|
||
```python
|
||
# backend/app/database.py 수정
|
||
DATABASE_URL = os.getenv(
|
||
"DATABASE_URL",
|
||
"postgresql://tkmp_user:tkmp_password_2025@postgres:5432/tk_mp_bom" # localhost → postgres
|
||
)
|
||
```
|
||
|
||
3. **환경별 설정 관리 개선**
|
||
- **문제**: 개발/프로덕션 환경 구분 없이 하드코딩
|
||
- **해결**: docker-compose 파일 분리
|
||
```bash
|
||
docker-compose.yml # 기본 설정
|
||
docker-compose.dev.yml # 개발 환경 오버라이드
|
||
docker-compose.prod.yml # 프로덕션 환경 오버라이드
|
||
```
|
||
|
||
### 실행 전 체크리스트
|
||
- [ ] Docker 및 Docker Compose 설치 확인
|
||
- [ ] 모든 컨테이너 정상 실행 확인: `docker-compose ps`
|
||
- [ ] 백엔드 API 문서 접근 가능: http://localhost:8000/docs
|
||
- [ ] 프론트엔드 로딩 확인: http://localhost:3000
|
||
- [ ] 데이터베이스 연결 확인: pgAdmin (http://localhost:5050)
|
||
|
||
**마지막 업데이트**: 2025-08-01 (Docker 환경 구성 완료) |