Flutter GraphQL 프로젝트 설정 및 데이터 활용 가이드
소스 코드가 포함된 Flutter GraphQL 프로젝트 다운로드 및 Back4App 사용 시작하기
소개
이 튜토리얼에서는 GraphQL을 통해 Back4App 백엔드에서 데이터를 파싱하는 애플리케이션을 구축할 것입니다. 아시다시피, GraphQL은 API를 위한 오픈 소스 데이터 쿼리 및 조작 언어이며, 기존 데이터를 사용하여 쿼리를 수행하는 런타임입니다. Back4App은 개발자가 확장 가능하고 확장 가능한 모바일 및 웹 애플리케이션을 빠른 속도로 구축할 수 있도록 돕는 저코드 백엔드 서비스(오픈 소스 Parse 플랫폼 기반)입니다.
목표
주요 목표는 프로그래밍 언어 목록과 해당 저장 형식을 보여주는 간단한 앱을 만드는 것입니다.

이 기사 끝에서 여러분은 다음을 할 수 있을 것으로 기대합니다:
- GraphQL을 사용하여 Back4App에서 API 호출하기;
- GraphQL API에서 데이터 가져오기;
- Flutter 앱에서 데이터 가져오기.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
- Android Studio 또는 Flutter와 같은 Flutter 코드를 작성할 수 있는 IDE. 그러나 이 튜토리얼에서는 Android Studio를 사용할 것입니다.
1 - Github에서 Android Studio로 프로젝트 복제하기
다음으로 가세요 Github 저장소, ZIP 파일을 다운로드하고, 압축을 풀고, Flutter IDE에서 엽니다.

이 단계를 완료하려면, zip 파일 안의 폴더를 바탕 화면으로 드래그하고 Android Studio를 열고 '기존 Android Studio 프로젝트 열기'를 클릭하세요. 프로젝트 디렉토리는 보통 'C:\Users\Username\Desktop\back4App_GraphQL-starting_code’입니다.
하지만 이는 사용자마다 다를 수 있습니다.

프로젝트를 열고 lib\main.dart 파일로 가세요. 거기에는 많은 오류가 보일 것입니다. 걱정하지 마세요, 'Get dependencies'를 모두 눌러주세요. 그러면 모든 오류가 사라질 것입니다. 만약 그렇지 않다면 'Upgrade dependencies'를 눌러주세요.

당신의 main.dart 코드가 이렇게 보여야 합니다.
1 import 'package:flutter/material.dart';
2 import 'package:graphql_flutter/graphql_flutter.dart';
3 import 'consonents.dart';
4
5 void main() {
6 runApp(MyApp());
7 }
8
9 class MyApp extends StatelessWidget {
10 @override
11 Widget build(BuildContext context) {
12 return MaterialApp(
13 home: MyHomePage(),
14 ),
15 );
16 }
17 }
18 class MyHomePage extends StatefulWidget {
19 @override
20 _MyHomePageState createState() => _MyHomePageState();
21 }
22
23 class _MyHomePageState extends State<MyHomePage> {
24
25 @override
26 Widget build(BuildContext context) {
27 return SafeArea(
28 child: Scaffold(
29 appBar: AppBar(
30 title: Text('Parsing data using GraphQL',
31 ),
32 ),
33 body: Container(),
34 ),);
35 }
36 }앱을 실행하면 에뮬레이터/장치에서 빈 화면만 보이게 되며, 제목이 ‘데이터 파싱하기 GraphQL’인 AppBar만 표시됩니다.

우리는 main.dart 파일에서 작업할 것이며, 우리의 중요한 값은 consonents.dart에 저장되어 있으며, 우리는 거기에서 직접 사용할 수 있습니다. 우리는 앱에서 graphQL을 사용하기 위해 graphql_flutter 의존성이 필요합니다.
graphql_flutter는 flutter를 위한 가장 인기 있는 GraphQL 클라이언트입니다. 이는 우리 코드에서 직접 GraphQL 쿼리를 사용할 수 있도록 도와줍니다. 이는 우리에게 GraphQLClient, GraphQLProvider 및 데이터베이스에서 데이터를 직접 파싱하는 데 도움이 되는 많은 유용한 위젯을 제공합니다. StreamBuilder조차 사용하지 않고도 말이죠. 이 패키지는 다음과 같은 많은 기능을 제공합니다:
- 구독
- 메모리 캐시
- 오프라인 캐시 동기화
- 낙관적인 결과
- GraphQl 파일 업로드
다음 코드를 pubspec.yaml 파일에 작성하여 가져올 수 있습니다:
자세한 내용은 graphql_flutter에서 확인하세요 GraphQL Flutter 문서
모든 종속성이 이미 사전 설치되어 있으며 이제 다음 단계로 진행할 수 있습니다.
2 - Back4App에서 백엔드 생성하기
Back4App 웹사이트에 가입한 후, 다음 단계로 진행하여 새 앱을 생성할 수 있습니다. Back4App를 클릭하세요. ‘새 앱 만들기’를 클릭합니다. 여기서 프로젝트 이름과 동일한 이름을 부여하세요. 즉, ‘back4app_Graphql’입니다.
이제 왼쪽에서 서버 설정으로 스크롤하여 설정 관리 파서 서버 카드를 선택한 다음, 목록에서 ‘3.10.0 - Parse Server 3.10.0’ 옵션을 선택합니다. 이제 아래의 저장 버튼을 눌러 저장될 때까지 기다리세요.

이제 코어로 돌아가서 (왼쪽), API 콘솔을 선택하고 그 안에서 GraphQL 콘솔을 선택하세요.

이곳은 GraphQL 쿼리/변경 코드 작성 및 테스트를 할 수 있는 창입니다. 다음 단계로 진행합시다.
3 - GraphQL을 통한 데이터 생성 및 가져오기
이제 GraphQL API 콘솔을 사용하여 Back4App에서 GraphQL API를 테스트해 보겠습니다. 먼저, 왼쪽 코드 상자에 다음 쿼리를 붙여넣으세요:
1 mutation CreateClass {
2 createClass(input:{
3 name: "Language"
4 schemaFields: {
5 addStrings: [{name: "name"}{name: "saveFormat"}]
6 }
7 }){
8 class{
9 schemaFields{
10 name
11 __typename
12 }
13 }
14 }
15 }
16위 코드는 “Language”라는 이름의 클래스를 생성합니다. 이 새로운 클래스를 몇 개의 행으로 채워봅시다:
1 mutation CreateObject{
2 createLanguage(input:{fields: {name: "Python", saveFormat:".py"}}){
3 language{
4 name,
5 saveFormat
6 }
7 }
8 }작업이 성공하면 GraphQL PlayGround의 오른쪽 코드 상자에서 이 메시지를 볼 수 있습니다:
1 {
2 "data": {
3 "createLanguage": {
4 "language": {
5 "name": "Python",
6 "saveFormat": ".py"
7 }
8 }
9 }
10 }변경 사항을 만들거나 클래스를 생성하는 데 변형이 사용됩니다. 위의 변형을 실행하면 데이터 필드가 있는 Language라는 새 클래스를 생성합니다:
- name: “Python”
- saveFormat: “.py”
같은 클래스에서 두 개의 객체를 더 생성하는 과정을 반복하십시오:
- name: “C” 및 saveFormat: “.c”
- name: “java” 및 saveFormat: “.java”.
이것에 대한 변형은 다음과 같습니다:
1 mutation CreateObject{
2 createLanguage(input:{fields: {name: "C", saveFormat:".c"}}){
3 language{
4 name,
5 saveFormat
6 }
7 }
8 }자바:
1 mutation CreateObject{
2 createLanguage(input:{fields: {name: "Java", saveFormat:".java"}}){
3 language{
4 name,
5 saveFormat
6 }
7 }
8 }이제 우리 클래스 Languages의 모든 데이터를 살펴보겠습니다. 데이터를 읽기 위해 우리는 query를 사용합니다. 그러니 아래 명령어를 입력하세요.
1 query FindLanguages{
2 languages{
3 count,
4 edges{
5 node{
6 name,
7 saveFormat
8 }
9 }
10 }
11 }‘query FindLanguage’는 당신의 쿼리 명령어에 대한 이름일 뿐이며, 다른 어떤 이름으로도 지을 수 있습니다. 우리는 find(className: "") 명령어를 사용하여 특정 클래스의 모든 요소를 찾습니다. count,는 클래스의 요소 수를 반환하며 모든 요소는 result 객체 안에 표시됩니다.
위 쿼리는 다음을 반환할 것입니다:
1 {
2 "data": {
3 "languages": {
4 "count": 3,
5 "edges": [
6 {
7 "node": {
8 "name": "Python",
9 "saveFormat": ".py"
10 }
11 },
12 {
13 "node": {
14 "name": "C",
15 "saveFormat": ".c"
16 }
17 },
18 {
19 "node": {
20 "name": "Java",
21 "saveFormat": ".java"
22 }
23 }
24 ]
25 }
26 }
27 }다른 모든 쿼리는 다음 링크에서 확인할 수 있습니다: GraphQL 쿼리 - Back4App
이제 다음 단계로 진행합시다.
4 - 우리 앱에서 GraphQL 설정하기
우리 앱을 코딩하기 시작합시다. 이 전에, 당신은 다음을 해야 합니다.lib\consonents.dart 파일에서:
- GraphQL 창 상단의 히스토리 버튼 옆에 있는 GraphQL 링크를 복사하고, kUrl 문자열 데이터 유형으로 붙여넣습니다.
- 이제 페이지 하단으로 이동하여 HTTP 헤더에서 코드를 복사합니다. 콜론(:) 오른쪽의 코드만 복사하고, 해당 이름과 함께 lib\consonents.dart 파일에 붙여넣습니다:

파일에는 다음 코드가 포함되어야 합니다:
1 String kParseApplicationId= "APPLICATION_ID_COPIED_FROM_HEADERS";
2 String kParseClientKey = "CLIENT_KEY_COPIED_FROM_HEADER";
3 String kUrl= "URL_COPIED";
4 // replace "APPLICATION_ID_COPIED_FROM_HEADERS", "CLIENT_KEY_COPIED_FROM_HEADER", "URL_COPIED" with real keys/ids copied
5 //from Http Headers tab.이제 main.dart 파일로 이동하여 MyApp Stateless Widget으로 가서 return 바로 위에 다음 코드를 추가하세요.MaterialApp():
1 final HttpLink httpLink = HttpLink(
2 uri: kUrl,
3 headers: {
4 'X-Parse-Application-Id' : kParseApplicationId,
5 'X-Parse-Client-Key' : kParseClientKey,
6 },
7 );HttpLink는 flutter_graphql.dart에서 가져온 것으로, 두 개의 매개변수를 가진 위젯 HttpLink()를 사용합니다. 첫 번째는 Back4App의 GraphQL API URL입니다. 두 번째는 Back4App API에서 인증하는 데 필요한 헤더입니다. 이 섹션 이후에는 GraphQL 클라이언트 코드를 포함해야 합니다(무엇인지 설명), 아래 코드를 복사한 후 HttpLink 섹션 아래에 붙여넣으세요:
1 ValueNotifier<GraphQLClient> client = ValueNotifier(
2 GraphQLClient(
3 cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
4 link: httpLink,
5 ),
6 );이제 우리는 GraphQLClient에 링크와 캐시 방법을 제공했습니다. 우리는 이를 ValueNotifier를 통해 수행하였고, 이를 client라고 명명했습니다. 이제 MyHomePage() 위젯을 MaterialApp의 자식으로 GraphQLProvider로 감싸고 MyHomePage()를 그 클라이언트로 전달합시다. GraphQLProvider 내부에 client라는 또 다른 매개변수를 추가하고 그 안에 client(우리의 ValueNotifier의 이름)를 전달합니다. 이제 MyApp 클래스는 다음과 같아야 합니다:
1 class MyApp extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 final HttpLink httpLink = HttpLink(
5 uri: kUrl,
6 headers: {
7 'X-Parse-Application-Id' : kParseApplicationId,
8 'X-Parse-Client-Key' : kParseClientKey,
9 //'X-Parse-REST-API-Key' : kParseRestApiKey,
10 },//getheaders()
11 );
12 ValueNotifier<GraphQLClient> client = ValueNotifier(
13 GraphQLClient(
14 cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
15 link: httpLink,
16 ),
17 );
18 return MaterialApp(
19 home: GraphQLProvider(
20 child: MyHomePage(),
21 client: client,
22 ),
23 );
24 }
25 }API를 호출하고 데이터를 가져옵시다.
5 - API 호출하기
이제 MyHomePageState에서 작업할 것입니다. 우리는 ‘query’라는 이름의 String 데이터 유형을 초기화하고, 이를 통해 Language 클래스에서 모든 데이터를 찾기 위한 쿼리 문을 할당/전달할 것입니다. 쿼리가 여러 줄이므로, 우리는 쿼리를 삼중 따옴표로 전달할 것입니다. 다음은 그 모습입니다:
1 String query='''
2 query FindLanguages{
3 languages{
4 count,
5 edges{
6 node{
7 name,
8 saveFormat
9 }
10 }
11 }
12 }
13 ''';이제 Query() 위젯으로 가서 Scaffold의 body 매개변수 안에 두 가지 속성이 있습니다:
- 옵션
- 빌더
그리고 두 개 모두에 대해 null을 전달합니다. 이것이 귀하의 빌드 메서드가 어떻게 보일지입니다:
1 Widget build(BuildContext context) {
2 return SafeArea(
3 child: Scaffold(
4 appBar: AppBar(
5 title: Text('Parsing data using GraphQL',
6 ),
7 ),
8 body: Query(
9 options: null,
10 builder: null
11 ),
12 ),);
13 }“Query() 위젯은 우리가 GraphQL 쿼리를 작성하는 데 도움을 주며, 데이터를 읽고 가져오는 데 도움을 줄 것입니다. 우리는 옵션에서 문자열로 가져온 쿼리 문을 전달하고 빌더 매개변수로 위젯을 빌드할 것입니다.
따라서 다음과 같이 옵션에 QueryOptions() 위젯을 전달합니다:
1 options: QueryOptions(
2 documentNode: gql(query),
3 ),
4쿼리는 QueryOptions의 documentNode 매개변수를 통해 전달됩니다. 이제 builder 매개변수의 도움으로 빌드를 해봅시다. builder 메서드는 세 개의 매개변수를 가진 함수를 받습니다.
- QueryResult result;
- Refetch refetch;
- FetchMore fetchmore.
현재 우리는 쿼리의 결과를 제공하는 QueryResult에 대해서만 걱정하면 됩니다. 우리는 result.data에 접근할 수 있습니다. 그러니 아래 코드를 작성해봅시다:
1 builder : (QueryResult result, { Refetch refetch,FetchMore fetchMore,})
2 {
3 if(result.data==null){
4 return Center(child: Text("Loading...",style: TextStyle(fontSize: 20.0),));
5 }else{
6 return Text('SUCCESS');
7 }
8 },위 코드에서는 데이터를 접근하고 있습니다. 데이터가 없으면 ‘Loading…’라는 텍스트 위젯을 반환하고, 그렇지 않으면 ‘SUCCESS’라는 Text() 위젯을 반환합니다.
여기 당신의 myHomePage 클래스는 main.dart 다음과 같아야 합니다:
1 class MyHomePage extends StatefulWidget {
2 @override
3 _MyHomePageState createState() => _MyHomePageState();
4 }
5
6 class _MyHomePageState extends State<MyHomePage> {
7 String name;
8 String saveFormat;
9 String objectId;
10
11 String query = '''
12 query FindLanguages{
13 languages{
14 count,
15 edges{
16 node{
17 name,
18 saveFormat
19 }
20 }
21 }
22 }
23 ''';
24
25 @override
26 Widget build(BuildContext context) {
27 return SafeArea(
28 child: Scaffold(
29 appBar: AppBar(
30 title: Text(
31 'Parsing data using GraphQL',
32 ),
33 ),
34 body: Query(
35 options: QueryOptions(
36 documentNode: gql(query),
37 ),
38 builder: (
39 QueryResult result, {
40 Refetch refetch,
41 FetchMore fetchMore,
42 }) {
43 if (result.data == null) {
44 return Center(
45 child: Text(
46 "Loading...",
47 style: TextStyle(fontSize: 20.0),
48 ),
49 );
50 } else{
51 return Text('SUCCESS');
52 }
53 }
54 ),
55 ),
56 );
57 }
58 }이제 앱을 시작하고 재시작 후 몇 초 동안 기다리세요. 화면에 ‘SUCCESS’가 보이면 축하합니다! 연결을 설정하고 API를 호출했습니다.
6 - API에서 데이터 가져오기 및 표시하기
Text 위젯을 반환하는 대신, 원본 데이터로 ListView.builder() 위젯을 반환할 것입니다. return 대신 다음 코드를 작성하세요 Text('SUCCESS').
1 return ListView.builder(
2 itemBuilder: (BuildContext context, int index){
3 return ListTile(
4 title: Text(result.data["languages"]["edges"][index]["node"]['name']),
5 trailing: Text(result.data["languages"]["edges"][index]["node"]['saveFormat']),
6 );
7 },
8 itemCount: result.data["languages"]["edges"].length,
9 );이제 Back4App의 API 콘솔에서 find 메서드를 입력했던 GraphQL 결과 화면을 다시 보면, 결과는 다음과 같았습니다:
1 {
2 "data": {
3 "languages": {
4 "count": 3,
5 "edges": [
6 {
7 "node": {
8 "name": "Python",
9 "saveFormat": ".py"
10 }
11 },
12 {
13 "node": {
14 "name": "C",
15 "saveFormat": ".c"
16 }
17 },
18 {
19 "node": {
20 "name": "Java",
21 "saveFormat": ".java"
22 }
23 }
24 ]
25 }
26 }
27 }따라서 위 코드에서 “Python”의 위치는:
- “data”-> “languages” -> “count” -> “edges” -> “node” -> “name”. 또한 name은 edges의 대괄호 ‘[]’ 안에 있어 edges 목록/배열의 첫 번째 요소임을 나타냅니다.
따라서 우리는 “Python”을 얻기 위해 이 위치에 들어가야 하며, 다른 모든 것에 대해서도 마찬가지입니다. 우리가 result.data, 를 작성할 때, 우리는 “data” 위치에 들어갑니다. 따라서 다른 위치를 제공하기 위해 ["location_name"]을 추가합니다. 따라서 “Python”의 위치는 result.data["languages"]["edges"][0]["node"]["name"]이 될 것입니다.
“},{ListView”를 사용할 때, 두 개의 매개변수를 사용합니다. itemCount, 이는 API 호출의 요소 수를 나타내고, itemBuilder, 이는 (BuildContext context, int index)라는 매개변수를 가진 함수를 사용하여 데이터 표시를 위한 목록 위젯을 반환합니다. 여기서는 ListTile 목록을 사용하여 데이터를 표시할 것입니다:
1 return ListView.builder(
2 itemCount: result.data["languages"]["edges"].length,
3 itemBuilder: (BuildContext context, int index){
4 return ListTile(
5 title: Text(result.data["languages"]["edges"][index]["node"]['name']),
6 trailing: Text(result.data["languages"]["edges"][index]["node"]['saveFormat']),
7 );
8 },
9
10 );우리가 Text('SUCCESS')를 위의 ListView.builder() 위젯으로 교체할 때, 우리는 먼저 itemCount를 전달하는데, 여기서 결과 리스트의 요소 수를 전달하므로 더 이상 걱정할 필요가 없습니다. itemBuilder에서는 "name"의 "Languages" 클래스의 ListTiles 목록을 반환하고, "saveFormat"에서 후행으로 사용합니다. 결과 뒤에 어떤 숫자 대신 인덱스를 사용했다는 점에 유의하세요. 이것이 itemCount가 처리한 것입니다.
이제 당신의 main.dart는 이렇게 보여야 합니다:
1 import 'package:flutter/material.dart';
2 import 'package:graphql_flutter/graphql_flutter.dart';
3 import 'consonents.dart';
4 import 'dart:ui';
5
6 void main() {
7 runApp(MyApp());
8 }
9
10 class MyApp extends StatelessWidget {
11 @override
12 Widget build(BuildContext context) {
13 final HttpLink httpLink = HttpLink(
14 uri: 'https://parseapi.back4app.com/graphql',
15 headers: {
16 'X-Parse-Application-Id': kParseApplicationId,
17 'X-Parse-Client-Key': kParseClientKey,
18 }, //getheaders()
19 );
20
21 ValueNotifier<GraphQLClient> client = ValueNotifier(
22 GraphQLClient(
23 cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
24 link: httpLink,
25 ),
26 );
27
28 return MaterialApp(
29 home: GraphQLProvider(
30 child: MyHomePage(),
31 client: client,
32 ),
33 );
34 }
35 }
36
37 class MyHomePage extends StatefulWidget {
38 @override
39 _MyHomePageState createState() => _MyHomePageState();
40 }
41
42 class _MyHomePageState extends State<MyHomePage> {
43 String name;
44 String saveFormat;
45 String objectId;
46
47 String query = '''
48 query FindLanguages{
49 languages{
50 count,
51 edges{
52 node{
53 name,
54 saveFormat
55 }
56 }
57 }
58 }
59 ''';
60
61 @override
62 Widget build(BuildContext context) {
63 return SafeArea(
64 child: Scaffold(
65 appBar: AppBar(
66 title: Text(
67 'Parsing data using GraphQL',
68 ),
69 ),
70 body: Query(
71 options: QueryOptions(
72 documentNode: gql(query),
73 ),
74 builder: (
75 QueryResult result, {
76 Refetch refetch,
77 FetchMore fetchMore,
78 }) {
79 if (result.data == null) {
80 return Center(
81 child: Text(
82 "Loading...",
83 style: TextStyle(fontSize: 20.0),
84 ));
85 } else {
86 return ListView.builder(
87 itemBuilder: (BuildContext context, int index) {
88 return ListTile(
89 title: Text(result.data["languages"]["edges"][index]["node"]
90 ['name']),
91 trailing: Text(result.data["languages"]["edges"][index]
92 ["node"]['saveFormat']),
93 );
94 },
95 itemCount: result.data["languages"]["edges"].length,
96 );
97 }
98 },
99 ),
100 ),
101 );
102 }
103 }그리고 우리의 마지막 앱 화면:

결론
이제 여러분은 Back4App에서 데이터를 저장하고 검색할 수 있는 GraphQL API에 연결된 Flutter 앱을 갖게 되었습니다.
우리는 json 데이터를 별도로 인코딩하거나 디코딩할 필요가 없었기 때문에 몇 줄의 코드로 작업이 더 쉽고 빨라졌습니다.
좋은 하루 되세요!