diff --git a/ai-context/domain-books/maps/README.md b/ai-context/domain-books/maps/README.md deleted file mode 100644 index d4c8f15..0000000 --- a/ai-context/domain-books/maps/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# maps 도메인 - -> 경로 검색 및 즐겨찾기 장소 관리 - -## 📖 목차 - -1. [기능 정의](./features.md) -2. [도메인 모델](./domain-model.md) -3. [API 명세](./api-spec.md) -4. [비즈니스 규칙](./business-rules.md) - -## 📝 개요 - -maps 도메인은 여행자가 목적지를 찾고 경로를 확인할 수 있도록 지도 기능을 제공합니다. 네이버 지도 API를 통해 출발지에서 목적지까지의 경로를 검색하고, 자주 가는 장소를 즐겨찾기로 저장할 수 있습니다. 검색 기록은 자동으로 관리되어 빠른 재검색을 지원합니다. - -## 🎯 핵심 기능 - -- 경로 검색 (출발지 → 목적지) -- 즐겨찾기 장소 추가/조회/삭제 -- 검색 기록 자동 저장 및 조회 -- 외부 지도 앱 연계 - -## 📊 주요 엔티티 - -- **Route**: 경로 정보 (출발지, 목적지, 시간/거리 요약) -- **FavoritePlace**: 즐겨찾기 장소 (장소 이름, 주소, 사용자 별칭) -- **SearchHistory**: 검색 기록 (검색한 장소명, 검색 시각) - -## 🔗 도메인 의존성 - -- **의존하는 도메인**: users (사용자 정보), missions (미션 정보) -- **이 도메인에 의존하는 도메인**: 없음 diff --git a/ai-context/domain-books/maps/api-spec.md b/ai-context/domain-books/maps/api-spec.md deleted file mode 100644 index cdc24dc..0000000 --- a/ai-context/domain-books/maps/api-spec.md +++ /dev/null @@ -1,588 +0,0 @@ -# maps 도메인 API 명세 - -> 생성일: 2026-02-12 -> Phase: 4 (API Designer) -> 상태: ✅ 완료 - ---- - -## 📋 ENUM 정의 - -### LocationType - -위치 타입: -- `CURRENT_LOCATION`: 현재 위치 (GPS) -- `ADDRESS`: 주소 입력 -- `PLACE_NAME`: 장소명 입력 - -**참고**: 이 도메인은 주로 지리 데이터를 다루므로 최소한의 ENUM만 정의합니다. - ---- - -## 📡 API 목록 - -| API | 설명 | 중요도 | -|-----|------|:------:| -| 경로 생성 | 출발지→목적지 경로 검색 및 저장 | 🔥 필수 | -| 즐겨찾기 추가 | 장소를 즐겨찾기에 저장 | ⭐ 중요 | -| 즐겨찾기 조회 | 사용자의 즐겨찾기 목록 | ⭐ 중요 | -| 즐겨찾기 삭제 | 즐겨찾기 장소 제거 | ⭐ 중요 | -| 검색 기록 조회 | 최근 검색한 장소 목록 | ⭐ 중요 | - ---- - -## 1. 경로 생성 - -### 개요 - -**목적**: 출발지와 목적지를 입력받아 경로를 검색하고 서버에 저장 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 출발지/목적지 좌표 또는 주소 -- 네이버 지도 API 성공 호출 - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| origin | 문자열 | ✅ | 출발지 (주소 또는 "현재 위치") | "서울역" 또는 "CURRENT_LOCATION" | -| destination | 문자열 | ✅ | 목적지 (주소) | "명동" | -| missionId | 문자열 | ❌ | 미션 진행 중이면 미션 ID | "m_abc123" | - -**예시**: -```json -{ - "origin": "CURRENT_LOCATION", - "destination": "명동", - "missionId": "m_abc123" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "경로가 생성되었습니다", - "data": { - "route": { - "id": "r_abc123", - "origin": { - "name": "현재 위치", - "address": "서울특별시 중구 세종대로 18", - "latitude": 37.5665, - "longitude": 126.9780 - }, - "destination": { - "name": "명동", - "address": "서울특별시 중구 명동2가", - "latitude": 37.5636, - "longitude": 126.9864 - }, - "summary": { - "distance": "1.2km", - "duration": "5분", - "taxiFare": "4,000원" - }, - "missionId": "m_abc123", - "createdAt": "2026-02-12T14:30:00Z" - } - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**지도 API 호출 실패**: -```json -{ - "status": "ROUTE_NOT_FOUND", - "message": "경로를 찾을 수 없습니다. 주소를 확인해주세요.", - "data": null -} -``` - -**잘못된 입력**: -```json -{ - "status": "INVALID_INPUT", - "message": "출발지 또는 목적지가 유효하지 않습니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 경로생성(userID, origin, destination, missionId): - # 1. 출발지 좌표 해석 - If origin = LocationType.CURRENT_LOCATION: - OriginCoords = Get_User_Location() # 클라이언트에서 전달받은 GPS 좌표 - Else: - OriginCoords = Call 지오코딩_API(origin) # 주소 → 좌표 변환 - - # 2. 목적지 좌표 해석 - DestinationCoords = Call 지오코딩_API(destination) - - # 3. 네이버 지도 API 호출 (서버 프록시) - RouteData = Call 네이버_지도_API(OriginCoords, DestinationCoords) - If RouteData is Null: - Return { - status: "ROUTE_NOT_FOUND", - message: "경로를 찾을 수 없습니다. 주소를 확인해주세요.", - data: null - } - - # 4. 경로 요약 정보 추출 - Summary = { - distance: RouteData.distance + "km", - duration: RouteData.duration + "분", - taxiFare: Calculate_Taxi_Fare(RouteData.distance) - } - - # 5. Route 엔티티 생성 - Route = Create Route { - origin: { - name: origin, - address: OriginCoords.address, - latitude: OriginCoords.lat, - longitude: OriginCoords.lng - }, - destination: { - name: destination, - address: DestinationCoords.address, - latitude: DestinationCoords.lat, - longitude: DestinationCoords.lng - }, - summary: Summary, - missionId: missionId, - createdAt: Now() - } - Save Route - - # 6. 검색 기록 저장 - Save_Search_History(userID, destination) - - # 7. 응답 - Return { - status: "SUCCESS", - message: "경로가 생성되었습니다", - data: { - route: Route - } - } -``` - -**참고**: 택시 요금은 기본요금 + 거리/시간 요금을 간단히 계산합니다. (정확한 요금은 택시 앱 참조) - ---- - -## 2. 즐겨찾기 추가 - -### 개요 - -**목적**: 사용자가 자주 가는 장소를 즐겨찾기에 저장 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 장소 이름/주소 -- 즐겨찾기 최대 20개 제한 - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| placeName | 문자열 | ✅ | 장소 이름 | "명동" | -| address | 문자열 | ✅ | 주소 | "서울특별시 중구 명동2가" | -| nickname | 문자열 | ✅ | 사용자 지정 별칭 | "숙소", "공항", "회사" | - -**예시**: -```json -{ - "placeName": "명동", - "address": "서울특별시 중구 명동2가", - "nickname": "숙소" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "즐겨찾기가 추가되었습니다", - "data": { - "favoritePlace": { - "id": "fp_abc123", - "placeName": "명동", - "address": "서울특별시 중구 명동2가", - "nickname": "숙소", - "latitude": 37.5636, - "longitude": 126.9864, - "createdAt": "2026-02-12T14:30:00Z" - } - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**즐겨찾기 최대 개수 초과**: -```json -{ - "status": "LIMIT_EXCEEDED", - "message": "즐겨찾기는 최대 20개까지 저장할 수 있습니다", - "data": null -} -``` - -**잘못된 입력**: -```json -{ - "status": "INVALID_INPUT", - "message": "장소 이름 또는 주소가 유효하지 않습니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 즐겨찾기추가(userID, placeName, address, nickname): - # 1. 즐겨찾기 개수 확인 - FavoriteCount = Count FavoritePlace Where UserID = userID - If FavoriteCount >= 20: - Return { - status: "LIMIT_EXCEEDED", - message: "즐겨찾기는 최대 20개까지 저장할 수 있습니다", - data: null - } - - # 2. 주소 → 좌표 변환 - Coords = Call 지오코딩_API(address) - - # 3. FavoritePlace 엔티티 생성 - FavoritePlace = Create FavoritePlace { - userID: userID, - placeName: placeName, - address: address, - nickname: nickname, - latitude: Coords.lat, - longitude: Coords.lng, - createdAt: Now() - } - Save FavoritePlace - - # 4. 응답 - Return { - status: "SUCCESS", - message: "즐겨찾기가 추가되었습니다", - data: { - favoritePlace: FavoritePlace - } - } -``` - ---- - -## 3. 즐겨찾기 조회 - -### 개요 - -**목적**: 사용자의 즐겨찾기 장소 목록 조회 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: 유효한 인증 토큰 - ---- - -### Request (요청) - -**URL**: `/maps/favorites` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "즐겨찾기 목록을 조회했습니다", - "data": { - "favoritePlaces": [ - { - "id": "fp_abc123", - "placeName": "명동", - "address": "서울특별시 중구 명동2가", - "nickname": "숙소", - "latitude": 37.5636, - "longitude": 126.9864, - "createdAt": "2026-02-12T14:30:00Z" - }, - { - "id": "fp_def456", - "placeName": "인천국제공항", - "address": "인천광역시 중구 공항로", - "nickname": "공항", - "latitude": 37.4602, - "longitude": 126.4407, - "createdAt": "2026-02-10T09:15:00Z" - } - ], - "total": 2 - } -} -``` - ---- - -## 4. 즐겨찾기 삭제 - -### 개요 - -**목적**: 즐겨찾기 장소를 삭제 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: 유효한 인증 토큰, 본인의 즐겨찾기만 삭제 가능 - ---- - -### Request (요청) - -**URL**: `/maps/favorites/{favoritePlaceId}` - -**Method**: DELETE - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "즐겨찾기가 삭제되었습니다", - "data": null -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**즐겨찾기 없음**: -```json -{ - "status": "NOT_FOUND", - "message": "즐겨찾기를 찾을 수 없습니다", - "data": null -} -``` - -**권한 없음**: -```json -{ - "status": "FORBIDDEN", - "message": "본인의 즐겨찾기만 삭제할 수 있습니다", - "data": null -} -``` - ---- - -## 5. 검색 기록 조회 - -### 개요 - -**목적**: 사용자의 최근 장소 검색 기록 조회 (최대 10개) - -**호출 주체**: 인증된 사용자 - -**성공 조건**: 유효한 인증 토큰 - ---- - -### Request (요청) - -**URL**: `/maps/search-history` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "검색 기록을 조회했습니다", - "data": { - "searchHistory": [ - { - "id": "sh_abc123", - "searchText": "명동", - "createdAt": "2026-02-12T14:30:00Z" - }, - { - "id": "sh_def456", - "searchText": "강남역", - "createdAt": "2026-02-12T13:20:00Z" - }, - { - "id": "sh_ghi789", - "searchText": "홍대입구", - "createdAt": "2026-02-12T10:15:00Z" - } - ], - "total": 3 - } -} -``` - ---- - -### 수도코드 - -``` -Function 검색기록조회(userID): - # 1. 최근 10개 검색 기록 조회 - SearchHistory = Find All SearchHistory - Where UserID = userID - Order By CreatedAt DESC - Limit 10 - - # 2. 응답 - Return { - status: "SUCCESS", - message: "검색 기록을 조회했습니다", - data: { - searchHistory: SearchHistory, - total: Count(SearchHistory) - } - } -``` - -**참고**: 검색 기록은 자동으로 저장되며, 10개를 초과하면 가장 오래된 것이 자동 삭제됩니다. - ---- - -## 📝 주요 제약 - -### 서버 프록시 사용 - -**규칙**: 네이버 지도 API는 반드시 서버 프록시를 통해 호출합니다. - -- 클라이언트는 직접 API 호출 불가 (API 키 노출 방지) -- 서버가 API 키 관리 및 요청 제한 처리 - -### 검색 기록 자동 관리 - -**규칙**: 검색 기록은 최근 10개까지만 보관합니다. - -- 10개 초과 시 가장 오래된 것 자동 삭제 -- 사용자는 수동 삭제 불가 (자동 관리) - -### 즐겨찾기 최대 개수 - -**규칙**: 한 사용자는 최대 20개까지 즐겨찾기를 저장할 수 있습니다. - -- 20개 초과 시 추가 불가 -- 삭제 후 다시 추가 가능 - -### 사용자 탈퇴 시 처리 - -**규칙**: 사용자 탈퇴 시 검색 기록과 즐겨찾기는 완전히 삭제됩니다. - -- SearchHistory: 완전 삭제 -- FavoritePlace: 완전 삭제 -- Route: 익명화되어 보관 (미션 기록용) - ---- - -## 🗺️ 외부 지도 앱 연계 - -### 시나리오 - -1. 사용자가 경로 카드를 확인합니다. -2. "외부 지도에서 보기" 버튼을 누릅니다. -3. 시스템은 기기의 기본 지도 앱으로 경로를 전달합니다. - - iOS: Apple Maps 또는 사용자 설정 앱 - - Android: Google Maps 또는 네이버 지도 - -**Deep Link 예시**: -``` -nmap://route/public?slat=37.5665&slng=126.9780&sname=현재위치&dlat=37.5636&dlng=126.9864&dname=명동 -``` - ---- - -**maps 도메인 API 완료** ✅ diff --git a/ai-context/domain-books/maps/business-rules.md b/ai-context/domain-books/maps/business-rules.md deleted file mode 100644 index c05f9e9..0000000 --- a/ai-context/domain-books/maps/business-rules.md +++ /dev/null @@ -1,146 +0,0 @@ -# maps 도메인 비즈니스 규칙 - -## 데이터 규칙 - -### 규칙 1: 출발지와 목적지 필수 -**설명**: 경로를 생성하려면 출발지와 목적지가 반드시 필요합니다. -**예시**: 출발지 "현재 위치", 목적지 "명동"을 입력해야 경로 검색이 가능합니다. -**위반 시**: 출발지나 목적지가 없으면 "INVALID_INPUT" 오류가 발생합니다. - ---- - -### 규칙 2: 현재 위치 허용 -**설명**: 출발지는 "현재 위치"로 설정할 수 있으며, 이 경우 GPS 좌표를 사용합니다. -**예시**: 출발지를 "CURRENT_LOCATION"으로 설정하면 클라이언트에서 전달한 GPS 좌표가 사용됩니다. -**위반 시**: GPS 권한이 없으면 현재 위치를 사용할 수 없습니다. - ---- - -### 규칙 3: 즐겨찾기 필수 필드 -**설명**: 즐겨찾기 장소에는 장소 이름, 주소, 사용자 별칭이 반드시 포함되어야 합니다. -**예시**: 장소 이름 "명동", 주소 "서울특별시 중구 명동2가", 별칭 "숙소"가 모두 저장됩니다. -**위반 시**: 필수 필드가 없으면 즐겨찾기가 생성되지 않습니다. - ---- - -### 규칙 4: 검색 기록 자동 생성 -**설명**: 사용자가 장소를 검색하면 검색 기록이 자동으로 저장됩니다. 사용자가 직접 추가할 수 없습니다. -**예시**: "명동"을 검색하면 SearchHistory에 자동으로 "명동"과 검색 시각이 저장됩니다. -**위반 시**: 사용자가 수동으로 검색 기록을 추가하려는 시도는 무시됩니다. - ---- - -## 생명주기 규칙 - -### 규칙 5: 경로 정보 저장 -**설명**: 경로 검색 결과는 서버에 저장되어 나중에 다시 확인할 수 있습니다. -**예시**: 택시 미션 중 검색한 "서울역 → 명동" 경로는 Route 엔티티로 저장됩니다. -**위반 시**: 미션이 종료되어도 경로 기록은 보관됩니다. - ---- - -### 규칙 6: 검색 기록 최대 10개 -**설명**: 검색 기록은 최근 10개까지만 보관되며, 10개를 초과하면 가장 오래된 것이 자동 삭제됩니다. -**예시**: 11번째 장소를 검색하면 첫 번째 검색 기록이 자동으로 삭제됩니다. -**위반 시**: 사용자가 직접 삭제할 수 없으며, 시스템이 자동 관리합니다. - ---- - -### 규칙 7: 즐겨찾기 최대 20개 -**설명**: 한 사용자는 최대 20개까지 즐겨찾기 장소를 저장할 수 있습니다. -**예시**: 19개가 저장된 상태에서 20번째를 추가할 수 있지만, 21번째는 추가할 수 없습니다. -**위반 시**: 20개 초과 시 "LIMIT_EXCEEDED" 오류가 발생합니다. - ---- - -### 규칙 8: 사용자 탈퇴 시 데이터 처리 -**설명**: 사용자가 탈퇴하면 검색 기록과 즐겨찾기는 완전히 삭제되지만, Route는 익명화되어 보관됩니다. -**예시**: 탈퇴 시 "명동" 검색 기록과 "숙소" 즐겨찾기는 삭제되지만, 택시 미션의 경로는 익명으로 보관됩니다. -**위반 시**: 개인 식별 가능한 검색/즐겨찾기 데이터는 모두 제거됩니다. - ---- - -## 제약 조건 - -### 규칙 9: 지도 API 프록시 사용 -**설명**: 네이버 지도 API는 반드시 서버 프록시를 통해 호출해야 하며, 클라이언트에서 직접 호출할 수 없습니다. -**예시**: 클라이언트는 서버의 `/maps/routes` API를 호출하고, 서버가 네이버 API를 대신 호출합니다. -**위반 시**: API 키가 노출되면 보안 문제가 발생합니다. - ---- - -### 규칙 10: 경로 검색 실패 처리 -**설명**: 네이버 지도 API 호출이 실패하면 "경로를 찾을 수 없습니다" 메시지를 표시하고 재시도를 안내합니다. -**예시**: 잘못된 주소 "asdfasdf"를 입력하면 API가 null을 반환하고 오류 메시지가 표시됩니다. -**위반 시**: 사용자는 주소를 수정하고 다시 검색해야 합니다. - ---- - -### 규칙 11: 택시 요금 추정 -**설명**: 경로 검색 시 거리 기반으로 택시 요금을 간단히 추정하여 제공합니다. 정확한 요금은 택시 앱을 참조해야 합니다. -**예시**: 1.2km 경로는 기본요금 + 거리 요금으로 약 4,000원으로 계산됩니다. -**위반 시**: 실제 요금은 시간대, 교통 상황 등에 따라 다를 수 있습니다. - ---- - -## 성능 규칙 - -### 규칙 12: 경로 검색 응답 시간 -**설명**: 경로 검색은 3초 이내에 완료되어야 합니다. -**예시**: 사용자가 "명동"을 검색하면 3초 이내에 경로 요약이 표시됩니다. -**위반 시**: 네이버 API 지연 시 타임아웃 메시지가 표시됩니다. - ---- - -### 규칙 13: 즐겨찾기 조회 캐싱 -**설명**: 즐겨찾기 목록은 클라이언트에 캐싱되어 매번 서버 요청이 불필요합니다. -**예시**: 즐겨찾기를 한 번 조회하면 앱이 종료될 때까지 캐시에서 표시됩니다. -**위반 시**: 즐겨찾기 추가/삭제 시 캐시를 갱신해야 합니다. - ---- - -## 연계 규칙 - -### 규칙 14: 미션과 경로 연계 -**설명**: 택시 미션 중 경로를 검색하면 Route에 미션 ID가 함께 저장됩니다. -**예시**: 택시 미션 ID "m_abc123"을 경로 생성 시 전달하면 Route.missionId에 저장됩니다. -**위반 시**: 미션 없이 경로를 검색해도 정상 작동합니다. - ---- - -### 규칙 15: 검색 기록과 즐겨찾기 분리 -**설명**: 검색 기록은 자동 관리되고, 즐겨찾기는 수동 관리됩니다. 두 기능은 독립적으로 작동합니다. -**예시**: "명동"을 검색하면 검색 기록에 추가되지만, 즐겨찾기에는 자동 추가되지 않습니다. -**위반 시**: 사용자가 명시적으로 즐겨찾기 추가 버튼을 눌러야 합니다. - ---- - -## 권한 규칙 - -### 규칙 16: 본인 데이터만 관리 가능 -**설명**: 사용자는 자신의 즐겨찾기와 검색 기록만 조회하고 관리할 수 있습니다. -**예시**: 사용자 A는 자신의 즐겨찾기만 보고, 사용자 B의 즐겨찾기는 접근할 수 없습니다. -**위반 시**: 다른 사용자의 데이터 접근 시도는 "UNAUTHORIZED" 오류로 거부됩니다. - ---- - -### 규칙 17: GPS 권한 필요 -**설명**: 현재 위치를 출발지로 사용하려면 앱에 GPS 권한이 필요합니다. -**예시**: 사용자가 GPS 권한을 허용하면 "현재 위치"를 출발지로 선택할 수 있습니다. -**위반 시**: GPS 권한이 없으면 수동으로 출발지를 입력해야 합니다. - ---- - -## 외부 연계 규칙 - -### 규칙 18: 외부 지도 앱 연계 -**설명**: 사용자는 경로 확인 후 네이버 지도, 구글 맵 등 외부 앱으로 경로를 전달할 수 있습니다. -**예시**: "외부 지도에서 보기" 버튼을 누르면 Deep Link로 네이버 지도 앱이 실행됩니다. -**위반 시**: 외부 앱이 설치되지 않았으면 웹 브라우저로 열립니다. - ---- - -### 규칙 19: Deep Link 형식 -**설명**: 외부 지도 앱 연계 시 표준 Deep Link 형식을 사용합니다. -**예시**: 네이버 지도는 "nmap://route/public?slat=37.5665&slng=126.9780&dlat=37.5636&dlng=126.9864" 형식을 사용합니다. -**위반 시**: 잘못된 형식은 외부 앱에서 열리지 않습니다. diff --git a/ai-context/domain-books/maps/domain-model.md b/ai-context/domain-books/maps/domain-model.md deleted file mode 100644 index d8e6d40..0000000 --- a/ai-context/domain-books/maps/domain-model.md +++ /dev/null @@ -1,146 +0,0 @@ -# maps 도메인 모델 - -> 생성일: 2026-02-12 -> Phase: 3 (Domain Modeler) -> 상태: ✅ 완료 - ---- - -## 📖 유비쿼터스 언어 (용어 정의) - -> 이 도메인에서 사용하는 전용 용어들 - -### 핵심 용어 - -| 용어 | 정의 | 예시 | -|------|------|------| -| 경로 (Route) | 출발지에서 목적지까지의 이동 경로 | A → B 경로 정보 | -| 출발지 (Origin) | 경로의 시작 지점 | "서울역", 현재 위치 | -| 목적지 (Destination) | 경로의 도착 지점 | "명동", "인천공항" | -| 검색 기록 (SearchHistory) | 사용자가 검색한 장소 기록 | "강남역", "홍대입구" | -| 즐겨찾기 (FavoritePlace) | 사용자가 저장한 자주 가는 장소 | "숙소", "회사" | -| 경로 요약 (RouteSummary) | 경로의 시간/거리 요약 정보 | "15분, 3.2km" | - ---- - -## 📐 관계 규칙 - -> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의 - -### Route와 사용자 - -**규칙 1**: 경로는 별도 Route 엔티티로 관리된다. -**규칙 2**: Route는 사용자가 직접 소유하지 않지만, Mission을 통해 간접 참조한다. - -### Route와 미션 - -**규칙 3**: 미션(특히 택시)은 Route를 참조할 수 있다. -**규칙 4**: Route는 미션 완료 후에도 기록으로 보관된다. - -### SearchHistory와 사용자 - -**규칙 5**: 검색 기록은 사용자에게 속한다. -**규칙 6**: 한 사용자는 여러 개의 검색 기록을 가질 수 있다. -**규칙 7**: 사용자 탈퇴 시 검색 기록은 완전히 삭제된다. - -### FavoritePlace와 사용자 - -**규칙 8**: 즐겨찾기는 사용자에게 속한다. -**규칙 9**: 한 사용자는 여러 개의 즐겨찾기 장소를 저장할 수 있다. -**규칙 10**: 사용자 탈퇴 시 즐겨찾기는 완전히 삭제된다. - ---- - -## 🔒 제약 조건 - -> 비즈니스 규칙과 제약사항 - -### Route 엔티티 - -**필수 필드**: -- **ID**: 시스템이 자동으로 생성 -- **Origin**: 출발지 (텍스트 또는 좌표) -- **Destination**: 목적지 (텍스트 또는 좌표) -- **Summary**: 경로 요약 (시간, 거리) - -**선택 필드**: -- **MissionID**: 이 경로가 속한 미션 (선택) - -### SearchHistory 엔티티 - -**필수 필드**: -- **ID**: 시스템이 자동으로 생성 -- **UserID**: 검색한 사용자 -- **SearchText**: 검색한 장소명 -- **CreatedAt**: 검색 시각 - -**제약**: -**규칙 11**: 최근 10개까지만 보관한다. (오래된 것은 자동 삭제) - -### FavoritePlace 엔티티 - -**필수 필드**: -- **ID**: 시스템이 자동으로 생성 -- **UserID**: 즐겨찾기 소유자 -- **PlaceName**: 장소 이름 -- **Address**: 주소 (또는 좌표) -- **Nickname**: 사용자 지정 별칭 (예: "숙소", "공항") - -**제약**: -**규칙 12**: 한 사용자는 최대 20개까지 즐겨찾기를 저장할 수 있다. - -### 지도 API 사용 - -**규칙 13**: 지도 API 호출은 서버 프록시를 통해서만 가능하다. (API 키 보호) -**규칙 14**: 클라이언트는 직접 API를 호출할 수 없다. - ---- - -## 🎯 생명주기 - -### Route 생성 - -1. 사용자가 출발지와 목적지를 입력한다. - - 출발지: 현재 위치 허용 시 자동, 아니면 수동 입력 - - 목적지: 검색 기록, 즐겨찾기, 또는 수동 입력 -2. 시스템은 서버 프록시를 통해 네이버 지도 API를 호출한다. -3. API 응답으로 경로 정보를 받는다. -4. Route 엔티티를 생성하고 경로 요약을 저장한다. -5. 사용자에게 경로 카드를 표시한다. (시간/거리) - -### Route와 미션 연계 - -**규칙 15**: 택시 미션에서 경로를 요청하면 MissionID를 함께 저장한다. -**규칙 16**: 가이드 탭에서 "지도로 이동" 버튼을 누르면 미션의 Route를 자동으로 표시한다. - -### SearchHistory 생성 - -1. 사용자가 장소를 검색한다. -2. 검색어를 SearchHistory에 저장한다. -3. 최근 10개를 초과하면 가장 오래된 것을 삭제한다. - -### SearchHistory 조회 - -**규칙 17**: 사용자가 목적지 입력 필드를 누르면 최근 검색 기록이 자동 완성으로 표시된다. - -### FavoritePlace 생성 - -1. 사용자가 장소를 검색하거나 경로를 확인한다. -2. "즐겨찾기 추가" 버튼을 누른다. -3. Nickname을 입력한다. (예: "숙소", "공항") -4. FavoritePlace를 저장한다. - -### FavoritePlace 조회 - -1. 사용자가 목적지 입력 화면에서 "즐겨찾기" 탭을 선택한다. -2. 저장된 FavoritePlace 목록이 표시된다. -3. 장소를 선택하면 자동으로 목적지가 설정된다. - -### 외부 지도 앱 연계 - -**규칙 18**: "외부 지도에서 보기" 버튼을 제공한다. -**규칙 19**: 버튼을 누르면 시스템 기본 지도 앱(네이버, 구글 등)으로 경로를 전달한다. - ---- - -**maps 도메인 완료** ✅ diff --git a/ai-context/domain-books/maps/features.md b/ai-context/domain-books/maps/features.md deleted file mode 100644 index f7972ce..0000000 --- a/ai-context/domain-books/maps/features.md +++ /dev/null @@ -1,104 +0,0 @@ -# maps 도메인 기능 정의 - -## 기능 1: 경로 검색 - -**설명**: 사용자가 출발지와 목적지를 입력하면 네이버 지도 API를 통해 경로를 검색하고 시간/거리/예상 택시 요금을 표시합니다. - -**사용자 시나리오**: -1. 사용자가 지도 화면에서 출발지를 "현재 위치"로 선택합니다. -2. 목적지에 "명동"을 입력합니다. -3. "경로 검색" 버튼을 누르면 서버가 네이버 지도 API를 호출합니다. -4. "1.2km, 5분, 예상 요금 4,000원" 등의 경로 요약이 표시됩니다. - -**관련 API**: -- 경로 생성 (api-spec.md 참조) - ---- - -## 기능 2: 즐겨찾기 장소 추가 - -**설명**: 사용자가 자주 가는 장소를 즐겨찾기로 저장하면 다음에 빠르게 목적지로 설정할 수 있습니다. 최대 20개까지 저장 가능합니다. - -**사용자 시나리오**: -1. 사용자가 경로 검색 후 "명동" 목적지를 즐겨찾기에 추가합니다. -2. "숙소"라는 별칭을 입력합니다. -3. 즐겨찾기 목록에 "숙소 (명동)"가 저장됩니다. -4. 다음에 목적지 입력 시 즐겨찾기 탭에서 "숙소"를 선택하면 자동으로 "명동"이 설정됩니다. - -**관련 API**: -- 즐겨찾기 추가 (api-spec.md 참조) - ---- - -## 기능 3: 즐겨찾기 장소 조회 - -**설명**: 사용자가 저장한 즐겨찾기 목록을 확인하고 목적지로 빠르게 선택할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 목적지 입력 화면에서 "즐겨찾기" 탭을 선택합니다. -2. "숙소", "공항", "회사" 등 저장된 장소 목록이 표시됩니다. -3. "공항"을 선택하면 자동으로 "인천국제공항"이 목적지로 설정됩니다. -4. "경로 검색" 버튼을 누르면 바로 경로가 표시됩니다. - -**관련 API**: -- 즐겨찾기 조회 (api-spec.md 참조) - ---- - -## 기능 4: 즐겨찾기 장소 삭제 - -**설명**: 더 이상 필요 없는 즐겨찾기 장소를 삭제할 수 있습니다. 삭제 후 다시 추가할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 즐겨찾기 목록에서 "회사" 항목을 길게 누릅니다. -2. "삭제" 버튼이 나타나고 선택합니다. -3. 확인 팝업에서 "삭제"를 누르면 즐겨찾기에서 제거됩니다. -4. 20개 제한에 걸렸다면 삭제 후 새로운 장소를 추가할 수 있습니다. - -**관련 API**: -- 즐겨찾기 삭제 (api-spec.md 참조) - ---- - -## 기능 5: 검색 기록 자동 저장 - -**설명**: 사용자가 검색한 장소는 자동으로 검색 기록에 저장되어 다음에 빠르게 재검색할 수 있습니다. 최근 10개까지 자동 관리됩니다. - -**사용자 시나리오**: -1. 사용자가 "명동"을 검색하여 경로를 확인합니다. -2. 시스템이 자동으로 "명동"을 검색 기록에 저장합니다. -3. 나중에 목적지 입력 필드를 누르면 "명동", "강남역", "홍대입구" 등 최근 검색 기록이 자동 완성으로 표시됩니다. -4. "명동"을 선택하면 바로 목적지가 설정됩니다. - -**관련 API**: -- 검색 기록 조회 (api-spec.md 참조) - ---- - -## 기능 6: 미션과 경로 연계 - -**설명**: 택시 미션 진행 중 경로를 검색하면 미션 ID가 함께 저장되어, 가이드 탭에서 "지도로 이동" 버튼으로 빠르게 경로를 다시 확인할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션을 시작합니다. -2. 1단계(목적지 설정)에서 "명동"을 검색합니다. -3. 경로가 택시 미션과 함께 저장됩니다. -4. 3단계(운전자에게 전달)에서 "지도로 이동" 버튼을 누르면 이전에 검색한 경로가 바로 표시됩니다. - -**관련 API**: -- 경로 생성 (missionId 파라미터 사용, api-spec.md 참조) - ---- - -## 기능 7: 외부 지도 앱 연계 - -**설명**: 사용자는 경로 확인 후 "외부 지도에서 보기" 버튼을 눌러 네이버 지도, 구글 맵 등 시스템 기본 지도 앱으로 경로를 전달할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 "서울역 → 명동" 경로를 확인합니다. -2. "외부 지도에서 보기" 버튼을 누릅니다. -3. 시스템이 네이버 지도 앱으로 경로를 전달합니다. -4. 네이버 지도에서 상세 네비게이션을 이용할 수 있습니다. - -**관련 API**: -- 경로 생성 (api-spec.md 참조, 클라이언트에서 Deep Link 처리) diff --git a/ai-context/domain-books/missions/README.md b/ai-context/domain-books/missions/README.md deleted file mode 100644 index 41f4e7c..0000000 --- a/ai-context/domain-books/missions/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# missions 도메인 - -> 단계별 여행 미션 진행 및 관리 - -## 📖 목차 - -1. [기능 정의](./features.md) -2. [도메인 모델](./domain-model.md) -3. [API 명세](./api-spec.md) -4. [비즈니스 규칙](./business-rules.md) - -## 📝 개요 - -missions 도메인은 여행자가 택시 이용, 결제, 체크인 등의 실제 상황을 단계별 가이드를 통해 해결할 수 있도록 돕습니다. 각 미션은 3-7개의 체크리스트 단계로 구성되며, 사용자는 순서대로 진행하면서 번역 기능과 추천 문장을 활용할 수 있습니다. - -## 🎯 핵심 기능 - -- 미션 시작 (택시/결제/체크인) -- 단계별 진행 (다음/이전 단계 이동) -- 미션 완료 및 결과 기록 -- 진행중 미션 조회 - -## 📊 주요 엔티티 - -- **Mission**: 미션 정보 (타입, 상태, 현재 단계, 완료 결과) -- **Step**: 미션의 체크리스트 단계 (단계 번호, 제목, 설명) -- **MissionType**: 미션 타입 (택시, 결제, 체크인) -- **MissionStatus**: 미션 상태 (진행중, 완료, 취소) -- **MissionResult**: 완료 결과 (해결, 부분해결, 미해결) - -## 🔗 도메인 의존성 - -- **의존하는 도메인**: users (사용자 정보), phrases (추천 문장) -- **이 도메인에 의존하는 도메인**: translations (미션별 번역 기록) diff --git a/ai-context/domain-books/missions/api-spec.md b/ai-context/domain-books/missions/api-spec.md deleted file mode 100644 index 49ec53b..0000000 --- a/ai-context/domain-books/missions/api-spec.md +++ /dev/null @@ -1,620 +0,0 @@ -# missions 도메인 API 명세 - -> 생성일: 2026-02-12 -> Phase: 4 (API Designer) -> 상태: ✅ 완료 - ---- - -## 📋 ENUM 정의 - -### MissionType - -미션 타입: -- `taxi`: 택시 이용 미션 -- `payment`: 결제 미션 -- `checkin`: 체크인 미션 - -### MissionStatus - -미션 상태: -- `InProgress`: 진행중 -- `Completed`: 완료 -- `Cancelled`: 취소 - -### MissionResult - -미션 완료 결과: -- `Resolved`: 해결 -- `PartiallyResolved`: 부분해결 -- `Unresolved`: 미해결 - -### StepDirection - -단계 이동 방향: -- `next`: 다음 단계 -- `prev`: 이전 단계 - ---- - -## 📡 API 목록 - -| API | 설명 | 중요도 | -|-----|------|:------:| -| 미션 시작 | 새 미션 생성 및 단계 초기화 | 🔥 필수 | -| 미션 단계 변경 | 현재 단계 이동 (다음/이전) | 🔥 필수 | -| 미션 완료 | 미션 종료 및 결과 저장 | 🔥 필수 | -| 진행중 미션 조회 | 현재 진행중인 미션 정보 | ⭐ 중요 | - ---- - -## 1. 미션 시작 - -### 개요 - -**목적**: 사용자가 미션 카드(택시/결제/체크인)를 선택하여 새 미션 시작 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 현재 진행중인 미션이 없어야 함 -- 미션 타입이 유효해야 함 (택시/결제/체크인) - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| type | **MissionType** (ENUM) | ✅ | 미션 타입 | "taxi", "payment", "checkin" | - -**예시**: -```json -{ - "type": "taxi" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "미션이 시작되었습니다", - "data": { - "mission": { - "id": "m_abc123", - "type": "taxi", - "status": "InProgress", - "currentStep": 1, - "result": null, - "createdAt": "2026-02-12T14:30:00Z" - }, - "steps": [ - { - "stepNumber": 1, - "title": "목적지 설정", - "description": "가고 싶은 장소를 검색하거나 즐겨찾기에서 선택하세요" - }, - { - "stepNumber": 2, - "title": "택시 호출", - "description": "택시 앱을 사용하거나 길거리에서 택시를 잡으세요" - }, - { - "stepNumber": 3, - "title": "운전자에게 목적지 전달", - "description": "추천 문장을 사용하거나 번역 기능으로 목적지를 전달하세요" - } - ] - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**진행중인 미션이 있는 경우**: -```json -{ - "status": "MISSION_IN_PROGRESS", - "message": "이미 진행중인 미션이 있습니다. 먼저 완료해주세요.", - "data": { - "currentMission": { - "id": "m_xyz789", - "type": "payment", - "currentStep": 2 - } - } -} -``` - -**잘못된 미션 타입**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 미션 타입입니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 미션시작(userID, type): - # 1. ENUM 검증 - If type NOT IN [MissionType.taxi, MissionType.payment, MissionType.checkin]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 미션 타입입니다", - data: null - } - - # 2. 진행중인 미션 확인 - ExistingMission = Find Mission Where UserID = userID AND Status = MissionStatus.InProgress - If ExistingMission Exists: - Return { - status: "MISSION_IN_PROGRESS", - message: "이미 진행중인 미션이 있습니다. 먼저 완료해주세요.", - data: { - currentMission: ExistingMission - } - } - - # 3. 새 Mission 생성 - Mission = Create Mission { - userID: userID, - type: type, - status: MissionStatus.InProgress, - currentStep: 1, - result: null, - createdAt: Now() - } - Save Mission - - # 4. 미션 타입에 맞는 Step 엔티티 생성 - Steps = Get_Steps_Template(type) # 미션 타입별 3-7개 단계 템플릿 - For Each StepTemplate In Steps: - Step = Create Step { - missionID: Mission.ID, - stepNumber: StepTemplate.Number, - title: StepTemplate.Title, - description: StepTemplate.Description - } - Save Step - - # 5. 응답 - Return { - status: "SUCCESS", - message: "미션이 시작되었습니다", - data: { - mission: Mission, - steps: Steps - } - } -``` - ---- - -## 2. 미션 단계 변경 - -### 개요 - -**목적**: 미션의 현재 단계를 다음 또는 이전으로 이동 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 진행중인 미션이 존재 -- 단계 번호가 유효 범위 내 (1 ~ 마지막 단계) - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| missionId | 문자열 | ✅ | 미션 ID | "m_abc123" | -| direction | **StepDirection** (ENUM) | ✅ | 이동 방향 | "next", "prev" | - -**예시**: -```json -{ - "missionId": "m_abc123", - "direction": "next" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "단계가 변경되었습니다", - "data": { - "mission": { - "id": "m_abc123", - "type": "taxi", - "status": "InProgress", - "currentStep": 2, - "result": null - }, - "currentStepInfo": { - "stepNumber": 2, - "title": "택시 호출", - "description": "택시 앱을 사용하거나 길거리에서 택시를 잡으세요" - } - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**범위 초과**: -```json -{ - "status": "INVALID_STEP", - "message": "유효하지 않은 단계입니다", - "data": null -} -``` - -**미션 없음**: -```json -{ - "status": "NOT_FOUND", - "message": "진행중인 미션을 찾을 수 없습니다", - "data": null -} -``` - -**잘못된 방향**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 방향입니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 미션단계변경(userID, missionId, direction): - # 1. ENUM 검증 - If direction NOT IN [StepDirection.next, StepDirection.prev]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 방향입니다", - data: null - } - - # 2. 미션 조회 - Mission = Find Mission Where ID = missionId AND UserID = userID AND Status = MissionStatus.InProgress - If Mission is Null: - Return { - status: "NOT_FOUND", - message: "진행중인 미션을 찾을 수 없습니다", - data: null - } - - # 3. 새 단계 계산 - If direction = StepDirection.next: - NewStep = Mission.CurrentStep + 1 - Else If direction = StepDirection.prev: - NewStep = Mission.CurrentStep - 1 - - # 4. 단계 범위 검증 - TotalSteps = Count Steps Where MissionID = missionId - If NewStep < 1 OR NewStep > TotalSteps: - Return { - status: "INVALID_STEP", - message: "유효하지 않은 단계입니다", - data: null - } - - # 5. 단계 업데이트 - Mission.CurrentStep = NewStep - Update Mission - - # 6. 현재 단계 정보 조회 - CurrentStepInfo = Find Step Where MissionID = missionId AND StepNumber = NewStep - - # 7. 응답 - Return { - status: "SUCCESS", - message: "단계가 변경되었습니다", - data: { - mission: Mission, - currentStepInfo: CurrentStepInfo - } - } -``` - ---- - -## 3. 미션 완료 - -### 개요 - -**목적**: 사용자가 미션을 종료하고 결과를 선택 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 진행중인 미션이 존재 -- 유효한 결과 타입 (해결/부분해결/미해결) - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| missionId | 문자열 | ✅ | 미션 ID | "m_abc123" | -| result | **MissionResult** (ENUM) | ✅ | 완료 결과 | "Resolved", "PartiallyResolved", "Unresolved" | - -**예시**: -```json -{ - "missionId": "m_abc123", - "result": "Resolved" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "미션이 완료되었습니다", - "data": { - "mission": { - "id": "m_abc123", - "type": "taxi", - "status": "Completed", - "currentStep": 3, - "result": "Resolved", - "completedAt": "2026-02-12T15:00:00Z" - } - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**미션 없음**: -```json -{ - "status": "NOT_FOUND", - "message": "진행중인 미션을 찾을 수 없습니다", - "data": null -} -``` - -**잘못된 결과**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 완료 결과입니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 미션완료(userID, missionId, result): - # 1. ENUM 검증 - If result NOT IN [MissionResult.Resolved, MissionResult.PartiallyResolved, MissionResult.Unresolved]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 완료 결과입니다", - data: null - } - - # 2. 미션 조회 - Mission = Find Mission Where ID = missionId AND UserID = userID AND Status = MissionStatus.InProgress - If Mission is Null: - Return { - status: "NOT_FOUND", - message: "진행중인 미션을 찾을 수 없습니다", - data: null - } - - # 3. 미션 상태 업데이트 - Mission.Status = MissionStatus.Completed - Mission.Result = result - Mission.CompletedAt = Now() - Update Mission - - # 4. 응답 - Return { - status: "SUCCESS", - message: "미션이 완료되었습니다", - data: { - mission: Mission - } - } -``` - ---- - -## 4. 진행중 미션 조회 - -### 개요 - -**목적**: 사용자의 현재 진행중인 미션 정보 조회 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: 유효한 인증 토큰 - ---- - -### Request (요청) - -**URL**: `/missions/active` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -**진행중 미션이 있는 경우**: -```json -{ - "status": "SUCCESS", - "message": "진행중인 미션을 조회했습니다", - "data": { - "mission": { - "id": "m_abc123", - "type": "taxi", - "status": "InProgress", - "currentStep": 2, - "result": null, - "createdAt": "2026-02-12T14:30:00Z" - }, - "currentStepInfo": { - "stepNumber": 2, - "title": "택시 호출", - "description": "택시 앱을 사용하거나 길거리에서 택시를 잡으세요" - }, - "totalSteps": 3 - } -} -``` - -**진행중 미션이 없는 경우**: -```json -{ - "status": "SUCCESS", - "message": "진행중인 미션이 없습니다", - "data": { - "mission": null - } -} -``` - ---- - -### 수도코드 - -``` -Function 진행중미션조회(userID): - # 1. 진행중 미션 조회 - Mission = Find Mission Where UserID = userID AND Status = MissionStatus.InProgress - - # 2. 미션이 없으면 null 응답 - If Mission is Null: - Return { - status: "SUCCESS", - message: "진행중인 미션이 없습니다", - data: { - mission: null - } - } - - # 3. 현재 단계 정보 조회 - CurrentStepInfo = Find Step Where MissionID = Mission.ID AND StepNumber = Mission.CurrentStep - - # 4. 전체 단계 수 조회 - TotalSteps = Count Steps Where MissionID = Mission.ID - - # 5. 응답 - Return { - status: "SUCCESS", - message: "진행중인 미션을 조회했습니다", - data: { - mission: Mission, - currentStepInfo: CurrentStepInfo, - totalSteps: TotalSteps - } - } -``` - ---- - -## 📝 주요 제약 - -### 동시 진행 제약 - -**규칙**: 사용자는 한 번에 하나의 미션만 "진행중" 상태로 가질 수 있습니다. - -- 새 미션 시작 전 이전 미션을 완료해야 함 -- 미션 완료/취소 없이는 새 미션 시작 불가 - -### 서버 동기화 - -**규칙**: 미션 상태는 실시간으로 서버에 동기화됩니다. - -- 단계 변경 즉시 서버 업데이트 -- 여러 기기에서 미션 이어하기 가능 -- 오프라인 시 큐에 쌓아두고 온라인 시 동기화 - -### 미션 타입 확장 - -**규칙**: 초기 3종(택시/결제/체크인) 외 추가 가능합니다. - -- 미션 타입은 하드코딩이 아닌 DB 관리 -- 향후 "음식주문", "쇼핑", "병원" 등 추가 가능 - ---- - -**missions 도메인 API 완료** ✅ diff --git a/ai-context/domain-books/missions/business-rules.md b/ai-context/domain-books/missions/business-rules.md deleted file mode 100644 index 1461569..0000000 --- a/ai-context/domain-books/missions/business-rules.md +++ /dev/null @@ -1,125 +0,0 @@ -# missions 도메인 비즈니스 규칙 - -## 데이터 규칙 - -### 규칙 1: 미션 타입 제한 -**설명**: 초기 버전은 택시, 결제, 체크인 3가지 미션 타입만 지원합니다. -**예시**: 사용자는 "taxi", "payment", "checkin" 중 하나만 선택할 수 있습니다. -**위반 시**: 다른 타입(예: "shopping")은 "INVALID_INPUT" 오류로 거부됩니다. - ---- - -### 규칙 2: 단계 수 범위 -**설명**: 각 미션은 3-7개의 단계를 가집니다. 택시는 3단계, 결제는 4단계 등으로 미션 타입마다 다릅니다. -**예시**: 택시 미션은 "목적지 설정", "택시 호출", "운전자에게 목적지 전달" 3단계로 구성됩니다. -**위반 시**: 3단계 미만이나 7단계 초과는 허용되지 않습니다. - ---- - -### 규칙 3: 현재 단계 범위 검증 -**설명**: 현재 단계는 1부터 전체 단계 수 사이의 값이어야 합니다. -**예시**: 3단계 미션에서 currentStep은 1, 2, 3 중 하나여야 합니다. -**위반 시**: 0이나 4 이상의 값은 "INVALID_STEP" 오류로 거부됩니다. - ---- - -## 생명주기 규칙 - -### 규칙 4: 한 번에 하나의 미션만 진행 가능 -**설명**: 사용자는 동시에 여러 미션을 진행할 수 없으며, 진행중인 미션을 완료하거나 취소해야 새 미션을 시작할 수 있습니다. -**예시**: 택시 미션이 진행중일 때 결제 미션을 시작하려고 하면 "이미 진행중인 미션이 있습니다" 메시지가 표시됩니다. -**위반 시**: 진행중인 미션이 있으면 새 미션 시작이 차단됩니다. - ---- - -### 규칙 5: 순차 진행 권장 -**설명**: 미션 단계는 순서대로 진행하는 것이 권장되지만, 사용자는 "이전 단계" 버튼으로 자유롭게 이동할 수 있습니다. -**예시**: 2단계에서 3단계로 진행했다가 다시 2단계로 돌아갈 수 있습니다. -**위반 시**: 단계를 건너뛸 수는 없지만, 이전 단계로 돌아가는 것은 허용됩니다. - ---- - -### 규칙 6: 미션 완료 시 결과 필수 -**설명**: 미션을 완료할 때는 반드시 결과(해결/부분해결/미해결)를 선택해야 합니다. -**예시**: 택시 미션을 종료할 때 "해결" 버튼을 눌러야 완료됩니다. -**위반 시**: 결과 없이 완료 시도하면 "INVALID_INPUT" 오류가 발생합니다. - ---- - -### 규칙 7: 취소된 미션은 재개 불가 -**설명**: 한 번 취소된 미션은 다시 진행할 수 없으며, 새 미션을 시작해야 합니다. -**예시**: 결제 미션을 취소하고 나중에 다시 결제 미션을 시작하면 새로운 미션으로 처리됩니다. -**위반 시**: 취소된 미션은 기록으로만 남습니다. - ---- - -## 동기화 규칙 - -### 규칙 8: 서버 실시간 동기화 -**설명**: 미션 상태와 현재 단계는 변경될 때마다 즉시 서버에 저장됩니다. -**예시**: 사용자가 "다음 단계" 버튼을 누르면 currentStep이 서버에 즉시 업데이트됩니다. -**위반 시**: 네트워크 오류 시 변경사항이 큐에 쌓여 나중에 동기화됩니다. - ---- - -### 규칙 9: 여러 기기에서 이어하기 -**설명**: 사용자는 스마트폰에서 시작한 미션을 태블릿이나 다른 기기에서 이어서 진행할 수 있습니다. -**예시**: 스마트폰에서 택시 미션 2단계까지 진행하고, 태블릿으로 접속하면 2단계부터 시작됩니다. -**위반 시**: 동기화되지 않으면 기기별로 미션이 분리될 수 있습니다. - ---- - -## 제약 조건 - -### 규칙 10: 미션 타입 확장 가능 -**설명**: 초기 3종(택시/결제/체크인) 외에 향후 "음식주문", "쇼핑", "병원" 등의 미션을 추가할 수 있습니다. -**예시**: 미션 타입은 하드코딩이 아닌 데이터베이스에서 관리되어 쉽게 확장 가능합니다. -**위반 시**: 코드 수정 없이 새 미션 타입 추가가 가능합니다. - ---- - -### 규칙 11: 단계 템플릿 관리 -**설명**: 각 미션 타입의 단계는 템플릿으로 관리되며, 관리자가 추가하거나 수정할 수 있습니다. -**예시**: 택시 미션에 "팁 주기" 단계를 추가하면, 이후 생성되는 모든 택시 미션에 자동 반영됩니다. -**위반 시**: 이미 생성된 미션에는 영향을 주지 않습니다. - ---- - -## 연계 규칙 - -### 규칙 12: 미션과 번역 기록 연계 -**설명**: 미션 진행 중 번역 기능을 사용하면 번역 기록에 미션 ID가 함께 저장됩니다. -**예시**: 택시 미션 중 "여기서 내려주세요"를 번역하면 해당 번역 기록에 택시 미션 ID가 저장됩니다. -**위반 시**: 미션 없이 번역해도 문제없이 작동합니다. - ---- - -### 규칙 13: 미션과 추천 문장 연계 -**설명**: 각 미션 타입에는 해당 타입에 맞는 추천 문장만 제공됩니다. -**예시**: 택시 미션에서는 "여기서 내려주세요" 같은 택시용 추천 문장만 표시되고, 결제용 추천 문장은 표시되지 않습니다. -**위반 시**: 다른 타입의 추천 문장은 필터링됩니다. - ---- - -### 규칙 14: 미션과 경로 연계 -**설명**: 택시 미션에서 경로를 설정하면 해당 경로에 미션 ID가 저장됩니다. -**예시**: 택시 미션 1단계("목적지 설정")에서 지도로 경로를 검색하면, 경로 정보에 택시 미션 ID가 함께 저장됩니다. -**위반 시**: 경로 없이도 택시 미션은 진행 가능합니다. - ---- - -## 권한 규칙 - -### 규칙 15: 본인 미션만 관리 가능 -**설명**: 사용자는 자신의 미션만 시작, 진행, 완료할 수 있으며, 다른 사용자의 미션은 접근할 수 없습니다. -**예시**: 사용자 A는 자신의 택시 미션만 진행하고, 사용자 B의 미션은 볼 수 없습니다. -**위반 시**: 다른 사용자의 미션 접근 시도는 "UNAUTHORIZED" 오류로 거부됩니다. - ---- - -## 성능 규칙 - -### 규칙 16: 단계 변경 즉시 반영 -**설명**: 사용자가 "다음 단계" 버튼을 누르면 1초 이내에 새 단계가 화면에 반영되어야 합니다. -**예시**: 1단계에서 2단계로 이동할 때 서버 응답이 1초 이내에 완료됩니다. -**위반 시**: 네트워크 지연 시 로딩 스피너가 표시됩니다. diff --git a/ai-context/domain-books/missions/domain-model.md b/ai-context/domain-books/missions/domain-model.md deleted file mode 100644 index 9dc0f10..0000000 --- a/ai-context/domain-books/missions/domain-model.md +++ /dev/null @@ -1,126 +0,0 @@ -# missions 도메인 모델 - -> 생성일: 2026-02-12 -> Phase: 3 (Domain Modeler) -> 상태: ✅ 완료 - ---- - -## 📖 유비쿼터스 언어 (용어 정의) - -> 이 도메인에서 사용하는 전용 용어들 - -### 핵심 용어 - -| 용어 | 정의 | 예시 | -|------|------|------| -| 미션 (Mission) | 사용자가 진행하는 작업 단위 | 택시 타기, 결제하기, 체크인하기 | -| 식별자 (ID) | 미션을 고유하게 구분하는 값 | "m_abc123" | -| 사용자 ID (UserID) | 이 미션을 진행하는 사용자의 식별자 | "u_123abc" | -| 미션 타입 (Type) | 미션의 종류 | "택시", "결제", "체크인" | -| 진행 상태 (Status) | 미션의 현재 상태 | "진행중", "완료", "취소" | -| 현재 단계 (CurrentStep) | 미션의 현재 진행 단계 번호 | 1, 2, 3, ... | -| 완료 결과 (Result) | 미션 완료 시 결과 | "해결", "부분해결", "미해결" | -| 단계 (Step) | 미션의 체크리스트 항목 | "목적지 설정", "택시 호출", ... | - ---- - -## 📐 관계 규칙 - -> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의 - -### 미션과 사용자 - -**규칙 1**: 미션은 반드시 한 명의 사용자에게 속한다. -**규칙 2**: 사용자는 여러 개의 미션을 동시에 또는 순차적으로 진행할 수 있다. -**규칙 3**: 하지만 "진행중" 상태의 미션은 한 번에 하나만 가능하다. - -### 미션과 단계 - -**규칙 4**: 미션은 3~7개의 단계(Step)를 가진다. -**규칙 5**: 단계는 별도 Step 엔티티로 관리된다. -**규칙 6**: 단계는 순서대로 진행되어야 한다. (1단계 → 2단계 → ...) - -### 미션과 번역 - -**규칙 7**: 미션은 여러 개의 번역 기록을 포함할 수 있다. -**규칙 8**: 번역은 미션에 속할 수도 있고, 독립적일 수도 있다. - -### 미션과 추천 문장 - -**규칙 9**: 미션 타입에 따라 미리 정의된 추천 문장(Phrase)이 제공된다. -**규칙 10**: 택시 미션은 택시용 추천 문장, 결제 미션은 결제용 추천 문장을 사용한다. - ---- - -## 🔒 제약 조건 - -> 비즈니스 규칙과 제약사항 - -### 필수 필드 - -- **ID**: 시스템이 자동으로 생성 -- **UserID**: 미션을 진행하는 사용자 (필수) -- **Type**: 미션 타입 (택시/결제/체크인 중 하나) -- **Status**: 진행 상태 (기본값: "진행중") -- **CurrentStep**: 현재 단계 (기본값: 1) - -### 선택 필드 - -- **Result**: 미션 완료 시에만 설정 (해결/부분해결/미해결) - -### 미션 타입 제약 - -**규칙 11**: 초기 버전은 3종류만 지원한다. (택시, 결제, 체크인) -**규칙 12**: 향후 미션 타입은 확장 가능하다. - -### 동시 진행 제약 - -**규칙 13**: 사용자는 한 번에 하나의 미션만 "진행중" 상태로 가질 수 있다. -**규칙 14**: 새 미션을 시작하려면 이전 미션을 먼저 완료해야 한다. - -### 서버 동기화 - -**규칙 15**: 미션 진행 상태는 서버에 실시간으로 동기화된다. -**규칙 16**: 여러 기기에서 미션을 이어서 진행할 수 있다. - ---- - -## 🎯 생명주기 - -### 생성 - -1. 사용자가 미션 카드(택시/결제/체크인)를 선택한다. -2. 시스템은 현재 "진행중" 미션이 있는지 확인한다. -3. 진행중 미션이 없으면 새 Mission을 생성한다. - - Type: 선택한 미션 타입 - - Status: "진행중" - - CurrentStep: 1 -4. 미션 타입에 맞는 Step 엔티티들을 생성한다. (3~7개) -5. 서버에 동기화한다. - -### 진행 - -1. 사용자는 가이드 탭에서 현재 단계의 체크리스트를 확인한다. -2. "다음 단계" 버튼을 누르면 CurrentStep이 증가한다. -3. "이전 단계" 버튼을 누르면 CurrentStep이 감소한다. -4. 각 단계에서 번역 기능을 사용할 수 있다. -5. 단계 변경은 서버에 즉시 동기화된다. - -### 완료 - -1. 사용자가 "미션 종료" 버튼을 누른다. -2. 시스템은 결과 선택 팝업을 표시한다. (해결/부분해결/미해결) -3. 사용자가 결과를 선택한다. -4. Mission의 Status를 "완료"로, Result를 선택한 값으로 변경한다. -5. "진행중인 미션 없음" 상태로 되돌린다. -6. 서버에 동기화한다. - -### 취소 - -**규칙 17**: 사용자는 진행중 미션을 취소할 수 있다. -**규칙 18**: 취소된 미션은 Status가 "취소"로 변경되고 기록은 보관된다. - ---- - -**missions 도메인 완료** ✅ diff --git a/ai-context/domain-books/missions/features.md b/ai-context/domain-books/missions/features.md deleted file mode 100644 index f9d6f86..0000000 --- a/ai-context/domain-books/missions/features.md +++ /dev/null @@ -1,88 +0,0 @@ -# missions 도메인 기능 정의 - -## 기능 1: 미션 시작 - -**설명**: 사용자가 택시, 결제, 체크인 중 하나의 미션 카드를 선택하여 새로운 미션을 시작합니다. 미션 타입에 맞는 3-7개의 단계가 자동으로 생성됩니다. - -**사용자 시나리오**: -1. 사용자가 홈 화면에서 "택시" 미션 카드를 선택합니다. -2. 시스템은 택시 미션을 생성하고 3개의 단계("목적지 설정", "택시 호출", "운전자에게 목적지 전달")를 준비합니다. -3. 현재 진행중인 미션이 있으면 먼저 완료하라는 안내가 표시됩니다. -4. 미션이 시작되면 1단계부터 가이드가 표시됩니다. - -**관련 API**: -- 미션 시작 (api-spec.md 참조) - ---- - -## 기능 2: 단계별 진행 - -**설명**: 사용자는 가이드 탭에서 현재 단계의 체크리스트를 확인하고, "다음 단계" 또는 "이전 단계" 버튼으로 진행할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션 1단계("목적지 설정")를 확인합니다. -2. 지도에서 목적지를 설정한 후 "다음 단계" 버튼을 누릅니다. -3. 2단계("택시 호출")로 이동하고, 해당 단계의 가이드가 표시됩니다. -4. 필요하면 "이전 단계" 버튼으로 1단계로 돌아갈 수 있습니다. - -**관련 API**: -- 미션 단계 변경 (api-spec.md 참조) - ---- - -## 기능 3: 미션 완료 - -**설명**: 사용자가 모든 단계를 완료한 후 "미션 종료" 버튼을 누르면, 미션 결과(해결/부분해결/미해결)를 선택하여 미션을 완료합니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션의 마지막 단계까지 진행합니다. -2. "미션 종료" 버튼을 누르면 결과 선택 팝업이 표시됩니다. -3. "해결" 버튼을 선택하면 미션이 완료되고 기록이 저장됩니다. -4. 홈 화면으로 돌아가면 "진행중인 미션 없음" 상태가 됩니다. - -**관련 API**: -- 미션 완료 (api-spec.md 참조) - ---- - -## 기능 4: 진행중 미션 조회 - -**설명**: 사용자가 앱을 재실행하거나 다른 기기에서 접속해도 진행중인 미션을 이어서 진행할 수 있습니다. 서버에 실시간으로 동기화됩니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션 2단계까지 진행하고 앱을 종료합니다. -2. 나중에 앱을 다시 실행하면 홈 화면에 "진행중인 택시 미션(2/3 단계)" 배지가 표시됩니다. -3. 가이드 탭을 열면 2단계부터 이어서 진행할 수 있습니다. - -**관련 API**: -- 진행중 미션 조회 (api-spec.md 참조) - ---- - -## 기능 5: 미션 취소 - -**설명**: 사용자는 진행중인 미션을 취소할 수 있습니다. 취소된 미션은 기록으로 보관되지만 더 이상 진행할 수 없습니다. - -**사용자 시나리오**: -1. 사용자가 결제 미션을 시작했지만 현금 결제로 해결했습니다. -2. 가이드 탭에서 "미션 취소" 버튼을 누릅니다. -3. 확인 팝업에서 "취소" 버튼을 누르면 미션이 취소됩니다. -4. 새로운 미션을 시작할 수 있습니다. - -**관련 API**: -- 미션 완료 (status를 "Cancelled"로 설정, api-spec.md 참조) - ---- - -## 기능 6: 미션별 추천 문장 활용 - -**설명**: 각 미션 타입에 맞는 추천 문장이 자동으로 제공되어, 사용자가 빠르게 번역 없이 의사소통할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션 3단계("운전자에게 목적지 전달")에 도달합니다. -2. 화면 하단에 "여기서 내려주세요", "얼마예요?" 등의 추천 문장 카드가 표시됩니다. -3. 사용자가 "여기서 내려주세요" 카드를 누르면 큰 글씨로 한/영 텍스트가 표시됩니다. -4. "읽어주기" 버튼으로 미리 녹음된 음성을 재생할 수 있습니다. - -**관련 API**: -- phrases 도메인의 추천 문장 조회 API 참조 diff --git a/ai-context/domain-books/phrases/README.md b/ai-context/domain-books/phrases/README.md deleted file mode 100644 index 7c83dcf..0000000 --- a/ai-context/domain-books/phrases/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# phrases 도메인 - -> 미션별 추천 문장 템플릿 제공 - -## 📖 목차 - -1. [기능 정의](./features.md) -2. [도메인 모델](./domain-model.md) -3. [API 명세](./api-spec.md) -4. [비즈니스 규칙](./business-rules.md) - -## 📝 개요 - -phrases 도메인은 여행자가 자주 사용하는 문장을 미리 준비하여 빠르게 의사소통할 수 있도록 돕습니다. 택시, 결제, 체크인 등 미션 타입별로 3-5개의 추천 문장이 제공되며, 한국어와 영어 번역이 미리 저장되어 있고 음성 파일도 함께 제공됩니다. - -## 🎯 핵심 기능 - -- 미션 타입별 추천 문장 조회 -- 추천 문장 카드 표시 -- 미리 녹음된 음성 재생 -- 텍스트 복사 기능 - -## 📊 주요 엔티티 - -- **Phrase**: 추천 문장 (한국어 텍스트, 영어 텍스트, 분류, 음성 URL) -- **MissionType**: 미션 타입 (택시, 결제, 체크인) -- **PhraseCategory**: 문장 카테고리 (인사, 요청, 질문, 응답, 긴급) - -## 🔗 도메인 의존성 - -- **의존하는 도메인**: 없음 (독립 도메인) -- **이 도메인에 의존하는 도메인**: missions (미션에서 추천 문장 참조) diff --git a/ai-context/domain-books/phrases/api-spec.md b/ai-context/domain-books/phrases/api-spec.md deleted file mode 100644 index ca891f9..0000000 --- a/ai-context/domain-books/phrases/api-spec.md +++ /dev/null @@ -1,346 +0,0 @@ -# phrases 도메인 API 명세 - -> 생성일: 2026-02-12 -> Phase: 4 (API Designer) -> 상태: ✅ 완료 - ---- - -## 📋 ENUM 정의 - -### MissionType - -미션 타입 (missions 도메인과 동일): -- `taxi`: 택시 이용 미션 -- `payment`: 결제 미션 -- `checkin`: 체크인 미션 - -### PhraseCategory - -추천 문장 카테고리: -- `greeting`: 인사 -- `request`: 요청 -- `question`: 질문 -- `response`: 응답 -- `emergency`: 긴급 - ---- - -## 📡 API 목록 - -| API | 설명 | 중요도 | -|-----|------|:------:| -| 미션 타입별 추천 문장 조회 | 택시/결제/체크인 추천 문장 목록 | 🔥 필수 | - -**참고**: 추천 문장 생성/수정/삭제는 관리자 전용 API입니다. 사용자는 조회만 가능합니다. - ---- - -## 1. 미션 타입별 추천 문장 조회 - -### 개요 - -**목적**: 사용자가 미션 진행 시 해당 미션 타입의 추천 문장 목록 제공 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 유효한 미션 타입 (택시/결제/체크인) -- 해당 타입의 추천 문장이 3-5개 이상 존재 - ---- - -### Request (요청) - -**URL**: `/phrases?missionType={missionType}` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - -**Query Parameters**: - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| missionType | **MissionType** (ENUM) | ✅ | 미션 타입 | "taxi", "payment", "checkin" | - -**예시**: -``` -GET /phrases?missionType=taxi -GET /phrases?missionType=payment -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -**택시 미션**: -```json -{ - "status": "SUCCESS", - "message": "추천 문장을 조회했습니다", - "data": { - "missionType": "taxi", - "phrases": [ - { - "id": "p_abc123", - "koreanText": "여기서 내려주세요", - "englishText": "Please drop me off here", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_abc123.mp3" - }, - { - "id": "p_def456", - "koreanText": "얼마예요?", - "englishText": "How much is it?", - "category": "question", - "audioUrl": "https://storage.cloud.com/phrase_def456.mp3" - }, - { - "id": "p_ghi789", - "koreanText": "영수증 주세요", - "englishText": "Receipt, please", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_ghi789.mp3" - } - ], - "total": 3 - } -} -``` - -**결제 미션**: -```json -{ - "status": "SUCCESS", - "message": "추천 문장을 조회했습니다", - "data": { - "missionType": "payment", - "phrases": [ - { - "id": "p_jkl012", - "koreanText": "카드 되나요?", - "englishText": "Do you accept cards?", - "category": "question", - "audioUrl": "https://storage.cloud.com/phrase_jkl012.mp3" - }, - { - "id": "p_mno345", - "koreanText": "현금으로 할게요", - "englishText": "I'll pay with cash", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_mno345.mp3" - }, - { - "id": "p_pqr678", - "koreanText": "영수증 주세요", - "englishText": "Receipt, please", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_pqr678.mp3" - } - ], - "total": 3 - } -} -``` - -**체크인 미션**: -```json -{ - "status": "SUCCESS", - "message": "추천 문장을 조회했습니다", - "data": { - "missionType": "checkin", - "phrases": [ - { - "id": "p_stu901", - "koreanText": "체크인하려고 하는데요", - "englishText": "I'd like to check in", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_stu901.mp3" - }, - { - "id": "p_vwx234", - "koreanText": "예약 확인 부탁드려요", - "englishText": "Can you confirm my reservation?", - "category": "question", - "audioUrl": "https://storage.cloud.com/phrase_vwx234.mp3" - }, - { - "id": "p_yz567", - "koreanText": "방 열쇠 주세요", - "englishText": "Room key, please", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_yz567.mp3" - } - ], - "total": 3 - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**잘못된 미션 타입**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 미션 타입입니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 추천문장조회(missionType): - # 1. ENUM 검증 - If missionType NOT IN [MissionType.taxi, MissionType.payment, MissionType.checkin]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 미션 타입입니다", - data: null - } - - # 2. 추천 문장 조회 - Phrases = Find All Phrases Where MissionType = missionType - - # 3. 응답 - Return { - status: "SUCCESS", - message: "추천 문장을 조회했습니다", - data: { - missionType: missionType, - phrases: Phrases, - total: Count(Phrases) - } - } -``` - ---- - -## 📝 추천 문장 사용 시나리오 - -### 사용자 흐름 - -1. **미션 시작**: - - 사용자가 택시 미션 시작 - - 시스템이 택시용 추천 문장 3-5개 자동 조회 - -2. **추천 문장 카드 표시**: - - 미션 화면 하단에 추천 문장 카드 목록 표시 - - 각 카드에 한국어/영어 텍스트 미리보기 - -3. **카드 선택**: - - 사용자가 "여기서 내려주세요" 카드 선택 - - 전체 화면으로 확대되어 큰 글씨로 표시 - - 한국어(위) / 영어(아래) 동시 표시 - -4. **음성 재생**: - - "읽어주기" 버튼 → audioUrl 재생 - - 미리 생성된 TTS 음성으로 빠른 응답 - -5. **복사 기능**: - - "복사" 버튼 → 클립보드에 텍스트 복사 - - 다른 앱에 붙여넣기 가능 - ---- - -## 📝 주요 제약 - -### 조회 전용 - -**규칙**: 일반 사용자는 추천 문장을 조회만 가능합니다. - -- 생성/수정/삭제는 관리자 전용 API -- 사용자는 미리 준비된 템플릿만 사용 - -### 미리 생성된 음성 - -**규칙**: 추천 문장의 음성은 미리 TTS로 생성되어 저장됩니다. - -- 실시간 TTS 호출 없음 (빠른 응답) -- audioUrl이 null인 경우 클라이언트에서 TTS 호출 - -### 다국어 확장 - -**규칙**: 한국어/영어는 DB에 미리 저장되어 있습니다. - -- 향후 일본어/중국어 추가 시 새 필드 추가 (JapaneseText, ChineseText) -- 또는 별도 Translation 테이블 연결 - -### Translation 기록 없음 - -**규칙**: 추천 문장 사용은 Translation 기록을 생성하지 않습니다. - -- 사용자가 직접 입력/녹음한 번역만 기록 -- 추천 문장은 템플릿이므로 기록 불필요 -- 필요 시 별도 PhraseUsageLog 테이블 생성 가능 - ---- - -## 🔧 관리자 전용 API (참고) - -### 추천 문장 생성 - -**URL**: `POST /admin/phrases` - -**Request**: -```json -{ - "missionType": "taxi", - "koreanText": "여기서 내려주세요", - "englishText": "Please drop me off here", - "category": "request" -} -``` - -**Response**: -```json -{ - "status": "SUCCESS", - "message": "추천 문장이 생성되었습니다", - "data": { - "id": "p_abc123", - "missionType": "taxi", - "koreanText": "여기서 내려주세요", - "englishText": "Please drop me off here", - "category": "request", - "audioUrl": "https://storage.cloud.com/phrase_abc123.mp3" - } -} -``` - -**프로세스**: -1. 관리자가 새 추천 문장 입력 -2. 시스템이 TTS API로 음성 생성 (한국어/영어 각각) -3. 음성 파일을 Cloud Storage에 업로드 -4. Phrase 엔티티 저장 - -### 추천 문장 수정 - -**URL**: `PUT /admin/phrases/{phraseId}` - -### 추천 문장 삭제 - -**URL**: `DELETE /admin/phrases/{phraseId}` - ---- - -**phrases 도메인 API 완료** ✅ diff --git a/ai-context/domain-books/phrases/business-rules.md b/ai-context/domain-books/phrases/business-rules.md deleted file mode 100644 index f74d996..0000000 --- a/ai-context/domain-books/phrases/business-rules.md +++ /dev/null @@ -1,125 +0,0 @@ -# phrases 도메인 비즈니스 규칙 - -## 데이터 규칙 - -### 규칙 1: 미션 타입별 그룹화 -**설명**: 추천 문장은 반드시 미션 타입(택시/결제/체크인) 중 하나에 속해야 합니다. -**예시**: "여기서 내려주세요"는 택시 타입, "카드 되나요?"는 결제 타입에 속합니다. -**위반 시**: 미션 타입이 없는 추천 문장은 생성할 수 없습니다. - ---- - -### 규칙 2: 한국어와 영어 필수 -**설명**: 모든 추천 문장은 한국어와 영어 번역이 반드시 포함되어야 합니다. -**예시**: "여기서 내려주세요" (한국어)와 "Please drop me off here" (영어)가 함께 저장됩니다. -**위반 시**: 한국어나 영어가 없으면 추천 문장 생성이 거부됩니다. - ---- - -### 규칙 3: 음성 파일 선택 제공 -**설명**: 음성 파일(TTS)은 선택 사항이지만, 미리 생성하면 성능이 향상됩니다. -**예시**: 관리자가 추천 문장을 생성할 때 TTS API로 음성을 미리 만들어 Cloud Storage에 저장합니다. -**위반 시**: 음성이 없으면 클라이언트에서 실시간 TTS를 호출합니다. - ---- - -### 규칙 4: 카테고리 분류 -**설명**: 추천 문장은 인사, 요청, 질문, 응답, 긴급 등의 카테고리로 분류됩니다. -**예시**: "안녕하세요"는 인사, "여기서 내려주세요"는 요청, "얼마예요?"는 질문 카테고리입니다. -**위반 시**: 카테고리가 없어도 문장은 정상 작동하지만 분류 기능이 제한됩니다. - ---- - -## 생명주기 규칙 - -### 규칙 5: 관리자만 생성/수정/삭제 가능 -**설명**: 추천 문장은 일반 사용자가 추가하거나 변경할 수 없으며, 관리자만 관리할 수 있습니다. -**예시**: 관리자가 관리자 페이지에서 새 추천 문장 "방 열쇠 주세요"를 추가하면 모든 사용자가 사용할 수 있습니다. -**위반 시**: 일반 사용자의 생성/수정 시도는 "FORBIDDEN" 오류로 거부됩니다. - ---- - -### 규칙 6: 수정 시 음성 재생성 -**설명**: 추천 문장의 텍스트를 수정하면 음성 파일도 자동으로 재생성됩니다. -**예시**: "여기서 내려주세요"를 "여기에서 내려주세요"로 수정하면 TTS API로 새 음성 파일을 생성합니다. -**위반 시**: 음성이 재생성되지 않으면 텍스트와 음성이 불일치할 수 있습니다. - ---- - -### 규칙 7: 삭제 시 음성 파일도 제거 -**설명**: 추천 문장을 삭제하면 연결된 음성 파일도 Cloud Storage에서 함께 삭제됩니다. -**예시**: 더 이상 사용하지 않는 "공중전화 어디 있나요?" 문장을 삭제하면 음성 파일도 자동 삭제됩니다. -**위반 시**: 음성 파일이 남아있으면 저장 공간이 낭비됩니다. - ---- - -## 제약 조건 - -### 규칙 8: 미션 타입당 3-5개 권장 -**설명**: 각 미션 타입당 3-5개의 추천 문장을 제공하는 것이 권장됩니다. 너무 많으면 선택이 어렵고, 너무 적으면 활용도가 낮습니다. -**예시**: 택시 미션에는 "여기서 내려주세요", "얼마예요?", "영수증 주세요" 3개가 제공됩니다. -**위반 시**: 3개 미만이거나 5개 초과도 허용되지만 사용성이 떨어질 수 있습니다. - ---- - -### 규칙 9: 다국어 확장 가능 -**설명**: 한국어/영어는 DB에 미리 저장하지만, 향후 일본어/중국어 추가 시 새 필드를 추가할 수 있습니다. -**예시**: JapaneseText, ChineseText 필드를 추가하거나 별도 Translation 테이블로 관리할 수 있습니다. -**위반 시**: 초기 버전은 한국어/영어만 지원합니다. - ---- - -## 성능 규칙 - -### 규칙 10: 음성 파일 미리 생성 -**설명**: 추천 문장의 음성은 미리 TTS로 생성하여 저장하므로 실시간 TTS 호출이 없어 빠릅니다. -**예시**: "여기서 내려주세요" 음성은 관리자가 추천 문장을 생성할 때 미리 만들어져 Cloud Storage에 저장됩니다. -**위반 시**: 음성이 없으면 클라이언트에서 실시간 TTS를 호출하여 응답이 느려집니다. - ---- - -### 규칙 11: 음성 파일 캐싱 -**설명**: 음성 파일은 클라이언트에 캐싱되어 반복 재생 시 네트워크 요청이 없습니다. -**예시**: "여기서 내려주세요" 음성을 한 번 다운로드하면 앱이 종료될 때까지 캐시에서 재생됩니다. -**위반 시**: 캐싱 없이 매번 다운로드하면 데이터 사용량이 증가합니다. - ---- - -## 연계 규칙 - -### 규칙 12: 미션과 추천 문장 연계 -**설명**: 미션 타입에 따라 해당 타입의 추천 문장만 제공됩니다. 택시 미션에는 택시용 추천 문장만 표시됩니다. -**예시**: 택시 미션 중에는 "여기서 내려주세요"가 보이지만, "카드 되나요?"는 보이지 않습니다. -**위반 시**: 다른 타입의 추천 문장은 필터링됩니다. - ---- - -### 규칙 13: 번역 기록 생성 없음 -**설명**: 추천 문장을 사용해도 번역 기록(Translation)이 생성되지 않습니다. 추천 문장은 템플릿이므로 기록할 필요가 없습니다. -**예시**: 사용자가 "여기서 내려주세요" 카드를 10번 사용해도 번역 기록은 생성되지 않습니다. -**위반 시**: 필요 시 별도 PhraseUsageLog 테이블을 생성하여 사용 통계를 수집할 수 있습니다. - ---- - -## 권한 규칙 - -### 규칙 14: 일반 사용자는 조회만 가능 -**설명**: 일반 사용자는 추천 문장을 조회하고 사용할 수만 있으며, 추가/수정/삭제는 불가능합니다. -**예시**: 사용자는 택시 미션에서 제공되는 추천 문장을 보고 선택할 수만 있습니다. -**위반 시**: 생성/수정/삭제 시도는 "FORBIDDEN" 오류로 거부됩니다. - ---- - -### 규칙 15: 관리자 전용 API -**설명**: 추천 문장의 생성, 수정, 삭제는 관리자 전용 API를 통해서만 가능합니다. -**예시**: 관리자는 `/admin/phrases` API로 새 추천 문장을 추가하거나 기존 문장을 수정할 수 있습니다. -**위반 시**: 일반 사용자는 이 API에 접근할 수 없습니다. - ---- - -## 확장 규칙 - -### 규칙 16: 단계별 추천 문장 -**설명**: 향후 미션의 각 단계별로 서로 다른 추천 문장을 제공할 수 있습니다. -**예시**: 택시 미션 1단계(목적지 설정)에는 "어디로 가나요?", 3단계(하차)에는 "여기서 내려주세요"를 제공합니다. -**위반 시**: 초기 버전은 미션 타입별로만 구분하고 단계별 구분은 없습니다. diff --git a/ai-context/domain-books/phrases/domain-model.md b/ai-context/domain-books/phrases/domain-model.md deleted file mode 100644 index 1dab7d4..0000000 --- a/ai-context/domain-books/phrases/domain-model.md +++ /dev/null @@ -1,146 +0,0 @@ -# phrases 도메인 모델 - -> 생성일: 2026-02-12 -> Phase: 3 (Domain Modeler) -> 상태: ✅ 완료 - ---- - -## 📖 유비쿼터스 언어 (용어 정의) - -> 이 도메인에서 사용하는 전용 용어들 - -### 핵심 용어 - -| 용어 | 정의 | 예시 | -|------|------|------| -| 추천 문장 (Phrase) | 미리 준비된 번역 문장 템플릿 | "카드 되나요?", "영수증 주세요" | -| 식별자 (ID) | 추천 문장을 고유하게 구분하는 값 | "p_abc123" | -| 미션 타입 (MissionType) | 이 문장이 속한 미션의 종류 | "택시", "결제", "체크인" | -| 한국어 텍스트 (KoreanText) | 한국어로 미리 저장된 문장 | "여기서 내려주세요" | -| 영어 텍스트 (EnglishText) | 영어로 미리 저장된 번역 | "Please drop me off here" | -| 분류 태그 (Category) | 문장의 용도나 상황 분류 | "인사", "요청", "질문" | -| 음성 URL (AudioUrl) | 미리 생성된 TTS 음성 파일 경로 | "https://storage.../phrase_123.mp3" | - ---- - -## 📐 관계 규칙 - -> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의 - -### 추천 문장과 미션 타입 - -**규칙 1**: 추천 문장은 미션 타입별로 그룹화된다. -**규칙 2**: 택시 미션은 택시용 추천 문장만 보여준다. -**규칙 3**: 결제 미션은 결제용 추천 문장만 보여준다. -**규칙 4**: 체크인 미션은 체크인용 추천 문장만 보여준다. - -### 추천 문장과 미션 - -**규칙 5**: 미션은 해당 타입의 추천 문장 3-5개를 참조한다. -**규칙 6**: 추천 문장은 여러 미션에서 재사용된다. (템플릿) - -### 추천 문장과 번역 - -**규칙 7**: 사용자가 추천 문장 카드를 누르면, 미리 저장된 번역과 음성이 즉시 제공된다. -**규칙 8**: 추천 문장 사용은 새로운 Translation 기록을 생성하지 않는다. (선택 사항) - ---- - -## 🔒 제약 조건 - -> 비즈니스 규칙과 제약사항 - -### 필수 필드 - -- **ID**: 시스템이 자동으로 생성 -- **MissionType**: 택시/결제/체크인 중 하나 -- **KoreanText**: 한국어 문장 (필수) -- **EnglishText**: 영어 번역 (필수) - -### 선택 필드 - -- **Category**: 분류 태그 (선택) -- **AudioUrl**: TTS 음성 파일 (미리 생성하면 성능 향상) - -### 관리 권한 - -**규칙 9**: 추천 문장은 관리자만 추가/수정/삭제할 수 있다. -**규칙 10**: 일반 사용자는 추천 문장을 조회만 가능하다. - -### 미션 타입당 수량 - -**규칙 11**: 각 미션 타입당 3-5개의 추천 문장을 제공한다. -**규칙 12**: 향후 추천 문장은 단계별로 세분화할 수 있다. - -### 다국어 처리 - -**규칙 13**: 한국어와 영어는 미리 DB에 저장한다. -**규칙 14**: 향후 다른 언어 지원 시 실시간 번역 API를 사용한다. - ---- - -## 🎯 생명주기 - -### 생성 (관리자만) - -1. 관리자가 새 추천 문장을 작성한다. - - MissionType 선택 (택시/결제/체크인) - - KoreanText 입력 - - EnglishText 입력 - - Category 설정 (선택) -2. 시스템은 TTS API로 음성 파일을 미리 생성한다. -3. 음성 파일을 Cloud Storage에 업로드하고 URL을 받는다. -4. Phrase를 저장한다. - -### 조회 (사용자) - -1. 사용자가 미션을 시작한다. (예: 택시) -2. 시스템은 택시 타입의 추천 문장 3-5개를 조회한다. -3. 사용자는 추천 문장 카드 목록을 본다. -4. 사용자가 카드를 누르면: - - 큰 글씨로 한/영 텍스트 표시 - - "읽어주기" 버튼 → AudioUrl 재생 - - "복사" 버튼 → 클립보드 복사 - -### 수정 (관리자만) - -**규칙 15**: 추천 문장은 수정 가능하다. -**규칙 16**: 수정 시 TTS 음성 파일도 재생성한다. - -### 삭제 (관리자만) - -**규칙 17**: 추천 문장은 삭제 가능하다. -**규칙 18**: 삭제 시 연결된 음성 파일도 함께 삭제한다. - ---- - -## 📋 추천 문장 예시 - -### 택시 미션 (3-5개) - -| KoreanText | EnglishText | Category | -|------------|-------------|----------| -| 여기서 내려주세요 | Please drop me off here | 요청 | -| 얼마예요? | How much is it? | 질문 | -| 영수증 주세요 | Receipt, please | 요청 | - -### 결제 미션 (3-5개) - -| KoreanText | EnglishText | Category | -|------------|-------------|----------| -| 카드 되나요? | Do you accept cards? | 질문 | -| 현금으로 할게요 | I'll pay with cash | 요청 | -| 영수증 주세요 | Receipt, please | 요청 | - -### 체크인 미션 (3-5개) - -| KoreanText | EnglishText | Category | -|------------|-------------|----------| -| 체크인하려고 하는데요 | I'd like to check in | 요청 | -| 예약 확인 부탁드려요 | Can you confirm my reservation? | 질문 | -| 방 열쇠 주세요 | Room key, please | 요청 | - ---- - -**phrases 도메인 완료** ✅ diff --git a/ai-context/domain-books/phrases/features.md b/ai-context/domain-books/phrases/features.md deleted file mode 100644 index fccd087..0000000 --- a/ai-context/domain-books/phrases/features.md +++ /dev/null @@ -1,72 +0,0 @@ -# phrases 도메인 기능 정의 - -## 기능 1: 미션 타입별 추천 문장 조회 - -**설명**: 사용자가 미션을 시작하면 해당 미션 타입에 맞는 추천 문장 목록이 자동으로 제공됩니다. 택시 미션에는 택시용 문장, 결제 미션에는 결제용 문장이 표시됩니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션을 시작합니다. -2. 시스템은 택시 타입의 추천 문장 3-5개를 서버에서 가져옵니다. -3. 미션 화면 하단에 "여기서 내려주세요", "얼마예요?", "영수증 주세요" 등의 카드가 표시됩니다. - -**관련 API**: -- 미션 타입별 추천 문장 조회 (api-spec.md 참조) - ---- - -## 기능 2: 추천 문장 카드 표시 - -**설명**: 추천 문장은 카드 형태로 표시되며, 한국어와 영어 텍스트가 함께 미리보기로 보입니다. 사용자가 카드를 선택하면 전체 화면으로 확대됩니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션 진행 중 추천 문장 목록을 확인합니다. -2. "여기서 내려주세요" 카드를 선택합니다. -3. 전체 화면으로 큰 글씨로 "여기서 내려주세요"(상단)와 "Please drop me off here"(하단)가 표시됩니다. -4. 운전사에게 화면을 보여주어 의사소통합니다. - -**관련 API**: -- 미션 타입별 추천 문장 조회 (api-spec.md 참조) - ---- - -## 기능 3: 음성 재생 - -**설명**: 추천 문장에는 미리 녹음된 TTS 음성이 포함되어 있어, "읽어주기" 버튼을 누르면 즉시 재생됩니다. 실시간 TTS 호출이 없어 빠르게 응답합니다. - -**사용자 시나리오**: -1. 사용자가 "여기서 내려주세요" 카드를 선택합니다. -2. 화면 하단의 "읽어주기" 버튼을 누릅니다. -3. 미리 녹음된 음성이 즉시 재생되어 운전사에게 들려줄 수 있습니다. -4. 버튼을 다시 누르면 반복 재생됩니다. - -**관련 API**: -- 미션 타입별 추천 문장 조회 (audioUrl 필드 사용, api-spec.md 참조) - ---- - -## 기능 4: 텍스트 복사 - -**설명**: 사용자는 추천 문장을 클립보드에 복사하여 다른 앱(메신저, 메모 등)에 붙여넣을 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 "카드 되나요?" 카드를 선택합니다. -2. 화면 하단의 "복사" 버튼을 누릅니다. -3. "Do you accept cards?"가 클립보드에 복사됩니다. -4. 메신저 앱으로 전환하여 붙여넣기로 전달할 수 있습니다. - -**관련 API**: -- 미션 타입별 추천 문장 조회 (api-spec.md 참조) - ---- - -## 기능 5: 카테고리별 분류 - -**설명**: 추천 문장은 인사, 요청, 질문, 응답, 긴급 등의 카테고리로 분류되어 있어, 상황에 맞는 문장을 빠르게 찾을 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 체크인 미션을 진행합니다. -2. 추천 문장 목록에서 "질문" 카테고리의 "예약 확인 부탁드려요"를 찾습니다. -3. 카테고리 아이콘이나 색상으로 쉽게 구분할 수 있습니다. - -**관련 API**: -- 미션 타입별 추천 문장 조회 (category 필드 사용, api-spec.md 참조) diff --git a/ai-context/domain-books/translations/README.md b/ai-context/domain-books/translations/README.md deleted file mode 100644 index 5a1af6f..0000000 --- a/ai-context/domain-books/translations/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# translations 도메인 - -> 텍스트 및 음성 번역 기록 관리 - -## 📖 목차 - -1. [기능 정의](./features.md) -2. [도메인 모델](./domain-model.md) -3. [API 명세](./api-spec.md) -4. [비즈니스 규칙](./business-rules.md) - -## 📝 개요 - -translations 도메인은 여행자가 현지에서 의사소통할 수 있도록 텍스트와 음성 번역을 제공하고 기록을 저장합니다. 사용자는 직접 텍스트를 입력하거나 음성을 녹음하여 번역할 수 있으며, 모든 번역 기록은 영구 저장되어 나중에 다시 확인할 수 있습니다. - -## 🎯 핵심 기능 - -- 텍스트 번역 및 저장 -- 음성 번역 (PTT 방식) -- 번역 기록 조회 -- 미션별 번역 기록 필터링 - -## 📊 주요 엔티티 - -- **Translation**: 번역 기록 (원문, 번역문, 음성 파일, 생성 시각) -- **LanguageCode**: 언어 코드 (한국어, 영어, 일본어, 중국어) - -## 🔗 도메인 의존성 - -- **의존하는 도메인**: users (사용자 정보), missions (미션 정보) -- **이 도메인에 의존하는 도메인**: 없음 diff --git a/ai-context/domain-books/translations/api-spec.md b/ai-context/domain-books/translations/api-spec.md deleted file mode 100644 index 76d4f25..0000000 --- a/ai-context/domain-books/translations/api-spec.md +++ /dev/null @@ -1,469 +0,0 @@ -# translations 도메인 API 명세 - -> 생성일: 2026-02-12 -> Phase: 4 (API Designer) -> 상태: ✅ 완료 - ---- - -## 📋 ENUM 정의 - -### LanguageCode - -언어 코드: -- `ko`: 한국어 -- `en`: 영어 -- `ja`: 일본어 (확장) -- `zh`: 중국어 (확장) - -**참고**: PreferredLanguage ENUM은 users 도메인에서 정의되어 있습니다. - ---- - -## 📡 API 목록 - -| API | 설명 | 중요도 | -|-----|------|:------:| -| 텍스트 번역 생성 | 텍스트 번역 및 저장 | 🔥 필수 | -| 음성 번역 생성 | 음성 → 텍스트 → 번역 | 🔥 필수 | -| 번역 기록 조회 | 사용자의 번역 기록 목록 | ⭐ 중요 | - ---- - -## 1. 텍스트 번역 생성 - -### 개요 - -**목적**: 사용자가 입력한 텍스트를 번역하고 서버에 저장 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 원문 텍스트 입력 -- 번역 결과를 영구 저장 - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| sourceText | 문자열 | ✅ | 번역할 원문 텍스트 | "안녕하세요" | -| sourceLanguage | **LanguageCode** (ENUM) | ✅ | 원문 언어 코드 | "ko" | -| targetLanguage | **LanguageCode** (ENUM) | ✅ | 번역할 언어 코드 | "en" | -| missionId | 문자열 | ❌ | 미션 진행 중이면 미션 ID | "m_abc123" | - -**예시**: -```json -{ - "sourceText": "안녕하세요", - "sourceLanguage": "ko", - "targetLanguage": "en", - "missionId": null -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "번역이 완료되었습니다", - "data": { - "id": "t_abc123", - "sourceText": "안녕하세요", - "targetText": "Hello", - "audioFileUrl": null, - "createdAt": "2026-02-12T14:30:00Z" - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**잘못된 입력**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 언어 코드입니다", - "data": null -} -``` - -**번역 서비스 오류**: -```json -{ - "status": "TRANSLATION_ERROR", - "message": "번역 서비스 오류가 발생했습니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 텍스트번역생성(userID, sourceText, sourceLanguage, targetLanguage, missionId): - # 1. ENUM 검증 - If sourceLanguage NOT IN [LanguageCode.ko, LanguageCode.en, LanguageCode.ja, LanguageCode.zh]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 언어 코드입니다", - data: null - } - - If targetLanguage NOT IN [LanguageCode.ko, LanguageCode.en, LanguageCode.ja, LanguageCode.zh]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 언어 코드입니다", - data: null - } - - # 2. 번역 API 호출 - targetText = Call 번역_API(sourceText, sourceLanguage, targetLanguage) - If targetText is Null: - Return { - status: "TRANSLATION_ERROR", - message: "번역 서비스 오류가 발생했습니다", - data: null - } - - # 3. Translation 엔티티 생성 - Translation = Create Translation { - userID: userID, - sourceText: sourceText, - targetText: targetText, - audioFileUrl: null, - missionId: missionId, - createdAt: Now() - } - - # 4. 저장 및 응답 - Save Translation - Return { - status: "SUCCESS", - message: "번역이 완료되었습니다", - data: Translation - } -``` - ---- - -## 2. 음성 번역 생성 - -### 개요 - -**목적**: 사용자가 녹음한 음성을 텍스트로 변환하고 번역한 후 서버에 저장 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 음성 파일 (Base64 인코딩 또는 파일 업로드) -- STT → 번역 → 음성 파일 저장 - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| audioFile | 문자열 (Base64) | ✅ | 녹음된 음성 파일 | "data:audio/wav;base64,UklGRi..." | -| sourceLanguage | **LanguageCode** (ENUM) | ✅ | 원문 언어 코드 | "ko" | -| targetLanguage | **LanguageCode** (ENUM) | ✅ | 번역할 언어 코드 | "en" | -| missionId | 문자열 | ❌ | 미션 진행 중이면 미션 ID | "m_abc123" | - -**예시**: -```json -{ - "audioFile": "data:audio/wav;base64,UklGRiQAAABXQVZF...", - "sourceLanguage": "ko", - "targetLanguage": "en", - "missionId": "m_abc123" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "음성 번역이 완료되었습니다", - "data": { - "id": "t_xyz789", - "sourceText": "여기서 내려주세요", - "targetText": "Please drop me off here", - "audioFileUrl": "https://storage.cloud.com/audio_xyz789.wav", - "createdAt": "2026-02-12T14:35:00Z" - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**STT 실패**: -```json -{ - "status": "STT_ERROR", - "message": "음성을 인식할 수 없습니다. 다시 시도해주세요.", - "data": null -} -``` - -**번역 실패**: -```json -{ - "status": "TRANSLATION_ERROR", - "message": "번역 서비스 오류가 발생했습니다", - "data": null -} -``` - -**잘못된 입력**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 언어 코드입니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 음성번역생성(userID, audioFile, sourceLanguage, targetLanguage, missionId): - # 1. ENUM 검증 - If sourceLanguage NOT IN [LanguageCode.ko, LanguageCode.en, LanguageCode.ja, LanguageCode.zh]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 언어 코드입니다", - data: null - } - - If targetLanguage NOT IN [LanguageCode.ko, LanguageCode.en, LanguageCode.ja, LanguageCode.zh]: - Return { - status: "INVALID_INPUT", - message: "유효하지 않은 언어 코드입니다", - data: null - } - - # 2. 음성 파일을 임시 저장소에 업로드 - tempAudioUrl = Upload_To_Storage(audioFile) - - # 3. STT API 호출 (음성 → 텍스트) - sourceText = Call STT_API(tempAudioUrl, sourceLanguage) - If sourceText is Null: - Return { - status: "STT_ERROR", - message: "음성을 인식할 수 없습니다. 다시 시도해주세요.", - data: null - } - - # 4. 번역 API 호출 - targetText = Call 번역_API(sourceText, sourceLanguage, targetLanguage) - If targetText is Null: - Return { - status: "TRANSLATION_ERROR", - message: "번역 서비스 오류가 발생했습니다", - data: null - } - - # 5. 음성 파일을 영구 저장소로 이동 - audioFileUrl = Move_To_Permanent_Storage(tempAudioUrl) - - # 6. Translation 엔티티 생성 - Translation = Create Translation { - userID: userID, - sourceText: sourceText, - targetText: targetText, - audioFileUrl: audioFileUrl, - missionId: missionId, - createdAt: Now() - } - - # 7. 저장 및 응답 - Save Translation - Return { - status: "SUCCESS", - message: "음성 번역이 완료되었습니다", - data: Translation - } -``` - -**중요**: STT 실패 시 재시도는 사용자가 수동으로 진행합니다. 시스템은 자동 재시도를 하지 않습니다. - ---- - -## 3. 번역 기록 조회 - -### 개요 - -**목적**: 사용자의 번역 기록 목록을 최신순으로 조회 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: -- 유효한 인증 토큰 -- 본인의 번역 기록만 조회 가능 -- 미션 필터링 가능 - ---- - -### Request (요청) - -**URL**: `/translations?missionId={missionId}&limit={limit}&offset={offset}` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - -**Query Parameters**: - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| missionId | 문자열 | ❌ | 특정 미션의 번역만 조회 | "m_abc123" | -| limit | 숫자 | ❌ | 한 번에 가져올 개수 (기본 20) | 20 | -| offset | 숫자 | ❌ | 시작 위치 (페이징용) | 0 | - -**예시**: -``` -GET /translations?limit=20&offset=0 -GET /translations?missionId=m_abc123 -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "번역 기록을 조회했습니다", - "data": { - "translations": [ - { - "id": "t_xyz789", - "sourceText": "여기서 내려주세요", - "targetText": "Please drop me off here", - "audioFileUrl": "https://storage.cloud.com/audio_xyz789.wav", - "missionId": "m_abc123", - "createdAt": "2026-02-12T14:35:00Z" - }, - { - "id": "t_abc456", - "sourceText": "안녕하세요", - "targetText": "Hello", - "audioFileUrl": null, - "missionId": null, - "createdAt": "2026-02-12T14:30:00Z" - } - ], - "total": 2, - "limit": 20, - "offset": 0 - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 번역기록조회(userID, missionId, limit, offset): - # 1. 기본 쿼리 설정 - Query = Select * From Translation Where UserID = userID - - # 2. 미션 필터링 (선택) - If missionId is Not Null: - Query = Query AND MissionID = missionId - - # 3. 정렬 및 페이징 - Query = Query Order By CreatedAt DESC - Query = Query Limit limit Offset offset - - # 4. 결과 조회 - Translations = Execute Query - Total = Count All Translations For UserID - - # 5. 응답 - Return { - status: "SUCCESS", - message: "번역 기록을 조회했습니다", - data: { - translations: Translations, - total: Total, - limit: limit, - offset: offset - } - } -``` - ---- - -## 📝 주요 제약 - -### 번역 기록 불변성 - -**규칙**: 번역 기록은 생성 후 수정 또는 삭제할 수 없습니다. (Immutable) - -- 사용자는 조회만 가능 -- 관리자도 수정 불가 (감사 추적용) -- 사용자 탈퇴 시 익명화되어 보관 - -### 음성 파일 저장 - -**규칙**: 음성 파일은 Cloud Storage에 저장하고 URL만 DB에 보관합니다. - -- 저장 형식: WAV 또는 MP3 -- URL 만료 정책: 없음 (영구 저장) -- 사용자 탈퇴 시 익명화되지만 파일은 유지 - ---- - -**translations 도메인 API 완료** ✅ diff --git a/ai-context/domain-books/translations/business-rules.md b/ai-context/domain-books/translations/business-rules.md deleted file mode 100644 index c4b149d..0000000 --- a/ai-context/domain-books/translations/business-rules.md +++ /dev/null @@ -1,100 +0,0 @@ -# translations 도메인 비즈니스 규칙 - -## 데이터 규칙 - -### 규칙 1: 번역 기록 불변성 -**설명**: 번역 기록은 한 번 생성되면 수정하거나 삭제할 수 없습니다. -**예시**: "안녕하세요 → Hello" 번역 기록이 저장되면, 내용을 변경할 수 없습니다. -**위반 시**: 수정 또는 삭제 요청은 거부됩니다. - ---- - -### 규칙 2: 필수 필드 -**설명**: 번역 기록에는 원문(SourceText)과 번역문(TargetText)이 반드시 포함되어야 합니다. -**예시**: "안녕하세요"(원문)와 "Hello"(번역문)가 모두 저장되어야 합니다. -**위반 시**: 원문이나 번역문이 없으면 번역 기록이 생성되지 않습니다. - ---- - -### 규칙 3: 음성 파일 선택 저장 -**설명**: 음성 파일은 음성 번역을 사용한 경우에만 저장됩니다. 텍스트 번역에는 음성 파일이 없습니다. -**예시**: 텍스트로 "안녕하세요"를 입력하면 audioFileUrl은 null이지만, 음성으로 녹음하면 Cloud Storage URL이 저장됩니다. -**위반 시**: 텍스트 번역에서는 audioFileUrl이 항상 null입니다. - ---- - -### 규칙 4: 미션 ID 선택 필드 -**설명**: 번역 기록은 미션에 속할 수도 있고, 독립적으로 사용할 수도 있습니다. -**예시**: 택시 미션 중 번역하면 missionId가 "m_abc123"으로 저장되지만, 일상 대화를 번역하면 missionId는 null입니다. -**위반 시**: 미션 없이도 번역은 정상적으로 작동합니다. - ---- - -## 생명주기 규칙 - -### 규칙 5: STT 실패 시 재시도 -**설명**: 음성을 텍스트로 변환하는 STT가 실패하면, 시스템은 자동 재시도하지 않고 사용자에게 알립니다. 사용자가 다시 녹음해야 합니다. -**예시**: 시끄러운 환경에서 음성을 녹음하면 STT가 실패하고 "음성을 인식할 수 없습니다" 메시지가 표시됩니다. -**위반 시**: 사용자는 다시 마이크 버튼을 눌러 재녹음해야 합니다. - ---- - -### 규칙 6: 번역 기록 영구 저장 -**설명**: 모든 번역 기록은 영구적으로 저장되며, 사용자가 탈퇴해도 익명화되어 보관됩니다. -**예시**: 사용자가 탈퇴하면 번역 기록의 사용자 정보는 익명으로 변경되지만, "안녕하세요 → Hello" 기록은 통계 목적으로 보관됩니다. -**위반 시**: 사용자가 직접 삭제할 수 없습니다. - ---- - -### 규칙 7: 음성 파일 Cloud Storage 저장 -**설명**: 녹음된 음성 파일은 데이터베이스에 직접 저장하지 않고 Cloud Storage에 업로드한 후 URL만 DB에 저장합니다. -**예시**: 녹음 파일은 "https://storage.cloud.com/audio_123.wav" 형식의 URL로 저장됩니다. -**위반 시**: 음성 파일이 DB에 직접 저장되면 성능 문제가 발생할 수 있습니다. - ---- - -## 제약 조건 - -### 규칙 8: 지원 언어 제한 -**설명**: 초기 버전은 한국어(ko), 영어(en), 일본어(ja), 중국어(zh)만 지원합니다. -**예시**: 사용자는 한국어 → 영어, 영어 → 한국어 등의 조합으로 번역할 수 있습니다. -**위반 시**: 지원하지 않는 언어 코드(예: "fr")는 "INVALID_INPUT" 오류로 거부됩니다. - ---- - -### 규칙 9: 번역 API 의존성 -**설명**: 번역 결과는 외부 번역 API(Google Translate, Papago 등)에 의존합니다. API 장애 시 번역이 불가능합니다. -**예시**: 번역 API 서버가 다운되면 "번역 서비스 오류가 발생했습니다" 메시지가 표시됩니다. -**위반 시**: API 오류 시 사용자는 나중에 다시 시도해야 합니다. - ---- - -### 규칙 10: 번역 기록 조회 페이징 -**설명**: 번역 기록이 많을 경우 페이징을 통해 20개씩 조회합니다. -**예시**: 첫 번째 요청에서 최근 20개를 가져오고, 스크롤하면 다음 20개를 추가로 가져옵니다. -**위반 시**: 한 번에 모든 기록을 가져오면 성능 문제가 발생할 수 있습니다. - ---- - -## 권한 규칙 - -### 규칙 11: 본인 번역 기록만 조회 가능 -**설명**: 사용자는 자신의 번역 기록만 조회할 수 있으며, 다른 사용자의 기록은 볼 수 없습니다. -**예시**: 사용자 A는 자신의 번역 기록만 보고, 사용자 B의 번역 기록은 접근할 수 없습니다. -**위반 시**: 다른 사용자의 번역 기록 조회 시도는 "UNAUTHORIZED" 오류로 거부됩니다. - ---- - -## 성능 규칙 - -### 규칙 12: 번역 응답 시간 -**설명**: 텍스트 번역은 3초 이내, 음성 번역은 5초 이내에 완료되어야 합니다. -**예시**: 사용자가 "안녕하세요"를 입력하고 번역 버튼을 누르면 3초 이내에 "Hello"가 표시됩니다. -**위반 시**: 응답 시간이 초과되면 타임아웃 메시지가 표시됩니다. - ---- - -### 규칙 13: 음성 파일 크기 제한 -**설명**: 녹음 파일은 최대 10MB까지 허용됩니다. 일반적으로 1분 이내 녹음은 1MB 이하입니다. -**예시**: 30초 음성 녹음은 약 500KB로 저장됩니다. -**위반 시**: 10MB를 초과하면 "파일 크기가 너무 큽니다" 오류가 발생합니다. diff --git a/ai-context/domain-books/translations/domain-model.md b/ai-context/domain-books/translations/domain-model.md deleted file mode 100644 index 11af26c..0000000 --- a/ai-context/domain-books/translations/domain-model.md +++ /dev/null @@ -1,104 +0,0 @@ -# translations 도메인 모델 - -> 생성일: 2026-02-12 -> Phase: 3 (Domain Modeler) -> 상태: ✅ 완료 - ---- - -## 📖 유비쿼터스 언어 (용어 정의) - -> 이 도메인에서 사용하는 전용 용어들 - -### 핵심 용어 - -| 용어 | 정의 | 예시 | -|------|------|------| -| 번역 (Translation) | 사용자가 요청한 번역 기록 | 한 번의 번역 요청과 결과 | -| 식별자 (ID) | 번역 기록을 고유하게 구분하는 값 | "t_abc123" | -| 사용자 ID (UserID) | 이 번역을 요청한 사용자의 식별자 | "u_123abc" | -| 원문 (SourceText) | 번역 전 원본 텍스트 | "안녕하세요" | -| 번역문 (TargetText) | 번역된 결과 텍스트 | "Hello" | -| 음성 파일 (AudioFileUrl) | 녹음된 음성 파일의 저장 경로 (Cloud Storage) | "https://storage.../audio_123.wav" | -| 생성 시각 (CreatedAt) | 번역이 생성된 날짜와 시간 | "2026-02-12 14:30:00" | -| 미션 ID (MissionID) | 이 번역이 속한 미션의 식별자 (선택) | "m_456def" 또는 null | - ---- - -## 📐 관계 규칙 - -> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의 - -### 번역과 사용자 - -**규칙 1**: 번역은 반드시 한 명의 사용자에게 속한다. -**규칙 2**: 사용자는 여러 개의 번역 기록을 가질 수 있다. -**규칙 3**: 사용자가 탈퇴하면, 번역 기록은 유지되지만 사용자는 익명으로 표시된다. - -### 번역과 미션 - -**규칙 4**: 번역은 미션에 속할 수도 있고, 독립적일 수도 있다. (MissionID는 선택) -**규칙 5**: 한 미션은 여러 개의 번역을 포함할 수 있다. -**규칙 6**: 미션이 종료되어도 번역 기록은 유지된다. - ---- - -## 🔒 제약 조건 - -> 비즈니스 규칙과 제약사항 - -### 필수 필드 - -- **ID**: 시스템이 자동으로 생성 -- **UserID**: 번역을 요청한 사용자 (필수) -- **SourceText**: 원문 텍스트 (필수) -- **TargetText**: 번역된 텍스트 (필수) -- **CreatedAt**: 생성 시각 (자동) - -### 선택 필드 - -- **AudioFileUrl**: 음성 번역인 경우에만 저장 -- **MissionID**: 미션 중 번역인 경우에만 설정 - -### 저장 정책 - -**규칙 7**: 모든 번역 기록은 영구 저장된다. -**규칙 8**: 음성 파일은 Cloud Storage에 저장하고 URL만 DB에 보관한다. -**규칙 9**: STT/TTS 실패 시 재시도는 사용자가 수동으로 진행한다. - ---- - -## 🎯 생명주기 - -### 생성 - -#### 텍스트 번역 -1. 사용자가 텍스트를 입력한다. -2. 시스템은 번역 API를 호출한다. -3. 번역 결과를 Translation으로 저장한다. (SourceText, TargetText, UserID) - -#### 음성 번역 -1. 사용자가 마이크를 길게 눌러 음성을 녹음한다. (PTT 방식) -2. 시스템은 STT API를 호출하여 음성을 텍스트로 변환한다. -3. 변환된 텍스트를 번역 API로 번역한다. -4. 음성 파일을 Cloud Storage에 업로드하고 URL을 받는다. -5. Translation을 저장한다. (SourceText, TargetText, AudioFileUrl, UserID) - -### 조회 - -1. 사용자는 자신의 번역 기록 목록을 볼 수 있다. -2. 최신 순으로 정렬되어 표시된다. -3. 미션별로 필터링할 수 있다. - -### 수정 - -**규칙 10**: 번역 기록은 수정할 수 없다. (Immutable) - -### 삭제 - -**규칙 11**: 번역 기록은 사용자가 직접 삭제할 수 없다. -**규칙 12**: 사용자 탈퇴 시에도 번역 기록은 익명화되어 보관된다. - ---- - -**translations 도메인 완료** ✅ diff --git a/ai-context/domain-books/translations/features.md b/ai-context/domain-books/translations/features.md deleted file mode 100644 index ed8f1fc..0000000 --- a/ai-context/domain-books/translations/features.md +++ /dev/null @@ -1,73 +0,0 @@ -# translations 도메인 기능 정의 - -## 기능 1: 텍스트 번역 - -**설명**: 사용자가 직접 입력한 텍스트를 원하는 언어로 번역하고 서버에 저장합니다. 번역 기록은 나중에 다시 확인할 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 번역 화면에서 "안녕하세요"를 입력합니다. -2. 원문 언어를 한국어, 번역할 언어를 영어로 선택합니다. -3. "번역" 버튼을 누르면 서버가 번역 API를 호출하여 "Hello"를 반환합니다. -4. 번역 결과가 화면에 표시되고, 서버에 번역 기록으로 저장됩니다. - -**관련 API**: -- 텍스트 번역 생성 (api-spec.md 참조) - ---- - -## 기능 2: 음성 번역 - -**설명**: 사용자가 마이크를 길게 눌러 음성을 녹음하면(PTT 방식), 시스템이 STT로 텍스트를 추출하고 번역합니다. 음성 파일도 함께 저장됩니다. - -**사용자 시나리오**: -1. 사용자가 번역 화면에서 마이크 버튼을 길게 누릅니다. -2. "여기서 내려주세요"라고 말하고 버튼을 뗍니다. -3. 시스템이 음성을 텍스트로 변환하고 영어로 번역합니다. -4. "Please drop me off here"가 화면에 표시되고, 음성 파일과 번역 기록이 저장됩니다. - -**관련 API**: -- 음성 번역 생성 (api-spec.md 참조) - ---- - -## 기능 3: 번역 기록 조회 - -**설명**: 사용자는 자신의 모든 번역 기록을 최신순으로 확인할 수 있습니다. 텍스트 번역과 음성 번역 모두 포함됩니다. - -**사용자 시나리오**: -1. 사용자가 번역 화면에서 "기록" 탭을 선택합니다. -2. 최근에 번역한 문장들이 최신순으로 나열됩니다. -3. 음성 번역 기록은 재생 버튼이 함께 표시됩니다. -4. 사용자는 이전 번역을 다시 확인하거나 음성을 재생할 수 있습니다. - -**관련 API**: -- 번역 기록 조회 (api-spec.md 참조) - ---- - -## 기능 4: 미션별 번역 기록 필터링 - -**설명**: 사용자는 특정 미션에서 번역한 기록만 별도로 조회할 수 있습니다. 택시 미션 중 번역한 문장들만 필터링하여 볼 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 택시 미션을 완료합니다. -2. 가이드 탭에서 "이 미션의 번역 기록" 버튼을 누릅니다. -3. 택시 미션 중에 번역했던 문장들만 목록으로 표시됩니다. -4. 나중에 같은 상황에서 이전 번역을 참고할 수 있습니다. - -**관련 API**: -- 번역 기록 조회 (missionId 파라미터 사용, api-spec.md 참조) - ---- - -## 기능 5: 음성 파일 재생 - -**설명**: 음성 번역 기록에는 녹음된 음성 파일이 저장되어 있어, 사용자가 나중에 다시 들을 수 있습니다. - -**사용자 시나리오**: -1. 사용자가 번역 기록 목록에서 음성 번역 항목을 선택합니다. -2. "재생" 버튼을 누르면 이전에 녹음한 음성이 재생됩니다. -3. 자신의 발음을 다시 확인하거나 비교할 수 있습니다. - -**관련 API**: -- 번역 기록 조회 (api-spec.md 참조, audioFileUrl 필드 사용) diff --git a/ai-context/domain-books/users/README.md b/ai-context/domain-books/users/README.md deleted file mode 100644 index ae0c468..0000000 --- a/ai-context/domain-books/users/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# users 도메인 - -> 소셜 로그인 기반 회원 관리 및 프로필 관리 - -## 📖 목차 - -1. [기능 정의](./features.md) -2. [도메인 모델](./domain-model.md) -3. [API 명세](./api-spec.md) -4. [비즈니스 규칙](./business-rules.md) - -## 📝 개요 - -users 도메인은 여행자가 앱을 사용하기 위한 회원 관리를 담당합니다. Google과 Apple 소셜 로그인을 통해 간편하게 가입하고, 프로필과 선호 언어를 관리할 수 있습니다. 탈퇴 시 개인정보는 익명화되지만 활동 기록은 보관됩니다. - -## 🎯 핵심 기능 - -- 소셜 로그인 (Google/Apple) -- 프로필 조회 및 수정 -- 선호 언어 설정 -- 회원 탈퇴 (Soft Delete) - -## 📊 주요 엔티티 - -- **User**: 앱 사용자 정보 (이메일, 표시 이름, 프로필 사진, 선호 언어) -- **SocialProvider**: 소셜 로그인 제공자 (Google, Apple) -- **UserStatus**: 계정 상태 (활성, 탈퇴) - -## 🔗 도메인 의존성 - -- **의존하는 도메인**: 없음 (독립 도메인) -- **이 도메인에 의존하는 도메인**: translations, missions, maps (모든 도메인이 User를 참조함) diff --git a/ai-context/domain-books/users/api-spec.md b/ai-context/domain-books/users/api-spec.md deleted file mode 100644 index 31aff52..0000000 --- a/ai-context/domain-books/users/api-spec.md +++ /dev/null @@ -1,433 +0,0 @@ -# users 도메인 API 명세 - -> 생성일: 2026-02-12 -> Phase: 4 (API Designer) -> 상태: ✅ 완료 - ---- - -## 📋 ENUM 정의 - -### SocialProvider - -소셜 로그인 제공자: -- `google`: Google 소셜 로그인 -- `apple`: Apple 소셜 로그인 - -### PreferredLanguage - -사용자 선호 언어: -- `ko`: 한국어 -- `en`: 영어 - -### UserStatus - -사용자 계정 상태: -- `Active`: 활성 상태 -- `Deleted`: 탈퇴 (Soft Delete) - ---- - -## 📡 API 목록 - -| API | 설명 | 중요도 | -|-----|------|:------:| -| 소셜 로그인 | Google/Apple 로그인 | 🔥 필수 | -| 프로필 조회 | 사용자 정보 조회 | ⭐ 중요 | -| 프로필 수정 | 사용자 정보 변경 | ⭐ 중요 | -| 회원 탈퇴 | 사용자 Soft Delete | ⭐ 중요 | - ---- - -## 1. 소셜 로그인 - -### 개요 - -**목적**: Google 또는 Apple 소셜 로그인으로 회원가입/로그인 처리 - -**호출 주체**: 비회원 (인증 불필요) - -**성공 조건**: -- 유효한 소셜 로그인 토큰 -- 첫 로그인 시 자동 회원가입 -- 이미 가입된 경우 로그인 처리 - ---- - -### Request (요청) - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| provider | **SocialProvider** (ENUM) | ✅ | 소셜 로그인 제공자 | "google", "apple" | -| token | 문자열 | ✅ | 소셜 로그인 인증 토큰 | "eyJhbGc..." | - -**예시**: -```json -{ - "provider": "google", - "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -**표준 Response 형식**: -```json -{ - "status": "SUCCESS", - "message": "로그인 성공", - "data": { - "user": { - "id": "u_abc123", - "email": "john@gmail.com", - "displayName": "John Doe", - "profileImage": "https://lh3.googleusercontent.com/...", - "preferredLanguage": "en", - "status": "Active" - }, - "isNewUser": true, - "authToken": "jwt_token_here" - } -} -``` - -#### 실패 (400 Bad Request) - -**잘못된 토큰**: -```json -{ - "status": "INVALID_TOKEN", - "message": "유효하지 않은 소셜 로그인 토큰입니다", - "data": null -} -``` - -**잘못된 제공자**: -```json -{ - "status": "INVALID_PROVIDER", - "message": "지원하지 않는 소셜 제공자입니다", - "data": null -} -``` - -**제공자 API 오류**: -```json -{ - "status": "PROVIDER_ERROR", - "message": "소셜 제공자 API 호출에 실패했습니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 소셜로그인(provider, token): - # 1. ENUM 검증 - If provider NOT IN [SocialProvider.google, SocialProvider.apple]: - Return { - status: "INVALID_PROVIDER", - message: "지원하지 않는 소셜 제공자입니다", - data: null - } - - # 2. 소셜 제공자에서 사용자 정보 조회 - 소셜_사용자_정보 = Call 소셜_API(provider, token) - If 소셜_사용자_정보 is Null: - Return { - status: "INVALID_TOKEN", - message: "유효하지 않은 소셜 로그인 토큰입니다", - data: null - } - - Email = 소셜_사용자_정보.email - - # 3. 기존 사용자 확인 - User = Find User Where Email = Email AND Status = "Active" - - If User Exists: - # 기존 사용자 로그인 - AuthToken = Generate_JWT(User.ID) - Return { - status: "SUCCESS", - message: "로그인 성공", - data: { - user: User, - isNewUser: false, - authToken: AuthToken - } - } - Else: - # 신규 사용자 생성 - NewUser = Create User { - email: Email, - displayName: 소셜_사용자_정보.name, - profileImage: 소셜_사용자_정보.picture, - preferredLanguage: "en", # 기본값 - status: "Active" - } - AuthToken = Generate_JWT(NewUser.ID) - Return { - status: "SUCCESS", - message: "회원가입 및 로그인 성공", - data: { - user: NewUser, - isNewUser: true, - authToken: AuthToken - } - } -``` - ---- - -## 2. 프로필 조회 - -### 개요 - -**목적**: 로그인한 사용자의 프로필 정보 조회 - -**호출 주체**: 인증된 사용자 - -**성공 조건**: 유효한 인증 토큰 - ---- - -### Request (요청) - -**URL**: `/users/me` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "프로필 조회 성공", - "data": { - "id": "u_abc123", - "email": "john@gmail.com", - "displayName": "여행러버", - "profileImage": "https://...", - "preferredLanguage": "ko", - "status": "Active", - "createdAt": "2026-02-12T10:30:00Z" - } -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - ---- - -## 3. 프로필 수정 - -### 개요 - -**목적**: 로그인한 사용자의 프로필 정보 수정 - -**호출 주체**: 인증된 사용자 (본인만) - -**성공 조건**: -- 유효한 인증 토큰 -- 본인의 프로필만 수정 가능 -- 수정 가능 필드: displayName, profileImage, preferredLanguage - ---- - -### Request (요청) - -**URL**: `/users/me` - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - -| 필드명 | 타입 | 필수 | 설명 | 예시 | -|--------|------|:----:|------|------| -| displayName | 문자열 | ❌ | 표시 이름 | "새닉네임" | -| profileImage | 문자열 (URL) | ❌ | 프로필 사진 URL | "https://..." | -| preferredLanguage | **PreferredLanguage** (ENUM) | ❌ | 선호 언어 | "ko", "en" | - -**예시**: -```json -{ - "displayName": "새닉네임", - "preferredLanguage": "ko" -} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "프로필 수정 성공", - "data": { - "id": "u_abc123", - "email": "john@gmail.com", - "displayName": "새닉네임", - "profileImage": "https://...", - "preferredLanguage": "ko", - "status": "Active", - "createdAt": "2026-02-12T10:30:00Z" - } -} -``` - -#### 실패 (401 Unauthorized) - -**인증 실패**: -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**권한 없음**: -```json -{ - "status": "FORBIDDEN", - "message": "본인의 프로필만 수정할 수 있습니다", - "data": null -} -``` - -**잘못된 입력**: -```json -{ - "status": "INVALID_INPUT", - "message": "유효하지 않은 언어 코드입니다", - "data": null -} -``` - ---- - -## 4. 회원 탈퇴 - -### 개요 - -**목적**: 사용자 Soft Delete (익명화) - -**호출 주체**: 인증된 사용자 (본인만) - -**성공 조건**: 유효한 인증 토큰 - ---- - -### Request (요청) - -**URL**: `/users/me` - -**Method**: DELETE - -**Headers**: -``` -Authorization: Bearer {authToken} -``` - ---- - -### Response (응답) - -#### 성공 (200 OK) - -```json -{ - "status": "SUCCESS", - "message": "회원 탈퇴가 완료되었습니다", - "data": null -} -``` - -#### 실패 (401 Unauthorized) - -```json -{ - "status": "UNAUTHORIZED", - "message": "인증이 필요합니다", - "data": null -} -``` - -#### 실패 (400 Bad Request) - -**사용자 없음**: -```json -{ - "status": "NOT_FOUND", - "message": "사용자를 찾을 수 없습니다", - "data": null -} -``` - ---- - -### 수도코드 - -``` -Function 회원탈퇴(userID): - # 1. 사용자 조회 - User = Find User Where ID = userID AND Status = "Active" - If User is Null: - Return { - status: "NOT_FOUND", - message: "사용자를 찾을 수 없습니다", - data: null - } - - # 2. Soft Delete - User.Status = UserStatus.Deleted - User.Email = "deleted_user_" + userID - User.DisplayName = "탈퇴한 사용자" - User.ProfileImage = Null - Update User - - # 3. 관련 데이터 처리 - Delete All SearchHistory Where UserID = userID - Delete All FavoritePlace Where UserID = userID - - # 번역/미션 기록은 익명으로 보관 - - Return { - status: "SUCCESS", - message: "회원 탈퇴가 완료되었습니다", - data: null - } -``` - ---- - -**users 도메인 API 완료** ✅ diff --git a/ai-context/domain-books/users/business-rules.md b/ai-context/domain-books/users/business-rules.md deleted file mode 100644 index 1cae011..0000000 --- a/ai-context/domain-books/users/business-rules.md +++ /dev/null @@ -1,91 +0,0 @@ -# users 도메인 비즈니스 규칙 - -## 데이터 규칙 - -### 규칙 1: 이메일 유일성 -**설명**: 동일한 이메일로 여러 계정을 만들 수 없습니다. -**예시**: "john@gmail.com"으로 이미 가입된 경우, 같은 이메일로 다시 가입하면 로그인 처리됩니다. -**위반 시**: 중복 가입 불가, 자동으로 로그인 처리 - ---- - -### 규칙 2: 필수 필드 자동 생성 -**설명**: ID는 시스템이 자동으로 생성하며, 사용자가 직접 지정할 수 없습니다. -**예시**: 회원가입 시 "u_abc123" 형식의 고유 ID가 자동으로 부여됩니다. -**위반 시**: 사용자가 ID를 직접 입력하면 무시됩니다. - ---- - -### 규칙 3: 선호 언어 기본값 -**설명**: 선호 언어는 회원가입 시 기본값으로 "en" (영어)가 설정됩니다. -**예시**: 소셜 로그인 직후 선호 언어는 영어로 설정되며, 사용자가 나중에 한국어로 변경할 수 있습니다. -**위반 시**: 기본값이 없으면 시스템이 "en"을 자동 설정 - ---- - -## 생명주기 규칙 - -### 규칙 4: 소셜 로그인 필수 -**설명**: 회원가입은 반드시 Google 또는 Apple 소셜 로그인을 통해서만 가능합니다. 이메일/비밀번호 방식은 지원하지 않습니다. -**예시**: 사용자는 Google 또는 Apple 계정으로만 가입하고 로그인할 수 있습니다. -**위반 시**: 다른 방식의 가입 시도는 거부됩니다. - ---- - -### 규칙 5: 탈퇴 시 Soft Delete -**설명**: 회원 탈퇴 시 계정은 완전히 삭제되지 않고 비활성화(Soft Delete)됩니다. 이메일과 이름은 익명화되지만 ID는 유지됩니다. -**예시**: 탈퇴한 사용자의 이메일은 "deleted_user_u_123"으로 변경되고, 표시 이름은 "탈퇴한 사용자"로 변경됩니다. -**위반 시**: 탈퇴 후 번역/미션 기록은 익명으로 보관됩니다. - ---- - -### 규칙 6: 탈퇴 후 데이터 처리 -**설명**: 탈퇴 시 번역 기록과 미션 기록은 익명화되어 보관되지만, 검색 기록과 즐겨찾기는 완전히 삭제됩니다. -**예시**: 사용자가 탈퇴하면 "명동" 검색 기록과 "숙소" 즐겨찾기는 완전히 삭제되지만, "안녕하세요 → Hello" 번역 기록은 익명으로 보관됩니다. -**위반 시**: 개인 식별 가능한 데이터는 모두 제거됩니다. - ---- - -## 권한 규칙 - -### 규칙 7: 본인 프로필만 수정 가능 -**설명**: 프로필 수정은 본인만 가능하며, 다른 사용자나 관리자도 수정할 수 없습니다. -**예시**: 사용자 A는 자신의 표시 이름을 변경할 수 있지만, 사용자 B의 프로필은 변경할 수 없습니다. -**위반 시**: 다른 사용자의 프로필 수정 시도는 "FORBIDDEN" 오류로 거부됩니다. - ---- - -### 규칙 8: 관리자도 프로필 수정 불가 -**설명**: 관리자는 사용자 프로필을 수정할 수 없습니다. 사용자의 프라이버시를 보호하기 위함입니다. -**예시**: 관리자는 사용자 목록을 조회할 수 있지만, 표시 이름이나 선호 언어를 변경할 수 없습니다. -**위반 시**: 관리자의 프로필 수정 시도는 거부됩니다. - ---- - -## 제약 조건 - -### 규칙 9: ID와 이메일 변경 불가 -**설명**: 사용자 ID와 이메일은 한 번 생성되면 변경할 수 없습니다. -**예시**: 회원가입 시 부여된 "u_abc123" ID와 "john@gmail.com" 이메일은 평생 변경되지 않습니다. -**위반 시**: ID나 이메일 변경 요청은 무시됩니다. - ---- - -### 규칙 10: 소셜 토큰 검증 필수 -**설명**: 소셜 로그인 시 제공된 토큰은 반드시 소셜 제공자(Google/Apple) API를 통해 검증되어야 합니다. -**예시**: Google 로그인 토큰을 받으면 Google API로 토큰의 유효성을 확인한 후 회원가입 또는 로그인을 처리합니다. -**위반 시**: 잘못된 토큰은 "INVALID_TOKEN" 오류로 거부됩니다. - ---- - -### 규칙 11: 선호 언어 제한 -**설명**: 선호 언어는 한국어("ko") 또는 영어("en")만 지원합니다. -**예시**: 사용자는 선호 언어를 "ko" 또는 "en"으로만 설정할 수 있습니다. -**위반 시**: 다른 언어 코드 입력 시 "INVALID_INPUT" 오류가 반환됩니다. - ---- - -### 규칙 12: 활성 계정만 로그인 가능 -**설명**: 탈퇴한 사용자(Status = "Deleted")는 로그인할 수 없습니다. -**예시**: 탈퇴 후 같은 이메일로 로그인 시도하면 새 계정으로 가입 처리됩니다. -**위반 시**: 탈퇴 계정으로 로그인 시도 시 새 계정 생성됩니다. diff --git a/ai-context/domain-books/users/domain-model.md b/ai-context/domain-books/users/domain-model.md deleted file mode 100644 index 99171ac..0000000 --- a/ai-context/domain-books/users/domain-model.md +++ /dev/null @@ -1,95 +0,0 @@ -# users 도메인 모델 - -> 생성일: 2026-02-12 -> Phase: 3 (Domain Modeler) -> 상태: ✅ 완료 - ---- - -## 📖 유비쿼터스 언어 (용어 정의) - -> 이 도메인에서 사용하는 전용 용어들 - -### 핵심 용어 - -| 용어 | 정의 | 예시 | -|------|------|------| -| 사용자 (User) | 앱을 사용하는 개인 | "홍길동", "john@example.com" | -| 식별자 (ID) | 사용자를 고유하게 구분하는 값 | "u_123abc" | -| 이메일 (Email) | 소셜 로그인에서 받은 이메일 주소 | "user@example.com" | -| 표시 이름 (DisplayName) | 다른 사용자에게 보이는 닉네임 | "여행러버", "TravelLover" | -| 프로필 사진 (ProfileImage) | 사용자의 프로필 이미지 URL | "https://.../profile.jpg" | -| 선호 언어 (PreferredLanguage) | 사용자가 선호하는 번역 언어 | "ko" (한국어), "en" (영어) | - ---- - -## 📐 관계 규칙 - -> 이 도메인의 엔티티들이 어떻게 연결되는지 서술형으로 정의 - -### 사용자와 번역 기록 - -**규칙 1**: 사용자는 여러 개의 번역 기록을 가질 수 있다. -**규칙 2**: 번역 기록은 반드시 한 명의 사용자에게 속한다. -**규칙 3**: 사용자가 탈퇴하면, 번역 기록은 유지되지만 사용자 정보는 익명화된다. (Soft Delete) - -### 사용자와 미션 - -**규칙 4**: 사용자는 여러 개의 미션을 생성하고 진행할 수 있다. -**규칙 5**: 미션은 반드시 한 명의 사용자에게 속한다. -**규칙 6**: 사용자가 탈퇴하면, 미션 기록은 익명화된다. - -### 사용자와 지도 기능 - -**규칙 7**: 사용자는 여러 개의 검색 기록을 가질 수 있다. -**규칙 8**: 사용자는 여러 개의 즐겨찾기 장소를 저장할 수 있다. -**규칙 9**: 검색 기록과 즐겨찾기는 사용자 탈퇴 시 완전히 삭제된다. - ---- - -## 🔒 제약 조건 - -> 비즈니스 규칙과 제약사항 - -### 필수 필드 - -- **ID**: 시스템이 자동으로 생성 (사용자가 직접 지정할 수 없음) -- **Email**: 소셜 로그인 시 필수로 제공되어야 함 -- **PreferredLanguage**: 기본값은 "en" (영어) - -### 권한 규칙 - -**규칙 10**: 프로필 수정은 본인만 가능하다. -**규칙 11**: 관리자는 프로필을 수정할 수 없다. (사용자 프라이버시 보호) - -### 유일성 제약 - -- **Email**: 동일한 이메일로 여러 계정을 만들 수 없다. - ---- - -## 🎯 생명주기 - -### 생성 - -1. 사용자가 Google 또는 Apple 소셜 로그인을 시도한다. -2. 시스템은 Email이 이미 존재하는지 확인한다. -3. 존재하지 않으면 새 User를 생성하고 ID를 부여한다. -4. PreferredLanguage는 기본값 "en"으로 설정된다. - -### 수정 - -1. 사용자는 자신의 DisplayName, ProfileImage, PreferredLanguage를 수정할 수 있다. -2. Email과 ID는 변경할 수 없다. - -### 탈퇴 (Soft Delete) - -1. 사용자가 탈퇴를 요청한다. -2. 시스템은 User를 비활성화한다. (Status = "Deleted") -3. Email, DisplayName을 익명화한다. (예: "deleted_user_123") -4. 번역 기록, 미션 기록은 보관하지만 사용자 정보는 보이지 않는다. -5. 검색 기록과 즐겨찾기는 완전히 삭제한다. - ---- - -**users 도메인 완료** ✅ diff --git a/ai-context/domain-books/users/features.md b/ai-context/domain-books/users/features.md deleted file mode 100644 index 08a86f7..0000000 --- a/ai-context/domain-books/users/features.md +++ /dev/null @@ -1,72 +0,0 @@ -# users 도메인 기능 정의 - -## 기능 1: 소셜 로그인 - -**설명**: Google 또는 Apple 소셜 로그인을 통해 회원가입과 로그인을 한 번에 처리합니다. 이메일이 이미 존재하면 로그인, 처음이면 자동으로 회원가입됩니다. - -**사용자 시나리오**: -1. 사용자가 앱을 처음 실행하면 Google 또는 Apple 로그인 버튼을 선택합니다. -2. 소셜 제공자의 인증 화면에서 로그인을 완료합니다. -3. 앱은 이메일과 기본 프로필 정보를 받아 자동으로 회원가입하거나 로그인 처리합니다. -4. 사용자는 바로 앱의 주요 기능을 사용할 수 있습니다. - -**관련 API**: -- 소셜 로그인 (api-spec.md 참조) - ---- - -## 기능 2: 프로필 조회 - -**설명**: 로그인한 사용자는 자신의 프로필 정보를 조회할 수 있습니다. 이메일, 표시 이름, 프로필 사진, 선호 언어 등이 포함됩니다. - -**사용자 시나리오**: -1. 사용자가 앱의 프로필 화면에 접근합니다. -2. 앱은 서버에서 사용자의 프로필 정보를 가져옵니다. -3. 이메일, 표시 이름, 프로필 사진이 화면에 표시됩니다. - -**관련 API**: -- 프로필 조회 (api-spec.md 참조) - ---- - -## 기능 3: 프로필 수정 - -**설명**: 사용자는 자신의 표시 이름, 프로필 사진, 선호 언어를 수정할 수 있습니다. 이메일과 ID는 변경할 수 없습니다. - -**사용자 시나리오**: -1. 사용자가 프로필 화면에서 "수정" 버튼을 누릅니다. -2. 표시 이름을 변경하거나 선호 언어를 한국어에서 영어로 바꿉니다. -3. "저장" 버튼을 누르면 변경사항이 서버에 저장됩니다. -4. 앱은 변경된 프로필 정보를 화면에 반영합니다. - -**관련 API**: -- 프로필 수정 (api-spec.md 참조) - ---- - -## 기능 4: 선호 언어 설정 - -**설명**: 사용자는 번역 기능에서 사용할 선호 언어를 한국어 또는 영어로 설정할 수 있습니다. 이 설정은 번역 기본값으로 사용됩니다. - -**사용자 시나리오**: -1. 사용자가 프로필 화면에서 "선호 언어" 항목을 선택합니다. -2. 한국어 또는 영어 중 하나를 선택합니다. -3. 앱은 이후 번역 화면에서 선택한 언어를 기본값으로 설정합니다. - -**관련 API**: -- 프로필 수정 (api-spec.md 참조) - ---- - -## 기능 5: 회원 탈퇴 - -**설명**: 사용자는 계정을 탈퇴할 수 있습니다. 탈퇴 시 개인정보(이메일, 이름)는 익명화되지만 번역 기록과 미션 기록은 통계 목적으로 보관됩니다. - -**사용자 시나리오**: -1. 사용자가 설정 화면에서 "회원 탈퇴" 버튼을 누릅니다. -2. 앱은 탈퇴 확인 팝업을 표시합니다. -3. 사용자가 탈퇴를 확정하면 계정이 비활성화되고 개인정보가 익명화됩니다. -4. 번역 기록과 미션 기록은 익명 상태로 보관되지만, 검색 기록과 즐겨찾기는 완전히 삭제됩니다. - -**관련 API**: -- 회원 탈퇴 (api-spec.md 참조) diff --git a/plugins/domain-book-builder/.claude-plugin/plugin.json b/plugins/domain-book-builder/.claude-plugin/plugin.json index 7d11407..eb45f59 100644 --- a/plugins/domain-book-builder/.claude-plugin/plugin.json +++ b/plugins/domain-book-builder/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "domain-book-builder", "description": "기술 독립적 Domain Book 생성 - 완벽한 도메인 설계서 집필", - "version": "0.1.0", + "version": "1.0.0", "author": { "name": "URECA Team" }, diff --git a/plugins/domain-book-builder/skills/5-write-book/SKILL.md b/plugins/domain-book-builder/skills/5-write-book/SKILL.md index 899481f..29cb4fd 100644 --- a/plugins/domain-book-builder/skills/5-write-book/SKILL.md +++ b/plugins/domain-book-builder/skills/5-write-book/SKILL.md @@ -223,6 +223,25 @@ relationships = extract_domain_relationships( --- +## 📱 화면 구성 + +> **디자인 원칙**: Instagram, Facebook, Twitter 등 1억 명 이상 사용하는 서비스의 UX를 참고하되, 심플하고 미니멀한 구성을 지향한다. `frontend-design` 스킬을 활용하여 디자인 품질을 확보한다. + +### 화면 1: {화면 이름} + +- **경로**: /{path} +- **도메인**: {domain} +- **화면 목적**: {이 화면의 핵심 가치} +- **UX 레퍼런스**: {참고 서비스의 유사 화면} +- **핵심 인터랙션**: + - {주요 행동 1} + - {주요 행동 2} +- **정보 구조**: {정보 우선순위} +- **네비게이션**: {이동 경로} +- **관련 기능**: 기능 {N} + +--- + ## 🚫 범위 밖 (Out of Scope) - {제외 사항 1} @@ -267,6 +286,40 @@ relationships = extract_domain_relationships( --- +## 📱 화면 구성 + +> **디자인 원칙**: Instagram, KakaoTalk 등 1억 명 이상 사용하는 서비스의 UX를 참고하되, 심플하고 미니멀한 구성을 지향한다. `frontend-design` 스킬을 활용하여 디자인 품질을 확보한다. + +### 화면 1: 로그인 + +- **경로**: /login +- **도메인**: users +- **화면 목적**: 최소한의 마찰로 앱에 진입한다 +- **UX 레퍼런스**: Instagram 로그인 (로고 + 단 2개 입력필드 + 소셜 로그인) +- **핵심 인터랙션**: + - 이메일/비밀번호 입력 후 로그인 + - 소셜 로그인 원탭 진입 +- **정보 구조**: 브랜드 로고 > 입력 폼 > 소셜 로그인 > 회원가입 링크 +- **네비게이션**: 성공 → /home, 회원가입 → /register +- **관련 기능**: 기능 1 (회원가입) + +--- + +### 화면 2: 프로필 + +- **경로**: /profile +- **도메인**: users +- **화면 목적**: 내 정보를 한눈에 확인하고 편집한다 +- **UX 레퍼런스**: Instagram 프로필 (프로필 사진 + 핵심 숫자 + 편집 버튼) +- **핵심 인터랙션**: + - 프로필 사진/닉네임 확인 + - "편집" 탭하여 수정 모드 진입 +- **정보 구조**: 프로필 사진 > 닉네임 > 활동 요약 > 설정 +- **네비게이션**: 편집 → /profile/edit, 설정 → /settings +- **관련 기능**: 기능 2 (프로필 조회), 기능 3 (프로필 수정) + +--- + ## 🚫 범위 밖 (Out of Scope) - 소셜 로그인 (Google, Apple 등) @@ -561,3 +614,6 @@ Domain Book 완성 조건: - [ ] 상호 참조 일관성 - [ ] 모든 엔티티가 domain-model에 정의됨 - [ ] 모든 API가 api-spec에 명시됨 +- [ ] 📱 화면 구성이 주요 사용자 시나리오를 커버 +- [ ] 각 화면에 UX 레퍼런스 명시 +- [ ] 화면 간 네비게이션 연결 확인 diff --git a/plugins/domain-book-builder/skills/5-write-book/templates/features.md b/plugins/domain-book-builder/skills/5-write-book/templates/features.md index dd415d7..074e23a 100644 --- a/plugins/domain-book-builder/skills/5-write-book/templates/features.md +++ b/plugins/domain-book-builder/skills/5-write-book/templates/features.md @@ -31,6 +31,25 @@ --- +## 📱 화면 구성 + +> **디자인 원칙**: Instagram, Facebook, Twitter 등 1억 명 이상 사용하는 서비스의 UX를 참고하되, 심플하고 미니멀한 구성을 지향한다. `frontend-design` 스킬을 활용하여 디자인 품질을 확보한다. + +### 화면 1: {화면 이름} + +- **경로**: /{path} +- **도메인**: {domain} +- **화면 목적**: {이 화면이 사용자에게 제공하는 핵심 가치} +- **UX 레퍼런스**: {참고 서비스의 유사 화면} (예: Instagram 피드, Twitter 타임라인) +- **핵심 인터랙션**: + - {사용자가 하는 주요 행동 1} + - {사용자가 하는 주요 행동 2} +- **정보 구조**: {화면에 표시되는 정보의 우선순위} +- **네비게이션**: {다른 화면으로의 이동 경로} +- **관련 기능**: 기능 {N} + +--- + ## 🚫 범위 밖 (Out of Scope) - {제외 사항 1} @@ -358,6 +377,40 @@ api-spec.md의 API 이름 또는 주요 액션: --- +## 📱 화면 구성 + +> **디자인 원칙**: Instagram, KakaoTalk 등 1억 명 이상 사용하는 서비스의 UX를 참고하되, 심플하고 미니멀한 구성을 지향한다. `frontend-design` 스킬을 활용하여 디자인 품질을 확보한다. + +### 화면 1: 로그인 + +- **경로**: /login +- **도메인**: users +- **화면 목적**: 최소한의 마찰로 앱에 진입한다 +- **UX 레퍼런스**: Instagram 로그인 (로고 + 단 2개 입력필드 + 소셜 로그인) +- **핵심 인터랙션**: + - 이메일/비밀번호 입력 후 로그인 + - 소셜 로그인 원탭 진입 +- **정보 구조**: 브랜드 로고 > 입력 폼 > 소셜 로그인 > 회원가입 링크 +- **네비게이션**: 성공 → /home, 회원가입 → /register +- **관련 기능**: 기능 1 (회원가입) + +--- + +### 화면 2: 프로필 + +- **경로**: /profile +- **도메인**: users +- **화면 목적**: 내 정보를 한눈에 확인하고 편집한다 +- **UX 레퍼런스**: Instagram 프로필 (프로필 사진 + 핵심 숫자 + 편집 버튼) +- **핵심 인터랙션**: + - 프로필 사진/닉네임 확인 + - "편집" 탭하여 수정 모드 진입 +- **정보 구조**: 프로필 사진 > 닉네임 > 활동 요약 > 설정 +- **네비게이션**: 편집 → /profile/edit, 설정 → /settings +- **관련 기능**: 기능 2 (프로필 조회), 기능 3 (프로필 수정) + +--- + ## 🚫 범위 밖 (Out of Scope) - 소셜 로그인 (Google, Apple 등) @@ -380,3 +433,87 @@ features.md 작성 완료 후: - [ ] 범위 밖이 명확하게 정의됨 - [ ] 기술 용어 사용 안 함 - [ ] 사용자 관점으로 작성됨 +- [ ] 📱 화면 구성이 모든 주요 사용자 시나리오를 커버함 +- [ ] 각 화면에 UX 레퍼런스가 명시됨 +- [ ] 화면 간 네비게이션이 끊김 없이 연결됨 +- [ ] 빈 상태(Empty State) 시나리오가 고려됨 + +--- + +## 📱 화면 구성 작성 + +### 화면 설계 원칙 + +> 1억 명 이상이 사용하는 서비스(Instagram, Facebook, Twitter, YouTube, KakaoTalk 등)의 검증된 UX 패턴을 참고한다. 단, 그대로 복제하지 않고 **심플하고 미니멀한 방향**으로 재해석한다. + +**핵심 관점** (`frontend-design` 스킬 연동): +- **정보 계층 (Information Hierarchy)**: 가장 중요한 정보가 시선을 먼저 사로잡는가? +- **인터랙션 플로우 (Interaction Flow)**: 사용자의 다음 행동이 자연스러운가? +- **감성 디자인 (Emotional Design)**: 사용자가 이 화면에서 어떤 감정을 느끼는가? +- **접근성 (Accessibility)**: 다양한 사용자가 불편 없이 사용할 수 있는가? +- **컨텍스트 (Context)**: 사용자가 이 화면에 도달하는 상황은? + +**안티패턴**: +- ❌ 단순 CRUD 나열 (목록 → 상세 → 수정 → 삭제) +- ❌ 데이터베이스 필드를 그대로 화면에 배치 +- ❌ 기능 중심 설계 (기능이 아닌 사용자 목표 중심으로) +- ❌ 과도한 정보 표시 (한 화면에 너무 많은 정보) + +**좋은 패턴**: +- ✅ 사용자 목표 중심 설계 (인스타그램: "순간을 공유한다" → 카메라 버튼이 중앙) +- ✅ 점진적 정보 공개 (페이스북: 피드에서 요약 → 탭하면 상세) +- ✅ 빈 상태(Empty State) 설계 (첫 사용자가 보는 화면) +- ✅ 로딩/에러/성공 상태 고려 + +### 템플릿 + +```markdown +### 화면 {N}: {화면 이름} + +- **경로**: /{path} +- **도메인**: {domain} +- **화면 목적**: {사용자 관점에서 이 화면의 핵심 가치 한 줄} +- **UX 레퍼런스**: {참고할 대형 서비스의 유사 화면} +- **핵심 인터랙션**: + - {사용자의 주요 행동 1} + - {사용자의 주요 행동 2} +- **정보 구조**: {화면에 표시되는 정보의 우선순위 - 가장 중요한 것부터} +- **네비게이션**: {성공 → /path, 취소 → /path} +- **관련 기능**: 기능 {N} +``` + +### 예시 (users 도메인) + +```markdown +## 📱 화면 구성 + +> **디자인 원칙**: Instagram, KakaoTalk 등 1억 명 이상 사용하는 서비스의 UX를 참고하되, 심플하고 미니멀한 구성을 지향한다. `frontend-design` 스킬을 활용하여 디자인 품질을 확보한다. + +### 화면 1: 로그인 + +- **경로**: /login +- **도메인**: users +- **화면 목적**: 최소한의 마찰로 앱에 진입한다 +- **UX 레퍼런스**: Instagram 로그인 (로고 + 단 2개 입력필드 + 소셜 로그인) +- **핵심 인터랙션**: + - 이메일/비밀번호 입력 후 로그인 + - 소셜 로그인 원탭 진입 +- **정보 구조**: 브랜드 로고 > 입력 폼 > 소셜 로그인 > 회원가입 링크 +- **네비게이션**: 성공 → /home, 회원가입 → /register +- **관련 기능**: 기능 1 (회원가입) + +--- + +### 화면 2: 프로필 + +- **경로**: /profile +- **도메인**: users +- **화면 목적**: 내 정보를 한눈에 확인하고 편집한다 +- **UX 레퍼런스**: Instagram 프로필 (프로필 사진 + 핵심 숫자 + 편집 버튼) +- **핵심 인터랙션**: + - 프로필 사진/닉네임 확인 + - "편집" 탭하여 수정 모드 진입 +- **정보 구조**: 프로필 사진 > 닉네임 > 활동 요약 > 설정 +- **네비게이션**: 편집 → /profile/edit, 설정 → /settings +- **관련 기능**: 기능 2 (프로필 조회), 기능 3 (프로필 수정) +``` diff --git a/plugins/flutter-ddd-builder/.claude-plugin/plugin.json b/plugins/flutter-ddd-builder/.claude-plugin/plugin.json index c69ec66..3546300 100644 --- a/plugins/flutter-ddd-builder/.claude-plugin/plugin.json +++ b/plugins/flutter-ddd-builder/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "flutter-ddd-builder", - "version": "0.1.0", + "version": "1.0.0", "description": "Domain Book 기반 Flutter DDD 아키텍처 코드 자동 생성 플러그인. 비즈니스 로직과 UI를 병렬 팀 작업으로 구축하고 실시간 품질 검증을 제공합니다.", "author": { "name": "Andy", diff --git a/plugins/flutter-ddd-builder/README.md b/plugins/flutter-ddd-builder/README.md index 94cae90..297858c 100644 --- a/plugins/flutter-ddd-builder/README.md +++ b/plugins/flutter-ddd-builder/README.md @@ -9,7 +9,7 @@ ## ✨ Features - **🔄 도메인 → 코드 자동 변환**: Domain Book을 읽고 Freezed 모델, Riverpod 서비스, API 클라이언트 자동 생성 -- **🎨 화면 기획 자동 생성**: PRD 기반 ASCII art 화면 기획 후 UI 코드 생성 +- **🎨 화면 기획 자동 생성**: Domain Book features (📱 화면 구성) 기반 ASCII art 화면 기획 후 UI 코드 생성 - **👥 팀 기반 병렬 처리**: Git worktree + 에이전트 팀으로 여러 도메인/화면 동시 구현 - **✅ 실시간 품질 검증**: 파일 작성 후 즉시 `flutter analyze`, 통합 전 `flutter build` 검증 - **🔗 기존 인프라 활용**: `swagger_parser` + Freezed 3.x + Riverpod 3.x @@ -48,13 +48,27 @@ cc # Claude Code 실행 └── post/ └── ... ``` -3. **PRD 문서**: `ai-context/PRD.md` (UI 생성 시 필요) -4. **Git 저장소**: 프로젝트가 git으로 관리되어야 함 +3. **Git 저장소**: 프로젝트가 git으로 관리되어야 함 -### 선택 사항 +### 선택 사항 (API 클라이언트 자동 생성) -- `swagger_parser.yaml` 설정 (OpenAPI 기반 API 클라이언트 생성 시) -- `swagger/api_spec.json` (API 스펙이 있는 경우) +`swagger_parser.yaml`에서 OpenAPI 스펙 소스를 설정합니다 (둘 중 하나 선택): + +```yaml +swagger_parser: + # 방법 1 (추천): 실행 중인 백엔드에서 직접 가져오기 + schema_url: http://localhost:8000/openapi.json + + # 방법 2: 정적 파일 사용 + # schema_path: swagger/api_spec.json + + output_directory: lib/generated/api + json_serializer: freezed + use_freezed3: true + language: dart +``` + +> **Note**: `schema_url` 사용 시 python-fastapi-programmer로 생성된 백엔드 서버가 실행 중이어야 합니다 (`uvicorn main:app --reload`) ## 🎯 Usage @@ -112,14 +126,10 @@ Domain Book을 읽고 도메인 레이어를 자동 생성합니다. ### `/ui` - UI 레이어 생성 -PRD와 Domain Book API 명세를 읽고 화면을 자동 생성합니다. +Domain Book features (📱 화면 구성)와 API 명세를 읽고 화면을 자동 생성합니다. ```bash -# 기본 경로 사용 (ai-context/PRD.md) /ui - -# 커스텀 경로 지정 -/ui --prd-path custom-path/requirements.md ``` **생성되는 코드:** @@ -131,7 +141,7 @@ PRD와 Domain Book API 명세를 읽고 화면을 자동 생성합니다. - `lib/apps/ui/router/domains/{domain}.dart` - Route 클래스 **워크플로우:** -1. PRD + Domain Book API 읽기 +1. Domain Book features (📱 화면 구성) + API 읽기 2. ASCII art 화면 기획 생성 → 터미널 출력 + `ai-context/screen-plan.md` 저장 3. 사용자 승인 (수정 요청 가능) 4. 에이전트 팀 생성 + Git worktree 분리 @@ -149,7 +159,7 @@ PRD와 Domain Book API 명세를 읽고 화면을 자동 생성합니다. ## 경로 설정 - domain_book_path: ai-context/domain-books/ -- prd_path: ai-context/PRD.md +- domain_book_features: ai-context/domain-books/*/features.md - screen_plan_path: ai-context/screen-plan.md ## Git 설정 @@ -184,15 +194,13 @@ EOF ### 전체 프로세스 ``` -1. Domain Book 작성 (domain-book-builder 사용) +1. Domain Book 작성 (domain-book-builder 사용, 📱 화면 구성 포함) ↓ 2. /logic 실행 → 비즈니스 로직 레이어 생성 ↓ -3. PRD 작성 - ↓ -4. /ui 실행 → UI 레이어 생성 +3. /ui 실행 → UI 레이어 생성 ↓ -5. 완성! 🎉 +4. 완성! 🎉 ``` ### 팀 기반 병렬 처리 diff --git a/plugins/flutter-ddd-builder/agents/ui-planner.md b/plugins/flutter-ddd-builder/agents/ui-planner.md index 9af72da..2f1de8b 100644 --- a/plugins/flutter-ddd-builder/agents/ui-planner.md +++ b/plugins/flutter-ddd-builder/agents/ui-planner.md @@ -1,12 +1,12 @@ --- -description: Generates ASCII art screen wireframes from PRD and Domain Book APIs. Creates structured screen plan with layouts, service mappings, and component specifications for UI implementation. +description: Generates ASCII art screen wireframes from Domain Book features (📱 화면 구성 section) and APIs. Creates structured screen plan with layouts, service mappings, and component specifications for UI implementation. whenToUse: | This agent is spawned by the /ui command to generate screen plans before UI implementation. Context: /ui command needs screen plan generation orchestrator: "Spawn ui-planner to create ASCII art wireframes" - system: "ui-planner agent generates screen layouts from PRD" + system: "ui-planner agent generates screen layouts from Domain Book features" name: ui-planner model: sonnet @@ -19,12 +19,12 @@ tools: # UI Planner System Prompt -You are the ui-planner, responsible for creating ASCII art screen wireframes from PRD. +You are the ui-planner, responsible for creating ASCII art screen wireframes from Domain Book features. ## Your Task Generate screen plans by: -1. Reading PRD and Domain Book APIs +1. Reading Domain Book features and APIs 2. Creating ASCII art wireframes 3. Mapping services to screens 4. Outputting structured JSON + Markdown @@ -33,13 +33,18 @@ Generate screen plans by: ### 1. Read Inputs -**PRD**: +**Domain Book features (📱 화면 구성):** ``` -Read ai-context/PRD.md -Extract: -- Screen list -- User flows -- Feature requirements +For each domain in ai-context/domain-books/: + Read {domain}/features.md + Extract from 📱 화면 구성 section: + - Screen list (화면 이름, 경로, 도메인) + - UX references (참고 서비스 패턴) + - Navigation flows (화면 간 이동 경로) + - Key interactions (핵심 인터랙션) + +Apply `frontend-design` skill for design quality. +Reference patterns from services with 100M+ users (Instagram, Facebook, Twitter, etc.) - but keep it simple and minimal. ``` **Domain APIs**: @@ -60,7 +65,7 @@ available_services = { ### 2. Design Screens -For each screen in PRD, create ASCII art layout. +For each screen in Domain Book features.md (📱 화면 구성), create ASCII art layout. **Example - Login Screen**: ``` @@ -214,7 +219,7 @@ Follow project conventions: ## Success Criteria -- [ ] All screens from PRD included +- [ ] All screens from Domain Book features included - [ ] ASCII art is clear and detailed - [ ] Services mapped correctly - [ ] Navigation flows defined diff --git a/plugins/flutter-ddd-builder/commands/logic.md b/plugins/flutter-ddd-builder/commands/logic.md index d735f12..5a38878 100644 --- a/plugins/flutter-ddd-builder/commands/logic.md +++ b/plugins/flutter-ddd-builder/commands/logic.md @@ -135,8 +135,19 @@ git worktree list **Execute once before spawning teammates:** ```bash -# Generate API clients if swagger spec exists -if [ -f swagger/api_spec.json ]; then +# Generate API clients from OpenAPI spec +# swagger_parser.yaml의 schema_url(런타임 엔드포인트) 또는 schema_path(정적 파일) 사용 +if [ -f swagger_parser.yaml ]; then + # schema_url 방식이면 백엔드 서버 접근 가능 여부 확인 + if grep -q "schema_url" swagger_parser.yaml; then + SCHEMA_URL=$(grep "schema_url" swagger_parser.yaml | awk '{print $2}') + if ! curl -s --max-time 5 "$SCHEMA_URL" > /dev/null 2>&1; then + echo "⚠️ Backend server not reachable at $SCHEMA_URL" + echo " Start the server first: uvicorn main:app --reload" + fi + fi + dart run swagger_parser +elif [ -f swagger/api_spec.json ]; then dart run swagger_parser fi diff --git a/plugins/flutter-ddd-builder/commands/start.md b/plugins/flutter-ddd-builder/commands/start.md index 2338d0f..30bd675 100644 --- a/plugins/flutter-ddd-builder/commands/start.md +++ b/plugins/flutter-ddd-builder/commands/start.md @@ -46,8 +46,22 @@ flutter --version # Check required files/directories ls ai-context/domain-books/ # Domain Book must exist -ls swagger/api_spec.json # Optional: OpenAPI spec -ls ai-context/PRD.md # Required for UI generation + +# Check OpenAPI spec availability (swagger_parser용) +# 방법 1: swagger_parser.yaml에 schema_url 설정 (백엔드 서버 실행 필요) +# 방법 2: swagger/api_spec.json 정적 파일 (오프라인 가능) +if [ -f swagger_parser.yaml ]; then + echo "swagger_parser.yaml found" + # schema_url이 설정되어 있으면 백엔드 서버 접근 가능 여부 확인 + if grep -q "schema_url" swagger_parser.yaml; then + SCHEMA_URL=$(grep "schema_url" swagger_parser.yaml | awk '{print $2}') + curl -s --max-time 3 "$SCHEMA_URL" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "⚠️ Backend server not reachable at $SCHEMA_URL" + echo " Start the server: uvicorn main:app --reload" + fi + fi +fi ``` **Validation checklist:** @@ -55,8 +69,8 @@ ls ai-context/PRD.md # Required for UI generation | Requirement | Required For | Check | |-------------|-------------|-------| | `ai-context/domain-books/` with at least 1 domain | Logic + UI | Must exist | -| `swagger/api_spec.json` | API client generation | Optional | -| `ai-context/PRD.md` | UI generation | Required unless --skip-ui | +| `swagger_parser.yaml` with `schema_url` 또는 `swagger/api_spec.json` | API client generation | 둘 중 하나 (Optional) | +| Backend server running (if `schema_url` used) | API client generation | `curl` reachable | | `pubspec.yaml` | All | Must exist | | Git repository | Worktree management | Must be initialized | | `lib/global/types/paginated_response.dart` | Logic | Should exist (boilerplate) | @@ -83,16 +97,16 @@ Display error: Stop execution. ``` -**If PRD is missing and --skip-ui not set:** +**If features.md missing 📱 화면 구성 section and --skip-ui not set:** ``` AskUserQuestion({ questions: [{ - question: "PRD (ai-context/PRD.md) not found. How would you like to proceed?", - header: "Missing PRD", + question: "Domain Book features.md에 📱 화면 구성 섹션이 없습니다. 어떻게 진행할까요?", + header: "화면 구성 없음", multiSelect: false, options: [ {label: "Skip UI generation", description: "Generate only business logic layer"}, - {label: "Create PRD first", description: "I'll stop so you can create ai-context/PRD.md"} + {label: "Add screen section first", description: "I'll stop so you can add 📱 화면 구성 to features.md"} ] }] }) @@ -166,14 +180,38 @@ Run swagger_parser and build_runner to prepare infrastructure code. ```bash # Step 3a: Generate API clients from OpenAPI spec -if [ -f swagger/api_spec.json ]; then +# swagger_parser.yaml 설정에 따라 schema_url(런타임) 또는 schema_path(정적 파일) 사용 +if [ -f swagger_parser.yaml ]; then echo "Step 3/8: Generating API clients from OpenAPI spec..." + + # schema_url 방식이면 백엔드 서버 접근 가능 여부 재확인 + if grep -q "schema_url" swagger_parser.yaml; then + SCHEMA_URL=$(grep "schema_url" swagger_parser.yaml | awk '{print $2}') + if ! curl -s --max-time 5 "$SCHEMA_URL" > /dev/null 2>&1; then + echo "⚠️ Backend server not reachable at $SCHEMA_URL" + AskUserQuestion({ + questions: [{ + question: "Backend server가 응답하지 않습니다. API client 생성을 어떻게 할까요?", + header: "서버 미응답", + multiSelect: false, + options: [ + {label: "서버 시작 후 재시도", description: "uvicorn main:app --reload 실행 후 계속"}, + {label: "건너뛰기", description: "API client 없이 진행 (수동 생성 필요)"} + ] + }] + }) + fi + fi + dart run swagger_parser if [ $? -ne 0 ]; then echo "swagger_parser failed, retrying..." dart run swagger_parser fi +elif [ -f swagger/api_spec.json ]; then + echo "Step 3/8: Generating API clients from static OpenAPI spec..." + dart run swagger_parser fi # Step 3b: Run build_runner for existing code @@ -268,7 +306,7 @@ Otherwise, execute the same workflow as `/ui` command: **6a. Screen Plan Generation:** - Spawn ui-planner agent -- Read PRD + Domain Book APIs +- Read Domain Book features (📱 화면 구성) + APIs - Generate ASCII art wireframes - Create `ai-context/screen-plan.json` and `ai-context/screen-layouts.md` @@ -457,7 +495,7 @@ Load these skills automatically: ## Success Criteria - [ ] All domains from Domain Book implemented -- [ ] All screens from PRD implemented (unless --skip-ui) +- [ ] All screens from Domain Book features implemented (unless --skip-ui) - [ ] Router fully configured - [ ] flutter analyze: no issues - [ ] flutter build: success (appbundle + ios) diff --git a/plugins/flutter-ddd-builder/commands/ui.md b/plugins/flutter-ddd-builder/commands/ui.md index e631495..c6ef733 100644 --- a/plugins/flutter-ddd-builder/commands/ui.md +++ b/plugins/flutter-ddd-builder/commands/ui.md @@ -1,19 +1,19 @@ --- name: ui -description: Generate Flutter UI layer from PRD and screen plan. Creates ASCII art screen designs for approval, then generates ConsumerStatefulWidget pages, components, and router integration with parallel team implementation. -argument-hint: "[--prd-path PATH]" +description: Generate Flutter UI layer from Domain Book features and screen plan. Creates ASCII art screen designs for approval, then generates ConsumerStatefulWidget pages, components, and router integration with parallel team implementation. +argument-hint: "" allowed-tools: "*" --- # /ui - UI Layer Generation -Generate complete Flutter UI layer by creating ASCII art screen plans from PRD, getting user approval, then implementing pages and components using parallel team-based workflow. +Generate complete Flutter UI layer by creating ASCII art screen plans from Domain Book features, getting user approval, then implementing pages and components using parallel team-based workflow. ## Your Task Implement the UI layer by: -1. **Reading PRD and Domain APIs** - Parse requirements and available services +1. **Reading Domain Book features and APIs** - Parse requirements and available services 2. **Generating Screen Plan** - Create ASCII art wireframes for user approval 3. **Creating Team** - Spawn agent team for parallel page implementation 4. **Setting Up Worktrees** - Create isolated git worktrees for each screen @@ -21,26 +21,20 @@ Implement the UI layer by: 6. **Quality Verification** - Run `flutter analyze` and build checks 7. **Integration** - Merge worktrees, register routes, cleanup -## Arguments - -- `--prd-path PATH`: Custom path to PRD (default: `ai-context/PRD.md`) - **Example:** ```bash /ui -/ui --prd-path docs/requirements.md ``` ## Step-by-Step Workflow ### Step 1: Read Input Documents -**Read PRD:** +**Read Domain Book features (📱 화면 구성):** ``` -ai-context/PRD.md -- Product requirements -- User flows -- Screen descriptions +ai-context/domain-books/*/features.md +- 📋 주요 기능 (사용자 시나리오) +- 📱 화면 구성 (화면 목록, 경로, 네비게이션, UX 레퍼런스) ``` **Read Domain Book APIs:** @@ -68,7 +62,7 @@ available_services = { Task({ description: "Generate ASCII art screen plan", subagent_type: "ui-planner", - prompt: `Create ASCII art wireframes for all screens described in PRD. + prompt: `Create ASCII art wireframes for all screens defined in Domain Book features.md (📱 화면 구성 section). For each screen: 1. Design layout with ASCII art (boxes, lines, text) @@ -121,7 +115,7 @@ Services: auth.AuthService ... (all screens) -Follow PRD requirements and use available services from Domain Book.` +Follow Domain Book features (📱 화면 구성) and use available services. Apply \`frontend-design\` skill for UX quality. Reference large-scale service patterns (Instagram, Facebook, etc.) for proven UX.` }) ``` @@ -552,14 +546,14 @@ Next Steps: ## Troubleshooting -**PRD not found:** -- Check `ai-context/PRD.md` exists -- Suggest creating PRD first -- Provide template if needed +**Domain Book features not found:** +- Check `ai-context/domain-books/*/features.md` exists +- Verify 📱 화면 구성 section is present in features.md +- Run domain-book-builder first if missing **Screen plan generation fails:** - Retry with ui-planner agent -- Simplify PRD if too complex +- Simplify features.md if too complex - Generate screens one-by-one **Component conflicts:** diff --git a/plugins/python-fastapi-programmer/.claude-plugin/plugin.json b/plugins/python-fastapi-programmer/.claude-plugin/plugin.json index ec4182f..21c896c 100644 --- a/plugins/python-fastapi-programmer/.claude-plugin/plugin.json +++ b/plugins/python-fastapi-programmer/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "python-fastapi-programmer", "description": "Domain Book 기반 FastAPI 프로젝트 자동 생성 - 병렬 코드 생성 및 품질 보증", - "version": "0.1.0", + "version": "1.0.0", "author": { "name": "ureca" },