[flutter] 플러터 RxDart로 도서 및 미디어 검색 앱 개발하기

이번에는 플러터(Flutter)와 RxDart를 사용하여 도서 및 미디어 검색 앱을 개발하는 방법에 대해 알아보겠습니다.

필요한 패키지 설치하기

먼저, RxDart를 사용하기 위해 rxdart 패키지를 프로젝트에 추가해야 합니다. pubspec.yaml 파일을 열고 다음과 같이 패키지를 추가하세요.

dependencies:
  flutter:
    sdk: flutter

  rxdart: ^0.27.0

변경 내용을 반영하기 위해 터미널에서 flutter pub get 명령을 실행하여 패키지를 설치하세요.

API 통신 구현하기

도서와 미디어 검색을 위해 외부 API를 호출해야 합니다. 이 예시에서는 OpenLibrary API를 사용하도록 하겠습니다.

우선, API 통신을 위해 http 패키지를 사용하여 HTTP 요청을 보내고 응답을 받아오는 함수를 작성합니다.

import 'package:http/http.dart' as http;

Future<String> getMediaFromApi(String query) async {
  var url = 'https://openlibrary.org/search.json?q=$query';
  var response = await http.get(Uri.parse(url));

  if (response.statusCode == 200) {
    return response.body;
  } else {
    throw Exception('API 통신 중에 오류가 발생했습니다.');
  }
}

위의 예시에서는 http 패키지를 사용하여 getMediaFromApi 함수를 작성했습니다. 이 함수는 주어진 쿼리를 사용하여 OpenLibrary API에 HTTP GET 요청을 보내고 응답을 받아옵니다. 응답이 성공적인 경우에는 응답 본문을 반환하고, 오류가 발생한 경우에는 예외를 던집니다.

RxDart를 사용한 검색 기능 구현하기

이제 RxDart를 사용하여 API 요청과 응답을 처리하는 검색 기능을 구현해보겠습니다.

먼저, RxDart를 사용하기 위해 rxdart 패키지를 import 합니다.

import 'package:rxdart/rxdart.dart';

다음으로, 검색 기능을 구현하기 위해 BehaviorSubjectdebounceTime을 사용합니다.

class SearchBloc {
  final _querySubject = BehaviorSubject<String>();
  Stream<List<dynamic>> get searchResultsStream => _querySubject
    .distinct()
    .debounceTime(Duration(milliseconds: 300))
    .switchMap((query) => Stream.fromFuture(getMediaFromApi(query))
      .map((res) => json.decode(res)['docs'])
      .handleError((error) {
        print('Error: $error');
        // 에러 처리 로직 추가
      })
    );

  void search(String query) {
    _querySubject.add(query);
  }

  void dispose() {
    _querySubject.close();
  }
}

위의 예시에서는 SearchBloc 클래스를 정의하고, BehaviorSubject를 사용하여 검색어를 기록합니다. searchResultsStreamdebounceTime으로 검색어 입력을 지연시킨 다음, switchMap을 사용하여 API 요청 결과를 받아옵니다. 이후 map을 사용하여 응답 본문을 JSON으로 변환하고, handleError를 사용하여 에러 처리 로직을 추가할 수 있습니다.

UI 구현하기

검색 기능을 구현했으니 이제 UI를 구현해야 합니다. 이번 예시에서는 플러터의 ListView.builder 위젯을 사용하여 검색 결과를 표시하도록 하겠습니다.

class SearchPage extends StatefulWidget {
  @override
  _SearchPageState createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
  final _searchBloc = SearchBloc();

  @override
  void dispose() {
    _searchBloc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('도서 및 미디어 검색'),
      ),
      body: Column(
        children: [
          TextField(
            onChanged: (query) => _searchBloc.search(query),
            decoration: InputDecoration(
              hintText: '검색어를 입력하세요',
            ),
          ),
          Expanded(
            child: StreamBuilder<List<dynamic>>(
              stream: _searchBloc.searchResultsStream,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return ListView.builder(
                    itemCount: snapshot.data.length,
                    itemBuilder: (context, index) {
                      // 검색 결과를 UI에 표시하는 로직 추가
                    },
                  );
                } else if (snapshot.hasError) {
                  return Text('에러가 발생했습니다: ${snapshot.error}');
                } else {
                  return CircularProgressIndicator();
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}

위의 예시에서는 SearchPage 위젯을 정의하고, SearchBloc 인스턴스를 생성하여 검색 기능을 사용합니다. 검색 입력에 따라 _searchBloc.search 메소드를 호출하고, 검색 결과를 StreamBuilder를 사용하여 표시합니다.

마무리

플러터와 RxDart를 사용하여 도서 및 미디어 검색 앱을 개발하는 방법에 대해 알아보았습니다. 이렇게 구현된 앱은 사용자가 검색어를 입력할 때마다 실시간으로 API 요청을 보내고 검색 결과를 업데이트할 수 있습니다.

더 많은 기능을 추가하고 사용자 경험을 개선할 수 있으며, 필요에 따라 코드를 수정하거나 외부 API를 변경할 수도 있습니다.