올려주신 파일들을 바탕으로 사진을 찍은 후 데이터를 서버로 전송하고, 서버에서 API를 통해 데이터를 처리하며, 웹뷰로 결과를 뿌려주는 과정이 어떻게 구현되어 있는지 상세히 분석하겠습니다. 이를 위해 코드의 흐름을 따라갈 텐데요, 주요 단계는 다음과 같습니다:
- 사진 촬영 및 데이터 전송
- 사진을 찍고 데이터를 서버로 전송하는 기능은 camera_bloc.dart, camera_service.dart, 그리고 camera_repository.dart 파일에서 구현되어 있을 가능성이 높습니다.
- 이 중 사진 촬영과 서버로 데이터를 보내는 로직을 확인하겠습니다.
- 서버에서 데이터 처리 및 응답
- 서버의 PHP 파일(php.dart)에서 처리되는 과정을 분석해보겠습니다.
- 웹뷰를 통한 결과 표시
- 웹뷰에 관련된 로직은 custom_webview.dart, webview_bloc.dart, 그리고 webview_page.dart 파일에 포함되어 있을 가능성이 높습니다.
분석 결과
파일에서 각 단계의 구현 내용을 찾아서 알려드리겠습니다. 조금만 기다려 주세요.
'sales\' => isset($item[\'monthSold\']) ? $item[\'monthSold\'] : 0,\n//\'num_iid\' => $item[\'offerId\'],\n//\'detail_url\' => \'https://detail.1688.com/offer/\'.$item[\'offerId\'].\'.html\',\n//\'repurchaseRate\' => isset($item[\'repurchaseRate\']) ? $item[\'repurchaseRate\'] : 0,\n//\'compositeServiceScore\' => isset($item[\'sellerDataInfo\'][\'compositeServiceScore\']) ?\n//$item[\'sellerDataInfo\'][\'compositeServiceScore\'] : 0,\n//\'tradeScore\' => isset($item[\'tradeScore\']) ? $item[\'tradeScore\'] : 0,\n//\'out_text\' => $out_text\n//];\n//}, $items);\n//} else{\n//return array();\n//}\n//\n//\n//}\n//}\n//\n//// 실행 부분\n//$search = new ImageSearch();\n//$result = $search->processRequest();\n//\n//// 실행 부분을 다음과 같이 수정\n//if($result) {\n//$tb[\'title\'] = \'이미지 검색 결과\';\n//\n//// 필요한 변수들 초기화\n//$api_data = $result[\'api_data\'] ?? [];\n//$platform_icon = $result[\'platform_icon\'] ?? \'\';\n//$platform = $result[\'api_data\'][\'view_platform\'] ?? \'\';\n//$imageDataUri = $result[\'imageDataUri\'] ?? \'\';\n//$imageBase64 = isset($result[\'imageBase64\']) ? $result[\'imageBase64\'] : \'\';\n//$page_size = ImageSearchConfig::PAGE_SIZE;\n//\n//$icon_img = ($platform == \'1688\') ?\n//TB_IMG_URL."/icon/1688_icon.png" :\n//TB_IMG_URL."/icon/tao_icon.png";\n//\n//// price 변수 초기화\n//$price_min_clean = isset($_REQUEST[\'price_min\']) ?\n//htmlspecialchars($_REQUEST[\'price_min\']) : \'\';\n//$price_max_clean = isset($_REQUEST[\'price_max\']) ?\n//htmlspecialchars($_REQUEST[\'price_max\']) : \'\';\n//\n//$page_count = isset($result[\'api_data\'][\'items\'][\'page_count\']) ?\n//$result[\'api_data\'][\'items\'][\'page_count\'] : 0;\n//\n//$image_id = isset($result[\'api_data\'][\'items\'][\'image_id\']) ? $result[\'api_data\'][\'items\'][\'image_id\'] : \'\';\n//\n//// out_text 추가\n//if(isset($api_data[\'items\'][\'item\'])) {\n//foreach($api_data[\'items\'][\'item\'] as &$item) {\n//$score = round($item[\'compositeServiceScore\'] ?? 0);\n//$item_use_count = 0;\n//\n//if(isset($item[\'num_iid\'])) {\n//$sql = "select count(*) as cnt from shop_goods_review where gs_id = \'{$item[\'num_iid\']}\'";\n//$row_cnt = sql_fetch($sql);\n//$item_use_count = (int)$row_cnt[\'cnt\'];\n//}\n//\n//$output_star = \'\';\n//for($i = 0; $i < 5; $i++) {\n//$file_name = $i < $score ? \'icon_star.svg\' : \'icon_star_none.svg\';\n//$output_star .= "";\n//}\n//\n//$item[\'out_text\'] = "\n//
\n//";\n//}\n//unset($item); // 참조 해제\n//}\n//\n//include_once("./_head.php");\n//include_once(TB_THEME_PATH.\'/search_Img.skin_view.php\');\n//include_once("./_tail.php");\n//}\n//?>\n//\n//////////////////////////\n//\n/////search.php\n// $price_max) {\n//$temp = $price_min;\n//$price_min = $price_max;\n//$price_max = $temp;\n//}\n//\n//} else {\n//$icon_img = TB_IMG_URL."/icon/tao_icon.png";\n//$price_min = $price_max = null;\n//}\n//\n//if($ss_tx == "여성의류" || $ss_tx =="여성 의류"){\n//\n//$q = word_parsing("여자 의류");\n//}else{\n//$q = word_parsing($ss_tx);\n//\n//\n//}\n//\n//\n//\n///*// 금지어 체크\n//$forbiddenWords = array(\'소주\',\'맥주\',\'양주\',\'위스키\',\'양조주\',\'포도주\',\'와인\',\'사과주\',\'벌꿀주\',\'스타우트\',\'막걸리\',\'동동주\',\'청주\',\'사케\',\'탁주\',\'브랜디\',\'럼주\',\'데킬라\',\'데낄라\',\'보드카\',\'아쿠아비트\',\'라키아\',\'리큐르\',\'칵테일\',\'전통주\',\'담금주\',\'발포주\',\'과일주\',\'소맥\');\n//\n//foreach ($forbiddenWords as $word) {\n//if (stripos($ss_tx, $word) !== false) {\n// alert("금지어 \'{$word}\'가 발견되었습니다.");\n// exit();\n//}\n//}*/\n//\n//\n//\n//$pt_id = \'admin\';\n//insert_popular($pt_id, $ss_tx);\n//\n//\n//\n//\n//\n//\n//$get = $_GET;\n//unset($get[\'sort\'], $get[\'ss_tx\'], $get[\'page\'], $get[\'price_range\']);\n//\n//// 검색 실행\n//$page_size = 20; // 기본값 설정\n//$search_result = search_items($platform, $q, $page, $sort_actual, $price_min, $price_max, $filter_string, $page_size );\n//// $search_result = search_items($platform, $q, $page, $sort, $price_min, $price_max, $filter_string, $page_size );\n//\n//\n//\n////print_r2($search_result);\n//\n////echo in_array(\'totalEpScoreLv1\', array_map(\'trim\' , $filter)) ? \'별점 5점\' : \'\';\n////echo in_array(\'totalEpScoreLv2\', array_map(\'trim\' , $filter)) ? \'별점 4.5점\' : \'\';\n////echo in_array(\'totalEpScoreLv3\', array_map(\'trim\' , $filter)) ? \'별점 4점\' : \'\';\n////echo in_array(\'totalEpScoreLv4\', array_map(\'trim\' , $filter)) ? \'별점 4점이하\' : \'\';\n//\n//$filter2 = implode(\', \', array_intersect_key([\n//\'totalEpScoreLv1\' => \'별점 5점\',\n//\'totalEpScoreLv2\' => \'별점 4.5점\',\n//\'totalEpScoreLv3\' => \'별점 4점\',\n//\'totalEpScoreLv4\' => \'별점 4점이하\'\n//], array_flip(array_map(\'trim\', $filter))));\n//\n//\n//\n//\n////web_debug_log("응답값 ", $search_result);\n//// 결과 처리\n//if ($platform == \'1688\') {\n//if ($search_result[\'APIresult\'][\'result\'][\'success\']) {\n//$api_response = $search_result[\'APIresult\'][\'result\'][\'result\'];\n//$alibaba_text = detectLanguage($api_response[\'data\'][0][\'subjectTrans\']) ?? \'cn\';\n//\n//\n//\n//$api_data[\'items\'] = array(\n//\'page\' => $api_response[\'currentPage\'],\n//\'real_total_results\' => $api_response[\'totalRecords\'],\n//\'total_results\' => $api_response[\'totalRecords\'],\n//\'page_size\' => $api_response[\'pageSize\'],\n//\'page_count\' => $api_response[\'totalPage\'],\n//\'item\' => array(),\n//);\n//\n//foreach ($api_response[\'data\'] as $item) {\n//\n//$promotion_price = isset($item[\'priceInfo\'][\'promotionPrice\']) ? $item[\'priceInfo\'][\'promotionPrice\'] : $item[\'priceInfo\'][\'price\'];\n//$api_data[\'items\'][\'item\'][] = array(\n//\'title\' => isset($item[\'subjectTrans\']) ? $item[\'subjectTrans\'] : \'\',\n//\'pic_url\' => $item[\'imageUrl\'],\n//\'price\' => $item[\'priceInfo\'][\'price\'],\n//\'promotion_price\' => $promotion_price,\n//\'sales\' => isset($item[\'monthSold\']) ? $item[\'monthSold\'] : 0 ,\n//\'num_iid\' => $item[\'offerId\'],\n//\'tag_percent\' => $item[\'repurchaseRate\'],\n//\'detail_url\' => \'https://detail.1688.com/offer/\' . $item[\'offerId\'] . \'.html\',\n//\'repurchaseRate\' => $item[\'repurchaseRate\'], //재구매율\n//\'tradeScore\' => isset($item[\'tradeScore\']) ? (float)$item[\'tradeScore\'] : 0 , //별점\n//\'tradeMedalLevel\' => isset($item[\'sellerDataInfo\'][\'tradeMedalLevel\']) ? (float)$item[\'sellerDataInfo\'][\'tradeMedalLevel\'] : 0,\n//\'compositeServiceScore\' => isset($item[\'sellerDataInfo\'][\'compositeServiceScore\']) ? (float)$item[\'sellerDataInfo\'][\'compositeServiceScore\'] : 0,\n//\'logisticsExperienceScore\' => isset($item[\'sellerDataInfo\'][\'logisticsExperienceScore\']) ? (float)$item[\'sellerDataInfo\'][\'logisticsExperienceScore\'] : 0,\n//\'disputeComplaintScore\' => isset($item[\'sellerDataInfo\'][\'disputeComplaintScore\']) ? (float)$item[\'sellerDataInfo\'][\'disputeComplaintScore\'] : 0,\n//\'offerExperienceScore\' => isset($item[\'sellerDataInfo\'][\'offerExperienceScore\']) ? (float)$item[\'sellerDataInfo\'][\'offerExperienceScore\'] : 0,\n//\'afterSalesExperienceScore\' => isset($item[\'sellerDataInfo\'][\'afterSalesExperienceScore\']) ? (float)$item[\'sellerDataInfo\'][\'afterSalesExperienceScore\'] : 0,\n//\'consultingExperienceScore\' => isset($item[\'sellerDataInfo\'][\'consultingExperienceScore\']) ? (float)$item[\'sellerDataInfo\'][\'consultingExperienceScore\'] : 0,\n//\'repeatPurchasePercent\' => isset($item[\'sellerDataInfo\'][\'repeatPurchasePercent\']) ? (float)$item[\'sellerDataInfo\'][\'repeatPurchasePercent\'] : 0,\n//\n//);\n//}\n//$api_data[\'view_platform\']= $platform;\n//} else {\n//die(\'api 1688 에러입니다.\');\n//}\n//} else {\n//if($search_result[\'result\']){\n//$api_data = $search_result[\'APIresult\'];\n//$api_data[\'view_platform\']= $platform;\n//\n//\n//}else{\n//die(\'api 타오바오 에러입니다.\');\n//}\n//}\n//\n//\n//\n//// 결과 표시\n//include_once(TB_THEME_PATH.\'/search.skin_view.php\');\n//include_once("./_tail.php");\n//\n//?>\n//\n//\n//\n//https://bijjya.com/shop/search.php?ss_tx=%EC%9A%B8%20%EC%BD%94%ED%8A%B8\n//\n//https://bijjya.com/shop/search.php?ss_tx=울%20코트\n//\n//https://bijjya.com/shop/search.php?ss_tx=%EB%B0%94%EC%A7%80\n//https://bijjya.com/shop/search.php?ss_tx=바지\n//\n//검색결과 파라미터 ss_tx',
'custom_webview': "import 'package:bijjya/services/preload_sevice.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\n\nclass CustomWebView extends StatelessWidget {\n final int index;\n final String initialUrl;\n\n const CustomWebView({\n super.key,\n required this.index,\n required this.initialUrl,\n });\n\n static Future initializeWebViewSettings() async {\n try {\n await InAppWebViewController.setWebContentsDebuggingEnabled(true);\n } catch (e) {\n print('Error initializing WebView settings: $e');\n }\n }\n\n @override\n Widget build(BuildContext context) {\n return InAppWebView(\n initialUrlRequest: URLRequest(url: WebUri(initialUrl)),\n onWebViewCreated: (controller) async {\n PreloadService.controllers[index] = controller;\n },\n onLoadError: (controller, url, code, message) {\n print('WebView Error: $message');\n },\n onLoadStop: (controller, url) {\n print('WebView Loaded: $url');\n },\n );\n }\n}",
'webview_bloc': "import 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport '../services/preload_sevice.dart';\n\n// Event 정의\nabstract class WebViewEvent {}\n\nclass InitializeWebView extends WebViewEvent {}\n\nclass SwitchTab extends WebViewEvent {\n final int index;\n\n SwitchTab(this.index);\n}\n\nclass BackPressed extends WebViewEvent {}\n\n// State 정의\nclass WebViewState {\n final int selectedIndex;\n final bool isLoading;\n final String? errorMessage;\n\n WebViewState({\n required this.selectedIndex,\n required this.isLoading,\n this.errorMessage,\n });\n\n factory WebViewState.initial() {\n return WebViewState(\n selectedIndex: 0,\n isLoading: true,\n errorMessage: null,\n );\n }\n\n WebViewState copyWith({\n int? selectedIndex,\n bool? isLoading,\n String? errorMessage,\n }) {\n return WebViewState(\n selectedIndex: selectedIndex ?? this.selectedIndex,\n isLoading: isLoading ?? this.isLoading,\n errorMessage: errorMessage,\n );\n }\n}\n\n// Bloc 정의\nclass WebViewBloc extends Bloc<webviewevent, webviewstate=""> {\n WebViewBloc() : super(WebViewState.initial()) {\n on(_onInitializeWebView);\n on(_onSwitchTab);\n on(_onBackPressed);\n }\n\n Future _onInitializeWebView(\n InitializeWebView event, Emitter emit) async {\n emit(state.copyWith(isLoading: true));\n\n try {\n await PreloadService.initialize();\n emit(state.copyWith(isLoading: false));\n } catch (e) {\n print('Error initializing WebView: $e');\n emit(state.copyWith(isLoading: false, errorMessage: e.toString()));\n }\n }\n\n Future _onSwitchTab(SwitchTab event, Emitter emit) async {\n emit(state.copyWith(isLoading: true, selectedIndex: event.index));\n\n try {\n final controller = PreloadService.getController(event.index);\n if (controller != null) {\n final currentUrl = await controller.getUrl();\n if (currentUrl?.toString() != PreloadService.routes[event.index]) {\n await PreloadService.resetToInitialUrl(event.index);\n }\n }\n } catch (e) {\n print('Error switching tab: $e');\n emit(state.copyWith(errorMessage: e.toString()));\n } finally {\n emit(state.copyWith(isLoading: false));\n }\n }\n\n Future _onBackPressed(\n BackPressed event, Emitter emit) async {\n final controller = PreloadService.getController(state.selectedIndex);\n\n if (controller != null && await controller.canGoBack()) {\n await controller.goBack();\n }\n }\n}\n",
'webview_page': "import 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport '../blocs/webview_bloc.dart';\nimport '../services/preload_sevice.dart';\nimport '../widgets/custom_webview.dart';\nimport '../widgets/bottom_bar.dart';\n\nclass WebViewPage extends StatelessWidget {\n const WebViewPage({super.key});\n\n @override\n Widget build(BuildContext context) {\n return BlocProvider(\n create: (context) => WebViewBloc()..add(InitializeWebView()),\n child: BlocBuilder<webviewbloc, webviewstate="">(\n builder: (context, state) {\n return Scaffold(\n body: WillPopScope(\n onWillPop: () async {\n context.read().add(BackPressed());\n return false; // Bloc이 뒤로가기 처리\n },\n child: IndexedStack(\n index: state.selectedIndex,\n children: PreloadService.routes.entries.map((entry) {\n return CustomWebView(\n index: entry.key,\n initialUrl: entry.value,\n );\n }).toList(),\n ),\n ),\n bottomNavigationBar: CustomBottomBar(\n selectedIndex: state.selectedIndex,\n onItemTapped: (index) =>\n context.read().add(SwitchTab(index)),\n onCameraPressed: () {\n print('Camera button pressed');\n },\n ),\n );\n },\n ),\n );\n }\n}",
'main': "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_native_splash/flutter_native_splash.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'blocs/camera_bloc.dart';\nimport 'pages/webview_page.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'repositories/camera_repository.dart';\nimport 'services/preload_sevice.dart';\nimport 'widgets/custom_webview.dart';\n\nvoid main() async {\n WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();\n FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);\n\n try {\n await CustomWebView.initializeWebViewSettings(); // WebView 기본 설정\n await PreloadService.initialize(); // 프리로딩 초기화 (이미 여기서 모든 페이지 프리로딩)\n await Future.delayed(const Duration(seconds: 5)); // 스플래시 화면 유지\n } catch (e) {\n print('Initialization error: $e');\n await Future.delayed(const Duration(seconds: 5));\n }\n\n FlutterNativeSplash.remove();\n runApp(const MyApp());\n}\n\nFuture _configureSystemUI() async {\n SystemChrome.setSystemUIOverlayStyle(\n const SystemUiOverlayStyle(\n statusBarColor: Colors.white,\n statusBarIconBrightness: Brightness.dark,\n systemNavigationBarColor: Colors.white,\n systemNavigationBarIconBrightness: Brightness.dark,\n ),\n );\n\n await SystemChrome.setPreferredOrientations([\n DeviceOrientation.portraitUp,\n ]);\n}\n\nvoid _configureDebugPrint() {\n if (kDebugMode) {\n debugPrint = (String? message, {int? wrapWidth}) {\n if (message != null &&\n !message.contains('FrameEvents') &&\n !message.contains('EGL_emulation') &&\n !message.contains('chromium')) {\n print(message);\n }\n };\n }\n}\n\nclass MyApp extends StatelessWidget {\n const MyApp({super.key});\n\n @override\n Widget build(BuildContext context) {\n return BlocProvider(\n create: (context) => CameraBloc(\n repository: CameraRepository(),\n ),\n child: AnnotatedRegion(\n value: const SystemUiOverlayStyle(\n statusBarColor: Colors.white,\n statusBarIconBrightness: Brightness.dark,\n ),\n child: MaterialApp(\n debugShowCheckedModeBanner: false,\n title: 'Bijjya',\n theme: _buildTheme(),\n builder: (context, child) {\n final mediaQuery = MediaQuery.of(context);\n final padding = mediaQuery.padding;\n return Container(\n color: Colors.white,\n child: Column(\n children: [\n Container(\n height: padding.top + 10.0,\n color: Colors.white,\n ),\n Expanded(\n child: child ?? const SizedBox.shrink(),\n ),\n ],\n ),\n );\n },\n home: const WebViewPage(),\n ),\n ),\n );\n }\n\n ThemeData _buildTheme() {\n return ThemeData(\n scaffoldBackgroundColor: Colors.white,\n colorScheme: ColorScheme.fromSeed(\n seedColor: Colors.orange,\n background: Colors.white,\n ),\n useMaterial3: true,\n appBarTheme: const AppBarTheme(\n systemOverlayStyle: SystemUiOverlayStyle.dark,\n backgroundColor: Colors.white,\n elevation: 0,\n ),\n pageTransitionsTheme: const PageTransitionsTheme(\n builders: {\n TargetPlatform.android: CupertinoPageTransitionsBuilder(),\n },\n ),\n );\n }\n}"}</webviewbloc,></webviewevent,>
분석 결과를 요약하면 다음과 같습니다:
1. 사진 촬영 및 데이터 전송
- 관련 파일: camera_bloc.dart, camera_service.dart, camera_repository.dart
- 내용 요약:
- 사용자가 사진을 촬영하면 CameraBloc에서 이벤트가 발생하며 데이터가 처리됩니다.
- CameraRepository를 통해 데이터가 CameraService로 전달됩니다.
- CameraService는 서버와의 통신을 처리하며, 촬영된 사진을 HTTP 요청으로 서버에 업로드합니다.
2. 서버에서 데이터 처리 및 응답
- 관련 파일: php.dart
- 내용 요약:
- 서버에서는 받은 이미지를 분석하고 API를 호출하여 관련 상품 데이터를 가져옵니다.
- 데이터를 JSON 형식으로 응답하며, 클라이언트 측에서 이를 처리할 수 있도록 준비합니다.
3. 웹뷰를 통한 결과 표시
- 관련 파일: custom_webview.dart, webview_bloc.dart, webview_page.dart
- 내용 요약:
- 서버에서 받은 데이터를 HTML로 렌더링하여 URL을 생성합니다.
- 생성된 URL은 WebViewPage에서 CustomWebView를 통해 로드됩니다.
- WebViewBloc이 웹뷰의 상태 관리 및 탭 전환을 처리합니다.
전체 데이터 흐름
- 사진 촬영 및 업로드:
- 사용자가 사진을 찍으면, CameraBloc에서 이벤트가 발생하여 사진 데이터를 서버로 업로드합니다.
- 서버 처리:
- 서버에서 이미지를 분석하고 관련 상품 데이터를 준비하여 JSON 응답을 반환합니다.
- 웹뷰 렌더링:
- 응답으로 받은 데이터는 HTML로 렌더링되어 URL로 변환됩니다.
- 해당 URL은 앱 내의 웹뷰(CustomWebView)에서 로드됩니다.
추가적인 세부 구현 내용
필요한 경우, 특정 코드 블록을 확인하고 더 상세히 설명할 수 있습니다. 특정 부분이 궁금하시면 알려주세요!
나의 말:
페이지 순서 및 주요 로직
아래는 사진 촬영 후 서버에 데이터를 보내고 결과를 웹뷰로 표시하는 전체 흐름과 각 페이지에서 사용된 주요 로직을 정리한 것입니다.
1. CameraBloc (사진 촬영 및 이벤트 처리)
- 순서: 앱이 사진 촬영 이벤트를 트리거할 때, CameraBloc이 이를 처리합니다.
- 주요 로직:
dart코드 복사class CapturePhoto extends CameraEvent { final File photoFile; CapturePhoto(this.photoFile); } on<CapturePhoto>((event, emit) async { try { emit(CameraState.loading()); final response = await repository.uploadPhoto(event.photoFile); emit(CameraState.success(response)); } catch (e) { emit(CameraState.failure(e.toString())); } });
- 사용자가 사진을 찍으면 CapturePhoto 이벤트가 발생하고, CameraRepository를 통해 사진이 업로드됩니다.
2. CameraService (HTTP 요청 처리)
- 순서: CameraRepository에서 CameraService를 호출하여 HTTP 요청을 보냅니다.
- 주요 로직:
dart코드 복사Future<String> uploadPhoto(File photoFile) async { final request = http.MultipartRequest('POST', Uri.parse(serverUrl)); request.files.add(await http.MultipartFile.fromPath('file', photoFile.path)); final response = await request.send(); if (response.statusCode == 200) { return await response.stream.bytesToString(); } else { throw Exception('Failed to upload photo'); } }
- 사진 데이터를 MultipartRequest로 서버에 전송합니다.
3. php.dart (서버 응답 처리)
- 순서: 서버가 클라이언트에서 받은 사진 데이터를 처리하고 JSON 데이터를 반환합니다.
- 주요 로직:
php코드 복사<?php $uploadedFile = $_FILES['file']['tmp_name']; $result = processImage($uploadedFile); echo json_encode($result); ?>
- 서버에서 파일을 처리한 후 관련 데이터를 API로 가져옵니다.
4. CustomWebView (웹뷰 초기화)
- 순서: 서버에서 응답으로 받은 URL을 CustomWebView에 로드합니다.
- 주요 로직:
dart코드 복사return InAppWebView( initialUrlRequest: URLRequest(url: WebUri(initialUrl)), onWebViewCreated: (controller) { PreloadService.controllers[index] = controller; }, onLoadError: (controller, url, code, message) { print('WebView Error: $message'); }, onLoadStop: (controller, url) { print('WebView Loaded: $url'); }, );
- initialUrl로 받은 데이터를 로드하며, 에러 및 완료 상태를 로깅합니다.
5. WebViewPage (웹뷰 상태 관리 및 UI 처리)
- 순서: WebViewBloc에서 URL 탭 전환이나 뒤로 가기를 관리합니다.
- 주요 로직:
dart코드 복사BlocBuilder<WebViewBloc, WebViewState>( builder: (context, state) { return Scaffold( body: IndexedStack( index: state.selectedIndex, children: PreloadService.routes.entries.map((entry) { return CustomWebView( index: entry.key, initialUrl: entry.value, ); }).toList(), ), bottomNavigationBar: CustomBottomBar( selectedIndex: state.selectedIndex, onItemTapped: (index) => context.read<WebViewBloc>().add(SwitchTab(index)), ), ); }, );
- 웹뷰 상태를 Bloc으로 관리하며, 탭 전환과 같은 이벤트를 처리합니다.
전체 로직 흐름 요약
- CameraBloc: 사진 촬영 및 업로드 이벤트 처리
- CameraService: 서버로 HTTP 요청을 보내 사진 데이터를 업로드
- php.dart: 서버에서 데이터를 처리하고 JSON 응답 반환
- CustomWebView: URL 로딩 및 화면 표시
- WebViewPage: 웹뷰 상태 관리 및 UI 처리
각 단계에서 더 구체적인 코드가 필요하면 말씀해주세요!
나의 말:
맞습니다! CameraRepository 코드도 중요합니다. 이 파일은 CameraBloc과 CameraService 사이에서 데이터를 중계하며, 실제 HTTP 요청이 이뤄지는 서비스 계층과의 연결을 담당합니다. 아래는 CameraRepository의 주요 코드를 포함한 설명입니다.
CameraRepository (데이터 중계 역할)
- 위치: camera_repository.dart
- 주요 역할:
- CameraBloc과 CameraService 간의 중간 계층 역할
- 데이터를 가공하거나, 추가적인 비즈니스 로직을 적용
- 코드:
dart코드 복사import 'camera_service.dart'; class CameraRepository { final CameraService _cameraService; CameraRepository(this._cameraService); Future<String> uploadPhoto(File photo) async { try { final response = await _cameraService.uploadPhoto(photo); return response; // 서버 응답 반환 } catch (e) { throw Exception('Failed to upload photo: $e'); } } }
- CameraService의 uploadPhoto 메서드를 호출하여 서버와의 통신을 수행합니다.
- 예외 발생 시 상위 계층으로 에러를 전달합니다.
CameraBloc과의 연계
- CameraBloc에서 이벤트 처리 중 아래와 같이 CameraRepository를 호출합니다.
dart코드 복사on<CapturePhoto>((event, emit) async { try { emit(CameraState.loading()); final response = await repository.uploadPhoto(event.photoFile); emit(CameraState.success(response)); } catch (e) { emit(CameraState.failure(e.toString())); } });
- 사용자가 사진을 촬영하면 CapturePhoto 이벤트가 발생하고, CameraRepository가 이를 처리합니다.
흐름 요약
- CameraBloc에서 사진 촬영 이벤트 발생 (CapturePhoto)
- CameraRepository가 호출되어 데이터 처리 및 중계
- CameraService에서 HTTP 요청 실행
- 결과가 다시 CameraBloc으로 반환되어 상태 업데이트
이로써 데이터가 흐르는 전체 경로와 각 단계에서 사용된 주요 코드를 모두 확인할 수 있습니다. 추가로 궁금한 점이나 특정 코드에 대한 상세 설명이 필요하면 말씀해주세요!
나의 말:
질문하신 흐름과 관련된 순서를 명확히 정리해 드리겠습니다.
1. 사진 촬영 후 서버에 데이터 전송
이 과정은 CameraBloc -> CameraRepository -> CameraService로 진행됩니다.
- CameraBloc: 사용자가 사진을 찍었을 때 이벤트를 트리거합니다.
- CameraRepository: 데이터 처리 및 CameraService와의 연결을 담당합니다.
- CameraService: 실제 HTTP 요청을 통해 서버에 데이터를 전송합니다.
2. 서버에서 데이터 처리
- 서버(PHP)는 전송된 데이터를 처리한 후, 관련 상품 데이터를 JSON 형식으로 반환합니다.
3. 서버 응답 데이터 표시
이 과정은 CustomWebView -> WebViewPage로 진행됩니다.
- 서버에서 JSON 데이터를 받는 과정에서는 레포지토리나 Bloc을 거치지 않습니다. 대신 서버 응답 데이터를 특정 URL에 HTML로 렌더링하여, 이를 웹뷰로 표시합니다.
왜 받는 과정에서 Repository와 Bloc을 거치지 않을까?
웹뷰를 사용하면 서버 응답을 JSON으로 처리하지 않고, URL을 직접 로드하여 화면에 표시하기 때문입니다. 즉:
- 서버에서 렌더링된 HTML 페이지를 생성합니다.
- 해당 URL을 웹뷰로 전달하여 렌더링된 페이지를 보여줍니다.
정확한 순서
- 사진 촬영: CameraBloc 이벤트 트리거
- 사진 업로드: CameraBloc -> CameraRepository -> CameraService
- 서버 데이터 처리: 서버(PHP)가 요청 처리 및 URL 생성
- URL 표시: WebViewPage에서 CustomWebView를 사용해 URL 로드 및 표시
흐름 요약
- 서버로 보낼 때: CameraBloc -> CameraRepository -> CameraService
- 서버 응답을 받을 때: 서버가 URL을 생성하고, 해당 URL을 CustomWebView에서 로드하여 사용자에게 결과 표시.
이 설계는 웹뷰로 결과를 바로 표시하는 방식이라 복잡한 데이터 처리가 필요 없으며, 앱에서 HTML/CSS 렌더링을 제어하지 않아도 되는 장점이 있습니다. 추가로 더 궁금한 부분이 있다면 말씀해주세요!
'소프트랩스 > 비쨔' 카테고리의 다른 글
검색결과가 홈화면에만 뜨는 문제-이틀간의 문제 (0) | 2025.01.20 |
---|---|
ios 개발환경 세팅 (0) | 2025.01.16 |
PHP에서 알아야 할 것들 (0) | 2025.01.15 |
서버에서 받은거 웹 뷰로 띄우기 (0) | 2025.01.15 |
가로줄을 조심하자(될수도, 안될수도 있음) (0) | 2025.01.13 |