feat: 초기 프로젝트 설정 및 LunchPick 앱 구현

LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다.

주요 기능:
- 네이버 지도 연동 맛집 추가
- 랜덤 메뉴 추천 시스템
- 날씨 기반 거리 조정
- 방문 기록 관리
- Bluetooth 맛집 공유
- 다크모드 지원

기술 스택:
- Flutter 3.8.1+
- Riverpod 상태 관리
- Hive 로컬 DB
- Clean Architecture

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-07-30 19:03:28 +09:00
commit 85fde36157
237 changed files with 30953 additions and 0 deletions

View File

@@ -0,0 +1,319 @@
# 기술 스택 결정 문서
## 1. 개요
이 문서는 "오늘 뭐 먹Z?" 앱의 네이버 단축 URL 처리 기능 확장을 위한 기술 스택 선택 근거와 결정 사항을 설명합니다.
## 2. 현재 기술 스택 분석
### 2.1 기존 스택
- **상태 관리**: Riverpod 2.4.0
- **로컬 저장소**: Hive 2.2.3
- **네트워킹**: Dio 5.4.0, HTTP 1.1.0
- **HTML 파싱**: html 0.15.4
- **아키텍처**: Clean Architecture
### 2.2 강점 분석
- Riverpod의 강력한 의존성 주입과 상태 관리
- Hive의 빠른 성능과 간단한 사용법
- Clean Architecture로 인한 명확한 책임 분리
## 3. 네이버 URL 처리를 위한 기술 선택
### 3.1 HTTP 클라이언트
#### 선택: Dio + HTTP (하이브리드 접근)
**근거:**
- **Dio**: 인터셉터, 타임아웃, 재시도 등 고급 기능 필요한 API 호출용
- **HTTP**: 단순한 리다이렉션 처리와 기존 NaverMapParser 호환성
**구현 전략:**
```dart
// API 호출용 (Dio)
class NaverLocalApiClient {
final Dio _dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
))..interceptors.addAll([
LogInterceptor(),
RetryInterceptor(),
]);
}
// 단순 요청용 (HTTP)
class NaverUrlResolver {
final http.Client _client;
}
```
### 3.2 HTML 파싱
#### 선택: html 패키지 유지
**근거:**
- 이미 프로젝트에서 사용 중
- DOM 기반 파싱으로 안정적
- 네이버 지도 페이지 구조에 적합
**대안 검토:**
- ❌ BeautifulSoup (Python 전용)
- ❌ web_scraper (기능 중복, 추가 의존성)
- ✅ html (현재 선택)
### 3.3 상태 관리
#### 선택: Riverpod 유지
**근거:**
- 이미 프로젝트 전체에서 사용 중
- 컴파일 타임 안전성
- 의존성 주입 기능 내장
- 테스트 용이성
**Provider 구조:**
```dart
// 새로운 Provider 추가
final naverUrlProcessorProvider = Provider((ref) {
return NaverUrlProcessor(
mapParser: ref.watch(naverMapParserProvider),
apiClient: ref.watch(naverLocalApiClientProvider),
);
});
```
### 3.4 로컬 캐싱
#### 선택: Hive + 메모리 캐시 조합
**근거:**
- **Hive**: 영구 저장이 필요한 식당 데이터
- **메모리 캐시**: URL 리다이렉션, API 결과 등 임시 데이터
**구현:**
```dart
class CacheManager {
// 메모리 캐시 (LRU)
final _urlCache = LruMap<String, String>(maximumSize: 100);
final _apiCache = LruMap<String, dynamic>(maximumSize: 50);
// Hive 박스
late Box<Restaurant> _restaurantBox;
}
```
### 3.5 에러 처리 및 로깅
#### 선택: 계층별 예외 + 구조화된 로깅
**근거:**
- Clean Architecture의 계층 분리 원칙 준수
- 디버깅 용이성
- 프로덕션 모니터링 준비
**구현:**
```dart
// 계층별 예외
sealed class AppException implements Exception {
final String message;
final StackTrace? stackTrace;
}
class DataException extends AppException {}
class DomainException extends AppException {}
class PresentationException extends AppException {}
// 구조화된 로깅
class StructuredLogger {
void log(LogLevel level, String message, {
Map<String, dynamic>? data,
Exception? error,
StackTrace? stackTrace,
});
}
```
## 4. 아키텍처 패턴 결정
### 4.1 Repository Pattern 확장
**결정**: Repository Pattern + Facade Pattern
**근거:**
- 복잡한 네이버 URL 처리 로직을 단순한 인터페이스로 제공
- 기존 Repository 구조와 일관성 유지
```dart
// Facade 패턴 적용
class NaverUrlProcessor {
// 복잡한 내부 로직을 숨김
Future<Restaurant> processNaverUrl(String url) {
// 1. URL 검증
// 2. 리다이렉션
// 3. 스크래핑
// 4. API 호출
// 5. 매칭 및 병합
}
}
```
### 4.2 의존성 주입
**결정**: Riverpod Provider 기반 DI
**근거:**
- 기존 프로젝트 구조와 일치
- 런타임 오버헤드 최소화
- 테스트 시 모킹 용이
## 5. 외부 서비스 통합
### 5.1 네이버 로컬 API
**결정**: 직접 통합
**근거:**
- 공식 SDK 없음
- REST API로 간단한 구조
- 필요한 기능만 선택적 구현 가능
**API 엔드포인트:**
```dart
class NaverApiEndpoints {
static const String localSearch = '/v1/search/local.json';
static const String placeDetail = '/v1/search/place/detail';
}
```
### 5.2 CORS 프록시 (웹 환경)
**결정**: allorigins.win 사용
**근거:**
- 무료 서비스
- 안정적인 가동률
- JSON 응답 지원
**대안:**
- ❌ 자체 프록시 서버 (유지보수 부담)
- ❌ cors-anywhere (제한적)
- ✅ allorigins.win (선택)
## 6. 테스트 전략
### 6.1 테스트 프레임워크
**결정**: Flutter Test + Mockito
**근거:**
- Flutter 기본 제공
- Riverpod과 호환
- 풍부한 매칭 기능
### 6.2 테스트 범위
```yaml
단위 테스트:
- URL 파서: 90% 이상
- API 클라이언트: 85% 이상
- 매칭 알고리즘: 95% 이상
통합 테스트:
- URL 처리 파이프라인: 핵심 시나리오
- Repository 통합: 주요 플로우
E2E 테스트:
- 실제 네이버 URL로 테스트 (CI 제외)
```
## 7. 성능 고려사항
### 7.1 네트워크 최적화
**결정:**
- Connection pooling (Dio 기본 제공)
- Request 타임아웃: 10초
- 재시도: 최대 3회
### 7.2 메모리 최적화
**결정:**
- LRU 캐시 크기 제한
- 이미지 데이터 제외
- 주기적 캐시 정리
## 8. 보안 고려사항
### 8.1 API 키 관리
**결정**: 환경 변수 + 난독화
```dart
// 컴파일 타임 주입
const String apiKey = String.fromEnvironment('NAVER_API_KEY');
// 런타임 난독화
final obfuscatedKey = base64.encode(utf8.encode(apiKey));
```
### 8.2 네트워크 보안
**결정:**
- HTTPS 전용
- Certificate pinning (선택적)
- Request 서명 검증
## 9. 마이그레이션 계획
### 9.1 단계별 적용
1. **Phase 1**: 기본 구조 구현
- NaverLocalApiClient
- 기본 에러 처리
2. **Phase 2**: 고급 기능
- 캐싱 레이어
- 매칭 알고리즘
3. **Phase 3**: 최적화
- 성능 튜닝
- 모니터링 추가
### 9.2 롤백 계획
- Feature flag로 새 기능 제어
- 기존 NaverMapParser 유지
- 점진적 트래픽 전환
## 10. 결론
### 10.1 핵심 결정 사항
| 영역 | 선택 | 이유 |
|------|------|------|
| HTTP 클라이언트 | Dio + HTTP | 용도별 최적화 |
| 상태 관리 | Riverpod | 프로젝트 일관성 |
| 로컬 저장소 | Hive | 기존 인프라 활용 |
| 캐싱 | Hive + Memory | 성능과 영속성 균형 |
| 아키텍처 | Clean + Facade | 복잡도 관리 |
### 10.2 예상 효과
- **개발 속도**: 기존 스택 활용으로 빠른 구현
- **유지보수성**: 명확한 책임 분리로 관리 용이
- **확장성**: 다른 플랫폼 추가 시 쉬운 확장
- **성능**: 캐싱과 최적화로 빠른 응답
### 10.3 리스크 및 대응
| 리스크 | 영향도 | 대응 방안 |
|--------|--------|-----------|
| API 제한 | 중 | 캐싱 강화, Rate limiting |
| 네이버 구조 변경 | 높 | 파서 업데이트 자동화 |
| CORS 프록시 장애 | 중 | 대체 프록시 준비 |
### 10.4 향후 고려사항
- GraphQL 도입 검토 (복잡한 쿼리 증가 시)
- 자체 백엔드 구축 (사용자 증가 시)
- ML 기반 매칭 알고리즘 (정확도 개선)