Skip to content

fix(mindrecord): 일기 목록 응답 파싱·날짜 해석 정정 — date/createdAt 분리, 오늘 폴백 제거, 임시저장 제외 #699

Closed

Description

@Sadturtleman

📜 Overview (Required)

2026-08-03 재조정. 최초 제목·본문은 "저장 후 목록 0건"(2차 QA 2026-07-28 18번)을 다뤘으나, 라이브 실측에서 재현되지 않았습니다. 저장→목록 왕복이 정상이고(POST /diary 200 직후 GET /diary?yearMonth= 에 바로 잡힘), 실제 응답은 diaryId 를 쓰므로 "명세 형태(id)가 와서 목록이 죽는다" 는 원인 추정도 틀렸습니다. 원 QA 시점의 서버 상태를 알 수 없어 확정이 불가하므로, 이 이슈는 실측으로 뒷받침되는 범위로 좁힙니다. 0건 증상이 다시 관측되면 그때의 응답 캡처와 함께 새 이슈로 여는 편이 맞습니다.

GET /diary 목록 응답의 파싱과 날짜 해석이 실제 서버 계약과 어긋난다. 필드 하나가 어긋나면 그 달 목록이 통째로 날아가고, 날짜는 조용히 오늘로 폴백하며, 임시저장이 캘린더에 섞인다.

라이브 응답 (2026-08-03 실측)

{ "diaryId": 4, "title": "QA diary title", "content": "<p>...</p>",
  "isDraft": false, "emotion": null, "todayMood": "HAPPY",
  "date": "2026-07-19", "createdAt": "2026.07.19 일", "updatedAt": "2026.07.19 일",
  "receivers": [] }

① date 와 createdAt 은 별개 필드인데 한 프로퍼티에 묶여 있다

date 는 사용자가 작성 화면에서 고른 일기 날짜(ISO), createdAt 은 레코드 생성 시각(한국어 포맷)이다. 형식까지 다르다. 한 프로퍼티에 @JsonNames 로 묶으면 서버 키 순서에 따라 값이 뒤바뀌고, 무엇보다 캘린더에 찍어야 하는 값은 date 인데 코드는 createdAt 을 쓴다 — 지난 날짜로 쓴 일기가 작성한 날 칸에 찍힌다. 작성 화면에 날짜 선택 UI(onDateSelected)가 있으므로 실제로 갈린다.

② 날짜가 조용히 오늘로 폴백한다

MindRecordUiMapper.parseLocalDate 가 실패하면 ?: LocalDate.now() 로 떨어져 날짜 없는 기록이 오늘 칸에 정상 기록처럼 앉는다. 서버 필드 누락이 화면에서 감춰진다.

③ 값 하나가 그 달 목록 전체를 죽인다

DiaryListItemDto 가 title·content·createdAt·todayMood 를 필수로 요구한다. 항목 하나라도 어긋나면 MissingFieldException 으로 그 달 목록이 통째로 날아간다. 클라 enum(HAPPY·SOSO·SAD)에 없는 기분 값도 같은 결과가 된다 — coerceInputValues 는 기본값이 있어야 동작하는데 이 프로퍼티엔 기본값이 없다.

④ 임시저장 일기가 캘린더 목록에 섞인다

서버가 isDraft 를 주는데 클라가 버리고 있다. GET /diary 는 draftOnly 를 생략하면 그 달 전체(임시저장 포함)를 내려주므로 클라가 걸러야 한다. 같은 모듈의 DailyQuestionListViewModel·ReceiverMindRecordViewModel 은 이미 거르고 있어 일기만 새어 들어온다.

수정 방향

  1. date 를 독립 프로퍼티로 분리하고, 표시 날짜는 date ?: createdAt 순으로 고른다.
  2. 두 날짜가 모두 없거나 해석 불가면 오늘로 메우지 말고 항목을 뺀다.
  3. 식별자를 뺀 나머지에 기본값을 두고 todayMood 를 nullable 로 내린다.
  4. isDraft 를 도메인까지 올리고 일기 목록·추억 공간에서 거른다.
  5. 위 응답 형태들을 계약 테스트로 고정한다.

📌 Child Issue(Optional)

No response

📍 Note (Optional)

  • 원 증상("저장 후 목록 0건")은 이 이슈에서 추적하지 않는다. 위 재조정 사유 참고. 재관측 시 응답 캡처와 함께 새 이슈로 연다.
  • @JsonNames("id")(노션 명세 형태)와 ISO_DATE_TIME 은 현재 서버에서 쓰이지 않는 순수 방어다. 두 문서가 갈려 있어 함께 받아 두는 쪽을 골랐다.
  • 작업 중 확인된 별건 — 일기 날짜 수정이 서버에 반영되지 않고(DiaryUpdateRequest 에 date 없음), 일기 imageUrl 이 전 계층에서 동작하지 않는다(DiaryCreateRequest·DiaryUpdateRequest·DiaryResponse 어디에도 없음). 각각 별도 이슈가 필요하다.
  • fix(mindrecord): 기록 탭 목록이 저장 후 복귀 시 미갱신 — 탭 전환 시에만 refresh #520(저장 후 복귀 시 미갱신)은 재조회 자체가 나가지 않던 문제로 이미 닫혔다.

Activity

  1. OnlyReadOneBook commented on Aug 3, 2026

    @OnlyReadOneBook
    Collaborator

    임시저장 목록도 이 이슈의 영향 범위에 들어갑니다. DraftListViewModel.kt:115 가 diaryRepository.getList(yearMonth, draftOnly = true) 로 같은 DiaryListItemDto 를 타므로, 여기서 파싱이 깨지면 임시저장 목록도 통째로 0건이 됩니다.

    같은 화면의 재조회 결선 누락은 #702 로 분리해 두었습니다 — DraftListViewModel.kt:45 fun refresh() = load() 가 정의만 있고 호출부가 0건이라, 이 이슈가 해소돼도 화면 재진입 시 재조회 자체가 나가지 않습니다. 두 건이 모두 해소되어야 "저장한 항목이 목록에 보인다" 가 성립해서 #702 에 이 이슈를 선행으로 걸었습니다.

    경계는 이렇게 잡았습니다 — 이 이슈는 "재조회는 나가는데 결과가 0건", #702 는 "재조회가 아예 나가지 않음".

  2. OnlyReadOneBook commented on Aug 3, 2026

    @OnlyReadOneBook
    Collaborator

    이 이슈가 미확인으로 남긴 todayMood 값 문제에 서버 실측 근거가 이미 있습니다 — #591 입니다.

    #591 이 2026-07-29 dev 서버에서 확인한 바로는, 같은 diaryId 인데 등록 직후와 이후의 값이 다릅니다.

    POST /api/v1/diary  {"todayMood":"HAPPY", ...}
    → 등록 직후   "week":[{"diaryId":7,"emotion":"HAPPY"}]
    → 수 분 뒤    "week":[{"diaryId":7,"emotion":"기쁨"}]   (재조회 2회 동일)
    

    즉 실제로 오는 값은 명세 예시의 SMILE 이 아니라 한글 기쁨 이고, 서버가 저장 후 어느 시점에 값을 바꿉니다(시점·주체는 #591 에서도 미확인).

    두 이슈는 같은 TodayMoodDto(HAPPY·SOSO·SAD) 의 값 불일치를 서로 다른 지점에서 잡은 것입니다.

    같은 원인에 파급만 다르므로, 한쪽에서 enum 수용 방식을 정하면 다른 쪽도 같이 풀립니다.

    DTO nullable 기준은 #676 에서 정하는 중입니다. 이 이슈의 수정 방향 1번("식별자 외에는 기본값을 둔다, todayMood 는 nullable 로")이 #676 이 세우려는 기준과 같은 판단이라, 기준이 나중에 다르게 정해지면 재작업이 생깁니다. 급한 파싱 방어를 먼저 넣더라도 #676 쪽에 결론을 남겨 주시면 기준을 그쪽에 맞춰 정리하겠습니다.

  3. Sadturtleman commented on Aug 3, 2026

    @Sadturtleman
    CollaboratorAuthor

    서버 복구 후 Swagger 실측 (2026-08-03) — afternote.kro.kr/v3/api-docs 기준으로 이슈 본문의 판단을 일부 정정합니다.

    1. id/diaryId — Swagger 는 diaryId 입니다. 노션 명세의 id 는 Swagger 에 없습니다. 두 문서가 갈려 있어 대체 키로 함께 받는 편이 안전하지만, 본문에 적은 "명세 형태가 와서 목록이 통째로 죽는다" 는 근거로는 약합니다.

    2. date 와 createdAt 은 별개 필드였습니다 — 이쪽이 더 큰 문제입니다.

    DiaryResponse
      diaryId    integer  int64
      date       string   format=date    ← 사용자가 고른 일기 날짜
      createdAt  string                  ← 레코드 생성 시각
      updatedAt  string
      todayMood  string   enum=[HAPPY, SOSO, SAD]
      emotion    string   (enum 없음)
      isDraft    boolean
    

    코드는 createdAt 을 캘린더 날짜로 쓰고 있습니다. 작성 화면에 날짜 선택 UI 가 있으므로 지난 날짜로 쓴 일기는 작성한 날 칸에 찍힙니다. 두 필드를 @JsonNames 로 묶으면 서버 키 순서에 따라 값이 뒤바뀌기까지 합니다. PR #720 에서 분리했습니다.

    3. 응답 스키마에 required 가 하나도 없습니다. 전체 164개 스키마 중 required 를 선언한 36개는 전부 Request 스키마입니다. 응답은 어느 필드든 생략될 수 있다는 뜻이라, 식별자 외 기본값을 두는 방향은 그대로 유효합니다.

    4. todayMood 의 "SMILE" — Swagger enum 은 HAPPY/SOSO/SAD 뿐입니다. 노션 명세 예시의 "SMILE" 은 문서 오류로 보입니다. nullable 화는 required 부재(3번)를 근거로 유지합니다.


    여전히 미확정: "0건" 자체의 원인. 계정 자격이 없어 인증 엔드포인트를 못 불렀습니다 (GET /api/v1/diary → 401). 로그인 가능한 분이 아래를 한 번 떠서 붙여 주시면 확정됩니다.

    GET /api/v1/diary?yearMonth=2026-08
    

    추가 발견 (이 이슈 범위 밖)

  4. Sadturtleman commented on Aug 3, 2026

    @Sadturtleman
    CollaboratorAuthor

    라이브 서버 실측 (2026-08-03) — 보고된 "0건" 은 현재 서버에서 재현되지 않습니다.

    실계정으로 저장→목록 왕복을 그대로 재현했습니다. 클라가 보내는 형태 그대로입니다.

    POST /api/v1/diary
    {"title":"...","content":"<p>...</p>","isDraft":false,"todayMood":"HAPPY"}
    → 200 { "diaryId": 13, ... }
    
    GET /api/v1/diary?yearMonth=2026-08
    → 200 { "diaries": [ { "diaryId": 13, "date": "2026-08-03", ... } ], "monthDiaryCount": 1 }
    

    저장 직후 조회에 바로 잡힙니다. 서버 왕복도, 응답 파싱도 정상입니다. 따라서 이 이슈 본문에 제가 적은 "명세 형태(id·date)가 와서 목록이 통째로 죽는다" 는 실제 원인이 아닙니다. 실제 응답은 diaryId 를 쓰고, 기존 DTO 로도 파싱됩니다. (테스트로 만든 diaryId 13 은 DELETE 로 정리했습니다.)

    실제 응답 형태 (기존 일기, diaryId 4)

    { "diaryId": 4, "title": "QA diary title", "content": "<p>...</p>",
      "isDraft": false, "emotion": null, "todayMood": "HAPPY",
      "date": "2026-07-19", "createdAt": "2026.07.19 일", "updatedAt": "2026.07.19 일",
      "receivers": [] }
    • date 는 ISO, createdAt 은 한국어 포맷(2026.07.19 일) 입니다 — 둘은 형식까지 다른 별개 필드입니다.
    • imageUrl 은 응답에 없습니다.

    이 이슈 재조정 제안

    보고된 0건은 재현되지 않으므로, PR #720 은 "0건 수정" 이 아니라 응답 파싱 견고화 + 날짜 필드 정정 으로 봐 주시는 게 맞습니다. 그중 실질이 있는 것은:

    1. date / createdAt 분리 — 한 프로퍼티에 묶으면 서버 키 순서에 따라 값이 뒤바뀝니다. 그리고 캘린더가 써야 하는 값은 date 입니다.
    2. isDraft 수신 — 서버가 주는데 클라가 버리고 있었습니다.
    3. todayMood nullable + 기본값 — 응답 스키마에 required 가 전혀 없어(전체 164개 중 required 선언 36개는 모두 Request) 어느 필드든 생략될 수 있습니다.

    원 QA(2026-07-28 2차 QA 18번)에서 왜 0건이었는지는 그날의 서버 상태를 알 수 없어 확정 불가입니다. 참고로 현재 diaryId 시퀀스에 5~12 구간이 어느 달 조회에도 안 잡히는데, ID 가 전역 시퀀스라 다른 사용자 레코드일 수 있어 근거로 삼지 않았습니다.


    작업 중 확인된 별건 (실측 근거 있음)

    ① 일기 날짜 수정이 서버에 반영되지 않습니다. 작성/수정 화면에 날짜 선택 UI(onDateSelected)가 있고 클라는 PATCH 에 date 를 싣지만, Swagger DiaryUpdateRequest 에 date 가 없고 실제로도 무시됩니다.

    PATCH /api/v1/diary/13  {"...","todayMood":"SAD","date":"2026-08-01","imageUrl":null}
    → 200, todayMood 는 SAD 로 반영됨. date 는 "2026-08-03" 그대로.
    

    ② 일기 imageUrl 은 전 계층에서 동작하지 않습니다. DiaryCreateRequest·DiaryUpdateRequest·DiaryResponse 어디에도 없는데 클라는 요청에 싣고 응답에서 읽으려 합니다 (데일리질문 #549 의 일기 버전).

    ③ week[].emotion 이 한글로 옵니다 — #591 의 서버측 근거입니다.

    "week": [ { "diaryId": 13, "day": 3, "isDiary": true, "emotion": "슬픔" } ]
  5. changed the title [-]fix(mindrecord): 일기 저장 후 목록 0건 — 목록 DTO 가 명세 키(id·date)를 못 받고 날짜는 오늘로 폴백[/-] [+]fix(mindrecord): 일기 목록 응답 파싱·날짜 해석 정정 — date/createdAt 분리, 오늘 폴백 제거, 임시저장 제외[/+] on Aug 13, 2026
  6. added a commit that references this issue on Aug 16, 2026
    41702c8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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