프로그래밍/Android

[Compose] 빨간 점 시스템 만들기

Lou Park 2024. 10. 20. 16:29

https://www.reddit.com/r/facebook/comments/1emt6v4/messenger_on_android_has_a_red_dot_in_the_3_bars/

 

UI에 작게 보이는 빨간 점은 유저에게 이것을 따라 가보라는 작은 넛지를 준다. 카카오톡에서도 내 친구들이 프로필을 업데이트했을때의 빨간 점을 못이기고 클릭했던 경험도 있을것이다.  빨간 점 시스템을 가장 잘 만들고 사용하고있는 쪽이 어딜까? 바로 게임이다! 

원신 UI

 

게임덕후이자 개발자로서 평소에 게임을 하면서도 이건 어떻게 구현했을까, 상태관리 어떻게 하는걸까 감탄/고민을 자주한다. 요즘은 원신을 정말 재미있게 즐기고있는데, 원신은 유저가 빨간 점을 누르면 "원석"(게임 내 중요 재화)이 생긴다는걸 정말 잘 훈련시켜서 은근슬쩍 원신에서 일어나는 모든 이벤트들을 선전한다. 

 

이런 빨간 점 시스템을 안드로이드에서 구현해보기위해, 내가 게임 개발자다...생각하고 자료를 찾아보았다.  유니티 에셋스토어에 올라온 Unity Red Dot Notify System과 레딧 포스트가 특히 도움되었다. 빨간 점 시스템에서 다루는 데이터는 트리처럼 계층적 구조로 되어있고, 이벤트는 노드의 알림 수에 변화가 생겼을 경우 이를 전파하는 형태다. 그리고 이 알림을 숫자로 보이게 할 것인가, 그냥 느낌표로 띄울 것인가는 별도의 UI 레이어에서 처리한다. 

 

원신을 예시로 구현해보기

앞에서 살짝 원신에 대해서 얘기했으니, 원신을 예시로 빨간 점 시스템을 안드로이드에서 직접 만들어보도록하자. 일반적인 게임들처럼 퀘스트 메뉴에는 메인 퀘스트, 일일 퀘스트가 있다. 메인 퀘스트에는 "페이몬을 도와줘!"라는 퀘스트가 새로 떠서 진행중이라고 해보자.

 

다음의 계층구조가 그려지게 된다.

. 
└── 퀘스트 
    ├── 메인 퀘스트 
    │ └── 페이몬을 도와줘! 
    └── 일일 퀘스트

 

이런 계층적 관계를 표현하기 위해서 `DotNode`라는 인터페이스를 준비했다. Node는 다른 `DotNode`들을 자식으로 가질 수 있고, `LeafNode`는 자식이 없다. `match`는 나중에 사용할것이지만, 어떤 `LeafNode`가 자신 또는 자식이 맞는지 체크하는 함수다.

 

 

그러면 원신의 빨간점 시스템을 방금 정의한 `DotNode`로 표현해보자. 

이렇게 선언해두면 다음에 `HelpPaimon`에 대한 빨간 점을 조작하고 싶을때 `GenshinRedDot.Quest.MainQuest.HelpPaimon`로 접근해서 쓸 수 있다. 

object GenshinRedDot {  
    data object Quest : DotNode.Node(  
        MainQuest,  
        DailyQuest  
    ) {  
        data object MainQuest : DotNode.Node(  
            HelpPaimon  
        ) {  
            data object HelpPaimon : DotNode.LeafNode  
        }  
        data object DailyQuest : DotNode.LeafNode  
    }  
}

 

 

다음은 `DotNode`들을 관리하고, 업데이트를 처리하는 `DotSystem`의 구현이다.

 

빨간 점의 숫자는 `LeafNode`만 가질 수 있도록 설계되었다. 일반 노드는 자식들의 합으로만 빨간 점 숫자를 표현가능하다. 숫자를 읽는 것은 어느 노드든 가능하지만, 숫자를 할당하는 것은 `LeafNode`로만 가능하다. 그리고 어떤 `LeafNode`든 업데이트가 일어날 경우 모든 리스너들에게 이 변화를 알린다.

유저가 "페이몬을 도와줘" 퀘스트를 완료할 수 있을 경우, 이 값을 1로 만들어주면 될 것이다.

DotSystem.setCount(GenshinRedDot.Quest.MainQuest.HelpPaimon, 1)

 

이것으로 빨간 점 시스템은 완성되었다. 
이제 이것을 어떻게 표현하면 좋을지 Compose 함수를 작성해보자.

핵심은 `OnDotChangedEffect`다. 

`DisposableEffect`를 이용해 컴포저블이 떨어졌을때 리스너도 깔끔하게 제거될 수 있도록 만들었다. 어떤 `LeafNode`의 숫자에 대한 업데이트가 일어날때, 내가 관찰해야하는 변화인지 `match` 함수를 통해 체크하고, 새로운 `count` 변화를 상위 컴포저블에게 알려준다.

 

이제 이 모든 코드를 합쳐서 Preview로 직접 확인해보자.

 

Daily Quest의 [+] 버튼을 눌러 3으로 만들면 상위 계층인 Group: Quest에도 해당 숫자가 반영된다.

유저가 "페이몬을 도와줘" 퀘스트를 완료할 수 있는 상태가 됨을 가정하고 HelpPaimon 옆의 [+] 버튼을 눌러서 숫자를 올려보면 상위 계층인 MainQuest와 Quest각각에 일어난 변화를 관찰 할 수 있다.