> For the complete documentation index, see [llms.txt](https://nsl-solution.gitbook.io/livesolution/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nsl-solution.gitbook.io/livesolution/viewer/web/iframe.md).

# iframe 임베딩

본 페이지에서는 라이브 솔루션 웹뷰어의 URL을 iframe으로 서드파티 서비스 페이지 내에 임베딩하는 방식을 가이드합니다.&#x20;

뷰어 위로 자유롭게 팝업, 토스트 등의 컴포넌트를 올리거나, iframe으로부터 postMessage를 수신하여 이에 따른 비즈니스로직을 구현하실 수 있습니다.

{% hint style="info" %}
**서드파티 서비스의 URL 내에서 iframe이 열리는 경우, 해당 서비스에서 아래 조건을 만족해야 합니다.**

* 연결될 서비스의 도메인이 https 이어야 합니다.
* 자사몰 response header에 `X-Frame-Options` 를 설정하지 않아야 합니다
* iframe 내에서 서드파티 서비스의 결제모듈 등 구매 플로우가 정상적으로 동작하는지 확인해야합니다.
* iframe 부모 창에서 `Access-Control-Allow-Origin: *` 설정을 추가해야합니다.
  {% endhint %}

## 전체화면 노출  <a href="#fullpage" id="fullpage"></a>

라이브 뷰어는 페이지 내 GNB나 푸터 등 다른 요소와 함께 배치되지 않고, 풀페이지로 노출함을 전제합니다. 풀페이지 형태로 노출하지 않는 경우 노출 및 동작의 완결성을 보장하지 않습니다.

뷰어가 임베딩된 DOM element에 대해 다음과 같은 스타일을 지정합니다.&#x20;

```css
.iframe {
    display: block;
    width: 100%; 
    height: 100%;
    border: 0;
}
```

## iframe 영역 추가

다음 예제와 같이 솔루션 뷰어를 iframe으로 임베딩합니다.

```html
<iframe class="iframe" src="https://view.shoppinglive.naver.com/externals/{externalServiceId}/{lives|replays}/{broadcastId}?tr=lisol&fm=livesolution&sn=yourservice"/>
```

{% hint style="info" %}
단, 이 때 소스 URL은 직접 구성하지 않고, [**broadcast API**의 응답값](#user-content-fn-1)[^1]으로 제공합니다.&#x20;

뷰어 파라미터는 상태 등에 따라 동적으로 변경되기 때문에 자체적으로 URL패턴으로 구현하시는 경우, 유저 혜택, 통계 집계 등에 오류가 발생할 수 있습니다.
{% endhint %}

## 이벤트 리스너 <a href="#event-listener" id="event-listener"></a>

iframe과 통신하기 위한 이벤트리스너를 다음과 같이 추가합니다.&#x20;

#### javascript 예제

```javascript
// postMessage 를 받기위해서 하위 이벤트 리스너를 등록해주세요
window.addEventListener('message', handleReceiveMessage)

// 닫기 눌렀을 경우 예시 iFrame 동작가이드입니다.
function handleReceiveMessage(event) {	
    const {type, payload} = event.data	
    switch (type) {		
        case 'history.back’: // 닫기버튼을 누르면 아래 이벤트 타입이 날라옵니다.		
            if (window.history.length > 1) {			
                window.history.back() // 목록에서 접근 시 이전 페이지로 돌아가게 됩니다.
            } else {			
                window.close() // 직접 접근 시 해당 페이지를 닫습니다.		
            }		
            break		
        case 'location.move’:		
            location.href = payload		
            break		
        case 'share.open’:		
            alert(`공유하기가 눌렸습니다.\npayload.broadcastId: ${payload.broadcastId}\npayload.url:${payload.url}`)		
            break
        // 이외 정의된 이벤트 타입에 따른 동작 정의
    }
}
```

### postMessage 데이터 타입

<table><thead><tr><th width="203">TYPE</th><th>DESC</th><th width="219">payload</th><th data-hidden>TYPE</th></tr></thead><tbody><tr><td><code>history.back</code></td><td>뷰어 우상단 닫기버튼 클릭</td><td><p>type: 'history.back’</p><p>payload: {}</p></td><td>HISTORY_BACK</td></tr><tr><td><code>location.move</code></td><td>페이지 이동</td><td><p>type: 'locaiton.move'</p><p>payload: {}</p></td><td>LOCATION_MOVE</td></tr><tr><td><code>viewer.load</code></td><td>뷰어 초기화 완료시 호출</td><td><p>type: 'viewer.load’</p><p>payload: {}</p></td><td>VIEWER_LOAD</td></tr><tr><td><code>video.paused</code></td><td><p>영상 일시정지. </p><p>유저 액션 외 오류, bg전환 등으로 인한 정지를 모두 포함합니다.</p></td><td><p>type: 'video.paused’</p><p>payload: {}</p></td><td>VIDEO_PAUSED</td></tr><tr><td><code>video.playing</code></td><td><p>영상 재생.</p><p>유저 액션 외 뷰어 리프시, FG전환 등으로 인한 재생을 모두 포함합니다.</p></td><td><p>type: 'video.playing’</p><p>payload: {}</p></td><td>VIDEO_PLAYING</td></tr><tr><td><code>broadcast.start</code></td><td>방송이 온에어로 변경되는 경우 호출</td><td><p>type: 'broadcast.start’</p><p>payload: {}</p></td><td>BROADCAST_START</td></tr><tr><td><code>broadcast.end</code></td><td>방송이 종료되어 END로 변경되는 경우 호출</td><td><p>type: 'broadcast.end’</p><p>payload: {}</p></td><td>BROADCAST_END</td></tr><tr><td><code>product.open</code></td><td>상품 클릭시 호출. 상품 타입에 무관하게 클릭에 대해 항상 호출합니다.</td><td><p>type: 'product.open’</p><p>payload: {</p><p>    url: string</p><p>    name: string</p><p>}</p></td><td>PRODUCT_OPEN</td></tr><tr><td><code>share.open</code></td><td>뷰어 더보기>공유하기 버튼 클릭시 호출</td><td><p>type: 'share.open’</p><p>payload: {</p><p>    url: string</p><p>    broadcastId : integer</p><p>}</p></td><td>SHARE_OPEN</td></tr></tbody></table>

## 회원연동 <a href="#member" id="member"></a>

iframe의 `src`로 적용한 뷰어 URL에 쿼리 스트링으로 회원정보를 추가합니다.

<table><thead><tr><th width="118.33333333333331">params</th><th width="109">type</th><th>desc</th></tr></thead><tbody><tr><td>esuk</td><td>string</td><td>서드파티 유저의 유니크키 : 없는 경우 비로그인으로 식별합니다.</td></tr><tr><td>esun</td><td>string</td><td>서드파티 유저의 닉네임(UTF-8 encode) 없는 경우 <a data-footnote-ref href="#user-content-fn-2">랜덤 닉네임</a>을 부여합니다.</td></tr><tr><td>loginUrl</td><td>string</td><td><p>뷰어 이용중 유저가 로그인이 필요한 액션을 하는 경우, 본 <code>loginUrl</code> 설정값으로 랜딩합니다. 이 때 다음과 같이 <code>returnUrl</code> 파라미터로 진입한 뷰어 URL을 전달합니다.</p><pre><code>https://yourservice.com/login?returnUrl={뷰어주소}
</code></pre><p>로그인 처리가 완료되면, 뷰어URL에 esuk, enum을 포함하여 랜딩처리합니다.</p><pre><code><strong>{뷰어주소}?esun=%EB%8B%89%EB%84%A4%EC%9E%84&#x26;esuk=3f1ced532b187eb8a691371
</strong></code></pre></td></tr></tbody></table>

> **예시** view\.shoppinglive.naver.com/externals/{externalServiceId}/lives/{broadcastId}?<mark style="color:blue;">esuk</mark>=a8fYfb1ifFs88&<mark style="color:blue;">esun</mark>=%EB%8B%89%EB%84%A4%EC%9E%84&<mark style="color:blue;">loginUrl</mark>=<https://yourservice.com/member/login>

{% hint style="warning" %}

#### 크로스 도메인으로 적용하는 경우의 한계

웹뷰어를 크로스도메인 환경에서 iframe 형태로 적용하시는 경우, 다음과 같은 기능적 제약이 발생합니다.

* 네이버 로그인 불가 - 네이버 로그인이 필수적인 기능 미노출
* 네이버 쿠폰 다운로드 불가 (쿠폰 버튼 미노출) : 상품상세(새창)에서 다운로드 가능합니다.
* 상품 클릭시 상세 페이지는 새창으로 랜딩됩니다. (크로스도메인 아닌 경우 본창 내 모달 형태로 랜딩)
  {% endhint %}

[^1]: 방송 조회 API 응답 필드 중 activeLinkUrl로 적용하시면, 별도의 방송 상태에 따른 분기처리 없이도 라이브/다시보기 뷰어로 랜딩하도록 구현이 가능합니다.

[^2]: 형용사 + 명사 조합 (ex. 귀여운+고양이)
