Skip to content

refactor(dto): 응답 DTO 를 BE 실코드에 동기화 — 유령 필드 9건·누락 필드 21건 #676

Closed
4 of 4 issues completed

Description

@OnlyReadOneBook

2026-08-24 재정의. 최초 전제("nullable 판단 기준이 없어 한 DTO 안에서 세 방식이 섞였다")는 BE 실코드 대조로 기각됐다.
actions·leaveMessage·credentials·playlist 넷 모두 BE 가 @Schema(nullable = true) 로 명시한 조건부 필드고
FE 가 맞게 반영하고 있었다. 최초 근거였던 "어느 필드에도 nullable 표기가 없다" 는 2026-08-02 Swagger 기준인데,
그 뒤 BE #189 → PR #221 이 해당 응답의 명세를 정비했다.

대신 필드 구성 자체가 어긋난 곳 30건이 나왔다. 이슈를 그 동기화로 재정의한다.

FE 응답 DTO 와 BE 응답 record 의 필드 구성이 어긋난 곳이 30건이다.
서버가 보내는 값을 화면이 못 쓰거나(누락 21), 영영 오지 않을 값을 기다린다(유령 9).


판정 방법

OpenAPI 문서는 근거가 못 된다. BE 응답 record 65개 중 requiredMode 를 명시한 건 2개뿐이다.
근거는 BE 실코드, 특히 배포본 origin/release 다.

git -C <BE> branch -r --contains <필드추가커밋>     # release 에 들어갔나 = 배포됐나
git -C <BE> show origin/release:<파일>              # 배포본 실물

Jackson NON_NULL 설정이 없어 record 에 있으면 키가 항상 나가고, 없으면 절대 안 온다.
값이 null 일 수 있는지는 서비스 조립부로 판정한다 (Boolean.TRUE.equals(...) 면 null 불가).

dev 서버는 02시대 후반~12시(KST) 닫힌다. 서버가 닫혀 있어도 위 경로로 계약은 확정된다.

2026-08-24 13:21 배포 서버 /v3/api-docs 실측으로 아래 판정을 교차 검증했다 (인증 불필요, curl -k).
AfternotedetailResponse.required = [afternoteId, category, isDraft, receivers, title, updatedAt] —
#972 의 isDraft·receivers non-null 화와 createdAt 제거가 배포 스키마와 일치한다.
AfternoteReceiverResponse 는 3필드로 phone 이 없어 그 제거도 확인됐다.


⚠ 누락 필드를 전부 받는 것이 답이 아니다

"BE record 에 있으면 FE 가 받는다"로 일괄 적용했다가 두 건을 되돌렸다. 안 받는 것이 의도인 경우가 있다.

받을지 말지는 화면이 그 값을 쓰는지로 정한다. 안 쓸 값이면 "안 받는다"가 결론이고,
그 판단은 커밋 메시지에 남긴다 — 코드 주석에 계약을 복제하면 BE 가 바뀔 때 FE 주석만 낡아 거짓이 된다
(Detail.kt 의 계약 주석 3종이 실제로 그렇게 낡아 삭제됐다).


현황

core — 1hyok

FE 어긋남 상태
SocialLoginDto FE newUser ↔ 서버 isNewUser 🔴 실해 확인 — 아래
PresignedUrlDto contentLength maxContentLength 미수신 보류 — 배포 스키마에 존재하나 쓰는 데 없음, #953 과 같은 파일

🔴 SocialLoginDto.isNewUser — 소셜 신규 가입자가 온보딩을 건너뛴다

배포 스키마 SocialLoginResponse 의 키는 isNewUser 인데 FE 는 @SerialName("newUser") 로 읽는다.
Boolean? = null 이라 파싱은 조용히 통과하고 값만 항상 null 이 된다.

// LoginUseCase.kt:61 — null == true → false
(session as? Session.SocialSession)?.isNewUser == true

// LoginViewModel.kt:112 — 그래서 이 분기가 영영 안 탄다
.onSuccess { isNewUser -> if (isNewUser) it.copy(shouldStartOnboarding = true) }

BE 는 처음부터 isNewUser 였고(2aba678 이전), FE 가 29f409ba(2026-05-09)에 newUser 로 적었다.
약 3개월 반 동안 소셜 로그인 신규 가입자가 온보딩을 못 밟았다.

afternote — 1hyok

FE 어긋남 상태
AfternoteDetailDto 유령 createdAt · 누락 isDraft · receivers nullable ✅ #972
AfternoteDetailReceiverDto 유령 phone ✅ #972
AfternoteListItemDto 누락 isDraft ✅ #972
ReceivedAfternoteDetailDto 유령 credentials 판단 필요 — 아래
AfternoteSongDto 유령 id 미확인 — FE 내부 선택 키일 수 있다(#823)
ReceiverAuthPresignedUrlDto contentLength maxContentLength 미수신 보류 — core 와 같은 사유

mindrecord — @Sadturtleman → #789 로 이관

receivers(5곳) · yearMonth · emotion · updatedAt 누락.
imageUrl 유령 4건과 deepThoughtAmount·emotionAnalysis 는 이미 판정됐거나 의도적이라 대상이 아니다.
→ #789 코멘트

timeletter — @koongmai → #790 으로 이관

TimeLetterDto.deliveredAt 유령 1건. → #790 코멘트


판단이 필요한 것 — ReceivedAfternoteDetailDto.credentials

배포 스키마 ReceivedAfternoteDetailResponse 는 8필드(id·category·title·actions·leaveMessage·senderName·createdAt·playlist)로
credentials 가 아예 없다(2026-08-24 실측). FE 는 기대하고 매퍼까지 연결해 뒀다.

단순 유령 제거로 지우면 수신자가 SOCIAL·BUSINESS 애프터노트의 계정 정보를 못 보는 상태가 고착된다.
제품 의도가 "수신자도 계정 정보를 열람한다"라면 이건 FE 결함이 아니라 BE 미구현이고, 서버에 요청할 사안이다.
449aaf31(상세 credentials 부분 응답 허용)이 이 값을 다룬 이력이 있어 의도 확인이 선행돼야 한다.


산출물 — 보정형 기본값 재발 가드

ResponseDtoContractKonsistTest (#972). 응답 DTO 의 non-null 프로퍼티에 보정형 기본값
(= "" · = 0 · = false · = emptyList())을 금지한다 — 서버가 키를 빼도 파싱이 성공해 계약 누락이 은폐되는 조합이고,
AfternoteDetailDto.createdAt = "" 이 그 실패 모양이었다.

실측 시점 잔여 38건은 담당 이슈별로 등재했다 — #789 26 · #957 11 · #790 1.
해소된 항목이 목록에 남아 있어도 실패시켜 목록이 썩지 않게 했다.


재현

FE 와이어 키(@SerialName·@JsonNames) ↔ BE record 필드(@JsonProperty)를 대조한다.
클래스명이 달라 필드 유사도로 매칭한 쌍은 대응 관계부터 확인할 것 —
AfternoteDetailReceiverDto ↔ UserPatchReceiverResponse 로 잘못 붙어 phone 유령을 놓치고 email 누락을 오탐한 전례가 있다.
BE 가 요청 record 를 응답에 재사용하는 곳도 있다(AfternotedetailResponse.playlist 의 타입이 AfternoteCreateRequest.PlaylistRequest).

Activity

  1. OnlyReadOneBook commented on Aug 3, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    적용 사례가 하나 더 생겼습니다 — #699 (일기 목록 DTO).

    DiaryListItemDto 가 diaryId·createdAt·todayMood 를 필수로 요구해서, 항목 하나가 어긋나면 MissingFieldException 으로 그 달 목록이 통째로 날아갑니다. #699 의 수정 방향은 "식별자 외에는 기본값을 두고 todayMood 는 nullable 로 내린다" 인데, 이 이슈가 세우려는 세 갈래 기준(항상 오는 값 → non-null / 없을 수 있는 값 → nullable / 빈 값으로 표현 가능 → 기본값)과 같은 판단입니다.

    기준이 나중에 다르게 정해지면 재작업이 생기므로, 이 이슈에서 기준을 확정할 때 DiaryListItemDto 를 검증 사례로 함께 훑는 것이 좋겠습니다. #699 쪽에도 같은 내용을 남겨 두었습니다.

  2. changed the title [-]chore(core): 응답 DTO nullable 판단 기준 부재 — 같은 DTO 안에서 세 방식 혼재, 명세도 근거 아님[/-] [+]refactor(dto): 응답 DTO 를 BE 실코드에 동기화 — 유령 필드 9건·누락 필드 21건[/+] on Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingenhancementNew feature or request

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions