# 기술 스택 결정 문서 ## 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(maximumSize: 100); final _apiCache = LruMap(maximumSize: 50); // Hive 박스 late Box _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? data, Exception? error, StackTrace? stackTrace, }); } ``` ## 4. 아키텍처 패턴 결정 ### 4.1 Repository Pattern 확장 **결정**: Repository Pattern + Facade Pattern **근거:** - 복잡한 네이버 URL 처리 로직을 단순한 인터페이스로 제공 - 기존 Repository 구조와 일관성 유지 ```dart // Facade 패턴 적용 class NaverUrlProcessor { // 복잡한 내부 로직을 숨김 Future 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 기반 매칭 알고리즘 (정확도 개선)