📌 앱 소개: 맞춤형 콘텐츠 제공 및 오프라인 저장 기능을 갖춘 뉴스 앱
🕒 기간: 2021.03.15 ~ 2021.06.30 (3.5개월)
📱 플랫폼: 안드로이드 네이티브 앱
🏢 회사명: Desoft (쿠바 국영 소프트웨어 개발사)
👥 개발 인원: 1명
💼 역할: UI/UX 설계 및 전체 안드로이드 앱 개발 담당
🛠️ 사용 기술: Android Kotlin Coroutines MVVM Room Retrofit Moshi Navigation Material Design Glide Lottie ViewBinding
🔗 GitHub: daehan-lim/cubadebate-app
├── database/ # 로컬 데이터베이스 관련 클래스
│ ├── CubadebateDatabase.kt # Room 데이터베이스 메인 클래스
│ ├── converters/ # 데이터 타입 변환기
│ ├── dao/ # 데이터 액세스 객체
│ │ ├── PostDao.kt # 게시글 관련 데이터 액세스
│ │ ├── RecentCategoryDao.kt # 최근 카테고리 데이터 액세스
│ │ └── TagDao.kt # 태그 관련 데이터 액세스
│ └── model/ # 데이터베이스 엔티티 모델
│ ├── post/ # 게시글 관련 엔티티
│ │ ├── DatabasePost.kt # 게시글 메인 엔티티
│ │ ├── DatabaseCategory.kt # 카테고리 엔티티
│ │ └── ...(기타 게시글 관련 엔티티)
│ ├── savedpost/ # 저장된 게시글 관련 엔티티
│ │ ├── SavedPost.kt # 저장된 게시글 메인 엔티티
│ │ ├── SavedCategory.kt # 저장된 게시글 카테고리
│ │ └── ...(기타 저장된 게시글 관련 엔티티)
│ ├── ...(기타 엔티티들)
│
├── model/ # 데이터 모델 클래스
│ ├── api/ # API 응답 모델
│ │ ├── comment/ # 댓글 API 모델
│ │ │ ├── Content.kt # 댓글 내용
│ │ │ └── ResponseComment.kt # 댓글 응답 모델
│ │ ├── post/ # 게시글 API 모델
│ │ │ ├── NetworkPost.kt # 네트워크 게시글 모델
│ │ │ └── ...(기타 API 모델들)
│ ├── categories/ # 카테고리 관련 모델
│ │ └── MyCategoriesGridViewItem.kt
│ ├── comment/ # 댓글 도메인 모델
│ │ └── Comment.kt # 댓글 정보
│ └── post/ # 게시글 도메인 모델
│ ├── Post.kt # 게시글 메인 모델
│ ├── Category.kt # 카테고리 모델
│ └── ...(기타 게시글 관련 모델)
│
├── network/ # 네트워크 통신 관련 클래스
│ └── CubadebateApiService.kt # Retrofit API 서비스 인터페이스
│
├── repository/ # 데이터 저장소 (Repository 패턴)
│ ├── PostRepository.kt # 게시글 데이터 관리
│ ├── RecentCategoryRepository.kt # 최근 카테고리 데이터 관리
│ └── TagRepository.kt # 태그 데이터 관리
│
├── ui/ # 사용자 인터페이스 관련 클래스
│ ├── CoroutineBaseViewModel.kt # 코루틴 기반 베이스 뷰모델
│ ├── PostsViewModel.kt # 게시글 공통 뷰모델
│ ├── HeadingsAdapter.kt # 게시글 목록 어댑터
│ ├── EndlessRecyclerViewScrollListener.kt # 무한 스크롤 리스너
│ ├── main/ # 메인 화면 관련 클래스
│ │ ├── MainActivity.kt # 메인 액티비티
│ │ ├── MainActivityViewModel.kt # 메인 액티비티 뷰모델
│ ├── categories/ # 카테고리별 화면
│ │ ├── BaseCategoryFragment.kt # 카테고리 베이스 프래그먼트
│ │ ├── HomeFragment.kt # 홈 프래그먼트
│ ├── details/ # 게시글 상세 화면
│ ├── comments/ # 댓글 관련 화면
│ │ ├── CommentsActivity.kt # 댓글 액티비티
│ │ ├── CommentsFragment.kt # 댓글 프래그먼트
│ │ ├── RepliesFragment.kt # 댓글 답글 프래그먼트
│ │ └── ...(ViewModels 및 기타 클래스)
│ ├── search/ # 검색 관련 화면
│ ├── saved/ # 저장된 게시글 화면
│ ├── forme/ # 개인화 추천 화면
│ │ ├── ForMeFragment.kt # 추천 메인 프래그먼트
│ │ ├── MyCategoriesFragment.kt # 내 카테고리 프래그먼트
│ │ ├── MyTopicsFragment.kt # 내 토픽 프래그먼트
│ │ └── ...(ViewModels 및 기타 클래스)
│ │
│ ├── headingspertag/ # 태그별 게시글 화면
│ ├── subscription/ # 구독 관련 화면
│ └── settings/ # 설정 관련 화면
│ ├── categories/ # 카테고리 관리
│ └── topics/ # 토픽 관리
│
└── util/ # 유틸리티 클래스 및 헬퍼 함수
├── ActivityUtils.kt # 액티비티 관련 유틸리티
├── BindingUtils.kt # 데이터 바인딩 관련 유틸리티
├── MappingUtils.kt # 데이터 매핑 유틸리티
├── PostUtils.kt # 게시글 관련 유틸리티
├── PreferenceManager.kt # 설정 관리 유틸리티
└── Util.kt # 일반 유틸리티 함수
1. 오프라인 저장 아키텍처 선택
요구 사항
불안정한 네트워크 환경에서도 뉴스 기사를 오프라인으로도 안정적으로 저장하고 접근할 수 있어야 함
의사 결정
Room Database를 활용한 포괄적인 캐싱 시스템 구축을 결정
@Entity(tableName = "posts")
data class DatabasePost(
@PrimaryKey val id: Long,
val title: String,
val content: String,
val imageUrl: String?,
val publishedDate: String,
val isSaved: Boolean = false
)
2. MVVM 아키텍처 도입
요구 사항
복잡한 뉴스 데이터 흐름과 UI 상태를 체계적으로 관리하고, 네트워크와 로컬 데이터 소스를 효율적으로 통합해야 함
의사 결정
MVVM와 Repository 패턴을 결합한 계층형 아키텍처 구축을 결정
class PostRepository(private val database: CubadebateDatabase) {
suspend fun getPosts(categoryId: Long?): MutableList<Post> {
return withContext(Dispatchers.IO) {
try {
// 네트워크에서 최신 데이터 가져오기 시도
val networkPosts = when(categoryId) {
null -> CubadebateApi.retrofitService.getPostsAsync()
else -> CubadebateApi.retrofitService.getPostsByCategoryAsync(categoryId)
}.await()
// 로컬 DB에 저장 후 반환
networkPosts.map { it.mapToPost() }
} catch (e: Exception) {
// 네트워크 실패 시 로컬 데이터 반환
getPostsFromDb(categoryId)
}
}
}
}
1. 사용자 중심의 콘텐츠 탐색 개선
문제 상황
기존 웹사이트에서 사용자들이 관심 있는 주제의 뉴스를 자유롭게 찾아보거나 구독하는 것이 어려웠고 맞춤형 뉴스 소비가 사실상 불가능한 상황
RecyclerView와 Room Database를 활용하여 사용자가 선택한 주제들의 뉴스만 모아볼 수 있는 맞춤형 피드 시스템 개발