LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
319 lines
7.2 KiB
Markdown
319 lines
7.2 KiB
Markdown
# 기술 스택 결정 문서
|
|
|
|
## 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 기반 매칭 알고리즘 (정확도 개선) |