
ScreenNameViewer는 현재 앱에서 표시 중인 Activity / Fragment의 클래스 이름과 Compose Route를 화면 상단에 오버레이로 출력하는 디버깅용 라이브러리입니다.
회사 내에서 여러 개의 프로젝트를 담당하다보니 특정 화면이 어떤 파일에 있는지 찾기가 힘들었습니다.
이를 해결하기 위해 ScreenNameViewer라는 디버깅 오버레이 라이브러리를 만들었고, 이를 간단히 소개하고자 합니다.
전체 코드는 아래 링크에서 확인할 수 있습니다.
ScreenNameViewer
ScreenNameViewer For Compose
GitHub - DongLab-DevTools/ScreenNameViewer-For-Compose: Activity/Framgent 및 Compose Screen Route를 화면에 오버레이로
Activity/Framgent 및 Compose Screen Route를 화면에 오버레이로 표시해주는 디버그 라이브러리 - DongLab-DevTools/ScreenNameViewer-For-Compose
github.com
개발 동기
저는 현재 회사 내에서 4개 앱의 파트장으로써 관리 및 개발을 담당하고 있습니다.
여러 프로젝트를 동시에 다루면서, 지금 보고 있는 화면이 어떤 파일에 정의되어 있는지를 빠르게 파악하기 어려웠던 적이 많았습니다.
이 과정에서 현재 화면의 클래스 정보를 직접 오버레이로 노출하는 기능이 필요하다고 느꼈습니다.
개발을 시작하기에 앞서, 회사 내 다른 팀원들도 같은 불편을 겪고 있는지 먼저 확인했습니다.
그 결과, 다음과 같은 케이스에서 비슷한 문제를 겪고 있다는 점을 확인했습니다.
1. 신규 입사자 및 담당자 변경 시
회사에서는 신입을 대상으로 상시 채용을 진행하고 있으며, 6개월에서 1년 주기로 프로젝트 내 담당자 로테이션이 이루어지고 있습니다.
이러한 특성으로 인해, 프로젝트에 충분히 익숙하지 않은 신규 입사자나 새롭게 프로젝트를 맡게 된 담당자는 특정 화면이 어느 파일에 위치하는지 찾는 과정에서 어려움을 겪고 있었습니다.
2. 여러 프로젝트를 담당하는 광고팀
사내의 안드로이드 팀 내에는 광고팀이 별도로 존재합니다.
광고팀은 여러 프로젝트에서 광고 탑재가 필요한 지면에 한해서만 작업을 진행합니다.
즉, 각 프로젝트의 전체 구조를 모두 파악하고 있는 것이 아니라, 광고가 탑재된 일부 화면 위주로만 코드를 접하게 되는 구조입니다.
새로운 화면에 광고를 추가해야 하는 상황이 발생했을 때, 해당 화면이 어떤 파일에 정의되어 있는지 찾는 과정에서 상당한 시간이 소요되고 있다는 점을 확인했습니다.
3. Single Activity 구조의 Compose 앱
사내 프로젝트 중 Single Acitivity 구조와 Jetpack Compose로 개발된 앱이 있었습니다.
Compose와 Single Activity 구조를 함께 사용해 본 개발자라면 Activity가 아닌 Screen Composable만으로 구성된 앱의 화면을 역으로 추적하는 과정이 쉽지 않다는 점에 공감하실 수 있을 것 입니다.
저 또한 사이드 프로젝트에서 동일한 불편을 느껴왔기 때문에 개선이 필요하다고 생각이 들었습니다.
주요 기능 소개
자동 라이프사이클 추적
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initScreenNameViewer(this) {
/* Setting */
}
}
}
ScreenNameViewer는 Activity와 Fragment의 라이프사이클을 자동으로 추적하여 현재 활성화된 화면의 클래스 이름을 오버레이로 출력합니다.
따라서, 위와 같이 Application에서 initScreenNameViewer만 호출하면, 각 Activity나 Fragment에 별도 코드를 추가하지 않고도 프로젝트 전체에 기능이 적용될 수 있도록 설계했습니다.
ScreenNameTracker(navController) {
NavHost() { /*...*/ }
}
Compose도 동일하게 ScreenNameTracker 컴포저블로 NavHost()를 감싸주기만하면 됩니다.
활성화 조건 설정
initScreenNameViewer(this) {
settings {
debugModeCondition = BuildConfig.DEBUG
enableCondition = PreferenceManager
.getDefaultSharedPreferences(this@MyApplication)
.getBoolean("debug_overlay_enabled", true)
}
}
settings 블럭을 통해 라이브러리 활성화 조건을 주입할 수 있습니다.
- debugModeCondition
- 현재 빌드가 디버그 모드인지 여부를 판별하는 속성입니다.
- 라이브러리 내부적으로 디버그 모드일 때만 노출 되도록 처리 되어 있습니다.
- enableCondition
- 기능 활성화 여부를 판별하는 속성입니다.
- 디버그 모드이면서 해당 값이 true인 경우에만 기능이 활성화 됩니다.
- 이 속성을 통해 앱 내 메뉴에서 On/Off 형태로 설정 가능합니다.
UI 커스터마이징
initScreenNameViewer(this) {
/*...*/
config {
textStyle {
size = 12f // Text size
color = Color.WHITE // Text color
}
background {
color = Color.argb(128, 0, 0, 0) // Background color
padding = 16 // Padding
}
position {
topMargin = 64 // Top margin
activity = Gravity.TOP or Gravity.START // Activity display position
fragment = Gravity.TOP or Gravity.END // Fragment display position
composeRoute = Gravity.TOP or Gravity.END // Compose Route display position
}
}
}
ScreenNameViewer는 오버레이에 대한 커스터마이징도 제공합니다.
config 블록을 통해 다음과 같은 항목을 설정할 수 있습니다.
- 텍스트 스타일(textStyle)
- 텍스트 크기 (size)
- 텍스트 색상 (color)
- 배경 스타일(background)
- 배경 색상 (color)
- 패딩 (padding)
- 표시 위치(position)
- 오버레이 상단 마진 (topMargin)
- Activity / Fragment / Compose Route 각각의 표시 위치
위 예시와 같이 Dsl 스타일로 간결하게 원하는 스타일과 위치를 지정할 수 있습니다.
마치며
사내에 ScreenNameViewer를 도입한 이후 신규 입사자나 여러 프로젝트를 오가며 작업하는 팀에서도 화면 구조를 파악하는 데 드는 부담이 눈에 띄게 감소했습니다.
특히 여러 Fragment가 전환 되는 구조의 화면이나, Compose 환경에서의 화면 추적 과정이 훨씬 수월해졌습니다.
프로젝트 규모가 커지면서 "이 화면이 어디에 정의되어 있는지"를 자주 찾아보게 된다면
디버깅용 도구로 ScreenNameViewer를 한 번 적용해 보시는 것도 도움이 될 것이라고 생각합니다.