diff --git a/backend/doc/FRONTEND_IMPLEMENTATION_GUIDE.md b/backend/doc/FRONTEND_IMPLEMENTATION_GUIDE.md deleted file mode 100644 index c42d518..0000000 --- a/backend/doc/FRONTEND_IMPLEMENTATION_GUIDE.md +++ /dev/null @@ -1,2521 +0,0 @@ -# 유전능력 컨설팅 서비스 프론트엔드 구현 가이드 - -**작성일**: 2025-10-26 -**버전**: 1.0 -**목적**: 백엔드 API 완전 활용한 완벽한 프론트엔드 구현 - ---- - -## 목차 - -1. [개요](#1-개요) -2. [기술 스택](#2-기술-스택) -3. [프로젝트 구조](#3-프로젝트-구조) -4. [백엔드 API 매핑](#4-백엔드-api-매핑) -5. [페이지별 구현 가이드](#5-페이지별-구현-가이드) -6. [컴포넌트 설계](#6-컴포넌트-설계) -7. [상태 관리](#7-상태-관리) -8. [API 클라이언트](#8-api-클라이언트) -9. [개발 우선순위](#9-개발-우선순위) -10. [테스트 가이드](#10-테스트-가이드) - ---- - -## 1. 개요 - -### 1.1 프로젝트 소개 - -한우 유전능력 컨설팅 서비스의 프론트엔드 애플리케이션으로, 농장주가 개체별 유전능력, 유전자 정보를 확인하고 최적의 KPN(종모우)을 추천받아 교배 계획을 수립할 수 있는 웹 애플리케이션입니다. - -### 1.2 주요 기능 - -- **농장 대시보드**: 농장 종합 현황, 분석연도별 비교, 등급 분포 -- **개체 관리**: 개체 목록 조회, 상세 정보, 유전자/유전능력 분석 -- **KPN 추천 시스템**: - - 암소→KPN 추천 (4단계 알고리즘) - - KPN→암소 추천 (역방향) - - 대립유전자 시뮬레이션 - - 근친도 계산 -- **교배 조합 관리**: 교배 조합 저장, 수정, 삭제, 조회 -- **다세대 시뮬레이션**: 1~20세대 교배 시뮬레이션, KPN 순환 전략 -- **농장 패키지 추천**: 농장 전체 최적 KPN 패키지 추천 - -### 1.3 PRD 참조 - -- 기능 요구사항: `E:\repo5\prd\기능요구사항14.md` -- 백엔드 태스크: `E:\repo5\prd\backend-tasklist.md` -- 백엔드 상세: `E:\repo5\prd\backend-detailed-tasks.md` -- DB 스키마: `E:\repo5\prd\GENE_TABLE_SPEC.md` - ---- - -## 2. 기술 스택 - -### 2.1 프레임워크 및 라이브러리 - -| 카테고리 | 기술 | 버전 | 용도 | -|---------|------|------|------| -| **프레임워크** | Next.js | 14+ | React 기반 풀스택 프레임워크 | -| **언어** | TypeScript | 5+ | 타입 안전성 | -| **UI 라이브러리** | shadcn/ui | latest | 재사용 가능한 컴포넌트 | -| **스타일링** | Tailwind CSS | 3+ | 유틸리티 기반 CSS | -| **상태 관리** | Zustand | 4+ | 경량 상태 관리 | -| **API 클라이언트** | Axios | 1+ | HTTP 클라이언트 | -| **차트** | Recharts | 2+ | 데이터 시각화 | -| **폼 관리** | React Hook Form | 7+ | 폼 상태 관리 | -| **유효성 검사** | Zod | 3+ | 스키마 기반 검증 | - -### 2.2 개발 환경 - -- **Node.js**: 18+ -- **패키지 매니저**: npm or yarn -- **IDE**: VSCode (권장) -- **브라우저**: Chrome, Edge (최신 버전) - ---- - -## 3. 프로젝트 구조 - -``` -frontend/ -├── src/ -│ ├── app/ # Next.js App Router -│ │ ├── (auth)/ # 인증 관련 라우트 -│ │ │ ├── login/ -│ │ │ ├── signup/ -│ │ │ ├── findid/ -│ │ │ └── findpw/ -│ │ ├── dashboard/ # 대시보드 -│ │ │ ├── page.tsx -│ │ │ └── cows/ # 개체 관리 서브 페이지 -│ │ │ ├── list/ -│ │ │ ├── excellent/ -│ │ │ └── cull/ -│ │ ├── cow/ # 개체 상세 -│ │ │ ├── page.tsx -│ │ │ ├── [cowNo]/ -│ │ │ │ ├── page.tsx -│ │ │ │ └── genes/ -│ │ │ │ ├── [category]/ -│ │ │ │ │ └── page.tsx -│ │ ├── kpn/ # KPN 관리 -│ │ │ ├── page.tsx -│ │ │ ├── [kpnNo]/ -│ │ │ │ └── page.tsx -│ │ │ ├── recommend/ -│ │ │ │ └── page.tsx -│ │ │ └── package/ -│ │ │ └── page.tsx -│ │ ├── breeding/ # 교배 관리 -│ │ │ ├── page.tsx -│ │ │ ├── saved/ -│ │ │ │ └── page.tsx -│ │ │ └── simulator/ -│ │ │ └── page.tsx -│ │ └── layout.tsx -│ │ -│ ├── components/ # 재사용 컴포넌트 -│ │ ├── ui/ # shadcn/ui 기본 컴포넌트 -│ │ ├── dashboard/ # 대시보드 전용 컴포넌트 -│ │ │ ├── section-cards.tsx -│ │ │ ├── year-comparison.tsx -│ │ │ ├── grade-distribution.tsx -│ │ │ └── gene-status.tsx -│ │ ├── cow/ # 개체 관련 컴포넌트 -│ │ │ ├── cow-list-table.tsx -│ │ │ ├── cow-detail-card.tsx -│ │ │ └── cow-ranking-table.tsx -│ │ ├── kpn/ # KPN 관련 컴포넌트 -│ │ │ ├── kpn-recommendation-card.tsx -│ │ │ ├── kpn-detail-modal.tsx -│ │ │ └── allele-simulation-chart.tsx -│ │ ├── breeding/ # 교배 관련 컴포넌트 -│ │ │ ├── breeding-combination-card.tsx -│ │ │ └── multi-generation-timeline.tsx -│ │ ├── shared/ # 공통 컴포넌트 -│ │ │ ├── app-sidebar.tsx -│ │ │ ├── site-header.tsx -│ │ │ ├── filter-panel.tsx -│ │ │ └── pagination.tsx -│ │ └── charts/ # 차트 컴포넌트 -│ │ ├── bar-chart.tsx -│ │ ├── line-chart.tsx -│ │ └── radar-chart.tsx -│ │ -│ ├── lib/ # 유틸리티 및 헬퍼 -│ │ ├── api/ # API 클라이언트 -│ │ │ ├── client.ts # Axios 설정 -│ │ │ ├── cow.api.ts -│ │ │ ├── kpn.api.ts -│ │ │ ├── dashboard.api.ts -│ │ │ ├── breed.api.ts -│ │ │ ├── gene.api.ts -│ │ │ ├── genome.api.ts -│ │ │ └── index.ts -│ │ ├── utils.ts # 공통 유틸 -│ │ └── constants.ts # 상수 -│ │ -│ ├── types/ # TypeScript 타입 정의 -│ │ ├── cow.types.ts -│ │ ├── kpn.types.ts -│ │ ├── gene.types.ts -│ │ ├── genome.types.ts -│ │ ├── breeding.types.ts -│ │ ├── dashboard.types.ts -│ │ └── api.types.ts -│ │ -│ ├── store/ # Zustand 스토어 -│ │ ├── auth-store.ts -│ │ ├── filter-store.ts -│ │ └── farm-store.ts -│ │ -│ ├── contexts/ # React Context -│ │ ├── GlobalFilterContext.tsx -│ │ └── AnalysisYearContext.tsx -│ │ -│ └── hooks/ # Custom Hooks -│ ├── use-cow-data.ts -│ ├── use-kpn-recommendation.ts -│ └── use-pagination.ts -│ -├── public/ # 정적 파일 -└── package.json -``` - ---- - -## 4. 백엔드 API 매핑 - -### 4.1 API 베이스 URL - -- **개발**: `http://localhost:4000` -- **프로덕션**: `https://api.example.com` - -### 4.2 인증 - -모든 API 요청은 JWT 토큰을 포함해야 합니다: - -```typescript -headers: { - Authorization: `Bearer ${token}` -} -``` - -### 4.3 API 엔드포인트 전체 목록 - -#### 4.3.1 인증 (`/auth`) - -| 메서드 | 엔드포인트 | 설명 | 요청 | 응답 | -|--------|-----------|------|------|------| -| POST | `/auth/login` | 로그인 | `{ userId, password }` | `{ accessToken, user }` | -| POST | `/auth/signup` | 회원가입 | `CreateUserDto` | `{ success: true }` | -| POST | `/auth/find-id` | 아이디 찾기 | `{ name, phone }` | `{ userId }` | -| POST | `/auth/reset-password` | 비밀번호 재설정 | `{ userId, phone, newPassword }` | `{ success: true }` | - -#### 4.3.2 개체 관리 (`/cow`) - -| 메서드 | 엔드포인트 | 설명 | PRD 참조 | -|--------|-----------|------|----------| -| GET | `/cow` | 전체 개체 목록 | SFR-COW-001 | -| GET | `/cow/paginated?page=1&limit=10` | 페이징 목록 | SFR-COW-001 | -| GET | `/cow/farm/:farmNo` | 농장별 개체 목록 | - | -| GET | `/cow/search?keyword=...&farmNo=...` | 개체 검색 | SFR-COW-002 | -| GET | `/cow/:cowNo` | 개체 상세 정보 | SFR-COW-004 | -| POST | `/cow/ranking` | 개체 랭킹 (필터+정렬) | SFR-COW-003 | -| POST | `/cow/:cowNo/recommendations` | KPN 추천 | SFR-COW-016 | -| POST | `/cow/:cowNo/recommendations/:kpnNo/next-generation` | 다음 세대 KPN 추천 | - | -| POST | `/cow/simulate-breeding` | 교배 시뮬레이션 | SFR-COW-015 | -| POST | `/cow/simulate-multi-generation` | 다세대 시뮬레이션 | SFR-COW-038, 039 | -| POST | `/cow/farm-package-recommendation` | 농장 패키지 추천 | SFR-COW-037 | -| POST | `/cow/rotation-strategy` | KPN 순환 전략 | SFR-COW-038 | - -**POST /cow/ranking 요청 예시**: -```typescript -{ - "filterOptions": { - "filters": [ - { "field": "grade", "operator": "in", "value": ["A", "B"] } - ], - "sorts": [ - { "field": "overallScore", "direction": "DESC" } - ], - "pagination": { "page": 1, "limit": 10 } - }, - "rankingOptions": { - "criteriaType": "GENOME", - "traitConditions": [ - { "traitNm": "근내지방도", "weight": 1.0 } - ], - "limit": 100 - } -} -``` - -**POST /cow/:cowNo/recommendations 요청 예시**: -```typescript -{ - "targetGenes": ["PLAG1", "CAPN1", "FASN"], - "inbreedingThreshold": 12.5, - "limit": 10 -} -``` - -**응답 예시**: -```typescript -{ - "cow": { - "cowNo": "KOR002108023350", - "genes": [ - { "markerNm": "PLAG1", "genotype": "AG" } - ] - }, - "recommendations": [ - { - "kpn": { "kpnNo": "KPN1385", "kpnNm": "..." }, - "rank": 1, - "matchingScore": 90, - "inbreeding": { - "generation1": 6.25, - "generation2": 3.125, - "riskLevel": "normal" - }, - "recommendationReason": "PLAG1 AA 보유로 자손 50% AA 고정 가능", - "geneMatching": [ - { - "markerNm": "PLAG1", - "cowGenotype": "AG", - "kpnGenotype": "AA", - "offspringProbability": { "AA": 0.5, "AG": 0.5, "GG": 0.0 }, - "favorableProbability": 0.5 - } - ], - "isOwned": true, - "isSaved": false - } - ], - "totalCount": 10 -} -``` - -#### 4.3.3 KPN 관리 (`/kpn`) - -| 메서드 | 엔드포인트 | 설명 | PRD 참조 | -|--------|-----------|------|----------| -| GET | `/kpn/search?keyword=...` | KPN 검색 | - | -| GET | `/kpn/owned` | 보유 KPN 목록 | SFR-COW-026 | -| GET | `/kpn/owned/check/:kpnNo` | 보유 여부 확인 | SFR-COW-028 | -| POST | `/kpn/ranking` | KPN 랭킹 | - | -| POST | `/kpn/:kpnNo/recommendations` | KPN→암소 역추천 | SFR-COW-016-4 | -| POST | `/kpn/owned` | 보유 KPN 등록 | SFR-COW-025 | -| PATCH | `/kpn/owned/:id` | 보유 KPN 수정 | - | -| DELETE | `/kpn/owned/:id` | 보유 KPN 삭제 | SFR-COW-027 | - -#### 4.3.4 대시보드 (`/dashboard`) - -| 메서드 | 엔드포인트 | 설명 | PRD 참조 | -|--------|-----------|------|----------| -| GET | `/dashboard/summary/:farmNo` | 농장 요약 통계 | SFR-HOME-001 | -| GET | `/dashboard/analysis-completion/:farmNo` | 분석 완료 현황 | - | -| GET | `/dashboard/evaluation/:farmNo` | 농장 종합 평가 | SFR-FARM-002 | -| GET | `/dashboard/region-comparison/:farmNo` | 보은군 비교 | SFR-FARM-003 | -| GET | `/dashboard/cow-distribution/:farmNo` | 등급별 분포 | - | -| GET | `/dashboard/kpn-aggregation/:farmNo` | KPN 추천 집계 | SFR-HOME-009 | -| GET | `/dashboard/farm-kpn-inventory/:farmNo` | KPN 재고 현황 | SFR-COW-024 | -| GET | `/dashboard/analysis-years/:farmNo` | 분석연도 목록 | SFR-HOME-002 | -| GET | `/dashboard/analysis-years/:farmNo/latest` | 최신 분석연도 | - | -| GET | `/dashboard/year-comparison/:farmNo` | 3개년 비교 | SFR-HOME-011 | -| GET | `/dashboard/gene-status/:farmNo` | 유전자 보유 현황 | - | -| GET | `/dashboard/repro-efficiency/:farmNo` | 번식 효율 분석 | - | -| GET | `/dashboard/excellent-cows/:farmNo?limit=10` | 우수 개체 추천 | SFR-HOME-007 | -| GET | `/dashboard/cull-cows/:farmNo` | 도태 후보 추천 | SFR-HOME-008 | - -**응답 예시 - /dashboard/summary/:farmNo**: -```typescript -{ - "totalCows": 50, - "analysisSummary": { - "geneAnalyzed": 45, - "genomeAnalyzed": 48, - "fertilityAnalyzed": 40 - }, - "breedingTypeDistribution": { - "AI": 30, - "Donor": 10, - "Recipient": 8, - "Cull": 2 - }, - "cowStatusDistribution": { - "Active": 45, - "Sold": 3, - "Dead": 2 - } -} -``` - -**응답 예시 - /dashboard/year-comparison/:farmNo**: -```typescript -{ - "years": [2024, 2023, 2022], - "compositeScores": [82.5, 80.0, 78.5], - "traitTrends": { - "coldCarcassWeight": [78, 76, 75], - "marbling": [85, 83, 80] - }, - "genePossessionTrends": { - "meatQuality": [65.0, 62.0, 60.0], - "meatQuantity": [68.0, 66.0, 65.0] - }, - "trendAnalysis": { - "overall": "improvement", - "change": "+4.0", - "insights": "최근 3년간 지속적 개선 중" - } -} -``` - -#### 4.3.5 교배 조합 (`/breed`) - -| 메서드 | 엔드포인트 | 설명 | PRD 참조 | -|--------|-----------|------|----------| -| GET | `/breed` | 교배 조합 목록 | SFR-COW-018 | -| GET | `/breed/search?keyword=...` | 교배 조합 검색 | - | -| GET | `/breed/:id` | 교배 조합 상세 | - | -| GET | `/breed/cow/:cowNo` | 암소별 교배 이력 | SFR-COW-020 | -| GET | `/breed/kpn/:kpnNo` | KPN별 사용 이력 | SFR-COW-021 | -| GET | `/breed/user/:userNo` | 사용자별 조회 | - | -| POST | `/breed` | 교배 조합 저장 | SFR-COW-017 | -| PATCH | `/breed/:id` | 교배 조합 수정 | - | -| DELETE | `/breed/:id` | 교배 조합 삭제 | SFR-COW-019 | - -**POST /breed 요청 예시**: -```typescript -{ - "fkUserNo": 1, - "fkCowNo": "002012345678", - "fkKpnNo": "KPN1385", - "saveMemo": "육질 개선을 위한 교배 조합" -} -``` - -#### 4.3.6 농장 관리 (`/farm`) - -| 메서드 | 엔드포인트 | 설명 | -|--------|-----------|------| -| GET | `/farm` | 현재 사용자 농장 목록 | -| GET | `/farm/:id` | 농장 상세 정보 | -| GET | `/farm/:farmNo/kpn` | 농장 보유 KPN 목록 | -| GET | `/farm/:farmNo/kpn/:kpnNo/check` | KPN 보유 여부 확인 | -| POST | `/farm/:farmNo/kpn` | 보유 KPN 등록 | -| DELETE | `/farm/:farmNo/kpn/:kpnNo` | 보유 KPN 삭제 | - -#### 4.3.7 유전자 (`/gene`) - -| 메서드 | 엔드포인트 | 설명 | -|--------|-----------|------| -| GET | `/gene` | 전체 유전자 목록 | -| GET | `/gene/major` | 주요 유전자 20개 | -| GET | `/gene/types` | 유전자 타입 (QTY/QLT) | -| GET | `/gene/type/:typeCd` | 타입별 유전자 | -| GET | `/gene/search?keyword=...` | 유전자 검색 | -| GET | `/gene/paginated` | 페이징 검색 (가상 스크롤) | -| GET | `/gene/marker/:markerName/cows` | 특정 유전자 보유 개체 | -| GET | `/gene/cows/multi?genes=PLAG1,CAPN1` | 다중 유전자 보유 개체 (AND) | -| GET | `/gene/cow-profile/:cowNo` | 개체 유전자 프로필 | -| GET | `/gene/:cowNo` | 개체 SNP 데이터 | - -#### 4.3.8 유전체 (`/genome`) - -| 메서드 | 엔드포인트 | 설명 | -|--------|-----------|------| -| GET | `/genome` | 전체 유전체 데이터 | -| GET | `/genome/:cowNo` | 개체 유전체 데이터 | - ---- - -## 5. 페이지별 구현 가이드 - -### 5.1 대시보드 (`/dashboard`) - -#### 5.1.1 페이지 개요 - -**목적**: 농장 전체 현황을 한눈에 파악 -**PRD 참조**: SFR-HOME-001~011, SFR-FARM-001~004 - -#### 5.1.2 레이아웃 - -``` -┌─────────────────────────────────────────────────────┐ -│ 사이드바 │ 헤더 (분석연도 선택, 전역 필터) │ -├─────────────────────────────────────────────────────┤ -│ │ 전역 필터 적용 현황 카드 │ -│ ├─────────────────────────────┤ -│ │ 기본 정보 카드 (4개) │ -│ 네비게이션 │ - 전체 개체 수 │ -│ - 대시보드 │ - 분석 완료율 │ -│ - 개체 관리 │ - 평균 유전능력 점수 │ -│ - KPN 추천 │ - 농장 종합 등급 │ -│ - 교배 관리 ├─────────────────────────────┤ -│ - 설정 │ 농장 현황 섹션 │ -│ │ ├─ 연도별 비교 차트 │ -│ │ └─ 등급별 분포 차트 │ -│ ├─────────────────────────────┤ -│ │ 유전자 보유 현황 │ -│ │ (주요 마커별 보유율 레이더 차트) │ -│ ├─────────────────────────────┤ -│ │ 주요 개체 및 추천 │ -│ │ ├─ 우수 개체 TOP 3 │ -│ │ ├─ 도태 후보 추천 │ -│ │ └─ 추천 KPN TOP 3 │ -└─────────────────────────────────────────────────────┘ -``` - -#### 5.1.3 컴포넌트 구성 - -**1. SectionCards (기본 정보 카드)** - -API: `GET /dashboard/summary/:farmNo` - -```tsx -// src/components/dashboard/section-cards.tsx -export function SectionCards({ farmNo }: { farmNo: number }) { - const { data, isLoading } = useDashboardSummary(farmNo) - - return ( -
{data?.totalCows || 0}두
-- {Math.round((data?.analysisSummary.geneAnalyzed / data?.totalCows) * 100)}% -
- -{cow.cowNo}
-- {cow.grade}등급 | {cow.overallScore}점 -
-{cow.cowNo}
-{cow.cullReason}
-{kpn.kpnNo}
-- {kpn.recommendedCount}두 추천 | 평균 {kpn.avgMatchingScore}점 -
-{cow.pkCowNo}
-- {cow.grade}등급 | {cow.cowSex === 'F' ? '암소' : '수소'} -
-{selectedCow.pkCowNo}
-- {selectedCow.grade}등급 | 생년월일: {selectedCow.cowBirthDt} -
-- {targetGenes.length}개 선택됨 -
-- {inbreedingThreshold < 10 ? '엄격' : inbreedingThreshold < 15 ? '보통' : '관대'} -
-- 교배 이력으로 제외된 KPN ({recommendations.excludedKpns.count}개) -
- {recommendations.excludedKpns.list.map((excluded) => ( -{rec.kpn.kpnNo}
-{rec.kpn.kpnNm}
-매칭점수
-{rec.matchingScore}점
-1세대 근친도
-- {rec.inbreeding.generation1}% -
-2세대
-{rec.inbreeding.generation2}%
-3세대
-{rec.inbreeding.generation3}%
-추천 이유
-{rec.recommendationReason}
-전략: {rec.strategy}
-{match.markerNm}
-암소 유전자형
-{match.cowGenotype}
-KPN 유전자형
-{match.kpnGenotype}
-자손 유전자형 확률
-{genotype}
- -{Math.round(prob * 100)}%
-{match.improvementReason}
- )} -KPN 번호
-{kpn?.basicInfo.kpnNumber}
-출생지
-{kpn?.basicInfo.origin}
-생년월일
-{kpn?.basicInfo.birthDate}
-모색
-{kpn?.basicInfo.coatColor}
-종합점수
-{kpn?.geneticAbility.compositeScore}
-등급
-종합 요약
-- 육질형: {kpn.geneMatchingSimulation.overallSummary.meatQuality} -
-- 육량형: {kpn.geneMatchingSimulation.overallSummary.meatQuantity} -
-{result.rotationInsights.rotationStartGeneration}세대
-{result.rotationInsights.rotationCycle}
-{result.rotationInsights.inbreedingReductionPrinciple}
-경고
- {result.warnings.map((warning, idx) => ( -{warning}
- ))} -{kpn.kpnNumber}
-추천 두수
-{kpn.recommendedCount}두
-평균 매칭점수
-{kpn.avgMatchingScore}점
-1세대 수요
-{kpn.generationDemand.generation1}개
-2세대 수요
-{kpn.generationDemand.generation2}개
-- 적용 가능 암소 ({kpn.applicableCows.length}두) -
-보유 중인 KPN
-구매 필요 KPN
-구매 가이드
-{result.ownedComparison.purchaseGuide}
-육질형 개선 두수
-- +{result.packageEffect.meatQualityImprovement}두 -
-육량형 개선 두수
-- +{result.packageEffect.meatQuantityImprovement}두 -
-평균 등급 상승
-- {result.packageEffect.expectedGradeIncrease} -
-{kpn.origin}
-- {kpn.recommendationReason} -
-| KPN 번호 | -수량 | -등록일 | -메모 | -작업 | -
|---|---|---|---|---|
| {kpn.kpnNo} | -{kpn.quantity} | -{kpn.registeredAt} | -{kpn.memo} | -- - - | -
적용 가능: {item.applicableCowCount}마리 ({item.coverage}%)
- - {/* 주요 유전자 */} -세대별 스트로 수요
-총 {totalCount}마리 추천
-농장: {cow.cowInfo.farmNo}
-생년월일: {cow.cowInfo.birthDate}
- - {/* 매칭 정보 */} -{cow.recommendationReason}
- - {/* 액션 */} - -| 순위 | -KPN 번호 | -매칭 점수 | -근친도 | -추천 이유 | -작업 | -
|---|---|---|---|---|---|
| #{kpn.rank} | -{kpn.kpnNumber} | -{kpn.matchingScore}% | -{kpn.inbreeding1}% | -{kpn.recommendationReason} | -- - - | -
개체번호: {cow.pkCowNo}
-성별: {cow.cowSex === 'M' ? '수소' : '암소'}
-농장번호: {cow.fkFarmNo}
-성별: {cow.cowSex === 'M' ? '수소' : '암소'}
-생년월일: {cow.cowBirthDt ? new Date(cow.cowBirthDt).toLocaleDateString() : '-'}
-총 농장 수: {stats.totalFarms}개
-총 개체 수: {stats.totalCows}마리
- {stats.farms.map(farm => ( -{farm.farmName}: {farm.cowCount}마리
-등록된 농장이 없습니다.
- -{error.message}
- -원하는 스타일을 선택해보세요. 우하단에 실제 버튼이 표시됩니다.
+ + {/* 아이콘 선택 */} +선택한 스타일 코드:
+
+ {`className="${styles.find(s => s.id === selectedStyle)?.className}"`}
+
+ 아래로 스크롤해서 버튼 동작을 확인하세요.
+ {Array.from({ length: 20 }).map((_, i) => ( ++ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +
+