feat(app): add vworld geocoding and native ads placeholders
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:lunchpick/core/utils/app_logger.dart';
|
||||
|
||||
import '../../../core/network/network_client.dart';
|
||||
@@ -37,6 +38,12 @@ class NaverUrlResolver {
|
||||
return location;
|
||||
}
|
||||
|
||||
// Location이 없는 경우, http.Client로 리다이렉트를 끝까지 따라가며 최종 URL 추출 (fallback)
|
||||
final expanded = await _followRedirectsWithHttp(shortUrl);
|
||||
if (expanded != null) {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// 리다이렉트가 없으면 원본 URL 반환
|
||||
return shortUrl;
|
||||
} on DioException catch (e) {
|
||||
@@ -54,6 +61,12 @@ class NaverUrlResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// Dio 실패 시 fallback으로 http.Client 리다이렉트 추적 시도
|
||||
final expanded = await _followRedirectsWithHttp(shortUrl);
|
||||
if (expanded != null) {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// 오류 발생 시 원본 URL 반환
|
||||
return shortUrl;
|
||||
}
|
||||
@@ -161,4 +174,26 @@ class NaverUrlResolver {
|
||||
void dispose() {
|
||||
// 필요시 리소스 정리
|
||||
}
|
||||
|
||||
/// http.Client를 사용해 리다이렉트를 끝까지 따라가며 최종 URL을 반환한다.
|
||||
/// 실패 시 null 반환.
|
||||
Future<String?> _followRedirectsWithHttp(String shortUrl) async {
|
||||
final client = http.Client();
|
||||
try {
|
||||
final request = http.Request('HEAD', Uri.parse(shortUrl))
|
||||
..followRedirects = true
|
||||
..maxRedirects = 5;
|
||||
final response = await client.send(request);
|
||||
return response.request?.url.toString();
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.error(
|
||||
'_followRedirectsWithHttp error: $e',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
return null;
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ class NaverMapParser {
|
||||
|
||||
// 정규식 패턴
|
||||
static final RegExp _placeIdRegex = RegExp(
|
||||
r'/p/(?:restaurant|entry/place)/(\d+)',
|
||||
r'(?:/p/(?:restaurant|entry/place)/|/place/)(\d+)',
|
||||
);
|
||||
static final RegExp _pinIdRegex = RegExp(r'pinId["=](\d+)');
|
||||
static final RegExp _shortUrlRegex = RegExp(r'naver\.me/([a-zA-Z0-9]+)$');
|
||||
|
||||
// 기본 좌표 (서울 시청)
|
||||
@@ -62,7 +63,7 @@ class NaverMapParser {
|
||||
throw NaverMapParseException('이미 dispose된 파서입니다');
|
||||
}
|
||||
try {
|
||||
AppLogger.debug('NaverMapParser: Starting to parse URL: $url');
|
||||
AppLogger.debug('[naver_url] 원본 URL 수신: $url');
|
||||
|
||||
// URL 유효성 검증
|
||||
if (!_isValidNaverUrl(url)) {
|
||||
@@ -72,7 +73,7 @@ class NaverMapParser {
|
||||
// 짧은 URL인 경우 리다이렉트 처리
|
||||
final String finalUrl = await _apiClient.resolveShortUrl(url);
|
||||
|
||||
AppLogger.debug('NaverMapParser: Final URL after redirect: $finalUrl');
|
||||
AppLogger.debug('[naver_url] resolveShortUrl 결과: $finalUrl');
|
||||
|
||||
// Place ID 추출 (10자리 숫자)
|
||||
final String? placeId = _extractPlaceId(finalUrl);
|
||||
@@ -80,13 +81,12 @@ class NaverMapParser {
|
||||
// 짧은 URL에서 직접 ID 추출 시도
|
||||
final shortUrlId = _extractShortUrlId(url);
|
||||
if (shortUrlId != null) {
|
||||
AppLogger.debug(
|
||||
'NaverMapParser: Using short URL ID as place ID: $shortUrlId',
|
||||
);
|
||||
AppLogger.debug('[naver_url] 단축 URL ID를 Place ID로 사용: $shortUrlId');
|
||||
return _createFallbackRestaurant(shortUrlId, url);
|
||||
}
|
||||
throw NaverMapParseException('URL에서 Place ID를 추출할 수 없습니다: $url');
|
||||
}
|
||||
AppLogger.debug('[naver_url] Place ID 추출 성공: $placeId');
|
||||
|
||||
// 단축 URL인 경우 특별 처리
|
||||
final isShortUrl = url.contains('naver.me');
|
||||
@@ -102,7 +102,10 @@ class NaverMapParser {
|
||||
userLatitude,
|
||||
userLongitude,
|
||||
);
|
||||
AppLogger.debug('NaverMapParser: 단축 URL 파싱 성공 - ${restaurant.name}');
|
||||
AppLogger.debug(
|
||||
'[naver_url] LocalSearch 파싱 성공: '
|
||||
'name=${restaurant.name}, road=${restaurant.roadAddress}',
|
||||
);
|
||||
return restaurant;
|
||||
} catch (e, stackTrace) {
|
||||
AppLogger.error(
|
||||
@@ -120,6 +123,12 @@ class NaverMapParser {
|
||||
userLatitude: userLatitude,
|
||||
userLongitude: userLongitude,
|
||||
);
|
||||
AppLogger.debug(
|
||||
'[naver_url] GraphQL/검색 파싱 결과 요약: '
|
||||
'name=${restaurantData['name']}, '
|
||||
'road=${restaurantData['roadAddress']}, '
|
||||
'phone=${restaurantData['phone']}',
|
||||
);
|
||||
return _createRestaurant(restaurantData, placeId, finalUrl);
|
||||
} catch (e) {
|
||||
if (e is NaverMapParseException) {
|
||||
@@ -150,7 +159,11 @@ class NaverMapParser {
|
||||
/// URL에서 Place ID 추출
|
||||
String? _extractPlaceId(String url) {
|
||||
final match = _placeIdRegex.firstMatch(url);
|
||||
return match?.group(1);
|
||||
if (match != null) return match.group(1);
|
||||
|
||||
// 핀 공유 형식: pinId="1234567890" 또는 pinId=1234567890
|
||||
final pinMatch = _pinIdRegex.firstMatch(url);
|
||||
return pinMatch?.group(1);
|
||||
}
|
||||
|
||||
/// 짧은 URL에서 ID 추출
|
||||
@@ -188,6 +201,10 @@ class NaverMapParser {
|
||||
longitude: userLongitude,
|
||||
display: _searchDisplayCount,
|
||||
);
|
||||
AppLogger.debug(
|
||||
'[naver_url] URL 기반 검색 응답 개수: ${searchResults.length}, '
|
||||
'첫 번째: ${searchResults.isNotEmpty ? searchResults.first.title : '없음'}',
|
||||
);
|
||||
|
||||
if (searchResults.isNotEmpty) {
|
||||
// place ID가 포함된 결과 찾기
|
||||
@@ -226,6 +243,10 @@ class NaverMapParser {
|
||||
longitude: userLongitude,
|
||||
display: _searchDisplayCount,
|
||||
);
|
||||
AppLogger.debug(
|
||||
'[naver_url] Place ID 검색 응답 개수: ${searchResults.length}, '
|
||||
'첫 번째: ${searchResults.isNotEmpty ? searchResults.first.title : '없음'}',
|
||||
);
|
||||
|
||||
if (searchResults.isNotEmpty) {
|
||||
AppLogger.debug(
|
||||
@@ -273,6 +294,9 @@ class NaverMapParser {
|
||||
variables: {'id': placeId},
|
||||
query: NaverGraphQLQueries.placeDetailQuery,
|
||||
);
|
||||
AppLogger.debug(
|
||||
'[naver_url] places query 응답 keys: ${response.keys.toList()}',
|
||||
);
|
||||
|
||||
// places 응답 처리 (배열일 수도 있음)
|
||||
final placesData = response['data']?['places'];
|
||||
@@ -299,6 +323,9 @@ class NaverMapParser {
|
||||
variables: {'id': placeId},
|
||||
query: NaverGraphQLQueries.nxPlaceDetailQuery,
|
||||
);
|
||||
AppLogger.debug(
|
||||
'[naver_url] nxPlaces query 응답 keys: ${response.keys.toList()}',
|
||||
);
|
||||
|
||||
// nxPlaces 응답 처리 (배열일 수도 있음)
|
||||
final nxPlacesData = response['data']?['nxPlaces'];
|
||||
|
||||
Reference in New Issue
Block a user