- 개발 단계별 문서 추가 (00_OVERVIEW ~ 06_PHASE) - 기술 표준 문서 추가 (99_TECHNICAL_STANDARDS) - 개발 프로세스 및 패턴 문서 추가 - API_FLOW_TESTER_DESIGN, DEV_PROCESS - HTMX_API_PATTERN, LAYOUT_PATTERN - SETUP_GUIDE, MNG_PROJECT_PLAN - 프로젝트 관리 문서 추가 (project-management/) - INDEX.md, MNG_CRITICAL_RULES.md 업데이트
454 lines
16 KiB
Markdown
454 lines
16 KiB
Markdown
# Phase 1: 기반 마스터 데이터
|
||
|
||
**기간:** 1-2주
|
||
**우선순위:** 최고 (모든 기능의 선행 조건)
|
||
|
||
## 📋 Phase 개요
|
||
|
||
모든 비즈니스 기능의 기반이 되는 핵심 마스터 데이터를 구축합니다.
|
||
|
||
**포함 기능:**
|
||
1. 회원관리 (User Management)
|
||
2. 테넌트관리 (Tenant Management)
|
||
3. 거래처관리 (Client Management)
|
||
|
||
---
|
||
|
||
## 1️⃣ 회원관리 (User Management)
|
||
|
||
### 기능 목록
|
||
|
||
#### 1.1 회원 목록 조회
|
||
- **경로:** `/mng/users`
|
||
- **기능:**
|
||
- 페이지네이션 (기본 20개/페이지)
|
||
- 검색 (이름, 이메일, 부서, 역할)
|
||
- 필터 (활성/비활성, 역할별, 부서별)
|
||
- 정렬 (가입일, 이름, 이메일)
|
||
- **권한:** `users.index`
|
||
|
||
#### 1.2 회원 상세 조회
|
||
- **경로:** `/mng/users/{id}`
|
||
- **기능:**
|
||
- 기본 정보 표시
|
||
- 소속 테넌트 정보
|
||
- 역할/부서 정보
|
||
- 최근 활동 로그
|
||
- **권한:** `users.show`
|
||
|
||
#### 1.3 회원 생성
|
||
- **경로:** `/mng/users/create`
|
||
- **기능:**
|
||
- 기본 정보 입력 (이름, 이메일, 비밀번호)
|
||
- 역할 할당 (다중 선택 가능)
|
||
- 부서 할당
|
||
- 활성/비활성 설정
|
||
- **권한:** `users.create`
|
||
- **검증:**
|
||
- 이메일 중복 체크
|
||
- 비밀번호 강도 검증 (8자 이상, 영문+숫자)
|
||
- 필수 필드 검증
|
||
|
||
#### 1.4 회원 수정
|
||
- **경로:** `/mng/users/{id}/edit`
|
||
- **기능:**
|
||
- 기본 정보 수정
|
||
- 역할/부서 변경
|
||
- 비밀번호 재설정
|
||
- 활성/비활성 전환
|
||
- **권한:** `users.update`
|
||
|
||
#### 1.5 회원 삭제
|
||
- **경로:** `/mng/users/{id}`
|
||
- **기능:**
|
||
- Soft Delete (복구 가능)
|
||
- 삭제 확인 모달
|
||
- 연관 데이터 처리 (담당 영업 등)
|
||
- **권한:** `users.delete`
|
||
|
||
#### 1.6 비밀번호 관리
|
||
- **경로:** `/mng/users/{id}/password`
|
||
- **기능:**
|
||
- 관리자 비밀번호 재설정
|
||
- 임시 비밀번호 발급 (이메일 전송)
|
||
- 비밀번호 변경 이력 기록
|
||
- **권한:** `users.password.reset`
|
||
|
||
### DB 스키마
|
||
|
||
```sql
|
||
-- users 테이블 (Laravel 기본 + 확장)
|
||
CREATE TABLE users (
|
||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||
tenant_id BIGINT UNSIGNED NULL COMMENT '소속 테넌트 ID',
|
||
name VARCHAR(255) NOT NULL COMMENT '사용자 이름',
|
||
email VARCHAR(255) UNIQUE NOT NULL COMMENT '이메일 (로그인 ID)',
|
||
email_verified_at TIMESTAMP NULL COMMENT '이메일 인증 시각',
|
||
password VARCHAR(255) NOT NULL COMMENT '비밀번호 (해시)',
|
||
phone VARCHAR(20) NULL COMMENT '연락처',
|
||
department_id BIGINT UNSIGNED NULL COMMENT '부서 ID',
|
||
is_active BOOLEAN DEFAULT TRUE COMMENT '활성 여부',
|
||
last_login_at TIMESTAMP NULL COMMENT '마지막 로그인',
|
||
remember_token VARCHAR(100) NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
deleted_at TIMESTAMP NULL COMMENT 'Soft Delete',
|
||
|
||
INDEX idx_tenant_id (tenant_id),
|
||
INDEX idx_email (email),
|
||
INDEX idx_department_id (department_id),
|
||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE SET NULL
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
|
||
-- model_has_roles 테이블 (Spatie Permission)
|
||
-- 이미 존재하는 테이블 활용
|
||
```
|
||
|
||
### API 엔드포인트
|
||
|
||
| Method | Endpoint | Description | FormRequest |
|
||
|--------|----------|-------------|-------------|
|
||
| GET | `/mng/users` | 회원 목록 | - |
|
||
| GET | `/mng/users/{id}` | 회원 상세 | - |
|
||
| GET | `/mng/users/create` | 회원 생성 폼 | - |
|
||
| POST | `/mng/users` | 회원 생성 | `StoreUserRequest` |
|
||
| GET | `/mng/users/{id}/edit` | 회원 수정 폼 | - |
|
||
| PUT | `/mng/users/{id}` | 회원 수정 | `UpdateUserRequest` |
|
||
| DELETE | `/mng/users/{id}` | 회원 삭제 | - |
|
||
| POST | `/mng/users/{id}/password/reset` | 비밀번호 재설정 | `ResetPasswordRequest` |
|
||
|
||
### Service 클래스
|
||
|
||
```php
|
||
// app/Services/UserService.php
|
||
class UserService
|
||
{
|
||
public function list(array $filters): LengthAwarePaginator;
|
||
public function find(int $id): User;
|
||
public function create(array $data): User;
|
||
public function update(User $user, array $data): User;
|
||
public function delete(User $user): bool;
|
||
public function resetPassword(User $user, string $newPassword): bool;
|
||
public function assignRoles(User $user, array $roleIds): void;
|
||
}
|
||
```
|
||
|
||
### UI/UX 와이어프레임 (텍스트)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ 회원 관리 [+ 새 회원] │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ 🔍 [검색: 이름, 이메일] [부서▼] [역할▼] [상태▼] [검색]│
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ ☑ | 이름 | 이메일 | 부서 | 역할 | 상태 | 작업 │
|
||
│ ☐ | 홍길동 | hong@ex.com | 개발팀 | Admin | 활성 | [수정][삭제] │
|
||
│ ☐ | 김철수 | kim@ex.com | 영업팀 | User | 활성 | [수정][삭제] │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ « 1 2 3 ... 10 » │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 개발 체크리스트
|
||
|
||
- [ ] `User` 모델에 `BelongsToTenant` trait 추가
|
||
- [ ] `UserService` 클래스 작성 (비즈니스 로직)
|
||
- [ ] `StoreUserRequest`, `UpdateUserRequest` 작성 (검증)
|
||
- [ ] `UserController` 작성 (라우팅 처리)
|
||
- [ ] Blade 템플릿 작성 (`users/index.blade.php` 등)
|
||
- [ ] Alpine.js 인터랙션 추가 (모달, 검색 필터)
|
||
- [ ] 권한 체크 미들웨어 적용 (`can:users.index`)
|
||
- [ ] Audit Log 자동 기록 (UserObserver)
|
||
- [ ] i18n 키 작성 (`lang/ko/users.php`)
|
||
- [ ] Pint 포맷팅 및 PHPStan 검사 통과
|
||
- [ ] 테스트 작성 (`UserServiceTest`, `UserControllerTest`)
|
||
|
||
---
|
||
|
||
## 2️⃣ 테넌트관리 (Tenant Management)
|
||
|
||
### 기능 목록
|
||
|
||
#### 2.1 테넌트 목록 조회
|
||
- **경로:** `/mng/tenants`
|
||
- **기능:**
|
||
- 페이지네이션
|
||
- 검색 (회사명, 도메인)
|
||
- 필터 (구독 상태, 플랜)
|
||
- 정렬 (생성일, 회사명)
|
||
- **권한:** `tenants.index`
|
||
|
||
#### 2.2 테넌트 상세 조회
|
||
- **경로:** `/mng/tenants/{id}`
|
||
- **기능:**
|
||
- 기본 정보 (회사명, 도메인, 연락처)
|
||
- 구독 정보 (플랜, 만료일)
|
||
- 사용 통계 (회원 수, 저장 용량)
|
||
- 최근 결제 내역
|
||
- **권한:** `tenants.show`
|
||
|
||
#### 2.3 테넌트 생성
|
||
- **경로:** `/mng/tenants/create`
|
||
- **기능:**
|
||
- 회사 정보 입력
|
||
- 도메인 설정 (예: company.sam.kr)
|
||
- 초기 구독 플랜 선택
|
||
- 관리자 계정 생성
|
||
- **권한:** `tenants.create`
|
||
|
||
#### 2.4 테넌트 수정
|
||
- **경로:** `/mng/tenants/{id}/edit`
|
||
- **기능:**
|
||
- 회사 정보 수정
|
||
- 구독 플랜 변경
|
||
- 활성/비활성 전환
|
||
- **권한:** `tenants.update`
|
||
|
||
#### 2.5 테넌트 삭제
|
||
- **경로:** `/mng/tenants/{id}`
|
||
- **기능:**
|
||
- Soft Delete
|
||
- 연관 데이터 처리 (사용자, 게시물 등)
|
||
- 삭제 전 데이터 백업 권장
|
||
- **권한:** `tenants.delete`
|
||
|
||
### DB 스키마
|
||
|
||
```sql
|
||
CREATE TABLE tenants (
|
||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||
name VARCHAR(255) NOT NULL COMMENT '회사명',
|
||
domain VARCHAR(100) UNIQUE NOT NULL COMMENT '도메인 (예: company)',
|
||
email VARCHAR(255) NOT NULL COMMENT '대표 이메일',
|
||
phone VARCHAR(20) NULL COMMENT '대표 전화',
|
||
address TEXT NULL COMMENT '주소',
|
||
business_number VARCHAR(50) NULL COMMENT '사업자번호',
|
||
subscription_plan ENUM('free', 'basic', 'pro', 'enterprise') DEFAULT 'free' COMMENT '구독 플랜',
|
||
subscription_expires_at TIMESTAMP NULL COMMENT '구독 만료일',
|
||
is_active BOOLEAN DEFAULT TRUE COMMENT '활성 여부',
|
||
max_users INT DEFAULT 10 COMMENT '최대 사용자 수',
|
||
storage_limit BIGINT DEFAULT 1073741824 COMMENT '저장 용량 제한 (바이트, 기본 1GB)',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
deleted_at TIMESTAMP NULL,
|
||
|
||
INDEX idx_domain (domain),
|
||
INDEX idx_subscription_plan (subscription_plan),
|
||
INDEX idx_is_active (is_active)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
```
|
||
|
||
### API 엔드포인트
|
||
|
||
| Method | Endpoint | Description | FormRequest |
|
||
|--------|----------|-------------|-------------|
|
||
| GET | `/mng/tenants` | 테넌트 목록 | - |
|
||
| GET | `/mng/tenants/{id}` | 테넌트 상세 | - |
|
||
| GET | `/mng/tenants/create` | 테넌트 생성 폼 | - |
|
||
| POST | `/mng/tenants` | 테넌트 생성 | `StoreTenantRequest` |
|
||
| GET | `/mng/tenants/{id}/edit` | 테넌트 수정 폼 | - |
|
||
| PUT | `/mng/tenants/{id}` | 테넌트 수정 | `UpdateTenantRequest` |
|
||
| DELETE | `/mng/tenants/{id}` | 테넌트 삭제 | - |
|
||
|
||
### Service 클래스
|
||
|
||
```php
|
||
// app/Services/TenantService.php
|
||
class TenantService
|
||
{
|
||
public function list(array $filters): LengthAwarePaginator;
|
||
public function find(int $id): Tenant;
|
||
public function create(array $data): Tenant;
|
||
public function update(Tenant $tenant, array $data): Tenant;
|
||
public function delete(Tenant $tenant): bool;
|
||
public function changePlan(Tenant $tenant, string $plan): bool;
|
||
public function getUsageStats(Tenant $tenant): array;
|
||
}
|
||
```
|
||
|
||
### 개발 체크리스트
|
||
|
||
- [ ] `Tenant` 모델 작성 (BelongsToTenant 제외 - 최상위)
|
||
- [ ] `TenantService` 클래스 작성
|
||
- [ ] `StoreTenantRequest`, `UpdateTenantRequest` 작성
|
||
- [ ] `TenantController` 작성
|
||
- [ ] Blade 템플릿 작성
|
||
- [ ] 구독 플랜 변경 로직 구현
|
||
- [ ] 사용량 통계 계산 로직 (회원 수, 저장 용량)
|
||
- [ ] i18n 키 작성
|
||
- [ ] 테스트 작성
|
||
|
||
---
|
||
|
||
## 3️⃣ 거래처관리 (Client Management)
|
||
|
||
### 기능 목록
|
||
|
||
#### 3.1 거래처 목록 조회
|
||
- **경로:** `/mng/clients`
|
||
- **기능:**
|
||
- 페이지네이션
|
||
- 검색 (회사명, 담당자명)
|
||
- 필터 (거래 상태, 업종)
|
||
- 정렬 (생성일, 회사명)
|
||
- **권한:** `clients.index`
|
||
|
||
#### 3.2 거래처 상세 조회
|
||
- **경로:** `/mng/clients/{id}`
|
||
- **기능:**
|
||
- 기본 정보 (회사명, 연락처, 주소)
|
||
- 담당자 정보 (이름, 직책, 연락처)
|
||
- 거래 이력 (견적서, 계약)
|
||
- 메모/코멘트
|
||
- **권한:** `clients.show`
|
||
|
||
#### 3.3 거래처 생성
|
||
- **경로:** `/mng/clients/create`
|
||
- **기능:**
|
||
- 회사 정보 입력
|
||
- 담당자 정보 입력 (다중 가능)
|
||
- 업종/분류 선택
|
||
- 메모 작성
|
||
- **권한:** `clients.create`
|
||
|
||
#### 3.4 거래처 수정
|
||
- **경로:** `/mng/clients/{id}/edit`
|
||
- **기능:**
|
||
- 기본 정보 수정
|
||
- 담당자 추가/수정/삭제
|
||
- 거래 상태 변경
|
||
- **권한:** `clients.update`
|
||
|
||
#### 3.5 거래처 삭제
|
||
- **경로:** `/mng/clients/{id}`
|
||
- **기능:**
|
||
- Soft Delete
|
||
- 연관 데이터 체크 (진행 중인 견적서 등)
|
||
- **권한:** `clients.delete`
|
||
|
||
### DB 스키마
|
||
|
||
```sql
|
||
CREATE TABLE clients (
|
||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '소속 테넌트',
|
||
name VARCHAR(255) NOT NULL COMMENT '회사명',
|
||
business_number VARCHAR(50) NULL COMMENT '사업자번호',
|
||
industry VARCHAR(100) NULL COMMENT '업종',
|
||
phone VARCHAR(20) NULL COMMENT '대표 전화',
|
||
email VARCHAR(255) NULL COMMENT '대표 이메일',
|
||
address TEXT NULL COMMENT '주소',
|
||
status ENUM('active', 'inactive', 'potential') DEFAULT 'potential' COMMENT '거래 상태',
|
||
notes TEXT NULL COMMENT '메모',
|
||
assigned_user_id BIGINT UNSIGNED NULL COMMENT '담당 영업 사원',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
deleted_at TIMESTAMP NULL,
|
||
|
||
INDEX idx_tenant_id (tenant_id),
|
||
INDEX idx_status (status),
|
||
INDEX idx_assigned_user_id (assigned_user_id),
|
||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (assigned_user_id) REFERENCES users(id) ON DELETE SET NULL
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
|
||
CREATE TABLE client_contacts (
|
||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||
client_id BIGINT UNSIGNED NOT NULL COMMENT '거래처 ID',
|
||
name VARCHAR(255) NOT NULL COMMENT '담당자 이름',
|
||
position VARCHAR(100) NULL COMMENT '직책',
|
||
phone VARCHAR(20) NULL COMMENT '연락처',
|
||
email VARCHAR(255) NULL COMMENT '이메일',
|
||
is_primary BOOLEAN DEFAULT FALSE COMMENT '주 담당자 여부',
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_client_id (client_id),
|
||
FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
```
|
||
|
||
### API 엔드포인트
|
||
|
||
| Method | Endpoint | Description | FormRequest |
|
||
|--------|----------|-------------|-------------|
|
||
| GET | `/mng/clients` | 거래처 목록 | - |
|
||
| GET | `/mng/clients/{id}` | 거래처 상세 | - |
|
||
| GET | `/mng/clients/create` | 거래처 생성 폼 | - |
|
||
| POST | `/mng/clients` | 거래처 생성 | `StoreClientRequest` |
|
||
| GET | `/mng/clients/{id}/edit` | 거래처 수정 폼 | - |
|
||
| PUT | `/mng/clients/{id}` | 거래처 수정 | `UpdateClientRequest` |
|
||
| DELETE | `/mng/clients/{id}` | 거래처 삭제 | - |
|
||
| POST | `/mng/clients/{id}/contacts` | 담당자 추가 | `StoreContactRequest` |
|
||
|
||
### Service 클래스
|
||
|
||
```php
|
||
// app/Services/ClientService.php
|
||
class ClientService
|
||
{
|
||
public function list(array $filters): LengthAwarePaginator;
|
||
public function find(int $id): Client;
|
||
public function create(array $data): Client;
|
||
public function update(Client $client, array $data): Client;
|
||
public function delete(Client $client): bool;
|
||
public function addContact(Client $client, array $contactData): ClientContact;
|
||
public function getTransactionHistory(Client $client): Collection;
|
||
}
|
||
```
|
||
|
||
### 개발 체크리스트
|
||
|
||
- [ ] `Client`, `ClientContact` 모델 작성 (BelongsToTenant)
|
||
- [ ] `ClientService` 클래스 작성
|
||
- [ ] FormRequest 작성
|
||
- [ ] `ClientController` 작성
|
||
- [ ] Blade 템플릿 작성 (담당자 다중 입력 UI)
|
||
- [ ] 거래 이력 연동 (견적서, 계약)
|
||
- [ ] 담당 영업 사원 할당 기능
|
||
- [ ] i18n 키 작성
|
||
- [ ] 테스트 작성
|
||
|
||
---
|
||
|
||
## 🎯 Phase 1 완료 조건
|
||
|
||
### 기능 완성도
|
||
- [ ] 3개 모듈 모두 CRUD 완성
|
||
- [ ] 각 모듈별 검색/필터/정렬 동작
|
||
- [ ] 권한 체크 정상 작동
|
||
|
||
### 코드 품질
|
||
- [ ] Service-First 패턴 준수
|
||
- [ ] FormRequest 검증 구현
|
||
- [ ] BelongsToTenant trait 적용
|
||
- [ ] i18n 키 사용 (한글 직접 사용 금지)
|
||
- [ ] Pint 포맷팅 통과
|
||
- [ ] PHPStan Level 5+ 통과
|
||
|
||
### 데이터 무결성
|
||
- [ ] Multi-tenant 격리 확인
|
||
- [ ] Soft Delete 동작 확인
|
||
- [ ] Audit Log 자동 기록 확인
|
||
- [ ] Foreign Key 제약 조건 정상 작동
|
||
|
||
### 테스트
|
||
- [ ] Service 계층 유닛 테스트
|
||
- [ ] Controller 계층 Feature 테스트
|
||
- [ ] 권한 체크 테스트
|
||
- [ ] 검증 로직 테스트
|
||
|
||
---
|
||
|
||
## 📚 참고 자료
|
||
|
||
- **SAM API Rules:** `API_RULES.md`
|
||
- **개발 명령어:** `DEV_COMMANDS.md`
|
||
- **품질 체크리스트:** `QUALITY_CHECKLIST.md`
|
||
|
||
---
|
||
|
||
**최종 업데이트:** 2025-11-21
|
||
**작성자:** Claude Code
|
||
**버전:** 1.0.0 |