# 더미 데이터 시딩 계획 > **작성일**: 2025-12-23 > **목적**: React API 연동 테스트를 위한 더미 데이터 생성 > **참고 문서**: `react-mock-to-api-migration-plan.md` --- ## 1. 현황 분석 ### 1.1 기존 데이터 현황 | 테이블 | 현재 개수 | 추가 목표 | 최종 목표 | |--------|----------|----------|----------| | tenants | 5 | - | 기존 활용 | | users | 13 | - | 기존 활용 | | clients | 4 (tenant 287) | +20 | 24개 | | client_groups | 0 | +5 | 5개 | | bank_accounts | 0 | +5 | 5개 | | sales | 0 (tenant 287) | +80 | 80개 | | purchases | 0 | +70 | 70개 | | deposits | 0 | +60 | 60개 | | withdrawals | 0 | +60 | 60개 | | bills | 0 | +30 | 30개 | | bill_installments | 0 | +15 | 15개 | | **총계** | - | **~345개** | - | ### 1.2 대상 테넌트 **Target: ID 287 (프론트_테스트회사)** - Code: `JTKKPNNG6D` - 기존 거래처 4개 보유 (ID: 9, 10, 11, 12) - 테스트 목적으로 적합 ### 1.3 데이터 기간 **2025년 1월 ~ 12월 (1년간)** - 월별 균등 분포 - 최근월(11~12월)은 draft 상태 포함 --- ## 2. 테이블 의존성 분석 ### 2.1 의존성 다이어그램 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Level 0: 기반 데이터 (이미 존재) │ ├─────────────────────────────────────────────────────────────────┤ │ tenants (ID: 287) ──┬── users (ID: 1) │ │ │ │ └──────────────────────┼──────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Level 1: 마스터 데이터 (5 + 5 = 10개) │ ├─────────────────────────────────────────────────────────────────┤ │ ├── client_groups (5개) - 거래처 그룹 │ │ └── bank_accounts (5개) - 은행 계좌 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Level 2: 거래처 데이터 (20개) │ ├─────────────────────────────────────────────────────────────────┤ │ clients (20개) ← client_group_id (optional) │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Level 3: 입출금/어음 데이터 (60 + 60 + 30 = 150개) │ ├─────────────────────────────────────────────────────────────────┤ │ ├── deposits (60개) ← client_id, bank_account_id │ │ ├── withdrawals (60개) ← client_id, bank_account_id │ │ └── bills (30개) ← client_id, bank_account_id │ │ └── bill_installments (15개) ← bill_id │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Level 4: 매출/매입 데이터 (80 + 70 = 150개) │ ├─────────────────────────────────────────────────────────────────┤ │ ├── sales (80개) ← client_id, deposit_id (optional) │ │ └── purchases (70개) ← client_id, withdrawal_id (optional) │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.2 삽입 순서 및 수량 ``` 1. client_groups (5개) - 거래처 그룹 2. bank_accounts (5개) - 은행 계좌 3. clients (20개) - 거래처 (매출처/매입처) 4. deposits (60개) - 입금 (월 5건) 5. withdrawals (60개) - 출금 (월 5건) 6. bills (30개) - 어음 (수취 15건 + 발행 15건) 7. sales (80개) - 매출 (월 6~7건) 8. purchases (70개) - 매입 (월 5~6건) ──────────────────────────── 총계 ~345개 ``` --- ## 3. 더미 데이터 스키마 ### 3.1 client_groups (거래처 그룹) - 5개 ```php [ ['group_code' => 'VIP', 'group_name' => 'VIP 고객', 'price_rate' => 0.95], ['group_code' => 'GOLD', 'group_name' => '골드 고객', 'price_rate' => 0.97], ['group_code' => 'SILVER', 'group_name' => '실버 고객', 'price_rate' => 0.98], ['group_code' => 'NORMAL', 'group_name' => '일반 고객', 'price_rate' => 1.00], ['group_code' => 'NEW', 'group_name' => '신규 고객', 'price_rate' => 1.00], ] ``` ### 3.2 bank_accounts (은행 계좌) - 5개 ```php [ ['bank_code' => '004', 'bank_name' => 'KB국민은행', 'account_number' => '123-45-6789012', 'account_holder' => '프론트테스트', 'account_name' => '운영계좌', 'is_primary' => true], ['bank_code' => '088', 'bank_name' => '신한은행', 'account_number' => '110-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '급여계좌', 'is_primary' => false], ['bank_code' => '020', 'bank_name' => '우리은행', 'account_number' => '1002-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '예비계좌', 'is_primary' => false], ['bank_code' => '081', 'bank_name' => '하나은행', 'account_number' => '123-456789-12345','account_holder' => '프론트테스트', 'account_name' => '법인카드', 'is_primary' => false], ['bank_code' => '011', 'bank_name' => 'NH농협은행', 'account_number' => '351-1234-5678-12','account_holder' => '프론트테스트', 'account_name' => '비상금', 'is_primary' => false], ] ``` ### 3.3 clients (거래처) - 20개 #### 매출처 (SALES) - 10개 | Code | 회사명 | 사업자번호 | 그룹 | 담당자 | |------|--------|-----------|------|--------| | S001 | 삼성전자 | 124-81-00998 | VIP | 김철수 | | S002 | LG전자 | 107-86-14075 | VIP | 이영희 | | S003 | SK하이닉스 | 204-81-17169 | GOLD | 박민수 | | S004 | 현대자동차 | 101-81-05765 | GOLD | 정은지 | | S005 | 네이버 | 220-81-62517 | GOLD | 최준호 | | S006 | 카카오 | 120-87-65763 | SILVER | 강미래 | | S007 | 쿠팡 | 120-88-00767 | SILVER | 임도현 | | S008 | 토스 | 120-87-83139 | NORMAL | 윤서연 | | S009 | 배달의민족 | 220-87-93847 | NORMAL | 한지민 | | S010 | 당근마켓 | 815-87-01234 | NEW | 오태양 | #### 매입처 (PURCHASE) - 7개 | Code | 회사명 | 사업자번호 | 그룹 | 담당자 | |------|--------|-----------|------|--------| | P001 | 한화솔루션 | 138-81-00610 | - | 김재원 | | P002 | 포스코 | 506-81-08754 | - | 이현석 | | P003 | 롯데케미칼 | 301-81-07123 | - | 박서준 | | P004 | GS칼텍스 | 104-81-23858 | - | 정해인 | | P005 | 대한항공 | 110-81-14794 | - | 송민호 | | P006 | 현대제철 | 130-81-12345 | - | 강동원 | | P007 | SK이노베이션 | 110-81-67890 | - | 유재석 | #### 매출매입처 (BOTH) - 3개 | Code | 회사명 | 사업자번호 | 그룹 | 담당자 | |------|--------|-----------|------|--------| | B001 | 두산에너빌리티 | 124-81-08628 | GOLD | 조인성 | | B002 | CJ대한통운 | 104-81-39849 | SILVER | 공유 | | B003 | 삼성SDS | 124-81-34567 | VIP | 이정재 | ### 3.4 월별 데이터 분포 | 월 | 입금 | 출금 | 매출 | 매입 | 상태 | |----|------|------|------|------|------| | 1월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced | | 2월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced | | 3월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced | | 4월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced | | 5월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced | | 6월 | 5건 | 5건 | 6건 | 6건 | confirmed/invoiced | | 7월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced | | 8월 | 5건 | 5건 | 6건 | 6건 | confirmed/invoiced | | 9월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced | | 10월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced | | 11월 | 5건 | 5건 | 8건 | 7건 | confirmed + draft | | 12월 | 5건 | 5건 | 7건 | 6건 | draft 위주 | | **합계** | **60건** | **60건** | **80건** | **70건** | | ### 3.5 금액 범위 및 분포 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 금액 분포 (공급가액 기준) │ ├─────────────────────────────────────────────────────────────────┤ │ 소액 (30%): 1,000,000 ~ 5,000,000원 │ │ 중액 (50%): 5,000,000 ~ 30,000,000원 │ │ 대액 (20%): 30,000,000 ~ 100,000,000원 │ ├─────────────────────────────────────────────────────────────────┤ │ 세액: 공급가액 × 10% │ │ 합계: 공급가액 × 110% │ └─────────────────────────────────────────────────────────────────┘ ``` ### 3.6 deposits (입금) - 60개 예시 ```php // 월별 5건씩, 총 60건 // 결제수단: transfer(70%), card(15%), cash(10%), check(5%) $deposits = [ // 1월 ['date' => '2025-01-05', 'client' => '삼성전자', 'amount' => 55000000, 'method' => 'transfer'], ['date' => '2025-01-10', 'client' => 'LG전자', 'amount' => 28000000, 'method' => 'transfer'], ['date' => '2025-01-15', 'client' => '현대자동차', 'amount' => 42000000, 'method' => 'transfer'], ['date' => '2025-01-20', 'client' => '네이버', 'amount' => 15000000, 'method' => 'card'], ['date' => '2025-01-25', 'client' => '카카오', 'amount' => 8500000, 'method' => 'transfer'], // 2월 ['date' => '2025-02-05', 'client' => 'SK하이닉스', 'amount' => 68000000, 'method' => 'transfer'], ['date' => '2025-02-10', 'client' => '쿠팡', 'amount' => 22000000, 'method' => 'transfer'], ['date' => '2025-02-15', 'client' => '토스', 'amount' => 9800000, 'method' => 'card'], ['date' => '2025-02-20', 'client' => '삼성SDS', 'amount' => 35000000, 'method' => 'transfer'], ['date' => '2025-02-25', 'client' => '두산에너빌리티','amount' => 48000000, 'method' => 'check'], // ... 3월 ~ 12월 (패턴 반복, Seeder에서 자동 생성) ]; ``` ### 3.7 withdrawals (출금) - 60개 예시 ```php // 월별 5건씩, 총 60건 $withdrawals = [ // 1월 ['date' => '2025-01-03', 'client' => '한화솔루션', 'amount' => 32000000, 'method' => 'transfer'], ['date' => '2025-01-08', 'client' => '포스코', 'amount' => 45000000, 'method' => 'transfer'], ['date' => '2025-01-15', 'client' => '롯데케미칼', 'amount' => 18000000, 'method' => 'transfer'], ['date' => '2025-01-22', 'client' => 'GS칼텍스', 'amount' => 12500000, 'method' => 'card'], ['date' => '2025-01-28', 'client' => '대한항공', 'amount' => 5800000, 'method' => 'transfer'], // 2월 ['date' => '2025-02-05', 'client' => '현대제철', 'amount' => 52000000, 'method' => 'transfer'], ['date' => '2025-02-12', 'client' => 'SK이노베이션', 'amount' => 28000000, 'method' => 'transfer'], ['date' => '2025-02-18', 'client' => 'CJ대한통운', 'amount' => 8500000, 'method' => 'transfer'], ['date' => '2025-02-22', 'client' => '한화솔루션', 'amount' => 25000000, 'method' => 'check'], ['date' => '2025-02-28', 'client' => '포스코', 'amount' => 38000000, 'method' => 'transfer'], // ... 3월 ~ 12월 (패턴 반복) ]; ``` ### 3.8 sales (매출) - 80개 예시 ```php // 월별 6~7건, 총 80건 // 상태: 1~10월 invoiced/confirmed, 11~12월 draft 포함 $sales = [ // 1월 (6건) ['number' => 'SAL-202501-0001', 'date' => '2025-01-05', 'client' => '삼성전자', 'supply' => 50000000, 'status' => 'invoiced'], ['number' => 'SAL-202501-0002', 'date' => '2025-01-08', 'client' => 'LG전자', 'supply' => 25454545, 'status' => 'invoiced'], ['number' => 'SAL-202501-0003', 'date' => '2025-01-12', 'client' => '현대자동차', 'supply' => 38181818, 'status' => 'invoiced'], ['number' => 'SAL-202501-0004', 'date' => '2025-01-18', 'client' => '네이버', 'supply' => 13636364, 'status' => 'confirmed'], ['number' => 'SAL-202501-0005', 'date' => '2025-01-22', 'client' => '카카오', 'supply' => 7727273, 'status' => 'confirmed'], ['number' => 'SAL-202501-0006', 'date' => '2025-01-28', 'client' => '삼성SDS', 'supply' => 31818182, 'status' => 'invoiced'], // ... 2월 ~ 10월 (invoiced/confirmed) // 11월 (8건 - draft 포함) ['number' => 'SAL-202511-0001', 'date' => '2025-11-03', 'client' => '삼성전자', 'supply' => 45000000, 'status' => 'invoiced'], ['number' => 'SAL-202511-0002', 'date' => '2025-11-06', 'client' => 'SK하이닉스', 'supply' => 62000000, 'status' => 'invoiced'], ['number' => 'SAL-202511-0003', 'date' => '2025-11-10', 'client' => '쿠팡', 'supply' => 18181818, 'status' => 'confirmed'], ['number' => 'SAL-202511-0004', 'date' => '2025-11-14', 'client' => '두산에너빌리티','supply' => 55000000, 'status' => 'confirmed'], ['number' => 'SAL-202511-0005', 'date' => '2025-11-18', 'client' => '당근마켓', 'supply' => 8909091, 'status' => 'confirmed'], ['number' => 'SAL-202511-0006', 'date' => '2025-11-22', 'client' => '토스', 'supply' => 12727273, 'status' => 'draft'], ['number' => 'SAL-202511-0007', 'date' => '2025-11-26', 'client' => '배달의민족', 'supply' => 15454545, 'status' => 'draft'], ['number' => 'SAL-202511-0008', 'date' => '2025-11-29', 'client' => 'LG전자', 'supply' => 28000000, 'status' => 'draft'], // 12월 (7건 - draft 위주) ['number' => 'SAL-202512-0001', 'date' => '2025-12-02', 'client' => '삼성전자', 'supply' => 48000000, 'status' => 'confirmed'], ['number' => 'SAL-202512-0002', 'date' => '2025-12-05', 'client' => '현대자동차', 'supply' => 35000000, 'status' => 'draft'], ['number' => 'SAL-202512-0003', 'date' => '2025-12-10', 'client' => '네이버', 'supply' => 22727273, 'status' => 'draft'], ['number' => 'SAL-202512-0004', 'date' => '2025-12-13', 'client' => '카카오', 'supply' => 16363636, 'status' => 'draft'], ['number' => 'SAL-202512-0005', 'date' => '2025-12-17', 'client' => '삼성SDS', 'supply' => 42000000, 'status' => 'draft'], ['number' => 'SAL-202512-0006', 'date' => '2025-12-20', 'client' => 'SK하이닉스', 'supply' => 58000000, 'status' => 'draft'], ['number' => 'SAL-202512-0007', 'date' => '2025-12-23', 'client' => '쿠팡', 'supply' => 20000000, 'status' => 'draft'], ]; ``` ### 3.9 purchases (매입) - 70개 예시 ```php // 월별 5~6건, 총 70건 $purchases = [ // 1월 (5건) ['number' => 'PUR-202501-0001', 'date' => '2025-01-03', 'client' => '한화솔루션', 'supply' => 29090909, 'status' => 'confirmed'], ['number' => 'PUR-202501-0002', 'date' => '2025-01-10', 'client' => '포스코', 'supply' => 40909091, 'status' => 'confirmed'], ['number' => 'PUR-202501-0003', 'date' => '2025-01-15', 'client' => '롯데케미칼', 'supply' => 16363636, 'status' => 'confirmed'], ['number' => 'PUR-202501-0004', 'date' => '2025-01-22', 'client' => 'GS칼텍스', 'supply' => 11363636, 'status' => 'confirmed'], ['number' => 'PUR-202501-0005', 'date' => '2025-01-28', 'client' => '대한항공', 'supply' => 5272727, 'status' => 'confirmed'], // ... 2월 ~ 10월 (confirmed) // 11월 (7건 - draft 포함) ['number' => 'PUR-202511-0001', 'date' => '2025-11-03', 'client' => '현대제철', 'supply' => 48000000, 'status' => 'confirmed'], ['number' => 'PUR-202511-0002', 'date' => '2025-11-08', 'client' => 'SK이노베이션', 'supply' => 32000000, 'status' => 'confirmed'], ['number' => 'PUR-202511-0003', 'date' => '2025-11-12', 'client' => 'CJ대한통운', 'supply' => 9090909, 'status' => 'confirmed'], ['number' => 'PUR-202511-0004', 'date' => '2025-11-18', 'client' => '한화솔루션', 'supply' => 35000000, 'status' => 'confirmed'], ['number' => 'PUR-202511-0005', 'date' => '2025-11-22', 'client' => '포스코', 'supply' => 42000000, 'status' => 'draft'], ['number' => 'PUR-202511-0006', 'date' => '2025-11-26', 'client' => '롯데케미칼', 'supply' => 18181818, 'status' => 'draft'], ['number' => 'PUR-202511-0007', 'date' => '2025-11-29', 'client' => '두산에너빌리티','supply' => 55000000, 'status' => 'draft'], // 12월 (6건 - draft 위주) ['number' => 'PUR-202512-0001', 'date' => '2025-12-02', 'client' => 'GS칼텍스', 'supply' => 15454545, 'status' => 'confirmed'], ['number' => 'PUR-202512-0002', 'date' => '2025-12-06', 'client' => '대한항공', 'supply' => 7272727, 'status' => 'draft'], ['number' => 'PUR-202512-0003', 'date' => '2025-12-11', 'client' => '현대제철', 'supply' => 52000000, 'status' => 'draft'], ['number' => 'PUR-202512-0004', 'date' => '2025-12-15', 'client' => 'SK이노베이션', 'supply' => 28000000, 'status' => 'draft'], ['number' => 'PUR-202512-0005', 'date' => '2025-12-19', 'client' => '한화솔루션', 'supply' => 38000000, 'status' => 'draft'], ['number' => 'PUR-202512-0006', 'date' => '2025-12-23', 'client' => '포스코', 'supply' => 45000000, 'status' => 'draft'], ]; ``` ### 3.10 bills (어음) - 30개 예시 ```php // 수취 어음 15건 + 발행 어음 15건, 총 30건 // bill_type: received(수취), issued(발행) // status (수취): stored, maturityAlert, maturityResult, paymentComplete, dishonored // status (발행): stored, maturityAlert, collectionRequest, collectionComplete, suing, dishonored $bills = [ // 수취 어음 (received) - 15건 ['bill_number' => '202501000001', 'type' => 'received', 'client' => '삼성전자', 'amount' => 50000000, 'issue_date' => '2025-01-15', 'maturity_date' => '2025-04-15', 'status' => 'paymentComplete'], ['bill_number' => '202501000002', 'type' => 'received', 'client' => 'LG전자', 'amount' => 35000000, 'issue_date' => '2025-02-10', 'maturity_date' => '2025-05-10', 'status' => 'paymentComplete'], ['bill_number' => '202502000001', 'type' => 'received', 'client' => 'SK하이닉스', 'amount' => 80000000, 'issue_date' => '2025-02-20', 'maturity_date' => '2025-05-20', 'status' => 'paymentComplete'], ['bill_number' => '202503000001', 'type' => 'received', 'client' => '현대자동차', 'amount' => 45000000, 'issue_date' => '2025-03-05', 'maturity_date' => '2025-06-05', 'status' => 'maturityResult'], ['bill_number' => '202504000001', 'type' => 'received', 'client' => '네이버', 'amount' => 25000000, 'issue_date' => '2025-04-12', 'maturity_date' => '2025-07-12', 'status' => 'maturityResult'], ['bill_number' => '202505000001', 'type' => 'received', 'client' => '카카오', 'amount' => 18000000, 'issue_date' => '2025-05-08', 'maturity_date' => '2025-08-08', 'status' => 'stored'], ['bill_number' => '202506000001', 'type' => 'received', 'client' => '쿠팡', 'amount' => 32000000, 'issue_date' => '2025-06-15', 'maturity_date' => '2025-09-15', 'status' => 'stored'], ['bill_number' => '202507000001', 'type' => 'received', 'client' => '삼성SDS', 'amount' => 65000000, 'issue_date' => '2025-07-20', 'maturity_date' => '2025-10-20', 'status' => 'stored'], ['bill_number' => '202508000001', 'type' => 'received', 'client' => '토스', 'amount' => 15000000, 'issue_date' => '2025-08-10', 'maturity_date' => '2025-11-10', 'status' => 'stored'], ['bill_number' => '202509000001', 'type' => 'received', 'client' => '두산에너빌리티','amount' => 55000000, 'issue_date' => '2025-09-05', 'maturity_date' => '2025-12-05', 'status' => 'maturityAlert'], ['bill_number' => '202510000001', 'type' => 'received', 'client' => '삼성전자', 'amount' => 42000000, 'issue_date' => '2025-10-15', 'maturity_date' => '2026-01-15', 'status' => 'stored'], ['bill_number' => '202511000001', 'type' => 'received', 'client' => 'LG전자', 'amount' => 28000000, 'issue_date' => '2025-11-08', 'maturity_date' => '2026-02-08', 'status' => 'stored'], ['bill_number' => '202511000002', 'type' => 'received', 'client' => '네이버', 'amount' => 38000000, 'issue_date' => '2025-11-20', 'maturity_date' => '2026-02-20', 'status' => 'stored'], ['bill_number' => '202512000001', 'type' => 'received', 'client' => '현대자동차', 'amount' => 52000000, 'issue_date' => '2025-12-10', 'maturity_date' => '2026-03-10', 'status' => 'stored'], ['bill_number' => '202512000002', 'type' => 'received', 'client' => 'SK하이닉스', 'amount' => 70000000, 'issue_date' => '2025-12-18', 'maturity_date' => '2026-03-18', 'status' => 'stored'], // 발행 어음 (issued) - 15건 ['bill_number' => '202501100001', 'type' => 'issued', 'client' => '한화솔루션', 'amount' => 40000000, 'issue_date' => '2025-01-20', 'maturity_date' => '2025-04-20', 'status' => 'collectionComplete'], ['bill_number' => '202502100001', 'type' => 'issued', 'client' => '포스코', 'amount' => 55000000, 'issue_date' => '2025-02-15', 'maturity_date' => '2025-05-15', 'status' => 'collectionComplete'], ['bill_number' => '202503100001', 'type' => 'issued', 'client' => '롯데케미칼', 'amount' => 30000000, 'issue_date' => '2025-03-10', 'maturity_date' => '2025-06-10', 'status' => 'collectionComplete'], ['bill_number' => '202504100001', 'type' => 'issued', 'client' => 'GS칼텍스', 'amount' => 22000000, 'issue_date' => '2025-04-18', 'maturity_date' => '2025-07-18', 'status' => 'collectionComplete'], ['bill_number' => '202505100001', 'type' => 'issued', 'client' => '대한항공', 'amount' => 18000000, 'issue_date' => '2025-05-12', 'maturity_date' => '2025-08-12', 'status' => 'collectionRequest'], ['bill_number' => '202506100001', 'type' => 'issued', 'client' => '현대제철', 'amount' => 48000000, 'issue_date' => '2025-06-20', 'maturity_date' => '2025-09-20', 'status' => 'collectionRequest'], ['bill_number' => '202507100001', 'type' => 'issued', 'client' => 'SK이노베이션', 'amount' => 35000000, 'issue_date' => '2025-07-15', 'maturity_date' => '2025-10-15', 'status' => 'stored'], ['bill_number' => '202508100001', 'type' => 'issued', 'client' => 'CJ대한통운', 'amount' => 25000000, 'issue_date' => '2025-08-22', 'maturity_date' => '2025-11-22', 'status' => 'stored'], ['bill_number' => '202509100001', 'type' => 'issued', 'client' => '두산에너빌리티','amount' => 60000000, 'issue_date' => '2025-09-10', 'maturity_date' => '2025-12-10', 'status' => 'maturityAlert'], ['bill_number' => '202510100001', 'type' => 'issued', 'client' => '한화솔루션', 'amount' => 45000000, 'issue_date' => '2025-10-08', 'maturity_date' => '2026-01-08', 'status' => 'stored'], ['bill_number' => '202511100001', 'type' => 'issued', 'client' => '포스코', 'amount' => 58000000, 'issue_date' => '2025-11-05', 'maturity_date' => '2026-02-05', 'status' => 'stored'], ['bill_number' => '202511100002', 'type' => 'issued', 'client' => '롯데케미칼', 'amount' => 32000000, 'issue_date' => '2025-11-18', 'maturity_date' => '2026-02-18', 'status' => 'stored'], ['bill_number' => '202512100001', 'type' => 'issued', 'client' => 'GS칼텍스', 'amount' => 28000000, 'issue_date' => '2025-12-05', 'maturity_date' => '2026-03-05', 'status' => 'stored'], ['bill_number' => '202512100002', 'type' => 'issued', 'client' => '현대제철', 'amount' => 42000000, 'issue_date' => '2025-12-15', 'maturity_date' => '2026-03-15', 'status' => 'stored'], ['bill_number' => '202512100003', 'type' => 'issued', 'client' => 'SK이노베이션', 'amount' => 38000000, 'issue_date' => '2025-12-22', 'maturity_date' => '2026-03-22', 'status' => 'stored'], ]; // 일부 어음에 차수 관리 내역 추가 (15건) $billInstallments = [ // 수취 어음 차수 ['bill_number' => '202501000001', 'installments' => [ ['date' => '2025-02-15', 'amount' => 25000000, 'note' => '1차 분할 입금'], ['date' => '2025-03-15', 'amount' => 25000000, 'note' => '2차 분할 입금'], ]], ['bill_number' => '202502000001', 'installments' => [ ['date' => '2025-03-20', 'amount' => 40000000, 'note' => '1차 분할 입금'], ['date' => '2025-04-20', 'amount' => 40000000, 'note' => '2차 분할 입금'], ]], ['bill_number' => '202507000001', 'installments' => [ ['date' => '2025-08-20', 'amount' => 30000000, 'note' => '1차 분할 입금'], ['date' => '2025-09-20', 'amount' => 35000000, 'note' => '2차 분할 입금'], ]], // 발행 어음 차수 ['bill_number' => '202501100001', 'installments' => [ ['date' => '2025-02-20', 'amount' => 20000000, 'note' => '1차 분할 지급'], ['date' => '2025-03-20', 'amount' => 20000000, 'note' => '2차 분할 지급'], ]], ['bill_number' => '202502100001', 'installments' => [ ['date' => '2025-03-15', 'amount' => 27500000, 'note' => '1차 분할 지급'], ['date' => '2025-04-15', 'amount' => 27500000, 'note' => '2차 분할 지급'], ]], ['bill_number' => '202506100001', 'installments' => [ ['date' => '2025-07-20', 'amount' => 24000000, 'note' => '1차 분할 지급'], ['date' => '2025-08-20', 'amount' => 24000000, 'note' => '2차 분할 지급'], ]], ]; ``` --- ## 4. Laravel Seeder 구현 전략 ### 4.1 Seeder 파일 구조 ``` database/seeders/ ├── DummyDataSeeder.php ← 메인 Seeder (순서 제어) └── Dummy/ ├── DummyClientGroupSeeder.php ← 거래처 그룹 (5개) ├── DummyBankAccountSeeder.php ← 은행 계좌 (5개) ├── DummyClientSeeder.php ← 거래처 (20개) ├── DummyDepositSeeder.php ← 입금 (60개) ├── DummyWithdrawalSeeder.php ← 출금 (60개) ├── DummyBillSeeder.php ← 어음 (30개) + 차수 (15개) ├── DummySaleSeeder.php ← 매출 (80개) └── DummyPurchaseSeeder.php ← 매입 (70개) ``` ### 4.2 메인 Seeder ```php command->info('🌱 더미 데이터 시딩 시작...'); $this->command->info(' 대상 테넌트: ID ' . self::TENANT_ID); $this->call([ Dummy\DummyClientGroupSeeder::class, Dummy\DummyBankAccountSeeder::class, Dummy\DummyClientSeeder::class, Dummy\DummyDepositSeeder::class, Dummy\DummyWithdrawalSeeder::class, Dummy\DummyBillSeeder::class, Dummy\DummySaleSeeder::class, Dummy\DummyPurchaseSeeder::class, ]); $this->command->info(''); $this->command->info('✅ 더미 데이터 시딩 완료!'); $this->command->table( ['테이블', '생성 수량'], [ ['client_groups', '5'], ['bank_accounts', '5'], ['clients', '20'], ['deposits', '60'], ['withdrawals', '60'], ['bills', '30'], ['bill_installments', '15'], ['sales', '80'], ['purchases', '70'], ['총계', '~345'], ] ); } } ``` ### 4.3 실행 방법 ```bash # 더미 데이터 시딩 php artisan db:seed --class=DummyDataSeeder # 특정 Seeder만 실행 php artisan db:seed --class=Database\\Seeders\\Dummy\\DummyClientSeeder ``` ### 4.4 DB 스키마 상세 정의 #### 4.4.1 client_groups 테이블 ```sql CREATE TABLE client_groups ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', group_code VARCHAR(30) NOT NULL COMMENT '그룹 코드', group_name VARCHAR(100) NOT NULL COMMENT '그룹명', price_rate DECIMAL(5,4) DEFAULT 1.0000 COMMENT '가격 배율', is_active TINYINT DEFAULT 1 COMMENT '활성 여부', created_by BIGINT UNSIGNED NULL COMMENT '생성자 ID', updated_by BIGINT UNSIGNED NULL COMMENT '수정자 ID', deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자 ID', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL, UNIQUE INDEX uq_client_groups_tenant_code (tenant_id, group_code), FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE ); ``` #### 4.4.2 bank_accounts 테이블 ```sql CREATE TABLE bank_accounts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', bank_code VARCHAR(10) NOT NULL COMMENT '은행 코드', bank_name VARCHAR(50) NOT NULL COMMENT '은행명', account_number VARCHAR(30) NOT NULL COMMENT '계좌번호', account_holder VARCHAR(50) NOT NULL COMMENT '예금주', account_name VARCHAR(100) NOT NULL COMMENT '계좌 별칭', status VARCHAR(20) DEFAULT 'active' COMMENT '상태: active/inactive', assigned_user_id BIGINT UNSIGNED NULL COMMENT '담당자 ID', is_primary BOOLEAN DEFAULT FALSE COMMENT '대표계좌 여부', created_by BIGINT UNSIGNED NULL COMMENT '생성자 ID', updated_by BIGINT UNSIGNED NULL COMMENT '수정자 ID', deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자 ID', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL ); ``` #### 4.4.3 clients 테이블 (주요 컬럼) ```sql -- 핵심 필드만 기재 (전체 스키마는 마이그레이션 참조) CREATE TABLE clients ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL, client_group_id BIGINT UNSIGNED NULL COMMENT '그룹 ID (FK)', client_code VARCHAR(20) NOT NULL COMMENT '거래처 코드', name VARCHAR(100) NOT NULL COMMENT '거래처명', client_type VARCHAR(20) NULL COMMENT 'SALES/PURCHASE/BOTH', contact_person VARCHAR(50) NULL COMMENT '담당자명', phone VARCHAR(20) NULL, mobile VARCHAR(20) NULL, email VARCHAR(100) NULL, address TEXT NULL, business_no VARCHAR(20) NULL COMMENT '사업자번호', business_type VARCHAR(50) NULL COMMENT '업종', business_item VARCHAR(100) NULL COMMENT '업태', is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL ); ``` #### 4.4.4 deposits 테이블 ```sql CREATE TABLE deposits ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', deposit_date DATE NOT NULL COMMENT '입금일', client_id BIGINT UNSIGNED NULL COMMENT '거래처 ID', client_name VARCHAR(100) NULL COMMENT '비회원 거래처명', bank_account_id BIGINT UNSIGNED NULL COMMENT '입금 계좌 ID', amount DECIMAL(15,2) NOT NULL COMMENT '금액', payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check', account_code VARCHAR(20) NULL COMMENT '계정과목', description TEXT NULL COMMENT '적요', reference_type VARCHAR(50) NULL COMMENT '참조 유형', reference_id BIGINT UNSIGNED NULL COMMENT '참조 ID', created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_by BIGINT UNSIGNED NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL ); ``` #### 4.4.5 withdrawals 테이블 ```sql CREATE TABLE withdrawals ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', withdrawal_date DATE NOT NULL COMMENT '출금일', client_id BIGINT UNSIGNED NULL COMMENT '거래처 ID', client_name VARCHAR(100) NULL COMMENT '비회원 거래처명', bank_account_id BIGINT UNSIGNED NULL COMMENT '출금 계좌 ID', amount DECIMAL(15,2) NOT NULL COMMENT '금액', payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check', account_code VARCHAR(20) NULL COMMENT '계정과목', description TEXT NULL COMMENT '적요', reference_type VARCHAR(50) NULL COMMENT '참조 유형', reference_id BIGINT UNSIGNED NULL COMMENT '참조 ID', created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_by BIGINT UNSIGNED NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL ); ``` #### 4.4.6 sales 테이블 ```sql CREATE TABLE sales ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', sale_number VARCHAR(30) NOT NULL COMMENT '매출번호', sale_date DATE NOT NULL COMMENT '매출일자', client_id BIGINT UNSIGNED NOT NULL COMMENT '거래처 ID', supply_amount DECIMAL(15,2) NOT NULL COMMENT '공급가액', tax_amount DECIMAL(15,2) NOT NULL COMMENT '세액', total_amount DECIMAL(15,2) NOT NULL COMMENT '합계', description TEXT NULL COMMENT '적요', status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed/invoiced', tax_invoice_id BIGINT UNSIGNED NULL COMMENT '세금계산서 ID', deposit_id BIGINT UNSIGNED NULL COMMENT '입금 연결 ID', created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_by BIGINT UNSIGNED NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL, UNIQUE INDEX uk_tenant_sale_number (tenant_id, sale_number) ); ``` #### 4.4.7 purchases 테이블 ```sql CREATE TABLE purchases ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', purchase_number VARCHAR(30) NOT NULL COMMENT '매입번호', purchase_date DATE NOT NULL COMMENT '매입일자', client_id BIGINT UNSIGNED NOT NULL COMMENT '거래처 ID', supply_amount DECIMAL(15,2) NOT NULL COMMENT '공급가액', tax_amount DECIMAL(15,2) NOT NULL COMMENT '세액', total_amount DECIMAL(15,2) NOT NULL COMMENT '합계', description TEXT NULL COMMENT '적요', status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed', withdrawal_id BIGINT UNSIGNED NULL COMMENT '출금 연결 ID', created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_by BIGINT UNSIGNED NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL, UNIQUE INDEX uk_tenant_purchase_number (tenant_id, purchase_number) ); ``` ### 4.5 모델 경로 및 네임스페이스 | 테이블 | 모델 클래스 | 네임스페이스 | |--------|------------|-------------| | client_groups | ClientGroup | `App\Models\Orders\ClientGroup` | | bank_accounts | BankAccount | `App\Models\Tenants\BankAccount` | | clients | Client | `App\Models\Orders\Client` | | deposits | Deposit | `App\Models\Tenants\Deposit` | | withdrawals | Withdrawal | `App\Models\Tenants\Withdrawal` | | sales | Sale | `App\Models\Tenants\Sale` | | purchases | Purchase | `App\Models\Tenants\Purchase` | ### 4.6 필수 상수값 #### 결제수단 (payment_method) ```php // Deposit::PAYMENT_METHODS, Withdrawal::PAYMENT_METHODS [ 'cash' => '현금', 'transfer' => '계좌이체', 'card' => '카드', 'check' => '수표', ] ``` #### 매출 상태 (Sale::STATUSES) ```php [ 'draft' => '임시저장', 'confirmed' => '확정', 'invoiced' => '세금계산서발행', ] ``` #### 매입 상태 (Purchase::STATUSES) ```php [ 'draft' => '임시저장', 'confirmed' => '확정', ] ``` #### 거래처 유형 (client_type) ```php // common_codes 테이블에서 관리 // group: 'client_type' [ 'SALES' => '매출처', 'PURCHASE' => '매입처', 'BOTH' => '매출매입처', ] ``` ### 4.7 완전한 Seeder 코드 구현 #### 4.7.1 DummyClientGroupSeeder.php ```php 'VIP', 'group_name' => 'VIP 고객', 'price_rate' => 0.95], ['group_code' => 'GOLD', 'group_name' => '골드 고객', 'price_rate' => 0.97], ['group_code' => 'SILVER', 'group_name' => '실버 고객', 'price_rate' => 0.98], ['group_code' => 'NORMAL', 'group_name' => '일반 고객', 'price_rate' => 1.00], ['group_code' => 'NEW', 'group_name' => '신규 고객', 'price_rate' => 1.00], ]; foreach ($groups as $group) { ClientGroup::create([ 'tenant_id' => $tenantId, 'group_code' => $group['group_code'], 'group_name' => $group['group_name'], 'price_rate' => $group['price_rate'], 'is_active' => true, 'created_by' => $userId, ]); } $this->command->info(' ✓ client_groups: ' . count($groups) . '건 생성'); } } ``` #### 4.7.2 DummyBankAccountSeeder.php ```php '004', 'bank_name' => 'KB국민은행', 'account_number' => '123-45-6789012', 'account_holder' => '프론트테스트', 'account_name' => '운영계좌', 'is_primary' => true], ['bank_code' => '088', 'bank_name' => '신한은행', 'account_number' => '110-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '급여계좌', 'is_primary' => false], ['bank_code' => '020', 'bank_name' => '우리은행', 'account_number' => '1002-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '예비계좌', 'is_primary' => false], ['bank_code' => '081', 'bank_name' => '하나은행', 'account_number' => '123-456789-12345','account_holder' => '프론트테스트', 'account_name' => '법인카드', 'is_primary' => false], ['bank_code' => '011', 'bank_name' => 'NH농협은행', 'account_number' => '351-1234-5678-12','account_holder' => '프론트테스트', 'account_name' => '비상금', 'is_primary' => false], ]; foreach ($accounts as $account) { BankAccount::create([ 'tenant_id' => $tenantId, 'bank_code' => $account['bank_code'], 'bank_name' => $account['bank_name'], 'account_number' => $account['account_number'], 'account_holder' => $account['account_holder'], 'account_name' => $account['account_name'], 'status' => 'active', 'is_primary' => $account['is_primary'], 'created_by' => $userId, ]); } $this->command->info(' ✓ bank_accounts: ' . count($accounts) . '건 생성'); } } ``` #### 4.7.3 DummyClientSeeder.php ```php pluck('id', 'group_code') ->toArray(); // 매출처 (SALES) - 10개 $salesClients = [ ['code' => 'S001', 'name' => '삼성전자', 'business_no' => '124-81-00998', 'group' => 'VIP', 'contact' => '김철수', 'phone' => '02-1234-5678', 'email' => 'kim@samsung.com'], ['code' => 'S002', 'name' => 'LG전자', 'business_no' => '107-86-14075', 'group' => 'VIP', 'contact' => '이영희', 'phone' => '02-2345-6789', 'email' => 'lee@lg.com'], ['code' => 'S003', 'name' => 'SK하이닉스', 'business_no' => '204-81-17169', 'group' => 'GOLD', 'contact' => '박민수', 'phone' => '031-123-4567', 'email' => 'park@skhynix.com'], ['code' => 'S004', 'name' => '현대자동차', 'business_no' => '101-81-05765', 'group' => 'GOLD', 'contact' => '정은지', 'phone' => '02-3456-7890', 'email' => 'jung@hyundai.com'], ['code' => 'S005', 'name' => '네이버', 'business_no' => '220-81-62517', 'group' => 'GOLD', 'contact' => '최준호', 'phone' => '031-234-5678', 'email' => 'choi@naver.com'], ['code' => 'S006', 'name' => '카카오', 'business_no' => '120-87-65763', 'group' => 'SILVER', 'contact' => '강미래', 'phone' => '02-4567-8901', 'email' => 'kang@kakao.com'], ['code' => 'S007', 'name' => '쿠팡', 'business_no' => '120-88-00767', 'group' => 'SILVER', 'contact' => '임도현', 'phone' => '02-5678-9012', 'email' => 'lim@coupang.com'], ['code' => 'S008', 'name' => '토스', 'business_no' => '120-87-83139', 'group' => 'NORMAL', 'contact' => '윤서연', 'phone' => '02-6789-0123', 'email' => 'yoon@toss.im'], ['code' => 'S009', 'name' => '배달의민족', 'business_no' => '220-87-93847', 'group' => 'NORMAL', 'contact' => '한지민', 'phone' => '02-7890-1234', 'email' => 'han@woowahan.com'], ['code' => 'S010', 'name' => '당근마켓', 'business_no' => '815-87-01234', 'group' => 'NEW', 'contact' => '오태양', 'phone' => '02-8901-2345', 'email' => 'oh@daangn.com'], ]; // 매입처 (PURCHASE) - 7개 $purchaseClients = [ ['code' => 'P001', 'name' => '한화솔루션', 'business_no' => '138-81-00610', 'group' => null, 'contact' => '김재원', 'phone' => '02-1111-2222', 'email' => 'kim@hanwha.com'], ['code' => 'P002', 'name' => '포스코', 'business_no' => '506-81-08754', 'group' => null, 'contact' => '이현석', 'phone' => '054-111-2222', 'email' => 'lee@posco.com'], ['code' => 'P003', 'name' => '롯데케미칼', 'business_no' => '301-81-07123', 'group' => null, 'contact' => '박서준', 'phone' => '02-2222-3333', 'email' => 'park@lottechem.com'], ['code' => 'P004', 'name' => 'GS칼텍스', 'business_no' => '104-81-23858', 'group' => null, 'contact' => '정해인', 'phone' => '02-3333-4444', 'email' => 'jung@gscaltex.com'], ['code' => 'P005', 'name' => '대한항공', 'business_no' => '110-81-14794', 'group' => null, 'contact' => '송민호', 'phone' => '02-4444-5555', 'email' => 'song@koreanair.com'], ['code' => 'P006', 'name' => '현대제철', 'business_no' => '130-81-12345', 'group' => null, 'contact' => '강동원', 'phone' => '032-555-6666', 'email' => 'kang@hyundaisteel.com'], ['code' => 'P007', 'name' => 'SK이노베이션', 'business_no' => '110-81-67890', 'group' => null, 'contact' => '유재석', 'phone' => '02-6666-7777', 'email' => 'yoo@skinnovation.com'], ]; // 매출매입처 (BOTH) - 3개 $bothClients = [ ['code' => 'B001', 'name' => '두산에너빌리티', 'business_no' => '124-81-08628', 'group' => 'GOLD', 'contact' => '조인성', 'phone' => '02-7777-8888', 'email' => 'cho@doosan.com'], ['code' => 'B002', 'name' => 'CJ대한통운', 'business_no' => '104-81-39849', 'group' => 'SILVER', 'contact' => '공유', 'phone' => '02-8888-9999', 'email' => 'gong@cjlogistics.com'], ['code' => 'B003', 'name' => '삼성SDS', 'business_no' => '124-81-34567', 'group' => 'VIP', 'contact' => '이정재', 'phone' => '02-9999-0000', 'email' => 'lee@samsungsds.com'], ]; $count = 0; // 매출처 생성 foreach ($salesClients as $client) { $this->createClient($client, 'SALES', $tenantId, $userId, $groups); $count++; } // 매입처 생성 foreach ($purchaseClients as $client) { $this->createClient($client, 'PURCHASE', $tenantId, $userId, $groups); $count++; } // 매출매입처 생성 foreach ($bothClients as $client) { $this->createClient($client, 'BOTH', $tenantId, $userId, $groups); $count++; } $this->command->info(' ✓ clients: ' . $count . '건 생성'); } private function createClient(array $data, string $type, int $tenantId, int $userId, array $groups): void { Client::create([ 'tenant_id' => $tenantId, 'client_group_id' => $data['group'] ? ($groups[$data['group']] ?? null) : null, 'client_code' => $data['code'], 'name' => $data['name'], 'client_type' => $type, 'contact_person' => $data['contact'], 'phone' => $data['phone'], 'email' => $data['email'], 'business_no' => $data['business_no'], 'business_type' => '제조업', 'business_item' => '전자제품', 'is_active' => true, ]); } } ``` #### 4.7.4 DummyDepositSeeder.php ```php whereIn('client_type', ['SALES', 'BOTH']) ->get() ->keyBy('name'); // 은행계좌 (대표계좌 우선) $bankAccounts = BankAccount::where('tenant_id', $tenantId) ->where('status', 'active') ->orderByDesc('is_primary') ->get(); $primaryBankId = $bankAccounts->first()?->id; // 결제수단 분포: transfer(70%), card(15%), cash(10%), check(5%) $methods = array_merge( array_fill(0, 14, 'transfer'), array_fill(0, 3, 'card'), array_fill(0, 2, 'cash'), array_fill(0, 1, 'check') ); // 거래처 순환 목록 $clientNames = [ '삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버', '카카오', '쿠팡', '토스', '배달의민족', '당근마켓', '두산에너빌리티', 'CJ대한통운', '삼성SDS', ]; // 금액 범위 $amounts = [ 'small' => [1000000, 5000000], // 30% 'medium' => [5000000, 30000000], // 50% 'large' => [30000000, 100000000], // 20% ]; $count = 0; $year = 2025; // 월별 5건씩, 총 60건 for ($month = 1; $month <= 12; $month++) { $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); for ($i = 0; $i < 5; $i++) { $day = rand(1, $daysInMonth); $clientName = $clientNames[($month * 5 + $i) % count($clientNames)]; $client = $clients->get($clientName); // 금액 결정 (분포에 따라) $rand = rand(1, 100); if ($rand <= 30) { $amount = rand($amounts['small'][0], $amounts['small'][1]); } elseif ($rand <= 80) { $amount = rand($amounts['medium'][0], $amounts['medium'][1]); } else { $amount = rand($amounts['large'][0], $amounts['large'][1]); } Deposit::create([ 'tenant_id' => $tenantId, 'deposit_date' => sprintf('%04d-%02d-%02d', $year, $month, $day), 'client_id' => $client?->id, 'client_name' => $client ? null : $clientName, 'bank_account_id' => $primaryBankId, 'amount' => $amount, 'payment_method' => $methods[array_rand($methods)], 'description' => $clientName . ' 입금', 'created_by' => $userId, ]); $count++; } } $this->command->info(' ✓ deposits: ' . $count . '건 생성'); } } ``` #### 4.7.5 DummyWithdrawalSeeder.php ```php whereIn('client_type', ['PURCHASE', 'BOTH']) ->get() ->keyBy('name'); // 은행계좌 $primaryBankId = BankAccount::where('tenant_id', $tenantId) ->where('is_primary', true) ->value('id'); // 결제수단 분포 $methods = array_merge( array_fill(0, 14, 'transfer'), array_fill(0, 3, 'card'), array_fill(0, 2, 'cash'), array_fill(0, 1, 'check') ); // 매입처 순환 목록 $clientNames = [ '한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공', '현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티', ]; $amounts = [ 'small' => [1000000, 5000000], 'medium' => [5000000, 30000000], 'large' => [30000000, 80000000], ]; $count = 0; $year = 2025; for ($month = 1; $month <= 12; $month++) { $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); for ($i = 0; $i < 5; $i++) { $day = rand(1, $daysInMonth); $clientName = $clientNames[($month * 5 + $i) % count($clientNames)]; $client = $clients->get($clientName); $rand = rand(1, 100); if ($rand <= 30) { $amount = rand($amounts['small'][0], $amounts['small'][1]); } elseif ($rand <= 80) { $amount = rand($amounts['medium'][0], $amounts['medium'][1]); } else { $amount = rand($amounts['large'][0], $amounts['large'][1]); } Withdrawal::create([ 'tenant_id' => $tenantId, 'withdrawal_date' => sprintf('%04d-%02d-%02d', $year, $month, $day), 'client_id' => $client?->id, 'client_name' => $client ? null : $clientName, 'bank_account_id' => $primaryBankId, 'amount' => $amount, 'payment_method' => $methods[array_rand($methods)], 'description' => $clientName . ' 지급', 'created_by' => $userId, ]); $count++; } } $this->command->info(' ✓ withdrawals: ' . $count . '건 생성'); } } ``` #### 4.7.6 DummySaleSeeder.php ```php whereIn('client_type', ['SALES', 'BOTH']) ->get() ->keyBy('name'); $clientNames = [ '삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버', '카카오', '쿠팡', '토스', '배달의민족', '당근마켓', '두산에너빌리티', 'CJ대한통운', '삼성SDS', ]; $amounts = [ 'small' => [1000000, 5000000], 'medium' => [5000000, 30000000], 'large' => [30000000, 100000000], ]; // 월별 건수: 1~10월 6~7건, 11월 8건, 12월 7건 = 80건 $monthlyCount = [6, 6, 7, 6, 7, 6, 7, 6, 7, 7, 8, 7]; $count = 0; $year = 2025; for ($month = 1; $month <= 12; $month++) { $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); $salesCount = $monthlyCount[$month - 1]; for ($i = 0; $i < $salesCount; $i++) { $day = (int) (($i + 1) * $daysInMonth / ($salesCount + 1)); $day = max(1, min($day, $daysInMonth)); $clientName = $clientNames[($count) % count($clientNames)]; $client = $clients->get($clientName); // 금액 결정 $rand = rand(1, 100); if ($rand <= 30) { $supply = rand($amounts['small'][0], $amounts['small'][1]); } elseif ($rand <= 80) { $supply = rand($amounts['medium'][0], $amounts['medium'][1]); } else { $supply = rand($amounts['large'][0], $amounts['large'][1]); } // 상태 결정: 1~10월 invoiced/confirmed, 11~12월 draft 포함 if ($month <= 10) { $status = rand(0, 1) ? 'invoiced' : 'confirmed'; } elseif ($month == 11) { $status = $i < 5 ? (rand(0, 1) ? 'invoiced' : 'confirmed') : 'draft'; } else { $status = $i < 1 ? 'confirmed' : 'draft'; } $tax = round($supply * 0.1); $total = $supply + $tax; Sale::create([ 'tenant_id' => $tenantId, 'sale_number' => sprintf('SAL-%04d%02d-%04d', $year, $month, $i + 1), 'sale_date' => sprintf('%04d-%02d-%02d', $year, $month, $day), 'client_id' => $client->id, 'supply_amount' => $supply, 'tax_amount' => $tax, 'total_amount' => $total, 'description' => $clientName . ' 매출', 'status' => $status, 'created_by' => $userId, ]); $count++; } } $this->command->info(' ✓ sales: ' . $count . '건 생성'); } } ``` #### 4.7.7 DummyPurchaseSeeder.php ```php whereIn('client_type', ['PURCHASE', 'BOTH']) ->get() ->keyBy('name'); $clientNames = [ '한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공', '현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티', ]; $amounts = [ 'small' => [1000000, 5000000], 'medium' => [5000000, 30000000], 'large' => [30000000, 80000000], ]; // 월별 건수: 1~10월 5~6건, 11월 7건, 12월 6건 = 70건 $monthlyCount = [5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 7, 6]; $count = 0; $year = 2025; for ($month = 1; $month <= 12; $month++) { $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); $purchaseCount = $monthlyCount[$month - 1]; for ($i = 0; $i < $purchaseCount; $i++) { $day = (int) (($i + 1) * $daysInMonth / ($purchaseCount + 1)); $day = max(1, min($day, $daysInMonth)); $clientName = $clientNames[($count) % count($clientNames)]; $client = $clients->get($clientName); $rand = rand(1, 100); if ($rand <= 30) { $supply = rand($amounts['small'][0], $amounts['small'][1]); } elseif ($rand <= 80) { $supply = rand($amounts['medium'][0], $amounts['medium'][1]); } else { $supply = rand($amounts['large'][0], $amounts['large'][1]); } // 상태 결정 if ($month <= 10) { $status = 'confirmed'; } elseif ($month == 11) { $status = $i < 4 ? 'confirmed' : 'draft'; } else { $status = $i < 1 ? 'confirmed' : 'draft'; } $tax = round($supply * 0.1); $total = $supply + $tax; Purchase::create([ 'tenant_id' => $tenantId, 'purchase_number' => sprintf('PUR-%04d%02d-%04d', $year, $month, $i + 1), 'purchase_date' => sprintf('%04d-%02d-%02d', $year, $month, $day), 'client_id' => $client->id, 'supply_amount' => $supply, 'tax_amount' => $tax, 'total_amount' => $total, 'description' => $clientName . ' 매입', 'status' => $status, 'created_by' => $userId, ]); $count++; } } $this->command->info(' ✓ purchases: ' . $count . '건 생성'); } } ``` #### 4.7.8 DummyBillSeeder.php ```php get()->keyBy('name'); // 은행계좌 (대표계좌) $primaryBankId = BankAccount::where('tenant_id', $tenantId) ->where('is_primary', true) ->value('id'); // 수취 어음 데이터 (received) - 15건 $receivedBills = [ ['bill_number' => '202501000001', 'client' => '삼성전자', 'amount' => 50000000, 'issue_date' => '2025-01-15', 'maturity_date' => '2025-04-15', 'status' => 'paymentComplete'], ['bill_number' => '202501000002', 'client' => 'LG전자', 'amount' => 35000000, 'issue_date' => '2025-02-10', 'maturity_date' => '2025-05-10', 'status' => 'paymentComplete'], ['bill_number' => '202502000001', 'client' => 'SK하이닉스', 'amount' => 80000000, 'issue_date' => '2025-02-20', 'maturity_date' => '2025-05-20', 'status' => 'paymentComplete'], ['bill_number' => '202503000001', 'client' => '현대자동차', 'amount' => 45000000, 'issue_date' => '2025-03-05', 'maturity_date' => '2025-06-05', 'status' => 'maturityResult'], ['bill_number' => '202504000001', 'client' => '네이버', 'amount' => 25000000, 'issue_date' => '2025-04-12', 'maturity_date' => '2025-07-12', 'status' => 'maturityResult'], ['bill_number' => '202505000001', 'client' => '카카오', 'amount' => 18000000, 'issue_date' => '2025-05-08', 'maturity_date' => '2025-08-08', 'status' => 'stored'], ['bill_number' => '202506000001', 'client' => '쿠팡', 'amount' => 32000000, 'issue_date' => '2025-06-15', 'maturity_date' => '2025-09-15', 'status' => 'stored'], ['bill_number' => '202507000001', 'client' => '삼성SDS', 'amount' => 65000000, 'issue_date' => '2025-07-20', 'maturity_date' => '2025-10-20', 'status' => 'stored'], ['bill_number' => '202508000001', 'client' => '토스', 'amount' => 15000000, 'issue_date' => '2025-08-10', 'maturity_date' => '2025-11-10', 'status' => 'stored'], ['bill_number' => '202509000001', 'client' => '두산에너빌리티', 'amount' => 55000000, 'issue_date' => '2025-09-05', 'maturity_date' => '2025-12-05', 'status' => 'maturityAlert'], ['bill_number' => '202510000001', 'client' => '삼성전자', 'amount' => 42000000, 'issue_date' => '2025-10-15', 'maturity_date' => '2026-01-15', 'status' => 'stored'], ['bill_number' => '202511000001', 'client' => 'LG전자', 'amount' => 28000000, 'issue_date' => '2025-11-08', 'maturity_date' => '2026-02-08', 'status' => 'stored'], ['bill_number' => '202511000002', 'client' => '네이버', 'amount' => 38000000, 'issue_date' => '2025-11-20', 'maturity_date' => '2026-02-20', 'status' => 'stored'], ['bill_number' => '202512000001', 'client' => '현대자동차', 'amount' => 52000000, 'issue_date' => '2025-12-10', 'maturity_date' => '2026-03-10', 'status' => 'stored'], ['bill_number' => '202512000002', 'client' => 'SK하이닉스', 'amount' => 70000000, 'issue_date' => '2025-12-18', 'maturity_date' => '2026-03-18', 'status' => 'stored'], ]; // 발행 어음 데이터 (issued) - 15건 $issuedBills = [ ['bill_number' => '202501100001', 'client' => '한화솔루션', 'amount' => 40000000, 'issue_date' => '2025-01-20', 'maturity_date' => '2025-04-20', 'status' => 'collectionComplete'], ['bill_number' => '202502100001', 'client' => '포스코', 'amount' => 55000000, 'issue_date' => '2025-02-15', 'maturity_date' => '2025-05-15', 'status' => 'collectionComplete'], ['bill_number' => '202503100001', 'client' => '롯데케미칼', 'amount' => 30000000, 'issue_date' => '2025-03-10', 'maturity_date' => '2025-06-10', 'status' => 'collectionComplete'], ['bill_number' => '202504100001', 'client' => 'GS칼텍스', 'amount' => 22000000, 'issue_date' => '2025-04-18', 'maturity_date' => '2025-07-18', 'status' => 'collectionComplete'], ['bill_number' => '202505100001', 'client' => '대한항공', 'amount' => 18000000, 'issue_date' => '2025-05-12', 'maturity_date' => '2025-08-12', 'status' => 'collectionRequest'], ['bill_number' => '202506100001', 'client' => '현대제철', 'amount' => 48000000, 'issue_date' => '2025-06-20', 'maturity_date' => '2025-09-20', 'status' => 'collectionRequest'], ['bill_number' => '202507100001', 'client' => 'SK이노베이션', 'amount' => 35000000, 'issue_date' => '2025-07-15', 'maturity_date' => '2025-10-15', 'status' => 'stored'], ['bill_number' => '202508100001', 'client' => 'CJ대한통운', 'amount' => 25000000, 'issue_date' => '2025-08-22', 'maturity_date' => '2025-11-22', 'status' => 'stored'], ['bill_number' => '202509100001', 'client' => '두산에너빌리티', 'amount' => 60000000, 'issue_date' => '2025-09-10', 'maturity_date' => '2025-12-10', 'status' => 'maturityAlert'], ['bill_number' => '202510100001', 'client' => '한화솔루션', 'amount' => 45000000, 'issue_date' => '2025-10-08', 'maturity_date' => '2026-01-08', 'status' => 'stored'], ['bill_number' => '202511100001', 'client' => '포스코', 'amount' => 58000000, 'issue_date' => '2025-11-05', 'maturity_date' => '2026-02-05', 'status' => 'stored'], ['bill_number' => '202511100002', 'client' => '롯데케미칼', 'amount' => 32000000, 'issue_date' => '2025-11-18', 'maturity_date' => '2026-02-18', 'status' => 'stored'], ['bill_number' => '202512100001', 'client' => 'GS칼텍스', 'amount' => 28000000, 'issue_date' => '2025-12-05', 'maturity_date' => '2026-03-05', 'status' => 'stored'], ['bill_number' => '202512100002', 'client' => '현대제철', 'amount' => 42000000, 'issue_date' => '2025-12-15', 'maturity_date' => '2026-03-15', 'status' => 'stored'], ['bill_number' => '202512100003', 'client' => 'SK이노베이션', 'amount' => 38000000, 'issue_date' => '2025-12-22', 'maturity_date' => '2026-03-22', 'status' => 'stored'], ]; // 차수 관리 데이터 $installmentsData = [ '202501000001' => [ ['date' => '2025-02-15', 'amount' => 25000000, 'note' => '1차 분할 입금'], ['date' => '2025-03-15', 'amount' => 25000000, 'note' => '2차 분할 입금'], ], '202502000001' => [ ['date' => '2025-03-20', 'amount' => 40000000, 'note' => '1차 분할 입금'], ['date' => '2025-04-20', 'amount' => 40000000, 'note' => '2차 분할 입금'], ], '202507000001' => [ ['date' => '2025-08-20', 'amount' => 30000000, 'note' => '1차 분할 입금'], ['date' => '2025-09-20', 'amount' => 35000000, 'note' => '2차 분할 입금'], ], '202501100001' => [ ['date' => '2025-02-20', 'amount' => 20000000, 'note' => '1차 분할 지급'], ['date' => '2025-03-20', 'amount' => 20000000, 'note' => '2차 분할 지급'], ], '202502100001' => [ ['date' => '2025-03-15', 'amount' => 27500000, 'note' => '1차 분할 지급'], ['date' => '2025-04-15', 'amount' => 27500000, 'note' => '2차 분할 지급'], ], '202506100001' => [ ['date' => '2025-07-20', 'amount' => 24000000, 'note' => '1차 분할 지급'], ['date' => '2025-08-20', 'amount' => 24000000, 'note' => '2차 분할 지급'], ], ]; $billCount = 0; $installmentCount = 0; // 수취 어음 생성 foreach ($receivedBills as $data) { $client = $clients->get($data['client']); $bill = Bill::create([ 'tenant_id' => $tenantId, 'bill_number' => $data['bill_number'], 'bill_type' => 'received', 'client_id' => $client?->id, 'client_name' => $client ? null : $data['client'], 'amount' => $data['amount'], 'issue_date' => $data['issue_date'], 'maturity_date' => $data['maturity_date'], 'status' => $data['status'], 'is_electronic' => rand(0, 1) === 1, 'bank_account_id' => $primaryBankId, 'installment_count' => isset($installmentsData[$data['bill_number']]) ? count($installmentsData[$data['bill_number']]) : 0, 'created_by' => $userId, ]); $billCount++; // 차수 관리 데이터 추가 if (isset($installmentsData[$data['bill_number']])) { foreach ($installmentsData[$data['bill_number']] as $instData) { BillInstallment::create([ 'bill_id' => $bill->id, 'installment_date' => $instData['date'], 'amount' => $instData['amount'], 'note' => $instData['note'], ]); $installmentCount++; } } } // 발행 어음 생성 foreach ($issuedBills as $data) { $client = $clients->get($data['client']); $bill = Bill::create([ 'tenant_id' => $tenantId, 'bill_number' => $data['bill_number'], 'bill_type' => 'issued', 'client_id' => $client?->id, 'client_name' => $client ? null : $data['client'], 'amount' => $data['amount'], 'issue_date' => $data['issue_date'], 'maturity_date' => $data['maturity_date'], 'status' => $data['status'], 'is_electronic' => rand(0, 1) === 1, 'bank_account_id' => $primaryBankId, 'installment_count' => isset($installmentsData[$data['bill_number']]) ? count($installmentsData[$data['bill_number']]) : 0, 'created_by' => $userId, ]); $billCount++; // 차수 관리 데이터 추가 if (isset($installmentsData[$data['bill_number']])) { foreach ($installmentsData[$data['bill_number']] as $instData) { BillInstallment::create([ 'bill_id' => $bill->id, 'installment_date' => $instData['date'], 'amount' => $instData['amount'], 'note' => $instData['note'], ]); $installmentCount++; } } } $this->command->info(' ✓ bills: ' . $billCount . '건 생성'); $this->command->info(' ✓ bill_installments: ' . $installmentCount . '건 생성'); } } ``` --- ## 5. 데이터 검증 ### 5.1 시딩 후 검증 쿼리 ```sql -- 테이블별 데이터 수 확인 SELECT 'client_groups' as tbl, COUNT(*) as cnt FROM client_groups WHERE tenant_id = 287 UNION ALL SELECT 'bank_accounts', COUNT(*) FROM bank_accounts WHERE tenant_id = 287 UNION ALL SELECT 'clients', COUNT(*) FROM clients WHERE tenant_id = 287 UNION ALL SELECT 'deposits', COUNT(*) FROM deposits WHERE tenant_id = 287 UNION ALL SELECT 'withdrawals', COUNT(*) FROM withdrawals WHERE tenant_id = 287 UNION ALL SELECT 'bills', COUNT(*) FROM bills WHERE tenant_id = 287 UNION ALL SELECT 'bill_installments', COUNT(*) FROM bill_installments WHERE bill_id IN (SELECT id FROM bills WHERE tenant_id = 287) UNION ALL SELECT 'sales', COUNT(*) FROM sales WHERE tenant_id = 287 UNION ALL SELECT 'purchases', COUNT(*) FROM purchases WHERE tenant_id = 287; -- 어음 현황 (수취/발행별) SELECT bill_type, status, COUNT(*) as count, SUM(amount) as total_amount FROM bills WHERE tenant_id = 287 GROUP BY bill_type, status ORDER BY bill_type, status; -- 월별 매출 현황 SELECT DATE_FORMAT(sale_date, '%Y-%m') as month, COUNT(*) as count, SUM(total_amount) as total FROM sales WHERE tenant_id = 287 GROUP BY month ORDER BY month; -- 거래처별 매출/매입 합계 SELECT c.name, COALESCE(SUM(s.total_amount), 0) as total_sales, COALESCE(SUM(p.total_amount), 0) as total_purchases FROM clients c LEFT JOIN sales s ON c.id = s.client_id AND s.deleted_at IS NULL LEFT JOIN purchases p ON c.id = p.client_id AND p.deleted_at IS NULL WHERE c.tenant_id = 287 GROUP BY c.id, c.name ORDER BY total_sales DESC; ``` ### 5.2 예상 결과 요약 | 항목 | 예상 값 | |------|---------| | 총 거래처 | 24개 (기존 4 + 신규 20) | | 총 매출건수 | 80건 | | 총 매입건수 | 70건 | | 총 입금건수 | 60건 | | 총 출금건수 | 60건 | | 총 어음건수 | 30건 (수취 15건 + 발행 15건) | | 총 차수건수 | 12건 (6개 어음 × 2차) | | 연간 매출액 | 약 25~30억원 | | 연간 매입액 | 약 18~22억원 | | 수취어음 총액 | 약 6.5억원 | | 발행어음 총액 | 약 5.8억원 | --- ## 6. 변경 이력 | 날짜 | 내용 | 작성자 | |------|------|--------| | 2025-12-23 | 문서 초안 작성 | Claude | | 2025-12-23 | 1년치 300개 데이터로 확장 | Claude | | 2025-12-23 | DB 스키마 상세 정의 추가 (4.4) | Claude | | 2025-12-23 | 모델 경로 및 네임스페이스 추가 (4.5) | Claude | | 2025-12-23 | 필수 상수값 정의 추가 (4.6) | Claude | | 2025-12-23 | 완전한 Seeder 코드 구현 추가 (4.7) | Claude | | 2025-12-23 | 어음(bills) 더미 데이터 추가 (30건 + 차수 12건) | Claude | --- ## 7. 다음 단계 1. [x] Seeder 파일 구현 → 4.7 섹션에 완전한 코드 포함 2. [ ] Seeder 파일 생성 (코드 복사 후 파일 생성) 3. [ ] 시딩 실행 (`php artisan db:seed --class=DummyDataSeeder`) 4. [ ] 데이터 검증 (5.1 검증 쿼리 실행) 5. [ ] React 연동 테스트 6. [ ] 추가 데이터 필요시 확장 --- ## 8. 빠른 시작 가이드 ### 8.1 Seeder 파일 생성 순서 ```bash # 1. Dummy 디렉토리 생성 mkdir -p api/database/seeders/Dummy # 2. 파일 생성 (4.2 ~ 4.7 코드 복사) # - api/database/seeders/DummyDataSeeder.php # - api/database/seeders/Dummy/DummyClientGroupSeeder.php # - api/database/seeders/Dummy/DummyBankAccountSeeder.php # - api/database/seeders/Dummy/DummyClientSeeder.php # - api/database/seeders/Dummy/DummyDepositSeeder.php # - api/database/seeders/Dummy/DummyWithdrawalSeeder.php # - api/database/seeders/Dummy/DummySaleSeeder.php # - api/database/seeders/Dummy/DummyPurchaseSeeder.php # 3. 시딩 실행 cd api php artisan db:seed --class=DummyDataSeeder # 4. 검증 php artisan tinker >>> \App\Models\Tenants\Sale::where('tenant_id', 287)->count() # 결과: 80 ``` ### 8.2 주요 참조 정보 | 항목 | 값 | |------|-----| | **대상 테넌트 ID** | 287 | | **생성자 사용자 ID** | 1 | | **데이터 기간** | 2025년 1월 ~ 12월 | | **총 레코드 수** | ~300개 | ### 8.3 관련 문서 - [React Mock to API Migration Plan](./react-mock-to-api-migration-plan.md) - API 마이그레이션 계획 - [API Rules](../reference/api-rules.md) - API 개발 규칙