프로젝트(나모)의 팀 블로그에서 작성한 내용을 가지고 왔습니다.
🔗 원본 링크: https://namo-log.vercel.app/android-category-color
이번에 리팩토링을 마친 기념으로 나모의 카테고리에 대해서 다뤄보려고 합니다.
그 중에서도 카테고리 색상을 저장하고 표시하는 부분을 크게 바꾸게 되었는데요,
오늘은 색상을 중심으로 왜 저장 방법을 바꾸게 되었는지, 그 이유와 바뀐 방법을 소개해보겠습니다.
흔히 아는 캘린더 앱을 생각해 보면, 캘린더에 일정을 표시할 때 색상으로 구분하는 경우가 많습니다.
나모의 카테고리도 색상으로 일정/기록 구별하기 위해 쓰입니다.
카테고리의 색상을 선택할 때는 팔레트 내의 색상을 선택하는 것이기 때문에, 팔레트의 개념에 대해서도 한 번 짚고 넘어가야 할 것 같은데요.
팔레트는 색상의 소속이라고 봐 주시면 됩니다!
’팔레트가 색상들의 집합으로 이루어져 있다’로도 말할 수 있을 것 같아요.
현재 나모에는 팔레트가 기본 팔레트 하나밖에 없지만, 후에 서비스의 규모를 늘려나가면서 다양한 색상들로 구성된 팔레트를 추가할 계획입니다 😊
나모에서 카테고리의 활용처는 꽤나 많습니다.
앱 화면을 통해 카테고리 색상이 어디에 쓰이는지 간단히 확인해 보겠습니다.
일단, 카테고리 그 자체입니다.
카테고리 설정 (리스트) | 카테고리 |
---|
한 눈에 봐도 여러 색이 눈에 띕니다.
다른 기능들과 연관이 되는, 카테고리 그 자체에 우선 색상 정보가 필요합니다.
다음은 일정과 기록 부분입니다.
캘린더 일정 표시 | 클릭한 날짜의 일정 조회 | 기록 조회 |
---|
캘린더에 일정을 표시할 때도, 클릭한 날짜의 일정을 조회할 때도 색상을 표시해주는 부분이 있습니다.
기록도 일정에 대해 남기는 것이기 때문에 일정의 색상(=카테고리 색상)을 그대로 사용해서 UI에 표시해 줍니다.
나모에서 색이 구별되는 곳은 모두 카테고리 색상이 쓰인다고 생각해도 될 만큼,
위의 예시처럼 많은 화면에서 카테고리 색상을 필요로 하게 됩니다.
때문에 색상을 앱 내부에서 색상을 사용하기 위해서는 색상을 저장할 방법이 필요했습니다.
이전에는 색상을 Int 형식으로 저장하고, 사용하고 있었습니다.
<!-- Color Array -->
<array name="categoryColorArr">
<!-- 기본 색상 -->
<item>@color/schedule</item>
<item>@color/schedule_plan</item>
<item>@color/schedule_parttime</item>
<item>@color/schedule_group</item>
<!-- 기본 팔레트 -->
<item>@color/palette1</item>
<item>@color/palette2</item>
<item>@color/palette3</item>
<item>@color/palette4</item>
<item>@color/palette5</item>
<item>@color/palette6</item>
<item>@color/palette7</item>
<item>@color/palette8</item>
<item>@color/palette9</item>
<item>@color/palette10</item>
</array>
@Entity(tableName="category_table")
data class Category(
@PrimaryKey(autoGenerate = true) val categoryId: Long = 0,
var name : String,
var color : Int = 0,
var share : Boolean = false,
}
기본적으로 colors.xml에 저장된 카테고리 색상을 color에 저장합니다.
categoryColorArray = resources.getIntArray(R.array.categoryColorArr)
val startIndex = 4
val endIndex = 13
val paletteDatas = categoryColorArray.filterIndexed { index, _ -> index in startIndex..endIndex }.toIntArray()
→ 팔레트 내에 있는 색상을 표시해줄 때 사용할 수 있습니다.
categoryColorView.background.setTint(category.color)
기존 방법으로도 색상을 표시하는 것 자체에는 큰 문제가 없었지만,
저장 방법을 바꿔야겠다고 생각한 큰 이유들이 있었습니다.
paletteId
: 색상 번호 (나모에서 사용하는 색상에 번호를 붙인 것)
때문에 앱에서 선택한 색상을 paletteId로 변환하는 과정이나, 서버에서 받아온 paletteId를 색으로 변환하는 과정이 조금 복잡했습니다. 하드코딩 되었던 부분도 조금 있었구요.
룸디비에 저장된 카테고리 색상을 확인하기 어려웠습니다.
⬆️ 룸디비에 저장된 카테고리 데이터를 확인한 모습
이처럼 color 필드의 값이 -1270981
식으로 들어가서 어떤 색인지 한 눈에 파악하기가 힘들었습니다.
앱에 처음 진입했을 때 캘린더 일정의 색상이 간혹 제대로 표시되지 않는 이슈가 있었습니다.
서버에서 받아온 일정의 색상을 앱 내부 카테고리 색상과 매치하는 과정에서 매칭되지 않는 카테고리의 일정은 색이 제대로 표시되지 않았습니다.
팔레트가 추가되었을 때의 대응이 어려움
지금은 팔레트가 기본 팔레트 하나밖에 없어서 괜찮지만, 팔레트가 후에 더 추가되면 로직이 많이 복잡해질 것 같았습니다.
처음 나모 개발을 시작했을 때는 색상을 어떻게 저장하는 게 좋을까, 하다가 colorArray로 저장해서 사용하기로 했었습니다. 색상 표시 문제로 초반에 많이 애를 먹었던 기억이 납니다.
그러나 후에 API 통신을 진행하고, 유지보수를 바라보다 보니 위와 같은 문제점들이 보여 카테고리 색상 저장 방법을 바꿀 필요성을 느꼈습니다.
‘서버DB와 데이터를 통일하고, 색상 확인과 사용을 더 간결하게 하자!’가 이번 리팩토링의 목표였습니다.
💡 [크게 변화한 부분]
1. 색상 자체를 int → String 형식으로 관리하게 됨
ex) R.color.namo (int) → “#DA6022” (String)
2. Category 클래스에 색상 정보를 color → paletteId 형태로 저장하도록 함
ex) -1270981 (color) → 4 (paletteId)
enum class PaletteType { DEFAULT_4, BASIC_PALETTE }
나모에서는 카테고리 색상을 선택할 때 기본 색상 4개(DEFAULT_4)를 제공하고, 팔레트 내의 색상도 선택할 수 있습니다. 팔레트는 현재 기본 팔레트(BASIC_PALETTE) 하나만 존재합니다.
후에 팔레트 구성이 더 증가되면 PaletteType을 하나씩 늘려가면 됩니다.
/** 내부 색상 저장 용도 */
enum class CategoryColor(val paletteType: PaletteType, val paletteId: Int, val hexColor: String) {
/** 기본 색상 */
SCHEDULE(PaletteType.DEFAULT_4, 1, "#DE8989"),
YELLOW(PaletteType.DEFAULT_4, 2, "#E1B000"),
BLUE(PaletteType.DEFAULT_4, 3, "#5C8596"),
MOIM(PaletteType.DEFAULT_4, 4, "#DA6022"),
/** 기본 팔레트 */
DEFAULT_PALETTE_COLOR1(PaletteType.BASIC_PALETTE, 5, "#EB5353"),
DEFAULT_PALETTE_COLOR2(PaletteType.BASIC_PALETTE, 6, "#EC9B3B"),
... (생략) ...
DEFAULT_PALETTE_COLOR10(PaletteType.BASIC_PALETTE, 14, "#858585");
}
소속을 구별하기 쉽게 PaletteType을 넣었고,
서버와는 paletteId를 통해 통신하기에 색상 번호인 paletteId도 추가했습니다.
색상 자체는 hex 코드 형태로 String으로 저장했습니다.
enum class CategoryColor(val paletteType: PaletteType, val paletteId: Int, val hexColor: String) {
companion object {
// enum class의 모든 hexColor 반환
fun getAllColors(): ArrayList<String> {
return values().map { it.hexColor } as ArrayList<String>
}
// PaletteType에 해당하는 리스트 반환
fun findPaletteByPaletteType(paletteType: PaletteType): ArrayList<CategoryColor> {
return values().filter { it.paletteType == paletteType } as ArrayList<CategoryColor>
}
// 팔레트의 hexColor만 반환하기
fun findColorsByPaletteType(paletteType: PaletteType): ArrayList<String> {
return findPaletteByPaletteType(paletteType).map { it.hexColor } as ArrayList<String>
}
// paletteId로 CategoryColor 찾기
fun findCategoryColorByPaletteId(paletteId: Int): CategoryColor { //
return values().find { it.paletteId == paletteId } ?: SCHEDULE // 못 찾으면 기본 색상으로
}
// ...
}
}
PaletteType를 인자로 넘기면 그 팔레트의 색상을 반환해 주거나, 저장된 paletteId로 색을 찾는다던가, 하는 프로젝트에서 전체적으로 쓰일 수 있는 메서드를 구현할 수 있었습니다.
// 기본 팔레트
val paletteDatas = CategoryColor.findPaletteByPaletteType(PaletteType.BASIC_PALETTE)
```kotlin
categoryColorView.backgroundTintList = CategoryColor.convertPaletteIdToColorStateList(it.paletteId)
```
여기저기에서 중복되게 쓰이는 코드들도 간결해졌고,
서버와 똑같은 저장 방식을 사용하여 통신도 더 편해졌습니다!
룸디비에 저장되는 데이터를 확인해 볼 때도,
위처럼 카테고리의 색상이 번호(paletteId)로 표시되어서 훨씬 확인하기가 쉬워졌습니다.
여러모로 바꾸기 잘 했다는 생각이 드는 지점이었습니다.
현재는 카테고리 색상을 모두 hex 코드의 String 형태로 저장하고 있지만,
Int 형식일 때와 성능을 비교해 내부 색상 저장 타입을 결정할 계획입니다!
아직까지 프로젝트를 진행하면서 성능 분석을 한 번도 해본 적이 없어서 데이터 타입의 변화만으로 성능에 어떤 결과를 낼 수 있을까가 궁금합니다ㅎㅎ
그럼 카테고리에 관해서는 다음번에 성능 분석 결과를 들고 다시 찾아오도록 하겠습니다.
감사합니다!
제가 작성한 다른 글들을 통해 확인해 보실 수 있습니다.