타이머 편집/리스트 초 단위 테스트

hyun·2025년 9월 3일
0

iOS

목록 보기
49/54

배경

목표 시간의 최소값이 5분인데 실제 동작을 빠르게 검증하려면 5분은 너무 김 → QA/개발 속도가 느려짐
#if DEBUG 빌드에서만 초 단위로 목표 시간을 선택/표시할 수 있도록 테스트 모드를 넣어, 30초·60초 같은 짧은 값으로 흐름을 즉시 확인하고자 함

시도한 접근

  1. TimerEditViewController
  • #if DEBUG에서 isTestMode = true.
  • 테스트 모드일 때 피커 옵션을 초 단위로 구성(timeOptions = 30~600초)
  • UI 단위 레이블“분/초)도 토글.
  1. TimerCell
  • 리스트에서 목표 시간을 초로 보여주기 위한 플래그 추가

겉으로 드러난 문제

편집 화면에서 30초로 선택했는데, 저장 후 리스트에는 60초로 보임.

-> 모델 저장 스키마가 ‘분’ 단위였음.
편집 화면 saveTapped()에서

// DEBUG(초 선택) → 분 스키마에 맞추기 위해 올림저장
minutes = max(1, Int(ceil(Double(selectedTime) / 60.0)))

30초 선택 → ceil(30/60) = 1분으로 저장

리스트는 저장된 값을 기준으로 초를 만들거나 그대로 표시
1분 저장 → 1 * 60 = 60초로 표기됨
→ 저장(분) vs 표시(초) 단위가 달라서 생긴 불일치

UI 버그

TimerEditViewController.updateCollapsedLabelText()가 단위를 항상 분으로 하드코딩해 테스트 모드에서도 분이 붙는 문제

해결 방법 (스키마 변경 없이)

스키마를 건드리지 않고 디버그에서만 실제 선택한 초값을 따로 저장해서 표시 우선으로 쓰도록 함

1) 디버그 전용 저장소 추가

  • UserDefaults를 이용해 timerID별 초값을 보관
  • DebugGoalSecondsStore 유틸
#if DEBUG
enum DebugGoalSecondsStore {
  private static let keyPrefix = "debug.goalSeconds."

  static func set(_ secs: Int, for id: String) {
    UserDefaults.standard.set(secs, forKey: keyPrefix + id)
  }
  static func get(for id: String) -> Int? {
    UserDefaults.standard.object(forKey: keyPrefix + id) as? Int
  }
  static func remove(for id: String) {
    UserDefaults.standard.removeObject(forKey: keyPrefix + id)
  }

  // UUID 오버로드(편의)
  static func set(_ secs: Int, for id: UUID) { set(secs, for: id.uuidString) }
  static func get(for id: UUID) -> Int? { get(for: id.uuidString) }
  static func remove(for id: UUID) { remove(for: id.uuidString) }
}
#endif

2) 저장 직후, 선택 초값을 기록

TimerEditViewController.saveTapped()의 save 성공 직후

#if DEBUG
if let id = viewModel.editing?.timerID {
  let secs = TimerEditViewController.isTestMode ? selectedTime : (minutes * 60)
  DebugGoalSecondsStore.set(secs, for: id) // UUID 지원 버전 사용
}
#endif

3) 리스트 표시에서 초값을 우선 사용

TimerCell.updateContent / 접근성 라벨에서

#if DEBUG
if Self.showSecondsInList, let secs = DebugGoalSecondsStore.get(for: timer.timerID) {
  focusValueLabel.text = "\(secs)초"
} else {
  focusValueLabel.text = "\(timer.goalTime)분"
}
#else
focusValueLabel.text = "\(timer.goalTime)분"
#endif
#if DEBUG
if Self.showSecondsInList, let secs = DebugGoalSecondsStore.get(for: timer.timerID) {
  accessibilityLabel = "\(timer.title), 집중 목표 \(secs)초, 오늘 \(today)"
} else {
  accessibilityLabel = "\(timer.title), 집중 목표 \(timer.goalTime)분, 오늘 \(today)"
}
#else
accessibilityLabel = "\(timer.title), 집중 목표 \(timer.goalTime)분, 오늘 \(today)"
#endif

4) 접힘 라벨 단위 버그 수정

updateCollapsedLabelText()에서 단위 하드코딩 제거

let unitText = TimerEditViewController.isTestMode ? "초" : "분"
let unit = Typography.attributed(unitText, style: .headingXl(weight: .semibold), color: .gray600)
number.append(unit)

데이터 스키마를 바꾸지 않음 → 마이그레이션/리스크 0.
변경 범위가 DEBUG 빌드에만 한정 → 릴리즈 품질에 영향 없음.
리스트/접근성 같은 표시부만 조건부로 분기 → 사이드이펙트 최소.

0개의 댓글