주차마다 리더보드가 갱신되는 게임을 개발하는 과정에서 생긴 버그에 대한 내용이다.
월~일이 하나의 주차로 보고, 월요일 오전 12시가 될 때마다 리더보드가 리셋되어야 한다.
하 지 만!!!!
11월 30일(화) -> 12월 1일(수)이 되는 날...
프론트 개발자분에게 연락이 왔다..
제이슨: 젠희.. 리더보드가 이상해요....
젠희(나): 녜,...,?
정말 심쿵 그자체였다. 리더보드가 갑자기 리셋이 되어버린 이 상황 말이다..
나는 리더보드 Id를 yyyy_m_n
(yyyy년 m월 n주차) 이런식으로 저장했다.
그래서 화요일 리더보드는 2021_11_5
였고, 12월 1일이 된 수요일의 리더보드 Id는 2021_12_1
이 된 것이다.10월부터 게임을 배포한 상황에 계속해서 개선을 하고 있었는데 왜 이제야 알았냐면..허허 10월 31일에서 11월 1일이 되는 날이 딱 월!요일이어서 이때는 문제가 발생하지 않았다. 이때 같은 문제가 생겼더라면.... 일찍 알아챘을텐데 말이닷 ^^.!
일단 이걸 어떻게 일시적으로 해결했느냐!
나는 LeaderboardIdGenerator
라는 이름으로 leaderboardId를 생성하는 역할을 하는 클래스로 따로 분리했었다.
그래서 오늘 날짜의 리더보드, 몇월 몇주차의 리더보드, 몇 주전 리더보드 Id 만드는 것을 모두 이 클래스에서 관리했다.
class LeaderboardIdGenerator {
companion object {
fun generate(): String {
// ...
return if (leaderboardId == "2021_12_1") "2021_11_5" else leaderboardId
}
fun generateBeforeWeek(week: Int): String {
// ...
return if (leaderboardId == "2021_12_1") "2021_11_5" else leaderboardId
}
fun generate(year: Int?, month: Int?, week: Int?): String {
// ...
return if (leaderboardId == "2021_12_1") "2021_11_5" else leaderboardId
}
}
}
휴우 그래서 이 4개의 메서드에서2021_12_1
일 때 2021_11_5
이렇게 일시적으로 고쳤다 ㅋㅋㅋ
진짜 리더보드 Id를 생성하는 역할만 하는 클래스로 만들어서 너무 다행이었다 정말!! ...
그래서 버그를 알아챈 5분동안 2021_12_1
로 저장된 데이터를 한 1시간 동안 수동으로 옮겼다.. 유저분들 너무 죄송해요.. (전국 풍피 돌리기 전에 알아채서 넘 다행)
쨋든 여기까지 일시적 해결기!
이제 정말 이 버그를 고쳐보도록 하자.
일단 처음에 설계를 할 때 멘토님이 해당 주차를 월 단위가 아니라 년 단위로 하면 어떻냐는 조언을 해주셨다. (ex) 52주차)
하지만.. 나는 이런 버그가 생길 줄이야 왜 생각도 못했을까.. 월 단위나 년 단위나 똑같을 것 같아서 나의 마음 가는 대로.. 월 단위로 했다..!
(역시 멘토님의 조언은 새겨듣는 게 답인 듯 ㅋㅋㅎㅎㅎ)
이 버그를 마주쳤으니 년 단위의 주차로 바꾸기로 했다!
처음엔 yyyy_m로 바꿀 때
val week = cal.get(Calendar.WEEK_OF_MONTH)
를 val week = cal.get(Calendar.WEEK_OF_YEAR)
로 수정했다.
하지만 여기서 또 문제인 것. 지금 12월 말이다 이제 1월이 되면 년도도 바뀐다. 이렇게 된다면 또 같은 버그가 생길 것이라고 예상할 수 있다.
(월 단위로 생기는 버그 👉 년 단위로 생기는 버그로 바뀌는 것...)
하지만 테스트를 하면서 이상한 점이 있었다.
2021년 11월 29일
인데 2021년 1주차
로 나오는 것이다.
너무 의아했다.
그래서 2021년 1월 1일
도 확인했는데 2021년 6주차
인 것이다.
너무 이상했다...
이런 고민을 나혼자만 했을까? 생각하고 서칭을 했다.
그러다 찾게 된!
WeekFields의 week based year
WeekFields
의 weekOfWeekBasedYear
는 무조건 한 주는 7일로 채워서 끊는 방법을 사용하는 방식이다.
weekOfWeekBasedYear
이 아닌weekOfYear
는 Week based year를 고려하지 않고 해당 년도의 주차를 계산한다. 즉, weekOfYear
은 2021년 12월 31일에서는 2021년의 해당 주차를 구하지만, 1월 1일이 되면 2022년의 주차를 구한다.
우선
weekOfYear
는 localdate.getYear()
과 weekFields.weekOfYear()
이 짝이다.
weekOfWeekBasedYear
는 weekFields.weekBasedYear()
과 weekFields.weekOfWeekBasedYear()
이 짝이다.
날짜 예시를 보면서 이해를 해보자
2021.12.31 금요일
weekOfYear: 2021_52
weekOfWeekBasedYear: 2021_522022.1.1 토요일
weekOfYear: 2022_0
weekOfWeekBasedYear: 2021_522022.1.2 일요일
weekOfYear: 2022_0
weekOfWeekBasedYear: 2021_522022.1.3 월요일
weekOfYear: 2022_1
weekOfWeekBasedYear: 2022_1
2022년 1월 1일을 보자. 2021년 12월 27일 ~ 2022년 1월 2일이 한 주차 세트(나는 월요일을 한 주차의 시작으로 설정했다.)인데, 2022년 1월 1일 - 2일은 어느 년도에도 속하지 못해서 2022년도의 0주차로 되었다.
이렇게 되면 또 금에서 토로 넘어가면 리더보드는 리셋이 되어있을 것이다....!
이 문제를 해결하기 위해 weekBasedYear와 weekOfWeekBasedYear가 나왔다.
위의 예시를 보면 어떻게 나의 고민이 풀렸는지 알 것이다!
이제 LeaderboardId를 만드는 클래스를 수정해보자!
class LeaderboardIdGenerator {
companion object {
private val weekFields = WeekFields.of(Locale.UK) // 월요일이 한 주차의 시작이다.
fun generate(): String {
val today = LocalDate.now()
return "%d_%d".format(today.get(weekFields.weekBasedYear()), today.get(weekFields.weekOfWeekBasedYear()))
}
fun generateBeforeWeek(week: Int): String {
val beforeWeekDate = LocalDate.now().minusWeeks(week.toLong())
return "%d_%d".format(beforeWeekDate.get(weekFields.weekBasedYear()), beforeWeekDate.get(weekFields.weekOfWeekBasedYear()))
}
fun generate(year: Int?, weekOfYear: Int?): String {
return if (year != null && weekOfYear != null) "%d_%d".format(year, weekOfYear) else generate()
}
}
}
WeekFields.of(Locale.UK)
가 아닌 WeekFields.of(Locale.KOREA)
로 하면 월요일이 아닌 일요일이 주차의 첫 시작으로 설정된다. 나는 월요일을 주차의 시작으로 해야하기 때문에 WeekFields.of(Locale.UK)
로 설정했다.
이건 나의 엄청난 고민이 담긴 낙서장이다... ^_^
참고 링크