사진
listingsapi-js SDK로 사업장에 로고, 커버 및 갤러리 사진을 첨부하고, 대표 이미지에 별표를 표시하고, 오래된 파일을 삭제합니다.
사진은 공개 URL을 통해 사업장에 첨부합니다. 이미지를 직접 호스팅하면 플랫폼이 해당 이미지를 가져와 저장하고, 게시 플랫폼은 저장된 이미지를 가져갑니다. 사업장에는 로고 1개, 커버 1개, 개수 제한 없는 추가 사진을 보관할 수 있으며, 추가 사진 중 최대 4개에 별표를 표시할 수 있습니다. 모든 사진 메서드는 ListingsAPI 클라이언트 인스턴스에 있으며 사업장 ID를 기준으로 동작합니다. 숫자형 사업장 ID는 자동으로 인코딩되므로 1800289과 'TG9jYXRpb246MTgwMDI4OQ=='는 서로 바꿔 사용할 수 있습니다.
업로드 성공 응답은 2가지 형태입니다. 소규모 배치는 즉시 저장되어 처리가 완료된 미디어와 함께 반환되고, 대규모 배치는 대기열에 추가되어 폴링에 사용할 요청 ID가 반환됩니다. 이미지 수를 직접 세는 대신 request.requestId의 존재 여부에 따라 분기합니다.
fetchLocationPhotos
사업장에 첨부된 모든 미디어 파일, 즉 로고, 커버, 추가 이미지를 조회합니다. 업로드 전에 호출하여 누락된 항목을 확인하고 별표 표시 및 삭제 메서드에 필요한 사진 ID를 수집합니다.
const photos = await client.fetchLocationPhotos(1800289); for (const p of photos) {console.log(p.id, p.categoryName, p.starred, p.url);}매개변수
호출이 완료되면 mediaFilesOfLocation 배열을 반환하며, 기본값은 빈 배열입니다. 각 파일에는 id, url, thumbnailUrl, categoryName(Logo, Cover 또는 Additional), categoryId, fileType, starred, viewsCount이 포함됩니다. 미디어가 없는 사업장에는 빈 배열이 반환되며, 이는 새로 생성한 사업장의 정상 응답으로 오류가 아닙니다.
addLocationPhotos
사업장에 이미지를 1개 이상 첨부합니다. 각 항목의 photo에는 공개적으로 접근할 수 있는 JPEG 또는 PNG URL이 필요하며, type에는 이미지를 배치할 슬롯을 지정해야 합니다.
const result = await client.addLocationPhotos(1800289, [{ photo: 'https://cdn.example.com/brightsmile/logo.png', type: 'LOGO' },{ photo: 'https://cdn.example.com/brightsmile/storefront.jpg', type: 'ADDITIONAL' },]); if (result.request?.requestId) {// Queued: poll fetchPhotoUploadStatus with this idconsole.log('queued', result.request.requestId);} else {for (const p of result.photos ?? []) { console.log('stored', p.id, p.type);}}사진 옵션
매개변수
호출이 완료되면 success, photos, request, errors, clientMutationId가 포함된 addLocationPhotos 페이로드를 반환합니다. 즉시 처리되는 업로드에서는 photos에 새 미디어가 포함되고 각 미디어에는 id, databaseId, url, thumbnailUrl, type, starred가 포함되며, request은 빈 객체입니다. 대기열에 추가되는 업로드에서는 photos가 비어 있고 request.requestId가 설정됩니다. 두 경우 모두 성공입니다.
이미지는 JPEG 또는 PNG 형식이어야 하며, 게시 플랫폼의 크기 규칙이 슬롯별로 적용됩니다. 사업장에는 로고와 커버를 각각 1개씩 보관하므로, 둘 중 하나를 다시 업로드하면 기존 이미지가 교체됩니다. type을 용도에 맞게 설정해야 합니다. 갤러리 이미지는 ADDITIONAL에 배치합니다.
fetchPhotoUploadStatus
대기열에 추가된 업로드의 처리가 끝날 때까지 폴링합니다. addLocationPhotos이 반환한 request.requestId을 사용합니다.
let job = await client.fetchPhotoUploadStatus('e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a'); while (job.status === 'PROCESSING') {await new Promise((resolve) => setTimeout(resolve, 5000));job = await client.fetchPhotoUploadStatus('e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a');} for (const p of job.photos ?? []) {if (p.status === 'ERROR') { console.log('rejected:', p.errorMessage);}}매개변수
페이로드에는 requestId, 값이 PROCESSING, SUCCESS 또는 ERROR인 작업 status, 그리고 각 항목에 mediaId, locationId, 해당 항목의 status 및 errorMessage가 포함된 photos 배열이 들어 있습니다.
작업의 status은 처리가 완료되었음을 나타내며, 모든 이미지가 저장되었음을 의미하지는 않습니다. SUCCESS 작업에도 거부된 사진이 포함될 수 있으므로, PROCESSING 상태를 벗어나면 photos을 순회하며 각 항목을 확인합니다. 폴링 사이에 몇 초씩 대기해야 합니다. 대기 없이 반복하면 빠르게 요청 빈도 제한에 걸릴 수 있습니다. 만료된 ID는 처리가 끝난 상태에 도달하지 않으므로, 최근 업로드에서 받은 ID만 폴링합니다.
starLocationPhotos
대표 이미지 기능을 지원하는 게시 플랫폼에서 추가 사진이 우선 배치되도록 별표를 표시하거나, false을 전달하여 별표를 해제합니다.
// Feature a photoconst result = await client.starLocationPhotos(1800289,['TWVkaWFGaWxlOjU4Nzg5MzI='],true,); console.log(result.success, result.photos[0].starred); // Reverse itawait client.starLocationPhotos(1800289, ['TWVkaWFGaWxlOjU4Nzg5MzI='], false);매개변수
호출이 완료되면 starUnstarLocationPhotos 페이로드를 반환하며, 여기에는 success, photos, 그리고 errors 배열 대신 단일 error 객체가 포함됩니다. 반환된 각 사진에는 id, databaseId, url, thumbnailUrl, type 및 새 starred 값이 포함됩니다.
ADDITIONAL 사진에만 별표를 표시할 수 있으며, 로고와 커버는 이미 기본 이미지입니다. 사업장에는 별표가 표시된 사진을 최대 4개까지 보관할 수 있으며, 제한을 확인할 때 이미 별표가 표시된 사진과 이번 호출에 포함된 사진을 합산하므로 4개를 초과하면 요청 전체가 실패합니다.
removeLocationPhotos
사진 ID를 사용하여 사업장에서 사진을 분리합니다. 오래된 갤러리 이미지를 제거하거나 잘못 가져온 이미지를 되돌릴 때 사용합니다.
const result = await client.removeLocationPhotos(1800289, ['TWVkaWFGaWxlOjU4Nzg5MzI=',]); console.log(result.removedCount); // 1매개변수
호출이 완료되면 removedCount, errors, clientMutationId이 포함된 removeLocationPhotos 페이로드를 반환합니다. removedCount이 0이고 errors가 null이면 전송한 ID 중 해당 사업장에 첨부된 사진의 ID가 없다는 뜻이며, 이를 통해 이미 제거된 ID를 식별할 수 있습니다.
여기서는 ADDITIONAL 사진만 삭제할 수 있습니다. 로고나 커버를 교체하려면 새 이미지를 업로드합니다. 미디어를 삭제하면 사업장과의 연결이 해제되지만, 계정 미디어 라이브러리에서 파일이 완전히 삭제되는 것은 아닙니다.
일반적인 갤러리 갱신
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // reads LISTINGSAPI_KEY from envconst LOCATION_ID = 1800289; // Brightsmile Dental // 1. See what is already attachedconst existing = await client.fetchLocationPhotos(LOCATION_ID);const hasLogo = existing.some((p) => p.categoryName === 'Logo'); // 2. Upload only what is missing, plus the new hero shotconst uploaded = await client.addLocationPhotos(LOCATION_ID, [...(hasLogo ? [] : [{ photo: 'https://cdn.example.com/brightsmile/logo.png', type: 'LOGO' as const }]),{ photo: 'https://cdn.example.com/brightsmile/hero-2026.jpg', type: 'ADDITIONAL' as const },]); // 3. Feature the new hero shotconst hero = (uploaded.photos ?? []).find((p) => p.type === 'ADDITIONAL');if (hero) {await client.starLocationPhotos(LOCATION_ID, [hero.id], true);} // 4. Retire last season's imagesconst stale = existing.filter((p) => p.categoryName === 'Additional' && !p.starred).map((p) => p.id);if (stale.length > 0) {const removed = await client.removeLocationPhotos(LOCATION_ID, stale);console.log('removed ' + removed.removedCount + ' stale photos');}