가을기 Workspace

[플러터] 22일차 - 카카오 api 활용하기 본문

개발/개인앱

[플러터] 22일차 - 카카오 api 활용하기

가을기_ 2021. 8. 11. 01:41

 

데이터 통신을 구현하려면 서버와 데이터가 필요하다. 서버를 직접 구축하고 통신으로 주고받을 데이터도 직접 생성할 수 있겠지만, 공개된 데이터를 활용하면 시간을 좀 더 아낄 수 있다.

 

카카오 API

여기서 이용하려는 데이터는 책 정보로, 카카오가 제공하는 검색 API를 이용해 얻는다. 카카오는 국내에서 네이버와 함께 손꼽히는 기업으로서 다양한 분야의 많은 데이터를 가지고 있다. 이들은 자사의 데이터를 개발자들이 사용할 수 있도록 여러 가지 API를 제공한다.

 

카카오 API를 이용하려면 API 키를 얻어야 한다.

로그인 후 메뉴에서 "내 애플리케이션" 클릭 > "애플리케이션 추가하기" 클릭

이름에 httpTest 입력 후, 각자 이름 입력

 

httpTest 애플리케이션 클릭해서 정보를 확인

 

검색 api에서 활용할 REST API 키를 기억해둘것.

https://developers.kakao.com/docs/latest/ko/daum-search/dev-guide#search-book

 

 

카카오 api 정보

 

 

패키지

플러터에서 http api를 이용할 수 있도록 http패키지를 사용하자.

 

 

  • pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.2

 

 

  • main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {

  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Http Example"),
      ),
      body: Container(
        child: Center(
          child: Text('$result'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          var url = Uri.parse('http://www.google.com');
          var response = await http.get(url);
          setState(() {
            result = response.body;
          });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }
}

이대로 앱을 실행하고 플로팅 버튼을 누르면

에러가 뜬다.

Unhandled Exception: SocketException: Failed host lookup: 'www.google.com' (OS Error: No address associated with hostname, errno = 7)

 

 

cloudflare dns인 1.1.1.1은 들어가지나 google.com이 들어가지지 않으므로 dns에러임을 알 수 있다.

windows라면 android dns settings을 해야한다.

> emulator.exe -list-avds
Pixel_4_API_30

> emulator.exe -avd Pixel_4_API_30 -dns-server 1.1.1.1
emulator: Android emulator version 30.6.5.0 (build_id 7324830) (CL:N/A)
emulator: WARNING: Another emualtor is still running, wait for a sec...
handleCpuAcceleration: feature check for hvf
added library vulkan-1.dll
Failed to open /qemu.conf, err: 2
Windows Hypervisor Platform accelerator is operational
emulator: INFO: GrpcServices.cpp:315: Started GRPC server at 127.0.0.1:8554, security: Local
emulator: INFO: EmulatorAdvertisement.cpp:93: Advertising in: C:\Users\SunMyeong Lee\AppData\Local\Temp\avd\running\pid_4308.ini

이제 앱을 키면 잘 된다.

 

책 정보 가져오기

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {

  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Http Example"),
      ),
      body: Container(
        child: Center(
          child: Text('$result'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          getJSONData();
          // var url = Uri.parse('http://www.google.com');
          // var response = await http.get(url);
          // setState(() {
          //   result = response.body;
          // });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var url = Uri.parse('https://dapi.kakao.com/v3/search/book?target=title&query=doit');
    var response = await http.get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});

    print(response.body);
    return "Successful";
  }
}

 

이제 본격적으로 api를 json으로 파싱해 활용해보자.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {
  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  List data = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Http Example"),
      ),
      body: Container(
        child: Center(
          child: data.length == 0
              ? Text(
                  "데이터가 없습니다",
                  style: TextStyle(fontSize: 20),
                  textAlign: TextAlign.center,
                )
              : ListView.builder(
                  itemBuilder: (context, index) {
                    return Card(
                      child: Container(
                        child: Column(
                          children: [
                            Text(data[index]['title'].toString()),
                            Text(data[index]['authors'].toString()),
                            Text(data[index]['sale_price'].toString()),
                            Text(data[index]['status'].toString()),
                            Image.network(
                              data[index]['thumbnail'],
                              height: 100,
                              width: 100,
                              fit: BoxFit.contain,
                            )
                          ],
                        ),
                      ),
                    );
                  },
                  itemCount: data.length,
                ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          getJSONData();
          // var url = Uri.parse('http://www.google.com');
          // var response = await http.get(url);
          // setState(() {
          //   result = response.body;
          // });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var url = Uri.parse(
        'https://dapi.kakao.com/v3/search/book?target=title&query=doit');
    var response = await http
        .get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});

    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON["documents"];
      data.addAll(result);
    });
    return response.body;
  }
}

 

카드 위젯을 조금 더 꾸며서,

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {
  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  List data = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Http Example"),
      ),
      body: Container(
        child: Center(
          child: data.length == 0
              ? Text(
                  "데이터가 없습니다",
                  style: TextStyle(fontSize: 20),
                  textAlign: TextAlign.center,
                )
              : ListView.builder(
                  itemBuilder: (context, index) {
                    return Card(
                      child: Container(
                        child: Row(
                          children: [
                            Image.network(
                              data[index]['thumbnail'],
                              height: 100,
                              width: 100,
                              fit: BoxFit.contain,
                            ),
                            Column(
                              children: [
                                Container(
                                  width: MediaQuery.of(context).size.width - 150,
                                  child: Text(data[index]['title'].toString()),
                                ),
                                Text(data[index]['authors'].toString()),
                                Text(data[index]['sale_price'].toString()),
                                Text(data[index]['status'].toString()),
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                  itemCount: data.length,
                ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          getJSONData();
          // var url = Uri.parse('http://www.google.com');
          // var response = await http.get(url);
          // setState(() {
          //   result = response.body;
          // });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var url = Uri.parse(
        'https://dapi.kakao.com/v3/search/book?target=title&query=doit');
    var response = await http
        .get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});

    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON["documents"];
      data.addAll(result);
    });
    return response.body;
  }
}

 

 

검색 기능도 넣어보자.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {
  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  List data = [];
  TextEditingController _editingController = new TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: _editingController,
          style: TextStyle(color: Colors.white),
          keyboardType: TextInputType.text,
          decoration: InputDecoration(icon: Icon(Icons.search), hintText: '검색어를 입력하세요'),
        ),
      ),
      body: Container(
        child: Center(
          child: data.length == 0
              ? Text(
                  "데이터가 없습니다",
                  style: TextStyle(fontSize: 20),
                  textAlign: TextAlign.center,
                )
              : ListView.builder(
                  itemBuilder: (context, index) {
                    return Card(
                      child: Container(
                        child: Row(
                          children: [
                            Image.network(
                              data[index]['thumbnail'],
                              height: 100,
                              width: 100,
                              fit: BoxFit.contain,
                            ),
                            Column(
                              children: [
                                Container(
                                  width: MediaQuery.of(context).size.width - 150,
                                  child: Text(data[index]['title'].toString()),
                                ),
                                Text(data[index]['authors'].toString()),
                                Text(data[index]['sale_price'].toString()),
                                Text(data[index]['status'].toString()),
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                  itemCount: data.length,
                ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          getJSONData();
          // var url = Uri.parse('http://www.google.com');
          // var response = await http.get(url);
          // setState(() {
          //   result = response.body;
          // });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var url = Uri.parse(
        'https://dapi.kakao.com/v3/search/book?target=title&query=${_editingController.text}');
    var response = await http
        .get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});

    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON["documents"];
      data.addAll(result);
    });
    return response.body;
  }
}

 

스크롤로 책 정보를 가져와보자.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.deepPurple,
      ),
      home: HttpApp(),
    );
  }
}

class HttpApp extends StatefulWidget {
  @override
  _HttpAppState createState() => _HttpAppState();
}

class _HttpAppState extends State<HttpApp> {
  String result = '';
  List data = [];
  TextEditingController _editingController = new TextEditingController();
  ScrollController _scrollController = new ScrollController();
  int page = 1;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _editingController.addListener(() {
      if (_scrollController.offset >= _scrollController.position.maxScrollExtent && !_scrollController.position.outOfRange) {
        // 리스트의 마지막일때.
        print('bottom');
        page++;
        getJSONData();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: _editingController,
          style: TextStyle(color: Colors.white),
          keyboardType: TextInputType.text,
          decoration: InputDecoration(icon: Icon(Icons.search), hintText: '검색어를 입력하세요'),
        ),
      ),
      body: Container(
        child: Center(
          child: data.length == 0
              ? Text(
                  "데이터가 없습니다",
                  style: TextStyle(fontSize: 20),
                  textAlign: TextAlign.center,
                )
              : ListView.builder(
                  itemBuilder: (context, index) {
                    return Card(
                      child: Container(
                        child: Row(
                          children: [
                            Image.network(
                              data[index]['thumbnail'],
                              height: 100,
                              width: 100,
                              fit: BoxFit.contain,
                            ),
                            Column(
                              children: [
                                Container(
                                  width: MediaQuery.of(context).size.width - 150,
                                  child: Text(data[index]['title'].toString()),
                                ),
                                Text(data[index]['authors'].toString()),
                                Text(data[index]['sale_price'].toString()),
                                Text(data[index]['status'].toString()),
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                  itemCount: data.length,
                  controller: _scrollController,
                ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          page = 1;
          data.clear();
          getJSONData();
          // var url = Uri.parse('http://www.google.com');
          // var response = await http.get(url);
          // setState(() {
          //   result = response.body;
          // });
        },
        child: Icon(Icons.file_download),
      ),
    );
  }

  Future<String> getJSONData() async {
    var url = Uri.parse(
        'https://dapi.kakao.com/v3/search/book?target=title&page=$page&query=${_editingController.text}');
    var response = await http
        .get(url, headers: {"Authorization": "KakaoAK {kakao_rest_api_key}"});

    setState(() {
      var dataConvertedToJSON = json.decode(response.body);
      List result = dataConvertedToJSON["documents"];
      data.addAll(result);
    });
    return response.body;
  }
}

 

Comments