갤러리 앱에는 왜 셔플 모드가 없을까?
개인적으로 사진이나 음악을 폴더별로 잘 저장해서 다니는 편이다.
음악 재생에는 셔플 모드가 있어서 오랫동안 잊고 있던 명곡들이 나오면 기분이 좋곤 했던 기억이 있다.
하지만 대부분의 사진 앨범(폴더)는 최신순으로 정렬되어 있고 오래된순 으로 바꿀 순 있지만 이 정렬 방법을 바꾸는 사람은 거의 없는것 같다.
만약 사진 앨범이나 앨범 안의 사진들을 무작위 순서로 보여줄 수 있다면 어떨까 하고 생각을 해 보았다.
오래전 사진을 우연히 발견해서 지인들에게 공유해서 추억을 되살리거나 새로운 아이디어를 얻는 경우가 생길 것 같아서 쓸모있지 않을까 싶었다.
개인적으로 쓰고 싶어서 만드는 앱이라서 간단하게만 만들어 사용할 수도 있겠지만 App Store에 올려본지도 오래 되었고 다른 사람들에게도 써 보라고 할 수 있는 정도까지 만들어보고 싶어서 미니 프로젝트로 진행해 보기로 하였다.
Shuffle Gallery - Google Play 앱
결과적으로 Flutter
로 Android 앱을 개발하여 Google Play에 등록까지 해 보았다.
앱은 사진 앨범이나 앨범 안의 사진들을 무작위로 섞어서 보여주며 맘에 드는 사진을 공유할 수 있도록 만들었다
매우 간단한 앱이었지만 나에게 있어서는 꽤 고생을 했기 때문에 앱을 만들면서 고민한 과정들을 글로 써 보고자 한다.
일단 내가 원하는 앱이 앱스토어에 올라와 있다면 굳이 만들 필요가 없으니 한 번 찾아 보았다.
내가 만드려는 앱과 유사한 앱은 찾아보니 Random Gallery
라는 앱만 있는 것 같다.
내가 처음 생각한 이름도 Random Gallery였는데 이름이 너무 개발자스럽다는 주변 의견이 있어서 Shuffle
이라는 단어를 쓰기로 했었다. 저 앱 개발자는 찐 개발자인가보다.
앱은 내가 바라던 것 처럼 사진을 무작위 순서로 보여주었지만 이런 부분이 부족했다.
경험해 보니 내가 필요로 하는 기능을 리스트화 할 수 있었다.
복잡한 시나리오를 가지고 있지 않으므로 간단하게 손그림으로 그려보았다.
디자인 센스는 영 없나 보다.
기존에 안드로이드를 해본 경험이 있고 Kotlin
을 배우고 있던 참이라 안드로이드에서 프로토타이핑을 진행하였다.
하던 도중에 Flutter
에 대해서 접하게 되었고, 아이폰에서도 돌아가는 앱을 만들 수 있다는 점이 흥미로워서 Flutter로 갈아탔다.
다만 지금은 Windows 개발 환경밖에 없어 일단 안드로이드 타겟으로 개발하기로 하였다.
개발 환경 설정은 Flutter 개발환경 설정 (with Flutter Doctor).
참고로
Mac : Android / iOS 앱 모두 개발 가능
Windows : Android 앱만 개발 가능하다.
얼른 돈모아서 Mac을 사도록 하자...
Flutter는 Dart
라는 언어를 사용하기에 Dart언어에 대한 스터디를 유튜브로 매우 간략하게 해 봤다. 그런데 꽤 배우기 쉬운 언어였던것 같다.
30분만에 배우는 Dart 언어 를 보고 이후에 필요한 내용은 구글링해서 가능할 정도였다!
평소 C++을 주로 사용하고 Java/Kotlin 을 알고 있었는데 몇 가지 언어의 패러다임이 다 들어가있는것 같아서 배우기에 접근성이 좋은 것 같다.
튜토리얼 을 따라해보고 정말 놀란 점은 간결하게 짠 코드 몇 줄로 안드로이드 앱을 쉽게 작성할 수 있다는 것이었다.
물론 안드로이드 native에 가까워질수록 활용하기 어려워 보이지만 일반적인 View만 가진 앱은 정말 쉽게 만들 수 있을 것 같아서 자주 활용하지 않을까 싶다.
pub.dev 에서 이미 만들어진 앱 관련 패키지들을 사용할 수 있어 안그래도 빠른 앱 개발이 더 빠르게 가능하도록 한다.
Shuffle Gallery를 만들면서 사용한 패키지들은
패키지 이름들만 보아도 앱 하나가 뚝딱 만들어질 것 같아 보인다!
그래도 역시 세세한 부분은 각각의 패키지를 조금 활용하거나 추가로 구현해야 할 부분들이 있었으며 이후 글에서 하나하나 기록해 보고자 한다.
첫 화면으로 진입 시 permission_handler
를 통해 권한을 획득한 다음,
photo_manager
api를 사용하여 모든 앨범의 정보를 얻어온 후 이를 무작위로 셔플하여 Grid View로 나열하였다.
사용한 셔플 알고리즘은 Knuth Shuffle 로 매우 간단하게 구현할 수 있었다.
List<T> shuffle<T>(List<T> items) {
var random = new Random();
for (var i = items.length - 1; i > 0; i--) {
var n = random.nextInt(i + 1);
var temp = items[i];
items[i] = items[n];
items[n] = temp;
}
return items;
}
photo_manager 에서는 Recent
라는 이름으로 모든 사진을 얻어올 수 있어 이름만 All
로 바꾸고 항상 첫 번째 아이템으로 위치하도록 지정했다.
이후 앨범의 대표 썸네일 이미지를 얻기 위해 앨범마다 가장 최근 사진 1장의 썸네일만 로드하여 가장자리가 둥글게 ClipRRect
를 사용하여 꾸며 보았다.
각각의 앨범 항목을 누르면 앨범의 모든 사진을 섞어서 보여주는 Grid/List View를 구성하였다.
정렬 버튼을 눌러 최신 순서대로 볼 수도 있다
앞서 앨범 목록과 사진 목록 모두 스크롤 시에 더 많은 사진을 보여주기 위해 SliverGrid(SliverList)
를 사용하였다.
안드로이드 native 기준으로는 CoordinatorLayout
를 사용해야 하고 이를 위해서 알아야 할 점이 많았는데 Flutter에서 매우 간단하게 지원하고 있었다.
이 화면에서는 photo_manager api 를 통해 선택한 앨범 내 모든 사진 정보를 받아온 후 이를 셔플하고 각각의 썸네일을 받아서 목록으로 보여주었다.
이 때 썸네일을 로드하는 과정은 시간이 오래 걸리므로 비동기식으로 구현하게 되는데
만약 앨범 안에 수많은 사진이 있을 수 있으므로 한 화면에 보이는 썸네일만 먼저 로드하고 이후 Scroll Event를 받아와 일정 수준 이하로 스크롤이 내려가면 추가로 썸네일을 로드하도록 구현하였다.
일반 갤러리만큼은 아니어도 꽤 부드러운 수준으로 스크롤이 가능해서 만족스러웠다.
사진 목록 화면에서는 셔플하지 않은 원래대로의 목록 및 Grid 칸 수를 1~8칸까지 조절가능하도록 구성하였다.
만들면서 가장 맘에 드는 기능으로 사진 목록을 ListView(Grid 1칸)로 보는 경우 밋밋한 사각형 View가 아닌 CardView
를 적용하여 나름 심미적인 부분을 신경써봤다.
이 때 리스트가 변경되고 썸네일을 다시 로드하게 되는데 만약 이미 로드한 썸네일이 있다면 미리 캐싱해두었다가 다시 사용하도록 하여 나름 성능을 높여 보았다.
만들고 보니 알고리즘을 공부할때 배운 동적 계획법
과 비슷한 것 같다. 현업에서도 활용할 일이 거의 없었는데 이럴 때 배운 것들을 사용해서 뭔가 뿌듯했다.
사진 목록에서 사진을 선택하면 한 화면에서 볼 수 있게 하며 zoom in/out, full screen mode가 가능하도록 구성하였다.
그리고 기존 갤러리 앱과 동일하게 PageView 형식으로 볼 수 있게 구성하였다.
Flutter에는 PageView
가 있지만 안드로이드처럼 미리 각 Page를 로드하지 않는 것 같았다.
하지만 찾아보니 이미 preload_page_view
패키지가 있었고 이를 통해 쉽게 만들 수 있었다.
그리고 zoom in/out 을 쉽게 사용할 수 있는 photo_view
라는 패키지도 있어 두 패키지를 조합하기만 해도 상세 보기 화면은 쉽게 구성할 수 있을 것 같았다.
하지만 zoom in 한 이후 사진에 대한 스크롤이 Page 스크롤까지 넘어가는 문제가 있어 이 부분을 해결하는데 꽤 고생하였다. 앱을 만들면서 가장 어려운 부분이었던 것 같다.
결과적으로 photo_view 의 Scale Listener를 통해 preload_page_view 의 Page Scroll 을 제어하는 방법으로 해결하였고 해결했을 때의 성취감이 상당했다.
(문제가 있던 부분. 사진의 오른쪽을 보려고 드래그했는데 페이지가 넘어간다)
(사진이 확대 모드일때는 스크롤하지 않는다. 일반 갤러리 UI와 다르게 이렇게 적용해보고 싶기도 했다.)
추가로 photo_view 패키지는 사진을 double touch 하는 경우 화면에 꽉 차도록 zoom in 해 주는데 기본 앱 갤러리에서는 제공하지 않던 기능이라 매우 마음에 들었다.
(double touch 할 때마다 BoxFit 을 Contain -> Cover -> None 으로 변경한다.)
Share기능은 정말 간단하게 share_plus 패키지의 Share에 사진 파일 경로만 전달해주면 바로 공유 기능이 완성되었다.
안드로이드 앱 배포는 2021년 8월부터 Apk
형태가 아닌 App Bundle
로만 가능하다고 한다.
앱의 모든 컴파일된 코드 및 리소스를 포함하며 APK 생성 및 서명을 Google Play에 맡기는 게시 형식이라고 하며, 차이점이라면 기존 Apk에 비해 서명 key를 분실하는 위험이 줄어들고 기기 최적화에 더 좋다고 한다.
이전까지 apk로만 배포해 보았어서 앱 배포에 대한 내용은 구글링하여 따라해 보았다. (참조 링크)
앱을 올리면서 코딩 외적으로 가장 어려웠던 부분은 역시 App Icon
만들기였다.
첫 번째 어려움은 내가 디자인 센스가 전무하다는 점과 두 번째로 이후 iOS 에도 적용할 수 있는 형식으로 만드는 점이었다.
혼자서 생각해서 그려본 아이콘은 너무 개발자 같은 디자인이었기에 pinterest 와 구글 검색을 통해 여러 아이콘을 벤치마킹하였다.
좌 : 손으로 그린 하찮은 아이콘
우 : 그나마 벤치마킹해서 툴로 그려본 아이콘
아이콘을 그릴 때 안드로이드의 Adaptive App Icon 와 Apple App Icon 의 가이드라인이 모두 맞게 디자인한 다음
그린 하나의 이미지를 AppIcon 이라는 웹에 업로드해서 각 플랫폼에 맞는 형식으로 변환할 수 있다. 결과물을 프로젝트 폴더에 그대로 덮어쓰기 하면 적용할 수 있었다.
마지막으로 앱 스토어에 올릴 때 가이드 스크린샷을 첨부해야해서 예제 이미지들을 UnDraw 를 사용하여 만들어 보았다.
한국어와 영어 두 가지로 어떻게 만드는지 몰라서 아직 텍스트가 들어갈 부분이 비어 있는데 더 찾아보니 String들과 같이 별도로 업로드하면 되는 것 같다.
예전에 개인적으로나 회사에서의 Apk를 배포한 적이 있었는데 지금은 개인정보 이슈들로 인해 설문이나 작성할 내용이 훨씬 더 많아진것 같다.
거의 반나절을 어떻게 작성하는지 찾아보며 업로드한 것 같다.
승인이 나는 것도 예전엔 하루 내에도 가능한 게 지금은 3일 정도는 기다린것 같다.
(기다린 만큼 뿌듯했다!)
가지고 있는 안드로이드 단말에 잘 설치가 되고 동작도 잘 되고 있다.
개인적으로 쓰려고 만든거라서 나에게는 매우 만족스러운 앱이지만 스토어에 올리고 보니 아직 부족한게 많아 보인다.
앱을 개발하고 느낀점을 간단하게 나열하면 이렇다.
이후 사용하며 버그나 Crash가 발생할 것이 자명한데 fix 및 새로운 기능을 업데이트하는 경험을 하게 될 것 같다. 벌써 버그를 하나 찾았기 때문이다.