refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동) - 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/) - 기획팀 폴더 requests/ 생성 - plans/ → dev/dev_plans/ 이름 변경 - README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용) - resources.md 신규 (노션 링크용, assets/brochure 이관 예정) - CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동 - 전체 참조 경로 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
21
dev/standards/README.md
Normal file
21
dev/standards/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Standards (개발 표준)
|
||||
|
||||
> 코드 작성 방법에 대한 규칙 - **"어떻게 코드를 작성할 것인가"**
|
||||
|
||||
## 목적
|
||||
- 일관된 코드 스타일 유지
|
||||
- 팀 간 협업 효율성 향상
|
||||
- 코드 리뷰 기준 명확화
|
||||
|
||||
## 문서 목록
|
||||
|
||||
| 문서 | 설명 | 필수 확인 시점 |
|
||||
|------|------|--------------|
|
||||
| [api-rules.md](api-rules.md) | API 개발 규칙 (Service-First, FormRequest, i18n) | API 개발 전 |
|
||||
| [pagination-policy.md](pagination-policy.md) | 페이지네이션 정책 (응답 구조, 파라미터) | 목록 API 개발 전 |
|
||||
| [git-conventions.md](git-conventions.md) | Git 커밋 메시지, 브랜치 전략 | 커밋 전 |
|
||||
| [quality-checklist.md](quality-checklist.md) | 코드 품질 체크리스트 | PR 전 |
|
||||
|
||||
## 관련 폴더
|
||||
- [system/](../system/) - 시스템 현황 (아키텍처, DB, 인프라)
|
||||
- [rules/](../rules/) - 비즈니스 규칙 (무엇이 유효한 데이터인가)
|
||||
203
dev/standards/api-rules.md
Normal file
203
dev/standards/api-rules.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# SAM API 개발 규칙
|
||||
|
||||
**업데이트**: 2026-01-28
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Philosophy
|
||||
|
||||
- **Service First**: All business logic must be written in Service classes (public functions)
|
||||
- **Controller**: Only responsible for DI injection of Services and calling them. Use ApiResponse::handle() for responses
|
||||
- **Exception Flow**: Errors are thrown → converted to JSON by global handler
|
||||
- **Context Enforcement**: Base Service requires tenantId(), apiUserId(). Throws exception if not set
|
||||
|
||||
---
|
||||
|
||||
## 2. Multi-tenancy & Models
|
||||
|
||||
- BelongsToTenant global scope applied
|
||||
- ModelTrait for is_active, date handling
|
||||
- SoftDeletes by default
|
||||
- Common columns: tenant_id, created_by, updated_by, deleted_by (COMMENT required)
|
||||
- FK constraints: Created during design, minimal in production
|
||||
|
||||
### 2.1 ModelTrait 사용 가이드
|
||||
|
||||
`ModelTrait`는 모든 모델에서 공통으로 사용하는 기능을 제공합니다.
|
||||
|
||||
**위치**: `app/Traits/ModelTrait.php`
|
||||
|
||||
**제공 기능**:
|
||||
```php
|
||||
// 1. 날짜 직렬화 포맷 (Y-m-d H:i:s)
|
||||
protected function serializeDate(DateTimeInterface $date)
|
||||
|
||||
// 2. Active 상태 조회 Scope
|
||||
public function scopeActive($query)
|
||||
// 사용: Model::active()->get()
|
||||
// SQL: WHERE is_active = 1
|
||||
```
|
||||
|
||||
**⚠️ 필수 요구사항**:
|
||||
|
||||
`scopeActive()` 메서드 사용 시 테이블에 `is_active` 컬럼이 **반드시 존재해야 함**
|
||||
|
||||
```sql
|
||||
-- 마이그레이션 예시
|
||||
$table->boolean('is_active')
|
||||
->default(true)
|
||||
->comment('활성화 여부 (ModelTrait::scopeActive() 사용)');
|
||||
```
|
||||
|
||||
**모델 설정**:
|
||||
```php
|
||||
class YourModel extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
// ...
|
||||
'is_active', // 반드시 추가
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean', // 반드시 추가
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**is_active 컬럼 적용 테이블** (2025-12-05 기준):
|
||||
| 테이블 | is_active | 비고 |
|
||||
|--------|-----------|------|
|
||||
| materials | ✅ 있음 | |
|
||||
| products | ✅ 있음 | |
|
||||
| item_pages | ✅ 있음 | |
|
||||
| item_fields | ✅ 있음 | 2025-12-05 추가 |
|
||||
| item_sections | ❌ 없음 | 필요시 마이그레이션 추가 |
|
||||
|
||||
---
|
||||
|
||||
## 3. Middleware Stack
|
||||
|
||||
- ApiKeyMiddleware, CheckSwaggerAuth, CorsMiddleware, CheckPermission, PermMapper
|
||||
- **ApiVersionMiddleware** - API 버전 선택 및 폴백 처리 (v2 없으면 v1 사용)
|
||||
- Default route group: auth.apikey (some with auth:sanctum)
|
||||
|
||||
---
|
||||
|
||||
## 4. Routing
|
||||
|
||||
### 4.1 라우트 파일 구조
|
||||
|
||||
API 라우트는 도메인별로 분리되어 있습니다:
|
||||
|
||||
```
|
||||
routes/api/
|
||||
├── v1/ # v1 API 라우트
|
||||
│ ├── auth.php # 인증 (login, logout, signup, token)
|
||||
│ ├── admin.php # 관리자 (users, global-menus, FCM)
|
||||
│ ├── users.php # 사용자 (me, profiles, invitations, roles)
|
||||
│ ├── tenants.php # 테넌트 (CRUD, settings, stat-fields)
|
||||
│ ├── hr.php # HR (departments, positions, employees, attendances)
|
||||
│ ├── finance.php # 재무 (cards, deposits, withdrawals, payrolls)
|
||||
│ ├── sales.php # 영업 (clients, quotes, orders, pricing)
|
||||
│ ├── inventory.php # 재고 (items, BOM, stocks, shipments)
|
||||
│ ├── production.php # 생산 (processes, work-orders, inspections)
|
||||
│ ├── design.php # 설계 (models, versions, BOM templates)
|
||||
│ ├── files.php # 파일 (upload, download, folders)
|
||||
│ ├── boards.php # 게시판 (boards, posts, comments)
|
||||
│ └── common.php # 공통 (menus, roles, permissions, settings)
|
||||
├── v2/ # v2 API 라우트 (필요시 생성)
|
||||
└── api.php # 라우트 로더
|
||||
```
|
||||
|
||||
### 4.2 API 버전 폴백 시스템
|
||||
|
||||
**버전 선택 방법 (우선순위 순):**
|
||||
1. `Accept-Version` 헤더: `Accept-Version: v2`
|
||||
2. `X-API-Version` 헤더: `X-API-Version: v2`
|
||||
3. `api_version` 쿼리 파라미터: `?api_version=v2`
|
||||
4. 기본값: `v1`
|
||||
|
||||
**폴백 동작:**
|
||||
- v2 요청 시 해당 라우트가 v2에 없으면 자동으로 v1 라우트 사용
|
||||
- 응답 헤더 `X-API-Version`에 실제 사용된 버전 표시
|
||||
|
||||
**사용 예시:**
|
||||
```bash
|
||||
# v1 명시적 요청
|
||||
curl -H "Accept-Version: v1" https://api.sam.kr/api/v1/users
|
||||
|
||||
# v2 요청 (v2 없으면 v1으로 폴백)
|
||||
curl -H "Accept-Version: v2" https://api.sam.kr/api/v1/users
|
||||
|
||||
# 쿼리 파라미터로 버전 지정
|
||||
curl "https://api.sam.kr/api/v1/users?api_version=v2"
|
||||
|
||||
# 버전 미지정 (기본 v1)
|
||||
curl https://api.sam.kr/api/v1/users
|
||||
```
|
||||
|
||||
### 4.3 REST 컨벤션
|
||||
|
||||
- 기본 CRUD: `index`, `show`, `store`, `update`, `destroy`
|
||||
- 확장 메서드: `toggle`, `bulkUpsert`, `reorder`, `stats`, `options`
|
||||
|
||||
---
|
||||
|
||||
## 5. Controller/Service Rules
|
||||
|
||||
- **Controller**: FormRequest type-hint → only pass $request->validated() to Service
|
||||
- **Service**: extends Service, tenantId()/apiUserId() required
|
||||
- **Lists**: Pagination, explicit search parameters
|
||||
- **Responses**: {success, message, data}, message must be i18n key only
|
||||
|
||||
---
|
||||
|
||||
## 6. i18n & Response Messages
|
||||
|
||||
- Messages: lang/{locale}/message.php
|
||||
- Errors: lang/{locale}/error.php
|
||||
- **Rule**: No direct strings, must use __('message.xxx'), __('error.xxx')
|
||||
- Common keys: message.fetched/created/updated/deleted/bulk_upsert/reordered
|
||||
- Resource-specific keys allowed: message.product.created, message.bom.bulk_upsert
|
||||
|
||||
---
|
||||
|
||||
## 7. Validation (FormRequest)
|
||||
|
||||
- **No direct validate() calls**. Separate all into FormRequest classes
|
||||
- **Reuse common Requests**: PaginateRequest, BomItemsRequest, DateRangeRequest
|
||||
- **Boundary validation** (effective_from ≤ effective_to) in Request
|
||||
|
||||
---
|
||||
|
||||
## 8. Audit Logging
|
||||
|
||||
- **Table**: audit_logs (tenant_id, target_type, target_id, action, before/after(json), actor_id, ip, ua, created_at)
|
||||
- **Actions**: created, updated, deleted, released, cloned, items_replaced, diff_viewed
|
||||
- **Retention**: 13 months default, audit:prune scheduler cleanup
|
||||
- **Failure tolerance**: Log failures don't block business operations
|
||||
|
||||
---
|
||||
|
||||
## 9. Domain Rules
|
||||
|
||||
- **Status**: Strings (DRAFT/RELEASED/ARCHIVED)
|
||||
- **Unique/Indexes**: models(code), model_versions(version_no), bom_items(parent, order)
|
||||
- **BOM**: Supports summary/validate/bulkUpsert/reorder
|
||||
|
||||
---
|
||||
|
||||
## 개발 시 필수 체크
|
||||
|
||||
```
|
||||
✓ Service-First 패턴 (Controller는 단순)
|
||||
✓ BelongsToTenant scope 적용
|
||||
✓ ModelTrait 사용
|
||||
✓ SoftDeletes 적용
|
||||
✓ FormRequest 검증
|
||||
✓ i18n 키 사용 (__('message.xxx'))
|
||||
✓ 감사 로그 고려
|
||||
✓ tenant_id 필터링 확인
|
||||
```
|
||||
194
dev/standards/git-conventions.md
Normal file
194
dev/standards/git-conventions.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Git 커밋 컨벤션
|
||||
|
||||
**업데이트**: 2025-12-02
|
||||
|
||||
---
|
||||
|
||||
## 커밋 메시지 형식
|
||||
|
||||
```
|
||||
[타입]: [scope] 작업내용
|
||||
|
||||
- 세부항목 (생략가능)
|
||||
- 세부항목 2
|
||||
|
||||
Issue: URL (생략가능)
|
||||
```
|
||||
|
||||
### 예시
|
||||
|
||||
```bash
|
||||
feat: [calendar] 달력 기능 개선
|
||||
|
||||
- 클릭시 오류 기능 개선
|
||||
- 색상 변경
|
||||
- 각 항목별 색상 지정
|
||||
|
||||
Issue: https://www.notion.so/hamss/2269c8d34ba080489680e0607a95d9c4
|
||||
```
|
||||
|
||||
```bash
|
||||
fix: [auth] 로그인 시 세션 만료 오류 수정
|
||||
```
|
||||
|
||||
```bash
|
||||
refactor: [user] UserService 메서드 분리
|
||||
|
||||
- validateUser 메서드 추출
|
||||
- 중복 코드 제거
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit Types
|
||||
|
||||
| Type | 설명 | 예시 |
|
||||
|------|------|------|
|
||||
| `feat` | 새로운 기능 추가 | feat: [file] 파일 업로드 기능 추가 |
|
||||
| `fix` | 버그 수정 | fix: [auth] 세션 만료 오류 수정 |
|
||||
| `chore` | 설정, 빌드 등 변경 | chore: composer 패키지 업데이트 |
|
||||
| `refactor` | 프로덕션 코드 리팩토링 | refactor: [user] 서비스 메서드 분리 |
|
||||
| `style` | 포맷/코딩 스타일 수정 | style: Pint 포맷팅 적용 |
|
||||
| `test` | 테스트 추가/수정 | test: Product API 테스트 추가 |
|
||||
| `docs` | 문서 변경 | docs: API 문서 업데이트 |
|
||||
|
||||
### fix vs feat vs refactor 판단 기준
|
||||
|
||||
"고치는 작업"이 많으므로 타입 선택 시 다음 기준을 따른다:
|
||||
|
||||
| 상황 | 타입 | 핵심 기준 |
|
||||
|------|------|----------|
|
||||
| 기존 기능이 의도대로 동작하지 않는 것을 수정 | `fix` | **원래 되어야 하는데 안 되는 것** |
|
||||
| 계산 결과가 틀림, 데이터가 누락됨, 에러 발생 | `fix` | 기대 결과와 실제 결과가 다름 |
|
||||
| 기존 기능에 새 조건/필터/옵션을 추가 | `feat` | 없던 것을 **새로 만드는 것** |
|
||||
| 요청에 의한 동작 변경 (사양 변경) | `feat` | 버그가 아닌 **새로운 요구사항** |
|
||||
| 동작은 동일, 코드 구조만 개선 | `refactor` | 결과는 같고 **내부만 변경** |
|
||||
| 설정 파일, 의존성, 빌드 변경 | `chore` | 코드 동작과 무관한 관리 작업 |
|
||||
|
||||
```
|
||||
✅ fix = "깨진 것을 고친다" (버그, 오류, 잘못된 결과)
|
||||
✅ feat = "없던 것을 만든다" (신규 기능, 사양 변경, 기능 확장)
|
||||
✅ refactor = "같은 것을 더 좋게 만든다" (구조 개선, 중복 제거)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 커밋 빈도 가이드
|
||||
|
||||
### 원칙: "논리적 단위"로 커밋
|
||||
|
||||
```
|
||||
❌ 파일 하나 수정할 때마다 커밋 (너무 잦음)
|
||||
❌ 몇 시간 작업을 한 번에 커밋 (되돌리기 어려움)
|
||||
✅ 하나의 의미 있는 변경이 완성되면 커밋
|
||||
```
|
||||
|
||||
### "의미 있는 변경" 기준
|
||||
|
||||
- 마이그레이션 1건 완성
|
||||
- Service/Controller 로직 수정 1건 완성
|
||||
- Swagger 문서 업데이트 완성
|
||||
- 설정 파일 변경 완성
|
||||
|
||||
### 예시 — WorkOrder API 수정 작업
|
||||
|
||||
```bash
|
||||
# 1) DB 변경 완성 → 커밋
|
||||
fix: [work-order] options 컬럼 추가 마이그레이션
|
||||
|
||||
# 2) 비즈니스 로직 완성 → 커밋
|
||||
fix: [work-order] 옵션 필터링 로직 수정
|
||||
|
||||
# 3) 문서 업데이트 → 커밋
|
||||
docs: [work-order] options 파라미터 Swagger 추가
|
||||
```
|
||||
|
||||
### 장점
|
||||
|
||||
- **되돌리기 용이**: 마이그레이션만, 로직만, 문서만 선택적 revert 가능
|
||||
- **코드 리뷰 용이**: 커밋 단위로 변경 의도 파악 가능
|
||||
- **작업 손실 방지**: 중간에 문제 발생 시 이전 커밋까지 안전
|
||||
|
||||
---
|
||||
|
||||
## Claude 서명 제외 정책
|
||||
|
||||
**커밋 메시지에 다음 내용을 포함하지 않음:**
|
||||
|
||||
```
|
||||
❌ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||||
❌ Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
- Git hooks로 자동 제거됨
|
||||
- 간결하고 명확한 한글 커밋 메시지만 유지
|
||||
|
||||
---
|
||||
|
||||
## 저장소별 커밋
|
||||
|
||||
```bash
|
||||
# 각 저장소에서 개별 커밋
|
||||
cd api && git add . && git commit -m "feat: [scope] 작업 내용"
|
||||
cd ../mng && git add . && git commit -m "feat: [scope] 작업 내용"
|
||||
cd ../react && git add . && git commit -m "feat: [scope] 작업 내용"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Branch 전략
|
||||
|
||||
### Feature Development
|
||||
```bash
|
||||
# main/master에서 직접 작업 금지
|
||||
git checkout -b feature/file-storage-system
|
||||
# 작업 진행...
|
||||
git commit -m "feat: [file] ..."
|
||||
# PR 생성
|
||||
```
|
||||
|
||||
### Hotfix
|
||||
```bash
|
||||
git checkout -b hotfix/login-session-bug
|
||||
# 긴급 수정...
|
||||
git commit -m "fix: [auth] ..."
|
||||
# PR 또는 즉시 병합
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 커밋 전 체크리스트
|
||||
|
||||
```
|
||||
✓ ./vendor/bin/pint 실행 (코드 포맷팅)
|
||||
✓ php artisan migrate:status 확인
|
||||
✓ 테스트 실행 (해당하는 경우)
|
||||
✓ git diff 로 변경사항 검토
|
||||
✓ 불필요한 파일 제외 (.env, node_modules 등)
|
||||
✓ CURRENT_WORKS.md 업데이트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 푸시 정책
|
||||
|
||||
- **사용자가 수동으로 푸시 진행**
|
||||
- 자동 푸시 하지 않음
|
||||
- 커밋 후 푸시 여부를 묻지 않음
|
||||
|
||||
---
|
||||
|
||||
## 중요 사항
|
||||
|
||||
- **각 저장소는 독립적으로 운영**
|
||||
- 저장소별 개별 커밋 메시지 사용
|
||||
- 일관된 커밋 타입 및 형식 유지
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- [개발 명령어](../quickstart/dev-commands.md)
|
||||
- [품질 체크리스트](./quality-checklist.md)
|
||||
|
||||
---
|
||||
|
||||
**최종 업데이트**: 2025-12-02
|
||||
404
dev/standards/options-column-policy.md
Normal file
404
dev/standards/options-column-policy.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# JSON options 컬럼 표준 정책
|
||||
|
||||
> **작성일**: 2026-02-27
|
||||
> **상태**: 설계 확정
|
||||
> **적용 범위**: 모든 비즈니스 테이블 신규 생성 및 확장
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
|
||||
멀티테넌시 환경에서 테넌트별 스키마 변경(ALTER TABLE) 없이 유연하게 속성을 확장하기 위한 `options` JSON 컬럼의 표준 사용 정책을 정의한다.
|
||||
|
||||
### 1.2 핵심 원칙
|
||||
|
||||
> **FK/조인키만** 테이블 컬럼으로 추가한다.
|
||||
> **나머지 속성은** `options` JSON에 저장한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 컬럼 분류 기준
|
||||
|
||||
### 2.1 전용 컬럼으로 만들어야 하는 경우
|
||||
|
||||
```
|
||||
✅ FK/조인키 (다른 테이블 참조)
|
||||
✅ WHERE 조건에 자주 사용되는 필터 (status, is_active)
|
||||
✅ ORDER BY 정렬 대상 (sort_order, created_at)
|
||||
✅ UNIQUE 제약이 필요한 필드 (code, number)
|
||||
✅ INDEX가 필요한 고빈도 조회 필드
|
||||
✅ 집계/통계 대상 (SUM, AVG 등)
|
||||
```
|
||||
|
||||
### 2.2 options JSON에 넣어야 하는 경우
|
||||
|
||||
```
|
||||
✅ 테넌트별로 다를 수 있는 확장 속성
|
||||
✅ 선택적(nullable) 부가 정보
|
||||
✅ 구조가 유동적인 데이터 (키-값 쌍이 변할 수 있음)
|
||||
✅ 드롭다운 선택지 목록
|
||||
✅ 중첩 구조 (배열, 객체)
|
||||
✅ 이력/스냅샷성 데이터 (취소 사유, 변환 전 원본 등)
|
||||
```
|
||||
|
||||
### 2.3 판단 흐름도
|
||||
|
||||
```
|
||||
새 필드 추가 필요?
|
||||
├── FK/조인? ──────────────────→ 전용 컬럼
|
||||
├── WHERE/ORDER BY 필수? ──────→ 전용 컬럼
|
||||
├── UNIQUE 제약 필요? ─────────→ 전용 컬럼
|
||||
├── 집계(SUM/AVG) 대상? ──────→ 전용 컬럼
|
||||
└── 그 외 ─────────────────────→ options JSON
|
||||
```
|
||||
|
||||
> **참고**: MySQL 8.0은 `options->key` 경로로 JSON 내부 필드를 쿼리할 수 있다. 저빈도 필터링은 JSON 경로 쿼리로 충분하다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 마이그레이션 표준
|
||||
|
||||
### 3.1 신규 테이블 생성 시
|
||||
|
||||
모든 비즈니스 테이블에 `options` 컬럼을 기본 포함한다.
|
||||
|
||||
```php
|
||||
Schema::create('example_table', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
|
||||
// --- 전용 컬럼 (FK, 필터, 정렬, 유니크) ---
|
||||
$table->string('code', 50)->comment('코드');
|
||||
$table->string('status', 20)->default('draft')->comment('상태');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
$table->integer('sort_order')->default(0)->comment('정렬 순서');
|
||||
|
||||
// --- options JSON (확장 속성) ---
|
||||
$table->json('options')->nullable()->comment('추가 옵션');
|
||||
|
||||
// --- 감사 컬럼 ---
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
```
|
||||
|
||||
### 3.2 기존 테이블에 options 추가 시
|
||||
|
||||
```php
|
||||
Schema::table('existing_table', function (Blueprint $table) {
|
||||
$table->json('options')->nullable()->after('remark')->comment('추가 옵션');
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 선언 규칙
|
||||
|
||||
| 항목 | 표준 |
|
||||
|------|------|
|
||||
| 컬럼명 | `options` (고정) |
|
||||
| 타입 | `json` |
|
||||
| nullable | `nullable()` (필수) |
|
||||
| comment | 용도 설명 포함 |
|
||||
| 위치 | 비즈니스 컬럼 이후, 감사 컬럼 이전 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 모델 표준
|
||||
|
||||
### 4.1 기본 선언 (필수)
|
||||
|
||||
모든 모델에 공통 적용한다.
|
||||
|
||||
```php
|
||||
class ExampleModel extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
// ... 전용 컬럼들
|
||||
'options',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'options' => 'array', // ✅ 'array' 통일 (❌ 'json' 사용 금지)
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
> **주의**: `'array'`와 `'json'` cast의 차이
|
||||
> - `'array'` → PHP 배열로 변환 (표준)
|
||||
> - `'json'` → JSON 문자열로 유지
|
||||
> - 프로젝트 전체에서 `'array'`로 통일한다.
|
||||
|
||||
### 4.2 헬퍼 메서드 (필수)
|
||||
|
||||
`options`를 사용하는 모든 모델에 아래 2개 메서드를 추가한다.
|
||||
|
||||
```php
|
||||
/**
|
||||
* options에서 값 조회 (점표기 지원)
|
||||
*/
|
||||
public function getOption(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return data_get($this->options, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* options에 값 설정 (점표기 지원)
|
||||
*/
|
||||
public function setOption(string $key, mixed $value): static
|
||||
{
|
||||
$options = $this->options ?? [];
|
||||
data_set($options, $key, $value);
|
||||
$this->options = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
```
|
||||
|
||||
> **`data_get`/`data_set`을 사용하는 이유**: 중첩 키를 점표기(`meta.color`)로 접근할 수 있다.
|
||||
|
||||
### 4.3 키 상수 정의 (권장)
|
||||
|
||||
options에 3개 이상의 키를 사용하면 상수로 정의한다.
|
||||
|
||||
```php
|
||||
// Options 키 상수
|
||||
public const OPTION_MANUFACTURER = 'manufacturer';
|
||||
public const OPTION_INSPECTION_STATUS = 'inspection_status';
|
||||
public const OPTION_INSPECTION_DATE = 'inspection_date';
|
||||
```
|
||||
|
||||
### 4.4 Accessor + $appends (API 노출 시)
|
||||
|
||||
options 내부 값을 API 응답에 **일급 필드**로 노출해야 할 때 적용한다.
|
||||
|
||||
```php
|
||||
protected $appends = [
|
||||
'manufacturer',
|
||||
'inspection_status',
|
||||
];
|
||||
|
||||
public function getManufacturerAttribute(): ?string
|
||||
{
|
||||
return $this->getOption(self::OPTION_MANUFACTURER);
|
||||
}
|
||||
|
||||
public function getInspectionStatusAttribute(): ?string
|
||||
{
|
||||
return $this->getOption(self::OPTION_INSPECTION_STATUS);
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 구현 수준 가이드
|
||||
|
||||
상황에 따라 적절한 수준을 선택한다.
|
||||
|
||||
| 수준 | 적용 조건 | 구현 항목 |
|
||||
|------|----------|----------|
|
||||
| **L1 기본** | options 키 1~2개, 내부 사용 | cast + `getOption`/`setOption` |
|
||||
| **L2 상수** | options 키 3개 이상 | L1 + `OPTION_*` 상수 |
|
||||
| **L3 노출** | API 응답에 옵션값 포함 | L2 + accessor + `$appends` |
|
||||
| **L4 쿼리** | options 키로 필터/정렬 필요 | L3 + JSON 스코프 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 서비스/컨트롤러 사용 패턴
|
||||
|
||||
### 5.1 저장 시 (Create/Update)
|
||||
|
||||
```php
|
||||
// 서비스에서 options 구성
|
||||
$options = [];
|
||||
if ($data['manufacturer'] ?? null) {
|
||||
$options[Receiving::OPTION_MANUFACTURER] = $data['manufacturer'];
|
||||
}
|
||||
if ($data['inspection_status'] ?? null) {
|
||||
$options[Receiving::OPTION_INSPECTION_STATUS] = $data['inspection_status'];
|
||||
}
|
||||
|
||||
Receiving::create([
|
||||
'tenant_id' => $tenantId,
|
||||
// ... 전용 컬럼
|
||||
'options' => $options ?: null, // 빈 배열이면 null 저장
|
||||
]);
|
||||
```
|
||||
|
||||
### 5.2 수정 시 (기존 options 유지)
|
||||
|
||||
```php
|
||||
// ✅ 올바른 방법: 기존 options를 유지하면서 특정 키만 변경
|
||||
$model->setOption('cancelled_at', now()->toIso8601String());
|
||||
$model->setOption('cancelled_by', $userId);
|
||||
$model->save();
|
||||
|
||||
// ❌ 잘못된 방법: options 전체를 덮어씀 (기존 키 소실)
|
||||
$model->options = ['cancelled_at' => now()];
|
||||
$model->save();
|
||||
```
|
||||
|
||||
### 5.3 조회 시 (읽기)
|
||||
|
||||
```php
|
||||
// 헬퍼 사용 (권장)
|
||||
$manufacturer = $receiving->getOption('manufacturer');
|
||||
|
||||
// 중첩 키 (점표기)
|
||||
$lotNo = $workOrderItem->getOption('result.lot_no');
|
||||
|
||||
// 기본값 지정
|
||||
$status = $receiving->getOption('inspection_status', '-');
|
||||
```
|
||||
|
||||
### 5.4 중첩 키 구조 (도메인 분리)
|
||||
|
||||
하나의 options에 여러 도메인 데이터를 저장할 때 최상위 키로 그룹핑한다.
|
||||
|
||||
```php
|
||||
// 작업 결과 + 검사 데이터 + 동적 BOM을 하나의 options에
|
||||
$item->setOption('result', [
|
||||
'good_qty' => 100,
|
||||
'defect_qty' => 2,
|
||||
'lot_no' => 'LOT-2026-001',
|
||||
'completed_at' => now()->toIso8601String(),
|
||||
]);
|
||||
|
||||
$item->setOption('inspection_data', [
|
||||
'process_type' => 'bending',
|
||||
'inspector_id' => 5,
|
||||
]);
|
||||
|
||||
$item->save();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. MySQL JSON 경로 쿼리
|
||||
|
||||
### 6.1 기본 필터링
|
||||
|
||||
```php
|
||||
// 단순 키 비교
|
||||
->where('options->manufacturer', '삼성전자')
|
||||
|
||||
// 중첩 키 비교
|
||||
->where('options->result->worker_id', $workerId)
|
||||
|
||||
// NULL 체크
|
||||
->whereNotNull('options->result')
|
||||
->whereNull('options->cancelled_at')
|
||||
```
|
||||
|
||||
### 6.2 LIKE 검색
|
||||
|
||||
```php
|
||||
->where('options->result->lot_no', 'like', "LOT-2026-%")
|
||||
```
|
||||
|
||||
### 6.3 정렬
|
||||
|
||||
```php
|
||||
->orderByDesc('options->result->completed_at')
|
||||
```
|
||||
|
||||
### 6.4 JSON 스코프 (L4)
|
||||
|
||||
자주 사용하는 JSON 필터는 스코프로 정의한다.
|
||||
|
||||
```php
|
||||
// 모델에 스코프 정의
|
||||
public function scopeHasResult($query)
|
||||
{
|
||||
return $query->whereNotNull('options->result');
|
||||
}
|
||||
|
||||
public function scopeByProcessType($query, string $type)
|
||||
{
|
||||
return $query->where('options->inspection_data->process_type', $type);
|
||||
}
|
||||
|
||||
// 사용
|
||||
WorkOrderItem::hasResult()->byProcessType('bending')->get();
|
||||
```
|
||||
|
||||
### 6.5 성능 주의사항
|
||||
|
||||
```
|
||||
⚠️ JSON 경로 쿼리는 인덱스를 사용하지 않는다.
|
||||
⚠️ 대용량 테이블에서 고빈도 필터링이 필요하면 전용 컬럼으로 승격한다.
|
||||
⚠️ Generated Column + INDEX로 JSON 필드에 인덱스를 걸 수 있지만, 필요시에만 적용한다.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 유사 JSON 컬럼과의 구분
|
||||
|
||||
`options` 외에 용도별로 분화된 JSON 컬럼을 사용한다.
|
||||
|
||||
| 컬럼명 | 용도 | 사용 조건 |
|
||||
|--------|------|----------|
|
||||
| `options` | 범용 확장 속성 | 기본 (모든 테이블) |
|
||||
| `attributes` | EAV 대체 동적 필드값 | Item, Product 등 카탈로그성 엔티티 |
|
||||
| `metadata` | 감사/관계 메타 정보 | 감사 로그, 이벤트 로그 |
|
||||
| `settings` | 설정값 | 알림, 환경 설정 |
|
||||
| `bom` | BOM 구성품 배열 | 제품/품목 |
|
||||
|
||||
> **원칙**: 하나의 테이블에 JSON 컬럼이 2개 이상이면, 각 컬럼의 역할을 comment에 명확히 구분한다.
|
||||
|
||||
---
|
||||
|
||||
## 8. 드롭다운 선택지 패턴
|
||||
|
||||
필드 정의 테이블에서 선택지 목록을 options에 저장하는 패턴이다.
|
||||
|
||||
```php
|
||||
// 마이그레이션
|
||||
$table->json('options')->nullable()->comment('드롭다운 옵션 [{label, value}]');
|
||||
|
||||
// 저장 구조
|
||||
[
|
||||
{"label": "블라인드", "value": "blind"},
|
||||
{"label": "스크린", "value": "screen"},
|
||||
{"label": "셔터", "value": "shutter"}
|
||||
]
|
||||
```
|
||||
|
||||
적용 테이블: `item_master_fields`, `item_fields`, `category_fields`, `document_template_section_fields`
|
||||
|
||||
---
|
||||
|
||||
## 9. 체크리스트
|
||||
|
||||
### 신규 테이블 생성 시
|
||||
|
||||
- [ ] `$table->json('options')->nullable()->comment('...')` 포함
|
||||
- [ ] 비즈니스 컬럼 이후, 감사 컬럼 이전 위치
|
||||
- [ ] 모델에 `'options' => 'array'` cast 선언
|
||||
- [ ] 모델에 `getOption()` / `setOption()` 헬퍼 추가
|
||||
- [ ] options 키 3개 이상 시 `OPTION_*` 상수 정의
|
||||
|
||||
### options 키 추가 시
|
||||
|
||||
- [ ] 기존 options 구조와 키 충돌 없는지 확인
|
||||
- [ ] 서비스에서 `setOption()` 사용 (직접 배열 덮어쓰기 금지)
|
||||
- [ ] API 응답 노출 필요 시 accessor + `$appends` 추가
|
||||
- [ ] 고빈도 필터링 필요 시 전용 컬럼 승격 검토
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
| 문서 | 설명 |
|
||||
|------|------|
|
||||
| [system/database/README.md](/home/aweso/sam/docs/system/database/README.md) | DB 스키마 현황 및 공통 패턴 |
|
||||
| [guides/PROJECT_DEVELOPMENT_POLICY.md](/home/aweso/sam/docs/guides/PROJECT_DEVELOPMENT_POLICY.md) | 개발 공통 정책 |
|
||||
| [standards/api-rules.md](/home/aweso/sam/docs/standards/api-rules.md) | API 개발 규칙 |
|
||||
|
||||
---
|
||||
|
||||
**최종 업데이트**: 2026-02-27
|
||||
210
dev/standards/pagination-policy.md
Normal file
210
dev/standards/pagination-policy.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# SAM API 페이지네이션 정책
|
||||
|
||||
**작성일**: 2025-12-09
|
||||
**수정일**: 2025-12-30
|
||||
**적용 범위**: 모든 목록 조회 API
|
||||
|
||||
---
|
||||
|
||||
## 1. 응답 구조
|
||||
|
||||
### 1.1 표준 페이지네이션 응답 (Laravel 기본)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "조회되었습니다.",
|
||||
"data": {
|
||||
"current_page": 1,
|
||||
"data": [
|
||||
{ "id": 1, "name": "항목1" },
|
||||
{ "id": 2, "name": "항목2" }
|
||||
],
|
||||
"first_page_url": "http://sam.kr/api/v1/items?page=1",
|
||||
"from": 1,
|
||||
"last_page": 5,
|
||||
"last_page_url": "http://sam.kr/api/v1/items?page=5",
|
||||
"links": [
|
||||
{ "url": null, "label": "« Previous", "active": false },
|
||||
{ "url": "http://sam.kr/api/v1/items?page=1", "label": "1", "active": true },
|
||||
{ "url": "http://sam.kr/api/v1/items?page=2", "label": "Next »", "active": false }
|
||||
],
|
||||
"next_page_url": "http://sam.kr/api/v1/items?page=2",
|
||||
"path": "http://sam.kr/api/v1/items",
|
||||
"per_page": 20,
|
||||
"prev_page_url": null,
|
||||
"to": 20,
|
||||
"total": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 응답 구조 설명
|
||||
|
||||
| 키 | 타입 | 설명 |
|
||||
|----|------|------|
|
||||
| `success` | boolean | 요청 성공 여부 |
|
||||
| `message` | string | 응답 메시지 (i18n 키) |
|
||||
| `data` | object | **페이지네이션 객체** (Laravel 기본) |
|
||||
|
||||
### 1.3 data 객체 필드
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| `current_page` | int | 현재 페이지 번호 (1부터 시작) |
|
||||
| `data` | array | **실제 데이터 배열** |
|
||||
| `per_page` | int | 페이지당 항목 수 |
|
||||
| `total` | int | 전체 항목 수 |
|
||||
| `last_page` | int | 마지막 페이지 번호 |
|
||||
| `from` | int\|null | 현재 페이지 첫 번째 항목 번호 |
|
||||
| `to` | int\|null | 현재 페이지 마지막 항목 번호 |
|
||||
| `first_page_url` | string | 첫 페이지 URL |
|
||||
| `last_page_url` | string | 마지막 페이지 URL |
|
||||
| `next_page_url` | string\|null | 다음 페이지 URL |
|
||||
| `prev_page_url` | string\|null | 이전 페이지 URL |
|
||||
| `path` | string | 기본 경로 |
|
||||
| `links` | array | 페이지 링크 배열 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 요청 파라미터
|
||||
|
||||
### 2.1 표준 파라미터
|
||||
|
||||
| 파라미터 | 타입 | 기본값 | 설명 |
|
||||
|----------|------|--------|------|
|
||||
| `page` | int | 1 | 페이지 번호 |
|
||||
| `size` | int | 20 | 페이지당 항목 수 (max: 100) |
|
||||
|
||||
### 2.2 요청 예시
|
||||
|
||||
```http
|
||||
GET /api/v1/items?page=1&size=20
|
||||
GET /api/v1/items?page=2&size=50&search=스크린
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 구현 가이드
|
||||
|
||||
### 3.1 Controller 구현
|
||||
|
||||
```php
|
||||
public function index(IndexRequest $request)
|
||||
{
|
||||
$items = $this->service->getItems($request->validated());
|
||||
|
||||
return ApiResponse::success($items, __('message.fetched'));
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Service 구현
|
||||
|
||||
```php
|
||||
public function getItems(array $params): LengthAwarePaginator
|
||||
{
|
||||
$size = min($params['size'] ?? 20, 100); // 최대 100개
|
||||
|
||||
return Model::query()
|
||||
->when($params['search'] ?? null, fn($q, $s) => $q->where('name', 'like', "%{$s}%"))
|
||||
->orderByDesc('created_at')
|
||||
->paginate($size);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 최대 페이지 크기 제한
|
||||
|
||||
```php
|
||||
$size = min($params['size'] ?? 20, 100); // 최대 100개
|
||||
```
|
||||
|
||||
- 기본값: 20
|
||||
- 최대값: 100
|
||||
- 서버 부하 방지 및 응답 시간 최적화
|
||||
|
||||
---
|
||||
|
||||
## 4. 프론트엔드 사용 가이드
|
||||
|
||||
### 4.1 TypeScript 타입 정의
|
||||
|
||||
```typescript
|
||||
interface PaginatedResponse<T> {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data: {
|
||||
current_page: number;
|
||||
data: T[];
|
||||
first_page_url: string;
|
||||
from: number | null;
|
||||
last_page: number;
|
||||
last_page_url: string;
|
||||
links: Array<{
|
||||
url: string | null;
|
||||
label: string;
|
||||
active: boolean;
|
||||
}>;
|
||||
next_page_url: string | null;
|
||||
path: string;
|
||||
per_page: number;
|
||||
prev_page_url: string | null;
|
||||
to: number | null;
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 React 사용 예시
|
||||
|
||||
```typescript
|
||||
const fetchItems = async (page: number, size: number) => {
|
||||
const response = await api.get<PaginatedResponse<Item>>('/items', {
|
||||
params: { page, size }
|
||||
});
|
||||
|
||||
// 데이터 접근
|
||||
const items = response.data.data.data;
|
||||
|
||||
// 페이지네이션 정보
|
||||
const { current_page, total, last_page } = response.data.data;
|
||||
|
||||
return { items, pagination: response.data.data };
|
||||
};
|
||||
```
|
||||
|
||||
### 4.3 간편 접근 패턴
|
||||
|
||||
```typescript
|
||||
// 구조 분해로 간결하게 사용
|
||||
const { data: pagination } = response.data;
|
||||
const items = pagination.data;
|
||||
const { current_page, total, last_page, per_page } = pagination;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 설계 원칙
|
||||
|
||||
### 5.1 Laravel 기본 형식 유지
|
||||
|
||||
- 프레임워크 기본 동작 활용
|
||||
- 추가 변환 로직 불필요
|
||||
- 생태계 호환성 유지
|
||||
|
||||
### 5.2 일관성 우선
|
||||
|
||||
- 기존 API와 동일한 형식 유지
|
||||
- 프론트엔드 코드 재사용
|
||||
- 학습 비용 최소화
|
||||
|
||||
### 5.3 최대 페이지 크기 제한
|
||||
|
||||
- 서버 부하 방지
|
||||
- 응답 시간 최적화
|
||||
- DoS 공격 방어
|
||||
|
||||
---
|
||||
|
||||
## 6. 관련 문서
|
||||
|
||||
- [API 개발 규칙](./api-rules.md)
|
||||
200
dev/standards/quality-checklist.md
Normal file
200
dev/standards/quality-checklist.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 코드 품질 체크리스트
|
||||
|
||||
**업데이트**: 2025-12-26
|
||||
|
||||
---
|
||||
|
||||
## SAM API 개발 규칙 준수
|
||||
|
||||
```
|
||||
□ Service-First 아키텍처
|
||||
- 비즈니스 로직은 Service 클래스에 작성
|
||||
- Controller는 DI + ApiResponse::handle()만 사용
|
||||
|
||||
□ Multi-tenancy
|
||||
- BelongsToTenant global scope 적용
|
||||
- ModelTrait 사용 (is_active, date handling)
|
||||
- tenant_id 컬럼 및 필터링 확인
|
||||
|
||||
□ Soft Delete
|
||||
- SoftDeletes 트레잇 적용
|
||||
- deleted_at, deleted_by 컬럼
|
||||
|
||||
□ Audit 컬럼
|
||||
- created_by, updated_by, deleted_by
|
||||
- COMMENT 추가 필수
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation & i18n
|
||||
|
||||
```
|
||||
□ FormRequest 검증 사용
|
||||
- 직접 validate() 호출 금지
|
||||
- PaginateRequest, BomItemsRequest 등 공통 Request 재사용
|
||||
|
||||
□ i18n 메시지 키 사용
|
||||
- __('message.xxx') 형식
|
||||
- __('error.xxx') 형식
|
||||
- 직접 문자열 사용 금지
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Swagger 문서화
|
||||
|
||||
```
|
||||
□ Swagger 파일 위치 확인
|
||||
- app/Swagger/v1/{Resource}Api.php
|
||||
|
||||
□ Controller Clean
|
||||
- Controller에 Swagger 주석 없음
|
||||
- 비즈니스 로직만 포함
|
||||
|
||||
□ 필수 요소 포함
|
||||
- Resource 태그 적용
|
||||
- ApiKeyAuth + BearerAuth security
|
||||
- 스키마 정의 (Model, Pagination, CreateRequest, UpdateRequest)
|
||||
- 예시 값 제공
|
||||
|
||||
□ 중복 방지
|
||||
- 공통 스키마 재사용
|
||||
- nullable/oneOf 정확히 구분
|
||||
|
||||
□ 재생성 확인
|
||||
- php artisan l5-swagger:generate 실행
|
||||
- Swagger UI 접근 테스트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 데이터베이스
|
||||
|
||||
```
|
||||
□ 마이그레이션 파일 작성
|
||||
- 명확한 파일명 (YYYY_MM_DD_HHMMSS_action_table.php)
|
||||
- up/down 메서드 모두 구현
|
||||
- 롤백 테스트 완료
|
||||
|
||||
□ 인덱스 최적화
|
||||
- tenant_id 인덱스
|
||||
- 검색/정렬에 사용되는 컬럼 인덱스
|
||||
- 복합 인덱스 고려
|
||||
|
||||
□ FK 제약조건
|
||||
- 개발 환경: 제약조건 생성
|
||||
- 운영 환경: 최소화 또는 제거
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 코드 품질
|
||||
|
||||
```
|
||||
□ 코드 포맷팅
|
||||
- ./vendor/bin/pint 실행
|
||||
- PSR-12 준수 확인
|
||||
|
||||
□ 네이밍
|
||||
- 명확하고 일관된 변수/메서드명
|
||||
- camelCase (PHP), snake_case (DB)
|
||||
|
||||
□ 주석
|
||||
- 복잡한 로직에 주석 추가
|
||||
- PHPDoc 작성 (@param, @return, @throws)
|
||||
|
||||
□ 에러 처리
|
||||
- 예외 던지기 (Exception)
|
||||
- 글로벌 핸들러에서 JSON 변환
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 테스트
|
||||
|
||||
```
|
||||
□ 테스트 케이스 작성
|
||||
- Feature Test (API 엔드포인트)
|
||||
- Unit Test (Service 메서드)
|
||||
|
||||
□ 로컬 테스트 실행
|
||||
- php artisan test
|
||||
- 모든 테스트 통과 확인
|
||||
|
||||
□ Swagger UI 테스트
|
||||
- http://api.sam.kr/api-docs/index.html
|
||||
- 실제 API 호출 테스트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 감사 로그
|
||||
|
||||
```
|
||||
□ 감사 로그 고려
|
||||
- 중요한 작업 (created, updated, deleted)
|
||||
- before/after 데이터 저장
|
||||
- 실패 허용 (비즈니스 로직 차단 금지)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 환경 확인
|
||||
|
||||
```
|
||||
□ Docker 서비스 실행 확인
|
||||
- composer dev 또는 개별 서비스
|
||||
|
||||
□ 마이그레이션 상태 확인
|
||||
- php artisan migrate:status
|
||||
- 미실행 마이그레이션 확인
|
||||
|
||||
□ Git 상태 확인
|
||||
- git status
|
||||
- 불필요한 파일 제외
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 문서 업데이트
|
||||
|
||||
```
|
||||
□ CURRENT_WORKS.md 업데이트
|
||||
- 진행 중인 작업 상태
|
||||
- 완료된 작업 기록
|
||||
- 히스토리 제거 (현재 상태만)
|
||||
|
||||
□ API 변경사항 문서화
|
||||
- 엔드포인트 추가/변경/삭제
|
||||
- 스키마 변경
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 최종 체크
|
||||
|
||||
```
|
||||
□ 모든 변경 사항 커밋 준비
|
||||
□ 커밋 메시지 작성 (Git 컨벤션 준수)
|
||||
□ 테스트 통과
|
||||
□ 문서 최신화
|
||||
□ 코드 리뷰 준비
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 빠른 체크 (요약)
|
||||
|
||||
```
|
||||
✓ Service-First
|
||||
✓ BelongsToTenant
|
||||
✓ SoftDeletes
|
||||
✓ FormRequest
|
||||
✓ i18n 키
|
||||
✓ Swagger 완성도
|
||||
✓ Pint 포맷팅
|
||||
✓ 테스트 통과
|
||||
✓ 감사 로그
|
||||
✓ 문서 업데이트
|
||||
```
|
||||
Reference in New Issue
Block a user