카테고리 없음

4.15 TIL

개발일지27 2025. 4. 15. 20:47

Flutter 숙련

 

#OpenAPI (Open Application Programming Interface)

: 특정 서비스나 소프트웨어 기능을 외부 개발자가 사용할 수 있도록 정의한 인터페이스 

우리가 실습해볼 네이버 책 검색 API 의 경우, 우리가 검색어를 네이버에 요청하면 네이버에서 그에 대한 결과를 JSON 형태로 반환해주는 서비스

 

1. 네이버 책 검색 OPEN API 키 발급

2.Model class생성 및 테스트

(fromJson 네임드 생성자 만들기 -> toJson 메서드 만들기)

3. Repository 구현

4. HomeViewModel 구현

5. HomeViewModel 테스트

6. HomeViewModel 데이터 바인딩

 

 

3-1 

flutter pub add http.   -앱 내에서 http요청을 하기 위한 dart 패키지

 

 

repository/book_repository.dart  

import 'dart:convert';

import 'package:flutter_book_search_page/data/model/book.dart';
import 'package:http/http.dart';

class BookRepository {                     // DTO 를 응답받아서 List 으로 가공해서 반환
  Future<List<Book>?> search(String query) async {
    // 네트워크 통신할 땐 반드시 try catch문 감싸기!
    // 모바일에서 인터넷 없을 수도 있고 서버가 응답안하는 경우 등 다양한 예외가 있음!
    try {
      Client client = Client();
      Response result = await client.get(
          Uri.parse(
              'https://openapi.naver.com/v1/search/book.json?query=$query'),
          headers: {
            'X-Naver-Client-Id': 'Fr8WRdHW93iHVIUdQ_Ph',
            'X-Naver-Client-Secret': '9B_N4Opr9x',
          });

      // https://developer.mozilla.org/ko/docs/Web/HTTP/Status/200
      // GET 요청 성공 시 응답코드 200을 반환
      if (result.statusCode == 200) {
       return List.from(json["items"]).map((e) => Book.fromJson(e)).toList()
      }
      return null;
    } catch (e) {
                                                     // 에러나면 확인해보기!
      print(e);
      return null;
    }
  }
}

 

 

test/test_book_repository_test.dar

import 'package:flutter_book_search_page/data/model/book.dart';
import 'package:flutter_book_search_page/data/repository/book_repository.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  test(
    'BookRepository search test',
                                                                                                                    // 통신은 비동기니까 테스트 할 함수에 async 달아주기!
    () async {
      BookRepository bookRepository = BookRepository();
      List<Book>? results = await bookRepository.search('harry');
      expect(results != null, true);
      expect(results!.isNotEmpty, true);
   
      for (var book in results) {.                                                       //for문을 통한 출력해보기


        print(book.title);
      }
    },
  );
}

 

 

4-1

import 'package:flutter_book_search_page/data/model/book.dart';
import 'package:flutter_book_search_page/data/repository/book_repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1. 화면에서 필요한 상태 만들기!
class HomeState {
  HomeState({required this.books});
  List<Book>? books;
}

// 2. 상태를 관리할 뷰모델 만들기! Notifier<뷰모델이 관리할 상태 클래스>
class HomeViewModel extends Notifier<HomeState> {
  // 3. build 함수 재정의 해서 초기상태 리턴해주기!
  @override
  HomeState build() {
    return HomeState(books: null);
  }

  // 4. Repository에서 데이터 받아와서 상태 업데이트 해주기!
  Future<void> search(String query) async {
    BookRepository bookRepository = BookRepository();
    state = HomeState(
      books: await bookRepository.search(query),
    );
  }
}

// 5. HomeViewModel을 관리할 관리자 만들어주기
// NotifierProvider<HomeViewModel, HomeState>
// => HomeState 상태를 관리하는 HomeViewModel 관리해주세요.
// ref.watch를 통해 homeViewModelProvider를 부르면 아직 생성안됐으면 만들어서 주고, 있으면 있던거 주세요!
final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>(() {
  return HomeViewModel();
});

 

 

5-1

import 'package:flutter_book_search_page/ui/pages/home/home_view_model.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

// 앱 내에서는 ProviderScope 에서 ViewModel을 관리하지만
// 테스트 시는 ProviderContainer 가 관리
// HomeViewModel 을 생성자로 생성하면 HomeViewModel 이 관리하는 상태에 접근할 수가 없음!!!
ProviderContainer createContainer() {
  final container = ProviderContainer();
  // 테스트가 끝나면 container를 폐기(dispose)
  addTearDown(container.dispose);
  return container;
}

void main() {
  //
  test(
    'HomeViewModel test',
    () async {
      final container = createContainer();
      HomeState homeState = container.read(homeViewModelProvider);
      // 초기값 확인
      expect(homeState.books, null);
      // 검색
      await container.read(homeViewModelProvider.notifier).search('harry');

      // 검색 후 상태 업데이트 되는지 확인
      HomeState homeStateAfterSearch = container.read(homeViewModelProvider);
      expect(homeStateAfterSearch.books != null, true);
      expect(homeStateAfterSearch.books!.isNotEmpty, true);
      // 출력
      for (var book in homeStateAfterSearch.books!) {
        print(book.title);
      }
    },
  );
}