Repository navigation
perf: 장소 조회 응답 캐시 도입 (역지오코딩 7일 · 검색 1시간) - #372
Merged
Merged
Conversation
GetCurrentLocation / ReverseGeocode / SearchPlaces / RecentSearches 를 지우고 호출처가 Domain 포트(LocationService·PlaceRepository· RecentSearchRepository)를 직접 주입받는다. 넷 다 단일 Repository 메서드를 한 줄 위임하던 계층이라, 같은 프로토콜에 이름만 하나 더 입히고 있었다. Presentation → Domain 경계는 그대로다 — 피처가 받는 건 여전히 Domain 프로토콜이고 구체 Data 타입은 AppDIContainer만 본다. 부수 정리: - 위임 여부만 검증하던 Domain 테스트 5개 삭제 (Observe/ObserveChange/ RequestAlarmSync/GetLastRouteDetail/ReverseGeocode). 앞 커밋에서 UseCase를 지울 때 테스트 파일이 남아 DomainTests가 컴파일되지 않고 있었다. - Example/Tests 스텁이 좁은 UseCase 대신 PlaceRepository 전체를 채택하게 되어 쓰지 않는 메서드에 기본 구현을 달았다(의도된 Tests/Example 중복 규약). - RecentSearchesUseCase.fetch() → RecentSearchRepository.recentSearches()로 호출명이 바뀐다. 검증: Debug/Stage/Release 3구성 빌드 + Domain 112 · AtchaData 53 · AtchaV2 39 · HomeFeature 66 · SearchFeature 26 · SettingsFeature 18 전부 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
검색 화면은 진입할 때마다 위치 획득 + GET /locations/rgeo를 다시 돌았고, 같은 키워드를 다시 쳐도 매번 서버로 갔다. 코드베이스 전체에 응답 캐시가 0건이었다. CoreStorage에 ExpiringCache<Value>를 추가하고, Data 레이어에 CachingPlaceRepository 데코레이터로 감쌌다. 피처는 여전히 Domain 프로토콜만 보므로 호출처는 한 줄도 바뀌지 않는다. 캐시 대상은 "틀렸을 때의 피해"로 골랐다: - 역지오코딩 7일 — 좌표→주소는 행정구역 개편 수준에서만 바뀐다 - 장소 검색 1시간 — POI는 생기고 없어진다 - 서비스 지역 판정 캐시 안 함 — 틀리면 되는 지역을 막는다. 호출도 집 주소 저장 시 1회뿐이라 아낄 왕복이 없다 좌표는 키로 쓰기 전에 반올림한다(역지오코딩 4자리≈11m, 검색 편향 3자리≈110m). GPS는 가만히 있어도 미터 단위로 떨려서, 원시 좌표를 키로 쓰면 적중률이 사실상 0이 된다. 만료에는 ±10% 지터를 넣는다. 같은 TTL로 한꺼번에 채워진 항목은 한꺼번에 만료되는데, 이 앱의 실제 피크는 막차 시간대에 수천 기기가 동시에 앱을 여는 순간이라 만료가 몰리면 그 순간 요청이 서버에 몰린다. 실패는 캐시하지 않는다 — 지하철에서 한 번 실패한 키워드가 지상에 나와도 계속 실패로 답하면 재시도 버튼이 의미를 잃는다. 저장은 FileKeyValueStore(.cache) — 백업에서 제외된다. UserDefaults는 첫 접근에 plist 전체를 올리므로 이 크기(≈20KB)를 담을 자리가 아니다. 부수: RecentSearchRecordDTO → PlaceRecordDTO. 최근 검색과 캐시가 같은 표현을 쓰게 되어 이름이 용도를 좁게 말하고 있었다. 검증: 3구성 빌드 + CoreStorage 44(+11) · AtchaData 64(+11) 등 전 스킴 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
리팩토링으로 확정된 규칙이 커밋 메시지에만 남아 있었다. 다음 작업자가 같은 판단을 다시 내리지 않도록 규약으로 적는다. - UseCase는 기본이 아니다(포트 2개 조합 또는 비즈니스 규칙일 때만) - 화면 상태 채널은 정확히 2개, != oldValue 게이트 유지 - 영속·캐시는 CoreStorage의 actor 3종으로만, 매체는 값 크기로 가름 - 응답 캐시는 Data 데코레이터로, 대상은 "틀렸을 때의 피해"로 선정 - 막차 경로 캐시를 왜 아직 안 했는지와 도입 조건 2가지 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AlarmSessionLifecycleService.isAcknowledged는 쓰기 2곳, 읽기 0곳이었다. "스냅샷 영속화의 인메모리 미러"였는데 정본(AlarmSessionStore)이 세션 수명을 들게 되면서 미러를 볼 이유가 사라졌다. 남겨 두면 두 곳이 어긋날 수 있다는 인상만 준다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
왜
실측으로 확인됐다. 응답 캐시가 코드베이스 전체에 0건이라, 검색 화면은 진입할 때마다 위치 획득 +
GET /locations/rgeo를 다시 돌았고 같은 키워드를 다시 쳐도 매번 서버로 갔다.무엇을
CoreStorage에ExpiringCache<Value>를 추가하고, Data 레이어에서CachingPlaceRepository데코레이터로 감쌌다. 피처·Domain은 한 줄도 바뀌지 않는다 — 여전히PlaceRepository프로토콜만 본다.캐시 대상을 고른 기준: 틀렸을 때의 피해
막차 경로(
/routes/last-routes)는 이 PR에 없다. 피해가 "막차를 놓친다"라서, 화면 스탬프("HH:mm 기준")와 "알람 등록 근거로는 캐시 금지"를 함께 넣어야 하고 후자는RegisterAlarmUseCase시그니처가 바뀐다. 별도 리뷰가 필요한 크기라 분리했다 (조건은 CLAUDE.md에 적어 뒀다).설계에서 값을 하는 지점 3개
저장은
FileKeyValueStore(.cache)— 백업 제외. UserDefaults는 첫 접근에 plist 전체를 메모리로 올리므로 이 크기(≈20KB)를 담을 자리가 아니다.부수
RecentSearchRecordDTO→PlaceRecordDTO. 최근 검색과 캐시가 같은 표현을 쓰게 되어 이름이 용도를 좁게 말하고 있었다.검증
🤖 Generated with Claude Code