어떤 기능인가요?
운동 기록 읽기 캐시의 저장층입니다. Entity와 DAO, DataSource 분리까지만 하고 화면 전환은 별도 이슈(#584)에서 다룹니다.
- 대상은 셋입니다: 캘린더 집계, 목록, 상세
- 캘린더 집계는 서버 응답(
GetExerciseCountByRangeDTO의 date, exerciseCount)을 그대로 전용 테이블에 저장합니다. 로컬 기록 행을 세는 방식이면 캘린더만 열어본 달이 오프라인에서 빈 달로 보입니다. 캘린더 API는 개수만 주고 기록 자체를 주지 않기 때문입니다
- 모든 테이블에 memberId 컬럼과 계정 조건을 겁니다 (#581에서 정한 규칙)
- 캐시 대상 응답에 점수 필드는 없습니다 (
GetExerciseRecordData, GetExerciseRecordListDto, GetExerciseCountByRangeDTO 확인). 점수를 빼는 별도 규칙은 필요 없습니다
- 기존 API 호출부를
RemoteDataSource로, Room 접근을 LocalDataSource로 분리합니다
작업 상세 내용
진행 중 변경 사항
쓰기용 컬럼을 먼저 넣었습니다
읽기 캐시만 놓고 보면 serverId를 PK로 써도 되고 syncState는 필요 없습니다. 그런데 디자인 문서 최종본이 운동 기록 CRUD까지 범위에 넣으면서 다음 셋을 지금 반영했습니다.
- 행 식별을
localId(UUID)로, serverId는 nullable — 오프라인에서 만든 기록은 서버 ID를 받기 전이라 서버 ID를 PK로 쓰면 들어갈 자리가 없습니다 (문서 2) 원본 데이터 위치)
syncState 컬럼 (문서 설계 규칙 D4)
- 서버 목록 반영이
SYNCED 행만 교체 (문서 설계 규칙 D6, 위험 요소 8)
지금 정한 이유는 DB가 아직 배포된 적이 없어서 마이그레이션 비용이 0이고, #584가 ViewModel 3개와 exercise_nav_graph.xml의 recordId: long 7곳을 건드리기 때문입니다. serverId 기준으로 한 번 전환한 뒤 CRUD 단계에서 localId로 다시 전환하면 같은 파일을 두 번 고칩니다.
읽기가 1회 조회입니다
디자인 문서의 읽기 갱신 방식이 "로컬 먼저 + 뒤에서 갱신"에서 "온라인이면 서버를 기다리고, 실패·오프라인이면 로컬"로 바뀌었습니다(문서 3) 읽기 갱신 방식, 설계 규칙 D2). 그래서 DAO 반환 타입을 Flow가 아니라 suspend 1회 조회로 두었습니다.
DAO 테스트를 미뤘습니다
메모리 DB로 DAO를 돌리려면 Robolectric이 필요합니다. CI가 testDebugUnitTest만 실행해서 androidTest에 두면 아무도 돌리지 않기 때문입니다. data 모듈에 테스트가 하나도 없는 상태에서 이 인프라를 들이는 비용 대비, 지금 SQL은 단순 조회이고 틀리면 화면에 바로 드러납니다.
타이머 동기화(#587~#589)와 운동 기록 CRUD 단계에서 다시 넣습니다. 그때는 syncState 전이와 대기 행 보존 쿼리가 생기는데, 이건 틀려도 화면에 안 보이고 사용자 데이터가 조용히 사라지는 종류입니다.
참고할만한 자료(선택)
오프라인 1단계 디자인 문서의 데이터 모델 절 기준입니다. #581이 선행이며 같은 PR로 올라갑니다.
어떤 기능인가요?
운동 기록 읽기 캐시의 저장층입니다. Entity와 DAO, DataSource 분리까지만 하고 화면 전환은 별도 이슈(#584)에서 다룹니다.
GetExerciseCountByRangeDTO의 date, exerciseCount)을 그대로 전용 테이블에 저장합니다. 로컬 기록 행을 세는 방식이면 캘린더만 열어본 달이 오프라인에서 빈 달로 보입니다. 캘린더 API는 개수만 주고 기록 자체를 주지 않기 때문입니다GetExerciseRecordData,GetExerciseRecordListDto,GetExerciseCountByRangeDTO확인). 점수를 빼는 별도 규칙은 필요 없습니다RemoteDataSource로, Room 접근을LocalDataSource로 분리합니다작업 상세 내용
LocalDataSource추가, 기존 API 호출을RemoteDataSource로 분리GetScoreDTO) 표시용 스냅샷 테이블을 이 이슈에 포함할지 별도 이슈로 뺄지 PR에서 선택 → 별도 이슈로 분리. 점수는 운동 기록 읽기와 소스가 달라 묶을 이유가 약합니다DAO 테스트 (메모리 DB로 날짜 범위 조회, 계정 조건)→ 타이머·CRUD 단계로 미룸 (아래 참고)진행 중 변경 사항
쓰기용 컬럼을 먼저 넣었습니다
읽기 캐시만 놓고 보면
serverId를 PK로 써도 되고syncState는 필요 없습니다. 그런데 디자인 문서 최종본이 운동 기록 CRUD까지 범위에 넣으면서 다음 셋을 지금 반영했습니다.localId(UUID)로,serverId는 nullable — 오프라인에서 만든 기록은 서버 ID를 받기 전이라 서버 ID를 PK로 쓰면 들어갈 자리가 없습니다 (문서 2) 원본 데이터 위치)syncState컬럼 (문서 설계 규칙 D4)SYNCED행만 교체 (문서 설계 규칙 D6, 위험 요소 8)지금 정한 이유는 DB가 아직 배포된 적이 없어서 마이그레이션 비용이 0이고, #584가 ViewModel 3개와
exercise_nav_graph.xml의recordId: long7곳을 건드리기 때문입니다.serverId기준으로 한 번 전환한 뒤 CRUD 단계에서localId로 다시 전환하면 같은 파일을 두 번 고칩니다.읽기가 1회 조회입니다
디자인 문서의 읽기 갱신 방식이 "로컬 먼저 + 뒤에서 갱신"에서 "온라인이면 서버를 기다리고, 실패·오프라인이면 로컬"로 바뀌었습니다(문서 3) 읽기 갱신 방식, 설계 규칙 D2). 그래서 DAO 반환 타입을
Flow가 아니라suspend1회 조회로 두었습니다.DAO 테스트를 미뤘습니다
메모리 DB로 DAO를 돌리려면 Robolectric이 필요합니다. CI가
testDebugUnitTest만 실행해서androidTest에 두면 아무도 돌리지 않기 때문입니다. data 모듈에 테스트가 하나도 없는 상태에서 이 인프라를 들이는 비용 대비, 지금 SQL은 단순 조회이고 틀리면 화면에 바로 드러납니다.타이머 동기화(#587~#589)와 운동 기록 CRUD 단계에서 다시 넣습니다. 그때는
syncState전이와 대기 행 보존 쿼리가 생기는데, 이건 틀려도 화면에 안 보이고 사용자 데이터가 조용히 사라지는 종류입니다.참고할만한 자료(선택)
오프라인 1단계 디자인 문서의 데이터 모델 절 기준입니다. #581이 선행이며 같은 PR로 올라갑니다.