왜
호출자(core)가 INCOMPLETE 상태를 도입해 부분값을 받아 사용자가 나머지를 채우는 흐름으로 간다 (TeamPiKi/core#944). 그런데 지금은 응답 경계가 세 필드(name·imageUrl·currentPrice) 중 하나라도 비면 422 UNTRUSTWORTHY_VALUE 로 닫아, 채운 값까지 함께 버린다.
// ExtractionResponse.java:33
if (snapshot.missingReadyField()) {
throw ProductSnapshotException.untrustworthyValue();
}
prod 30일 이미지 추출 18건 중 16건이 이 게이트로 실패했다. 사용자가 직접 찍은 사진이나 가격이 박혀 있지 않은 제품컷에는 가격이 존재하지 않으므로, 이 계약은 "쇼핑몰 화면을 캡처한 것"만 통과시킨다.
이미지 경로에는 별개의 버그도 있다. ImageCropper 가 ImageIO 로 디코딩하는데 png·jpeg 만 지원해 나머지는 크롭을 건너뛴다(의도된 fallback). 그런데 업로드는 항상 items/{uuid}.png · image/png 로 하드코딩돼 있어, HEIC 바이트가 png 로 위장돼 저장된다. 브라우저 대부분이 렌더링하지 못한다. presign 이 허용하는 5개 포맷(png·jpeg·webp·heic·heif) 중 셋이 해당된다.
실측: prod items/fcc2600e-...png 는 확장자·content-type 이 png 인데 실제 바이트는 HEIC 다.
무엇을
- 응답 경계를 부분값 허용으로 바꾼다. 값이 하나라도 있으면 200 으로 그대로 내려보내고, 하나도 없을 때만 422
UNTRUSTWORTHY_VALUE 로 닫는다. 응답 필드는 이미 nullable 이라 스키마 변경은 없다
- 에스컬레이션 판정은 그대로 둔다.
FallbackProductLinkExtractor 가 부분값일 때 헤드리스로 한 번 더 시도하는 것은 여전히 옳다 — 달라지는 건 "그래도 부분값이면 어떻게 끝내는가" 뿐이다
- 크롭 불가 포맷의 위장 업로드를 고친다. 크롭이 실제로 일어났을 때만 png 로 올리고, 원본을 그대로 올릴 때는 원본의 mimeType·확장자를 따른다 (
ProductImage.mimeType() · extension() 이 이미 있다)
- 부분값으로 내려보낼 때 무엇이 비었는지 로그에 남긴다. 지금은
UNTRUSTWORTHY_VALUE 만 남아 name 이 없었는지 price 가 없었는지 사후 판별이 불가능하다
배포 순서
core 가 먼저다. core 는 부분값 200 을 받을 준비(INCOMPLETE 전이)가 된 뒤에도 기존 422 를 그대로 처리하므로, core 배포는 단독으로 안전하다. 반대로 이 변경이 먼저 나가면 core 가 부분값을 READY 불변식 위반으로 받아 FAILED 로 떨어뜨린다.
왜
호출자(core)가
INCOMPLETE상태를 도입해 부분값을 받아 사용자가 나머지를 채우는 흐름으로 간다 (TeamPiKi/core#944). 그런데 지금은 응답 경계가 세 필드(name·imageUrl·currentPrice) 중 하나라도 비면 422UNTRUSTWORTHY_VALUE로 닫아, 채운 값까지 함께 버린다.prod 30일 이미지 추출 18건 중 16건이 이 게이트로 실패했다. 사용자가 직접 찍은 사진이나 가격이 박혀 있지 않은 제품컷에는 가격이 존재하지 않으므로, 이 계약은 "쇼핑몰 화면을 캡처한 것"만 통과시킨다.
이미지 경로에는 별개의 버그도 있다.
ImageCropper가ImageIO로 디코딩하는데 png·jpeg 만 지원해 나머지는 크롭을 건너뛴다(의도된 fallback). 그런데 업로드는 항상items/{uuid}.png·image/png로 하드코딩돼 있어, HEIC 바이트가 png 로 위장돼 저장된다. 브라우저 대부분이 렌더링하지 못한다. presign 이 허용하는 5개 포맷(png·jpeg·webp·heic·heif) 중 셋이 해당된다.실측: prod
items/fcc2600e-...png는 확장자·content-type 이 png 인데 실제 바이트는 HEIC 다.무엇을
UNTRUSTWORTHY_VALUE로 닫는다. 응답 필드는 이미 nullable 이라 스키마 변경은 없다FallbackProductLinkExtractor가 부분값일 때 헤드리스로 한 번 더 시도하는 것은 여전히 옳다 — 달라지는 건 "그래도 부분값이면 어떻게 끝내는가" 뿐이다ProductImage.mimeType()·extension()이 이미 있다)UNTRUSTWORTHY_VALUE만 남아name이 없었는지price가 없었는지 사후 판별이 불가능하다배포 순서
core 가 먼저다. core 는 부분값 200 을 받을 준비(
INCOMPLETE전이)가 된 뒤에도 기존 422 를 그대로 처리하므로, core 배포는 단독으로 안전하다. 반대로 이 변경이 먼저 나가면 core 가 부분값을 READY 불변식 위반으로 받아 FAILED 로 떨어뜨린다.