안드로이드 스튜디오의 셋업이 끝났으니 이제 플러터 프로젝트를 만들고 자동 생성되는 기본 구조를 살펴보겠습니다. 프로젝트가 생성되면 조금 복잡해 보이는 디렉토리 구조와 프로젝트에 필요한 기본 파일들이 생성됩니다.
안드로이드와 iOS를 모두 지원하는 앱을 만들어야 하기 때문에 android, ios 이름을 갖고 있는 폴더가 먼저 눈에 띕니다. 우리가 직접 코드를 만들어 지지고 볶는 곳은 main.dart 파일이 포함된 lib 디렉토리라고 합니다. 그럼 main.dart 를 한 번 열어보겠습니다. 보물상자를 여는 기분으로다...
복잡해 보이는 main.dart 파일이지만 코드 블럭을 접어서 보면 크게 위의 다섯가지 부분 정도로 나뉩니다. 모듈이나 라이브러리를 불러오기 위해 사용하는 import 가 가장 먼저 눈에 띕니다. 그리고 자바나 C, C# 등에서 익숙하게 봤을 main() 함수가 어플리케이션의 진입점이 된다는 것을 눈치챌 수 있었습니다.
main() 에 자동 생성된 코드가 거의 없을 거라는 것을 예상하셨겠지만, 정말로 들어 있는 내용이 없습니다 ^^;; main() 함수는 7번 행부터 정의하고 있는 실제 플러터 어플리케이션의 시작이라 할 수 있는 <위젯>을 호출하는 역할만 수행하고 있습니다. 샘플 프로젝트에는 MyApp 이라는 클래스가 StatelessWidget 을 상속받아 생성되고 있습니다.
<위젯>은 플러터에서 UI의 컴포넌트나 화면 단위를 만드는 단위입니다. 오래된 사람인 저는 컨트롤이라는 용어가 익숙합니다만 이제 <위젯>이라는 단어에 스스로를 맞춰보도록 하겠습니다 ^^;;
위젯은 크게 상태 값이 보존되는 위젯과 상태 값이 보존되지 않는 위젯으로 나뉜다고 합니다. MyApp 과 MyHomePage 클래스가 상속 받고 있는 StatelessWidget 과 StatefulWidget 이 정의하고 있는 위젯 클래스의 목적을 나타내주고 있습니다. 자세한건 저도 아직 잘 몰라서 천천히 좋은 감정으로 알아가 보도록 하겠습니다. ㅋ
샘플 프로젝트를 생성했을 때 이 프로젝트는 완전히 빈 깡통 프로젝트는 아닙니다. 간단하게 StatelessWidget과 StatefulWidget의 차이를 알아볼 수 있는 카운트 앱이 샘플 프로젝트에 담겨 있습니다. MyApp 과 MyHomePage 클래스에 들어 있는 코드를 자세히 볼까요?
최초 샘플 프로젝트 생성시에는 엄청난 양의 주석이 같이 있습니다. 주석을 모두 제거하면 두 클래스의 코드는 몇 줄 되지 않습니다. MyApp 클래스는 어플리케이션의 윤곽을 나타내기 위한 위젯을 리턴하는 코드가 전부입니다. 코드 내에서 다시 MyHomePage 클래스를 사용하여 `Flutter Demo Home Page`라는 타이틀을 가진 StatefulWidget 을 만들고 있습니다.
MyHomePage 위젯도 코드가 무척 단순합니다. 클래스의 인스턴스가 만들어 질 때 매개변수로 타이틀을 받아서 멤버 변수에 저장하고 _MyHomePageState 클래스 형식으로 createState() 함수를 호출해 위젯의 상태를 저장할 객체를 생성하게 됩니다.
마지막으로 _MyHomePageState 클래스입니다. 주석이 많이 생기기 때문에 주석을 삭제하면 내용을 한 눈에 알아보기 더 쉽습니다. 물론 주석에 자세한 설명들이 있기 때문에 공부하는 동안에는 한 번 읽어보는 것도 도움이 될 것 같긴합니다.
멤버 변수로 _counter 를 갖고 있으며 앱이 수행되는 동안 카운터 된 숫자를 기억하기 위해 사용하는 변수입니다. 앱을 실행하면 더하기 floatingActionButton 이 하나 화면에 나오고, 버튼을 누를때마다 _counter 의 값을 1씩 증가시킵니다. 내장 함수인 _incrementCounter() 에 증감연산자를 사용해 _conuter++ 를 실행하는 코드가 보입니다.
이처럼 플러터는 값의 저장, 보존이 필요한 경우 StatefulWidget 과 State 를 사용하고 그렇지 않은 경우 StatelessWidget 을 사용한다는 점을 기억하고 익숙해져야 할 것 같네요!
지난 포스팅에서 안드로이드 스튜디오를 업데이트하고 플러터 플러그인을 설치해보았습니다. 추가된 플러그인을 통해 샘플 프로젝트를 만들고 실행을 해보았지만 예상대로 문제가 발생했고, 안드로이드 가상 장치에서 시험을 해볼 수 없었습니다. 무슨 문제가 있었던 것일까요?
안드로이드 스튜디오에서 플러터 프로젝트를 생성하고 기본 샘플 앱을 가상 안드로이드 기기로 실행했을때 발생한 에러 화면입니다. 뭔가 SDK와 관련된 라이센스가 제대로 적용이 안된 것 같은 에러메세지입니다. 바로 터미널을 실행하여 플러터 닥터 flutter docter 명령을 통해 문제점을 확인해 보았습니다. 명령은 터미널에서 flutter docter 를 입력하여 수행합니다.
초록색 체크표시가 된 것들은 문제가 없는 부분들입니다. 느낌표로 출력된 내용을 보니 비주얼 스튜디오 코드를 위한 플러터 환경 구성 이후 변경된 것들이 좀 있어 보입니다. 가장먼저 Android toolchain 항목에 대한 명령을 통해 첫번째 문제를 해결해 보겠습니다.
flutter doctor --android-licenses
위의 플러터 닥터 명령은 동의해야 하는 약관, 라이센스에 대한 확인을 해주는 명령입니다. 실행후 나오는 내용을 살펴보고 (영어입니다 -_-;) 동의한다는 의미로 y 키를 몇 번 눌러주면 됩니다. 다시 닥터를 실행해서 처리가 잘 되었는지 보겠습니다.
오호라. 첫번째 Android toolchain 항목도 이제 초록색 체크로 바뀌었습니다. 플러터로 개발된 코드를 iOS 에서 실행할 수 있도록 하기 위해서는 Xcode 환경도 준비가 되어 있어야 합니다. 지난번에도 CocoaPods 설치하느라 고생했는데... 그래도 다시 한 번 해보았습니다.
# sudo gem install cocoapods
Password:
Fetching i18n-1.8.9.gem
Fetching tzinfo-1.2.9.gem
Fetching activesupport-5.2.4.5.gem
Fetching nap-1.1.0.gem
Fetching fuzzy_match-2.0.4.gem
Fetching concurrent-ruby-1.1.8.gem
...
...
Parsing documentation for gh_inspector-1.1.3
Installing ri documentation for gh_inspector-1.1.3
Parsing documentation for ruby-macho-1.4.0
Installing ri documentation for ruby-macho-1.4.0
Parsing documentation for cocoapods-1.10.1
Installing ri documentation for cocoapods-1.10.1
Done installing documentation for concurrent-ruby, i18n, thread_safe, tzinfo, activesupport, nap, fuzzy_match, httpclient, algoliasearch, ffi, ethon, typhoeus, netrc, public_suffix, addressable, cocoapods-core, claide, cocoapods-deintegrate, cocoapods-downloader, cocoapods-plugins, cocoapods-search, cocoapods-trunk, cocoapods-try, molinillo, atomos, CFPropertyList, colored2, nanaimo, xcodeproj, escape, fourflusher, gh_inspector, ruby-macho, cocoapods after 25 seconds
34 gems installed
자, 코코아포드 관련한 패키지들의 추가도 끝났으니 다시 flutter docter 를 실행해 봐야겠죠?
에러 메세지가 확~ 사라졌습니다. 이제 마지막으로 남은 것은 안드로이드 스튜디오에서 플러그인을 설치하는 일입니다. 에..? 플러그인? 분명히 설치했다고 생각했는데... 무슨 문제일까요? 일단 에러 메세지를 무시하고 코드를 안드로이드 에뮬레이터에서 실행해 보겠습니다. 희안하게도 문제 없이 실행이 됩니다.
어렵지 않죠? 네, 저는 어렵습니다 ㅎㅎ. 일단 동작이 잘 되는 것을 확인했으니, 본격적으로 뭔가를 만들어 봐야겠습니다!
먹고 살기 바쁘다 보니 첫 포스팅을 올리고 두달여만에 후속 포스팅을 올립니다. 그 사이에 생성해 둔 Firebase 의 Firestore 가 보안 정책 만료가 되었네요. 스스로 적어둔 지난번 포스팅을 다시 참고해 가면서 Firebase 에 Firestore NoSQL DB 를 사용 가능하도록 변경하고 글을 시작하도록 하겠습니다. 지난번 글은 아래의 링크입니다! (블로그가 역시 이렇게 좋습니다 ㅎㅎ)
Firestore 정책 업데이트 하기
Firebase 의 Firestore 는 최초 생성시 30일간 인증 없이 접근할 수 있는 Rule을 제공합니다. Firestore 콘솔의 <Rules> 탭에서 확인할 수 있습니다. 아마도 8월 14일에 DB 를 생성했고 그 시점으로부터 +1m 시점이 만료 시점으로 잡힌 것 같네요. 불필요한 비용 발생을 막기 위해서 들어가는 코드라고 생각하면 괜찮다는 생각이 듭니다.
물론 실제로 상용 서비스에서 Firestore 를 사용하는 경우에는 날짜 단위의 정책이 아니라 인증을 받은 경우 액세스가 가능하도록 변경하는 것이 좋을 것 같습니다. 자세한 내용은 Firestore 의 Rule 관련한 가이드를 살펴보시기 바랍니다! (이 글이 Firestore 를 위한 글은 아니라서라고 변명해 봅니다)
Firestore 사용을 위한 패키지 Import
지난 포스팅을 잘 따라오셨다면 Firestore 사용을 위한 준비가 끝났습니다. main.dart 파일을 열어서 추가한 Firestore 패키지를 코드에서 사용할 수 있도록 해보겠습니다. dart_todo 앱의 전체 코드 구조는 아래와 같습니다.
2행에 추가된 것처럼 cloud_firestore.dart 를 import 해주셔야 합니다. 간단한 프로젝트이기 때문에 코드의 구조도 간단합니다. Firestore 를 다루는 코드는 당연히 가장 마지막에 있는 _TodoListPageState 클래스에 기술되어 있습니다. 8행에서 시작되는 Todo 클래스는 Firestore 에 저장할 객체를 들고 있을 코드상의 객체를 위한 클래스 입니다. Firestore 에 생성한 컬럼과 동일한 구조, 자료타입으로 만들어 두었습니다.
StatelessWidget 으로 선언된 MyApp 은 기본 스캐폴딩을 통해 생성된 코드로 타이틀의 설정, 진입점을 TodoListPage 클래스로 지정해 준 정도의 변경사항만 가지고 있습니다. 코드는 아래와 같습니다
class Todo {
bool isDone = false;
String title;
Todo(this.title, {this.isDone = false});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '할 일 관리',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: TodoListPage(),
);
}
}
_TodoListPageState 클래스 살펴보기
Todo 앱은 ListTile 위젯으로 할일 하나하나를 표현하고 ListTile 은 ListView 의 Child 가 됩니다. ListView 는 다시 Expanded 위젯으로 감싸여 화면에 보여지게 됩니다. Expanded 위젯은 다시 StreamBuilder 위젯으로 감싸 스트림으로 Firestore 의 변경사항과 UI 의 변경이 이루어지도록 코드를 만들었습니다 (네, 제가 한건 아니고 <오준석의 플러터 생존코딩> 책의 내용입니다!)
위젯을 빌드하는 코드를 살펴보겠습니다. 위젯을 또 위젯이 감싸고 또 감싸고... 하는 연속이다 보니 복잡해 보이네요. UI 코드 보다는 StreamBuilder 위젯의 내용을 중점적으로 보시면 됩니다.
StreamBuilder 위젯의 가장 안쪽 코드에 _buildItemWidget 위젯은 할 일 하나하나를 ListTile 위젯으로 리턴하는게 목표인 위젯입니다. 유의할 점은 Firestore 에서 가져온 NoSQL Document 는 DocumentSnapshot 타입을 이용해야 한다는 점입니다. 코드 한번 보시고 가겠습니다.
네, 이렇게 UI 에서 Firestore 에 있는 정보를 연동할 준비가 되었습니다. 중간 중간에 보이는 _addTodo(doc), _deleteTodo(doc) 그리고 _toggleTodo(doc) 가 실제 비즈니스로직(?)이 들어간 메서드들이 되겠습니다. 절단 신공을 이용하여 이 세가지 메서드는 다음 포스팅에서 확인해 보도록 하겠습니다!
Flutter 를 이용하면서 Firebase 를 이용하는 방법을 살펴보겠습니다. 둘 다 구글에서 만든 것이라 그런지 연동하는 것이 어렵지 않습니다. 다만 손이 좀 가게되고 처음 다루는 경우에 조금 헤멜 수 있습니다. 참고로, iOS 용으로 프로젝트를 구성하는 방법을 다루고 있습니다. Android OS 는 별도로 설명해 보도록 하겠습니다. 순서는 아래와 같습니다.
Firebase / Firebase 에 새로운 Project 등록하기
Project Code / Firebase SDK 를 개발중인 Project Code 에 추가하기
Firebase / Cloud Firestore 에 새로운 Database 생성
Project Code / Cloud Firestore 의존성을 Project Code 에 추가하기
Firebase 에 새로운 Project 등록하기
Firebase 는 구글이 제공하는 PaaS 플랫폼입니다. 모바일 앱이나 웹 어플리케이션 등을 개발할 때 필요한 다양한 구성요소와 분석도구를 플랫폼으로 제공하고 있어 별도의 환경을 구성하는 어려움과 번거로움을 줄여주기 때문에 (돈만 잘 내면) 무척 편리합니다. http://firebase.google.com/로 접근하여 구글 계정으로 로그인한 후 <Create a project> 로 새로운 프로젝트를 생성해 보겠습니다.
Firebase 는 Project 를 만드는 것에서 부터 시작됩니다. Project 별로 서비스 구성요소나 분석도구 등을 사용할지 결정하고 이용하는 구조입니다. 간단하게 식별할 수 있는 Project 이름과 Project ID 를 취향껏 지정하도록 하겠습니다.
구글 애널리틱스는 사용자들의 행동 정보를 수집하는데 무척 유용한 도구입니다. 요즘은 웹 어플리케이션 뿐만 아니라 모바일 앱에서도 구글 애널리틱스를 이용하여 사용자 정보를 수집하는 경우가 많습니다. 이 포스팅은 Firebase 를 사용하기 위한 기본적인 설정 방법과 Cloud Firestore 의 셋업에 촛점을 맞추고 있으니 Enable 하지 않고 프로젝트를 생성해 보겠습니다.
어이쿠! 클릭 몇 번하고 키보드로 몇 가지를 입력하니 금방 프로젝트가 생성되었습니다.
Firebase SDK 를 개발중인 Project Code 에 추가하기
프로젝트가 생성되고 <Continue> 버튼을 누르면 프로젝트의 첫 화면으로 이동하게 됩니다. 이제 우리가 할 일은 Firebase 공통 SDK 의 설치를 통해 앱 또는 웹 어플리케이션이 Firebase 를 사용할 수 있는 기본 환경을 갖도록 하는 것입니다. 이후 어떤 기능, 제품을 쓰느냐에 따라 라이브러리 의존성 등 필요한 셋팅을 해주면 됩니다. 이 포스팅에서는 <iOS> 환경에 대해 이야기 하고 있기 떄문에 화면의 동그란 아이콘들 중 <iOS> 라고 적힌 버튼을 눌러보도록 하겠습니다.
앱에서 Firebase 를 사용하기 위한 첫번째 절차는 사용할 앱의 정보를 등록하는 것입니다. iOS Bundle ID 와 닉네임, 앱스토어 ID 등의 정보를 입력해야 합니다. VS Code 를 이용해서 Flutter 를 사용하는 중인데요 VS Code 의 Explorer 에서 Shift + Command + F 를 눌러 PRODUCT_BUNDLE_IDENTIFIER 를 검색하면 Flutter 가 미리 만들어 준 iOS Bundle ID 를 찾을 수 있습니다. ID 값을 복사하여 Firebase 의 등록 화면에 입력해 줍니다.
Bundle ID 를 넣고 나면 이제 Firebase SDK Config 파일을 다운로드 받을 수 있는 화면으로 이동합니다. Download 버튼을 눌러 GoogleService-Info.plist 파일을 다운로드 받아 Project Code 에 추가해 주어야 합니다. 추가 작업을 위해서는 Xcode 가 필요하니 잠시 Xcode 를 실행해서 VS Code 에서 만들고 있는 Flutter 프로젝트를 열도록 하겠습니다.
Xcode 에서 Runner/Runner 위치에 GoogleService-Info.plist 파일을 추가해 줍니다. 이유는 확인해 보지 못했습니다만 VS Code 에서 해당 파일을 Runner 하위에 넣어주었을 때는 프로젝트 빌드후 실행시 앱이 실행되지 못하고 크래시 되는 문제가 발생했습니다.
Xcode 에서 파일을 추가해 준 경우 동일한 위치에 파일이 생성되긴 하는데... 혹시 이유를 아시는 분은 댓글 부탁드립니다! 여튼, Xcode 에서 파일을 잘 추가했다면 저장후 종료하고 VS Code 로 돌아오면 되겠습니다.
Firebase 를 위한 환경 설정 파일을 추가했으니 SDK 가 잘 설치되도록 하기 위한 CocoaPod 관련한 확인을 해봐야합니다. Firebase 는 CocoaPod 를 이용해서 의존성 관리를 하고 있기 때문에 CocoaPod 가 잘 동작하고 있어야 합니다. VS Code 에서 Flutter 프로젝트를 생성한 경우 이미 .xcworkspace 파일이 추가되어 있습니다. 따라서 아래 내용은 참고만 하셔도 무방합니다.
아래의 내용은 Swift 나 Ojective-C 를 이용하는 경우 Firebase 를 초기화 하는 코드의 예제입니다. 우리는 Flutter 를 쓰고 있기 때문에 아래의 코드 역시 무시하고 넘어가셔도 무방합니다. Next 버튼 고고씽!
마지막으로 iOS 에서 Firebase 를 사용하기 위한 가이드 문서 안내가 나옵니다. 시간이 되시는 분들은 한번 문서를 열람해 보셔도 좋습니다. 추후 필요할 때 내용을 찾아보는 정도로 충분하니, 가이드가 존재하는 구나 정도를 인지하고 넘어가도 좋습니다.
Cloud Firestore 에 새로운 Database 생성
이제 Firebase 사용을 위한 기본적인 준비가 끝났습니다. 이 포스팅에서는 Firebase 가 제공하는 Cloud Firestore 를 쓰기 위해 해주어야 하는 작업을 이야기하고 있기 때문에 Firestore 에 Database 를 생성하는 과정도 살펴보도록 하겠습니다. 앱의 기본적인 설정이 끝났다면 아래와 같은 화면에 도착했을겁니다. 우측의 오렌지색 <Cloud Firestore> 이미지를 눌러 DB 생성을 해보겠습니다.
네, 거침 없이 <Create database> 버튼을 누르겠습니다. 이제 거의 고지에 도달했으니 조금만 더 힘내서 가보겠습니다. 영차, 영차!
Cloud Firestore 는 Production 과 Test 모드를 제공합니다. 연습을 해볼때는 Test 모드로도 충분하겠습니다만 실전에서는 Production 모드를 이용해야겠죠? Test 모드를 선택하고 Next 버튼을 누릅니다.
Firebase 도 여느 클라우드 서비스와 마찬가지로 여러 리전 Region 을 운영하고 있습니다. 실제 서비스에서는 응답 속도를 감안해야 하기 때문에 주요 사용자가 있는 국가, 장소를 감안하여 리전을 선택해야 하지만 지금은 어떤 것을 선택해도 크게 문제는 없으니... 임의로 선택하시고 Done 버튼을 누르겠습니다.
Cloud Firestore 는 NoSQL DB 입니다. 다양한 형태의 문서 Document 들의 집합을 취급하게 되고 각각의 Document 집합은 Collection 이라 부르고 있습니다. Collection 을 하나 만들고 샘플 데이터를 넣어보도록 하겠습니다.
Collection 의 이름을 임의로 지정하고 Next 버튼을 누릅니다. Unique Key 로 사용할 Document ID 는 자동으로 생성할 수 있습니다. Auto-ID 버튼을 활용하시기 바랍니다. NoSQL 이기 때문에 정해진 Scheme 이 없습니다. 원하는 필드를 정의하고 샘플 데이터를 자유롭게 넣어보시기 바랍니다.
Cloud Firestore 의존성을 Project Code 에 추가하기
이제 대장정의 끝에 <거의> 도달했습니다. 우리는 지금까지 Firebase 사용을 위한 설정 파일을 생성, 프로젝트 코드에 추가했고 Cloud Firestore DB 를 생성해 보았습니다. Firestore 를 사용하기 위해서 Flutter 프로젝트의 pubspec.yaml 파일에 의존성 정보를 추가해 주어야 합니다.
Firebase 는 개별 제품, 기능 별로 별도의 패키지를 사용합니다. Cloud Firestore 는 cloud_firestore 라는 이름의 패키지를 추가해야 합니다. Flutter 프로젝트 루트의 pubspec.yaml 에서 dependencies 노드를 찾아 cloud_firestore 를 추가하면 이제 작업 끝입니다.
다음 포스팅에서는 이렇게 추가된 Cloud Firestore 를 이용하는 방법을 정리해 보도록 하겠습니다. 참고로 다음 포스팅에서 사용할 코드는 오준석님이 쓰신 <오준석의 플러터 생존코딩> 의 Todo 앱 입니다. 해당 책에서는 Android OS 에서 연동 방법만 나와 있어서 조금 아쉬웠는데 이렇게 포스팅으로 정리해 보니 그렇게 어렵지 않다는 느낌입니다 :-) 플러터 입문자를 위한 훌륭한 기본서이니 한번 구매해서 읽어보시죠!