플러터(Flutter)를 공부하며 앱을 만들어 봤습니다.

플러터(Flutter)는 구글에서 만든 Dart 언어 기반의 멀티 플랫폼 개발도구 입니다. 개인적으로 발표 시점부터 꾸준히 지켜보고 있었는데 조만간 뭔가 하나 만들어야지.. 하다가 한해가 넘어가 버렸습니다.

이전글(http://practical.kr/?p=53)에서도 이야기 한적이 있지만 멀티 플랫폼에 대한 환상(?)을 가지고 있어서 페이스북에서 나온 ReactNative(이하 RN)로 프로젝트를 진행한적이 있고(http://practical.kr/?p=19) 네이티브(주로 iOS) 개발을 하고 있기 때문에 네이티브 개발과 멀티플랫폼 개발툴간의 장단점을 조금은 안다고 생각하고 개발 과정을 한번 정리해 봤습니다.

UI 디자인

좋아하던 Xcode를 싫어하기 시작한게 아마도 Auto Layout이 나오기 시작한 시점이이 아닌가 싶은데요. 맞춰놓으면 작은 변경에도 Layout 전체를 조정해야 하는 Auto 스럽지(?) 않은 Xcode가 최근에 SwiftUI라는 Flex 스러운 UI 디자이너를 만들어서 모바일 개발에서 UI를 만드는 일이 사실 전체적으로 비슷한 기조로 가고 있는듯 싶습니다

Flutter 의 UI 디자인은 코드로 해야 합니다. 그래픽 디자인 도구가 없어요. 하지만 Flex 디자인의 구조를 차용한 Cascade 구조를 사용해서 모듈화를 할 수 있습니다. 컴포넌트를 좀 더 잘게 구성하고 모듈화 시키면 디자인 화면에 cascade된 UI Design Tree의 복잡성을 줄일 수는 있습니다만 코드와 통합된 UI 코드는 꽤 복잡하게 보일 수도 있지만 어렵지 않게 화면을 구성할 수 있습니다. 그리고 개인적으로는 RN의 CSS 구조보다는 좋다고 생각합니다. 그건 아마도 제가 CSS를 좀 싫어(?)해서 그런건지도 모르겠습니다. CSS를 잘 쓰시는 분들은 CSS 재사용 구조가 더 좋을 수도 있습니다. 절대적으로 개취(?)입니다.

컴포넌트 혹은 패키지

재사용 가능한 라이브러리를 플러터에서는 패키지라고 부릅니다. Node가 먼저인지 CocoaPod이 먼저인지 아니면 그전에도 패키지 매니저가 있었는지 잘모르겠습니다만 플러터에도 패키지 매니저가 내장되어 있습니다. pubspec.yaml 파일에 https://pub.dev 에서 검색한 패키지를 추가하고

> flutter get pub

위와 같이 실행하면 패키지를 설치해 줍니다. 다 찾아본건 아닙니다만 이젠 필요한건 거의 있는것 같습니다. 이번 앱 개발하면서도 여러개 테스트 해봤는데 보는 눈만 좋다면 코드를 상당히 줄일 수 있습니다. pub.dev 페이지에서 인기도를 참조하면 신뢰성 있는 패키지를 고룰 수 있습니다. 저는 15개나 사용했네요. 제가 페북에 패키지 설치를 의존성주입이라는 말을 썻더니 그게 아니라고 하시더라구요. 사실.. 아닐수도 있고 맞을 수도 있는 말입니다만 패키지는 그냥 설치라고 해두죠 ^^;

디버깅

Flutter는 개발도구를 Android Studio를 씁니다. 아마 구글 입장에서도 새 도구를 만드는것보다 이렇게 하는게 쉬웠을것 같네요. 익숙하기도 하구요. 그래서는 아니지만 디버깅 도구가 RN에 비해서 월등히 좋습니다. 물론 Xcode에 비할바는 아니지만 브레이크 포인트를 걸어서 스택데이터를 볼 수 있다는것만 해도 정말 좋습니다. 당연한거지만 RN으로 개발해 보면 이게 왜 좋은지 알게 됩니다.

문서화

이것도 RN과 비교할 수밖에 없는데 너무 빈약한 문서로 고생했던 기억이 있는데요. 그에 반해 구글답게 개발문서가 아주 잘 정리되어 있습니다. 게다가 이제 버전이 꽤 올라와 있는 상황이라 StackOverFlow에도 Flutter 관련 Q/A가 필요한만큼(?) 있습니다.

그래서

평소에 앱 혹은 프로그램을 하나 만드는게 가장 빠른 언어 공부 방법이라고 생각하고 있었습니다. 그래서 앱을 하나 만들었습니다. 대략 3주 정도 걸린것 같습니다. 딱히 별 아이디어가 없어서 만들기 쉬운걸로 만들었습니다. QRCode를 생성하고 읽어서 저장하고 조회하는 기본적인 기능을 가진 앱을 하나 만들었습니다.

앱을 만드는데 가장 기본적으로 필요한 라이브러리들을 선정하고 사용법을 읽히는 일은 기존에 네이티브앱을 만들때 하던 방식과 동일 할 수 밖에 없습니다. 기본 라이브러리를 쓸것인지 아니면 외부 패키지를 사용할 것인지를 선택하고 기능에 적절하게 구현하는 행위를 반복하다보면 앱이 하나 완성되어 있는거죠 ^^;

UI와 관련해서는 기본적으로 필요한 거의 대부분의 컴포넌트가 이미 만들어져 있습니다. 아래와 같은 화면을 구성하려면 화면 하단의 TabBar, 화면 상단의 TitleBar, 리스크 구조의 스크롤 그리드, 이미지를 보여기기 위한 Image 컴포넌트등 다양한 기본 컴포넌트와 그 구조를 정의 하기위한 Layout 컴포넌트들 준비되어 있습니다.

그외에 기본으로 지원되지 않거나 기본으로 지원되는 것들이 예쁘지(?) 않다면 외부 패키지를 설치해서 사용할 수 있습니다. 제경우 Store, QRCode Reader, 다국어 지원 등을 위하여 외부 패키지를 사용 했습니다.

제가 사용한 기본적인 몇가지 패캐지 리스트는 아래와 같습니다. 이외에도 몇가지가 더 있습니다만 대략 입맛에 맞게 고르시면 됩니다.

Data Store  - https://pub.dev/packages/provider
http 통신 - https://pub.dev/packages/http
다국어지원 - https://pub.dev/packages/easy_localization
QRCode - https://pub.dev/packages/qrcode
컬러피커 - https://pub.dev/packages/flutter_colorpicker

아이폰용앱은 아래 링크에서 다운 받으실 수 있습니다. 안드로이드용은 스토어 승인단계에 있는데 요즘은 안드로이드가 애플스토어보다 승인이 느리군요. 승인되면 링크 올릴께요. (5일만에 승인이 났습니다. 요즘은 아이폰이 더 빠릅니다)

아이폰앱 다운로드 - https://apps.apple.com/app/id1523448616
안드로이드 다운로드 - https://play.google.com/store/apps/details?id=com.rtlink.qscanner

결론

2020년에 앱개발은 굉장히 다양한 선택지가 있습니다. 업무의 특성에 따라 혹은 개발팀의 리소스, 혹은 취향에 맞게 다양한 개발도구를 선택 할 수 있습니다. 개인적으로 소프트웨어 개발의 가장 중요한 요소는 생산성이라고 생각하는 편이라 이런 도구가 많이 나오는건 환영하고 대부분 확인해 보는 편입니다만 그중에서도 Flutter는 굉장히 매력적인 도구라고 해야 할것 같습니다. 그리고 아마도 상당기간 사용하게 될것 같다는 생각을 했습니다.

아참! 앱은 무료! 공짜! 입니다. 그리고 개발의뢰 환영합니다. ^_^

Dart Cheat Sheet는 뽀너스!

2020년 7월 21일 박병일

iOS/Swift – iCloud Drive에 파일 업로드 하기

2011년 4월에 애플 앱스토어에 업로드한 Fake Location이 어뷰징 요소가 있다는 이유로 2020년 4월에 스토어에서 강제로 퇴출당했다. 이걸 마지막으로 앱스토어에 판매중인 앱이 한개도 남지 않게되어 새로운 앱을 하나 만들기로 했다.

그날 페북에 올린글

몇일간 남는 시간을 쪼개어 그럭저럭 보이스를 녹음하고 플레이하는 작업을 하고 있었는데 알수 없는 문제에 봉착했다. 녹음된 보이스 파일은 아이폰에 저장되어야 하고 옵션으로 iCloud Drive에 저장되어야 해서 iCloud에 저장하는 기능을 추가 했는데 파일은 분명히 저장이 되고 원격 플레이까지 정상적으로 되는데 iCloud Drive에 폴더와 파일이 보이지 않는 문제가 발생했다.

알고보면 간단한 문제였지만 문제라는게 항상 그렇듯 모르면 괴로울 뿐이다. 게다가 코드 문제라기보단 설정(?) 문제라면 해결후 허무함까지 동반한다.

아이폰 로컬 폴더 얻기

아이폰 로컬 도큐먼트 폴더를 얻는 방법은 아래의 코드로 저장할 파일을 생성 할 수 있다. 생성된 audioFilename을 AVAudioRecorder에 넘겨주면 녹음이 완료된후 파일이 생성되는 것을 확인 할 수 있다. 아래 코드의 결과로 대략 이런 로케이션이 얻어진다.

file:///var/mobile/Containers/Data/Application/9F57DEC2-3A40-40AA-9EC5-5D104E94053F/Documents/voice.m4a

let saveFilename = "voice.m4a"
let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let audioFilename = path!.appendingPathComponent(saveFilename) 

iCloud Drive 원격 폴더 얻기

원격 폴더를 얻는 방법은 약간의 설정작업을 해야한다. Xcode의 Signning & Capability탭에서 +Capability 탭을 선택하여 iCloud 사용설정을 해야한다. 물론 이걸 하려면 개발자 계정이 필요하다.

설정방법

위와 같은 설정을 해주면 .entitlement 파일이 생성된다. 그리고 아래 코드를 이용하면 iCloud Drive 폴더에 파일을 생성 할 수 있다. 파일명이 아래처럼 생성된다. 레코딩 결과가 생성된 파일명으로 잘 저장되었다.

file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~*****/Documents/voice.m4a

let saveFilename = "voice.m4a"
let path = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
let audioFilename = path!.appendingPathComponent(saveFilename) 

그런데 폴더가 보이지 않는다

iCloud Drive에 파일을 업로드 했으니 당연히 나의 맥북 Finder 에 iCloud Drive에 오디오 파일이 보여야 하는데 보이지 않았다. 여기에 보이지 않으면 파일을 복사하거나 이동 시킬 수 없다. 난감하네… 그래서 3일을 까먹었다. 결론적인 이야기지만 클라우드에 파일을 업로드 하고 사용자가 손대지 못하게 하려면 여기까지만 하면 된다. 물론 아이폰의 설정 – iCloud – 저장공간관리 – (앱) – 에서 삭제 할 수는 있다.

문제의 해결은 매우 간단했다. info.plist에 아래와 같은 Key값을 추가 해주면 된다. 물론 key는 각자의 것을 사용해야 한다. 개발자가 코드로 문제를 해결 못하고 설정때문에 몇일씩 까먹고 나면 매우 허무해진다. ^^; 이건 아래의 stackoverflow 링크의 중간쯤에 ‘난 이렇게 해결했어’ 가 있었는데… 글타래가 길어서 대충 읽고 넘어가느라 못본거 였던것 뿐이었다. 결국 3일째 모든 대답들을 다시 꼼꼼히 읽고 다 시도해 보고서야 문제를 찾을 수 있었다. 교훈은 좀 더 꼼꼼해 져야 한다.. 정도?

<key>NSUbiquitousContainers</key>
<dict>
    <key>iCloud.net.redacted.docTest</key>
    <dict>
        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
        <true/>
        <key>NSUbiquitousContainerSupportedFolderLevels</key>
        <string>Any</string>
    </dict>
</dict>

https://stackoverflow.com/questions/25203697/exposing-an-apps-ubiquitous-container-to-icloud-drive-in-ios-8

이 설정을 추가하면 아이폰의 파일앱 그리고 계정이 연결된 맥북 또는 다른 컴퓨터에서 생성된 폴더와 파일을 확인 할 수 있다.

이 프로젝트는 현재 대략 20% 정도 진행되었다. 시작만 해놓고 마무리는 하지 못한 일들이 너무 많아서 이제는 외부에 공표를 해가며 스스로를 채찍질을 해보려고 하고는 있지만 과연 마무리를 할 수 있을지는 잘 모르겠다. 블로그 한개 썻으니 그나마 보람이 있는것일까?

2020/05/05 박병일