종류 | 설명 |
---|---|
REST API | HTTP의 GET, POST, PUT, DELETE을 사용하는 가장 대중적인 API |
GraphQL | Graph 구조를 띄우며 클라이언트에서 직접 필요한 데이터를 명시할 수 있는 형태의 통신으로 필요한 데이터만 가져올 수 있다는 장점이 있다. |
gRPC | HTTP/2를 사용하는 통신방식으로 Protocol Buffers라는 방식을 사용하며 레이턴시를 최소화할 목적으로 설계되었다. |
💡TIP: PUT과 PATCH의 차이점 PUT은 리소스의 모든 것을 업데이트할때 사용하고 PATCH는 리소스의 일부를 업데이트 할 때 사용한다.
플러터에서 일반적으로 http플러그인이나 dio플러그인을 사용한다.
import "package:dio/dio.dart";
void main() async {
// get 요청
final getResp = Dio().get("http://test");
// post 요청
final postResp = Dio().post("http://test");
// put 요청
final putResp = Dio().put("http://test");
// delete 요청
final deleteResp = Dio().delete("http://test");
}
구글 클라우드 플랫폼에서 발급한 토큰이 필요하다. 13장에서 발행한 토큰을 활용하면 되지만 추가 설정이 필요하다.
dependencies:
cupertino_icons: ^1.0.6
dio: ^5.4.0
flutter:
sdk: flutter
youtube_player_flutter: ^8.1.2
// lib/model/video_model.dart
class VideoModel {
// 동영상 ID
final String id;
// 동영상 제목
final String title;
VideoModel({required this.id, required this.title});
}
// lib/screen/home_screen.dart
import 'package:cf_tube/component/custom_youtube_player.dart';
import 'package:cf_tube/model/video_model.dart';
import 'package:cf_tube/repository/youtube_repository.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
centerTitle: true,
title: const Text("코팩튜브"),
backgroundColor: Colors.black,
),
body: FutureBuilder<List<VideoModel>>(
future: YoutubeRepository.getVideos(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
return RefreshIndicator(
child: ListView(
// 아래로 당겨서 스크롤할 때 튕기는 애니메이션 추가
physics: const BouncingScrollPhysics(),
children: snapshot.data!
.map((e) => CustomYoutubePlayer(videoModel: e))
.toList(),
),
onRefresh: () async {
setState(() {});
});
},
),
);
}
}
// lib/repository/youtube_repository.dart
import "package:cf_tube/const/api.dart";
import "package:dio/dio.dart";
import "package:cf_tube/model/video_model.dart";
class YoutubeRepository {
static Future<List<VideoModel>> getVideos() async {
final resp = await Dio().get(YOUTUBE_API_BASE_URL, queryParameters: {
"channelId": CF_CHANNEL_ID,
"maxResults": 50,
"key": API_KEY,
"part": "snippet",
"order": "date",
});
// videoId와 title이 null이 아닌 값들만 필터링
final listWithData = resp.data["items"].where(
(item) =>
item?["id"]?["videoId"] != null && item?["snippet"]?["title"] != null,
);
return listWithData
.map<VideoModel>((item) => VideoModel(
id: item["id"]["videoId"], title: item["snippet"]["title"]))
.toList();
}
}
// lib/model/video_model.dart
class VideoModel {
// 동영상 ID
final String id;
// 동영상 제목
final String title;
VideoModel({required this.id, required this.title});
}
// lib/component/custom_youtube_player.dart
import "package:flutter/material.dart";
import "package:cf_tube/model/video_model.dart";
import "package:youtube_player_flutter/youtube_player_flutter.dart";
class CustomYoutubePlayer extends StatefulWidget {
// 상위 위젯에서 입력받을 동영상 정보
final VideoModel videoModel;
const CustomYoutubePlayer({required this.videoModel});
State<CustomYoutubePlayer> createState() => _CustomYoutubePlayerState();
}
class _CustomYoutubePlayerState extends State<CustomYoutubePlayer> {
YoutubePlayerController? controller;
void initState() {
super.initState();
controller = YoutubePlayerController(
initialVideoId: widget.videoModel.id,
flags: const YoutubePlayerFlags(
autoPlay: false,
));
}
void dispose() {
super.dispose();
controller!.dispose();
}
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 유튜브 재생기 렌더링
YoutubePlayer(
controller: controller!,
showVideoProgressIndicator: true,
),
const SizedBox(
height: 16.0,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
widget.videoModel.title,
style: const TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.w700),
),
),
const SizedBox(
height: 16.0,
)
],
);
}
}
// lib/const/api.dart
const API_KEY = "구글 클라우드 플랫폼 토큰";
const YOUTUBE_API_BASE_URL = "https://youtube.googleapis.com/youtube/v3/search";
const CF_CHANNEL_ID = "UCAJ-meoCh1TrPZ7La3UpPrw";