본문 바로가기

운동하는 개발자/Flutter

13년차 응용프로그래머, Gemini에게 Flutter 배우기 - 4 RestAPI

728x90

REST API연동

 

1단계 패키지 설치

flutter pub add http

 

2단계 데이터 모델 (DTO) 클래스 만들기

// order_model.dart (새 파일로 분리하는 것을 추천합니다)
class Order {
  final int id;
  final String title;
  final int amount;

  Order({required this.id, required this.title, required this.amount});

  // JSON Map을 받아서 Order 객체로 뱉어내는 팩토리(Factory) 생성자
  factory Order.fromJson(Map<String, dynamic> json) {
    return Order(
      id: json['id'] ?? 0, // null 방어 코드
      title: json['title'] ?? '알 수 없음',
      amount: json['amount'] ?? 0,
    );
  }
}

 

3단계 Provider에 네트워크 로직 결합 (핵심)

import 'dart:convert'; // jsonDecode를 위해 필요
import 'package:http/http.dart' as http; // http 모듈 임포트
import 'package:flutter/material.dart';
// 앞서 만든 Order 모델이 있다고 가정합니다.

class OrderProvider extends ChangeNotifier {
  List<Order> _orders = [];
  bool _isLoading = false; // 1. 네트워크 통신 중인지 확인하는 플래그

  List<Order> get orders => _orders;
  bool get isLoading => _isLoading;

  // 비동기 함수(Future) 선언
  Future<void> fetchOrders() async {
    _isLoading = true;
    notifyListeners(); // 2. "로딩 시작했어! 화면에 스피너(빙글빙글) 띄워!" 방송

    try {
      // 3. 실제 REST API 호출 (여기는 가상의 무료 테스트 API 주소입니다)
      final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

      if (response.statusCode == 200) {
        // 4. JSON 텍스트를 파싱하여 List 객체로 변환
        final List<dynamic> jsonData = jsonDecode(response.body);
        
        // 데이터 매핑 (API 구조에 맞게 임의 가공)
        _orders = jsonData.take(5).map((item) => Order(
          id: item['id'],
          title: item['title'].toString().substring(0, 10), // 제목 일부만
          amount: 15000 + (item['id'] as int) * 1000,
        )).toList();
      }
    } catch (e) {
      print('네트워크 에러: $e');
    } finally {
      _isLoading = false;
      notifyListeners(); // 5. "데이터 다 가져왔다! 로딩 스피너 끄고 리스트 그려!" 방송
    }
  }
}

 

4단계 UI에서 연결하기

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final provider = context.watch<OrderProvider>();

    return Scaffold(
      appBar: AppBar(title: const Text('REST API 연동')),
      body: Center(
        // switch문을 UI 내부에서 식(Expression)으로 바로 사용할 수 있습니다.
       child: switch (provider) {
          _ when provider.isLoading => const CircularProgressIndicator(),
          _ when provider.orders.isEmpty => const Text('데이터가 없습니다.'),
          _ => ListView.builder(
            itemCount: provider.orders.length,
            itemBuilder: (context, index) => ListTile(
              title: Text(provider.orders[index].title),
              trailing: Text('${provider.orders[index].amount}원'),
            ),
          ),
        },
      ),
      floatingActionButton: FloatingActionButton(
        // 2. 버튼 클릭 시 read()를 이용해 API 호출 함수 실행
        onPressed: () => context.read<OrderProvider>().fetchOrders(),
        child: const Icon(Icons.download),
      ),
    );
  }
}

 

비동기식으로 데이터 획득 및 provider로 화면 갱신 확인

728x90