Skip to content

Commit 431da2a

Browse files
authored
Merge branch 'develop' into feat/#13
2 parents 099a0c0 + 59bc004 commit 431da2a

32 files changed

Lines changed: 1274 additions & 13 deletions

.github/workflows/ci.yml

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ on:
66
pull_request:
77
branches: [ main, develop ]
88

9-
permissions:
10-
contents: read
11-
checks: write
12-
pull-requests: write
13-
149
jobs:
1510
test:
1611
runs-on: ubuntu-latest
@@ -59,7 +54,7 @@ jobs:
5954
run: chmod +x gradlew
6055

6156
- name: Run tests
62-
run: ./gradlew test
57+
run: ./gradlew test --stacktrace
6358
env:
6459
SPRING_PROFILES_ACTIVE: test
6560
DB_USERNAME: root
@@ -71,6 +66,21 @@ jobs:
7166
GOOGLE_CLIENT_SECRET: test
7267
NAVER_CLIENT_ID: test
7368
NAVER_CLIENT_SECRET: test
69+
GEMINI_API_KEY: test-gemini-key
70+
WEATHER_API_KEY: test-weather-key
71+
72+
- name: Upload test report
73+
uses: actions/upload-artifact@v4
74+
if: failure()
75+
with:
76+
name: test-report
77+
path: build/reports/tests/test/
78+
79+
- name: Publish test results
80+
uses: EnricoMi/publish-unit-test-result-action@v2
81+
if: always()
82+
with:
83+
files: build/test-results/test/**/*.xml
7484

7585
- name: Build
7686
run: ./gradlew build -x test
@@ -85,9 +95,5 @@ jobs:
8595
GOOGLE_CLIENT_SECRET: test
8696
NAVER_CLIENT_ID: test
8797
NAVER_CLIENT_SECRET: test
88-
89-
- name: Publish test results
90-
uses: EnricoMi/publish-unit-test-result-action@v2
91-
if: always()
92-
with:
93-
files: build/test-results/test/**/*.xml
98+
GEMINI_API_KEY: test-gemini-key
99+
WEATHER_API_KEY: test-weather-key

CategoryReadme.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# CategoryReadme
2+
3+
옷장난감 프로젝트 **공용 카테고리** 사용 가이드입니다.
4+
FE, BE, AI(Gemini) 모두 이 문서의 **code 값**을 기준으로 연동합니다.
5+
6+
---
7+
8+
## 1. 개요
9+
10+
| 구분 | 저장 위치 | 설명 |
11+
|------|-----------|------|
12+
| `category` | `clothes.category` | 대분류 (상의/하의/아우터/신발) |
13+
| `item_type` | `clothes.item_type` | 소분류 (반팔, 데님, 패딩 등) |
14+
| `color` | `clothes.color` | 컬러 코드 |
15+
| `styles` | `styles` 테이블 + `clothing_styles` | 스타일 코드 |
16+
17+
- **DB/API 저장값**: `code` (영문 enum name)
18+
- **화면 표시**: `name` (한글 label)
19+
- **컬러 UI**: `hex` (색상 원 표시용, DB 저장 ❌)
20+
21+
---
22+
23+
## 2. API
24+
25+
| Method | URL | 인증 | 설명 |
26+
|--------|-----|------|------|
27+
| GET | `/api/categories` | 불필요 | 전체 목록 + 사용 설명서 |
28+
| GET | `/api/categories/guide` | 불필요 | 사용 설명서만 |
29+
| GET | `/api/categories/ai-guide` | 불필요 | AI 분류용 텍스트 가이드 |
30+
31+
### 2.1 응답 구조 (`GET /api/categories`)
32+
33+
```json
34+
{
35+
"success": true,
36+
"data": {
37+
"guide": { ... },
38+
"categories": [ ... ],
39+
"styles": [ ... ],
40+
"colors": [ ... ]
41+
}
42+
}
43+
```
44+
45+
### 2.2 옷 등록 예시
46+
47+
```json
48+
{
49+
"category": "TOP",
50+
"item_type": "SHORT_SLEEVE",
51+
"color": "WHITE",
52+
"styles": ["CASUAL", "MINIMAL"]
53+
}
54+
```
55+
56+
### 2.3 컬러 표시 예시
57+
58+
```json
59+
{
60+
"code": "NAVY",
61+
"name": "네이비",
62+
"hex": "#1F3A5F"
63+
}
64+
```
65+
66+
---
67+
68+
## 3. 연동 규칙
69+
70+
1. **서버 저장 시 `code`만 사용** (`name`, `hex`는 저장하지 않음)
71+
2. **`item_type`은 선택한 `category` 하위 코드만** 사용 가능
72+
3. **컬러 UI**`hex`로 색 원(swatch) 표시, `name`은 tooltip/접근성용
73+
4. **`WHITE`** 색상 원은 밝은 배경에서 `border` 필요
74+
5. **카테고리 목록 변경 시** BE enum 수정 → 이 문서/API 동시 갱신
75+
76+
---
77+
78+
## 4. 대분류 (category)
79+
80+
| code | name |
81+
|------|------|
82+
| `TOP` | 상의 |
83+
| `BOTTOM` | 하의 |
84+
| `OUTER` | 아우터 |
85+
| `SHOES` | 신발 |
86+
87+
---
88+
89+
## 5. 소분류 (item_type)
90+
91+
### 5.1 TOP (상의)
92+
93+
| code | name | description |
94+
|------|------|-------------|
95+
| `LONG_SLEEVE` | 롱슬리브 | 긴소매 티셔츠 |
96+
| `SHORT_SLEEVE` | 반팔 | 반소매 티셔츠 |
97+
| `SHIRT` | 셔츠 | 셔츠 |
98+
| `HOODIE` | 후드 | 후드 |
99+
| `SWEAT` | 스웨트 | 스웨트 |
100+
| `COLLAR_TEE` | 카라 티셔츠 | 카라 티셔츠 |
101+
| `SLEEVELESS` | 민소매 | 민소매 |
102+
| `KNIT` | 니트 | 니트 |
103+
104+
### 5.2 BOTTOM (하의)
105+
106+
| code | name | description |
107+
|------|------|-------------|
108+
| `DENIM` | 데님 | 데님 |
109+
| `TRAINING` | 트레이닝 | 트레이닝 |
110+
| `COTTON` | 코튼 ||
111+
| `SLACKS` | 슬랙스 | 슬랙스 |
112+
| `SHORTS` | 숏츠 | 숏츠 |
113+
| `CARGO` | 카고 | 카고 |
114+
| `SKIRT` | 스커트 | 스커트 |
115+
116+
### 5.3 OUTER (아우터)
117+
118+
| code | name | description |
119+
|------|------|-------------|
120+
| `WINDBREAKER` | 윈드브레이커 | 바람막이 |
121+
| `HOOD_ZIPUP` | 후드집업 | 후드집업 |
122+
| `TRAINING_JACKET` | 트레이닝 자켓 | 트레이닝 자켓 |
123+
| `BLOUSON` | 블루종 | 블루종 |
124+
| `MA1` | MA-1 | MA-1 |
125+
| `VARSITY_JACKET` | 바시티 자켓 | 바시티 자켓 |
126+
| `LEATHER_JACKET` | 레더 자켓 | 가죽 |
127+
| `SHEARLING` | 무스탕 | 무스탕 |
128+
| `FLEECE_JACKET` | 플리스 자켓 | 후리스 |
129+
| `VEST` | 베스트 | 조끼 |
130+
| `WORK_JACKET` | 워크자켓 | 워크자켓 |
131+
| `DENIM_JACKET` | 데님자켓 | 데님자켓 |
132+
| `BLAZER` | 블레이저 | 블레이저 |
133+
| `COACH_JACKET` | 코치자켓 | 코치자켓 |
134+
| `PADDING` | 패딩 | 패딩 |
135+
| `LIGHT_PADDING` | 경량패딩 | 경량패딩 |
136+
| `SINGLE_COAT` | 싱글코트 | 싱글코트 |
137+
| `DOUBLE_COAT` | 더블코트 | 더블코트 |
138+
| `BALMACAAN_COAT` | 발마칸 코트 | 발마칸 코트 |
139+
| `TTEOKBOKKI_COAT` | 떡볶이 코트 | 떡볶이 코트 |
140+
141+
### 5.4 SHOES (신발)
142+
143+
| code | name | description |
144+
|------|------|-------------|
145+
| `SNEAKERS` | 스니커즈 | 스니커즈 |
146+
| `SPORTS_SHOES` | 스포츠화 | 운동화, 등산화 등 |
147+
| `LOAFER` | 로퍼 | 로퍼 |
148+
| `DERBY` | 더비 | 더비 |
149+
| `BOOTS` | 부츠 | 부츠 |
150+
| `SANDALS_SLIPPERS` | 샌들/슬리퍼 | 샌들/슬리퍼 |
151+
| `FLAT` | 플랫 | 플랫 |
152+
| `HEEL` |||
153+
154+
---
155+
156+
## 6. 스타일 (styles)
157+
158+
| code | name | description |
159+
|------|------|-------------|
160+
| `CASUAL` | 캐주얼 | 편안하고 일상적인 스타일 |
161+
| `STREET` | 스트릿 | 스트릿 패션 중심의 스타일 |
162+
| `MINIMAL` | 미니멀 | 단순하고 깔끔한 스타일 |
163+
| `SPORTY` | 스포티 | 스포츠웨어 기반의 활동적인 스타일 |
164+
| `CLASSIC` | 클래식 | 전통적이고 정돈된 스타일 |
165+
| `CHIC` | 시크 | 세련되고 도시적인 스타일 |
166+
| `WORKWEAR` | 워크웨어 | 작업복·유틸리티 중심의 스타일 |
167+
| `CITYBOY` | 시티보이 | 도심형 캐주얼 스타일 |
168+
| `GORPCORE` | 고프코어 | 아웃도어·기능성 중심의 스타일 |
169+
| `RETRO` | 레트로 | 복고풍을 연상시키는 스타일 |
170+
171+
> `styles` 테이블은 앱 기동 시 위 10개가 자동 시드됩니다.
172+
173+
---
174+
175+
## 7. 컬러 (colors)
176+
177+
| code | name | hex |
178+
|------|------|-----|
179+
| `PINK` | 핑크 | `#FFB6C1` |
180+
| `RED` | 레드 | `#E53935` |
181+
| `ORANGE` | 오렌지 | `#FF9800` |
182+
| `BEIGE` | 베이지 | `#D2B48C` |
183+
| `YELLOW` | 옐로우 | `#FDD835` |
184+
| `GREEN` | 그린 | `#43A047` |
185+
| `LIGHT_BLUE` | 라이트블루 | `#81D4FA` |
186+
| `NAVY` | 네이비 | `#1F3A5F` |
187+
| `PURPLE` | 퍼플 | `#8E24AA` |
188+
| `BROWN` | 브라운 | `#795548` |
189+
| `GRAY` | 그레이 | `#9E9E9E` |
190+
| `WHITE` | 화이트 | `#FFFFFF` |
191+
| `BLACK` | 블랙 | `#212121` |
192+
193+
---
194+
195+
## 8. 프론트엔드 (Vite) 연동
196+
197+
```ts
198+
// 1. 앱 초기화 시 카탈로그 로드
199+
const { data } = await fetch('/api/categories').then(r => r.json());
200+
201+
// 2. code → hex 매핑 (상품 상세 컬러 swatch)
202+
const colorMap = Object.fromEntries(
203+
data.colors.map((c) => [c.code, c.hex])
204+
);
205+
206+
// 3. 상품 상세 - 이름 대신 색 원 표시
207+
<div
208+
style={{
209+
backgroundColor: colorMap[product.color],
210+
border: product.color === 'WHITE' ? '1px solid #E0E0E0' : 'none',
211+
}}
212+
title={data.colors.find(c => c.code === product.color)?.name}
213+
/>
214+
```
215+
216+
개발 환경 proxy 예시:
217+
218+
```ts
219+
// vite.config.ts
220+
export default defineConfig({
221+
server: {
222+
proxy: {
223+
'/api': 'http://localhost:8080',
224+
},
225+
},
226+
});
227+
```
228+
229+
---
230+
231+
## 9. AI (Gemini) 연동
232+
233+
```java
234+
// GeminiService 예시
235+
String guide = categoryCatalogService.getAiClassificationGuide();
236+
// 프롬프트에 guide 포함 후 JSON 응답 요청
237+
238+
categoryCatalogService.validateClothesClassification(
239+
aiResult.getCategory(),
240+
aiResult.getItemType(),
241+
aiResult.getColor()
242+
);
243+
```
244+
245+
AI 응답은 반드시 위 **code 목록**만 사용해야 합니다.
246+
247+
---
248+
249+
## 10. 백엔드 코드 위치
250+
251+
```
252+
src/main/java/com/closetnangam/be/domain/catalog/
253+
├── enums/
254+
│ ├── ClothesCategory.java
255+
│ ├── ClothesItemType.java
256+
│ ├── ClothesColor.java
257+
│ └── StyleCode.java
258+
├── entity/Style.java
259+
├── controller/CategoryController.java
260+
└── service/CategoryCatalogService.java
261+
```
262+
263+
카테고리 추가/변경 시 **enum 수정 → API 자동 반영 → 이 문서 갱신** 순서로 진행하세요.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.closetnangam.be.domain.catalog.config;
2+
3+
import com.closetnangam.be.domain.catalog.entity.Style;
4+
import com.closetnangam.be.domain.catalog.enums.StyleCode;
5+
import com.closetnangam.be.domain.catalog.repository.StyleRepository;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.boot.ApplicationArguments;
8+
import org.springframework.boot.ApplicationRunner;
9+
import org.springframework.stereotype.Component;
10+
import org.springframework.transaction.annotation.Transactional;
11+
12+
@Component
13+
@RequiredArgsConstructor
14+
public class StyleDataInitializer implements ApplicationRunner {
15+
16+
private final StyleRepository styleRepository;
17+
18+
@Override
19+
@Transactional
20+
public void run(ApplicationArguments args) {
21+
for (StyleCode styleCode : StyleCode.values()) {
22+
if (styleRepository.existsByCode(styleCode.name())) {
23+
continue;
24+
}
25+
styleRepository.save(Style.from(styleCode));
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)