Kotlin, Room, Firebase를 활용한 Todo & Memo Android 애플리케이션
Todo와 Memo를 한 앱에서 관리할 수 있는 Android 애플리케이션입니다. XML 기반 UI와 ViewModel·StateFlow로 화면 상태를 관리하며, Room을 로컬 저장소로 사용합니다. Firebase Authentication으로 사용자를 인증하고 Cloud Firestore의 사용자별 컬렉션과 로컬 데이터를 동기화합니다.
- Email/Password 기반 회원가입 및 로그인
- Todo 생성·수정·삭제와 완료 상태 관리
- Todo 날짜 지정 및 날짜 제거
- 전체 Todo 완료율 표시
- Calendar에서 마감일 표시 및 날짜별 Todo 조회
- AlarmManager 기반 Todo 일정 알림
- Android 13 이상 알림 Runtime Permission 처리
- Firebase Cloud Messaging 수신 알림
- Memo 생성·수정·삭제
- Room 로컬 저장과 Firestore 원격 저장
- Firestore Snapshot Listener를 이용한 Remote → Local 동기화
- 사용자 선택 색상 테마 저장 및 적용
- Kotlin
- JDK 17 / JVM target 17
- Android SDK (minSdk 24, targetSdk 35, compileSdk 35)
- XML Layout
- ViewBinding / DataBinding
- Activity / Fragment
- ViewModel
- StateFlow
- Navigation Component
- Kotlin Coroutines
- Kizitonwose CalendarView
- Room
- Firebase Authentication
- Cloud Firestore
- Firebase Cloud Messaging
- AlarmManager
- BroadcastReceiver
- NotificationCompat
과도한 계층 분리를 적용하지 않고, 화면에서 ViewModel과 Repository를 거쳐 Room 및 Firebase에 접근하는 단순한 구조를 사용합니다.
flowchart TD
UI[Activity / Fragment] --> VM[ViewModel + StateFlow]
VM --> REPO[Repository]
REPO --> ROOM[Room Database]
REPO --> FIRESTORE[Cloud Firestore]
FIRESTORE --> SYNC[Snapshot Sync Manager]
SYNC --> ROOM
REPO --> ALARM[AlarmManager]
FCM[Firebase Cloud Messaging] --> NOTIFICATION[Android Notification]
app/src/main/java/com/example/todolist
├── data
│ ├── firebase
│ │ ├── FirebaseAuthDataSource.kt
│ │ └── UserDataSource.kt
│ ├── local
│ │ ├── dao
│ │ │ ├── MemoDao.kt
│ │ │ └── TodoDao.kt
│ │ ├── database
│ │ │ └── AppDatabase.kt
│ │ └── entity
│ │ ├── MemoEntity.kt
│ │ └── TodoEntity.kt
│ ├── repository
│ │ ├── AuthRepository.kt
│ │ ├── MemoRepository.kt
│ │ └── TodoRepository.kt
│ └── sync
│ ├── MemoSyncManager.kt
│ └── TodoSyncManager.kt
├── notification
│ ├── MyFirebaseMessagingService.kt
│ ├── NotificationScheduler.kt
│ └── TodoAlarmReceiver.kt
├── presentation
│ ├── calendar
│ ├── home
│ ├── intro
│ ├── login
│ ├── memo
│ └── signup
├── util
│ └── DateTimeUtils.kt
└── MainActivity.kt
Todo와 Memo 쓰기 작업은 현재 동기화 모델에 맞춰 원격 저장 성공 후 Room에 반영합니다.
User Action
→ ViewModel
→ Repository
→ Cloud Firestore
→ Room
→ Flow / StateFlow
→ UI
Firestore 변경은 사용자별 todos, memos 컬렉션의 Snapshot Listener가 수신합니다. ID 기준으로 정렬한 원격·로컬 목록이 다를 때만 Room 데이터를 Transaction으로 교체합니다. Fragment의 View가 제거되면 Listener와 내부 CoroutineScope도 함께 해제합니다.
Room은 UI가 관찰하는 로컬 데이터 소스이며, Firestore는 사용자별 원격 기준 데이터로 사용합니다. Todo와 Memo 쓰기는 Firestore → Room 순서로 통일해 네트워크 실패 시 로컬에만 데이터가 남는 상태를 줄였습니다. 완전한 Offline-first 또는 충돌 해결 엔진은 적용하지 않았습니다.
ViewModel이 Room의 Flow를 수집해 StateFlow로 노출합니다. Fragment는 View Lifecycle이 STARTED 이상인 동안만 상태를 수집하며, 저장 실패는 앱을 종료시키지 않고 사용자 메시지로 표시합니다.
Todo에 미래 날짜가 있을 때 Todo ID별 PendingIntent로 알람을 예약합니다. 날짜 수정 시 기존 알람을 취소하고 다시 예약하며, 날짜 제거·Todo 완료·삭제 시 알람을 취소합니다. Android 12 이상에서 Exact Alarm 권한이 없으면 앱 사용을 막거나 설정 화면을 강제로 열지 않고 inexact alarm으로 대체합니다. Android 13 이상에서는 알림 권한을 Runtime에 요청하고, 거절된 경우 알림만 생략합니다.
현재 UI는 날짜만 입력하므로 알림 시각은 선택한 날짜의 로컬 시간 00:00입니다. 이미 지난 시각과 날짜가 없는 Todo에는 새 알람을 예약하지 않습니다.
실제 google-services.json은 저장소에 포함하지 않습니다.
-
Firebase Console에서 프로젝트를 생성합니다.
-
Android 앱을 등록하고 package name에
com.example.todolist를 입력합니다. -
Authentication에서 Email/Password 로그인을 활성화합니다.
-
Cloud Firestore Database를 생성하고 사용자별 데이터 접근에 맞는 Security Rules를 설정합니다.
-
Firebase Cloud Messaging을 사용할 프로젝트 설정을 확인합니다.
-
Firebase Console에서
google-services.json을 다운로드합니다. -
파일을 아래 경로에 배치합니다.
app/google-services.json -
Android Studio에서 Gradle Sync를 실행합니다.
-
앱을 빌드하고 실행합니다.
Firebase 설정 파일이나 API key를 소스 코드에 직접 작성하지 마세요.
- Android Studio 또는 Android SDK 35
- JDK 17
- Firebase 프로젝트의
app/google-services.json
Android Studio에서 프로젝트 루트를 열고 Gradle Sync 후 API 24 이상의 Emulator 또는 기기에서 app 구성을 실행합니다.
macOS / Linux:
./gradlew clean test lint assembleDebugWindows PowerShell:
.\gradlew.bat clean test lint assembleDebugDateTimeUtilsTest에서 다음 순수 Kotlin 날짜 로직을 확인합니다.
- 미래 알림 시각 허용
- 날짜 없음·현재·과거 시각 거부
- 동일 날짜 비교
앱에는 아래 글꼴 파일이 포함되어 있습니다.
| 파일 | 식별된 글꼴 | 공개 전 확인 |
|---|---|---|
pretendardbold.ttf, pretendardlight.otf |
Pretendard | 공식 저장소의 SIL Open Font License 1.1 및 고지 포함 조건 확인 |
maruburisemibold.ttf |
NAVER MaruBuri | NAVER 나눔글꼴 라이선스의 재배포·고지 조건 확인 |
bokkmyungjobold.ttf |
BOOKK Myungjo | BOOKK 공식 배포 페이지의 최신 원문 기준으로 앱 번들·재배포 허용 범위 재확인 필요 |
Pretendard와 MaruBuri는 공식 페이지에서 공개 라이선스를 확인할 수 있지만, 저장소에 필요한 고지 파일을 어떤 형태로 포함할지는 공개 전에 결정해야 합니다. BOOKK Myungjo는 파일을 삭제하지 않고 유지했으며, GitHub에 폰트 파일 자체를 배포하기 전 원 배포처의 최신 조건을 직접 확인해야 합니다.
아이콘과 이미지 파일도 직접 제작한 자산인지 또는 재배포 가능한 출처인지 공개 전에 확인해야 합니다.
- 원격 변경을 로컬 전체 교체 방식으로 반영하며 복잡한 동기화 충돌 해결 정책은 없습니다.
- Firestore 쓰기 성공 후 Room 쓰기가 실패한 드문 경우에는 다음 Snapshot 동기화가 로컬 상태를 복구하는 구조입니다.
- 네트워크가 없는 상태에서 쓰기를 보장하는 Outbox·WorkManager 기반 Offline-first 구조는 아닙니다.
- Room schema 변경 시 현재는
fallbackToDestructiveMigration()을 사용하므로, 실제 배포 전 명시적 Migration 작성이 필요합니다. - Todo 입력 UI는 날짜만 지원하며 별도의 시·분 선택은 제공하지 않습니다.
- Unit Test는 날짜·알림 검증과 같은 순수 로직 중심으로 제한되어 있습니다.
- 프로젝트 자체 오픈소스 LICENSE는 아직 선택하지 않았습니다.
app/google-services.json이 Git에 포함되지 않았는지 확인local.properties,.idea/,.gradle/,.kotlin/,build/가 제외됐는지 확인- Firebase Authentication·Firestore Security Rules를 실제 계정 구조에 맞게 점검
- 포함된 font/image asset의 원본 출처와 재배포 조건 확인
- 프로젝트에 적용할 LICENSE를 직접 선택