ArtWhale
👀 Overview
한줄 소개 | Album을 그려주는 AI와 이를 공유하는 플랫폼 서비스 |
진행기간 | 2022.09.28 ~ 2022.11.29 |
Skills | Glide, Retrofit2, Coroutine, Flow, LiveData, Hilt, RoomDB, FileUtil, ViewModel |
팀구성 | Android 1 / Spring 1 |
❓ 개발 의도 및 개발 목표
기획 의도
우리가 유튜브에서 보고 싶은 영상을 고르게 되는 이유 중 가장 큰 이유인 썸네일이라고 볼 수 있습니다.
그리고 이를 노래에 적용하면 노래에서 썸네일 역활을 하는 부분은 앨범 표지라고 생각합니다.
또한 노래를 반복해서 듣다보면 노래 제목은 기억나지 않더라도 앨범표지는 흐릿하게 기억나는 경우가 있습니다.
그리고 모바일 환경이 더욱 활성화 된 요즘 앨범표지 썸네일의 중요도가 높아지고 있습니다.
개발 목표
앨범표지를 만들지 못해 검은 배경화면의 앨범 아트를 가지고 있는 분들을 위해 자동으로 노래의 가사를 입력받고 감정을 선택해서 앨범표지를 만들어주는 서비스를 제공하는 것이 목표입니다. 그 노래의 가사와 분위기를 담은 유일한 앨범표지를 만드는 것이 저희의 목표였습니다.
- Android 개발 목표
- Android MVVM Architecture를 준수하고, 근 1년간 학습한 안드로이드의 지식을 최대한 활용하고자 함
- RoomDB / Flow / Worker Thread / Jetpack Navigation / Fragment Transaction / Coroutine Etc..
🍀 서비스 내용
- 사운드 클라우드를 이용하는 비교적 작은 규모의 가수들은 앨범표지가 없다. 이를 가사에 맞는 이미지를 생성해주는 프로그램이다.
- 유튜브 영상도 썸네일을 통해 들어가는게 많은 것처럼 사운드 클라우드에서도 앨범표지가 있어야 조금이라도 더 관심을 받을 수 있다고 생각했다.
- 또한 자신이 SNS에 좋아하는 가사를 올릴때도 해당되는 가사를 배경화면으로 활용가능하다.
- 유튜브에서 노래 재생하면 가사별로 영상 백그라운드 이미지를 변환하여 넣는것도 가능하다.
🛠 기술 스택
- Glide
- Retrofit2
- Coroutine
- Flow
- LiveData
- Hilt
- RoomDB
- FileUtil
- ViewModel
- CircleIndicator2
🖥 개발 내용
Splash & Login 화면
- Android 공식 가이드에 따른 Splash화면을 구현함
- Google OAuth를 통해 구글계정 로그인 기능을 구현함
- SharedPreferenced를 통해 토큰을 저장하고, 자동 로그인 기능을 구현함
- 토큰이 expired 되었을 시 로그인 갱신을 하도록 구현함
공통
- CustomToolBar를 구현
- 각 화면에 맞도록 보여지는 뷰를 변경하도록 구현함
- MusicPlayer Class
- 음악을 플레이하는 클래스를 별도로 만들어서 관리함
- MediaPlayer를 가지고 음악을 로드 및 플레이를 수행, 타이머를 개별적으로 가지면서 음악 플레이하는 중 타이머를 동작시킨다.
- pause, play, stop, update의 공개 메소드를 제공하며 음악 플레이어의 상태를 LiveData로 View에게 알려준다.
- 타이머 또한 LiveData로 관측자에게 상태를 알려주며 관리한다.
- MusicPlay Screen
- BottomSheet로 구현을 함
- music data를 받아와서 이를 play함
- 어디서든 이를 호출할 수 있음
- Domain Layer
- UseCase를 적용하며 Domain Layer를 적용시켜 보았다.
- 필요한 기능들만 ViewModel에 제공하도록 하고자 함
Main - Music
- 상단 광고뷰
- 코루틴을 활용하여 자동 스크롤 구현
- RecyclerView와 Indicator를 이어 일정 시간이 지났을 때 다음 index로 움직인다.
- NestedScrollView를 활용해 중첩 Scroll를 구현함
- 음원 리스트를 불러오고 RecyclerView를 활용해 리스트를 구현함
- 전체 스크롤과 동시에 음원 리스트의 RV의 중첩 스크롤 문제 해결을 위해 NestedScrollView를 선택함
- 구현 기간이 부족해 이를 선택하게 되었음
- NestedScrollView를 RecyclerView와 중첩해서 사용하면 RecyclerView의 재활용을 활용할 수 없다.
- 참고
- 이를 활용할 경우 RecyclerView에서 보여주어야 할 데이터를 미리 다 생성하고 메모리에 올려두기에 효율적이라 볼 수 없다.
- 아무래도 각 화면을 분할하고, MultiViewType 또는 ConcatAdapter를 활용해 해결할 필요가 있다.
Main - Album
- 상단 Top 5 앨범 뷰
- 코루틴을 활용하야 자동 스크롤 구현
- 왼쪽 하단 round clip
- 문제
- 일반 view를 왼쪽 하단에 Round Clip을 적용하고자 하면, background에 drawable를 적용하는 방법으로 할 수 있다.
- 하지만, image를 보여주게 되면, background는 이미지에 가려져 그 clip된 이미지를 볼 수 없다.
- 또는 다른 방식으로 CustomView를 만들어 왼쪽 하단을 round로 그리지 않는 방식을 채택할 수 있을 것이다.
- 이는 만드는 오버헤드가 매우 크다.
- 해결
- CardView를 활용했다.
- CardView를 매우 크게 적용하고, 내부 뷰를 device의 width를 받아와 적용한다.
- 문제
- Search Flow
- debounce를 활용해 입력 후 일정시간이 지났을 때 검색의 기능을 수행하도록 구현함
Main - Search
- Search Bar
- 검색 탭에 커서가 있을 때 최근 검색리스트를 LocalDB에서 불러온다.
- 커서가 나가게 되면 최근 검색 리스트는 숨겨진다.
- RoomDB Flow
- 검색어를 입력하면 DB에 데이터가 저장이 된다.
- 이때 이미 존재하는 검색어라면, 기존 데이터를 삭제하고 새로이 추가한다.
- Table에 저장되는 순서를 정렬하기 위해서임
- 변경된 테이블은 소비자에게 알려주어 이를 갱신하게끔 한다.
Main - Profile
- User Profile
- Edit과 Info를 보여주는 Fragment를 각각 만들어 버튼을 통해 fragment 전환으로 구성하였다.
- Info화면에서는 사용자의 프로필 이미지 및 자신의 작품 수들 등의 정보를 보여준다.
- Like
- 앨범 및 음악은 프로필 요약 화면에서 각각 3개, 5개를 보여주며, 전체보기를 눌렀을 때 전체 내용을 볼 수 있다.
- TabLayout과 ViewPager2를 이어 만들었음
- My
- UserInfo에서 자신의 작품 개수를 보여주며, 그 숫자를 클릭하면 자신의 작품들을 확인할 수 있다.
- TabLayout과 ViewPager2를 이어 만들었음
Register - Album
- Local Image Load
- 서버에 데이터 전송
Register - Music
- Local MP3 Load
- Album 선택 시
- 음원과 제목, 감정, 가사들을 서버에 전송함
- AI가 그려주는 앨범으로 등록 시
- 음원과 제목, 감정, 가사들을 서버에 전송함
- 서버에서 AI가 그린 표지 9장을 받아오고 이를 선택할 수 있다.
- 선택한 Image는 선택되었음을 확인하기 위해 미니플레이어의 표지가 되며, GridView Item의 크기가 확장됨
📈 성장 경험
이 프로젝트는 컴퓨터 종합 설계 수업에서 진행한 내용이며 지금까지 배운 내용들을 모두 쏟아붓는 것이 목표이다.
내가 안드로이드 개발을 1년 동안 수행하며 해왔던 내용들을 담아보고자 노력했으며, 잘 마무리했다고 생각한 프로젝트이다.
안드로이드 개발 목표로 삼은 내용을 토대로 이를 하나씩 털어보자.
Android Architecture
- 안드로이드 공식 문서와 샘플을 토대로 3개의 Layer를 모두 구현해보고자 했다.
- Domain Layer
- 다른 Layer는 필수인만큼 또 많이 작성해본 만큼 Data Layer와 View Layer는 빠르게 작성해나갔다.
- Domain Layer에 UseCase를 작성하면서 ViewModel이 Repository의 모든 내용을 가지고 가는 일을 줄이며, 필요한 내용만 제공하면서 객체지향 5원칙 중 인터페이스 분리 원칙을 잘 준수한 것 같다.
- MVVM
- DataBinding을 통해 view와 view model을 최대한 분리하고자 했지만, 실제 코드에서는 이를 충분히 만족하지는 못했던 것 같다.
- 특히 CustomView에서 문제들이 많이 발생했는데, CustomView에서 Attribute로 onClick에 대한 콜백 메소드를 넘겨주어 동작하게끔 만들고자 한 것에 문제들이 야기되었다.
- AttributeSet으로 데이터를 넘기는 것들은 문자열로 넘어가는듯 하여 이 문자열을 함수로 변경하는 내용을 만들었지만, 제대로 수행하지 않는 것이 문제이다.
- 이전에도 문제였지만, 이를 수정하는데 너무 많은 시간이 들었고 제출 기한까지 마무리를 해야 했기에 잠시 보류로 두고 마무리를 하게 되었다.
- 궁극적으로 100%를 만족할만한 코드라고는 하기 어려울 것 같다.
RoomDB
- LocalDB는 굳이 사용할 필요는 없었다.
- 다만, RoomDB의 Transaction 코드를 이용해보고자 또, RoomDB에서 제공하는 Flow를 이용해보고자 사용해본 것이다.
- 성공적으로 다 완성을 했으며, 만족스럽다.
Flow
- 아직 Flow의 많은 내용 중 일부만 익숙해 그 부분들을 이용했다.
- 가능하면, SharedFlow를 이용해 직접 구독자 수를 지정하는 등의 내용들도 작성해보고 싶었지만, 다른 이슈들이 많아 포기를 하게 되었다.
- 그렇기에 추후 리팩토링을 통해 구현해보고자 한다.
Music Play
- 음악 재생을 수행하기 위해 Background Thread를 새로 만들어서 이를 관리하고자 했었다.
- 하지만, 필요성을 느끼지 못했고 이를 사용하는 곳에서는 Coroutine으로 미리 Background로 처리를 하기에 첫 설계인 Thread를 빼는 작업을 하게 되었다.
- 그리고, PlayerState를 이를 사용하는 곳에서 직관적으로 알 수 있도록 알려주고 싶었다.
- 그래서 나는 LiveData를 MusicPlayer 클래스 내부에 선언을 하게 되었다.
- 관찰자 패턴으로 따로 구현해서 이를 알리도록 구현을 할까도 고민을 했지만, LiveData가 이미 잘 만들어져 있기에 사용을 하게 되었다.
- 하지만 LiveData 내용에 따르면, 일반적으로 ViewModel에서 사용되고, UI의 생명주기에 따르기 때문에 메모리 누수가 없다는 장점을 가진다.
- MusicPlayer라는 클래스는 뷰와 직접적인 상관관계는 없는데 과연 이를 내부에서 관리해도 될까라는 고민을 하게 되었고, 이 부분에 대해 고찰을 가질 필요성이 있다.
- 아직 결론을 내진 못했고, 다른 지인분들에게도 다소 던져 볼만한 질문일 것 같다.
Jetpack Navigation
- Jetpack Navigation을 실제로 사용해 프로젝트에 적용하는 것은 처음인 듯 하다.
- 그래서 다른 블로그와 안드로이드 공식문서를 최대한 참고를 해왔으며 이를 적용해 왔다.
- 실제 이 프로젝트를 하면서 느낀 점은 다음과 같다.
- Fragment Transaction을 굳이 관리할 필요가 없다.
- Fragment 변환 시 제공되는 기본 애니메이션이 너무나도 사용하기 좋다.
- Fragment끼리 데이터를 전달하기도 편리하며, Hilt와의 접목도 용이하다.
- Fragment 변환이 편리하다.
- 더 많은 장점이 있을 것이지만, 지금 생각나는 점은 이 정도인 것 같다.
- 하지만 이를 사용하면서 한계점들은 존재했다.
- 특히 Fragment Transaction을 관리하면서 이를 느꼈다.
- 메인 Fragment 이외에 2번째 Fragment를 갔다가, 3번째 Fragment로 갔다면 이전 Fragment를 기억하고 돌아가고 싶었다.
- 하지만 이를 관리하는 부분이 어려워 결국 직접 Fragment Transaction을 관리하게 되었다.
- 내가 이를 충분히 활용하지 못한 점도 있었지만, 자료를 찾지 못해 이 부분은 그대로 둔 상태이다.
- Jecpack Navigation의 편리함에 심취해 앞으로의 프로젝트에서도 많이 이용할 듯 하다.
📱 서비스 화면
시연영상
시작화면
메인 화면과 음악 재생 화면
AI가 그려주는 앨범으로 음악 등록 화면
앨범 화면과 앨범 추가 화면
앨범 선택 후 음악 등록 화면
프로필
추가
Blur 처리에 대한 아쉬움
- Figma를 참고하면 프로필 Edit상태일 땐 백그라운드가 Blur처리가 되고, 터치가 안되게끔 막으려고 했다.
- 단순히 뒷 화면을 음영주듯 블러처리도 쉬울 줄 알았다.
- 컴퓨터 그래픽스 수업을 들을 때 블러처리에 관해 공부했던 것이 스쳐지나가면서 이 구현이 쉬울까라는 생각이 들었다.
- 하나의 픽셀에서 주위 픽셀들에게 자신의 색상들을 전달하며 각자의 픽셀들과 결합해 일정 수준의 색상들을 표현해나가는 방식들이 과연 구현할 수 있을까
- 이는 현재 내 안드로이드 수준으로는 불가능하다는 것을 깨달았다.
- 그래서 외부 Library의 힘을 빌리게 되었지만, 해당 라이브러리 사용법을 익히지 못해 블러처리를 궁극적으로 구현하지 못하게 되었다.
- 이 부분도 다시한번 공부가 필요해 보인다.
Home Mini Player View
- Youtube Music Player에서처럼 음악 플레이 화면을 밑으로 내리게 되면 메인 화면 하단에 미니 플레이어가 만들어지는 것을 만드는 것이 목표였다.
- 그래서, BottomSheet로 Music Play 화면을 만든 것이었다.
- 하지만 접근조차 잘못되었었고 이를 바꾸고자 하고 있다.
- 현재 V1.0에서 이를 적용해 V2.0으로 배포하는 것이 목표이다.
Test Code 작성 (TDD 방식의 부재)
- 이번 목표가 현재까지 배운 안드로이드에 대해서 최대한 적용하는 것이었다.
- Test Code를 작성하는 것도 이 내용에 실은 포함되어 있었다.
- 이전에 Data Layer만 Unit Test Code를 작성해본 것이 전부이다.
- 이번엔 UI Test까지 모두 진행해보는 것이 목표였으나 시간부족으로 이를 포기하게 되었음에 매우 아쉬움을 느끼고 있다.
- 모든 화면을 구현하고 지금에와서야 다시 테스트 코드를 작성하는 것도 웃기기에 지금은 작성하지 않을 듯 하다.