feat: 보관함 월별 조회를 감정별 카드 조회 API로 교체 - #62
Merged
Merged
Conversation
soyeonLee126
force-pushed
the
feature/card-monthly-emotion-api
branch
from
August 21, 2026 06:22
ff63b1e to
09b9412
Compare
월별 조회에 카드 식별자가 없어 날짜와 그날 순번을 카드의 신원으로 쓰고 있었습니다.
/api/cards/monthly/emotions/{emotion} 은 id 와 요약, 대사까지 담아 감정 필터도 서버가 겁니다.
- CardService.getCardsByMonth 를 getCardsByMonthAndEmotion 으로 교체합니다.
- 응답이 최신순이라 저장소에서 뒤집습니다. 보관함 더미가 목록 순서대로 위에 얹으며 쌓기
때문에, 오래된 순으로 줘야 최근 카드가 맨 위에 옵니다.
- 날짜별 조회와 스키마가 같아 CardResponse 와 toDomainOrNull 을 그대로 씁니다.
- CardEntry 와 CardCalendarResponse 를 지웁니다. 날짜+순번으로 카드를 지목하던 방식이
사라집니다.
목록이 id 와 요약, 대사를 이미 들고 있어 종이를 누를 때 다시 조회할 이유가 없어졌습니다. - ArchiveCards.Loaded 가 CardEntry 대신 Card 를 듭니다. - selectCard 가 네트워크 없이 상태만 바꿉니다. isCardLoading 과 CardLoadFailed 가 사라집니다. - 카드를 버리면 목록에서 그 id 만 빼냅니다. 순번이 당겨지는 문제가 없어 달을 다시 받지 않습니다. - 감정 필터가 서버로 넘어가 loadMonth 의 filter 가 사라집니다. 늦게 온 응답이 지금 보고 있는 달을 덮지 않게 막는 가드는 그대로 둡니다.
날짜와 그날 마지막 순번으로 어느 종이가 방금 버린 것인지 추측하고 있었습니다. 카드 id 가 생겼으므로 그대로 지목합니다. 같은 날 여러 장을 버려도 어긋나지 않습니다. - ChatRoomScreen 의 onCardDiscard 가 날짜 대신 카드 id 를 넘깁니다. - Navigator 가 드는 일회성 신호도 id 로 바뀝니다. - PaperPile 의 droppedIndex 가 indexOfFirst 한 줄이 됩니다.
감정별 조회를 넣으면서 붙인 주석 중 시그니처를 되풀이하는 부분을 지웁니다. 최신순으로 온다는 API 계약과 오래된 순으로 뒤집는 이유만 남깁니다.
순번(indexInDate)을 없앤 뒤에도 그 순번을 근거로 대는 주석이 두 군데 남아 있었습니다. 사실과 다르니 고칩니다. - ArchiveDetailScreen: "같은 날짜 뒤 순번이 당겨져" → 지운 카드가 목록에 남는다 - Navigator: "위 날짜와 같은 이유로" → 이제 날짜가 아니라 카드 id 다 같은 문장이 계층마다 반복되던 것도 한 곳만 남깁니다. 오래된 순으로 준다는 계약은 CardRepository 에만, YearMonth.toString() 이 맞물린다는 사실은 Room 구현에만 둡니다. CardService 와 글자까지 겹치던 CardRepository 의 삭제 KDoc 세 개를 줄입니다. Navigator 의 navigate·goBack·finishCurrentFlow·goToKey·goToTopLevel·clearSubStack 은 이름이 그대로 하는 말을 지우고 규칙만 남깁니다.
soyeonLee126
force-pushed
the
feature/card-monthly-emotion-api
branch
from
August 21, 2026 06:48
f02d822 to
904a1cb
Compare
index_in_date 를 지우면서 identityHash 가 바뀌었는데 version 은 1 에 그대로 있었습니다. Room 은 DB 를 열 때 저장된 해시를 먼저 확인하고 다르면 예외로 던지는데, fallbackToDestructiveMigration 은 버전이 바뀐 경우만 타므로 이 상황을 구제하지 못합니다. 이전 스키마가 깔린 기기는 카드 화면에 들어가는 순간 죽습니다. - version 을 2 로 올려 파괴적 마이그레이션이 실제로 돌게 합니다. - 덮어써 버린 1.json 을 변경 전 스키마로 되돌리고, 새 스키마는 2.json 으로 내보냅니다. - 엔티티를 고치면 version 도 올려야 한다는 이유를 클래스 KDoc 에 남깁니다.
캐시 경로는 SQL 의 date, id 오름차순을 쓰고 서버 경로는 "서버가 최신순으로 준다"는 가정 아래 reversed() 로 뒤집었습니다. 두 결과가 일치하는 것은 서버 정렬이 정확히 그 역순일 때뿐인데, 그 가정은 코드로도 테스트로도 고정돼 있지 않았습니다. 같은 목록이 첫 진입과 재진입에서 다른 순서로 쌓일 수 있습니다. - 두 경로 모두 date, id 기준 결정적 정렬로 정규화하고 서버 순서 가정을 걷어냅니다. - 두 경로가 같은 순서를 내는지, 캐시 조회가 실패하면 서버로 대체하는지, 월별 응답의 data 가 null 이면 실패로 전하는지 테스트로 덮습니다. - 사라진 indexInDate 를 설명하던 테스트 주석을 지금 동작으로 고칩니다.
한 번 쓰고 비우는 기록을 종이 더미가 들고 있었습니다. 달을 바꾸면 목록이 Loading 으로 돌아가 더미가 컴포지션에서 빠지므로 그 기록도 함께 폐기되고, 원래 달로 돌아오면 화면이 계속 들고 있는 값으로 다시 초기화됩니다. 카드를 버린 뒤 지난 달을 거쳐 돌아오면 같은 종이가 또 떨어졌습니다. - 소비 기록을 더미보다 오래 사는 화면으로 올리고, 더미는 다 썼다고 알리기만 합니다. - 다시 받는 동안 지난 목록을 남기지 않습니다. 파쇄 화면에서 돌아온 직후 응답이 늦으면 이미 지운 종이를 눌러 그 카드로 파쇄까지 들어갈 수 있었습니다. - cards.cards 로 읽히던 이중 이름을 cardsState 로 풉니다. - 날짜에서 id 로 바뀐 낙하 신호와 force 재조회 분기를 테스트로 덮습니다. 둘 다 검증이 없어 파쇄 후 목록이 갱신되지 않는 회귀가 조용히 지나갈 수 있었습니다.
- 카드 상세를 다시 조회하지 않게 되면서 쓰이지 않게 된 archive_card_load_error 를 지웁니다. - 삭제 메서드 셋의 KDoc 에 카드가 나온 채팅방까지 함께 지운다는 부수 효과를 되돌립니다. 호출부가 알아야 하는 정책인데 data 계층에만 남아 있었습니다. - toEntity 가 파싱 실패 값도 남긴다고 했지만 두 조회 경로 모두 걸러낸 카드만 저장합니다.
…ature/card-monthly-emotion-api 보관함 빈 상태에서 문구를 없애는 변경이 이쪽에서 이름을 바꾼 목록과 만났습니다. 문구를 없애는 쪽을 따르고, 참조만 cardsState.cards 로 맞춥니다. 조회 실패 문구는 그대로 남습니다.
캐시가 부분 데이터를 완전한 것으로 착각하고 있었습니다. 날짜별 조회는 하루치만 캐시에 넣는데, 감정+월 조회가 그 행 하나만 찾아도 비어 있지 않다는 이유로 캐시 히트로 판정해 서버를 건너뛰었습니다. 한 달치 대신 하루치 종이만 뜹니다. 캐시를 감정+월 조회 전용으로 만듭니다. 쓰는 쪽과 읽는 쪽의 키가 같아야 "있으면 완전하다"가 성립합니다. 날짜별 조회는 늘 서버를 봅니다. 읽는 곳이 없어진 findByDate 를 걷어냅니다. CardResponse 의 문자열 필드에 기본값을 둡니다. coerceInputValues 는 선언된 기본값이 있을 때만 null 을 바꿔 주므로, 기본값이 없으면 카드 한 장의 null 하나로 그 달 전체가 실패로 떨어졌습니다. toDomainOrNull 이 약속한 "한 장만 버린다" 가 작동하지 못했습니다. 공유하기는 아직 동작하지 않아 숨깁니다. GamssEmotionCard 에 isShareVisible 을 두고 보관함 카드 상세에서 끕니다. 자리는 남겨 카드 안 간격이 시안과 어긋나지 않게 하고, 투명한 동안은 터치도 받지 않습니다.
캐시는 SQL 로 감정과 달을 걸러 읽는데 서버 경로는 받은 것을 그대로 통과시켰습니다. 서버가 범위를 벗어난 카드를 섞어 보내면 첫 조회에만 보이고 캐시가 답하는 다음 조회에서 사라졌습니다. 범위 밖 카드를 캐시에 넣으면 그 달을 반쪽만 채우게도 됩니다. 이 걸러내기가 ViewModel 에서 없앤 감정 필터의 자리도 대신합니다. 서버가 다른 감정을 흘려도 분노 칸에 기쁨 카드가 뜨지 않습니다. 두 경로가 한 규칙을 쓰므로 답이 갈리지 않습니다. 아무도 호출하지 않는 ClearCardCacheUseCase 와 그 테스트를 지웁니다. 세션 정리는 AuthRepositoryImpl 이 CardRepository.clearCache 를 직접 부릅니다.
seunghee17
reviewed
Aug 21, 2026
Collaborator
|
제가 PR#58 을 먼저 머지 해버렸는데 혹시 base develop으로 변경부탁드려도 될까요..? |
파쇄 화면에서 돌아올 때 load(force = true) 로 그 달을 통째로 다시 받고 있었다. 카드를 날짜·순번으로 다루던 시절, 한 장을 지우면 같은 날 뒤 순번이 당겨져 목록이 서버와 어긋났기 때문이다. 카드를 id 로 다루게 된 뒤 그 이유가 사라졌는데 재조회만 남아 있었다. Navigator 가 파쇄한 카드 id 를 남기고, 보관함 상세가 목록에서 그 한 장만 뺀다. 파쇄가 끝났다는 건 서버에서 이미 지워졌다는 뜻이라 남은 목록이 어긋나지 않는다. 대신 남은 종이가 사라졌다 다시 쌓이지 않고, 조회가 실패해 지운 카드와 상관없는 나머지까지 못 보게 되는 일도 없어진다. 방금 만든 카드를 보고 들어오는 길(droppedCardId)은 목록에 없는 카드를 받아야 하므로 force 재조회를 그대로 둔다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ly-emotion-api base 를 develop 으로 옮기면서 ChatRoomScreen 의 import 블록이 충돌했다. 이 브랜치가 카드를 날짜 대신 id 로 지목하면서 지운 java.time.LocalDate 자리에 develop 이 kotlin.coroutines.cancellation.CancellationException 을 넣었다. 병합 결과에서 LocalDate 는 쓰이지 않으므로 CancellationException 만 남긴다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
seunghee17
approved these changes
Aug 21, 2026
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.
작업 개요
보관함 종이 더미가 쓰던 월별 조회 API를 감정별 카드 조회 API로 바꿉니다.
기존
GET /api/cards/monthly는 그 달 날짜별 대표 감정만 돌려주었습니다. 카드 식별자도, 요약도 없어서 보관함은 날짜와 그날 순번만 들고 있었고, 종이를 누를 때마다 그 날짜로 단건 조회를 한 번 더 해서 요약과 id를 채워야 했습니다. 같은 날 여러 장을 지우면 뒤 순번이 당겨져 목록을 다시 받아 와야 하는 문제도 여기서 나왔습니다.GET /api/cards/monthly/emotions/{emotion}은 한 달 한 감정 분량의 카드를 내용까지 한 번에 줍니다. 보관함 한 칸이 보는 범위와 API 응답 범위가 같아지므로, 상세 조회를 위한 두 번째 호출과 순번 추측을 모두 걷어냈습니다.base를
feature/add-card-motion으로 둔 stacked PR입니다. 카드 접기·낙하 연출은 PR #58에서 리뷰 중이고, 이 PR은 그 위에 올린 조회 API 교체분(커밋 4개)만 담았습니다.작업 유형
변경 사항
조회 API 교체
CardService.getCardsByMonth→getCardsByMonthAndEmotion(emotion, yearMonth). 응답이CardCalendarResponse에서CardResponse로 바뀌어 요약과 id가 함께 옵니다.CardRepository.getCardsByMonth(yearMonth): List<CardEntry>→getCardsByMonthAndEmotion(character, yearMonth): List<Card>. 서버는 최신순으로 주고, 종이 더미가 목록 순서대로 쌓기 때문에 repository에서 오래된 순으로 뒤집어 돌려줍니다.GetCardsByMonthUseCase→GetCardsByMonthAndEmotionUseCase. 파라미터는MonthlyEmotionQuery(character, yearMonth)로 묶었습니다. 감정 없이 달만으로는 조회하지 않습니다.CardEntry와CardCalendarResponse를 제거했습니다.보관함 상세가 카드를 통째로 다룸
ArchiveCards.Loaded가List<CardEntry>대신List<Card>를 듭니다.selectCard가 날짜 재조회 없이 눌린 카드를 그대로 선택합니다. 두 번째 호출이 사라지면서isCardLoading과CardLoadFailed사이드 이펙트도 함께 없어졌습니다.방금 버린 카드를 id로 지목
ChatRoomScreen.onCardDiscard와Navigator의 일회성 신호가 날짜 대신 카드 id를 넘깁니다(droppedCardDate→droppedCardId).PaperPile의droppedIndex가 날짜·순번 매칭 대신indexOfFirst한 줄이 됩니다. 같은 날 여러 장을 버려도 낙하할 종이를 정확히 고릅니다.원격 설정 키 정리
RemoteConfigKey.UseChatEndFeature를 제거했습니다. 카드 생성 버튼이 상시 노출로 바뀐 뒤 어느 화면도 읽지 않는 키였습니다. 열려 있는 플래그가 없어 enum은 비어 있고, 새 플래그를 여기에 한 줄로 더하면 됩니다.RemoteConfigRepositoryImplTest도 함께 제거했습니다.관련 이슈
관련 작업 (Notion)
스크린샷 / 동작 화면
화면 구성은 그대로입니다. 눈에 보이는 변화는 카드 상세를 열 때 있던 로딩 인디케이터가 사라진 것뿐입니다. 목록에 이미 내용이 들어 있어 곧바로 뜹니다.
체크리스트
develop으로 설정되어 있다feature/add-card-motion으로 두었습니다. #58이 머지되면 자연히develop기준으로 따라갑니다.:app:compileDebugKotlin+ 변경 모듈 유닛 테스트를 돌려 BUILD SUCCESSFUL을 확인했습니다(432 테스트, 실패 0).feat:,fix:등)을 따른다리뷰 요청 사항
RemoteConfigKey를 빈 enum으로 남겨 두었습니다. 지금 열려 있는 플래그가 없어서인데, 아예 파일을 지우는 편이 나을지 판단 부탁드립니다.