Skip to content

fix(mindrecord): 주간리포트 상단 카운트와 요약 기록일수 불일치 — diaryAmount 는 1인데 week[] 에 미포함 #590

Closed

Description

@Sadturtleman

📜 Overview (Required)

주간리포트에서 상단 카운트는 "일기 1" 인데 요약 문구는 "0일의 마음을 기록하셨네요" 로 나오는 경우가 있다. 두 값의 출처가 서로 다른 필드라서 생기는 불일치다.

증상 — 1차 QA(2026-07-19) 15번.

상단 카운트는 데일리질문 1·일기 1인데 본문은 "이준혁님은 0일의 마음을 기록하셨네요" — 수치 불일치

원인 (가설) — 같은 응답 안에서 두 필드가 어긋난다.

화면 요소 출처 필드
상단 카운트 "일기 N" report.diaryAmount
요약 문구 "N일" report.week[] 의 isDiary
diaryAmount = 1                     → 상단 "일기 1"
week = []  (또는 isDiary = false)   → 기록일수 0 → "0일"

week 가 기록이 있는 날만 담기는 sparse 배열(#563 에서 확인)이라는 점까지 감안하면, 일기가 있는데도 week 에 담기지 않는 조건이 존재하는 것으로 보인다.

확인이 필요한 것 — diaryAmount 는 1 이상인데 week 에는 해당 일기가 담기지 않는 조건이 무엇인지. 서버 응답 대조가 선행돼야 클라 수정 범위(방어 로직인지, 서버 수정 요청인지)가 정해진다.

📌 Child Issue(Optional)

No response

📍 Note (Optional)

Activity

  1. OnlyReadOneBook commented on Jul 29, 2026

    @OnlyReadOneBook
    Collaborator

    서버 대조 결과입니다 (2026-07-29 12:1x, QA 계정 dev 서버 실측).

    diaryAmount 와 week[] 는 두 경로 모두 일치했습니다 — 가설한 조합은 재현되지 않았습니다.

    조작 diaryAmount week[]
    일기 1건 등록 (isDraft:false, 07-29) 1 [{diaryId:7, day:29, isDiary:true}]
    임시저장 1건 추가 (isDraft:true) 1 (변화 없음) 1건 (변화 없음)

    임시저장은 양쪽 모두에서 제외되므로 draft 여부는 원인이 아닙니다. 정상 등록 건은 양쪽에 모두 반영됩니다.

    따라서 QA 15번의 조합은 다른 조건에서만 생깁니다. 아직 안 본 후보는 주 경계(일요일/월요일 등록분), 과거 날짜 등록분, 삭제된 일기의 잔여 카운트입니다. week 가 sparse 라는 전제는 그대로입니다.

    미대조 — QA 당시 계정(이준혁)의 07-13 주 데이터는 재현하지 못했습니다. QA 계정에는 그 주 기록이 없습니다.

  2. OnlyReadOneBook commented on Jul 29, 2026

    @OnlyReadOneBook
    Collaborator

    추가 실측 (2026-07-29 12:3x, QA 계정 dev 서버). week[] 는 일기 전용 배열이 아닙니다.

    데일리질문 답변 1건을 등록하니 같은 날에 원소가 하나 더 생겼습니다.

    POST /api/v1/daily-questions  {"questionId":22, "isDraft":false, ...}
    
    GET /api/v1/mind-record?date=2026-07-27
    "diaryAmount": 1, "dailyQuestionAmount": 1,
    "week": [
      {"diaryId":7,  "day":29, "isDiary":true,  "emotion":"기쁨"},
      {"diaryId":22, "day":29, "isDiary":false, "emotion":null}    ← 데일리질문
    ]
    "daily-question": [{"title":"…", "content":"…", "date":"2026.07.29 수"}]
    
    • week 는 날짜 단위가 아니라 기록 단위다. 같은 날에 일기+데일리질문이면 같은 day 가 2개 담긴다.
    • 데일리질문 원소는 isDiary:false · emotion:null 이고, diaryId 필드에 questionId(22) 가 실려 온다.
    • daily-question[].date 포맷은 "2026.07.29 수" 다.

    이 이슈의 가설에 대해 — diaryAmount >= 1 인데 week 에 일기 원소가 없는 조합은 여전히 재현되지 않았습니다(정상 등록·임시저장 모두 일치). 다만 QA 15번의 성립 경로는 더 구체적입니다: week 에 데일리질문 원소만 담기고 일기 원소가 빠지면 count { isDiary } 가 0 이 되어 "0일" 이 나옵니다. 그 상태에서도 diaryAmount 는 1 로 표시됩니다.

    미대조 — 일기 원소가 빠지는 조건 자체는 아직 못 찾았습니다.

  3. OnlyReadOneBook commented on Aug 7, 2026

    @OnlyReadOneBook
    Collaborator

    원인 후보를 하나 더 붙입니다. 서버가 week[] 에 담아 보내도 클라가 버리는 경로가 있습니다.

    // feature/mindrecord/data/.../dto/WeeklyReportDto.kt
    @SerialName("day") val day: Int = 0,   // 일자는 1~31 — 0 은 도메인 밖 값

    day 키가 응답에 없거나 null 이면 기본값 0 이 들어갑니다. 그러면 resolveDateInWeek(monday, 0) 이 월~일 7일 창 어디에도 매칭되지 않아 null 을 돌려주고, 그 원소는 indexWeekByDate 의 mapNotNull 에서 제외됩니다. 로그도 남지 않습니다. 결과가 이 이슈의 증상과 같습니다 — diaryAmount 는 1인데 기록일수는 0.

    기본값 자체는 필요합니다. kotlinx.serialization 은 기본값 없는 프로퍼티가 JSON 에 없으면 MissingFieldException 으로 응답 전체 파싱을 실패시키므로, 기본값이 그 프로퍼티를 optional 로 만듭니다(basic-serialization.md — Optional properties). 문제는 고른 값이 도메인 밖 sentinel 이라 "서버가 안 보냄" 과 "서버가 0 을 보냄" 이 구분되지 않는다는 점입니다. Int? = null 이면 부재가 타입에 드러나 매퍼 경계에서 걸리고 로그를 남길 수 있습니다.

    명세로는 갈리지 않습니다. GET /api/v1/mind-record 의 WeekRecordItem 은 diaryId·day·isDiary·emotion 에 required 목록도 nullable 표기도 없습니다(2026-08-07 실측). day 가 항상 실려 오는지가 명세에 확정돼 있지 않습니다.

    서버 응답을 대조하실 때 week[] 원소에 day 키가 실제로 실려 오는지를 함께 보시면 이 경로를 배제하거나 확정할 수 있습니다. 담겨 오는데도 화면에서 빠진다면 여기가 원인이고, 아예 원소가 없다면 이 이슈의 기존 가설이 맞습니다.

    서버 쪽 계약 확정(day 의 required 표기 포함)은 Afternote-BE#131 로 올렸습니다.

    nullable 판단 기준 일반론은 #676 에서 다루지만, 이 DTO 는 feature/mindrecord/data 소유라 그 이슈 스코프(chore(core)) 밖입니다.

  4. Sadturtleman commented on Aug 23, 2026

    @Sadturtleman
    CollaboratorAuthor

    서버 응답 대조 결과 (2026-08-23, release)

    이슈가 "미대조" 로 남겨 둔 부분을 실서버로 확인했습니다. 조건을 하나씩 바꿔가며 GET /mind-record 응답을 직접 찍은 결과입니다.

    week[] 의 실제 규칙

    만든 상태 상단 카운트 week[]
    기록 없음 0 / 0 / 0 []
    일기 1건 일기 1 [{diaryId:9741, day:23, type:"DIARY", emotion:"HAPPY"}]
    + 임시저장 일기 1건 일기 1 (변화 없음) 변화 없음
    + 데일리질문 답변 1건 데일리질문 1 변화 없음 (DIARY 하나뿐)
    + 같은 날 일기 1건 더 일기 2 [{diaryId:9745, ...}] — 여전히 1개
    일기 전부 삭제, 데일리질문만 데일리질문 1 [{diaryId:23, day:23, type:"DAILY_QUESTION", emotion:null}]
    깊은 생각만 1건 깊은생각 1 [{diaryId:1, day:23, type:"DEEP_THOUGHT", emotion:null}]

    정리하면:

    • week[] 는 하루 1개 (최대 7개). 레코드 배열이 아니라 날짜 배열이다.
    • 같은 날 일기가 여러 건이면 가장 큰 diaryId 하나만 실린다.
    • 같은 날에 일기와 데일리질문이 함께 있으면 DIARY 가 이긴다 — 기록 순서와 무관하다 (데일리질문을 지웠다 다시 답변해 일기보다 나중 기록으로 만들어도 type 은 DIARY 였다).
    • 일기가 없는 날은 DAILY_QUESTION·DEEP_THOUGHT 가 실린다. 이때 diaryId 는 각각 userDailyQuestionId·deepThoughtId 다.
    • 임시저장은 카운트·week[] 양쪽에서 제외된다.
    • PATCH /diary/{id} 는 date 를 무시한다(Swagger DiaryUpdateRequest 에 date 없음). 날짜를 다른 주로 옮겨 카운트와 week[] 를 갈라놓는 경로는 없다.

    그래서 "불일치" 의 정체

    diaryAmount 는 레코드 수, week[] 는 날짜 수로 단위가 다릅니다. 하루에 일기를 2건 쓰면 일기 2 / 1일 이 나오는데, 이건 정상 동작입니다.

    그리고 diaryAmount ≥ 1 인데 그 날이 week[] 에 담기지 않는 상태는 재현하지 못했습니다. 일기가 있으면 그 날은 언제나 week[] 에 있었습니다. QA 15번의 "일기 1 … 0일" 은 이 조건으로는 만들어지지 않아, #563(일자 매칭)·#524(데일리질문 집계) 로 이미 해소된 증상일 가능성이 큽니다.

    다만, 대조 중에 같은 계열의 클라 결함을 하나 찾았습니다

    countRecordedDays 가 week[] 에서 isDiary 인 날만 세고 있었습니다.

    val diaryDates = aggregateWeekRecordsByDate(monday, week)
        .filterValues { it.isDiary }   // ← DAILY_QUESTION·DEEP_THOUGHT 원소가 여기서 버려진다
        .keys

    데일리질문만 있는 날은 daily-question[] 로 되살아나지만, 깊은 생각만 있는 날은 어디에서도 복구되지 않습니다. 위 표의 마지막 행 상태(깊은 생각 1건)에서 화면은 이렇게 나왔습니다.

    WEEKLY SUMMARY | 8월 3주차 리포트 | 2026.08.17. - 2026.08.23.
    이번 주, adamtia님은 0일의 마음을 기록하셨네요.
    

    서버 week[] 에는 8/23 기록이 있는데 요약은 "0일" 입니다 — 이슈 제목이 말하는 "카운트와 요약 기록일수 불일치" 와 같은 뿌리입니다.

    수정 범위는 방어 로직이 아니라 집계 규칙으로 정했습니다. 문구가 세는 것은 "일기를 쓴 날" 이 아니라 "마음을 기록한 날" 이므로 종류를 가리지 않습니다. isDiary 는 달력 점 표시에만 계속 씁니다. PR 을 올려 두었습니다.

    수정 후 같은 서버 상태에서 0일 → 1일 로 바뀌고, 같은 날 일기를 추가해도 일기 1 / 1일 로 이중 계산되지 않는 것을 확인했습니다.

  5. added a commit that references this issue on Aug 24, 2026
    45d7f5c
  6. Sadturtleman commented on Aug 24, 2026

    @Sadturtleman
    CollaboratorAuthor

    보강 — 깊은 생각이 기획에서 완전히 제거되기로 하면서 PR #946 의 집계 규칙을 한 번 더 좁혔습니다.

    위 대조에서 «깊은 생각만 있는 날이 0일로 샌다» 를 결함으로 적었는데, 그 기능 자체가 없어지므로 이제는 세지 않는 것이 맞습니다. 서버는 week[].type=DEEP_THOUGHT 와 deepThoughtAmount 를 계속 내려주지만 앱은 무시합니다.

    수정 방향은 «종류를 가리지 않고 센다» → «깊은 생각만 제외하고 센다» 로 바꿨고, 종류 판단은 매퍼가 countsAsRecord 로 접어 도메인에 올립니다. isDiary 는 달력 점 표시 전용으로 남습니다.

    대조 결과 자체(«week[] 는 하루 1개인 날짜 배열», «diaryAmount ≥ 1 인데 week[] 미포함은 재현되지 않음»)는 그대로입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions