You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
같은 "아이템" 개념이 엔드포인트마다 다른 모양으로 나간다. 클라이언트가 이를 발견해 물어봤다.
응답
클라가 보는 것
GET /tournaments/{id}/items/{itemId}
name?: string — 키 생략
POST /tournaments/{id}/start
name: string | null
GET /wishes
name: string | null
토너먼트를 시작하면 null 로 받고, 같은 아이템을 상세 조회하면 키가 사라진다. 클라가 같은 데이터를 두 가지 방식으로 파싱해야 한다.
키가 없으면 클라는 "서버가 검증한 빈 값"인지 "필드가 누락된 것"인지 구분할 수 없다.null 은 그 자체가 정보지만 부재는 정보가 아니다. 그래서 required + nullable 이 낫다.
원인
@JsonInclude(NON_NULL) 이 tournament 5개 DTO 13곳에만 붙어 있고, 전역 설정이 없다. 작업자마다 판단이 갈릴 수밖에 없는 구조였고, 실제로 같은 tournament 도메인 안에서도 TournamentStartResponse 만 빠져 있어 시작 응답과 상세 응답의 모양이 다르다.
소수파가 tournament 쪽이고, 다수파가 이미 required + nullable 이라 통일 비용도 그쪽이 싸다.
이 판단을 되돌리는 이력
#835 에서 CodeRabbit 이 같은 불일치를 지적했을 때, "레포 전체 응답 DTO 가 NON_NULL 규약" 이라는 잘못된 근거로 생략을 계약으로 확정했다. 실제로는 tournament 5개 DTO 뿐이었다. 그때 박은 "레포 공통 규약" 주석(RecordMatchResponse.kt)도 근거 없는 서술이므로 함께 걷어낸다.
재발 방지 — 응답 DTO 의 nullable 처리 방식이 작업자 판단에 맡겨지지 않도록 한 곳에 고정한다
범위 밖
클라이언트 타입 변경(TeamPiKi/client). 응답 계약이 바뀌므로 배포 순서를 함께 잡아야 한다. name?: string 을 전제한 타입은 name: string | null 로 바뀌어야 하고, 옵셔널 체이닝으로 접근하던 코드는 대체로 무해하지만 확인이 필요하다.
왜
같은 "아이템" 개념이 엔드포인트마다 다른 모양으로 나간다. 클라이언트가 이를 발견해 물어봤다.
GET /tournaments/{id}/items/{itemId}name?: string— 키 생략POST /tournaments/{id}/startname: string | nullGET /wishesname: string | null토너먼트를 시작하면
null로 받고, 같은 아이템을 상세 조회하면 키가 사라진다. 클라가 같은 데이터를 두 가지 방식으로 파싱해야 한다.키가 없으면 클라는 "서버가 검증한 빈 값"인지 "필드가 누락된 것"인지 구분할 수 없다.
null은 그 자체가 정보지만 부재는 정보가 아니다. 그래서required + nullable이 낫다.원인
@JsonInclude(NON_NULL)이 tournament 5개 DTO 13곳에만 붙어 있고, 전역 설정이 없다. 작업자마다 판단이 갈릴 수밖에 없는 구조였고, 실제로 같은 tournament 도메인 안에서도TournamentStartResponse만 빠져 있어 시작 응답과 상세 응답의 모양이 다르다.NON_NULL)null로 내려감 (Jackson 기본)PageResponse·TournamentStartResponse소수파가 tournament 쪽이고, 다수파가 이미
required + nullable이라 통일 비용도 그쪽이 싸다.이 판단을 되돌리는 이력
#835 에서 CodeRabbit 이 같은 불일치를 지적했을 때, "레포 전체 응답 DTO 가 NON_NULL 규약" 이라는 잘못된 근거로 생략을 계약으로 확정했다. 실제로는 tournament 5개 DTO 뿐이었다. 그때 박은 "레포 공통 규약" 주석(
RecordMatchResponse.kt)도 근거 없는 서술이므로 함께 걷어낸다.무엇을
@JsonInclude(NON_NULL)13곳 제거 —RankedItemResponse·RecordMatchResponse·TournamentItemDetailResponse·TournamentDetailResponse(중첩 7곳 포함) ·GroupResultResponsedoesNotExist가 64건 있지만 JSONPath 필터([?(@.name == ...)])·쿠키·$.code처럼 무관한 것이 섞여 있어 미리 분류하지 않고 실측으로 가른다TournamentApi의 OpenAPI 설명 ·TournamentApiExamples의 example범위 밖
클라이언트 타입 변경(
TeamPiKi/client). 응답 계약이 바뀌므로 배포 순서를 함께 잡아야 한다.name?: string을 전제한 타입은name: string | null로 바뀌어야 하고, 옵셔널 체이닝으로 접근하던 코드는 대체로 무해하지만 확인이 필요하다.