# KPN 추천 시스템 UX 상세 설계서
> 참조 프로젝트 기반 UX 구현 가이드
> 작성일: 2025-01-XX
> 기준: KPN Recommendation System 참조 프로젝트
---
## 📋 목차
1. [UX 설계 철학](#1-ux-설계-철학)
2. [메뉴 구조](#2-메뉴-구조)
3. [페이지별 상세 설계](#3-페이지별-상세-설계)
4. [네비게이션 플로우](#4-네비게이션-플로우)
5. [API 연동 명세](#5-api-연동-명세)
6. [컴포넌트 재사용 전략](#6-컴포넌트-재사용-전략)
7. [모바일 반응형 가이드](#7-모바일-반응형-가이드)
---
## 1. UX 설계 철학
### 1.1 핵심 원칙
#### **양방향 탐색 지원**
- **KPN → 소**: "이 KPN을 누구한테 쓸 수 있지?"
- **소 → KPN**: "이 소한테 뭘 써야 하지?"
- **농장 전체**: "우리 농장엔 어떤 KPN이 필요하지?"
#### **3가지 독립적 진입점**
```
사용자 니즈별 진입점:
├── KPN 중심 사고 → KPN 관리 메뉴
├── 소 중심 사고 → 내 소보기 메뉴
└── 전략적 사고 → 홈 또는 KPN 관리 > 농장 추천
```
#### **정보 아키텍처**
```
일관된 3-패널 레이아웃 (Desktop):
┌────────┬──────────────┬──────────────┐
│사이드바 │ 메인 콘텐츠 │ 상세 패널 │
│(고정) │ (목록/그리드)│ (슬라이드) │
└────────┴──────────────┴──────────────┘
```
---
## 2. 메뉴 구조
### 2.1 사이드바 메뉴 (최종안)
```typescript
interface MenuItem {
label: string;
href: string;
icon: React.ReactNode;
badge?: string; // 알림 배지
}
const menuItems: MenuItem[] = [
{
label: "홈",
href: "/home",
icon:
},
{
label: "내 소보기",
href: "/cow",
icon:
},
{
label: "KPN 관리", // ⭐ 추가
href: "/kpn",
icon:
},
{
label: "교배 계획", // ⭐ 추가 (선택사항)
href: "/breeding",
icon:
}
]
```
### 2.2 PRD vs 최종 메뉴 비교
| 구분 | PRD (기능요구사항20.md) | 최종 구현안 | 사유 |
|------|----------------------|-----------|------|
| 메뉴 개수 | 2개 (홈, 내 소보기) | 4개 (홈, 내 소보기, KPN 관리, 교배 계획) | 사용성 개선 |
| KPN 접근 | 홈 섹션 / 소 상세 서브 | 독립 메뉴 | 양방향 탐색 지원 |
| 구매 계획 | 홈 섹션 | KPN 관리 > 농장 추천 | 전용 페이지로 분리 |
| 교배 이력 | 미정의 | 독립 메뉴 (선택사항) | 저장된 계획 관리 |
---
## 3. 페이지별 상세 설계
### 3.1 KPN 목록 페이지 (`/kpn`)
#### **페이지 목적**
- 전체 KPN 한눈에 조망
- 보유/미보유 KPN 필터링
- KPN 상세 정보 및 적합한 소 확인
#### **핵심 기능**
##### 1) 필터 상태 (3가지)
```typescript
type FilterStatus = 'all' | 'owned' | 'needed';
// 전체: 모든 KPN
// 보유: 농가가 보유 중인 KPN (초록 배지)
// 필요: 구매가 필요한 KPN
```
##### 2) 정렬 옵션
```typescript
type SortBy = 'matching' | 'inbreeding';
// matching: 매칭률 (우량형확률) 높은 순
// inbreeding: 근친도 낮은 순
```
##### 3) 액션 버튼 (2개)
```typescript
```
#### **API 연동**
```typescript
// 1. KPN 목록 조회
const response = await fetch('/api/kpn/ranking', {
method: 'POST',
body: JSON.stringify({
filterOptions: {
filters: [
// 필터 조건 (선택사항)
]
},
rankingOptions: {
criteriaType: 'GENE',
order: 'DESC',
weights: {}
}
})
});
// 2. 보유 KPN 확인
const ownedKpns = await fetch('/api/kpn/owned');
// Response: { isOwned: boolean } 각 KPN별
```
#### **UI 구성**
```tsx
{/* 헤더 */}
{/* 통계 카드 */}
{/* 액션 버튼 */}
{/* 필터 & 정렬 */}
{/* KPN 그리드 */}
```
#### **KPN 카드 디자인**
```tsx
{/* 헤더 */}
#{rank}
{isOwned &&
보유}
{kpn.pkKpnNo}
{kpn.origin}
{/* 주요 유전자 */}
{kpn.genes.map(gene => (
{gene}
))}
{/* 통계 */}
{/* 추천 이유 */}
{kpn.recommendationReason}
```
---
### 3.2 KPN 보유 등록 페이지 (`/kpn/inventory`)
#### **페이지 목적**
- 농가가 보유한 KPN 등록 및 관리
- 보유 KPN 목록 조회
- 보유 수량 및 메모 관리
#### **핵심 기능**
##### 1) KPN 검색 및 등록
```typescript
// KPN 검색
const kpns = await fetch(`/api/kpn/search?keyword=${keyword}`);
// 보유 등록
await fetch('/api/kpn/owned', {
method: 'POST',
body: JSON.stringify({
kpnNo: 'KPN1385',
quantity: 5,
memo: '2024년 구매'
})
});
```
##### 2) 보유 KPN 목록
```typescript
const ownedList = await fetch('/api/kpn/owned');
// Response:
{
totalCount: 3,
ownedKpns: [
{
id: 1,
kpnNo: 'KPN1385',
quantity: 5,
memo: '2024년 구매',
registeredAt: '2024-03-15',
kpnInfo: { /* KPN 상세 정보 */ }
}
]
}
```
##### 3) 수정 및 삭제
```typescript
// 수정
await fetch(`/api/kpn/owned/${id}`, {
method: 'PATCH',
body: JSON.stringify({ quantity: 3, memo: '사용 중' })
});
// 삭제
await fetch(`/api/kpn/owned/${id}`, {
method: 'DELETE'
});
```
#### **UI 구성**
```tsx
{/* 헤더 */}
{/* 등록 폼 */}
새 KPN 등록
{/* 보유 KPN 목록 */}
보유 KPN 목록 ({ownedKpns.length}개)
| KPN 번호 |
수량 |
등록일 |
메모 |
작업 |
{ownedKpns.map(kpn => (
| {kpn.kpnNo} |
{kpn.quantity} |
{kpn.registeredAt} |
{kpn.memo} |
|
))}
```
---
### 3.3 농장 전체 KPN 추천 페이지 (`/kpn/farm-recommend`)
#### **페이지 목적**
- 농장 전체 암소 분석하여 최적 KPN 세트 추천
- 세대별 KPN 수요량 예측
- 장기 교배 전략 제공
#### **핵심 기능**
##### 1) 농장 전체 분석
```typescript
const result = await fetch('/api/cow/farm-package-recommendation', {
method: 'POST',
body: JSON.stringify({
farmNo: 1,
targetGenes: ['PLAG1', 'CAPN1', 'FASN'],
inbreedingThreshold: 10,
maxPackageSize: 5
})
});
// Response:
{
farmNo: 1,
totalCowCount: 65,
recommendedPackageCount: 5,
totalCoverage: 89.2,
package: [
{
kpnNo: 'KPN1385',
category: 'ESSENTIAL', // 또는 'RECOMMENDED'
applicableCowCount: 28,
coverage: 43.1,
averageMatchingScore: 85.2,
majorGenes: ['PLAG1 AA', 'CAPN1 CC', 'FASN GG'],
generationDemand: {
generation1: 28,
generation2: 14,
generation3: 7,
total: 49
},
isOwned: false
}
],
purchaseGuide: {
ownedKpns: ['KPN1234'],
recommendedToPurchase: ['KPN1385', 'KPN2471']
},
summary: {
essentialKpnCount: 3,
recommendedKpnCount: 2,
averageCoverage: 78.4,
totalDemand: 245
}
}
```
##### 2) 세대별 순환 전략
```typescript
const strategy = await fetch('/api/cow/rotation-strategy', {
method: 'POST',
body: JSON.stringify({
farmNo: 1,
targetGenes: ['PLAG1', 'CAPN1'],
inbreedingThreshold: 6.25,
maxPackageSize: 5,
generations: 20
})
});
// Response:
{
selectedKpns: ['KPN1385', 'KPN2471', ...],
rotationCycle: 4, // 4세대마다 순환
cowBreedingPlans: [
{
cowNo: 'KOR001',
generationPlans: [
{ generation: 1, recommendedKpn: 'KPN1385', inbreeding: 8.2 },
{ generation: 2, recommendedKpn: 'KPN2471', inbreeding: 4.1 },
{ generation: 3, recommendedKpn: 'KPN3692', inbreeding: 2.1 },
{ generation: 4, recommendedKpn: 'KPN4125', inbreeding: 1.0 },
{ generation: 5, recommendedKpn: 'KPN1385', inbreeding: 0.5 } // 순환
]
}
]
}
```
#### **UI 구성**
```tsx
{/* 헤더 */}
{/* 필터 설정 */}
분석 조건
{/* 추천 KPN 패키지 */}
추천 KPN 패키지 (5개)
{packageItems.map((item, index) => (
{/* 우선순위 배지 */}
{item.category === 'ESSENTIAL' ? '필수' : '권장'}
{/* KPN 정보 */}
#{index + 1} {item.kpnNo}
적용 가능: {item.applicableCowCount}마리 ({item.coverage}%)
{/* 주요 유전자 */}
{item.majorGenes.map(gene => (
{gene}
))}
{/* 세대별 수요량 */}
세대별 스트로 수요
1세대: {item.generationDemand.generation1}
2세대: {item.generationDemand.generation2}
3세대: {item.generationDemand.generation3}
합계: {item.generationDemand.total}
{/* 보유 상태 */}
{item.isOwned ? (
보유 중
) : (
구매 필요
)}
))}
{/* 구매 가이드 */}
구매 가이드
보유 중인 KPN ({ownedKpns.length}개)
{ownedKpns.map(kpn => (
- {kpn}
))}
구매 추천 KPN ({recommendedToPurchase.length}개)
{recommendedToPurchase.map(kpn => (
- {kpn}
))}
{/* 요약 통계 */}
요약
```
---
### 3.4 KPN → 소 추천 페이지 (`/kpn/[kpnNo]/recommend`)
#### **페이지 목적**
- 특정 KPN에 적합한 암소 목록 표시
- 암소별 매칭률 및 근친도 계산
- 암소 상세 정보 확인
#### **핵심 기능**
```typescript
// KPN → 소 추천 (역방향)
const result = await fetch(`/api/kpn/${kpnNo}/recommendations`, {
method: 'POST',
body: JSON.stringify({
targetGenes: ['PLAG1', 'CAPN1'],
inbreedingThreshold: 10,
limit: 20,
farmNo: 1 // 특정 농장만 (선택사항)
})
});
// Response:
{
kpnId: 'KPN1385',
targetGenes: ['PLAG1', 'CAPN1'],
inbreedingThreshold: 10,
recommendations: [
{
rank: 1,
cowId: 'KOR001',
cowNumber: 'KOR002108023350',
matchingScore: 85.2,
inbreeding1: 8.2,
inbreeding2: 4.1,
inbreeding3: 2.1,
riskLevel: 'normal',
strategy: 'COMPLEMENT',
recommendationReason: 'PLAG1, CAPN1 유전자 보완 가능, 근친도 낮음',
geneMatchingDetails: [...],
cowInfo: {
farmNo: 1,
birthDate: '2021-03-15',
sex: 'F'
}
}
],
totalCount: 28
}
```
#### **UI 구성**
```tsx
{/* KPN 정보 */}
{kpnNo} 적합 암소
총 {totalCount}마리 추천
{/* 필터 조건 */}
{/* 추천 암소 목록 */}
{recommendations.map(cow => (
{/* 순위 배지 */}
#{cow.rank}
{cow.rank <= 3 && }
{/* 소 기본 정보 */}
{cow.cowNumber}
농장: {cow.cowInfo.farmNo}
생년월일: {cow.cowInfo.birthDate}
{/* 매칭 정보 */}
{/* 추천 이유 */}
{cow.recommendationReason}
{/* 액션 */}
))}
```
---
### 3.5 소 → KPN 추천 페이지 (`/cow/[cowNo]/kpn/recommend`)
#### **페이지 목적**
- 특정 암소에 최적 KPN 추천
- 유전자 매칭 시뮬레이션
- 교배 계획 저장
#### **핵심 기능**
```typescript
// 소 → KPN 추천 (정방향)
const result = await fetch(`/api/cow/${cowNo}/recommendations`, {
method: 'POST',
body: JSON.stringify({
targetGenes: ['PLAG1', 'CAPN1'],
inbreedingThreshold: 10,
limit: 10
})
});
// Response:
{
cowId: 'KOR002108023350',
targetGenes: ['PLAG1', 'CAPN1'],
inbreedingThreshold: 10,
recommendations: [
{
rank: 1,
kpnId: 'KPN1385',
kpnNumber: 'KPN1385',
matchingScore: 92.5,
inbreeding1: 8.2,
inbreeding2: 4.1,
inbreeding3: 2.1,
riskLevel: 'normal',
strategy: 'COMPLEMENT',
recommendationReason: 'PLAG1, CAPN1 유전자 보완 가능',
geneMatchingDetails: [
{
geneName: 'PLAG1',
cowGenotype: 'AG',
kpnGenotype: 'AA',
offspringProbability: [
{ genotype: 'AA', probability: 50, isFavorable: true },
{ genotype: 'AG', probability: 50, isFavorable: true }
],
favorableProbability: 75,
improvementReason: '우량형 향상 가능 (목표: AA)'
}
]
}
],
totalCount: 15
}
```
#### **UI 구성**
```tsx
{/* 암소 정보 */}
{cowNo} 맞춤 KPN 추천
{/* TOP 3 추천 (메달) */}
{recommendations.slice(0, 3).map((kpn, index) => (
{kpn.kpnNumber}
매칭 {kpn.matchingScore}%
))}
{/* 전체 추천 KPN */}
전체 추천 KPN ({totalCount}개)
| 순위 |
KPN 번호 |
매칭 점수 |
근친도 |
추천 이유 |
작업 |
{recommendations.map(kpn => (
| #{kpn.rank} |
{kpn.kpnNumber} |
{kpn.matchingScore}% |
{kpn.inbreeding1}% |
{kpn.recommendationReason} |
|
))}
{/* 유전자 매칭 시뮬레이션 */}
유전자 매칭 시뮬레이션
{selectedKpn && (
)}
```
---
## 4. 네비게이션 플로우
### 4.1 전체 네비게이션 맵
```
홈 (/home)
├── 농장 현황
├── KPN 구매 계획 섹션 → [상세 분석] → /kpn/farm-recommend
└── 개체 관리 의사결정
내 소보기 (/cow)
├── 소 목록
└── 소 상세 (/cow/:cowNo)
├── 기본 정보
├── 유전능력
├── 보유 유전자
└── [KPN 추천받기] → /cow/:cowNo/kpn/recommend
KPN 관리 (/kpn)
├── KPN 목록
├── [KPN 보유 등록] → /kpn/inventory
├── [농장 추천] → /kpn/farm-recommend
└── KPN 클릭 → /kpn/:kpnNo/recommend
교배 계획 (/breeding)
└── 저장된 교배 조합 목록
```
### 4.2 사용자 시나리오별 플로우
#### **시나리오 1: KPN 구매 계획 수립**
```
1. 사이드바 "KPN 관리" 클릭
2. [농장 추천] 버튼 클릭
3. /kpn/farm-recommend 진입
4. 5개 KPN 패키지 확인
5. 구매할 KPN 선택
6. [KPN 보유 등록] 버튼 (페이지 내 링크)
7. /kpn/inventory 진입
8. KPN 등록 완료
```
#### **시나리오 2: 특정 소에 KPN 추천**
```
1. 사이드바 "내 소보기" 클릭
2. /cow 페이지에서 소 목록 확인
3. 001번 소 클릭 → /cow/KOR001
4. [KPN 추천받기] 버튼 클릭
5. /cow/KOR001/kpn/recommend 진입
6. TOP 3 KPN 확인
7. KPN1385 [상세 보기] 클릭 → 모달 또는 /kpn/KPN1385
8. [저장] 클릭 → 교배 계획 저장
```
#### **시나리오 3: 특정 KPN에 맞는 소 찾기**
```
1. 사이드바 "KPN 관리" 클릭
2. /kpn 페이지에서 KPN 목록 확인
3. KPN1385 카드 클릭
4. /kpn/KPN1385/recommend 진입
5. 적합한 암소 28마리 목록 확인
6. 001번 소 클릭 → 소 상세 정보 (모달 또는 /cow/KOR001)
```
---
## 5. API 연동 명세
### 5.1 API 엔드포인트 매핑
| 페이지 | API 엔드포인트 | Method | 용도 |
|--------|--------------|--------|------|
| `/kpn` | `/api/kpn/ranking` | POST | KPN 목록 + 필터 |
| `/kpn` | `/api/kpn/owned` | GET | 보유 KPN 확인 |
| `/kpn/inventory` | `/api/kpn/owned` | POST | KPN 보유 등록 |
| `/kpn/inventory` | `/api/kpn/owned` | GET | 보유 목록 조회 |
| `/kpn/inventory` | `/api/kpn/owned/:id` | PATCH | 보유 정보 수정 |
| `/kpn/inventory` | `/api/kpn/owned/:id` | DELETE | 보유 삭제 |
| `/kpn/farm-recommend` | `/api/cow/farm-package-recommendation` | POST | 농장 전체 KPN 추천 |
| `/kpn/farm-recommend` | `/api/cow/rotation-strategy` | POST | 세대별 순환 전략 (선택) |
| `/kpn/:kpnNo/recommend` | `/api/kpn/:kpnNo/recommendations` | POST | KPN → 소 추천 |
| `/cow/:cowNo/kpn/recommend` | `/api/cow/:cowNo/recommendations` | POST | 소 → KPN 추천 |
### 5.2 공통 Request/Response 형식
#### **Global Filter 구조**
```typescript
interface GlobalFilterOptions {
viewMode: 'QUANTITY' | 'QUALITY';
analysisIndex: 'GENE' | 'ABILITY';
selectedGenes: string[];
selectedTraits: string[];
inbreedingThreshold: number;
isActive: boolean;
}
```
#### **공통 Response 구조**
```typescript
interface BaseRecommendationResponse {
targetGenes: string[];
inbreedingThreshold: number;
totalCount: number;
recommendations: Array<{
rank: number;
matchingScore: number;
inbreeding1: number;
inbreeding2: number;
inbreeding3: number;
riskLevel: 'normal' | 'low' | 'high' | 'very_high';
strategy: 'COMPLEMENT' | 'STRENGTHEN' | 'BALANCED';
recommendationReason: string;
geneMatchingDetails: GeneMatchingDetail[];
}>;
}
```
---
## 6. 컴포넌트 재사용 전략
### 6.1 공통 컴포넌트
```typescript
// 1. 필터 바
// 2. 통계 그리드
// 3. KPN 카드
// 4. 암소 카드
// 5. 유전자 매칭 시뮬레이션
```
### 6.2 Layout 구조
```tsx
// App Layout (공통)
{children}
```
---
## 7. 모바일 반응형 가이드
### 7.1 브레이크포인트
```css
/* Tailwind 기준 */
sm: 640px /* 모바일 가로 */
md: 768px /* 태블릿 */
lg: 1024px /* 작은 데스크톱 */
xl: 1280px /* 데스크톱 */
2xl: 1536px /* 큰 데스크톱 */
```
### 7.2 모바일 우선 설계
```tsx
// 모바일: 전체 너비, 수직 스택
// 태블릿: 2열 그리드
// 데스크톱: 3열 그리드
{items.map(item => {item.content})}
```
### 7.3 터치 최적화
```tsx
// 버튼 최소 크기: 44x44px (iOS 가이드라인)
// Active 상태 피드백
{content}
```
---
## 8. 구현 우선순위
### Phase 1: 기반 구축 (1주)
- [x] UX 설계 문서 작성
- [ ] `/kpn/page.tsx` 개선 (필터 상태, 액션 버튼)
- [ ] 레이아웃 메뉴 추가 ("KPN 관리")
- [ ] API 연동 (`POST /kpn/ranking`, `GET /kpn/owned`)
### Phase 2: KPN 관리 기능 (1주)
- [ ] `/kpn/inventory/page.tsx` 생성
- [ ] `/kpn/farm-recommend/page.tsx` 생성
- [ ] API 연동 (보유 등록, 농장 추천)
### Phase 3: 양방향 추천 (1주)
- [ ] `/kpn/[kpnNo]/recommend/page.tsx` 생성
- [ ] `/cow/[cowNo]/kpn/recommend/page.tsx` 개선
- [ ] API 연동 (KPN→소, 소→KPN)
### Phase 4: 교배 계획 (선택사항)
- [ ] `/breeding/page.tsx` 생성
- [ ] 교배 조합 저장/관리 기능
---
## 9. 참조 자료
### 9.1 백엔드 API 문서
- `E:\repo5\repo5\next_nest_docker_template-main\backend\src\cow\cow.controller.ts`
- `E:\repo5\repo5\next_nest_docker_template-main\backend\src\kpn\kpn.controller.ts`
### 9.2 참조 프로젝트
- `E:\repo5\KPN Recommendation System\src\pages\KPNListPage.tsx`
- `E:\repo5\KPN Recommendation System\src\pages\FarmKPNRecommendPage.tsx`
- `E:\repo5\KPN Recommendation System\src\MOBILE_DESIGN_SPECS.md`
### 9.3 PRD 문서
- `E:\repo5\prd\기능요구사항20.md`
- `E:\repo5\prd\GENE_TABLE_SPEC.md`
---
**작성일**: 2025-01-XX
**최종 수정**: 2025-01-XX
**작성자**: Claude
**버전**: 1.0