GuidesAPI GuideChangelog
Log In
Guides

상품 클릭 연동

상품 클릭 연동 — 소스라이브 플레이어

플레이어에서 상품을 클릭했을 때 sauceflexMoveProduct 이벤트가 전송됩니다. 이벤트를 수신하여 현재 창 이동, 새 창 열기, 상품 코드 기반 커스텀 라우팅 등 자사몰에 맞는 동작을 자유롭게 정의할 수 있습니다.

예상 소요 시간: 15분
📋 사전 조건: 브릿지 이벤트 연동 완료
플레이어 상품 목록
플레이어 상품 목록 패널
▲ 플레이어의 상품 목록 — 상품을 클릭하면 sauceflexMoveProduct 이벤트가 전송됩니다
이벤트 데이터

sauceflexMoveProduct 이벤트 수신 시 jsonData.params에 아래 필드가 담겨 전달됩니다. 상황에 맞는 필드를 선택하여 활용하세요.

필드 타입 필수 설명
linkUrl String 필수 어드민에 등록된 상품 페이지 링크 URL
broadcastIdx String 필수 이벤트가 발생한 라이브 ID
price String | Number 필수 상품 가격
externalProductId String | null 선택 자사몰에서 등록한 상품 코드. 커스텀 라우팅에 활용합니다.
isSoldout Boolean 필수 품절 여부. true이면 품절 상태입니다.
구현 방법

자사몰의 상품 이동 방식에 맞는 탭을 선택하세요. 세 가지 패턴 중 하나를 선택하거나 조합하여 사용할 수 있습니다.

🧭 어떤 방식을 선택해야 하나?
단순한 쇼핑몰이라면 현재 창 이동이 가장 간단합니다. 라이브를 보면서 상품을 구경하게 하려면 새 창 열기를 선택하세요. 자사몰의 라우팅 URL 체계가 있고, 어드민에서 외부 상품 코드를 등록했다면 상품코드 기반 라우팅이 가장 유연합니다.
현재 창에서 상품 페이지로 이동합니다. 가장 기본적인 패턴으로, 어드민에 등록된 linkUrl을 그대로 사용합니다.
JavaScript
window.addEventListener('message', (e) => {
  if (typeof e.data !== 'string') return
  const jsonData = JSON.parse(e.data)

  switch (jsonData.key) {
    case 'sauceflexMoveProduct': {
      const { linkUrl } = jsonData.params
      if (!linkUrl) return

      // 현재 창에서 상품 페이지로 이동
      window.location.href = linkUrl
      break
    }
  }
})
새 창(탭)에서 상품 페이지를 엽니다. 라이브 시청을 끊지 않고 상품을 확인할 때 유용합니다.
JavaScript
window.addEventListener('message', (e) => {
  if (typeof e.data !== 'string') return
  const jsonData = JSON.parse(e.data)

  switch (jsonData.key) {
    case 'sauceflexMoveProduct': {
      const { linkUrl } = jsonData.params
      if (!linkUrl) return

      // 새 창(탭)에서 상품 페이지 열기
      window.open(linkUrl, '_blank')
      break
    }
  }
})
자사몰의 상품 코드(externalProductId)를 기반으로 원하는 URL을 직접 구성합니다. 자사몰 라우팅 규칙에 따라 URL을 동적으로 생성할 때 사용합니다.
JavaScript
window.addEventListener('message', (e) => {
  if (typeof e.data !== 'string') return
  const jsonData = JSON.parse(e.data)

  switch (jsonData.key) {
    case 'sauceflexMoveProduct': {
      const { externalProductId } = jsonData.params

      // externalProductId가 없으면 처리하지 않음
      if (!externalProductId) return

      // 상품 코드를 이용해 자사몰 URL 구성
      window.location.href = `/products/${externalProductId}`
      break
    }
  }
})
💡 품절 상품 처리: isSoldouttrue인 경우 별도 안내 메시지를 표시하거나 클릭을 차단하려면 if (jsonData.params.isSoldout) return 조건을 추가하세요.
실전 예시 — 조건 분기 처리

externalProductId가 있으면 자사몰 라우팅을, 없으면 linkUrl로 폴백하는 방어적 패턴입니다. 실제 서비스에서 가장 많이 사용하는 구현 방식입니다.

JavaScript — 조건 분기 + 폴백
window.addEventListener('message', (e) => {
  if (typeof e.data !== 'string') return
  const jsonData = JSON.parse(e.data)

  switch (jsonData.key) {
    case 'sauceflexMoveProduct': {
      const { linkUrl, externalProductId, isSoldout } = jsonData.params

      // 품절 상품 클릭 시 얼리 리턴
      if (isSoldout) {
        alert('현재 품절된 상품입니다.')
        return
      }

      // externalProductId가 있으면 자사몰 라우팅 우선
      if (externalProductId) {
        window.location.href = `/products/${externalProductId}`
      } else if (linkUrl) {
        // 없으면 어드민 등록 linkUrl로 폴백
        window.location.href = linkUrl
      }
      break
    }
  }
})
Kotlin — SauceflexBridge.kt
class SauceflexBridge(
    private val context: Context,
    private val activity: Activity?,
    private val webView: WebView
) {
    @JavascriptInterface
    fun sauceflexMoveProduct(payload: String?) {
        val params = JSONObject(payload ?: "{}")
        val isSoldout      = params.optBoolean("isSoldout", false)
        val externalId     = params.optString("externalProductId", "")
        val linkUrl        = params.optString("linkUrl", "")

        // 품절 상품 — 안내 후 리턴
        if (isSoldout) {
            activity?.runOnUiThread {
                Toast.makeText(context, "현재 품절된 상품입니다.", Toast.LENGTH_SHORT).show()
            }
            return
        }

        val targetUrl = if (externalId.isNotEmpty()) {
            "https://myshop.com/products/$externalId"    // 자사몰 라우팅
        } else {
            linkUrl                                         // 폴백
        }

        if (targetUrl.isEmpty()) return
        activity?.runOnUiThread {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(targetUrl))
            context.startActivity(intent)
        }
    }
}
Kotlin — WebView 등록
webView.addJavascriptInterface(SauceflexBridge(context, activity, webView), "sauceflex")
Swift — WKWebView 등록
contentController.add(self, name: "sauceflexMoveProduct")
Swift — ViewController.swift
func userContentController(
    _ userContentController: WKUserContentController,
    didReceive message: WKScriptMessage
) {
    switch message.name {
    case "sauceflexMoveProduct":
        guard let body = message.body as? String,
              let data = body.data(using: .utf8),
              let params = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
        else { return }

        let isSoldout    = params["isSoldout"] as? Bool ?? false
        let externalId   = params["externalProductId"] as? String ?? ""
        let linkUrl      = params["linkUrl"] as? String ?? ""

        // 품절 상품 — 얼리 리턴
        if isSoldout {
            DispatchQueue.main.async {
                let alert = UIAlertController(title: nil, message: "현재 품절된 상품입니다.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "확인", style: .default))
                self.present(alert, animated: true)
            }
            return
        }

        let targetUrl = !externalId.isEmpty
            ? "https://myshop.com/products/\(externalId)"   // 자사몰 라우팅
            : linkUrl                                              // 폴백

        guard !targetUrl.isEmpty(), let url = URL(string: targetUrl) else { return }
        DispatchQueue.main.async {
            UIApplication.shared.open(url)
        }
    default: break
    }
}
externalProductId 등록: 소스라이브 어드민에서 상품 추가 시 자사몰의 상품 코드를 외부 상품 코드 필드에 입력하면 externalProductId가 전달됩니다.


bot에 문의하기