163 lines
3.9 KiB
Dart
163 lines
3.9 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import '../../../core/network/network_client.dart';
|
|
import '../../../core/errors/network_exceptions.dart';
|
|
|
|
/// 네이버 GraphQL API 클라이언트
|
|
///
|
|
/// 네이버 지도의 GraphQL API를 호출하여 상세 정보를 가져옵니다.
|
|
class NaverGraphQLApi {
|
|
final NetworkClient _networkClient;
|
|
|
|
static const String _graphqlEndpoint =
|
|
'https://pcmap-api.place.naver.com/graphql';
|
|
|
|
NaverGraphQLApi({NetworkClient? networkClient})
|
|
: _networkClient = networkClient ?? NetworkClient();
|
|
|
|
/// GraphQL 쿼리 실행
|
|
Future<Map<String, dynamic>> fetchGraphQL({
|
|
required String operationName,
|
|
required String query,
|
|
Map<String, dynamic>? variables,
|
|
}) async {
|
|
try {
|
|
final response = await _networkClient.post<Map<String, dynamic>>(
|
|
_graphqlEndpoint,
|
|
data: {
|
|
'operationName': operationName,
|
|
'query': query,
|
|
'variables': variables ?? {},
|
|
},
|
|
options: Options(
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Referer': 'https://map.naver.com/',
|
|
'Origin': 'https://map.naver.com',
|
|
},
|
|
),
|
|
);
|
|
|
|
if (response.data == null) {
|
|
throw ParseException(message: 'GraphQL 응답이 비어있습니다');
|
|
}
|
|
|
|
return response.data!;
|
|
} on DioException catch (e) {
|
|
debugPrint('fetchGraphQL error: $e');
|
|
throw ServerException(
|
|
message: 'GraphQL 요청 중 오류가 발생했습니다',
|
|
statusCode: e.response?.statusCode ?? 500,
|
|
originalError: e,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 장소 상세 정보 가져오기 (한국어 텍스트)
|
|
Future<Map<String, dynamic>> fetchKoreanTextsFromPcmap(String placeId) async {
|
|
const query = '''
|
|
query getKoreanTexts(\$id: String!) {
|
|
place(input: { id: \$id }) {
|
|
id
|
|
name
|
|
category
|
|
businessHours {
|
|
description
|
|
isDayOff
|
|
openTime
|
|
closeTime
|
|
dayOfWeek
|
|
businessHours
|
|
}
|
|
phone
|
|
address {
|
|
roadAddress
|
|
jibunAddress
|
|
}
|
|
description
|
|
menuInfo {
|
|
menus {
|
|
name
|
|
price
|
|
description
|
|
images {
|
|
url
|
|
}
|
|
}
|
|
}
|
|
keywords
|
|
priceCategory
|
|
imageCount
|
|
visitorReviewCount
|
|
visitorReviewScore
|
|
}
|
|
}
|
|
''';
|
|
|
|
try {
|
|
final response = await fetchGraphQL(
|
|
operationName: 'getKoreanTexts',
|
|
query: query,
|
|
variables: {'id': placeId},
|
|
);
|
|
|
|
if (response['errors'] != null) {
|
|
debugPrint('GraphQL errors: ${response['errors']}');
|
|
throw ParseException(message: 'GraphQL 오류: ${response['errors']}');
|
|
}
|
|
|
|
return response['data']?['place'] ?? {};
|
|
} catch (e) {
|
|
debugPrint('fetchKoreanTextsFromPcmap error: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 장소 기본 정보 가져오기
|
|
Future<Map<String, dynamic>> fetchPlaceBasicInfo(String placeId) async {
|
|
const query = '''
|
|
query getPlaceBasicInfo(\$id: String!) {
|
|
place(input: { id: \$id }) {
|
|
id
|
|
name
|
|
category
|
|
phone
|
|
address {
|
|
roadAddress
|
|
jibunAddress
|
|
}
|
|
location {
|
|
latitude
|
|
longitude
|
|
}
|
|
homepageUrl
|
|
bookingUrl
|
|
}
|
|
}
|
|
''';
|
|
|
|
try {
|
|
final response = await fetchGraphQL(
|
|
operationName: 'getPlaceBasicInfo',
|
|
query: query,
|
|
variables: {'id': placeId},
|
|
);
|
|
|
|
if (response['errors'] != null) {
|
|
throw ParseException(message: 'GraphQL 오류: ${response['errors']}');
|
|
}
|
|
|
|
return response['data']?['place'] ?? {};
|
|
} catch (e) {
|
|
debugPrint('fetchPlaceBasicInfo error: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
// 필요시 리소스 정리
|
|
}
|
|
}
|