GitHub Search App
👀 Overview
한줄 소개 | 자신의 이슈와 알림을 받아볼 수 있으며, 키워드를 통해 깃헙 레포 검색을 할 수 있는 깃헙 앱 |
진행기간 | 2022.07.11 ~ 2022.07.22 |
Skills | Retrofit2, Glide, Koin, Flow, LiveData |
팀구성 | Android 2 |
❓ 개발 목표
해당 프로젝트는 아래의 내용을 증명하는 것을 목표로 함.
- 협업을 배우고 같이 프로젝트를 진행하는 과정을 같이 수행하며 이를 익힘
- 최대한 많은 기술들을 학습하고 적용하는 것
- 코드를 최대한 한 사람이 작성한 것처럼 보이도록 협업을 잘 하는 것
🍀 서비스 내용
자신의 깃허브 아이디를 통해 로그인을 할 수 있습니다. 로그인을 통해 사용자는 직접 자신의 이슈와 알림을 받아볼 수 있으며, 궁금할 시 타인의 저장소도 검색해볼 수 있습니다.
🛠 기술 스택
Retrofit2
- 서버로부터 데이터를 효율적으로 불러오기 위해 사용됨
Glide
- Glide vs Coil
- 참고
- Glide
- 반복되는 이미지가 많아서 캐시 데이터가 많이 필요할 때
- 원본 이미지 사이즈와 ImageView 사이즈 간의 편차가 클 때
- Warm Start가 자주 일어나는 환경일 때
- 순간적으로 많은 이미지가 로딩됐을 때, 메모리 사용량 편차가 크면 안 되는 상황일 때
- Coil
- 메모리 사용량에 여유가 있고, 다양한 이미지를 사용해 캐시가 동작하기 어려울 때
- Custom Image Transform을 사용하는 부분이 많을 때
- 우리는 Glide를 선택함
- Glide와 Coil의 실질적인 차이를 느끼지 못함
- 그래서 가장 대중적이며, 인기가 많았기에 이를
- Coil은 코틀린과 코루틴 기반이기에 향후 프로젝트에서 사용해보려 함
Koin
- Hilt vs Koin
- Hilt
- 런닝커브가 높음
- Dagger2에 기반한 라이브러리
- 컴파일 타임에 의존성 주입
- 보일러 플레이트 코드가 감소
- Koin
- 런닝커브가 낮음
- 런타임에 의존성 주입
- 의존 파악 어려움
- Hilt
- 우리는 Koin을 선택함
- 학습단계이기에 런닝커브가 낮은 Koin을 선택했고
- 이후 프로젝트에 Hilt를 사용하면서 이와 비교하고자 한다.
- 상세 내용은 다음과 같다.
Kotlin Flow & Live Data
- Flow vs LiveData
- Flow
- 비동기 처리에 효율적
- 안드로이드 플랫폼에 독립적
- LiveData
- UI와 데이터 상태의 일치 보장
- 메모리 누수가 없음
- 데이터 유지
- 리소스 공유
- Flow
- 두 개 모두 장점들이 확실했고, 이 둘을 모두 사용할 수 있기에 우리는 장점만을 뽑아서 쓸 수 있도록 모두 사용하기로 결정함
- 상세 내용은 다음과 같다.
Moshi
- gson vs. moshi vs. kotlin serialization
- gson
- Default Value에 대해 무시하고 0 or Null로 처리된다.
- kotlin serialization
- 변수에 프로퍼티를 포함하고 있지 않으면 null 대신 기본 값
- moshi
- 파싱 실패에 대한 더 나은 실패 메시지를 확인이 가능하다.
- gson
- 우리는 Moshi를 선택함
- 데이터가 적절히 처리가 되지 않았을 때 자세한 오류 메시지로 어디서 발생한 것인지를 더 잘 확인할 수 있음.
- 대중적인 Gson과 Kotlin 기반의 Kotlin Serialization도 매력이 있기에 이후 프로젝트에서 고려해볼만한 대상인 듯 하다.
🖥 개발 내용
Notification 화면
동기 처리와 비동기 처리
- 알림 화면에서 Github Notification API를 활용해 알림 내역들을 받아오되, 커멘트 갯수나 기타 정보들은 하나의 API에서만 있는 것이 아니기에 각 아이템에 대한 기타 정보를 얻기위해 2개의 API를 통해 요청을 했습니다.
- 이때 주로 신경썼던 부분은 리스트의 아이템에 대해서 비동기 처리와 각 아이템의 추가 정보를 받아올 땐 동기처리를 해 데이터를 받아오는 것에 최대한 효율적으로 동작하게끔 만들고자 노력했습니다.
- 이때 비동기 처리를 위해 코루틴을 활용했으며, 각 아이템에서 동기적으로 데이터를 받아오기 위해 Defered를 이용해 결과값을 받아오고 데이터를 파싱해 뷰로 전달하게 했습니다.
페이징 처리
- 페이징 처리를 위해 페이징 라이브러리가 아닌 페이징 관리 클래스를 따로 만들어서 페이징 처리를 구현할 수 있었습니다.
- RecyclerViewScrollMediator라는 RecyclerView와 끝에 도달했을 때 수행할 함수를 받아와 이를 관리하는 클래스를 만들었습니다.
- 이 클래스는 RecyclerView.OnScrollListener를 상속받아 RecyclerView가 끝에 도달했을 때를 감지하고, 그때 새로운 페이지를 호출하는 메소드를 수행합니다.
스와이프 후 캐싱처리
알림 아이템을 스와이프를 했을 때 결과를 즉시 수행하는 것에 문제가 있었습니다.
- 특정 위치의 아이템이 지워졌다면, 현재 사용자가 가지고 있는 리스트의 n개와 서버의 첫 번째 페이지의 n개의 데이터는 불일치 할 것입니다.
우리는 크게 3가지의 해결 방안을 고안했습니다.
- Swipe 후, 현재 페이지 전체 호출
- 현재 페이지의 마지막 데이터 호출
- 모션 처리 후, 삭제 데이터 캐싱 처리
위에서 1,2의 경우 api 요청이 1번씩 더 요청이 되기에 비효율적이라고 판단해 3번으로 결정했습니다.
- 그래서 스와이프 된 아이템은 viewModel에 캐시로 데이터를 저장해두고 있다가
- 해당 화면을 벗어나게 되면 백그라운드에서 삭제 처리를 수행합니다.
Profile 화면
- 이는 데이터 바인딩을 통해 화면을 구현하고 없는 데이터에 대해서 뷰를 보여지지 않도록 삼항 연산자를 이용해 개발했습니다.
📈 성장 경험
협업 경험
저는 이 프로젝트를 통해서 협업 경험치를 크게 얻을 수 있었습니다.
- 프로젝트 수행 과정
- 안드로이드 협업은 처음 겪었습니다. 그래서 어떻게 진행되는지 모르던 저를 팀원께서 이끌어주며 프로젝트의 흐름을 이해시켜주셨습니다.
- 네이밍 컨벤션 정의 -> 깃 브랜치 전략 결정 -> 요구 사항을 정의 -> 사용할 기술 스택 선정 -> 프로젝트 구조 설계 -> 역할 분담 및 프로젝트 시작 -> 일일 회고를 통한 서로의 피드백 -> 코드 머지 등
- 순서는 조금씩 변경될 수 있겠지만, 큰 흐름을 잡아준 것에 크게 성장할 수 있었습니다.
- 문서 작업
- 백로그나 깃 위키를 사용하는 방법 등을 배울 수 있었습니다.
- 백로그 작성을 통해 기능 세부사항들을 정의하고 각 세부사항들에 대해 어떤 기술이 들어갈지를 결정하는 과정을 배울 수 있었습니다.
- 또한, 큰 범주를 나누면서 역할 분담이 수월하게 이루어 지는 것을 통해 백로그 작성의 이점을 이해할 수 있었습니다.
- 깃 위키를 통해 해당 프로젝트에 관련된 문서 내용들을 손쉽게 작성하고 확인할 수 있음을 알았고,
- 각종 트러블 슈팅, 스프린트, 스프린트 회고 등을 작성하면서 프로젝트 진행 과정들을 한 눈에 확인하며 지나온 행적들을 볼 수 있음을 알 수 있었습니다.
- 깃 사용
- 이전엔 git add / git commit / git push밖에 사용할 줄 몰랐었습니다.
- 하지만 이를 통해 Issue의 사용, Pull Request의 사용 그리고 commit 메시지의 상세 내용 작성 등을 할 수 있게 되었으며,
- Github의 Milestone과 Project를 활용해 프로젝트 진행 상황들을 가시적으로 확인도 할 수 있었습니다.
- 깃 브랜치 전략에 최대한 따르도록 해 깃 사용법을 더욱 익힐 수 있었습니다.
- 일일 회고
- 하루가 끝나기 전 모두 모여 현재 어디까지 진행되었음을 이야기 할 수 있었고, 궁금했던 코드들은 이때 직접 물어볼 수 있었던 것이 좋았습니다.
- 필요시에 이때 머지를 하며 하루를 마무리하는 개운함을 맛 볼 수 있었습니다.
페이징 처리
Paging Library를 사용하지 않고 직접 구현을 할 때 상당한 귀찮음을 안고 진행했습니다.
Paging Library의 편리함을 깨달았고, 직접 구현했을 때 아쉬운 부분은 다음과 같습니다.
- 아이템을 처음 호출할 때 넉넉히 2배수 정도 더 불러오는 것
- RecyclerView가 끝에 도달하기 전 즉, 마지막 아이템의 1~2번째 앞에 도달했을 때 미리 호출
위의 기능 2개를 같이 만들었다면, 바닥에 도달했을 때 뚝뚝 끊기는 느낌의 앱이 아니라 자연스레 무한스크롤 되는 앱을 개발할 수 있을 것이라 생각됩니다.
동기 처리와 비동기 처리
두 개의 다른 api요청을 하기 위해 어떨 때는 비동기 처리를 또, 어떨 때는 동기 처리를 수행해 정확한 데이터를 만들어야 했습니다.
이번 과정을 통해 적절한 시기에 비동기/동기 처리를 수행해 사용자가 원하는 결과를 만들 수 있게 되었습니다.
딥링크
OAuth 로그인 후 딥링크의 콜백을 받으면 이전 화면으로 돌아가는 것이 아닌 새로운 Activity를 만들어 버그를 발생시켰습니다.
그래서 이를 위해 SingleTask를 이용하여 버그를 해결할 수 있었습니다.
의존성
의존성 주입이 필요한 이유와 하는 방법들을 이해하고 적용할 수 있었습니다.
플로우
아직 플로우에 대해 심층적인 이해를 했다고 이야기 하기는 힘듭니다.
하지만 모든 상황에 LiveData를 쓰던 전, LiveData가 과연 전체를 커버하는데 가장 효율적인 방법인가를 다시 한번 생각할 수 있던 계기였고, 플로우와 LiveData의 조합을 통해 더 효율적인 성능을 뽐낼 수 있음을 이해했습니다.