Skip to content

fix(mindrecord): 일기 기록 날짜를 표시 전용으로 바꾼다 (#1008) - #1121

Merged
1hyok merged 6 commits into
developfrom
fix/1008-diary-date-display-only
Aug 25, 2026
Merged

1hyok merged 6 commits into
developfrom
fix/1008-diary-date-display-only

Conversation

@Sadturtleman

Copy link
Copy Markdown
Collaborator

#1008 의 선택지 중 (b) 표시 전용을 택한 결과다. (a) BE 가 date 를 수용하게 되면 피커를 되돌린다.

서버가 받지 않는다 — 오늘 다시 실측했다

요청 보낸 date 저장된 date
POST /api/v1/diary 2026-08-01 2026-08-25 (diaryId 41545)
PATCH /api/v1/diary/41545 2026-08-01 2026-08-25

Swagger 요청 스키마도 그대로다 — 양쪽 모두 title · content · isDraft · todayMood · receiverIds 뿐이고 date 가 없다.

$ curl .../v3/api-docs | jq ...
POST  /api/v1/diary            필드: ['title','content','isDraft','todayMood','receiverIds']
PATCH /api/v1/diary/{diaryId}  필드: ['title','content','isDraft','todayMood','receiverIds']

왜 «그냥 두기» 가 안 되나

고를 수 있게 두면 사용자는 고른 날짜로 기록이 남았다고 믿는다. 저장 결과는 항상 오늘이라 목록·상세에서 다른 날짜로 보인다. 최근 리뷰에서 반복해 잡아 온 「그럴듯한 거짓」과 같은 모양이고, 이쪽은 사용자가 직접 조작한 값이라 더 세게 배신한다.

무엇을 바꿨나

  • 날짜 행에서 clickable 과 chevron 버튼을 걷어 표시 전용으로 둔다. BottomSheetCalendar 호출도 함께 제거.
  • DiaryWriteViewModel.onDateSelected 삭제 — 창구가 남아 있으면 다음 사람이 그 함수를 보고 UI 를 되살린다.
  • DiaryWriteUiState.date 는 남긴다. 이어쓰기 프리필이 원래 날짜를 채워 주는 자리다.

회귀 가드

DiaryWriteDateDisplayTest — ViewModel 에 날짜를 바꾸는 공개 창구가 없는지 리플렉션으로 확인한다. 피커를 되살리려면 이 테스트를 함께 고쳐야 하므로, BE 수용 없이 UI 만 되돌아오지 않는다.

실기동 확인

에뮬레이터에서 일기 작성 진입 → 날짜 행 탭 → 피커가 열리지 않는다. 날짜 텍스트(2026년 8월 25일)는 그대로 보이고, chevron 이 빠진 만큼만 행 높이가 줄었다(제목 필드 y 439 → 이전 대비 위로 당겨짐).

검증

게이트 결과
전 모듈 testDebugUnitTest BUILD SUCCESSFUL
:app:assembleDebug · :app:compileDebugAndroidTestKotlin BUILD SUCCESSFUL
실기동 위 참조

baseline 재생성 불필요 — feature:mindrecord:presentation 은 screenshot 검증 대상 5개 모듈에 없다.

판단 근거는 docs/qa/assumptions.md 에도 남겼다 — 실측은 사실이고, 추측인 부분은 «어느 쪽으로 맞출지» 뿐이라는 점을 그대로 적었다.

날짜를 고를 수 있지만 서버에 전달되지 않아, 사용자는 고른 날짜로 남았다고 믿게 된다.
서버는 생성·수정 어느 쪽에서도 date 를 받지 않는다 — 실측 2026-08-25: POST·PATCH 에
date 를 실어도 저장분은 오늘이고, Swagger 요청 스키마에도 필드가 없다.

BE 가 수용할 때까지 피커를 닫는다. 상태(date)는 이어쓰기 프리필이 원래 날짜를 채워
주는 자리라 남긴다. ViewModel 의 변경 창구를 없애 UI 만 되살아나는 일도 막는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@1hyok 1hyok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경 요청

판단은 지지한다. 서버가 date 를 받지 않는다는 것은 실측으로 확정됐고, 「고를 수 있지만 반영되지 않는」 상태를 그대로 두는 것보다 고르지 못하게 하는 쪽이 맞다. 되돌릴 조건(BE 수용)도 명시돼 있다.

막는 것은 회귀 가드 하나와 남은 자국 둘이다. 셋 다 몇 줄이면 끝난다.

1. 미사용 import 가 남았다 (DiaryWriteScreen.kt:20)

import androidx.compose.material3.IconButton   // ← 이 PR 이 유일한 사용처를 지웠다

실측 — 이 브랜치의 DiaryWriteScreen.kt 에서 IconButton 은 import 줄에만 등장한다(사용 0건). ktlint 는 통과했지만 미사용인 것은 사실이다. painterResource · core.ui.R 은 수신자 행·감정 아이콘이 계속 쓰므로 그대로 두면 된다.

2. 이 PR 의 논리와 정면으로 어긋나는 주석이 남았다 (DiaryWriteViewModel.kt:203-205)

loadDraft 안에 이 문장이 그대로 있다:

// 서버가 날짜를 주지 않은 임시저장이면 날짜 선택기의 현재 값을
// 유지한다 — 표시용으로 오늘을 지어내는 것과 달리, 여기서는
// 사용자가 등록 전에 직접 고르는 값이다.
date = draft.toUi()?.date ?: it.date,

「사용자가 등록 전에 직접 고르는 값」이 이 PR 로 거짓이 된다. 「날짜 선택기」도 이제 없다. PR 본문이 onDateSelected 를 지운 이유로 «창구가 남아 있으면 다음 사람이 그 함수를 보고 UI 를 되살린다» 를 들었는데, 같은 이유가 이 주석에 그대로 적용된다 — 오히려 이쪽이 «되살려도 된다» 고 읽힌다. 표시 전용이라는 사실로 바꿔 주면 된다.

3. 회귀 가드가 엉뚱한 것을 잡는다

인라인 참조. 요약하면 "update" 라는 낱말이 "date" 를 부분 문자열로 품고 있어서, 날짜와 아무 상관 없는 updateXxx 공개 함수가 하나만 생겨도 이 테스트가 빨개진다.


시안 대조 — 의도적 이탈이다 (기록용, 블로커 아님)

Figma 2671:17922 을 받아 대조했다. 시안의 날짜 행에는 chevron(∨)이 그려져 있다 — 고를 수 있는 UI 다. 이 PR 은 그것을 걷어내므로 시안 이탈이 맞다.

그래도 막지 않는 이유: 디자인 미답변 질의는 FE 재량이라는 위임이 있고, 이 결정의 근거가 「시안에 없다」가 아니라 「서버가 못 받는다」는 실측이기 때문이다. 다만 시안과 다른 화면이 나가는 것이므로 디자이너에게 한 줄 알려 두는 편이 좋다 — BE 가 date 를 수용하면 시안대로 돌아간다는 것까지 함께.

그 밖에 확인한 것 (문제 없음)

  • BottomSheetCalendar 는 죽지 않는다 — setting DatePickerBottomSheet, timeletter TimeLetterWriteScreen:201 이 계속 쓴다.
  • core_ui_arrowdown 도 DailyQuestionBanner · WeeklyReportReviewCard · ReceiverRegisterScreen 에서 살아 있다.
  • DiaryWriteUiState.date 를 남긴 판단은 맞다 — loadDraft 가 실제로 draft.toUi()?.date 로 채운다(위 2번 인용 줄).
  • DiaryUpdatePayload.date 를 손대지 않은 것도 맞다. #1007 소관이라 겹치지 않는다.
  • androidTest 게이트 보유.

머지 전 주의 — #1120 과 텍스트 충돌한다

docs/qa/assumptions.md 끝에 두 PR 이 각자 새 절을 붙여 git merge-tree 에서 충돌한다(코드는 자동 병합됨). 나중에 들어가는 쪽이 해소해야 하고, 충돌 상태로 두면 CI 가 아예 안 돈다.

val setters =
DiaryWriteViewModel::class.java.methods
.map { it.name }
.filter { it.contains("Date", ignoreCase = true) }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"update" 안에 "date" 가 들어 있다 — 이 필터는 날짜와 무관한 함수를 잡는다.

$ echo "updateTitle" | grep -i date   →  매치
$ echo "updateDraft" | grep -i date   →  매치

지금 DiaryWriteViewModel 에 update… 로 시작하는 공개 함수가 없어서 통과하는 것뿐이다. 누군가 updateTitle 같은 함수를 하나 추가하는 순간 이 테스트가 빨개지고, 실패 메시지는 「날짜 변경 창구가 남아 있다」라고 말한다 — 날짜를 건드린 적 없는 사람이 이 메시지를 받는다. 원인을 찾기까지 시간이 걸리고, 최악의 경우 가드가 통째로 지워진다.

이름 매칭 대신 지키려는 것을 직접 고정하는 편이 낫다. 예를 들어 date 를 인자로 받는 공개 함수가 없는지:

val dateSetters =
    DiaryWriteViewModel::class.java.methods
        .filter { m -> m.parameterTypes.any { it == LocalDate::class.java } }

assertTrue("날짜를 받는 공개 창구가 남아 있다: ${dateSetters.map { it.name }}", dateSetters.isEmpty())

이러면 이름과 무관하게 «날짜를 바꿀 수 있는 표면» 만 잡고, update… 오탐도 사라진다.

@1hyok
1hyok requested a review from koongmai August 25, 2026 11:24
@github-actions github-actions Bot added the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

이 PR 은 develop 와 충돌 상태이고, 그래서 CI 가 돌지 않는다.

pull_request 워크플로는 base 와 head 를 합친 가상 머지 커밋(refs/pull/<N>/merge)에서
실행되는데, 충돌이면 GitHub 이 그 ref 를 만들지 못해 워크플로가 트리거조차 되지 않는다.
체크가 실패하는 것이 아니라 아예 생성되지 않으므로, 새 커밋을 올려도 no checks reported
인 채 최신 커밋이 한 번도 검증되지 않은 상태로 남는다 (#1028).

develop 를 병합해 충돌을 풀면 검증이 다시 돈다. 해소되면 이 라벨은 자동으로 떨어진다.

Sadturtleman and others added 2 commits August 25, 2026 22:17
- DiaryWriteScreen 의 IconButton import — 이 PR 이 유일한 사용처를 지웠다.
- loadDraft 의 «사용자가 등록 전에 직접 고르는 값» 주석이 이 변경으로 거짓이 됐다.
  주석이 남으면 «되살려도 된다» 로 읽힌다.
- 회귀 가드가 이름으로 걸러 "update" 가 "date" 를 품는 오탐을 냈다. LocalDate 를 받는
  공개 함수로 좁혔다 — 날짜 창구가 생기면 실패하고, 무관한 updateXxx 에는 반응하지
  않는 것을 양쪽으로 확인했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Sadturtleman

Copy link
Copy Markdown
Collaborator Author

지적 3건 모두 반영했습니다.

1. 미사용 IconButton import — 지웠습니다. 지적대로 이 PR 이 유일한 사용처를 걷어낸 자리입니다. painterResource · core.ui.R 은 수신자 행·감정 아이콘이 계속 써서 남겼습니다.

2. 어긋난 주석 — 「사용자가 등록 전에 직접 고르는 값」이 이 PR 로 거짓이 되는 게 맞습니다. 사실로 바꿨습니다.

// 표시 전용 값이다 — 서버가 날짜를 주면 그걸 보여 주고, 안 주면 화면에 이미 떠 있던
// 값을 유지한다. 고르는 수단은 없다 (#1008).
date = draft.toUi()?.date ?: it.date,

3. 가드가 엉뚱한 것을 잡는다 — "update" 가 "date" 를 품는다는 지적이 정확합니다. 이름 규칙 대신 LocalDate 를 받는 공개 함수로 좁혔습니다.

양쪽으로 확인했습니다.

onDateSelected(LocalDate) 를 되살림  → FAILED  (여전히 잡는다)
updateSomethingUnrelated(String) 만 추가 → BUILD SUCCESSFUL  (오탐 없음)

시안 이탈 기록 — chevron 이 시안에 있다는 확인 감사합니다. 디자이너 쪽에 «BE 가 date 를 수용하면 시안대로 되돌린다» 를 함께 알리겠습니다. 이슈 #1008 에도 같은 내용을 남겨 두겠습니다.

assumptions.md 충돌 — #1120 이 먼저 머지돼 develop 을 물면서 해소했습니다. 현재 이 브랜치는 develop 최신 기준이고 CI 도 다시 돌고 있습니다.

Sadturtleman and others added 2 commits August 25, 2026 22:28
assumptions.md 인접 추가라 양쪽을 모두 살렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
assumptions.md 인접 추가라 양쪽을 모두 살렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 and removed conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 labels Aug 25, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@1hyok 1hyok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재리뷰 — 승인. 지적 3건 모두 닫혔다

d3b48443 하나로 셋 다 정리됐다.

앞선 지적 결과
1. 미사용 IconButton import 제거됨
2. loadDraft 의 어긋난 주석 교체됨 — 「표시 전용 값이다 … 고르는 수단은 없다」로 사실과 맞다
3. 회귀 가드의 "update" 오탐 타입 기반으로 좁혀 오탐이 사라졌다

새 blocking 은 없다. 수정 커밋이 건드린 세 파일 밖으로 새어 나간 것도 없고(#1061 에서 겪었던 「고친 태스크 옆으로 새는」 유형), CI 도 그대로 초록이다.

남은 것 하나는 막지 않는다 — [normal] 회귀 가드에 아직 손볼 데가 있다 → DiaryWriteDateDisplayTest.kt:29 인라인 코멘트. 그중 하나는 내가 제안한 방식이 남긴 구멍이라 함께 적었다.


검증

항목 결과
CI (head 2127c9b8) 전 항목 pass
git merge-tree origin/develop clean
#1120 과의 docs/qa/assumptions.md 충돌 해소됨 — #1120 이 08-25 13:13 에 머지됐고 이 브랜치가 그 뒤 develop 을 병합했다
교차 확인 — #1152 와 상호 병합 clean (두 PR 이 DiaryWriteScreen·DiaryWriteViewModel·DiaryWriteUiState 를 함께 건드린다)

실기동 QA — 이번 증분은 미수행. 증분이 import 한 줄·주석 한 덩이·테스트 한 곳이라 화면 거동이 바뀌지 않는다. 앞 회차에서 확인한 「날짜 행을 눌러도 피커가 열리지 않는다」는 그대로 유효하다.

시안 대조 — 앞 회차 결론 유지. Figma 2671:17922 의 날짜 행에는 chevron 이 있어 이 PR 은 의도적 시안 이탈이다. 근거가 「시안에 없다」가 아니라 「서버가 못 받는다」는 실측이므로 막지 않는다 — 다만 디자이너에게 한 줄 알려 두는 편이 좋다는 앞 회차 권고는 그대로다. BE 가 date 를 수용하면 시안대로 되돌아간다는 것까지 함께.

DiaryWriteViewModel::class.java.methods
.filter {
java.lang.reflect.Modifier
.isPublic(it.modifiers)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[normal] 둘 다 막는 것은 아니다. 다음에 이 파일을 열 때 같이 정리하면 된다.

1. 이 필터는 항상 참이다. Class.getMethods() 는 정의상 public 멤버만 돌려준다(상속분 포함). 그래서 isPublic 은 걸러 내는 것이 없고, ktlint 가 이 호출 때문에 줄을 셋으로 쪼갰다. 지우면 한 줄로 돌아온다.

2. 내가 제안한 타입 기반에는 구멍이 있다 — 짚어 두는 게 맞겠다. LocalDate 를 받는 함수만 보므로, 날짜 창구가 onDateSelected(millis: Long) 이나 onDateSelected(iso: String) 으로 되살아나면 이 가드는 침묵한다. 가상의 이야기만은 아니다 — 피커를 되살리는 사람은 BottomSheetCalendar 의 콜백 시그니처를 그대로 받아 적을 가능성이 높고, Compose 의 날짜 피커 계열은 epoch millis 를 넘기는 쪽이 흔하다.

이름과 타입을 둘 다 보면 오탐 없이 촘촘해진다. 이름 쪽은 앞서 문제가 된 update… 접두만 빼면 되고, 그 예외를 주석이 아니라 코드로 적어 두면 다음 사람이 왜 빠졌는지 알 수 있다.

val dateSetters =
    DiaryWriteViewModel::class.java.methods
        .filter { method ->
            method.parameterTypes.any { it == LocalDate::class.java } ||
                // "update" 가 "date" 를 부분 문자열로 품는다 — 그 접두만 빼고 이름으로도 본다.
                (method.name.contains("date", ignoreCase = true) && !method.name.startsWith("update"))
        }.map { it.name }

updateDate 같은 이름이 생기면 이 예외에 가려지지만, 그때는 LocalDate 인자를 받든 다른 타입을 받든 첫 조건이나 리뷰가 잡는다. 지금처럼 한 조건만 두는 것보다는 넓다.

@1hyok
1hyok merged commit 90c19ee into develop Aug 25, 2026
14 checks passed
@1hyok
1hyok deleted the fix/1008-diary-date-display-only branch August 25, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants