18. Flutter- List<dynamic> or Map<String,dynamic> 파싱 및 데이터 받기

List<dynamic>, Map<String,dynamic>
홍윤's avatar
Oct 10, 2024
18. Flutter- List<dynamic> or Map<String,dynamic> 파싱 및 데이터 받기
 

1. Spring 서버(MVC)와 Flutter 서버(MVVM) 차이점

💡
Spring 서버(MVC)와 Flutter 서버(MVVM)는 서로 다른 아키텍처와 기술 스택을 기반으로 하여 동작합니다. 두 가지 모두 개발자들이 흔히 사용하는 디자인 패턴을 따르지만, 각각의 목적과 적용 방식이 다릅니다. 여기서 차이점을 설명드리겠습니다.

1. 아키텍처 패턴의 차이

Spring (MVC 패턴):

  • *MVC (Model-View-Controller)**는 웹 애플리케이션을 위한 전통적인 디자인 패턴으로, 주로 서버 측 애플리케이션에 적용됩니다.
  • Model: 애플리케이션의 데이터와 비즈니스 로직을 담당합니다.
  • View: 사용자에게 데이터를 표현하는 부분입니다. 웹 애플리케이션에서는 HTML, JSP 같은 뷰 레이어가 해당합니다.
  • Controller: 사용자의 요청을 받아서 처리하고, 모델과 뷰를 연결하는 역할을 합니다. 주로 REST API 요청을 처리하며, 비즈니스 로직을 실행하고 결과를 반환합니다.
Spring의 MVC 구조는 서버에서 주로 동작하며, 사용자의 요청을 처리하고, 데이터베이스나 다른 서비스에서 데이터를 받아 사용자에게 반환하는 구조입니다.

Flutter (MVVM 패턴):

  • *MVVM (Model-View-ViewModel)**은 주로 프론트엔드 애플리케이션에서 사용되는 패턴입니다. 특히, Flutter는 모바일, 웹, 데스크톱 애플리케이션을 위한 프레임워크로, 사용자 인터페이스(UI)와 사용자 경험(UX)에 초점을 맞춥니다.
  • Model: 데이터를 저장하고 비즈니스 로직을 관리합니다.
  • View: 사용자 인터페이스(UI)를 구성하며, 데이터를 화면에 보여주는 역할을 합니다.
  • ViewModel: View와 Model 사이에서 데이터를 변환하고, View와 Model을 분리하는 역할을 합니다. 데이터와 로직이 연결되어 사용자 인터페이스를 쉽게 업데이트할 수 있게 해줍니다.
Flutter에서의 MVVM은 UI 갱신과 상태 관리를 쉽게 하고, 사용자의 입력에 따라 동적으로 UI를 변경하는 데 적합합니다.

2. 적용되는 환경

  • Spring (서버 측 애플리케이션): Spring은 서버에서 동작하는 백엔드 프레임워크로, 주로 데이터 처리, 비즈니스 로직, API 제공 등의 역할을 합니다. 데이터베이스와의 상호작용, 외부 서비스 호출, 요청/응답 처리 등이 Spring 서버의 주된 역할입니다. MVC 패턴을 사용하여 사용자 요청을 받아 데이터베이스와 상호작용하고, 그 결과를 클라이언트에게 반환하는 방식으로 동작합니다.
  • Flutter (클라이언트 측 애플리케이션): Flutter는 클라이언트 측에서 동작하는 프레임워크로, 모바일 또는 웹 애플리케이션에서 UI를 구성하고 사용자와의 상호작용을 처리합니다. 사용자의 입력에 반응하여 UI를 동적으로 업데이트하고, 상태 관리를 통해 애플리케이션 로직을 처리합니다. MVVM 패턴을 사용하여 UI와 로직을 분리하며, 데이터와 사용자 인터페이스 간의 상호작용을 더 효율적으로 관리할 수 있습니다.

3. 역할 및 책임

  • Spring (MVC):
    • 서버 측의 비즈니스 로직을 처리하고, 클라이언트의 요청에 맞는 데이터를 제공하는 역할을 합니다.
    • 데이터를 처리하고, 데이터베이스와 상호작용하며, RESTful API로 데이터를 클라이언트에게 제공합니다.
    • 보안, 인증, 권한 관리와 같은 서버 측 기능을 주로 담당합니다.
  • Flutter (MVVM):
    • 클라이언트 측에서 사용자 인터페이스(UI)를 구성하고, 사용자와의 상호작용을 처리합니다.
    • 서버로부터 데이터를 받아 화면에 표시하고, 사용자의 행동에 따라 UI를 변경합니다.
    • 상태 관리 라이브러리(예: Provider, Riverpod, Bloc 등)를 사용하여 View와 ViewModel 사이의 데이터 흐름을 관리합니다.

4. 데이터 흐름

  • Spring (MVC): 클라이언트(예: Flutter 프런트엔드)가 요청을 보내면, 서버(Spring)가 요청을 받아 비즈니스 로직을 처리하고, 필요한 데이터를 반환합니다. 데이터를 클라이언트로 보내고, 클라이언트는 이 데이터를 화면에 표시합니다.
  • Flutter (MVVM): Flutter는 서버(Spring)로부터 데이터를 받아 ViewModel이 이 데이터를 가공하고, View는 이를 바탕으로 사용자에게 UI를 렌더링합니다. 사용자가 애플리케이션에서 입력을 하면, ViewModel이 이를 처리하여 다시 UI를 업데이트합니다.

5. 기술 스택 차이

  • Spring (MVC): Java 또는 Kotlin 기반으로 동작하며, Spring Boot와 같은 프레임워크로 구성됩니다. 데이터베이스, 서비스 계층과 연결되어 백엔드에서 복잡한 로직을 처리합니다.
  • Flutter (MVVM): Dart 언어를 기반으로 한 프레임워크로, 모바일, 웹, 데스크톱 애플리케이션에서 사용됩니다. 클라이언트 측의 UI와 상태 관리를 중심으로 동작합니다.

요약

구분
Spring (MVC)
Flutter (MVVM)
패턴
MVC
MVVM
역할
서버 측 로직 처리
클라이언트 UI 및 상태 관리
사용 환경
백엔드 (서버)
프론트엔드 (클라이언트)
주요 책임
요청 처리, 데이터 제공
UI 렌더링, 사용자 상호작용
언어/기술
Java, Kotlin
Dart, Flutter
 

2. Flutter-Blog 데이터 받기

1. main.dart

import 'package:blog/core/theme.dart'; import 'package:blog/ui/list/post_list_page.dart'; import 'package:blog/ui/write/post_write_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'core/utils.dart'; void main() { runApp(ProviderScope( child: MyApp(), )); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, theme: theme(), initialRoute: "/", //상세보기는 id값이 다르기 때문에 설정을 하지 못한다. (동적이기 때문에) routes: { "/": (context) => PostListPage(), "/write": (context) => PostWritePage(), }, ); } }
💡

코드설명

1. main() 함수

  • runApp() 함수는 Flutter 애플리케이션을 실행하는 함수입니다. ProviderScope로 감싸진 MyApp 위젯을 넘겨주는데, 이는 Riverpod 상태 관리 패키지에서 상태를 관리하는 범위를 설정하는 역할을 합니다. ProviderScope는 상태 관리를 가능하게 하며, 이 범위 내에서 프로바이더를 사용할 수 있습니다.

2. build() 메소드

  • MaterialApp: 이 위젯은 Flutter의 모든 애플리케이션에 기본적으로 사용되며, 기본적인 앱의 구조를 설정합니다. 여기서는 라우팅, 테마, 네비게이션 키 등을 설정하고 있습니다.
  • navigatorKey: 이 키는 네비게이션을 제어할 수 있는 전역적으로 정의된 키입니다. 이를 사용하여 앱 어디서든 네비게이션을 관리할 수 있습니다. 이 키는 utils.dart에서 정의된 것으로 보입니다.
  • initialRoute: "/": 앱이 시작할 때 기본으로 보여줄 화면(초기 경로)을 지정합니다. 여기서는 / 경로로 설정되어 있으며, PostListPage()가 첫 화면으로 렌더링됩니다.
  • routes: 앱 내에서 사용할 수 있는 경로를 정의하는 맵입니다.
    • "/": 메인 페이지로, PostListPage()를 반환하여 포스트 목록 페이지를 표시합니다.
    • "/write": 글 작성 페이지로, PostWritePage()를 반환하여 사용자가 새로운 포스트를 작성할 수 있는 페이지를 표시합니다.
    • Note: 코드에 언급된 대로, 상세보기 페이지는 동적으로 id 값이 바뀌므로 정적으로 경로를 설정할 수 없습니다. 이를 해결하기 위해 Flutter의 onGenerateRoute 또는 Navigator.push를 사용할 수 있습니다.

요약

이 코드는 Flutter 애플리케이션의 진입점으로, 상태 관리를 위해 Riverpod을 사용하며, 애플리케이션의 라우팅과 테마를 설정하고 있습니다. MyApp은 라우팅을 설정하는 메인 위젯으로, / 경로에서는 포스트 목록 페이지를, /write 경로에서는 글 작성 페이지를 표시합니다.
 

2. post_list_vm.dart

import 'package:blog/data/post_repository.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; //list_vm은 Service라고 생각하면 된다. //------------------------------------------------- //1. 창고 // "?" 붙인 이유는 null 값이 들어 올 수 있음! class PostLiveVM extends StateNotifier<PostListModel?> { PostLiveVM(super.state); //view는 리턴을 해줄필요가없다. //Future쓰는 이유는 awiat 사용할려고 Future<void> notifyInit() async { //1. 통신을 해서 응답 받기 List<dynamic> list = await PostRepository().findAll(); //2. 파싱 : 맵타입으로 되어있으니까 map타입으로 파싱한다. List<_Post> posts = list.map((e) => _Post.fromMap(e)).toList(); //3. 상태를 갱신 시킨다. state = PostListModel(posts); //깊은 복사 (기존 데이터를 건드리지 않는다.) } } //2. 창고 데이터(State) //통신해서 받아야 할 데이터 class PostListModel { List<_Post> posts; PostListModel(this.posts); } //_ 언더바를 쓰면 그 파일에서 찾을 수 있다. 다른 파일에서는 못 찾느다. class _Post { int id; String title; //fromMap = Entity와 같다고 생각하면 된다. //JSON(중간언어, 공용어) //요청 해서 JSON 데이터날아오면 자동으로 Map타입으로 자동으로 컨버팅 해준다. 자기 오브젝트로 만들어주는 게 좋다. _Post.fromMap(map) : this.id = map["id"], this.title = map["title"]; //this.content = map["contnet"]; // 이건 필요없는 이유는 우리는 다 만들어줄거기때문에 } //3. 창고 관리자 (Provider) final postListProvider = StateNotifierProvider<PostLiveVM, PostListModel?>((ref) { return PostLiveVM(null)..notifyInit(); });
💡

코드설명

이 코드는 Flutter에서 Riverpod 상태 관리 패키지를 활용하여 블로그의 게시물 데이터를 관리하는 예제입니다.

1. PostLiveVM 클래스 (StateNotifier)

  • PostLiveVMStateNotifier를 상속받아 상태 관리의 핵심 역할을 수행합니다.
  • StateNotifier는 상태를 관리하는 클래스이며, Riverpod에서 상태를 감시하고 UI에 반영할 수 있도록 도와줍니다.
  • 생성자: PostLiveVMStateNotifierstate를 상속받아 초기 상태를 설정합니다. 초기 상태는 null입니다.
  • notifyInit() 메서드:
    • 서버에서 데이터를 가져오기 위해 PostRepositoryfindAll() 메서드를 호출합니다. 이 과정은 비동기적으로 수행되며, await 키워드를 사용하여 통신 결과를 기다립니다.
    • 데이터를 받아온 후, 각 Map 형태의 데이터를 _Post 객체로 변환합니다. 이를 통해 API에서 가져온 JSON 데이터를 앱 내에서 사용하기 쉬운 Dart 객체로 변환합니다.
    • 마지막으로 statePostListModel로 갱신합니다. state는 Riverpod이 관리하는 상태이며, UI는 이 상태가 변경될 때 자동으로 갱신됩니다.

2. PostListModel 클래스 (상태 데이터)

  • PostListModel은 상태를 나타내는 클래스이며, 게시물들의 리스트를 저장합니다.
  • 이 클래스는 게시물 객체 _Post의 리스트를 보유하고 있으며, ViewModel(PostLiveVM)에서 이 모델을 상태로 관리합니다.

3. _Post 클래스 (데이터 모델)

  • _Post는 개별 게시물 데이터를 나타내는 모델입니다. 이 클래스는 외부 파일에서 접근할 수 없도록 파일 내에서만 사용되도록 설계되었습니다(_가 앞에 붙어있기 때문에).
  • fromMap(): 서버에서 받아온 Map 형태의 데이터를 _Post 객체로 변환합니다. API 호출 후 반환된 JSON 데이터를 Dart 객체로 만들어주는 역할을 합니다.

4. PostListProvider (Provider)

  • postListProvider는 Riverpod에서 상태를 제공하는 Provider입니다. 이 Provider는 StateNotifierProvider를 사용하여 PostLiveVM 클래스의 상태를 관리합니다.
  • StateNotifierProviderPostLiveVM을 생성하고, 이를 통해 상태를 관리합니다.
  • notifyInit() 메서드를 호출하여 초기 상태를 설정하고, 데이터를 가져와서 상태를 업데이트합니다.

종합 설명

  • PostLiveVM: 상태를 관리하는 ViewModel 역할을 하며, 서버에서 데이터를 가져와 상태를 업데이트하는 메서드를 포함합니다.
  • PostListModel: 게시물 리스트 데이터를 관리하는 모델입니다.
  • _Post: 서버로부터 받아온 개별 게시물의 데이터를 모델링한 클래스입니다.
  • postListProvider: Riverpod의 상태 관리자를 통해 상태를 공유하며, 앱 전체에서 사용할 수 있도록 합니다.
 

3. post_list_body.dart

import 'package:blog/ui/detail/post_detail_page.dart'; import 'package:blog/ui/list/post_list_vm.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class PostListBody extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { //1. 페이지 들어오면서 ViewModel이 만들어져야함 watch로 보기 PostListModel? model = ref.watch(postListProvider); if (model == null) { return CircularProgressIndicator(); } else { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ListView.separated( itemBuilder: (context, index) { return ListTile( leading: Text("${model.posts[index].id}"), title: Text("${model.posts[index].title}"), trailing: IconButton( icon: Icon(Icons.arrow_forward_ios), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => PostDetailPage(), )); }, ), ); }, separatorBuilder: (context, index) => Divider(), itemCount: model.posts.length), ); } } }
💡

코드설명

이 코드는 FlutterRiverpod를 사용하여 게시물 리스트를 보여주는 UI를 구현한 코드입니다. 사용자가 리스트를 보고, 각 게시물의 상세 페이지로 이동할 수 있는 기능을 제공합니다.

1. PostListBody 클래스

  • *PostListBody*는 ConsumerWidget을 상속받은 위젯으로, 이 위젯은 Riverpod의 상태 변화를 감시할 수 있습니다.
  • *ConsumerWidget*은 build 메서드에 WidgetRef 객체를 제공하며, 이를 통해 상태 관리 객체인 Provider의 상태를 읽거나 감시할 수 있습니다.

2. build 메서드

  • build 메서드는 Flutter 위젯의 UI를 그리는 역할을 합니다.
  • ref.watch(postListProvider): Riverpod의 watch 메서드를 사용하여 postListProvider의 상태를 감시합니다. 이 postListProvider는 상태 관리자에서 가져온 게시물 리스트 데이터(PostListModel?)를 나타냅니다.
  • model == null: 초기 상태에서 데이터가 아직 로드되지 않았거나, 로딩 중일 때 modelnull이 됩니다. 이 경우 로딩 인디케이터를 보여줍니다.

3. 로딩 상태 처리

if (model == null) { return CircularProgressIndicator(); }
  • 데이터가 아직 로드되지 않았을 때, 로딩 상태를 나타내기 위해 CircularProgressIndicator 위젯을 반환합니다. 이는 일반적으로 서버에서 데이터를 불러오는 동안 사용자에게 로딩 중임을 시각적으로 알리기 위해 사용됩니다.

4. 데이터가 로드된 후 리스트 표시

else { return Padding( .... (생략) itemCount: model.posts.length), ); }
  • 데이터가 성공적으로 로드되면 게시물 리스트를 표시합니다.
  • Padding: 리스트 전체에 좌우 16픽셀의 패딩을 줍니다.
  • ListView.separated: Flutter의 ListView를 사용하여 게시물 리스트를 렌더링합니다. separated는 각 항목 사이에 구분선을 넣을 수 있는 기능을 제공합니다.
    • itemBuilder: 각 리스트 아이템을 빌드합니다. 여기서는 ListTile을 사용하여 리스트 항목을 그립니다.
      • leading: 각 리스트 항목의 좌측에 게시물 ID를 표시합니다.
      • title: 각 게시물의 제목을 표시합니다.
      • trailing: 리스트 항목의 우측에 화살표 아이콘을 표시하며, 이를 클릭하면 상세 페이지로 이동합니다.
      • onPressed: 화살표 아이콘을 클릭하면 Navigator.push를 사용해 PostDetailPage로 이동합니다. 이는 새로운 화면으로 상세 정보를 보여줄 수 있게 합니다.
    • separatorBuilder: 각 항목 사이에 **Divider*를 넣어 리스트 항목들을 시각적으로 구분합니다.
    • itemCount: 리스트의 항목 개수를 나타냅니다. 여기서는 model.posts.length로 게시물의 개수만큼 리스트가 렌더링됩니다.

5. 상세 페이지 이동

Navigator.push( context, MaterialPageRoute( builder: (context) => PostDetailPage(), ));
  • IconButton을 눌렀을 때 Navigator를 통해 PostDetailPage로 이동합니다.
  • *MaterialPageRoute*는 Flutter의 기본 페이지 전환 방식으로, PostDetailPage가 새 화면으로 푸시됩니다.

요약

  • 이 코드는 게시물 리스트를 서버로부터 받아와서 UI에 표시하고, 사용자가 특정 게시물을 클릭하면 상세 페이지로 이동하는 기능을 제공합니다.
  • Riverpod를 사용해 상태를 관리하며, CircularProgressIndicator를 통해 로딩 상태를 처리하고, ListView를 통해 데이터를 표시합니다.
  • Navigator.push를 통해 상세 페이지로의 전환이 이루어집니다.

4. post_detail_vm.dart

//1. 창고(View) import 'package:flutter_riverpod/flutter_riverpod.dart'; class PostDetailVM extends StateNotifier<PostDetailModel?> { PostDetailVM(super.state); } //2. 창고 데이터(State) //통신해서 받아야 할 데이터 class PostDetailModel {} //3. 창고 관리자 (Provider) final postListProvider = StateNotifierProvider<PostDetailVM, PostDetailModel?>((ref) { //? 붙이 이유는 아직 데이터를 넣지않기때문에 나중에 바꿔준다. return PostDetailVM(null); });
💡
  1. ViewModel (PostDetailVM):
      • 게시물의 상세 데이터를 관리하는 클래스입니다. 상태는 PostDetailModel? 타입으로, 초기에는 null 상태로 설정됩니다.
      • 나중에 서버에서 데이터를 받아와 상태를 업데이트할 수 있습니다. 이를 통해 게시물의 상세 정보가 로드되면 UI가 변경됩니다.
  1. 상태 데이터 (PostDetailModel):
      • 실제 상세 데이터를 담는 클래스입니다. 이 클래스는 게시물의 속성(예: 제목, 내용, 작성자 등)을 포함할 수 있습니다.
  1. Provider (postListProvider):
      • PostDetailVM을 생성하고 상태를 관리하는 Provider입니다. UI에서 이 Provider를 감시하여 상태가 변경되면 자동으로 UI가 갱신됩니다.

추가 설명

  • 이 코드 구조에서는 아직 데이터를 불러오는 로직(예: API 호출)이 포함되어 있지 않습니다. **PostDetailVM*에 데이터를 가져오는 메서드를 추가하고, 상태를 **PostDetailModel*로 갱신하여 UI에서 사용할 수 있습니다.
  • 이후 UI 코드에서 **ref.watch(postListProvider)*를 통해 게시물 상세 정보의 상태를 감시하고, 상태가 변경될 때 자동으로 업데이트된 정보를 표시할 수 있습니다.
 
 
 

5. core→ utils.dart

import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; final navigatorKey = GlobalKey<NavigatorState>(); final dio = Dio( BaseOptions( baseUrl: "http://192.168.0.152:8080", // 서버 IP:PORT 입력 contentType: "application/json; charset=utf-8", ), );
💡
이 코드에서는 Dio 패키지를 사용하여 HTTP 통신을 설정하고, Flutter의 Navigator를 관리하기 위한 키를 정의하고 있습니다.

1. navigatorKey

final navigatorKey = GlobalKey<NavigatorState>();
  • *navigatorKey*는 Flutter 애플리케이션 내에서 Navigator를 제어할 수 있는 전역 키를 정의합니다.
  • 이 키를 통해 전역적으로 네비게이션 작업을 수행할 수 있습니다. 즉, 이 키를 사용하면 어디에서든지 Navigator를 통해 화면 전환을 할 수 있습니다.
  • 예를 들어, navigatorKey.currentState?.pushNamed('/someRoute')와 같이 키를 이용해 특정 화면으로 이동할 수 있습니다.

2. dio

final dio = Dio( BaseOptions( baseUrl: "http://192.168.0.152:8080", // 서버 IP:PORT 입력 contentType: "application/json; charset=utf-8", ), );
  • Dio는 Flutter에서 HTTP 요청을 처리할 수 있는 라이브러리입니다. 이를 통해 서버와의 통신을 간단하게 처리할 수 있습니다.
  • BaseOptions:
    • baseUrl: 모든 HTTP 요청에 공통으로 사용될 기본 URL을 지정합니다. 여기서 "http://192.168.0.152:8080"는 서버의 IP 주소와 포트 번호를 나타냅니다. 실제 서버 IP와 포트 번호를 여기에 입력하여 사용할 수 있습니다.
    • contentType: 요청의 콘텐츠 타입을 지정합니다. 여기서는 "application/json; charset=utf-8"로 설정하여 JSON 형식의 데이터를 서버로 보내고, UTF-8로 인코딩된 데이터를 처리합니다.

3. Dio 사용 방법

dio 인스턴스를 사용하여 서버와의 통신을 할 수 있습니다. 예를 들어, GET 또는 POST 요청을 아래와 같이 사용할 수 있습니다.
// GET 요청 Future<void> fetchData() async { try { final response = await dio.get('/posts'); print(response.data); // 서버에서 반환된 데이터를 출력 } catch (e) { print("Error: $e"); } } // POST 요청 Future<void> sendData(Map<String, dynamic> data) async { try { final response = await dio.post('/posts', data: data); print(response.data); // 서버에서 반환된 데이터를 출력 } catch (e) { print("Error: $e"); } }
  • GET 요청: 서버에서 데이터를 받아올 때 사용됩니다. /posts 엔드포인트에서 데이터를 받아오고, 받아온 데이터를 출력합니다.
  • POST 요청: 서버로 데이터를 보낼 때 사용됩니다. 예를 들어, 새 게시물을 생성할 때 sendData 함수에서 데이터를 서버로 보낼 수 있습니다.

요약

  • navigatorKey: 네비게이션을 전역적으로 제어하기 위한 키입니다. 이를 사용하여 어디서든 화면 전환을 수행할 수 있습니다.
  • Dio 설정:
    • baseUrl: 모든 요청에 공통으로 사용될 서버 URL을 설정합니다.
    • contentType: 서버와 주고받는 데이터의 형식을 지정합니다.
  • Dio 사용: GET, POST 등의 요청을 통해 서버와 통신할 수 있으며, try-catch 블록을 사용해 오류를 처리할 수 있습니다.
 

6. post_repository.dart

import 'package:blog/core/utils.dart'; import 'package:dio/dio.dart'; class PostRepository { // 하는일: 통신 후 body 데이터만 응답 // List<dynamic> or Map<String,dynamic> Future<List<dynamic>> findAll() async { //1. 통신 -> response [header, body] Response response = await dio.get("/api/post"); //2. body 부분 리턴 //body 부분이 컬렉션이면 파싱 할 때 List<dynamic>으로 받아드린다. //body 부분이 json이면 Map<String,dynamic>으로 받아드린다. List<dynamic> resposebody = response.data["body"]; //list의 map타입 return resposebody; } Future<Map<String, dynamic>> findById(int id) async { //1. 통신 -> response [header, body] Response response = await dio.get("/api/post/$id"); //2. body 부분 리턴 //body 부분이 컬렉션이면 파싱 할 때 List<dynamic>으로 받아드린다. //body 부분이 json이면 Map<String,dynamic>으로 받아드린다. Map<String, dynamic> resposebody = response.data["body"]; //list의 map타입 return resposebody; } }
💡

코드설명

이 코드는 Dio를 사용하여 서버와 통신하는 PostRepository 클래스를 정의하고 있으며, 서버로부터 게시물 목록과 특정 게시물의 세부 정보를 가져오는 역할을 합니다.

1. PostRepository 클래스

  • 이 클래스는 서버와의 통신을 담당하며, 주로 REST API를 통해 데이터를 요청하고 받아온 응답에서 필요한 부분(body)을 추출하여 반환합니다.
  • *findAll()**과 findById() 두 가지 메서드가 있습니다. 이 메서드들은 서버에서 데이터를 받아와 각각의 형식에 맞게 반환하는 역할을 합니다.

2. findAll() 메서드

Future<List<dynamic>> findAll() async { Response response = await dio.get("/api/post"); List<dynamic> resposebody = response.data["body"]; return resposebody; }
  • 기능: 이 메서드는 서버로부터 모든 게시물을 가져옵니다.
  • dio.get("/api/post"): /api/post 엔드포인트로 GET 요청을 보내서 게시물 목록을 가져옵니다. dio는 이미 설정된 서버의 Base URL을 사용합니다.
  • 응답 처리:
    • response.data["body"]: 서버에서 응답받은 데이터 중, body라는 키에 해당하는 부분만 추출합니다. 이 부분은 일반적으로 서버의 응답이 {"body": [...]} 형식으로 되어 있는 경우에 해당합니다.
    • 리턴 타입: 이 메서드는 List<dynamic> 타입을 반환합니다. 즉, 서버로부터 받은 데이터가 여러 개의 객체(게시물)로 이루어진 리스트라고 가정합니다.

3. findById() 메서드

Future<Map<String, dynamic>> findById(int id) async { Response response = await dio.get("/api/post/$id"); Map<String, dynamic> resposebody = response.data["body"]; return resposebody; }
  • 기능: 이 메서드는 서버로부터 특정 id에 해당하는 게시물의 상세 정보를 가져옵니다.
  • dio.get("/api/post/$id"): /api/post/{id} 경로로 GET 요청을 보내서 특정 게시물의 정보를 요청합니다. 여기서 id는 메서드에 전달된 게시물의 고유 식별자입니다.
  • 응답 처리:
    • response.data["body"]: 서버로부터 받은 응답에서 body라는 키에 해당하는 데이터를 추출합니다. 이 부분은 일반적으로 서버가 단일 객체로 데이터를 반환하는 경우입니다.
    • 리턴 타입: Map<String, dynamic> 형태로 데이터를 반환하여, 게시물의 세부 정보(예: 제목, 내용 등)를 담은 키-값 쌍을 제공합니다.

4. Dio 사용

  • dio.get(): HTTP GET 요청을 보내기 위해 사용되는 Dio 메서드입니다. 서버와 비동기적으로 통신하며, 응답을 기다리기 위해 await 키워드를 사용합니다.
  • 이 클래스에서 Dio를 사용하여 요청을 보내고, 응답 데이터를 response.data에서 추출한 후 필요한 부분만 반환합니다. response.data는 서버에서 JSON 형식으로 응답된 데이터를 Dart의 Map 형태로 자동 변환해줍니다.

5. 설명 요약

  • findAll(): 서버에서 모든 게시물 목록을 가져와 리스트(List<dynamic>)로 반환합니다.
  • findById(): 서버에서 특정 게시물의 세부 정보를 가져와 맵(Map<String, dynamic>)으로 반환합니다.
  • Dio는 HTTP 클라이언트로 사용되며, 서버와의 비동기 통신을 처리하고, 응답에서 필요한 부분을 추출하는 역할을 합니다.
 

3. JSON 데이터 MAP 파싱하기!

1. JSON 데이터 MAP 수업 예제 코드

String json = '{"id":1, "title":"제목1"}'; Map<String,dynamic> map = { "id":1, "title":"제목1" }; class Post { int id; String title; Post(this.id, this.title); } void main() { Post post = Post(map["id"], map["title"]); print(post.id); print(post.title); }
💡

코드설명

이 코드는 Dart에서 JSON 데이터를 Map으로 변환하고, 그 Map 데이터를 Post 객체로 만드는 기본적인 예제입니다. 하지만 이 코드는 Map<String, dynamic>을 직접 생성한 후, Post 객체로 변환하는 방식입니다.
 

2. JSON fromMAP 파싱 수업 예제 코드

String json = '{"id":1, "title":"제목1"}'; Map<String,dynamic> map = { "id":1, "title":"제목1" }; class Post { int id; String title; Post.fromMap(map): this.id = map["id"], this.title = map["title"]; } void main() { Post post = Post.fromMap(map); print(post.id); print(post.title); }
💡

코드설명

map 변수

Map<String,dynamic> map = { "id":1, "title":"제목1" };
  • map 변수는 Map<String, dynamic> 형식의 데이터를 나타냅니다. JSON 데이터를 Map으로 변환한 형태라고 할 수 있습니다.
  • "id":1"title":"제목1"은 각각 idtitle 필드를 나타냅니다.
  • map 변수는 이후에 객체로 변환되기 위해 Post 클래스의 fromMap 생성자에 전달됩니다.

3. Post 클래스

class Post { int id; String title; Post.fromMap(map): this.id = map["id"], this.title = map["title"]; }
  • Post 클래스는 두 개의 필드(id, title)를 가지고 있습니다. 이 클래스는 Dart 객체로서 JSON 데이터를 저장하기 위한 틀입니다.
  • Post.fromMap(map):
    • 이 생성자는 map이라는 데이터를 받아서 idtitle 필드를 초기화합니다.
    • map["id"]map에서 id 값을 가져오고, map["title"]map에서 title 값을 가져옵니다.
    • : (콜론)은 Dart에서 초기화 리스트를 나타냅니다. 이는 생성자 본문이 실행되기 전에 필드를 초기화하는 데 사용됩니다.
    • 따라서, 전달된 map 데이터를 이용해 idtitle 필드를 객체에 할당합니다.
Share article

Uni