📌 앱 소개: 맞춤형 콘텐츠 제공 및 오프라인 저장 기능을 갖춘 뉴스 앱
🕒 기간: 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
를 활용하여 사용자가 선택한 주제들의 뉴스만 모아볼 수 있는 맞춤형 피드 시스템 개발