

# IVS Web Broadcast SDK를 사용하여 게시 및 구독
<a name="getting-started-pub-sub-web"></a>

이 섹션에서는 웹 앱을 사용하여 스테이지에 게시하고 구독하는 데 관련된 단계를 안내합니다.

## HTML 표준 문안 생성
<a name="getting-started-pub-sub-web-html"></a>

먼저 HTML 표준 문안을 생성하고 라이브러리를 스크립트 태그로 가져오겠습니다.

```
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

  <!-- Import the SDK -->
  <script src="https://web-broadcast.live-video.net/1.33.0/amazon-ivs-web-broadcast.js"></script>
</head>

<body>

<!-- TODO - fill in with next sections -->
<script src="./app.js"></script>

</body>
</html>
```

## 토큰 입력 수락 및 Join/Leave 버튼 추가
<a name="getting-started-pub-sub-web-join"></a>

여기서는 입력 통제로 본문을 채웁니다. 입력 통제는 토큰을 입력으로 사용하고 **Join** 및 **Leave** 버튼을 설정합니다. 일반적으로 애플리케이션은 애플리케이션의 API에서 토큰을 요청하지만 이 예제에서는 토큰을 복사하여 토큰 입력에 붙여넣습니다.

```
<h1>IVS Real-Time Streaming</h1>
<hr />

<label for="token">Token</label>
<input type="text" id="token" name="token" />
<button class="button" id="join-button">Join</button>
<button class="button" id="leave-button" style="display: none;">Leave</button>
<hr />
```

## 미디어 컨테이너 요소 추가
<a name="getting-started-pub-sub-web-media"></a>

이러한 요소에는 로컬 및 원격 참가자를 위한 미디어가 포함됩니다. `app.js`에 정의된 애플리케이션 로직을 로드하는 스크립트 태그를 추가합니다.

```
<!-- Local Participant -->
<div id="local-media"></div>

<!-- Remote Participants -->
<div id="remote-media"></div>

<!-- Load Script -->
<script src="./app.js"></script>
```

이렇게 하면 HTML 페이지가 완성되고 브라우저에서 `index.html`을 로드할 때 다음이 표시됩니다.

![\[브라우저에서 실시간 스트리밍 보기: HTML 설정 완료.\]](http://docs.aws.amazon.com/ko_kr/ivs/latest/RealTimeUserGuide/images/RT_Browser_View.png)


## app.js 생성
<a name="getting-started-pub-sub-web-appjs"></a>

이제 `app.js` 파일의 내용을 정의하도록 하겠습니다. 먼저 SDK의 글로벌에서 필요한 모든 속성을 가져옵니다.

```
const {
  Stage,
  LocalStageStream,
  SubscribeType,
  StageEvents,
  ConnectionState,
  StreamType
} = IVSBroadcastClient;
```

## 애플리케이션 변수 생성
<a name="getting-started-pub-sub-web-vars"></a>

**Join** 및 **Leave** 버튼 HTML 요소에 대한 참조를 포함하고 애플리케이션의 상태를 저장하는 변수를 설정합니다.

```
let joinButton = document.getElementById("join-button");
let leaveButton = document.getElementById("leave-button");

// Stage management
let stage;
let joining = false;
let connected = false;
let localCamera;
let localMic;
let cameraStageStream;
let micStageStream;
```

## joinStage 1 생성: 함수 정의 및 입력 검증
<a name="getting-started-pub-sub-web-joinstage1"></a>

`joinStage` 함수는 입력 토큰을 가져와서 스테이지에 대한 연결을 생성하고 `getUserMedia`에서 검색된 비디오와 오디오를 게시하기 시작합니다.

먼저 함수를 정의하고 상태 및 토큰 입력을 검증합니다. 다음 몇몇 섹션에서 이 함수를 구체화하겠습니다.

```
const joinStage = async () => {
  if (connected || joining) {
    return;
  }
  joining = true;

  const token = document.getElementById("token").value;

  if (!token) {
    window.alert("Please enter a participant token");
    joining = false;
    return;
  }

  // Fill in with the next sections
};
```

## joinStage 2 생성: 게시할 미디어 가져오기
<a name="getting-started-pub-sub-web-joinstage2"></a>

다음은 스테이지에 게시될 미디어입니다.

```
async function getCamera() {
  // Use Max Width and Height
  return navigator.mediaDevices.getUserMedia({
    video: true,
    audio: false
  });
}

async function getMic() {
  return navigator.mediaDevices.getUserMedia({
    video: false,
    audio: true
  });
}

// Retrieve the User Media currently set on the page
localCamera = await getCamera();
localMic = await getMic();

// Create StageStreams for Audio and Video
cameraStageStream = new LocalStageStream(localCamera.getVideoTracks()[0]);
micStageStream = new LocalStageStream(localMic.getAudioTracks()[0]);
```

## joinStage 3 생성: 스테이지 전략 정의 및 스테이지 생성
<a name="getting-started-pub-sub-web-joinstage3"></a>

이 단계 전략은 SDK가 게시할 항목과 구독할 참가자를 결정하는 데 사용하는 결정 로직의 핵심입니다. 함수의 용도에 대한 자세한 내용은 [전략](web-publish-subscribe.md#web-publish-subscribe-concepts-strategy)을 참조하세요.

이 전략은 간단합니다. 스테이지에 참가한 후 방금 검색한 스트림을 게시하고 모든 원격 참가자의 오디오와 비디오를 구독합니다.

```
const strategy = {
  stageStreamsToPublish() {
    return [cameraStageStream, micStageStream];
  },
  shouldPublishParticipant() {
    return true;
  },
  shouldSubscribeToParticipant() {
    return SubscribeType.AUDIO_VIDEO;
  }
};

stage = new Stage(token, strategy);
```

## joinStage 4 생성: 스테이지 이벤트 처리 및 미디어 렌더링
<a name="getting-started-pub-sub-web-joinstage4"></a>

스테이지는 많은 이벤트를 내보냅니다. 페이지에서 미디어를 렌더링하고 제거하려면 `STAGE_PARTICIPANT_STREAMS_ADDED`와 `STAGE_PARTICIPANT_LEFT`를 수신해야 합니다. [이벤트](web-publish-subscribe.md#web-publish-subscribe-concepts-events)에는 보다 포괄적인 이벤트 세트가 나열됩니다.

여기서는 필요한 DOM 요소를 관리하는 데 도움이 되는 4가지 도우미 함수인 `setupParticipant`, `teardownParticipant`, `createVideoEl` 및 `createContainer`를 생성합니다.

```
stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {
  connected = state === ConnectionState.CONNECTED;

  if (connected) {
    joining = false;
    joinButton.style = "display: none";
    leaveButton.style = "display: inline-block";
  }
});

stage.on(
  StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED,
  (participant, streams) => {
    console.log("Participant Media Added: ", participant, streams);

    let streamsToDisplay = streams;

    if (participant.isLocal) {
      // Ensure to exclude local audio streams, otherwise echo will occur
      streamsToDisplay = streams.filter(
        (stream) => stream.streamType === StreamType.VIDEO
      );
    }

    const videoEl = setupParticipant(participant);
    streamsToDisplay.forEach((stream) =>
      videoEl.srcObject.addTrack(stream.mediaStreamTrack)
    );
  }
);

stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {
  console.log("Participant Left: ", participant);
  teardownParticipant(participant);
});


// Helper functions for managing DOM

function setupParticipant({ isLocal, id }) {
  const groupId = isLocal ? "local-media" : "remote-media";
  const groupContainer = document.getElementById(groupId);

  const participantContainerId = isLocal ? "local" : id;
  const participantContainer = createContainer(participantContainerId);
  const videoEl = createVideoEl(participantContainerId);

  participantContainer.appendChild(videoEl);
  groupContainer.appendChild(participantContainer);

  return videoEl;
}

function teardownParticipant({ isLocal, id }) {
  const groupId = isLocal ? "local-media" : "remote-media";
  const groupContainer = document.getElementById(groupId);
  const participantContainerId = isLocal ? "local" : id;

  const participantDiv = document.getElementById(
    participantContainerId + "-container"
  );
  if (!participantDiv) {
    return;
  }
  groupContainer.removeChild(participantDiv);
}

function createVideoEl(id) {
  const videoEl = document.createElement("video");
  videoEl.id = id;
  videoEl.autoplay = true;
  videoEl.playsInline = true;
  videoEl.srcObject = new MediaStream();
  return videoEl;
}

function createContainer(id) {
  const participantContainer = document.createElement("div");
  participantContainer.classList = "participant-container";
  participantContainer.id = id + "-container";

  return participantContainer;
}
```

## joinStage 5 생성: 스테이지에 참가
<a name="getting-started-pub-sub-web-joinstage5"></a>

드디어 스테이지에 참가하여 `joinStage` 함수를 완성해 보겠습니다.

```
try {
  await stage.join();
} catch (err) {
  joining = false;
  connected = false;
  console.error(err.message);
}
```

## leaveStage 생성
<a name="getting-started-pub-sub-web-leavestage"></a>

leave 버튼이 간접적으로 호출할 `leaveStage` 함수를 정의합니다.

```
const leaveStage = async () => {
  stage.leave();

  joining = false;
  connected = false;
};
```

## 입력 이벤트 핸들러 초기화
<a name="getting-started-pub-sub-web-handlers"></a>

`app.js` 파일에 마지막 함수를 하나 추가하겠습니다. 이 함수는 페이지가 로드될 때 즉시 간접적으로 호출되고 스테이지 참가 및 탈퇴를 위한 이벤트 핸들러를 설정합니다.

```
const init = async () => {
  try {
    // Prevents issues on Safari/FF so devices are not blank
    await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
  } catch (e) {
    alert(
      "Problem retrieving media! Enable camera and microphone permissions."
    );
  }

  joinButton.addEventListener("click", () => {
    joinStage();
  });

  leaveButton.addEventListener("click", () => {
    leaveStage();
    joinButton.style = "display: inline-block";
    leaveButton.style = "display: none";
  });
};

init(); // call the function
```

## 애플리케이션 실행 및 토큰 제공
<a name="getting-started-pub-sub-run-app"></a>

이 부분에서는 로컬에서 또는 다른 사용자와 웹 페이지를 공유하고, [페이지를 열고](#getting-started-pub-sub-web-media), 참가자 토큰을 입력하여 스테이지에 참가할 수 있습니다.

## 다음 단계
<a name="getting-started-pub-sub-next"></a>

npm, React 등과 관련된 자세한 예제를 확인하려면 [IVS Broadcast SDK: 웹 안내서(실시간 스트리밍 가이드)](broadcast-web.md)를 참조하세요.