Skip to content

Team-Rocket-nbc-team2/CosmicDetox

Repository files navigation

Cosmic Detox: 스마트폰에서 벗어나볼까요?


🎮 앱 설치

Available on the Play Store



프로젝트 소개

현대인이라면 누구나 도파민 중독?

코스믹 디톡스와 함께 우주여행 하며 디지털 디톡스 해보세요!

휴대폰을 놓지 못하는 현대인들, 이제는 디지털도 다이어트를 해보는 건 어떨까요?

쉽지 않은 도전을 코스믹 디톡스가 도와줄게요.



🚀 Team Rocket

임가람 김보라 서정우 김찬휘 정용찬
@ImGaram @kkevi @seojw0124 @1chanhue1 @ggilggilmonster



⚒️ 기술 스택


범위 기술 이름
Architecture Clean Architecture
Design Pattern MVVM , Repository
DI Hilt
Async Task Coroutine , Flow
Firebase Authentication, Firestore, Functions, Storage
Local DB SharedPreferences
Component Service , Broadcast Receiver
UI Jetpack Navigation, XML
Image Glide



📺 화면 구성

로그인 화면 홈 화면
레이스 화면 마이페이지
타이머 화면



기능 소개

스플래시 및 로그인

스플래시 및 앱 사용 튜토리얼 로그인

우주 여행 행성 소개

타이머

타이머 및 오버레이 잠깐 쉬기

레이스

순위

마이페이지

허용 앱 설정 앱 사용시간 설정 환경설정



기술적 의사결정

❓ 허용 앱 리스트의 경우 왜 로컬 DB가 아닌 FireStore를 사용했나요?
  1. 다중 기기 사용 가능성 → 디톡스 앱 사용자 중에는 공부나 일에 집중하기 위해 사용하는 사람들이 많습니다. 특히 학생이나 공시생의 경우, 스마트폰과 함께 태블릿으로 인터넷 강의를 듣는 경우가 많습니다. → 여러 기기에서 앱을 사용할 수 있게 하려면 동일한 계정으로 로그인했을 때, 기기마다 동일한 허용 앱 리스트가 있어야 합니다. 이를 위해 로컬 DB 대신 Firestore를 사용하여 서버에서 데이터를 관리하는 방식을 선택했습니다.

  2. 시간 초기화 기능 → 자정 마다 허용 앱 사용 시간을 초기화하려면 각 앱에 지정된 제한 시간을 서버가 알고 있어야 하기 때문에, Firestore에서 데이터를 관리하는 것이 더 적합하다고 판단했습니다.

  3. 앱 삭제 시 데이터 초기화 → 사용자가 앱을 삭제하고 다시 설치하면 로컬 DB의 데이터는 삭제되지만, Firestore를 사용하면 기존 허용 앱 리스트가 서버에 저장되어 유지됩니다. 이를 통해 사용자는 앱을 다시 설치해도 동일한 허용 앱 리스트를 복원할 수 있어, 데이터를 안전하게 관리할 수 있습니다.

❓ 왜 Hilt를 사용했나요?
  1. Context의 효율적인 관리 → Hilt는 Context를 효율적으로 관리할 수 있습니다. 안드로이드의 PackageManager나 UsageStatsManager와 같은 시스템 서비스들은 Context를 필요로 하는데, Hilt를 사용해 Application의 Context를 전역적으로 주입함으로써 필요한 객체들을 간편하게 사용할 수 있습니다. 이를 통해 Presentation 레이어로부터 Context를 직접적으로 전달받지 않아도 됩니다.

  2. 외부 서비스(Firebase 등)의 전역 관리 → Firebase와 같은 외부 서비스들도 Hilt를 통해 주입되어 여러 컴포넌트에서 동일한 인스턴스를 활용할 수 있습니다. Firestore, Firebase Auth, Firebase Functions 등은 전역적으로 한 번만 생성하고, 앱 전체에서 사용할 수 있도록 효율적으로 관리할 수 있어 성능 최적화와 코드를 간결하게 했습니다.

  3. 코드 가독성 및 유지보수성 → Hilt를 사용해 @Provides, @Binds 어노테이션을 통해 필요한 객체들을 명시적으로 주입함으로써, Repository나 ViewModel과 같은 컴포넌트에 직접 의존성을 관리할 필요 없이 Hilt가 자동으로 관리하게 합니다.

❓ 왜 LiveData가 아닌 Flow를 사용했나요?
  1. Firestore DB 구조 설계 → 유저 정보는 users 컬렉션 내에 uid로 정의된 문서에 저장하고, 허용 앱 리스트는 리스트 타입 필드 대신 apps라는 서브 컬렉션으로 설계했습니다. 화면마다 필요한 데이터가 다르기 때문입니다. → 마이페이지에서는 유저의 전체 정보가 필요하므로, 유저 문서에서 한 번에 모든 정보를 불러와야 합니다. → 타이머 화면에서는 허용된 앱 리스트만 필요하기 때문에, 유저 문서의 모든 필드를 불러오는 것보다 apps 서브 컬렉션만 읽어오는 것이 더 낫다고 판단했습니다. 이를 통해 데이터 호출 시 필요한 리소스를 줄일 수 있고, 유지보수 측면에서도 허용 앱 리스트의 변경이 더 쉽습니다.

  2. 데이터 스트림의 용이성 → 유저 정보는 users 컬렉션의 문서에서 불러오고, 허용 앱 리스트는 apps 서브 컬렉션에서 불러오는 구조인데, Flow는 데이터 스트림을 통해 여러 소스로부터 데이터를 병합하고 관리할 수 있습니다. → 네트워크에서 데이터를 가져올 때, Flow의 콜드 스트림 특성 덕분에 데이터를 필요할 때만 가져올 수 있어 리소스를 효율적으로 관리할 수 있습니다. 예를 들어, Flow API의 zip 과 같은 오퍼레이터를 사용해 유저 정보와 허용 앱 리스트를 동시에 가져와 병합 처리할 수 있습니다.

❓ 자정이 되거나 핸드폰의 전원이 꺼질 경우 타이머를 어떻게 처리했나요?

1. AlarmManager와 Broadcast Receiver 사용

  • 해설 코드

    자정이 되거나 기기의 전원이 꺼졌다가 다시 켜질 경우, 타이머를 자동으로 초기화하거나 재설정하기 위해 AlarmManagerBroadcastReceiver를 사용했습니다.

    1. 자정에 타이머를 초기화하는 방법

    자정이 되면 타이머가 자동으로 초기화되도록 설정하기 위해 AlarmManager를 이용하여 자정에 호출되는 알람을 설정했습니다. 자정이 되면 MidnightResetReceiver가 호출되고, 해당 리시버는 타이머를 초기화하도록 설계했습니다.

    MidnightResetReceiver 동작 방식

    • 자정이 되면 타이머 초기화를 위한 서비스 (TimerService)를 호출하고, 타이머를 초기화합니다.
    • 또한 다음 자정을 위해 다시 알람을 설정합니다.
    class MidnightResetReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val resetIntent = Intent(context, TimerService::class.java).apply {
                action = TimerService.ACTION_RESET_TIMER
            }
            context?.startService(resetIntent)  // TimerService에 초기화 요청
    
            // 다음 자정을 위한 알람 재설정
            val app = context.applicationContext as CosmicDetoxApplication
            app.scheduleExactAlarm(context)  // 다음 자정을 위해 다시 알람 설정
        }
    }

    2. 핸드폰이 꺼졌다가 켜졌을 때의 처리

    기기가 재부팅되거나 전원이 꺼졌다가 다시 켜지면, AlarmManager에 설정된 알람들이 모두 초기화되므로, 이때 자정을 위한 알람을 다시 설정해야 합니다. 이를 처리하기 위해 BootCompletedReceiver를 사용했습니다.

    • BootCompletedReceiver 동작 방식:
      • 기기가 재부팅되면 ACTION_BOOT_COMPLETED 브로드캐스트를 수신하여, 다시 자정을 위한 알람을 재설정합니다.
    class BootCompletedReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
                val app = context.applicationContext as CosmicDetoxApplication
                app.scheduleExactAlarm(context)  // 재부팅 후 알람 재설정
            }
        }
    }
    

    3. 타이머 초기화 처리

    타이머 초기화는 TimerService 내에서 수행되며, 자정에 도달했을 때 또는 기기가 다시 부팅되었을 때 타이머가 정상적으로 초기화됩니다.

    • resetTimer() 메소드를 사용하여 현재 타이머 상태를 저장하고, dailyTime을 초기화한 후 타이머를 다시 시작합니다.
❓ 허용 앱 사용 시간을 리마인드하기 위해 알람을 어떻게 보냈나요?
  1. Foreground Service 사용 → 허용 앱을 사용 중이므로 현재 우리 앱은 활성화 되어있지 않고, 허용 앱 사용시간이 5분 내로 남았을 경우 알람을 보낼 수 있어야 했습니다. 우리 앱은 백그라운드 상태에서 꾸준히 실행되고 있는 상태이기 때문에 Foreground Service를 사용하여 알람을 전송할 수 있도록 하였습니다.
❓ 왜 Firebase Functions를 사용했나요?
  1. Custom Token 사용을 위해서 → kakao sign in api를 이용하고 나면 전용 token이 발급되는데, 이 token을 firebase authentication에 바로 사용할 수 없고 custom token을 만들어야 합니다. Custom Token을 만드는 데에 서버가 필요하여 Firebase Functions을 사용해서 kakao sign in api와 custom token 제작을 동시에 진행하고, android에서 custom token을 받아올 수 있도록 해주었습니다.

About

cosmic detox, 우주가 위험해요!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages