토스쇼핑 Open API 상품 상세 조회를 1건 단위에서 여러 상품 단위로 확장했습니다. 개발기 8편에서는 플러그인 v1.6.0으로 그 구조를 구현했습니다. 최대 30개의 tacaItemId를 상품 상세 API 1회 요청으로 묶어 조회하고, 응답의 items와 notFoundIds를 나눠 WordPress DB의 기존 상품 레코드를 상품별로 업데이트하는 구조입니다. 다만 이번 서버 테스트는 DB에 저장된 1건으로 진행했기 때문에, 30건 동시 처리는 아직 검증하지 않았습니다.
토스쇼핑 Open API 상품 상세 조회, 8편에서 한 작업
이번 글은 상품을 소개하는 글이 아니라 토스쇼핑 Open API 상품 상세 조회를 실제로 연동해 가는 과정을 기록하는 DevLog입니다. 7편까지는 상품 1건을 기준으로 API를 호출하고 WordPress DB에 저장하고 갱신하는 흐름을 확인했습니다. 8편에서는 플러그인을 v1.6.0으로 올려 그 구조를 여러 상품으로 넓혔습니다.
핵심은 상품마다 API를 따로 호출하는 방식이 아닙니다. 상품 ID를 최대 30개까지 모아 상품 상세 API 한 번으로 묶어 요청하고, 응답을 상품별로 나눠 DB에 반영하는 구조입니다.
v1.6.0 한 줄 요약
상품 DB → 최대 30개 tacaItemId 수집 → 상품 상세 API 일괄 조회 → items / notFoundIds 분리 → 상품별 DB UPDATE → 상세 조회 상태 및 시각 기록
1~7편까지의 개발 흐름
8편은 앞선 작업 위에서 이어지므로 흐름을 간단히 정리합니다. Open API 키 발급부터 시작한 과정은 1편에 기록했고, OAuth와 Health API로 기본 연결을 확인한 과정은 2편에 정리했습니다.
실제 상품 조회 API 테스트는 3편, 상품 상세 API 테스트는 4편에서 다뤘습니다. 조회한 상품으로 쉐어링크 발급 API를 시험한 내용은 5편에 있습니다.
이후 조회한 상품을 WordPress DB에 저장하고 같은 상품이면 UPDATE하도록 만든 과정은 6편에서 구현했습니다. 저장된 상품의 tacaItemId로 상세 API를 호출해 기존 레코드에 상세 정보를 보강한 것은 7편입니다. 그리고 이번 8편에서 1건 단위 상세 조회 구조를 여러 상품 처리 구조로 확장했습니다.
v1.5.0의 한계: 상품 1건만 처리한다
v1.5.0은 상품 1건의 상세 정보를 조회해 DB에 저장하는 데는 문제가 없었습니다. 하지만 추천 채널을 만들려면 상품 하나만 다루는 것으로는 부족합니다. 앞으로 필요한 흐름은 대략 이렇습니다.
상품 목록 조회
→ 여러 상품 저장
→ 여러 상품 상세 정보 확보
→ 상품별 데이터 비교
→ 상품 선별
그래서 이번 단계에서는 상품 선별 자체가 아니라, 그 앞 단계인 여러 상품의 상세 데이터를 확보하고 DB를 갱신하는 구조를 먼저 만들었습니다. 상품 선별 기능은 v1.6.0에 포함되어 있지 않습니다.
v1.5.0과 v1.6.0의 차이
| 구분 | v1.5.0 | v1.6.0 |
|---|---|---|
| 처리 대상 | 상품 1건 | 상품 여러 건 |
| API 요청 | 상품 1건 상세 조회 | 최대 30개 tacaItemId를 묶어 1회 요청 |
| 응답 처리 | 단일 상품 응답을 기존 레코드에 반영 | items / notFoundIds를 나눠 상품별로 처리 |
| DB 반영 | 기존 레코드 UPDATE | items별 기존 레코드 UPDATE |
| 상태 기록 | 상세 저장 시각 중심 | 상세 조회 상태(success / not_found / error)와 시각 구분 기록 |
토스쇼핑 Open API 상품 상세 조회, 최대 30개 tacaItemId 일괄 처리 구조
상품 상세 API는 여러 개의 tacaItemId를 한 번에 전달해 최대 30개의 상품을 조회하는 구조를 제공합니다. 이번 v1.6.0은 이 구조를 그대로 활용했습니다. 문서에서 확인되지 않은 호출 제한이나 정책은 임의로 추가하지 않았습니다. 실제 요청 규격은 쉐어링크 Open API 연동 가이드에서 확인할 수 있습니다.
[이전 방식]
상품 A → API 호출
상품 B → API 호출
상품 C → API 호출
...
[v1.6.0]
tacaItemId 최대 30개
→ 상품 상세 API 1회 호출
상품 수만큼 요청을 반복하지 않고 한 번의 요청으로 묶으면, 여러 상품의 상세 정보를 확보하는 흐름이 훨씬 단순해집니다. 대신 응답을 상품별로 어떻게 나눌지가 중요해졌고, 이것이 v1.6.0의 실제 설계 포인트였습니다.
DB 업데이트 구조
상품 테이블은 기존 wp_jb_toss_products를 그대로 사용했습니다. 새 테이블을 만들지 않고, 6편과 7편에서 쌓은 레코드 위에 상세 정보를 계속 보강하는 방향입니다.
기존 DB 상품 조회
↓
tacaItemId 최대 30개 수집
↓
상품 상세 API 호출
↓
응답의 items 확인
↓
각 tacaItemId에 해당하는 기존 상품 검색
↓
기존 레코드 UPDATE
응답의 items에 들어 있는 각 상품은 tacaItemId를 기준으로 기존 레코드와 매칭해 UPDATE합니다. 신규 INSERT가 아니라 이미 저장된 상품을 갱신하는 흐름이라는 점은 7편과 같습니다.
success / not_found / error 상태 관리
여러 상품을 한 번에 요청하면 결과가 상품마다 다를 수 있습니다. 그래서 v1.6.0에서는 상품 상세 조회 상태를 상품 단위로 기록하도록 확장했습니다.
| 상태 | 의미 | 처리 원칙 |
|---|---|---|
success |
상세 API에서 정상적으로 상품 정보를 받아 기존 DB 레코드에 업데이트한 경우 | 상세 정보와 저장 시각을 갱신 |
not_found |
API 응답의 notFoundIds에 포함된 경우 | 상품을 영구 삭제된 것으로 판단하지 않고 상태만 기록 |
error |
API 호출 또는 처리 과정에서 오류가 발생한 경우 | 기존에 저장된 정상 데이터를 삭제하거나 초기화하지 않음 |
이렇게 나눈 이유는 일부 상품만 조회되지 않는 상황이 실제로 생길 수 있기 때문입니다. 예를 들어 30개를 요청했는데 items가 27개, notFoundIds가 2개, 오류가 1개라면 전체를 실패로 처리하지 않고 결과를 각각 기록할 수 있게 만들었습니다. 다만 이런 혼합 결과는 이번 테스트에서 실제로 확인한 것이 아니라 그렇게 처리하도록 구현한 구조라는 점을 분명히 해 둡니다.
조회 시각을 세 가지로 나눠 기록한 이유
이번 버전에서는 상태뿐 아니라 시각도 의미별로 구분해 저장합니다. 세 값은 서로 다른 사건을 가리킵니다.
| 필드 | 의미 |
|---|---|
detail_checked_at |
상세 API 조회를 시도한 시각 |
detail_fetched_at |
상세 정보를 정상적으로 받아 DB에 저장한 시각 |
last_seen_at |
상품 목록 조회 과정에서 마지막으로 확인된 시각 |
조회를 시도했지만 실패한 경우와 실제로 저장에 성공한 경우를 구분해 두면, 나중에 어떤 상품의 상세 정보가 오래되었는지 판단하는 기준으로 쓸 수 있습니다. 목록에서 마지막으로 보인 시각(last_seen_at)도 별개로 남겨 두었습니다.
실제 서버 테스트 결과
실제 서버에서 v1.6.0을 실행한 결과입니다.
| 항목 | 결과 |
|---|---|
| HTTP Status | 200 |
| resultType | SUCCESS |
| 요청 상품 수 | 1 |
| API 조회 상품 수 | 1 |
| notFoundIds 수 | 0 |
| DB 업데이트 성공 | 1 |
| not_found 기록 | 0 |
| 오류 | 0 |
테스트 상품 데이터 확인
테스트에 사용한 상품은 지금까지 시리즈에서 써 온 것과 같은 상품입니다. DB에 확인된 값은 다음과 같습니다.
| 항목 | 값 |
|---|---|
| 상품명 | 스파클 생수, 무라벨, 2L, 24개 |
| tacaItemId | 90382271 |
| 판매가 | 7,400원 |
| 할인율 | 63.00% |
| 평점 | 4.80 |
| 리뷰 수 | 72,669 |
| 상세 조회 상태 | success |
| 상세 확인(detail_checked_at) | 2026-09-29 11:42:48 |
| 상세 저장(detail_fetched_at) | 2026-09-29 11:42:48 |
| 마지막 목록 확인(last_seen_at) | 2026-09-26 11:27:00 |
상세 확인과 상세 저장 시각은 같은 시각이고, 마지막 목록 확인 시각은 그보다 앞선 2026-09-26 11:27:00입니다. 이번 실행은 목록 조회가 아니라 상세 조회였기 때문에 last_seen_at은 그대로 유지되었고, 세 시각이 서로 다른 의미로 기록된다는 점이 값으로 확인됩니다.
상세 API 응답이 DB에 반영된 것 확인
이번 테스트에서는 리뷰 수가 이전 상세 조회 결과와 달라졌습니다. 이전에 확인한 값은 69,162였고, 이번 상세 API 응답에서는 72,669가 반환되었으며 이 값이 DB에 반영되었습니다. 리뷰 수가 바뀐 구체적인 이유는 확인하지 않았고 여기서 추측하지도 않겠습니다. 확인한 것은 상세 API에서 받은 최신 값이 기존 WordPress DB 레코드에 실제로 업데이트되었다는 사실입니다.
이번 8편에서 검증한 것은 다건 처리를 위한 구조와 응답 처리, 상품별 DB UPDATE, 상태와 시각 기록이 서버에서 동작한다는 점입니다.
이번 단계에서 확인한 것과 확인하지 못한 것
이번 실제 테스트는 DB에 저장된 상품이 1건뿐이어서 요청 상품 수도 1건이었습니다. 따라서 이 결과만으로 30개 상품을 동시에 처리했다고 말할 수는 없습니다.
이번 테스트로 확인한 것
- 여러 상품을 다루기 위한 일괄 조회 구조가 구현되었다는 점
- tacaItemId를 모아 상품 상세 API에 요청하는 흐름
- API 응답 처리와 상품별 DB UPDATE
- 상세 조회 상태와 조회 시각 기록
아직 확인하지 않은 것
- 실제 30건 동시 조회
- notFoundIds에 상품이 포함된 실제 사례
- 오류가 섞인 혼합 결과의 실제 처리
notFoundIds 처리와 error 처리는 구조로는 구현되어 있지만, 이번 테스트 결과에서는 notFoundIds가 0, 오류가 0이었기 때문에 실제 사례로 검증하지는 못했습니다. 이 부분은 상품이 더 쌓인 뒤 추가로 확인할 계획입니다.
아직 구현하지 않은 기능
v1.6.0은 상품 상세 데이터 수집 및 DB 갱신 구조의 확장까지만 완료한 버전입니다. 아래 기능은 아직 구현하지 않았습니다.
- 자동 상품 선별
- 상품 추천 점수 계산
- AI 기반 상품 평가
- 자동 쉐어링크 발급 workflow
- 성과 API 연동
- 자동 SNS 콘텐츠 생성
- 자동 WordPress 게시
- 자동 스케줄링
- 30건 규모의 실제 부하 테스트
다음 개발 방향
API에서 상품 데이터를 가져오는 것만으로는 부족하고, 이제는 어떤 상품을 추천 채널에 사용할지 판단할 수 있는 데이터 구조가 필요합니다. 예상하는 큰 흐름은 다음과 같습니다.
상품 목록 조회
↓
상품 상세 정보 수집
↓
WordPress DB 저장
↓
상품 데이터 비교
↓
추천 후보 구성
↓
상품 선별
↓
쉐어링크 발급
↓
성과 데이터 수집
이 중 구체적인 구현은 아직 정해지지 않았습니다. 다음 단계에서는 상품 선별에 필요한 데이터를 어떻게 구성할지 먼저 검토할 예정입니다.
마무리
정리하면 API 연결, 상품 조회, 상품 상세 조회, 쉐어링크 발급, 상품 DB 저장, 상세 정보 보강을 거쳐 여러 상품의 상세 정보를 일괄 조회하는 구조까지 왔습니다. 토스쇼핑 API에서 받은 데이터를 화면에 보여주는 수준을 넘어, WordPress DB를 중심으로 계속 관리할 수 있는 상품 데이터 구조로 발전시키는 과정입니다. 다음 글에서는 이 데이터를 바탕으로 상품 선별을 어떻게 준비할지 이어서 기록하겠습니다.
자주 묻는 질문
토스쇼핑 Open API로 상품 상세를 한 번에 몇 개까지 조회하나요?
이번 v1.6.0은 상품 상세 API에 여러 tacaItemId를 전달해 최대 30개를 한 번에 조회하는 구조를 사용했습니다. 자세한 요청 규격은 공식 연동 가이드에서 확인하시길 권합니다.
notFoundIds에 포함된 상품은 삭제된 상품인가요?
이번 개발에서는 그렇게 단정하지 않았습니다. notFoundIds에 포함된 상품은 not_found 상태로 기록하고, 영구 삭제된 것으로 판단하지 않는 방식으로 처리합니다.
detail_checked_at과 detail_fetched_at은 무엇이 다른가요?
detail_checked_at은 상세 API 조회를 시도한 시각이고, detail_fetched_at은 상세 정보를 정상적으로 받아 DB에 저장한 시각입니다. 조회를 시도했지만 저장에 성공하지 못한 경우를 구분하기 위해 나눠 기록합니다.
이번 테스트에서 30개 상품이 실제로 처리되었나요?
아닙니다. 이번 실제 테스트는 DB에 저장된 1건으로 진행했습니다. 30건 동시 처리는 향후 추가로 검증할 항목입니다.