feat: 모모유통 유통관리 ERP (Next.js 16) — MOMO 브랜딩 + distribution DB + momo.junggomoa.com
- fito-nextjs 기반으로 재구성 - 로그인: MOMO 로고 + 모모유통 + 유통관리 ERP, 하단에 본사/지사 주소 표시 - 사이드바 상단: MOMO 아이콘 + 모모유통 + 유통관리 ERP - 파비콘: /src/app/icon.svg (MOMO 그린 배지) - layout.tsx title: 모모유통 | 유통관리 ERP - DB: 183.99.177.40:5432/distribution (fito 스키마 import 완료) - Traefik: Host(momo.junggomoa.com), 컨테이너 momo-erp
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
globs: src/app/api/**/*.ts
|
||||
---
|
||||
|
||||
# API Route 코딩 규칙
|
||||
|
||||
## 인증 체크
|
||||
모든 핸들러 첫 줄에 세션 검증 필수:
|
||||
```typescript
|
||||
const user = await getSession();
|
||||
if (!user) return NextResponse.json({ success: false }, { status: 401 });
|
||||
```
|
||||
|
||||
## import 순서
|
||||
1. `next/server` (NextRequest, NextResponse)
|
||||
2. `@/lib/session` (getSession)
|
||||
3. `@/lib/db` (queryRows, queryOne, execute)
|
||||
4. `@/lib/utils` (createObjectId, checkNull 등)
|
||||
5. 기타 (`@/lib/auth`, `@/lib/encrypt` — login 전용)
|
||||
|
||||
## 동적 SQL 파라미터
|
||||
```typescript
|
||||
const conditions: string[] = ["1=1"];
|
||||
const params: unknown[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (body.field) {
|
||||
conditions.push(`TABLE.COLUMN = $${idx++}`);
|
||||
params.push(body.field);
|
||||
}
|
||||
// LIKE: conditions.push(`COLUMN LIKE '%' || $${idx++} || '%'`);
|
||||
|
||||
const sql = `SELECT ... WHERE ${conditions.join(" AND ")} ORDER BY regdate DESC`;
|
||||
const rows = await queryRows(sql, params);
|
||||
```
|
||||
|
||||
## SQL alias 대문자
|
||||
PostgreSQL은 따옴표 없는 alias를 소문자로 반환한다. 대문자 유지하려면 큰따옴표 필수:
|
||||
```sql
|
||||
SELECT id AS "OBJID", name AS "USER_NAME" -- O (대문자 유지)
|
||||
SELECT id AS OBJID -- X (소문자 objid로 반환)
|
||||
```
|
||||
|
||||
## 응답 형식
|
||||
- 목록 조회: `{ RESULTLIST: rows, TOTAL_CNT: rows.length }`
|
||||
- 단순 조회: `{ success: true, data: rows }`
|
||||
- 저장/수정: `{ success: true }` 또는 `{ success: true, objId: newId }`
|
||||
- 오류: `{ success: false, message: "..." }` + HTTP 400/401/500
|
||||
|
||||
## 에러 처리
|
||||
```typescript
|
||||
try {
|
||||
// 비즈니스 로직
|
||||
} catch (error) {
|
||||
console.error("설명:", error);
|
||||
return NextResponse.json({ success: false, message: "처리 중 오류가 발생했습니다." }, { status: 500 });
|
||||
}
|
||||
```
|
||||
|
||||
## INSERT/UPDATE 구분
|
||||
`actionType` 필드로 분기:
|
||||
```typescript
|
||||
const { actionType } = body;
|
||||
if (actionType === "regist") {
|
||||
const newId = createObjectId();
|
||||
await execute(`INSERT INTO ...`, [...]);
|
||||
} else {
|
||||
await execute(`UPDATE ... WHERE OBJID = $${idx}`, [...]);
|
||||
}
|
||||
```
|
||||
|
||||
## SQL 공통 패턴
|
||||
- 삭제 플래그: `COALESCE(IS_DEL, 'N') != 'Y'`
|
||||
- 날짜 포맷: `TO_CHAR(regdate, 'YYYY-MM-DD')`
|
||||
- 기본 정렬: `ORDER BY regdate DESC`
|
||||
- objId 타입 변환: `objid::text AS "OBJID"`
|
||||
Reference in New Issue
Block a user