상품 게시
작성: seokjin8678PublishedProduct
Section titled “PublishedProduct”외부에 게시된 상품을 추적할 수 있는 식별자를 가지고 있으며, 상품이 동시에 2개 이상 게시되지 않도록 제어를 해주는 엔티티입니다.
productId,provider 유니크 인덱스를 가지고 있으며, 해당 유니크 인덱스를 통해 중복된 엔티티가 생기는 것을 방지합니다.
ProductPublishClient의 요청이 성공하면 providerResourceId를 갱신하며, 이때 최종적으로 게시가 완료된 상품으로 구분됩니다.
게시 상태를 가지고 있으며, WAITING -> PENDING -> SUCCESS/FAILED 상태로 변경됩니다.
상태가 변경될 때는 반드시 이전 상태가 보장되어야 하며, 이전 상태가 아닐 경우 UPDATE 되지 않습니다. (낙관적 락)
FAILED의 경우SUCCESS상태에서도 전환될 수 있기에, 이전 상태를 보장하지 않습니다.
이를 통해 단 한 건의 요청만 상태를 변경할 수 있도록 하여, 외부에 상품이 중복으로 등록되는 것을 방지합니다.
만약 이를 무시하고 강제로 상태를 변경해야 하는 경우, “Force”가 붙은 메서드를 사용해야 합니다.
ProductPublishClient
Section titled “ProductPublishClient”상품의 외부 게시를 담당하는 인터페이스이며, 2개의 추상 메서드 validatePublish, publish를 가지고 있습니다.
validatePublish는 동기적인 요청에서 사용될 수 있으며, 사용자가 상품 등록을 요청할 때, 등록하려는 상품의 정보가 외부의
요구사항을 충족하는지 미리 검사하여 사용자에게 빠른 피드백을 줄 목적으로 설계되었습니다.
publish은 실제 상품을 외부에 게시하며, 외부에 게시된 상품에 접근할 수 있는 식별자를 반환합니다.
해당 메서드는 외부 API를 호출하므로, 절대 트랜잭션 범위 내에서 호출하면 안 됩니다.
ProductPublishJob
Section titled “ProductPublishJob”상품 게시는 외부 API를 호출하기 때문에 필연적으로 시간이 오래 걸릴 수 밖에 없으며, 이 때문에 동기적인 요청에 사용하게 되면, 응답 시간이 매우 길어지는 문제가 발생할 수 있습니다.
또한 성공률이 100%가 아니므로, 이를 단순히 비동기로 실행한다면 부분 실패가 발생할 수 있기에, JobQueue를 통해 해당 로직을 실행합니다.
해당 Job에서 ProductPublishClient::publish를 호출하며, 성공/실패 여부에 따라 PublishedProduct의 상태를 변경합니다.
멱등하지는 않으나, PublishedProduct의 상태를 변경하는 로직으로 인해 ProductPublishClient::publish를 여러 번 호출하는 문제를
방지하기 때문에 동시에 호출되거나, 재시도 되더라도 동시성 문제는 발생하지 않습니다.
또한 재시도 가능한 예외와 재시도 불가능한 예외의 구분이 필요하며, 2025.10.05 기준 모두 재시도 불가능한 예외로 판단하여 WAITING 상태로 변경하지 않기에
JobQueue에서 재시도하더라도 실제 재시도 처리를 하지 않습니다.
상품 게시 실패
Section titled “상품 게시 실패”상품 게시는 외부 API 호출이기 때문에 성공한다는 보장이 없습니다.
호출에 실패하면 PublishedProduct는 FAILED 상태로 변경됩니다.
실패에는 네트워크 문제, 외부 서버의 일시적 장애 같이 재시도 하면 해결될 수 있는 실패도 있으나, 잘못된 구현 또는 버그로 인해 재시도 하더라도 의미가 없는 실패 또한 존재합니다.
따라서 이를 구분하여, 실패한 상품에 대해 재시도 처리가 필요합니다.
FAILED는 provider_resource_id의 null 여부에 따라 처리 방법이 달라집니다.
provider_resource_id가 null인 경우
Section titled “provider_resource_id가 null인 경우”외부 API 요청이 실패하여, 게시가 되지 않았거나, 실제 쇼핑몰에 게시는 됐으나, 이후 네트워크 또는 이후 코드에서 발생한 예외로 인해 발생한 경우입니다.
전자의 경우 해당 행을 제거한 뒤 재시도하면 됩니다. 하지만 후자의 경우, 재시도를 하면 쇼핑몰에 중복된 상품이 올라가게 되어 재고 연동이 되지 않고 중복 주문이 발생할 수 있으므로 확인이 필요합니다.
후자의 경우를 방지하기 위해, 상품 게시를 2단계로 나누어, 1단계에서는 사용자에게 노출이 되지 않는 상태로 상품을 게시(ex: 재고가 0)하여
provider_resource_id를 받아서 재고 연동이 되게 한 뒤, 2단계에서 재시도가 가능한 요청(사용자에게 노출, 재고, 본문, 이미지 등)을 통해 최종적 일관성을 지키도록 구현할 수 있습니다. (1단계에서 에러가 났는데, 실제 게시가 된 상황이어도, 사용자에게 상품이 노출되지 않으니 중복된 주문이 발생할 일이 없습니다.) 2025.11.28 기준 카페24 상품 게시가 이러한 방법으로 구현되어 있습니다. (Cafe24ProductPrePublishClient,Cafe24ProductPostPublishClient)
provider_resource_id가 null이 아닌 경우
Section titled “provider_resource_id가 null이 아닌 경우”2025.11.29 기준 상품 가격 변경 요청이 실패하면 FAILED 상태로 변경됩니다.
외부 API 요청은 실패했으나, 이미 상품이 게시된 상태이므로 해당 행은 절대로 삭제되면 안 됩니다.
실패 원인을 확인했으면 단순히 상태를 SUCCESS 상태로 변경하면 됩니다.
FailedPublishedProductHistory
Section titled “FailedPublishedProductHistory”게시에 실패한 상품의 사유를 확인하기 위해 만들어진 엔티티입니다.
사용자가 실패 사유를 알게 하여, 직접 처리할 수 있도록 하거나, 사용자가 처리할 수 없는 에러 발생 시 VOC를 통해 빠른 문제 해결이 가능하도록 설계되었습니다.
실패를 확인하는 것은 해당 엔티티를 제거하는 것으로 볼 수 있으며, 개발자가 에러를 분석하기 위해 Soft delete로 구현되었습니다.