소개
처음 아이디어는 단순했습니다.
해외 가구를 판매할 수 있는 권한을 가진 파트너가 있었고, 다음 과정을 반복하고 있었습니다.
해외 공급처 상품 확인 → 정보 수집 → 스프레드시트 정리 → Cafe24 업로드용 CSV 변환 → 상품 등록
상품 하나를 올리는 일 자체보다 더 큰 문제는, 상품 수가 늘어날수록 같은 정보를 여러 번 고쳐야 한다는 점이었습니다. 공급가·옵션·재고·이미지·상품 설명이 서로 다른 파일과 화면에 흩어져 있었고, 한 채널에서 수정한 내용이 다른 채널에 자동으로 이어지지 않았습니다.
그래서 처음부터 “크롤러를 하나 만들어 보자”로 시작하지 않았습니다. 먼저 공유받은 스프레드시트와 기존 업로드 양식을 비교하면서, 이 작업의 실제 단위가 무엇인지 다시 정의했습니다.
상품 하나는 상품명 하나가 아니라 다음의 묶음이었습니다.
- 상품 기본 정보
- 모델/품번
- 색상·사이즈·마감 같은 옵션
- 옵션별 공급가와 재고 상태
- 이미지와 상세 설명
- 가격 계산에 사용한 환율·물류·세금 검토 정보
- Cafe24와 자사몰에 공개할 채널별 상태
결국 CSV는 최종 출력물일 뿐이고, 운영의 기준은 별도의 상품 DB가 되어야 한다는 결론에 도달했습니다.
※ 이 글에서는 해외 공급처의 이름과 지역, 실제 제품명·가격·URL, 비공개 로그인 정보와 인프라 식별자를 공개하지 않았습니다. 함께 받은 스프레드시트도 실제 값을 공유하지 않고 구조만 재현했습니다.
진행 방법
1. 스프레드시트와 Cafe24 양식을 먼저 데이터 모델로 바꾸기
첫 단계에서는 웹페이지를 긁는 코드보다 필드 설계에 시간을 썼습니다.
공유 스프레드시트는 브랜드별 탭과 상품·옵션·규격·공급가·재고·이미지 정보가 섞인 운영 자료였습니다. 기존 Cafe24 업로드 파일은 판매 상태, 상품 분류, 상품명, 모델명, 공급가, 판매가, 상세 설명, 이미지 등 채널에 맞춘 필드가 매우 많았습니다.
두 자료를 그대로 이어 붙이면 한 번의 업로드는 가능하지만, 다음 업데이트부터 다시 사람이 비교해야 합니다. 그래서 다음과 같이 내부 모델을 나눴습니다.
- source product: 해외 공급처에서 관측한 상품의 기준 정보
- product variant: 색상·사이즈·마감 등 실제 판매 단위
- inventory item: 재고와 주문 제작 상태
- price policy: 원가·환율·물류·가산율·반올림 규칙
- channel listing: Cafe24와 자사몰에 각각 공개할 제목·가격·재고·상태
- order / settlement ledger: 어떤 옵션이 실제로 판매되었는지 추적하는 원장
이 구조를 잡고 나니 “상품을 등록한다”는 작업이 “기준 데이터를 만들고 각 채널에 투영한다”는 작업으로 바뀌었습니다.
2. 검색 결과가 아니라 상세 페이지의 근거를 수집하기
초기 크롤러는 상품명으로 검색하고 결과를 가져오는 수준에서 출발했습니다. 하지만 검색 결과만으로는 다음 정보를 안정적으로 알기 어려웠습니다.
- 같은 상품의 다른 옵션인지, 별도 상품인지
- 가격이 실제 판매가인지 문의용 placeholder인지
- 품절인지 주문 제작인지
- 제품의 품번과 URL 숫자 ID가 같은 의미인지
- 대표 이미지와 상세 갤러리 이미지가 무엇인지
그래서 검색 결과는 후보를 좁히는 용도로만 쓰고, 최종 반영은 상세 페이지의 여러 신호를 조합하도록 바꿨습니다. 보이는 가격, 구조화된 데이터, 상품 정보 영역, 옵션과 재고 문구를 따로 기록하고, 수집 시점과 원본 근거를 함께 저장했습니다.
가격 신호가 서로 다르거나, 오래된 값만 남았거나, 재고 상태가 불명확하면 바로 공개하지 않고 검수 대기 상태로 보냈습니다. 자동화의 목표를 “최대한 많이 올리기”에서 “확인된 것만 올리기”로 바꾼 순간부터 운영 안정성이 크게 좋아졌습니다.
3. 상품·옵션·재고를 먼저 DB에 넣고 Cafe24와 자사몰을 분리하기
중간 단계에서는 Cafe24 업로드 자동화만 먼저 완성할 수도 있었습니다. 하지만 그렇게 하면 Cafe24가 다시 기준 데이터가 되고, 자사몰을 만들 때 같은 문제를 다시 풀어야 합니다.
그래서 자체 DB를 중심에 두고 두 채널을 분리했습니다.
- Cafe24: 브랜드와 상품을 발견하는 채널, 채널별 업로드·분류·노출 상태 관리
- SugarSalt 자사몰: 주문 조건, 고객 동의, 결제, 배송·주문 진행 상태 관리
- 판매자 Admin: 상품 검수, 가격 수정, 재고·옵션 확인, 채널별 공개 승인
두 채널의 가격과 상태를 한 화면에서 보이게 하되, 내부적으로는 독립 저장했습니다. 한 채널의 프로모션 가격을 바꿨다고 다른 채널 가격이 조용히 바뀌지 않도록 한 것입니다.
이 구조 덕분에 Cafe24에 아직 연결되지 않은 상품도 자사몰에서 먼저 검수할 수 있고, 반대로 자사몰에서 숨긴 상품이 Cafe24에 잘못 공개되지 않게 통제할 수 있게 됐습니다.
4. 가격 산정 기준을 코드가 아니라 정책으로 만들기
해외 상품은 환율·배송·세금 검토 여부에 따라 가격이 달라지므로, 계산식을 코드 안에 숨기면 판매자가 결과를 믿기 어렵습니다. 가격을 다음처럼 단계로 나눴습니다.
해외 공급가 × 환율 + 해외 배송료 + 통관·세금 검토 항목 = 입고원가
그다음 정책을 적용합니다.
입고원가 × (1 + 원가 기준 가산율) → 5,000원 단위 정규화 → 채널별 판매가
현재 설계에서 중요한 것은 숫자 자체보다 추적 가능성입니다.
- 환율은 제공기관·기준·시점·계산 버전을 함께 저장
- 해외 배송료는 포장 치수에서 계산한 부피무게 기준으로 분리
- 옵션마다 원가·판매가·재고를 따로 보존
- 자사몰과 Cafe24의 판매가는 독립적으로 저장
- 판매가가 입고원가보다 낮거나 필수 계산값이 빠지면 자동 공개하지 않음
- 판매자가 수동 가격을 넣었을 때도 자동 계산값과 변경 이유를 남김
판매자는 Admin에서 가산율과 채널별 판매가를 조정할 수 있지만, 시스템은 “왜 이 가격인지”를 다시 설명할 수 있어야 합니다. 이것이 단순 계산기와 운영 시스템의 차이라고 생각합니다.
5. 재고·가격 업데이트는 관측, 검증, 반영의 3단계로 나누기
최신 정보를 가져오는 기능은 단순한 주기적 크롤링보다 상태 관리가 중요했습니다.
1. 관측: 공급처 페이지에서 현재 보이는 가격·재고·옵션 신호를 저장
2. 검증: 신호 간 일치 여부, 최신성, 급격한 변동, 누락 여부를 검사
3. 반영: 검증을 통과한 값만 상품 기준 데이터와 채널에 반영
예를 들어 가격이 갑자기 크게 내려갔거나, 상품 페이지는 주문 제작이라고 말하는데 목록에는 품절로 표시되어 있거나, 옵션 중 하나의 가격만 빠졌다면 자동 공개를 멈춥니다.
반대로 일시적인 네트워크 실패만으로 상품을 삭제하지도 않았습니다. 마지막으로 확인된 값을 보존하고, 실패 횟수와 최신성을 기준으로 검수 대기 또는 판매 중지 상태를 결정했습니다.
최근에는 판매자용 인증 관측값과 공개 관측값도 분리했습니다. 외부에 보이는 가격과 판매자가 실제로 확인할 수 있는 공급 정보를 같은 값처럼 취급하지 않고, 각각의 맥락과 시점을 보존하는 방식입니다.
6. 대량 수집보다 재시작 가능한 운영을 만들기
브랜드별 상품 수가 다르기 때문에 한 번에 전체를 처리하는 방식은 실패했을 때 복구가 어렵습니다. 이후에는 브랜드 단위 배치, 페이지 단위 이어받기, 요청 간격, 재시도, 작업 잠금, 중복 방지 키를 넣었습니다.
중간에 프로세스가 멈춰도 다음 실행이 처음부터 다시 시작되지 않고, 이미 처리한 상품은 건너뛰면서 실패한 구간만 이어서 처리할 수 있게 했습니다. 이미지도 원본 URL과 저장 모드를 분리해, 필요하지 않은 바이너리 복제를 기본 동작으로 만들지 않았습니다.
이 과정을 거치면서 “크롤링 성공”의 기준도 바뀌었습니다.
- 상품 문서가 생성되었는가?
- 옵션과 재고가 정상적으로 연결되었는가?
- 가격 계산에 필요한 값이 모두 있는가?
- 채널 공개 전에 검수 상태가 확인되었는가?
- 같은 작업을 다시 실행해도 중복 상품이 생기지 않는가?
- 실패했을 때 운영자가 어디를 확인해야 하는가?
7. 운영자가 관리할 수 있는 화면과 권한을 붙이기
자동화가 안정되려면 개발자만 로그를 보는 구조에서 벗어나야 합니다. Admin에는 상품 목록, 검수함, 채널별 가격, 재고 상태, 주문·정산, 시스템 상태를 나눠 넣었습니다.
최근에는 파트너 계정에 필요한 읽기 권한과 작업 권한도 분리했습니다. 파트너가 상품과 가격 근거를 읽을 수는 있지만, 검수되지 않은 상품을 곧바로 공개하거나 원본 데이터를 변경할 수는 없게 하는 방식입니다.
운영 리포트에는 다음을 한 번에 보여주도록 구성했습니다.
- 새로 수집된 상품과 가격 변경
- 검수 대기와 오류 수준
- 채널 공개 가능 여부
- 주문·정산 작업 상태
- 사람이 확인해야 하는 조치 목록
자동화의 마지막 단계는 무인화가 아니라, 사람이 꼭 봐야 하는 지점을 명확히 만드는 것에 가깝습니다.
결과와 배운 점
결과
처음에는 다음 한 줄짜리 자동화가 목표였습니다.
웹에서 가져오기 → CSV 만들기 → Cafe24 등록
지금은 다음 구조로 확장되었습니다.
해외 공급처 관측 → 정규화된 상품 DB → 옵션·재고 분리 → 가격 정책 계산 → 검수 게이트 → Cafe24 / SugarSalt 자사몰 공개 → 주문·정산 추적
그 결과 다음을 할 수 있게 됐습니다.
- 상품 하나를 여러 채널에 다시 입력하지 않고, 기준 데이터에서 투영
- 옵션별 재고와 가격을 분리해 다중 옵션 상품을 안전하게 공개
- 공급가·환율·배송·세금 검토를 포함한 가격 계산 근거 보존
- 판매자가 Admin에서 가격 정책과 채널별 판매가를 관리
- 가격 충돌·누락·오래된 정보는 자동으로 공개 중지
- 품절·주문 제작·검수 필요 상품을 같은 “판매 가능” 상태로 뭉개지 않음
- 원본 출처와 내부 정보를 고객 화면에 그대로 노출하지 않는 공개 경계 유지
가장 크게 배운 점 5가지
1. 크롤링보다 먼저 데이터 모델을 만들어야 한다
사이트 HTML은 바뀌지만, 상품·옵션·재고·가격·채널이라는 업무 개념은 오래 갑니다. 먼저 모델을 정하고 크롤러는 그 모델에 맞춰야 유지보수가 가능했습니다.
2. CSV는 시스템이 아니라 어댑터다
Cafe24 업로드 양식을 기준 데이터로 삼으면 다음 채널이 생길 때 다시 갈아엎어야 합니다. 자체 DB가 기준이고, CSV와 API는 채널 어댑터라는 관점이 중요했습니다.
3. 자동화는 “성공”보다 “보류”를 잘해야 한다
가격이 없거나 옵션 하나가 빠진 상품을 억지로 공개하는 것보다, 검수 대기 상태로 보내는 편이 훨씬 안전합니다. fail-closed 규칙이 상품 수를 잠시 줄이더라도 전체 운영 신뢰도를 높였습니다.
4. 가격은 숫자가 아니라 설명 가능한 정책이어야 한다
판매자가 가격을 바꿀 수 있는 것과, 가격이 왜 그렇게 계산되었는지 확인할 수 있는 것은 다른 문제입니다. 계산값·수동값·변경 이유·적용 채널을 함께 저장해야 운영이 이어집니다.
5. 공개 데이터와 내부 데이터를 처음부터 분리해야 한다
고객에게 필요한 것은 상품명·옵션·판매가·배송 안내입니다. 원본 URL, 내부 원가, 인증 관측 맥락, 작업 로그는 판매자와 운영자에게만 필요한 정보입니다. 나중에 가리는 것보다 처음부터 공개용 projection을 따로 만드는 편이 안전했습니다.
시행착오와 주의할 점
- 검색 결과의 가격 하나만 믿고 상품을 공개하지 않기
- URL 숫자를 모델/품번으로 착각하지 않기
- 공급처 페이지의 일시적 오류를 품절이나 삭제로 처리하지 않기
- 원가·환율·배송·세금 검토값을 코드 안에만 숨기지 않기
- 다중 옵션의 일부 가격이 비어 있는데 최저가 하나로 공개하지 않기
- 자동화 계정과 로그인 정보, API 토큰을 문서·스크린샷·프론트엔드에 넣지 않기
- 실제 공유 글에는 공급처 이름·지역·상품명·가격·URL·프로젝트 식별자를 노출하지 않기
사용한 프롬프트의 형태
처음부터 “전부 만들어줘”라고 요청하기보다, 다음처럼 문제를 쪼개서 질문했습니다.
해외 가구 공급처의 상품 정보를 가져와 Cafe24와 자사몰에서 판매하려고 한다.
공유 스프레드시트와 기존 업로드 양식을 기준으로,
상품·옵션·재고·가격·채널 상태를 분리한 내부 데이터 모델을 먼저 제안해줘.
CSV는 최종 출력이고 기준 데이터는 별도 DB에 둔다는 전제로,
중복·품절·가격 누락·옵션 누락을 어떻게 처리할지도 함께 설계해줘.
가격 정책을 정할 때는 이렇게 요청했습니다.
해외 공급가, 환율, 부피무게 기반 배송료, 통관·세금 검토를 포함해
입고원가와 채널별 판매가를 계산하는 정책을 설계해줘.
가산율과 5,000원 단위 정규화를 적용하되,
원가 이하·필수값 누락·가격 급변은 자동 공개하지 않는 규칙을 포함해줘.
계산 버전과 변경 이유를 판매자 화면에서 확인할 수 있어야 해.
검수 단계에서는 다음 질문을 반복했습니다.
이 상품을 바로 공개해도 되는지 판단해줘.
보이는 가격, 구조화된 가격, 옵션·재고 상태가 서로 일치하는지 확인하고,
확신할 수 없는 값은 추측하지 말고 검수 대기로 분류해줘.
고객에게 보여줄 정보와 내부 운영 정보도 분리해서 제안해줘.
앞으로의 방향
앞으로는 이 구조를 특정 상품군에만 쓰는 자동 등록기가 아니라, 해외 상품을 취급하는 판매자가 반복해서 사용할 수 있는 운영 도구로 발전시키고 싶습니다.
상품 수집, 가격 계산, 채널 공개, 주문·정산을 각각 독립적으로 테스트할 수 있게 만들고, 판매자는 정책과 예외만 관리하도록 하는 것이 다음 목표입니다. 동시에 공개 페이지에는 고객에게 필요한 정보만 남기고, 원본 출처와 내부 데이터는 안전한 경계 안에 두는 원칙을 계속 유지하려고 합니다.
비슷하게 해외 상품을 다루거나, 엑셀·CSV 업로드를 반복하고 있다면 처음부터 “어느 채널에 올릴까?”보다 “내가 계속 관리할 기준 데이터는 무엇인가?”부터 정해보시는 것을 권합니다.