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:
167
lib/data/api/naver/naver_graphql_api.dart
Normal file
167
lib/data/api/naver/naver_graphql_api.dart
Normal file
@@ -0,0 +1,167 @@
|
||||
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() {
|
||||
// 필요시 리소스 정리
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user