From 8e49fab63fa30ebf21bea9ed544ee5f805ad0a14 Mon Sep 17 00:00:00 2001 From: chpark Date: Fri, 29 May 2026 15:33:49 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20README=20=EB=A5=BC=20=ED=98=84=EC=9E=AC?= =?UTF-8?q?=20=EB=AA=A8=EB=AA=A8=EC=9C=A0=ED=86=B5=20ERP=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=A0=84=EB=A9=B4=20=EC=9E=AC?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이전엔 FITO PLM(Java→Next 컨버전 시절) 내용이 그대로 남아 있었음. 현 상태(유통/물류 ERP, momotogether.com, com.momotogether.app TWA)에 맞춰 주요 기능·기술 스택·디렉토리·환경변수·Gitea 자동배포·TWA 빌드·코딩 컨벤션 모두 갱신. --- README.md | 202 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 155 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 602a220..93d7532 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,178 @@ -# FITO — (주)피토 PLM (Next.js) +# 모모유통 ERP (Distribution ERP) -기존 Java/Spring MVC + JSP + MyBatis 기반 FITO PLM을 Next.js 15 + Node.js로 컨버전한 시스템. +식자재/도소매 **유통·물류 업무 통합 관리 시스템**. +거래처가 발주를 넣고, 본사가 출고·정산하고, 매입·입고·재고·세금계산서까지 한 화면에서 처리한다. -- 원본: [/Users/jhj/FITO](../FITO) (Java 7 + Spring 3.2.4 + MyBatis 3.2.3 + JSP) -- DB: 외부 PostgreSQL `211.115.91.141:11140/fito` (기존 스키마 그대로 사용) -- 이전 이력: `woosung-nextjs`에서 피벗. 스냅샷 태그 `woosung-v1-snapshot`. +- 운영 도메인: **https://momotogether.com** +- Android 앱: **`com.momotogether.app`** (TWA, Play 스토어 등록용 AAB / 사이드로드 APK) +- 코드 저장소(원격): `git.junggomoa.com/chpark/distribution_erp` +- 사용자: (주)모모유통 본사·김포지사 + 계약 거래처 전용 — 일반 소비자용 아님 -## 개발 시작 +--- + +## 주요 기능 + +### 거래처(구매자) 화면 +- **출고 요청** — 재고 있는 품목 선택 → 장바구니 → 발주 요청. 택배전용·용차비·환불 라인 자동/수동 추가. 상시판매·기간한정 품목 분리. +- **내 출고 이력** — 본인 발주 진행상태 추적. +- **푸시 알림 토글** — PWA 설치 후 새 상품 등록 시 알림 수신. +- **회원정보 수정** — 본인 정보·기준 명세표 변경. + +### 본사/지사 운영 +- **출고 처리** — 발주 검토 → 거래명세표 자동 생성(이미지/엑셀/메일 발송) → 재고 자동 차감. +- **입금 관리** — 출고 후 미수금 추적·입금 등록. +- **계산서·전자세금계산서** — 과세/면세 자동 분리. 국세청 ESERO 연동 어댑터(현재는 DB 기록). +- **매입 발주** — 공급업체별 발주서 작성·메일 발송. 발주지사(HQ/KIMPO) 선택. +- **매입 입금관리** — 진행상태와 결재상태(입금완료/미입금)를 **분리 관리**. 부분입고 시 입고금액 기준 입금 처리. +- **입고 처리** — 정상/불량 분리 입고, 유통기한·물류팀 최종완료자 체크리스트. +- **재고 관리** — 창고별 실시간 재고, 입출고 이력, 창고 간 이동, 유통기한 임박 알림. + +### 마스터·통계·관리 +- **품목 마스터** — 가격·원가·과세/면세·택배전용·1회 발주한도·판매기간(또는 상시판매) + 일괄 적용. +- **공급업체/거래처/창고/계산서 기준정보** 관리. +- **푸시알림 게시판** — 관리자가 공지(이미지+본문)를 작성하고 선택한 구독자에게 푸시 발송. 사용자가 알림 탭하면 공지 페이지로 이동. +- **통계** — 대시보드, 월간/일자별 매출, 원가·마진, 거래처×일자 매출, 지사 수수료, 창고 이동 통계. + +--- + +## 기술 스택 + +- **풀스택 단일 Next.js 프로젝트** — 프론트(React 19, App Router) + 백엔드(API Routes, Node.js) 한 저장소. +- **TypeScript** strict mode, **Tailwind CSS**. +- **DB**: 외부 PostgreSQL `183.99.177.40:5432/distribution` — raw SQL(`pg`). +- **인증**: JWT(jose) + HTTP Cookie 세션 + AES-128-ECB(비밀번호). +- **PWA**: `manifest.json` + Service Worker(`public/sw.js`) — 푸시 핸들러·알림 위임(badge·icon). +- **푸시**: `web-push`(VAPID) — `momo_push_subscriptions` 에 endpoint 저장, 발송은 `lib/push.ts`. +- **거래명세표 캡처**: `html-to-image` (이미지 공유/저장). +- **상태관리**: Zustand (auth/menu/theme). + +--- + +## 디렉토리 구조 + +``` +src/ +├── app/ +│ ├── (auth)/login 로그인 +│ ├── (main)/ +│ │ ├── m/orders/new 거래처 — 출고요청 +│ │ ├── m/orders 거래처 — 내 출고이력 +│ │ ├── m/notices/[id] 공지 도달 페이지 (푸시 클릭 시) +│ │ ├── m/admin/orders 출고 처리 +│ │ ├── m/admin/payments 입금 관리 +│ │ ├── m/admin/invoices 계산서 발행 +│ │ ├── m/admin/einvoices 전자세금계산서 +│ │ ├── m/admin/procurements매입 발주서 +│ │ ├── m/admin/proc-payments 매입 입금관리 +│ │ ├── m/admin/inbounds 입고 처리 +│ │ ├── m/admin/inventory 재고 관리 +│ │ ├── m/admin/items 품목 마스터 +│ │ ├── m/admin/notices 푸시알림 게시판 +│ │ ├── m/admin/vendors 공급업체 관리 +│ │ ├── m/admin/warehouses 창고 관리 +│ │ ├── m/admin/statistics 통계 대시보드 +│ │ └── profile 회원정보 수정 +│ └── api/m/ 업무 API (orders/items/inbounds/push/notices …) +├── lib/ +│ ├── db.ts PostgreSQL Pool (queryRows/queryOne/execute) +│ ├── auth.ts 인증 + 세션 +│ ├── push.ts web-push 발송 + 구독 관리 +│ ├── notices.ts 공지 테이블 자동 생성 +│ ├── momo-proc.ts 매입 진행/결재 분리 마이그레이션 +│ └── capture-share.ts 이미지 캡처/공유 +├── components/ +│ ├── layout/ Header / Sidebar +│ ├── grid/ DataGrid (TanStack Table) +│ ├── ui/ 버튼/입력/SearchableSelect 등 +│ └── push-optin.tsx 푸시 알림 켜기/끄기 토글 (localStorage 영속) +└── store/ Zustand (auth/menu/theme) + +public/ +├── sw.js Service Worker — fetch 캐시 + push/notificationclick +├── manifest.json PWA 매니페스트 +├── icon-{192,512}.png PWA 아이콘 (모모 로고) +├── badge-96.png 알림 상태바 단색 배지 +└── .well-known/assetlinks.json TWA Digital Asset Links + +android/ +├── twa-manifest.json Bubblewrap TWA 빌드 설정 (notification delegation ON) +└── README.md APK/AAB 빌드 가이드 (PWABuilder / Bubblewrap) +``` + +각 디렉토리별 상세는 `*/CLAUDE.md` 참고. + +--- + +## 로컬 개발 ```bash npm install -npm run dev # http://localhost:3000 +npm run dev # http://localhost:3000 +npm run build # 운영 빌드 검증 +npm run lint ``` -## 환경변수 +### 환경변수 (`.env.development`) -`.env.development`의 DB 접속 정보를 확인. 필수 키: +| 키 | 설명 | +|---|---| +| `DATABASE_URL` | PostgreSQL 접속 (예: `postgresql://momo_app:****@183.99.177.40:5432/distribution`) | +| `NEXTAUTH_URL` | 로컬: `http://localhost:3000` | +| `NEXTAUTH_SECRET` | JWT 서명 시크릿 | +| `AES_KEY` | 16바이트 — 비밀번호 AES 키 (기존 데이터 호환 필요) | +| `MASTER_PWD` | 마스터 비밀번호 (개발 편의) | +| `SMTP_HOST/USER/PASS/FROM` | 거래명세표·계산서 메일 발송 | +| `MOMO_BANK_ACCOUNT`, `MOMO_PHONE` | 거래명세표 공급자 정보 (기준명세표 미설정 폴백) | +| `VAPID_PUBLIC_KEY` / `VAPID_PRIVATE_KEY` | (선택) 웹푸시 VAPID — 미설정 시 `lib/push.ts` 의 기본키 사용 | -- `DATABASE_URL` — 외부 PostgreSQL 접속 -- `NEXTAUTH_SECRET` — JWT 서명 키 -- `MASTER_PWD` — 마스터 비밀번호 (개발 편의용) -- `AES_KEY` — 비밀번호 AES 암호화 키 (기존 Java 호환) +`.env.momo.example` 참고. -## 배포 표준 +--- -- Docker Compose (dev/prod 분리) — 기존 FITO(Java) 배포환경 재사용 -- Traefik 리버스 프록시 + `fito.wace.me` 도메인 (entrypoints: web, websecure / certresolver: le) -- 외부 네트워크 `toktork_server_default` -- DB는 외부 서버 공유 (`211.115.91.141:11140/fito`) — 컨테이너 내부 DB 없음 +## 배포 — Gitea Actions 자동 배포 -### `start.sh` 배포 스크립트 (권장) +`main` 브랜치에 push 하면 [`.gitea/workflows/deploy.yml`](.gitea/workflows/deploy.yml) 이 자동 실행되어 운영 서버에서 `git pull → docker compose build → up -d` 까지 수행. -```bash -# 첫 배포 (서버에서) -cp .env.production.example .env.production -vi .env.production # DATABASE_URL, NEXTAUTH_SECRET, AES_KEY 등 입력 +- 운영 서버: `183.99.177.40` (SSH, chpark) +- Compose 파일: [`docker-compose.prod.yml`](docker-compose.prod.yml) +- 컨테이너명: `momo-erp` / 이미지: `momo-erp:latest` +- 리버스 프록시: Traefik (`traefik-net` 외부 네트워크, Let's Encrypt 자동발급) +- 호스트: `momotogether.com`, `www.momotogether.com` +- 영구 스토리지: named volume `momo_data_storage` ↔ `/data_storage` (업로드 이미지) +- DB: 외부 공유 — 컨테이너 내부 DB 없음 -./start.sh prod # git pull → build → 기동 → Traefik 라우팅 확인 +> ⚠️ **수동 SSH 빌드 금지** — 자동 워크플로우와 충돌. 배포 검증은 `build-sha.txt` 또는 `docker logs momo-erp` 로 확인. -# 이후 배포 (git commit 후) -./start.sh prod # 자동 git pull + 재빌드 +--- -# 기타 운영 -./start.sh logs prod # 실시간 로그 -./start.sh restart prod # 재시작 (git pull 포함) -./start.sh stop prod # 중지 -./start.sh status prod # 컨테이너 상태 -./start.sh build prod # no-cache 재빌드 -./start.sh clean prod # 전체 삭제 (확인 필요) -``` +## Android 앱 (TWA) -스크립트는 start.sh 자체가 업데이트되면 새 버전으로 **자동 재실행**하므로 안전합니다. +`com.momotogether.app` — `momotogether.com` 을 감싸는 Trusted Web Activity. -### 로컬 개발 +- **알림 위임(notification delegation) ON** — 웹 푸시가 "삼성 인터넷" 등 브라우저가 아니라 **모모유통 앱 이름·아이콘**으로 표시됨. +- **AAB**: Play 스토어 업로드용 +- **APK**: 사이드로드(직접 설치) 테스트용 +- 빌드 가이드: [`android/README.md`](android/README.md) (PWABuilder 또는 Bubblewrap) +- ⚠️ 기존 서명키 재사용 필수 — 키가 바뀌면 `assetlinks.json` 지문이 안 맞아 위임 깨짐. -```bash -./start.sh # docker 기반 (localhost:3643, hot reload) -npm run dev # docker 없이 Node 직접 (localhost:3000) -``` +서비스워커 갱신 / 페이지 콘텐츠 변경은 **APK 재빌드 불필요** — 서버 배포만으로 앱에도 반영됨. -### 인프라 정보 +--- -- 컨테이너명: `plm-fito-next` (prod) / `plm-fito-next-dev` (dev) -- 도메인: `https://fito.wace.me` -- 내부 포트: 3000 (Traefik이 외부 80/443 → 3000) -- 파일 저장: 호스트 `./data_storage` (레포 상대경로) ↔ 컨테이너 `/data_storage` -- 이미지: Next.js `output: "standalone"` 기반 multi-stage build +## 코딩 컨벤션 -상세 구성은 [CLAUDE.md](CLAUDE.md) 참고. +상세 규칙은 [`.claude/rules/`](.claude/rules/) 와 디렉토리별 `CLAUDE.md` 참고. + +- **SQL alias 대문자 유지**: `SELECT col AS "OBJID"` (큰따옴표 필수, 없으면 PG 가 소문자 반환) +- **삭제 플래그**: `COALESCE(is_del,'N') != 'Y'` +- **objid 타입 변환**: `objid::text AS "OBJID"` +- **API 응답**: 목록 `{ RESULTLIST, TOTAL_CNT }`, 단건 `{ success, data }`, 저장 `{ success, objId? }`, 오류 `{ success:false, message }` + 적절한 status code +- **인증 가드**: 모든 API 라우트 첫 줄에 `getSession()` / `requireMomoAdmin()` / `requireMomoUser()` +- **신규 컬럼**: `ensureColumns` 패턴으로 라우트 첫 호출 시 자동 ALTER (운영 무중단) +- **푸시 알림 대상**: 일반 발송은 모든 구독자 / 거래처 전용은 `sendPush(payload, undefined, { generalOnly: true })` + +--- + +## 라이선스 + +내부용. 외부 배포·재사용 금지.