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.dart
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);
}
},
);
}