GeolocationCoordinates: speed 속성 - Web API

GeolocationCoordinates: speed 속성
================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`GeolocationCoordinates.speed`** 읽기 전용 속성은 장치의 속도를 초속 미터 단위로 나타낸 `double`입니다. 구현체가 속도를 측정하지 못하면 이 값은 `null`입니다.

값
-

장치의 속도를 초속 미터 단위로 나타낸 `double`입니다.

명세서
---

| Specification |
| --- |
| Geolocation API # speed-attribute |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `GeolocationCoordinates`


GeolocationCoordinates: longitude 속성 - Web API

GeolocationCoordinates: longitude 속성
====================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

`GeolocationCoordinates` 인터페이스의 읽기 전용 **`longitude`** 속성은 위치의 경도를 십진수 도(°, DD)로 나타낸 숫자입니다. `GeolocationCoordinates` 객체는 측정 시간을 나타내는 밀리초 Unix 시간의 타임스탬프와 함께 `GeolocationPosition` 인터페이스를 구성하며, `GeolocationPosition`은 Geolocation API에서 지리적 위치를 반환하는 함수들의 반환 타입입니다.

값
-

`longitude`의 값은 `Coordinates` 객체가 나타내는 지구상 위치의 경도를 십진수 도(°, DD)로 나타낸 숫자입니다. 이 값은 World Geodetic System 1984 명세 (WGS 84)가 정의합니다.

**참고:** 0°의 본초 자오선은 대부분의 사람들이 알고 있는 그리니치 자오선과 정확히 일치하지는 않습니다. 0°는 IERS 기준 자오선으로, 그리니치 자오선에서 5.3 아크초(102미터) 동쪽에 위치합니다. 이 표준은 GPS가 사용하는 것과 같습니다.

예제
--

다음의 간단한 예제에서는 사용자의 위치를 가져와서 그 좌표를 출력합니다.

### JavaScript

아래 JavaScript 코드는 사용자가 버튼을 클릭하면 위치 정보를 가져와서 출력하는 이벤트 수신기를 생성합니다.

```js
let button = document.getElementById("get-location");
let latText = document.getElementById("latitude");
let longText = document.getElementById("longitude");

button.addEventListener("click", () => {
  navigator.geolocation.getCurrentPosition((position) => {
    let lat = position.coords.latitude;
    let long = position.coords.longitude;

    latText.innerText = lat.toFixed(2);
    longText.innerText = long.toFixed(2);
  });
});
```

버튼과 위도/경도를 각각 표시할 두 요소에 대한 참조를 쉽게 하기 위해 변수를 설정한 후, `<button>`의 `addEventListener()`를 호출해서 이벤트 수신기는 부착하는 모습입니다. 사용자가 이 버튼을 클릭하면 위치 정보를 가져오고 출력할 것입니다.

`click` 이벤트를 수신하면, 우리 코드는 `getCurrentPosition()`을 호출해 장치의 현재 위치를 요청합니다. 이 요청은 비동기 작업이므로 요청 완료 시 호출될 콜백을 제공하고, 이 콜백은 알아낸 위치를 나타내는 `GeolocationPosition` 객체를 입력받습니다.

이 `GeolocationPosition` 객체에서 사용자의 위도와 경도를 각각 `position.coords.latitude`와 `position.coords.longitude`로 가져옵니다. 두 값을 소숫점 아래 두 자리까지의 값으로 변환한 후, `<span>` 요소들이 두 값을 각각 표시하도록 갱신합니다.

### HTML

결과를 출력하기 위한 HTML은 다음과 같습니다.

```html
<p>
  당신의 위치는 위도 <span id="latitude">0.00</span>°, 경도
  <span id="longitude">0.00</span>° 입니다.
</p>
<button id="get-location">내 위치 찾기</button>
```

### 결과

여기서 예제를 시험해 보세요.

명세서
---

| Specification |
| --- |
| Geolocation API # latitude-longitude-and-accuracy-attributes |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* 이 속성이 속한 `GeolocationCoordinates`
* `Geolocation.getCurrentPosition()`과 `watchPosition()`이 가져오는 위치정보를 나타내는 최상위 인터페이스, `GeolocationPosition`


GeolocationCoordinates: heading 속성 - Web API

GeolocationCoordinates: heading 속성
==================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`GeolocationCoordinates.heading`** 읽기 전용 속성은 장치가 이동 중인 방위를 나타내는 `double`입니다. 이 값은 장치가 북쪽에서 벗어난 각도를 도(°) 단위로 표현하며, 진북(真北)의 `0`도부터 시계방향으로 진행하므로 `90`도는 동쪽, `270`도는 서쪽입니다. `heading`은 `GeolocationCoordinates.speed`가 `0`이면 `NaN`이고, 장치가 방위 정보를 제공하지 못하면 `null`입니다.

값
-

장치가 이동 중인 방위를 나타내는 `double`입니다.

명세서
---

| Specification |
| --- |
| Geolocation API # heading-attribute |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `GeolocationCoordinates`


GeolocationCoordinates: accuracy 속성 - Web API

GeolocationCoordinates: accuracy 속성
===================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`GeolocationCoordinates.accuracy`** 읽기 전용 속성은 `GeolocationCoordinates.latitude`와 `GeolocationCoordinates.longitude`의 오차범위(신뢰수준 95%)를 미터 단위로 표현한, 엄격히 양수로 제한된 `double`입니다.

값
-

`GeolocationCoordinates.latitude`와 `GeolocationCoordinates.longitude`의 오차범위(신뢰수준 95%)를 미터로 나타낸 양의 `double`입니다.

명세서
---

| Specification |
| --- |
| Geolocation API # latitude-longitude-and-accuracy-attributes |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `GeolocationCoordinates`


GeolocationCoordinates: altitudeAccuracy 속성 - Web API

GeolocationCoordinates: altitudeAccuracy 속성
===========================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`GeolocationCoordinates.altitudeAccuracy`** 읽기 전용 속성은 `GeolocationCoordinates.altitude`의 오차범위(신뢰수준 95%)를 미터 단위로 표현한, 엄격히 양수로 제한된 `double`입니다. 구현체가 고도 데이터를 제공하지 못하면 이 값은 `null`입니다.

값
-

`GeolocationCoordinates.altitude`의 오차범위(신뢰수준 95%)를 미터로 나타낸 양의 `double`입니다.

명세서
---

| Specification |
| --- |
| Geolocation API # altitude-and-altitudeaccuracy-attributes |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `GeolocationCoordinates`


GeolocationCoordinates: altitude 속성 - Web API

GeolocationCoordinates: altitude 속성
===================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`GeolocationCoordinates.altitude`** 읽기 전용 속성은 평균 해수면을 정의하는 WGS84 타원체를 기준으로 했을 때, 이 위치의 고도를 미터 단위로 표현한 `double`입니다. 구현체가 고도 데이터를 제공하지 못하면 이 값은 `null`입니다.

값
-

WGS84 타원체를 기준으로 한 미터 단위의 고도를 나타내는 `double`입니다.

명세서
---

| Specification |
| --- |
| Geolocation API # altitude-and-altitudeaccuracy-attributes |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* 이 속성이 속한 `GeolocationCoordinates`
* National Imagery and Mapping Agency Technical Report 8350.2, Third Edition (WGS84)


DOMRect() - Web API

DOMRect()
=========

**`DOMRect()`** 생성자는 새로운 `DOMRect` 객체를 생성합니다.

구문
--

```js
new DOMRect(x, y, width, height);
```

### 매개변수

`x`

`DOMRect` 원점의 X 좌표입니다.

`y`

`DOMRect` 원점의 Y 좌표입니다.

`width`

`DOMRect`의 너비입니다.

`height`

`DOMRect`의 높이입니다.

### 반환 값

새로운 `DOMRect` 객체.

명세
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-domrect-domrect |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `DOMPoint` (en-US)
* `DOMRect`
* `DOMRect.fromRect()`


DOMRect.fromRect() - Web API

DOMRect.fromRect()
==================

`DOMRect` 객체의 **`fromRect()`** 정적 메서드는 주어진 좌표와 크기를 가진 새로운 `DOMRect` 객체를 반환합니다.

구문
--

```js
DOMRect.fromRect(rectangle);
```

### 매개변수

`rectangle` Optional

직사각형의 위치와 크기를 지정하는 객체입니다. 모든 속성의 기본 값은 `0`입니다. 포함할 수 있는 속성은 다음과 같습니다.

* `x`: 직사각형 왼쪽 모서리의 좌표입니다.
* `y`: 직사각형 위쪽 모서리의 좌표입니다.
* `width`: 직사각형의 너비입니다.
* `height`: 직사각형의 높이입니다.

### 반환 값

`DOMRect` 인스턴스.

명세
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-domrect-fromrect |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


HTMLCollection.item - Web API

HTMLCollection.item
===================

`HTMLCollection` 의 메소드 `item()` 은 컬렉션 안의 특정 인덱스에 위치한 노드를 반환합니다.

**참고:**`HTMLCollection`은 실시간이기 때문에, DOM을 변경하면 컬렉션 내의 노드도 변경됩니다. 따라서, 한 노드의 인덱스 값이 항상 일정하지는 않습니다.

Syntax
------

```js
var element = HTMLCollection.item(index);
```

### 파라미터

`index`

반환받을 `Node` (en-US)의 위치. `HTMLCollection`에 들어있는 요소들은 도큐먼트에 나타나는 순서와 동일합니다.

### 반환값

주어진 인덱스의 `Node` (en-US). `index`가 0보다 작거나 length 속성보다 크다면 `null`을 반환합니다.

참고사항
----

`item()` 메소드는 `HTMLCollection`으로부터 순서가 매겨진 하나의 요소를 반환합니다. JavaScript에서, `HTMLCollection`을 배열처럼 다루는건 아주 쉽습니다. 아래의 예시를 보세요.

Example
-------

```js
var c = document.images; // HTMLCollection입니다
var img0 = c.item(0); // 이렇게 item() 메소드를 이용할 수 있지만
var img1 = c[1]; // 이렇게 표기하는게 쉽고 더 보편적입니다
```

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `NodeList.item()`


HTMLCanvasElement.getContext() - Web API

HTMLCanvasElement.getContext()
==============================

**`HTMLCanvasElement.getContext()`** 메소드는 캔버스의 드로잉 컨텍스트를 반환합니다. 컨텍스트 식별자가 지원되지 않을 경우 `null`을 반환합니다.

동일한 캔버스 엘리먼트에서 나중에 이 메소드를 호출하면 동일한 `contextType` 인자와 함께 메소드가 마지막으로 호출되었을 때 반환된 것과 같이 동일한 드로잉 컨텍스트 인스턴스를 반환합니다. 다른 드로잉 컨텍스트 객체를 얻으려면 다른 `contextType`을 전달하거나 다른 캔버스 엘리먼트에서 메소드를 호출해야 합니다.

구문
--

```js
var ctx = canvas.getContext(contextType);
var ctx = canvas.getContext(contextType, contextAttributes);
```

### 파라미터

`contextType`

캔버스에 연관된 드로잉 컨텍스트를 정의하는 컨텍스트 식별자를 갖는 `DOMString`입니다. 가능한 값은 다음과 같습니다.\* `"2d"`, 2차원 렌더링 컨텍스트를 나타내는 `CanvasRenderingContext2D` (en-US) 객체를 생성하게 합니다.

* `"webgl"` (또는 `"experimental-webgl"`), 3차원 렌더링 컨텍스트를 나타내는 `WebGLRenderingContext` (en-US) 객체를 생성합니다. 이 컨텍스트는 WebGL 버전 1 (OpenGL ES 2.0)을 구현하는 브라우저에서만 사용가능합니다.
* `"webgl2"`, 3차원 렌더링 컨텍스트를 나타내는 `WebGL2RenderingContext` (en-US) 객체를 생성합니다. 이 컨텍스트는 WebGL 버전 2 (OpenGL ES 3.0)를 구현하는 브라우저에서만 사용가능합니다.
Experimental
* `"bitmaprenderer"`, 캔버스의 컨텐츠를 주어진 `ImageBitmap` (en-US)으로 대체하기위한 기능만을 제공하는 `ImageBitmapRenderingContext` (en-US)를 생성합니다.> **참고:** 식별자 `"experimental-webgl"`은 WebGL의 새로운 구현에서 사용됩니다. 이러한 구현은 테스트 스위트 적합성을 아직 만족하지 못하며, 플랫폼 상의 그래픽 드라이버도 아직 불안정합니다. Khronos Group은 특정 적합성 규칙에 따라 WebGL 구현을 인증합니다.

`contextAttributes`

렌더링 컨텍스트를 생성할 때 몇 가지 컨텍스트 속성을 사용할 수 있습니다. 예를 들면:`js const gl = canvas.getContext('webgl', { antialias: false, depth: false });` 2d 컨텍스트 속성:

* **`alpha`**: 캔버스가 알파 채널을 포함하는지를 나타내는 불리언입니다. `false`로 설정할 경우, 브라우저는 이제 배경이 항상 투명하다는 것을 알기때문에 투명한 컨텐츠나 이미지를 그리는 속도를 높일수 있습니다.
* 비표준
 (Gecko 전용) **`willReadFrequently`**: 많은 다시 읽기 작업이 계획되어있는지 여부를 나타내는 불리언입니다. 이는 소프트웨어(하드웨어 가속 대신) 2D 캔버스의 사용을 강제하며 `getImageData()` (en-US) 호출이 빈번할때 메모리를 절약할 수 있습니다. 이 옵션은 `gfx.canvas.willReadFrequently.enable` 플래그가 `true`(기본 값이며, B2G/Firefox OS에만 해당) 설정되었을 경우에만 사용가능합니다.
* 비표준
 (Blink only) **`storage`**: 어떤 스토리지가 사용되었는지를 나타내는 문자열입니다(기본값은 "persistent").WebGL 컨텍스트 속성:\* **`alpha`**: 캔버스가 알파 버퍼를 포함하는지 여부를 나타내는 불리언입니다.
* **`antialias`**: 안티 앨리어싱을 수행할지 여부를 나타내는 불리언입니다.
* **`depth`**: 드로잉 버퍼가 최소 16 비트의 깊이 버퍼를 갖는지 여부를 나타내는 불리언입니다.
* **`failIfMajorPerformanceCaveat`**: 시스템 성능이 낮을 경우에 컨텍스트를 생성할지 여부를 나타내는 불리언입니다.
* **`powerPreference`**: WebGL 컨텍스트에 대해 적합한 GPU 구성이 무엇인지를 나타내는 유저 에이전트에 대한 힌트입니다. 가능한 값은 다음과 같습니다.
    + `"default"`: 유저 에이전트가 가장 적합한 GPU 구성을 결정하도록 합니다. 기본 값입니다.
    + `"high-performance"`: 전력 소비보다 렌더링 성능을 우선시합니다.
    + `"low-power"`: 렌더링 성능보다 전력 절약을 우선시합니다.
* **`premultipliedAlpha`**: 페이지 컴포지터가 미리 곱해진 알파를 갖는 컬러를 포함하는 드로잉 버퍼를 가정할 것인지 여부를 나타내는 불리언입니다.
* **`preserveDrawingBuffer`**: 값이 true일 경우 버퍼는 제거되지 않으며 값이 제거되거나 덮어쓰여지기 전까지 유지됩니다.
* **`stencil`**: 드로잉 버퍼가 최소 8 비트의 스텐실 버퍼를 갖는지 여부를 나타내는 불리언입니다.

### 반환 값

`RenderingContext`는 다음 중 하나입니다.

* `"2d"` 용 `CanvasRenderingContext2D` (en-US),
* `"webgl"` 및 `"experimental-webgl"` 용 `WebGLRenderingContext` (en-US),
* `"webgl2"` 용 `WebGL2RenderingContext` (en-US)
* `"bitmaprenderer"` 용 `ImageBitmapRenderingContext` (en-US).

`contextType`이 가능한 드로잉 컨텍스트와 일치하지 않으면, `null`이 반환됩니다.

예시
--

다음 `<canvas>` 엘리먼트를 고려해볼 때:

```html
<canvas id="canvas" width="300" height="300"></canvas>
```

다음 코드를 사용해 캔버스의 `2d` 컨텍스트를 얻을 수 있습니다.

```js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
console.log(ctx); // CanvasRenderingContext2D { ... }
```

이제 캔버스에 대한 2D 렌더링 컨텍스트 (en-US)를 갖고 있으며 이 안에 그릴 수 있습니다.

명세
--

| Specification |
| --- |
| HTML Standard # dom-canvas-getcontext-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

함께 보기
-----

* 이를 정의하는 인터페이스 `HTMLCanvasElement`.
* `OffscreenCanvas.getContext()` (en-US)
* 사용 가능한 렌더링 컨텍스트: `CanvasRenderingContext2D` (en-US), `WebGLRenderingContext` (en-US), `WebGL2RenderingContext` (en-US), `ImageBitmapRenderingContext` (en-US).


Signaling and video calling - Web API

Signaling and video calling
===========================

**참고:** 이 글은 편집 및 검토가 필요하다. 도움을 줄 수 있는 방법을 살펴보자.WebRTC는 아직까지 **실험적인 기술이다.**
 일부의 기술 스펙이 안정화가 되지 않았기 때문에 각 브라우져에서 사용가능한 호환성 정보를 확인해야한다. 또한, 기술의 문법과 패턴들은 스펙이 바뀌는 것처럼 브라우져의 버전이 높아진다면 변경될 수 있다.

Summary
-------

WebRTC 는 리얼 타임 음성, 영상, 데이터 교환을 할 수 있는 완전한 p2p 기술이다. 다른 곳에서 논의한 것 처럼 (en-US) 서로 다른 네트워크에 있는 2개의 디바이스들을 서로 위치시키기 위해서는, 각 디바이스들의 위치를 발견하는 방법과 미디어 포맷 협의가 필요하다. 이 프로세스를 **시그널링** **signaling** 이라 부르고 각 디바이스들을 상호간에 동의된 서버(socket.io 혹은 websocket을 이용한 서버)에 연결시킨다. 이 서버는 각 디바이스들이 **negotiation**(협의) 메세지들을 교환할 수 있도록 한다.

이 글에서 우리는 더 나아가 유저들간에 양방향으로 화상 통화가 되는 예제인 WebSocket chat(웹소켓 문서를 작성하기 위해 만들어졌으며, 링크는 곧 활성화 될 것이다. 아직은 온라인으로 테스트가 불가능하다.)을 작동이 되도록 만들 예정이다. 이것에 관해 샘플 을 확인해 보거나 Github에서 전체 프로젝트를 확인해볼 수 있다.

**참고:** 깃헙에 있는 테스트 서버 코드는 앞으로 공부할 예제 코드보다 최신 버전이다. 이 글은 현재 업데이트 진행 중이며, 곧 완료될 예정이다. 업데이트가 완료된다면 이 글을 사라질 것이다.

**참고:** 앞으로 나올 예제들은 **promise** 를 사용한다. 만약 너가 이것을 잘 모른다면 이 글을 읽어 보길 바란다.

The signaling server
--------------------

두 디바이스들 사이에 WebRTC 커넥션을 만들기 위해, 인터넷 네트워크에서 그 둘을 연결 시키는 작업을 해줄 **signaling server** 가 필요하다. 어떻게 이 서버를 만들고 실제로 시그널링 과정이 어떻게 되는지 살펴보자.

가장 먼저, 시그널링 서버 자체가 필요하다. WebRTC는 시그널링 정보에 관한 transport 메커니즘을 제시하진 않는다. 두 피어들 사이에서 해리포터의 부엉이처럼 시그널링에 관련된 정보들을 전달해줄 수 있는 것이면 WebSocket 이든 XMLHttpRequest 든 상관없다.

여기서 중요한 점은 시그널링 서버는 시그널링 데이터 내용을 몰라도 된다는 것이다. 비록 이것은 SDP 이지만, 몰라도 큰 문제가 되진 않는다. 메세지의 내용들은 그저 시그널링 서버를 통해 상대편으로 가기만 하면된다. 중요한 점은 ICE subsystem이 신호 데이터를 다른 피어에게 보내도록 지시하면, 다른 피어는 이 정보를 수신하여 자체 ICE subsystem에 전달하는 방법을 알고 있다는 것이다.

### Readying the chat server for signaling

이 chat server 는 클라이언트와 서버 사이에 WebSocket API을 통해 JSON string으로 데이터를 전송한다. 서버는 새로운 유저를 등록하는 것, username을 세팅하는 것, 채팅 메세지를 전송하는 것 등등의 작업들을 하기 위해 다양한 메세지 타입들을 다룬다. 시그널링과 ICE negotiation 을 서버가 처리하기 위해서 코드를 작성해야한다. 모든 로그인된 유저들에게 브로드캐스팅하는 것이 아니라, 특정한 유저에게 직접 메세지를 전달해야한다. 그리고 서버가 따로 처리할 필요 없이, 수신된 원하지 않은 메세지 타입들을 처리한다. 이를 통해 여러 서버를 만들 필요없이 동일한 서버를 이용하여 시그널 메시지를 보낼 수 있다. 이 개념은 WebRTC가 아니라 WebSocket에 관한 개념이다.

이제, WebRTC 시그널링을 지원하는 chat server를 만들기 위해 어떻게 해야하는지 보자. 앞으로 나오는 코드들은 chatserver.js 안에 있는 코드이다.

우선 `sendToOneUser()`함수를 추가하자. 이름이 말하듯, stringified JSON 메세지를 특정한 유저에게 보내는 것이다.

```js
function sendToOneUser(target, msgString) {
  var isUnique = true;
  var i;

  for (i = 0; i < connectionArray.length; i++) {
    if (connectionArray[i].username === target) {
      connectionArray[i].sendUTF(msgString);
      break;
    }
  }
}
```

이 함수는 연결된 유저 리스트를 확인하면서 특정한 username을 가지는 유저을 찾고, 이 유저에게 메세지를 보낸다. 함수의 인자로 들어가는 메시지 `msgString`은 stringified JSON object 이다. Stringified 가 아닌 원본의 메세지 object를 받도록 해도 되지만, JSON이 더 유용하고 편하다. 이 메세지는 이미 stringified 된 상태로 함수에 전달되기 때문에, 더 이상의 메세지에 관한 처리 없이 메세지를 그대로 보내기만 하면 된다.

원본 chat demo는 특정 유저에게 메세지를 보내는 것을 지원하지 않는다. Main WebSocket message handler를 수정해야 이것이 가능하게 되며, 구체적으로는`connection.on()`함수의 마지막 부분을 수정하면 된다.

```js
if (sendToClients) {
  var msgString = JSON.stringify(msg);
  var i;

  // If the message specifies a target username, only send the
  // message to them. Otherwise, send it to every user.
  if (msg.target && msg.target !== undefined && msg.target.length !== 0) {
    sendToOneUser(msg.target, msgString);
  } else {
    for (i = 0; i < connectionArray.length; i++) {
      connectionArray[i].sendUTF(msgString);
    }
  }
}
```

이 코드는 메세지에서 `target` 특성이 정의되었는지 체크한다. 이 특성은 메세지를 전달하고 싶은 사람의 username으로 정의할 수 있다. 만약 `target`파라미터가 존재한다면, `sendToOneUser()`함수를 콜하면서 그 유저에게 메세지를 전송한다. 그렇지 않다면, 모든 유저에게 메세지를 브로드케스트를 한다.

위에 있는 코드는 별도의 수정이 필요 없이 임의의 메세지 타입들을 보낼 수 있다. 클라이언트들은 이제 특정한 유저에게 unknown 타입의 메세지도 보낼수 있고, 시그널링 메세지를 원하는 대로 보낼 수 있다. 구체적인 내용은 다음을 살펴보자.

### Designing the signaling protocol

이제 우리는 메세지를 교환하는 메커니즘을 만들었다. 이제 메세지들을 어떻게 구성할지에 대한 프로토콜이 필요하다. 이것은 여러 가지 방법으로 가능한데, 여기서 다루는 것은 그 중 하나의 시그널링 메세지 구조이다.

우리가 제공하는 시그널링 서버는 stringified JSON object 을 가지고 클라언트간에 데이터를 주고 받는다. 즉, 이것은 시그널링 메세지들이 JSON format으로 되어있으며, 메세지의 type 등 메세지를 적절하게 처리할 수 있도록 여러 정보들이 포함되어 있다.

#### Exchanging session descriptions

시그널링 프로세스를 시작할 때, call을 시작 하는 유저가 **\*offer** \*란 것을 만든다. 이 offer는 세션 정보를 SDP 포맷으로 가지고 있으며, 커넥션이 이어지기를 원하는 유저(callee)에게 전달되어야 한다. Callee 는 이 offer에 SDP description을 포함하는 **\*answer** \*메세지를 보내야한다. 우리가 사용할 offer 메세지들은 `"video-offer"` 이라는 타입을 사용할 것이고 answer 메세지들은 `"video-answer"` 타입의 메세지를 사용할 것이다. 이 메세지들은 아래와 같은 field를 가진다.

`type`

메세지의 타입이라; `"video-offer"` 또는 `"video-answer"`.

`name`

보내는 사람의 username 이다.

`target`

받는 사람의 username이다. (만약 caller가 메세지를 보낸다면, target은 callee 를 뜻한다, vice-versa.)

`sdp`

커넥션의 local 정보를 설명하는 SDP (Session Description Protocol) 스트링(e.g. 수신자의 관점으로 볼 때, SDP는 커넥션의 remote 정보이다.)

현 시점에서 두 피어들은 이 call에 대해 어떤 코덱들과 어떤 video parameter들이 사용될지 알게 된다. 하지만, 그들은 여전히 미디어 데이터 자체를 전송하는 방법을 모른다. 여기서 Interactive Connectivity Establishment (ICE)가 사용된다.

#### Exchanging ICE candidates

SDP를 서로 교환한 후에, 두 피어들은 **ICE candidate**(ICE 후보)들을 교환하기 시작한다. 각 ICE candidate는 발신 피어 입장에서 통신을 할 수 있는 방법을 설명한다. 각 피어는 검색되는 순서대로 candidate를 보내고 미디어가 이미 스트리밍을 시작 했더라도 모든 가능한 candidate가 전송 완료될 때까지 계속 보낸다. 두 피어가 서로 호환되는 candidate를 제안했다면, 미디어는 통신을 시작한다. 만약 나중에 더 나은 방법이 있다면(더 높은 성능을 가지는), 그 스트림은 필요에 따라 포맷을 바꿀 수도 있다.

비록 지금은 지원하지 않지만, 이 기술은 이론상 낮은 bandwidth의 연결에 대해 다운그레이드에 사용될 수 있다.

시그널링 서버를 통해 전달되는 ICE candidate들에 관한 메세지의 타입은 `"new-ice-candidate"` 이며, 이 메세지들은 아래 field를 가진다.

`type`

메세지 타입 : `"new-ice-candidate"`.

`target`

현재 협상을 진행 중인 사람의 username. 시그널링 서버는 이 유저에게만 직접 메세지를 보낸다.

`candidate`

제안된 커넥션 방법을 설명하는 SDP candidate string.

각 ICE 메세지들은 두 개의 컴퓨터를 서로 연결하기 위한 정보들에 덧붙여 프로토콜(TCP or UDP), IP 주소, 포트 넘버, 커넥션 타입 등을 제안한다. 여기에는 NAT 혹은 다른 복잡한 네트워킹을 포함한다.

**참고:** 중요. ICE negotiation 동안 너의 코드가 해야할 것은 오직 ICE layer에서 외부로 나갈 candidate들을 선택하는 것과, `icecandidate_event`handler가 불렸을 때 시그널링 서버를 통해 그것들을 다른 피어에 보내는 것이다. 그리고 시그널링 서버로부터 ICE candidate 메세지를 받고 `RTCPeerConnection.addIceCandidate()`를 호출하여 너의 ICE layer에 그들을 전달한다. 그것 뿐이다. 정확히 무엇을 하는지 알기 전까진, 더 이상 깊이 생각하지 말자!

너의 시그널링 서버가 이제 해야할 일은 요청된 메세지를 보내는 것이다. 부가적으로 login/authentication 같은 기능들이 필요할 수도 있는데, 자세한 내용은 달라질 수 있다.

Signaling transaction flow
--------------------------

시그널링 정보는 연결할 두 피어들 사이에서 교환된다. 아주 기초적인 수준에서 어떤 메세지들이 누가 누구에게 왜 전달해야하는지 보자.

시그널링 프로세스는 다양한 부분에서 다음과 같은 메시지 교환을 포함한다. 각 유저의 채팅 시스템의 웹 애플리케이션 인스턴스, 각 유저의 브라우저, 시그널링 서버 그리고 호스팅 웹 서버 등.

Naomi와 Priya는 채팅 소프트웨어를 사용해 대화에 참여했고 Naomi는 둘 사이에 영상 통화를 하기로 결정한다. 다음 표는 이벤트들이 발생하는 과정이다.

![Diagram of the signaling process](/ko/docs/Web/API/WebRTC_API/Signaling_and_video_calling/webrtc_-_signaling_diagram.svg)

곧 더 자세한 설명을 볼 수 있다.

ICE candidate exchange process
------------------------------

각 피어들의 ICE layer에서 candidate들을 보내기 시작할 때, 다음 그림과 같은 교환이 일어난다.

![Diagram of ICE candidate exchange process](/ko/docs/Web/API/WebRTC_API/Signaling_and_video_calling/webrtc_-_ice_candidate_exchange.svg)

각 피어들은 candidate 들을 전송하고, 준비가 되면 받은 candidate 들을 처리한다. Candidate들은 양 피어들이 동의할 때까지 계속 교환되며, 미디어가 송수신 되도록 만든다. "ICE exchange"은 양측이 교대로 제안을하는 것을 의미하지 않는다. 올바르게 작동할 경우, 각 피어들은 모두 소진되거나 서로 동의할 때까지 상대방에게 제안할 candidate 들을 계속 전송한다.

만약 조건들이 바뀐다면, 예를들어 네트워크 커넥션이 악화되면, 하나 혹은 양 피어들은 낮은 bandwidth의 미디어 해상도로 바꾸거나 다른 코덱을 사용하자고 제안할 것이다. 다음 candidate 교환에서 양 피어 모두 새로운 포맷에 동의한다면, 다른 미디어 포맷 혹은 다른 코덱으로 바뀔 수도 있다.

부가적으로 만약 ICE layer 내부의 프로세스를 더 자세히 이해하고 싶다면 RFC 5245: Interactive Connectivity Establishment,section 2.6 ("Concluding ICE") 를 참조해라. ICE layer가 준비 되자마자 candiate들이 교환되고 미디어들은 통신되기 시작한다는 것을 기억해라. 이 모든 것은 뒤에서 알아서 돌아간다. 우리의 역할은 그저 시그널링 서버를 통해 candidate들을 서로에게 보내는 것이다.

The client application
----------------------

지금부터 위에서 설명한 개념들을 샘플 코드를 통해서 자세히 배워보자.

어떤 시그널링 프로세스든지 핵심은 메세지 핸들링에 있다. Websocket을 시그널링에 꼭 사용할 필요는 없지만, 일반적인 솔루션으로 쓰인다. 다른 솔루션도 충분히 빠르고 같은 결과를 볼 수 있다.

### Updating the HTML

클라이언트는 비디오를 표시할 공간이 필요하다. 2개의 video와 전화를 걸 button 을 정의한 HTML 코드이다.

```html
<div class="flexChild" id="camera-container">
  <div class="camera-box">
    <video id="received\_video" autoplay></video>
    <video id="local\_video" autoplay muted></video>
    <button id="hangup-button" onclick="hangUpCall();" disabled>Hang Up</button>
  </div>
</div>
```

위에 있는 page structure은 `<div>` (en-US)태그를 이용하고 CSS 사용을 허용함으로써 페이지 레이아웃 전체를 구성한다. 여기서는 레이아웃에 관한 자세한 내용은 스킵하지만, 위의 코드가 어떻게 돌아가는지 확인해보자. take a look at the CSS on Github. 두개의 `<video>` 중 하나는 너의 self video이고 다른 하나는 상대방의 video를 위한 요소이다.

`id`가 "`received_video`" 인 `<video>`element는 연결된 상대방으로부터 수신되는 비디오를 보여주는 곳이다. `autoplay`attribute는 비디오가 도달하기 시작하면 즉시 재생시키는 역할을 한다. 이것은 따로 재생에 관련된 코드를 처리할 필요를 없애준다. `id`가 "`local_video`" 인 `<video>`element에는 너의 카메라의 영상이 나오게된다. `muted` attribute는 너의 로컬 오디오를 음소거한다.

마지막으로, 통화를 끊을 수 있는 `id`가 "`hangup-button`"인 `<button>`은 비활성화 된 상태(아무 전화도 연결되지 않은 default 상태)로 구성된다. 그리고 이 버튼을 클릭시에 `hangUpCall()`함수가 실행 된다. 이 함수의 역할은 현재 연결된 call을 끊고 다른 피어에게 연결을 끊으라는 메세지를 전달한다.

### The JavaScript code

어떻게 돌아가는지 알기 쉽게 하기 위해 각 기능별로 코드를 나누었다. 이 코드의 메인 부분은 `connect()`함수 안에 있다. 이 함수 안에서 6503 포트로 `WebSocket`server에 연결하며, JSON object format의 메세지를 받기 위한 handler를 설정한다. ~~이 코드는 일반적으로 이전처럼 문자 채팅 메세지를 처리한다.~~

#### Sending messages to the signaling server

코드 전반에 걸쳐서 시그널링 서버에 메세지를 보내기 위해 `sendToServer()`함수를 호출한다. 이 함수는 WebSocket 커넥션을 이용하여 작동한다.

```js
function sendToServer(msg) {
  var msgJSON = JSON.stringify(msg);

  connection.send(msgJSON);
}
```

전달된 메세지 object는 `JSON.stringify()`함수에 의해 JSON string으로 바뀐다. 그 후, WebSocket 커넥션의 `send()` (en-US)함수를 통해 서버로 전달된다.

#### UI to start a call

"userlist"에 관한 코드는 `handleUserlistMsg()`함수에 있다. 왼쪽 채팅 패널에 보여지는 유저 리스트에 있는 각 연결된 유저 마다 handler 를 걸어준다. 이 함수는 (온라인 상태인 유저들의 username을 배열로 저장하고 있는) `users`property를 가지고 있는 메세지 object를 받는다. 이해하기 쉽도록 여러 섹션들에서 이 코드를 살펴 보겠다.

```js
function handleUserlistMsg(msg) {
  var i;

  var listElem = document.getElementById("userlistbox");

  while (listElem.firstChild) {
    listElem.removeChild(listElem.firstChild);
  }

  // …
```

`listElem`변수를 통해 username들의 리스트인 `<ul>`을 참조한다. 그런 다음에 각 child element를 하나씩 제거하면서 목록을 비운다 .

**참고:** 명백히, 바뀔 때마다 전체 리스트를 새로 만드는 것보다, 개개인을 추가 및 제거 후 업데이트하는 것이 더 효율적이다. 그러나, 예제이므로 단순하게 하겠다.

그 후, 새로운 user 리스트를 만든다.

```js
  // …

  for (i=0; i < msg.users.length; i++) {
    var item = document.createElement("li");
    item.appendChild(document.createTextNode(msg.users[i]));
    item.addEventListener("click", invite, false);

    listElem.appendChild(item);
  }
}
```

다음으로 (채팅 서버에) 현재 연결된 각 유저들 각각을 나타내는 `<li>`element들을 DOM에 추가한다. 그런 다음에, username이 클릭 되었을 때 `invite()`함수를 실행시키는 listener을 추가한다. 이 함수 이것은 다른 유저에게 call을 하는 process를 시작한다.

#### Starting a call

통화를 하고 싶은 유저의 username을 클릭을 하면, `click` event의 handler인`invite()`함수가 실행된다.

```js
var mediaConstraints = {
  audio: true, // We want an audio track
  video: true, // ...and we want a video track
};

function invite(evt) {
  if (myPeerConnection) {
    alert("You can't start a call because you already have one open!");
  } else {
    var clickedUsername = evt.target.textContent;

    if (clickedUsername === myUsername) {
      alert(
        "I'm afraid I can't let you talk to yourself. That would be weird.",
      );
      return;
    }

    targetUsername = clickedUsername;

    createPeerConnection();

    navigator.mediaDevices
      .getUserMedia(mediaConstraints)
      .then(function (localStream) {
        document.getElementById("local\_video").srcObject = localStream;
        myPeerConnection.addStream(localStream);
      })
      .catch(handleGetUserMediaError);
  }
}
```

가장 먼저 해야할 일은 빠르게 여러 상태들을 점검하는 것이다. 유저가 이미 call을 열었는지, 혹은 유저가 자신에게 call을 신청했는지 등, 이 케이스들에는 새로운 call을 시도할 이유가 없다. 따라서 왜 call을 하지 못하는지 `alert()`를 통해 설명한다.

그 다음에 call을 하려는 유저의 이름을 `targetUsername`변수 안에 넣고 `createPeerConnection()`함수를 실행시킨다. 이 함수는 `RTCPeerConnection` (en-US) 의 기본적인 구성과 기능을 수행한다.

`RTCPeerConnection` 이 생성되면, `Navigator.mediaDevices.getUserMedia` (en-US)함수를 통해 유저의 카메라와 마이크에 권한을 요청한다. 카메라와 마이크에서 나오는 로컬 스트림을 로컬 비디오 preview의 `srcObject`property에 설정한다. 그리고 `<video>`element가 자동으로 들어오는 비디오를 재생하도록 구성되었기 때문에, stream은 로컬 preview box에서 재생을 시작한다.

그 다음에 `RTCPeerConnection` (en-US)에 stream을 추가하기 위해 `myPeerConnection.addStream()` (en-US)함수를 실행한다. WebRTC 커녁션이 완전히 준비되지 않았더라도 WebRTC 커넥션에 stream을 보내기 시작한다.

만약 local media stream을 가져오는 동안 에러가 발생한다면, `catch` clause가 `handleGetUserMediaError()`함수를 불러 필요에 따라 유저에게 적절한 에러 메세지를 보여줄 것이다.

#### Handling getUserMedia() errors

`getUserMedia()`에 의해 리턴된 `promise`가 실패로 끝나면, `handleGetUserMediaError()`함수가 실행된다.

```js
function handleGetUserMediaError(e) {
  switch (e.name) {
    case "NotFoundError":
      alert(
        "Unable to open your call because no camera and/or microphone" +
          "were found.",
      );
      break;
    case "SecurityError":
    case "PermissionDeniedError":
      // Do nothing; this is the same as the user canceling the call.
      break;
    default:
      alert("Error opening your camera and/or microphone: " + e.message);
      break;
  }

  closeVideoCall();
}
```

에러 메세지는 모든 케이스 중 하나만 표시된다. 이 예제에서 call을 취소하는 거와 같이, 미디어 하드웨어의 접근 권한을 거부하는 것에 대해 발생하는 에러들( `"SecurityError"` 와`"PermissionDeniedError"`)은 무시한다.

Stream을 가져오는 것을 실패하는 이유와 관계 없이, `RTCPeerConnection` (en-US)을 닫기 위해 `closeVideoCall()`function를 부른다. 그리고 call을 하기 위해 할당된 리소스들을 반납한다. 이 코드는 일부분만 실행된 call을 안전하게 처리할 수 있도록 설계되었다.

#### Creating the peer connection

`createPeerConnection()`함수는 caller와 callee에서 WebRTC 커넥션의 각 종점을 나타내는 `RTCPeerConnection` (en-US)object를 생성하는데 사용된다. Caller는 `invite()`함수를 통해, callee는 `handleVideoOfferMsg()` 에 의해 실행된다.

이것은 상당히 명료하다:

```js
var myHostname = window.location.hostname;

function createPeerConnection() {
  myPeerConnection = new RTCPeerConnection({
      iceServers: [     // Information about ICE servers - Use your own!
        {
          urls: "turn:" + myHostname,  // A TURN server
          username: "webrtc",
          credential: "turnserver"
        }
      ]
  });

// …
```

웹서버와 같은 호스트에 STUN/TURN 서버를 돌리고 있기 때문에, STUN/TURN 서버의 도메인 이름을 `location.hostname` (en-US)을 사용하여 설정했다. 만약 다른 서버의 STUN/TURN 서버를 사용한다면 urls 값을 그 서버로 바꿔주면 된다.

`RTCPeerConnection`을 만들 때, call을 구성하는 파라미터들을 명시해줘야한다. 가장 중요한 것은 STUN/TURN 서버의 리스트(ICE layer에서 caller와 callee의 경로를 찾는데 사용되는 서버)를 담고 있는 `iceServers`이다. **(주의. 웹소켓을 이용한 시그널링 서버와 전혀 다른 개념이다)**. WebRTC는 두 피어가 방화벽이나 NAT 뒤에 숨어 있어도, 각 피어들의 서로 연결될 수 있도록 피어간 연결 경로를 찾아주는 프로토콜(STUN, TURN)을 사용한다.

**참고:** 직접 만든 혹은 사용할 권한을 가지고 있는 STUN/TURN 서버를 사용해야 한다.

`iceServers`parameter는 object의 배열이고 각각은 STUN/TURN 서버의 URL인 `urls`field를 무조건 포함한다. 예제에서, ICE layer에서 다른 피어를 찾아 연결 시키기 위한 서버를 제공한다. 이 서버는 TURN 서버이며, Web 서버와 같은 hostname에서 돌아간다. TURN 서버의 description에 `username`과`credential`field에 각각 username과 password 정보를 항상 포함시켜야한다는 것을 유의해라.

##### Set up event handlers

`RTCPeerConnection` (en-US)이 생성되면, 중요한 이벤트들을 위한 handler를 설정해야한다.

```js
// …
  myPeerConnection.onicecandidate = handleICECandidateEvent;
  myPeerConnection.onaddstream = handleAddStreamEvent;
  myPeerConnection.onremovestream = handleRemoveStreamEvent;
  myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
  myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
  myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
  myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
}
```

위에 있는 이벤트 핸들러 중 처음 두 개는 필수이다. WebRTC로 스트리밍된 미디어와 관련된 것들을 다루기위해 두 핸들러를 설정해야한다. `removestream`event는 스트리밍이 중단된 것을 감지하는데 유용하다. 따라서 아마 이것도 사용하게 될 것이다. 남아 있는 4개는 필수적인 것은 아니나, 직접 사용해보자. 이것들 외에도 다른 이벤트들을 사용할 수 있으나 여기에서는 다루지 않겠다. 각 핸들러에 관한 요약 설명이다.

`RTCPeerConnection.onicecandidate`

로컬 ICE layer는 시그널링 서버를 통해 다른 피어에 ICE candidate를 전송하고자 할 때, 너의 `icecandidate`event handler를 호출한다.

`RTCPeerConnection.onaddstream` (en-US)

`addstream` (en-US)event를 위한 이 핸들러는 너의 커넥션에 remote stream이 추가된 것을 알려주기 위해, 로컬 WebRTC layer에 의해 불려진다. 예를들어, 이것은 들어오는 stream을 element에 연결시켜 디스플레이 되게 만들 때 사용된다. 더 자세한 내용은 Receiving new streams 을 참조해라.

`RTCPeerConnection.onremovestream` (en-US)

커넥션에서 remote가 stream을 제거할 때, `onaddstream`의 반대인 `onremovestream은` `removestream` (en-US) event을 처리하기위해 실행된다.

`RTCPeerConnection.oniceconnectionstatechange` (en-US)

ICE 커넥션의 상태 변경을 알리기위해 ICE layer가 `iceconnectionstatechange` (en-US) event 를 보낸다. 이것을 통해 커넥션이 실패하거나 끊어지는 것을 알 수 있다. 이 것에 대한 예제를 아래의 ICE connection state 에서 볼 것이다.

`RTCPeerConnection.onicegatheringstatechange`

하나의 상태에서 다른 상태(예를들어, candidate를 모으기 시작하거나 negotiation이 끝났을 때)로 ICE agent의 candidate 수집 프로세스가 변하면, ICE layer는 `icegatheringstatechange` event를 보낸다. 아래의 ICE gathering state 을 참조해라.

`RTCPeerConnection.onsignalingstatechange` (en-US)

시그널링 프로세스의 state가 바뀌게 될 때, WebRTC 인프라는 너에게 `signalingstatechange` (en-US) message를 보낸다. Signaling state 에서 코드를 볼 수 있다.

`RTCPeerConnection.onnegotiationneeded` (en-US)

이 함수는 WebRTC 인프라가 session negotiation 프로세스를 새로 시작해야할 때마다 불린다. 이것의 일은 callee에게 offer를 생성 후 전달하고, 우리에게 연결을 할 것인지 물어보는 것이다. 어떻게 처리하는지 Starting negotiation 를 참조해라.

#### Starting negotiation

Caller가 자신의 `RTCPeerConnection` (en-US)과 media stream을 생성하고 Starting a call에서 보이는 것처럼 커넥션에 추가하면, 브라우져는 다른 피어와 커넥션이 준비가 될 때 `negotiationneeded` (en-US) event를 활성화 시킬 것이다. 밑에는 이벤트를 핸들링하는 코드이다.

```js
function handleNegotiationNeededEvent() {
  myPeerConnection
    .createOffer()
    .then(function (offer) {
      return myPeerConnection.setLocalDescription(offer);
    })
    .then(function () {
      sendToServer({
        name: myUsername,
        target: targetUsername,
        type: "video-offer",
        sdp: myPeerConnection.localDescription,
      });
    })
    .catch(reportError);
}
```

Negotiation 프로세스를 시작하기 위해, 우리가 연결하고자 하는 피어에게 SDP offer를 생성하고 전송해야한다. 이 offer는 커넥션에 로컬로 추가한 media stream 정보(call의 다른 피어에게 전달하고 싶은 비디오)와 ICE layer에 의해 미리 모아 놓은 ICE candidates 정보들을 포함해, 커넥션에 지원되는 구성 목록들을 포함한다. `myPeerConnection.createOffer()`를 호출함으로써 이 offer를 생성한다. 이 것이 성공한다면(promise에서 fulfill되면), `myPeerConnection.setLocalDescription()`으로 생성된 offer 정보를 전달한다.`myPeerConnection.setLocalDescription()`은 커넥션에서 자신의 미디어 구성 상태나 연결 정보들을 구성한다.

**참고:** 기술적으로 말하자면, `createOffer()`에 의해 리턴되는 blob은 RFC 3264 offer 이다.

`setLocalDescription()`이 완료되어 promise를 리턴하면, description 이 유효하고 세팅 되었음을 알 수 있다. 그 이후에 local description을 포함하는 새로운 `"video-offer"`message를 만들어 시그널링 서버를 통해 다른 피어에게 전송한다. 이 offer는 다음과 같은 내용을 가진다.

`type`

메세지의 타입은 `"video-offer"`.

`name`

caller의 username.

`target`

call을 하고자 하는 user의 name.

`sdp`

offer에 관한 설명을 하는 SDP blob.

`createOffer()`이나 다른 fulfillment 핸들러에서 에러가 발생한다면, `reportError()`함수가 실행되어 에러를 보고한다.

`setLocalDescription()`의 fulfillment 핸들러가 실행되면, ICE agent는 `icecandidate`event들을 처리하기 시작한다.

#### Session negotiation

이제 다른 피어와 협상을 할 것이다. 다른 피어는 우리의 offer를 받을 것이고, `handleVideoOfferMsg()`에 전달한다. Callee에게 `"video-offer"`message가 도착 했을 때의 이야기를 계속해보자.

##### Handling the invitation

offer가 도착할 때, callee의 `handleVideoOfferMsg()`함수가 실행되고, offer를 포함한 `"video-offer"`message를 받을 것이다. 이 코드는 2가지를 해야한다. 첫째, 자기 자신의 `RTCPeerConnection` (en-US)과 media stream을 생성해야 한다. 두번째, 받은 offer를 분석하고 애이에 대한 answer를 만들어 보내야한다.

```js
function handleVideoOfferMsg(msg) {
  var localStream = null;

  targetUsername = msg.name;

  createPeerConnection();

  var desc = new RTCSessionDescription(msg.sdp);

  myPeerConnection.setRemoteDescription(desc).then(function () {
    return navigator.mediaDevices.getUserMedia(mediaConstraints);
  })
  .then(function(stream) {
    localStream = stream;

    document.getElementById("local\_video").srcObject = localStream;
    return myPeerConnection.addStream(localStream);
  })

// …
```

이 코드는 Starting a call에 있는 `invite()`함수와 매우 비슷하다. 먼저, `createPeerConnection()`함수를 이용해서 `RTCPeerConnection` (en-US)를 생성하고 구성한다. 그 후에, `"video-offer"`message로부터 얻은 SDP offer를 가지고 caller의 session description을 나타내는 `RTCSessionDescription` (en-US)object를 생성한다.

그 후에, session description은 `myPeerConnection.setRemoteDescription()` (en-US) 안으로 전달된다. 이를 통해, 받은 offer를 caller의 session 정보로 저장한다. 설정에 성공했다면, promise fulfillment handler(`then()`clause)은 callee의 카메라와 마이크에 접근하고 stream을 설정하는 등 이전에 `invite()`에서 본 것과 같은 프로세스를 시작한다.

local stream이 작동한다면, 이제 SDP answer를 만든 후 caller에게 보내야 한다.

```js
  .then(function() {
    return myPeerConnection.createAnswer();
  })
  .then(function(answer) {
    return myPeerConnection.setLocalDescription(answer);
  })
  .then(function() {
    var msg = {
      name: myUsername,
      target: targetUsername,
      type: "video-answer",
      sdp: myPeerConnection.localDescription
    };

    sendToServer(msg);
  })
  .catch(handleGetUserMediaError);
}
```

`RTCPeerConnection.addStream()` (en-US) 이 성공적으로 완료되었다면, 그 다음 fulfillment handler가 실행될 것이다. SDP answer string을 만들기 위해 `myPeerConnection.createAnswer()`를 실행한다. 커넥션에서 callee의 로컬 description을 설정하기 위해 `myPeerConnection.setLocalDescription`에 생성한 SDP를 전달한다.

최종 answer는 caller에게 보내져서, 어떻게 callee에게 닿을 수 있는지 알게해준다. `"video-answer"`message의 `sdp`property에 callee의 answer를 포함하고, caller에게 이 메세지를 전달한다.

에러가 발생하면 `handleGetUserMediaError()`으로 전달되고, Handling getUserMedia() errors에 잘 설명되어 있다.

**참고:** caller와 마찬가지로 `setLocalDescription()`fulfillment handler가 실행되면, 브라우져는 callee가 반드시 처리해야하는 `icecandidate`event들을 처리하기 시작한다.

##### Sending ICE candidates

caller가 callee로부터 answer를 받으면 모든 것이 끝났다고 생각할 수 있지만, 그렇지 않다. 뒷단 에서는 각 피어들의 ICE agent들이 열심히 ICE candidate message들을 교환한다. 미디어 통신이 어떻게 연결될 수 있는지에 대한 방법들을 알릴 때까지, 각 피어들은 상대방에게 계속해서 candidate들을 보낸다. 이 candidate들은 너의 시그널링 서버를 통해서 전송되어야 한다. ICE는 너의 시그널링 서버에 대해 모르기 때문에, 너는 `icecandidate`event를 위한 핸들러를 불러서 전송된 candidate 들을 너의 코드로 직접 처리해야한다.

너의 `onicecandidate`handler는 `candidate`property가 candidate의 정보를 담고 있는 SDP(단, candidate들의 끝에는`null`이 찍혀있다) 인 이벤트들을 받는다. 이것이 너의 시그널링 서버를 통해 다른 피어에게 전송해야할 것들이다. 밑에 구현 예제가 있다.

```js
function handleICECandidateEvent(event) {
  if (event.candidate) {
    sendToServer({
      type: "new-ice-candidate",
      target: targetUsername,
      candidate: event.candidate,
    });
  }
}
```

이 코드에서 candidate를 포함하는 object를 만들고 다른 피어에 보낸다. `sendToServer()`함수는 위에서 이미 다뤘으며 Sending messages to the signaling server에 코드가 있다. message의 property들이 의미하는 것은 다음과 같다.

`target`

ICE candidate가 보내야하는 곳의 username. 이것을 통해 시그널링 서버가 메세지를 타겟에게 전달한다.

`type`

메세지 타입은 `"new-ice-candidate"`.

`candidate`

ICE layer가 다른 피어에게 전송하고자하는 candidate object.

메세지의 포맷(시그널링을 처리하는 모든 메세지들은)은 모두 너의 영역이고, 너가 필요한 것에 달렸다. 너가 또다른 필요한 정보가 있다면 추가할 수 있다. 메세지는 그저 JSON stringfied 되어 상대방에게 전달될 뿐이다.

**참고:** Call의 다른 피어로부터 ICE candidate가 도착할 때, `icecandidate`event가 전송되는 것이 **아님을 항상 명심해라**. 대신에 너 자신이 call을 할 때 보내는 것으로, 너가 원하는 채널을 통해 data를 보낼 수 있다. WebRTC를 처음 접한다면 매우 헷갈릴 것이다.

##### Receiving ICE candidates

시그널링 서버는 어떤 방법을 고르던 간에 각 ICE candidate를 목적지까지 배달한다. 이번 예제 에서는 type이 `"new-ice-candidate"`인 JSON object를 사용한다. `handleNewICECandidateMsg()`함수는 이 메세지들을 처리하기 위해 실행된다.

```js
function handleNewICECandidateMsg(msg) {
  var candidate = new RTCIceCandidate(msg.candidate);

  myPeerConnection.addIceCandidate(candidate).catch(reportError);
}
```

수신된 SDP를 `RTCIceCandidate` 생성자의 인자로서 전달하여 object를 생성하고, 이 object를 `myPeerConnection.addIceCandidate()` 에 전달한다. 이 함수를 통해 새로운 ICE candidate를 local ICE layer에 전달하고, 드디어 candidate 를 핸들링하는 프로세스에서 우리의 역할은 끝났다.

각 피어는 작동할 것으로 보이는 각 커넥션 메소드의 candidate를 다른 피어에게 보낸다. 양측은 합의에 도달하고 커넥션을 open한다. 협약을 진행 중에도 더 나은 커넥션 메소드를 찾거나, 단순히 피어가 커넥션을 설정할 때 candidate 교환이 진행 중이었을 수 있기 때문에, candidate는 여전히 송,수신 될 수 있음을 기억해라.

##### Receiving new streams

리모트 피어가 `RTCPeerConnection.addStream()` (en-US)를 부름으로써, 또는 stream format에 대한 renegotiation(재협상)에 의해 새로운 스트림이 커넥션에 추가되었을 때, `addstream` (en-US)event가 발생한다. 어떻게 처리하는지 아래 코드를 보자.

```js
function handleAddStreamEvent(event) {
  document.getElementById("received\_video").srcObject = event.stream;
  document.getElementById("hangup-button").disabled = false;
}
```

이 함수는 들어오는 stream을 id가 `"received_video"`인 `<video>`element에 할당하고, 유저가 전화를 받을 수 있도록 버튼을 활성화한다.

이 코드가 제대로 실행된다면, 드디어 다른 피어에서 오는 비디오를 로컬 브라우저에서 볼 수 있게 된다!

##### Handling the removal of streams

리모트 피어가 `RTCPeerConnection.removeStream()` (en-US)를 호출하여 커넥션으로부터 스트림을 없애면, `removestream` (en-US)event가 발생하게 된다.

```js
function handleRemoveStreamEvent(event) {
  closeVideoCall();
}
```

이 함수는 `closeVideoCall()`함수를 실행시켜 call이 닫히도록 만들고, 다른 커넥션을 시작할 수 있도록 기존 인터페이스를 버린다. 어떻게 코드가 동작하는지 Ending the call을 참조해라.

#### Ending the call

There are many reasons why calls may end. A call might have completed, with one or both sides having hung up. Perhaps a network failure has occurred. Or one user might have quit their browser, or had a systen crash.

##### Hanging up

When the user clicks the "Hang Up" button to end the call, the `hangUpCall()` function is apllied:

```js
function hangUpCall() {
  closeVideoCall();
  sendToServer({
    name: myUsername,
    target: targetUsername,
    type: "hang-up",
  });
}
```

`hangUpCall()` executes `closeVideoCall()`, shutting down and resetting the connection and related resources. We then build a `"hang-up"` message, sending this to the other end of the call, allowing the other peer to neatly shut down.

##### Ending the call

아래에 있는 `closeVideoCall()`함수는 stream들을 멈추고 지운 후에,`RTCPeerConnection` (en-US)object를 없앤다.

```js
function closeVideoCall() {
  var remoteVideo = document.getElementById("received\_video");
  var localVideo = document.getElementById("local\_video");

  if (myPeerConnection) {
    if (remoteVideo.srcObject) {
      remoteVideo.srcObject.getTracks().forEach((track) => track.stop());
      remoteVideo.srcObject = null;
    }

    if (localVideo.srcObject) {
      localVideo.srcObject.getTracks().forEach((track) => track.stop());
      localVideo.srcObject = null;
    }

    myPeerConnection.close();
    myPeerConnection = null;
  }

  document.getElementById("hangup-button").disabled = true;

  targetUsername = null;
}
```

2개의 `<video>`element를 참조한 이후에, WebRTC 커넥션이 존재하는지 체크한다. 만약 있다면, call을 끊고 닫는다:

1. 리모트와 로컬 비디오 stream에 대해서, 각 track들 마다 `MediaTrack.stop()`를 실행시킨다.
2. 양 비디오의 `HTMLMediaElement.srcObject`property를 `null`로 바꿔 stream에 관한 모든 참조를 푼다.
3. `myPeerConnection.close()`를 불러 `RTCPeerConnection` (en-US)을 닫는다.
4. `myPeerConnection`변수의 값을 `null`로 바꿔서 계속 진행중인 call이 없다는 것을 전체 코드가 알게 한다. 이것은 유저가 유저 리스트에서 username을 클릭할 때 사용된다.

마지막으로, "Hang Up" 버튼의 `disabled` property를 `true`로 바꿔서 call이 없는 동안에는 클릭이 불가능하게 만든다. 그 다음에 더이상 통화를 하지 않으므로 `targetUsername`을 `null`로 바꾼다. 이것을 통해 또 다른 유저에게 call을 하거나 새로운 call을 받을 수 있다.

#### Dealing with state changes

다양한 상태 변화를 너의 코드에 알리기 위해 listener를 세팅할 수 있는 다양한 이벤트들이 있다. 그 중에 다음 3가지를 사용하겠다.: `iceconnectionstatechange`, `icegatheringstatechange`, and `signalingstatechange`.

##### ICE connection state

커넥션 state가 바뀌면(예를들어, call이 다른쪽에서 중단 될 때) ICE layer가 `iceconnectionstatechange`event를 우리에게 보낸다.

```js
function handleICEConnectionStateChangeEvent(event) {
  switch (myPeerConnection.iceConnectionState) {
    case "closed":
    case "failed":
    case "disconnected":
      closeVideoCall();
      break;
  }
}
```

ICE connection state가 `"closed"`, 또는`"failed"`, 또는 `"disconnected"`으로 바뀔 때 `closeVideoCall()`함수를 실행한다. 커넥션을 끊으며, 처음(또는 accept) call 상태로 돌아간다.

##### ICE signaling state

마찬가지로 `signalingstatechange` (en-US)event를 받을 수 있는데, 시그널링 상태가 `"closed"`으로 바뀌면 call을 완전히 종료시킨다.

```js
myPeerConnection.onsignalingstatechange = function (event) {
  switch (myPeerConnection.signalingState) {
    case "closed":
      closeVideoCall();
      break;
  }
};
```

##### ICE gathering state

`icegatheringstatechange` events are used to let you know when the ICE candidate gathering process state changes. Our example doesn't use this for anything, but we're implementing it for logging, observing via the console log how the whole process works.

```js
function handleICEGatheringStateChangeEvent(event) {
  // Our sample just logs information to console here,
  // but you can do whatever you need.
}
```

Next steps
----------

You can now play with this sample to see it in action. Open the Web console on both devices and look at the logged output—although you don't see it in the code as shown above, the code on the server (and on GitHub) has a lot of console output so you can see the signaling and connection processes at work.


WebRTC data channel 사용하기 - Web API

WebRTC data channel 사용하기
========================

`RTCPeerConnection` (en-US) 인터페이스를 사용하여 WebRTC Peerconnction을 연결하면 이제 두 Peer간의 커넥션을 통하여 미디어 데이터를 주고 받을수 있게됩니다. 그뿐아니라 WebRTC로 할수 있는 일은 더 있습니다. 이 가이드에서 우리는 peer connection에 데이터 채널을 추가하는 방법과 임의의 데이터, 즉 우리가 원하는 어떠한 포멧의 데이터들을 안전하게 주고 받는 방법을 배우게 될 것 입니다.

**참고:** 모든 WebRTC 컴포넌트들은 암호화를 사용하게 되어 있기 때문에 `RTCDataChannel`을 이용하는 어떤 데이터 전송도 자동적으로 Datagram Transport Layer Security (**DTLS**)을 사용하여 암호화 됩니다. 자세한 내용은 Security 를 참고하십시오.

데이터 채널 만들기
----------

`RTCDataChannel` (en-US)를 이용한 기초적인 데이터 전송은 아래의 두 방법중 하나를 이용하여 만들수 있습니다.

* WebRTC가 전송수단을 만들게 하고 `datachannel` 이벤트를 수신 받게 하여 remote peer에 알립니다. 참 쉽죠? 이 단순한 로직을 바탕으로 수많은 활용법을 만들어 낼 수 있습니다. 하지만 단순하기 때문에 구현하고자하는 모든 요구를 만족해주지는 못합니다.
* 데이터 전송을 할 수 있는 코드를 스스로 작성하고 새로운 채널이 연결되었다는 것을 알려줄수 있는 코드를 작성해주십시오.

위에서 언급한 두가지 방법을 각각 살펴보겠습니다. 우선은 가장 일반적으로 사용되는 첫번째 방법부터 살펴보겠습니다.

### 자동 협상(Automatic negotiation)

`RTCDataChannel` (en-US) 연결의 협상을 핸들링하기 위해 대부분 Peer 연결을 사용하게 됩니다. 이를 위해 간단히 `negotiated` 프로퍼티의 값을 지정하지 않고 `createDataChannel()` 를 호출하거나 그저 false로 설정합니다. 이 행위는 협상을 핸들링하기 위해 자동적으로 RTCPeerConnection를 발동시키고 원격 피어(remote peer)가 데이터 채널을 만들고 네트워크를 통하여 서로를 연결되게 만듭니다.

createDataChannel()를 통해 RTCDataChannel 객체가 즉시 반환됩니다. 그후 `open` (en-US)의 이벤트가 RTCDataChannel로 송신되는 것을 확인한다면 성공적으로 연결되었다고 말할 수 있습니다.

```js
let dataChannel = pc.createDataChannel("MyApp Channel");

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});
```

### 수동 협상(Manual negotiation)

Data channel의 수동협상을 위해서 우선 `RTCPeerConnection` (en-US)에 있는 `createDataChannel()` 매소드를 사용하여 `RTCDataChannel` (en-US) 객체를 생성해야 합니다. 이때 `negotiated` 프로퍼티의 값을 true로 설정해야 합니다. 이 피어 연결을 위한 신호들은 설정하는 측의 피어 연결 협상을 시도하지 않도록 합니다.

대역외의 연결 협상은 웹서버나 다른 방법을 이용해야 합니다. 이 절차들은 수동으로 RTCDataChannel을 원격 피어에게 신호를 보내야 하며 동일한 `id` (en-US)를 사용해야 하고 negotiated 프로퍼티는 true로 설정해야합니다. 이 설정들은 RTCPeerConnection를 이용해 두 오브젝트가 연결되게 합니다.

```js
let dataChannel = pc.createDataChannel("MyApp Channel", {
  negotiated: true,
});

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});

requestRemoteChannel(dataChannel.id);
```

위의 코드에서는 채널 생성시 negotiated 프로퍼티를 true로 설정해주었고 requestRemoteChannel()을 호출함으로 원격 채널과 동일한 로컬 채널 ID를 만들기 위한 협상(negotiation)을 발동시켰습니다.

이 절차를 밟으므로서 다른 프로퍼티를 갖는 각각의 피어들이 데이터 채널을 생성하고 같은 id 값을 사용하는 채널을 명확하고 쉽게 생성할수 있습니다.

버퍼링(Buffering)
--------------

WebRTC 데이터채널은 아웃바운드 데이터에 대해 버퍼링을 제공합니다. 이것은 자동적으로 처리가됩니다. buffer의 사이즈를 컨트롤 할 수 없는 동안 당신은 얼마나 많은 데이터가 현재 버퍼 되어 있는지 배울 수 있고 큐 데이터의 버퍼가 고갈되기 시작할 때 알림을 받도록 선택할 수 도 있습니다. 이것은 메모리 과다사용이나 채널을 완전히 밀어내버리는 것을 없애고 언제나 데이터를 보낼수 있도록 효과적인 루틴을 만들기 쉽게 해줍니다.

메세지 크기 제한에 대해 이해하기
------------------

네트워크를 통하여 전송되는 데이터라면 그 데이터는 반드시 사이즈가 제한됩니다. 기초적인 레벨의 이야기를 하자면, 각각의 네트워크 패킷은 어떠한 값보다 클 수 없습니다. (정확한 숫자는 네트워크와 전송 계층이 사용하고 있는 것에 따라 다릅니다.) 어플리케이션 계층에서는 — 즉 당신의 코드가 돌아가고 있는 WebRTC user agent's — WebRTC가 네트워크의 전송계층위의 최대 패킷사이즈보다 메시지가 더 큰지 확인하는 것을 구현할 수 있습니다.

만약 당신이 사이즈 제한 크기가 궁금하지 않다거나 대용량 메세지를 보내거나 받는 것이 필요하지 않다면 이 이야기는 복잡한 이야기가 될 수 있습니다. Even when user agents share the same underlying library for handling Stream Control Transmission Protocol (SCTP) data, there can still be variations due to how the library is used. 예를들어 Firefox와 구글 크롬은 SCTP를 구현하기 위해 `usrsctp` 라이브러리를 사용합니다. 이때 그 두 브라우저가 어떻게 라이브러리에 요청하고 이벤트에 반응하는지에 따라 `RTCDataChannel` 을 이용한 데이터 전송이 실패하는 경우가 있습니다.

두 유저가 Firefox에 있는 데이터채널을 사용하여 통실할 때 메세지 사이즈의 제한은 Firefox와 크롬을 각각 사용할 때보다 큽니다. 왜냐하면 Firefox의 구현 방법은 현재 다중 SCTP메세지를 전송하는 기술을 deprecated하여 놓았습니다. 하지만 크롬은 여전히 가능합니다. 크롬은 완성 될것이라 확신하는 메시지 시리즈를 보는 대신 RTCDataChannel을 다중 메시지로서 수신하는 것으로 대채할 것입니다.

메세지가 16kiB 작다면 별다른 무리 없이 보낼 수 있을 것 입니다. 대부분의 메이저 user agents도 동일하게 다룹니다.

### 대용량 메세지에 대해 고려해보기

~~현재 메세지 사이즈가 64kiB (16kiB if you want to support cross-browser exchange of data)를 넘는 `RTCDataChannel` 는 구현되지 않았습니다. 문제는 `RTCDataChannel`에서 송수신 프로토콜로 사용되는 SCTP가 원래 시그널링 프로토콜로 디자인 되었다는 것 입니다. 그래서 메세지가 상대적으로 작은 사이즈라고 가정하고 만들어진 프로토콜입니다. Support for messages larger than the network layer's Maximum transmission unit was added almost as an afterthought, in case signaling messages needed to be larger than the MTU. 이 기능은 각각의 조각에 연속적인 시퀀스 번호를 필요로 합니다. 즉 상호배치를 통해 동시에 여러 데이터를 보낼 수 없고 하나를 보낸 이후 다시 하나를 보내야 합니다.~~

~~이러한 점은 문제가 됩니다.~~ 지금 그리고 이후에도 WebRTC를 구현하고 있는 것들을 포함한 다양한 어플리케이션 들이 더욱더 큰 메시지를 전송하기 위해 SCTP를 사용합니다. 결국에 메시지가 감당할수 없을 만큼 커지게 된다면 정말 중요한 시그널링 메시지를 포함한 데이터 채널에서 전송되는 모든 다른 데이터들이 블락당할수 있다는 것을 알게되었습니다.

이것은 브라우저가 현재의 대용량 메시지를 처리하는 표준인 end-of-record(EOR) 플레그 (메시지가 여러 시리즈에서 마지막일때 하나의 페이로드로서 취급하게하는 플레그)를 제공할때 문제가 됩니다. 이 플레그는 Firefox 57에서는 구현이 되어있습니다. 그러나 Chrome 에서는 아직 구현이 되어있지 않습니다.(Chromium Bug 7774 참조). EOR를 제공하는 RTCDataChannel 페이로드는 더욱 커질 수 있습니다. (공식적으로 256kiB까지이며 Firfox의 구현으로는 1GiB까지가능). 256kiB에서 조차 긴급한 트래픽을 처리하기에는 유의미한 지연을 야기시키에 충분히 큰 용량입니다. 만약 여기서 더 커진다면 지연은 당신이 특정한 조작을 하더라도 줄일수 없을 것입니다.

이러한 문제점을 해결하기 위해 **스트림 스케쥴러**(**stream schedulers**)라고하는 이름지어져 있으며 SCTP ndata specification이라고도 불리우는 새로운 시스템을 디자인하였습니다. 이 스케쥴러는 WebRTC 데이터 채널에 구현되어 있는 스트림을 포함한 각기 다른 스트림에 메시지를 상호배치하여 전송가능합니다. 이 제안은 IETF 제안(draft form) 상태에 있지만 한번 구현된다면 SCTP 계층은 자동적으로 서브메시지들에게 모든 데이터 채널을 통과할수 있는 기회를 보장하는 상호배치를 하기때문에 기본적으로 사이즈에 제한이 없는 메시지를 보낼수 있게될것입니다.

Firefox는 ndata를 지원하기 위해 현재 구현단계에 있습니다. 일반적인 사용이 언제쯤 가능할지에 대해 궁금하시다면 Firefox bug 1381145 을 관심있게 보고 계십시요. Chrome 팀은 Chrome Bug 5696 를 통해 ndata 지원을 위한 구현을 트래킹하고 있습니다.

이 섹션에 있는 대부분의 정보는 다음 블로그 포스트를 기반으로 작성되었습니다. Demystifyijng WebRTC's Data Channel Message Size Limitations, written by Lennart Grahl. 이 블로그를 보시면 더욱 자세한 내용이 나와 있습니다. 그러나 브라우져들은 당시보다 업데이트 되었기때문에 그 정보들은 현재와 맞지 않을 수 도 있습니다. 또한 현재는 시간이 많이 흘러 대부분의 메이저 브라우저에서는 EOR이 구현되어 있습니다.

보안(Security)
------------

WebRTC로 전송되는 모든 데이터는 암호화됩니다. `RTCDataChannel` 의 암호화는Transport Layer Security (TLS)를 바탕으로 하는 Datagram Transport Layer Security (DTLS)를 사용합니다. TLS는 모든 암호화된 HTTPS 통신에 사용되기 때문에 data channel에 사용되는 모든 데이터는 사용자의 브라우저가 데이터를 주고받는 만큼의 보안이 보장됩니다.

조금더 근본적으로 WebRTC는 두 사용자간의 peer-to-peer 연결이기때문에 데이터가 어떠한 다른 웹이나 어플리케이션 서버를 경유하지 않습니다. 이러한 통신방식으로 인해 데이터가 중간에 가로채어질 가능성이 줄어듭니다.


WebRTC 프로토콜 소개 - Web API

WebRTC 프로토콜 소개
==============

이 글은 WebRTC API에 대한 프로토콜을 소개하기 위해 작성 되었습니다.

ICE
---

Interactive Connectivity Establishment (ICE) 는 브라우저가 peer를 통한 연결이 가능하도록 하게 해주는 프레임워크입니다. Peer A에서 Peer B까지 단순하게 연결하는 것으로는 작동하지 않는 것에 대한 이유는 많이 있습니다. 연결을 시도하는 방화벽을 통과해야하기도 하고, 단말에 퍼블릭 IP가 없다면 유일한 주소값을 할당해야할 필요도 있으며 라우터가 peer간의 직접연결을 허용하지 않을 때에는 데이터를 릴레이해야할 경우도 있습니다. ICE는 이러한 작업을 수행하기 위해 STUN과 TURN 서버 둘다 혹은 하나의 서버를 사용합니다.

STUN
----

Session Traversal Utilities for **NAT** (STU**N**) (단축어 안의 단축어) 는 당신의 공개 주소(public address)를 발견하거나 peer간의 직접 연결을 막는 등의 라우터의 제한을 결정하는 프로토콜입니다.

클라이언트는 인터넷을 통해 클라이언트의 공개주소와 라우터의 NAT 뒤에 있는 클라이언트가 접근가능한지에 대한 답변을 위한 요청을 STUN서버에 보냅니다.

![An interaction between two users of a WebRTC application involving a STUN server.](/ko/docs/Web/API/WebRTC_API/Protocols/webrtc-stun.png)

NAT
---

Network Address Translation (NAT) 는 단말에 공개 IP주소를 할당하기 위해 사용됩니다. 라우터는 공개 IP 주소를 갖고 있고 모든 단말들은 라우터에 연결되어 있으며 비공개 IP주소(private IP Address)를 갖고 있습니다. 요청은 단말의 비공개 주소로부터 라우터의 공개 주소와 유일한 포트를 기반으로 번역될 것입니다. 이러한 경유로 각각의 단말이 유일한 공개 IP 없이 인터넷 상에서 검색 될 수 있는 방법입니다.

어떠한 라우터들은 네트워크에 연결할수 있는 제한을 갖고 있습니다. 따라서 STUN서버에 의해 공개 IP주소를 발견한다고 해도 모두가 연결을 할수 있다는 것은 아닙니다. 이를 위해 TURN이 필요합니다.

TURN
----

몇몇의 라우터들은 Symmetric NAT이라고 불리우는 제한을 위한 NAT을 채용하고 있습니다. 이 말은 peer들이 오직 이전에 연결한 적 있는 연결들만 허용한다는 것입니다.

Traversal Using Relays around NAT (TURN) 은 TURN 서버와 연결하고 모든 정보를 그 서버에 전달하는 것으로 Symmetric NAT 제한을 우회하는 것을 의미합니다. 이를 위해 TURN 서버와 연결을 한 후 모든 peer들에게 저 서버에 모든 패킷을 보내고 다시 나에게 전달해달라고 해야 합니다. 이것은 명백히 오버헤드가 발생하므로 이 방법은 다른 대안이 없을 경우만 사용하게 됩니다.

![An interaction between two users of a WebRTC application involving STUN and TURN servers.](/ko/docs/Web/API/WebRTC_API/Protocols/webrtc-turn.png)

SDP
---

Session Description Protocol (SDP) 은 해상도나 형식, 코덱, 암호화등의 멀티미디어 컨텐츠의 연결을 설명하기 위한 표준입니다. 이러한 것이 두개의 peer가 다른 한쪽이 데이터가 전송되고 있다는 것을 알게 해줍니다. 이것은 기본적으로 미디어 컨텐츠 자체가 아닌 컨텐츠에 대한 메타데이터 설명이 됩니다.

기술적으로 보자면 SDP 는 프로토콜이 아닙니다. 그러나 데이터 포멧은 디바이스간의 미디어를 공유하기 위한 연결을 설명하기 위해 사용됩니다.

SDP의 문서화는 다른 웹상에서도 잘되어 있으므로 이 문서에서는 간단히 소개하는 것으로 마무리 짓도록 하겠습니다.

### 구조

SDP는 한줄 이상의 UTF-8 텍스트로 구성됩니다. 이 텍스트의 시작은 한글자로 구성되며 한글자 뒤에 등호기호 (`"="`)가 옵니다. 그리고 그 뒤에는 포멧에 맞게 값이나 설명이 적혀있습니다. 한글자로 시작되는 그 글자는 일반적으로 "letter-lines"를 뜻합니다. 예를들어 미디어 설명을 제공하는 것이라면 `"m"` 이라고 적어두고 이것은 m-lines을 뜻합니다.

### 자세히

SDP에 대해 더 알고 싶으시다면 아래 유용한 자료들이 있습니다.

* 명세: RFC 4566: SDP: Session Description Protocol
* IANA registry of SDP parameters


performance.mark() - Web API

performance.mark()
==================

**`mark()`** 메소드는 브라우저의 *performance entry buffer*에 주어진 이름으로 `timestamp` (en-US)를 생성합니다. timestamp가 정의된 응용프로그램은 `Performance` (en-US) 인터페이스의 `getEntries*()` 메소드들을 통해 불러올 수 있습니다. (`getEntries()` (en-US), `getEntriesByName()` (en-US), `getEntriesByType()` (en-US)).

**참고:** 이 기능은 Web Worker에서 사용할 수 있습니다

`mark`의 `performance entry`는 다음 속성값을 갖습니다:

* `entryType` (en-US) - "`mark`"로 설정됩니다.
* `name` (en-US) - mark가 생성될 때 주어진 "`name`"으로 설정됩니다.
* `startTime` (en-US) - `mark()`가 호출되었을 때의 `timestamp` (en-US)가 설정됩니다.
* `duration` (en-US) - "`0`"으로 설정됩니다. (*duration*이 없는 mark).

만약 메서드에 주어진 `name`이 이미 `PerformanceTiming` (en-US) 인터페이스 상에 존재한다면 `SyntaxError`를 throw 합니다.

Syntax
------

```js
performance.mark(name);
```

### Arguments

name

mark의 이름을 나타내는 `DOMString`.

### Return value

void

Example
-------

다음 예시는 mark()를 사용하여 `PerformanceMark` (en-US)를 생성하고 불러오는 방법을 보여줍니다.

```js
// Create a bunch of marks.
performance.mark("squirrel");
performance.mark("squirrel");
performance.mark("monkey");
performance.mark("monkey");
performance.mark("dog");
performance.mark("dog");

// Get all of the PerformanceMark entries.
const allEntries = performance.getEntriesByType("mark");
console.log(allEntries.length);
// 6

// Get all of the "monkey" PerformanceMark entries.
const monkeyEntries = performance.getEntriesByName("monkey");
console.log(monkeyEntries.length);
// 2

// Clear out all of the marks.
performance.clearMarks();
```

명세서
---

| Specification |
| --- |
| User Timing # dom-performance-mark |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


DataTransfer: files 속성 - Web API

DataTransfer: files 속성
======================

`DataTransfer` 객체의 **`files`** 속성은 드래그 작업에서의 `파일 목록`입니다. 작업에 파일이 없으면 목록은 비어 있습니다.

이 기능은 사용자의 데스크탑에서 브라우저로 파일을 드래그하기 위해 사용될 수 있습니다.

**참고:** `DataTransfer` 객체의 `files` 속성은 `drop` 이벤트 내에서만 접근될 수 있습니다. 모든 다른 이벤트의 경우 기본 데이터 저장소가 보호 모드이기 때문에 `files` 속성은 비어 있을 것입니다.

값
-

드래그 작업에서 파일의 `목록`으로 작업의 각 파일은 하나의 목록 항목입니다. 드래그 작업에 파일이 없으면 목록은 비어 있습니다.

예제
--

이 인터페이스에는 두 가지 실제 예제가 있습니다.

* Firefox: https://jsfiddle.net/9C2EF/
* 모든 브라우저: https://jsbin.com/hiqasek/
명세서
---

| Specification |
| --- |
| HTML Standard # dom-datatransfer-files-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


DataTransfer: DataTransfer() 생성자 - Web API

DataTransfer: DataTransfer() 생성자
================================

**`DataTransfer`** 생성자는 새로운 `DataTransfer` 객체 인스턴스를 생성합니다.

구문
--

```js
new DataTransfer()
```

### 매개변수

없음.

명세서
---

| Specification |
| --- |
| HTML Standard # dom-datatransfer-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


DataTransfer.getData() - Web API

DataTransfer.getData()
======================

**`DataTransfer.getData()`** 메소드는 특정 형태를 위해 (`DOMString`로) 끌어낸 데이터를 회수한다. 가령, 끌어내는 동작이 데이터를 포함하지 않는다면, 이 메소드는 빈 문자열을 반환한다.

예시 데이터 형식으로는 `text/plain` 와 `text/uri-list` 이 있다.

구문
--

```js
dataTransfer.getData(format);
```

### 매개변수

`format`

회수할 데이터 형식을 나타내는 `DOMString`

### 반환 값

`DOMString`

특정 `format`을 위해 끌어낸 데이터를 나타내는 `DOMString`. 만약 끌어내는 동작이 데이터를 가지고 있지 않거나 동작이 특정 `format` 에 대한 데이터를 가지고 있지 않다면, 이 메소드는 빈 문자열을 반환한다.

예제
--

이 예시는 `DataTransfer` 객체의 `getData()` 와 `setData()` (en-US) 메소드의 사용을 보여준다.

### HTML 내용

```html
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
  <span id="drag" draggable="true" ondragstart="drag(event)"
 >drag me to the other box</span
 >
</div>
<div id="div2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
```

### CSS 내용

```css
#div1,
#div2 {
  width: 100px;
  height: 50px;
  padding: 10px;
  border: 1px solid #aaaaaa;
}
```

### JavaScript 내용

```js
function allowDrop(allowdropevent) {
  allowdropevent.target.style.color = "blue";
  allowdropevent.preventDefault();
}

function drag(dragevent) {
  dragevent.dataTransfer.setData("text", dragevent.target.id);
  dragevent.target.style.color = "green";
}

function drop(dropevent) {
  dropevent.preventDefault();
  var data = dropevent.dataTransfer.getData("text");
  dropevent.target.appendChild(document.getElementById(data));
  document.getElementById("drag").style.color = "black";
}
```

### 결과

명세서
---

| Specification |
| --- |
| HTML Standard # dom-datatransfer-getdata-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Drag and drop
* Drag Operations (en-US)
* Recommended Drag Types (en-US)
* Dragging and Dropping Multiple Items
* DataTransfer test - Paste or Drag


DataTransfer: items 속성 - Web API

DataTransfer: items 속성
======================

읽기 전용 `DataTransfer`의 `items` 속성은 드래그 작업에서 데이터 전송 항목 (en-US)의 `목록` (en-US)입니다. 목록은 작업의 각 항목에 대해 하나의 항목이 포함되며 작업에 항목이 없으면 목록은 비어 있습니다.

값
-

드래그 작업에서 드래그되는 항목을 나타내는 `DataTransferItem` (en-US) 객체를 포함하는 `DataTransferItemList` (en-US) 객체입니다. 드래그되는 각 객체에 대해 하나의 목록 항목이 있습니다. 드래그 작업에 데이터가 없으면 목록은 비어 있습니다.

예제
--

### 드래그된 항목 로깅

이 예제는 드래그된 항목에 대한 정보를 기록하기 위해 `items`를 사용합니다.

#### HTML

```html
<ul>
  <li id="source1" draggable="true">Drag Item 1 to the Drop Zone</li>
  <li id="source2" draggable="true">Drag Item 2 to the Drop Zone</li>
</ul>
<div id="target">Drop Zone</div>

<button id="reset">Reset</button>
```

#### CSS

```css
div {
  margin: 0em;
  padding: 2em;
}

#target {
  border: 1px solid black;
}
```

#### JavaScript

```js
function dragstartHandler(ev) {
  console.log(`dragstart: target.id = ${ev.target.id}`);
  // 요소의 id를 드래그 페이로드에 추가합니다.
  // 그러면 드롭 핸들러가 트리에 추가할 요소를 알 수 있습니다.
  ev.dataTransfer.setData("text/plain", ev.target.id);
  ev.dataTransfer.effectAllowed = "move";
}

function dropHandler(ev) {
  ev.preventDefault();
  // 대상의 id를 가져오고 이동된 요소를 대상의 DOM에 추가합니다.
  const data = ev.dataTransfer.getData("text");
  ev.target.appendChild(document.getElementById(data));
  // 각 항목의 "kind"와 "type"을 출력합니다.
  if (ev.dataTransfer.items) {
    for (const item of ev.dataTransfer.items) {
      console.log(`kind = ${item.kind}, type = ${item.type}`);
    }
  }
}

function dragoverHandler(ev) {
  ev.preventDefault();
  // 이동할 dropEffect를 설정합니다.
  ev.dataTransfer.dropEffect = "move";
}

const source1 = document.querySelector("#source1");
const source2 = document.querySelector("#source2");
const target = document.querySelector("#target");

source1.addEventListener("dragstart", dragstartHandler);
source2.addEventListener("dragstart", dragstartHandler);
target.addEventListener("dragover", dragoverHandler);
target.addEventListener("drop", dropHandler);

const reset = document.querySelector("#reset");
reset.addEventListener("click", () => document.location.reload());
```

#### 결과

명세서
---

| Specification |
| --- |
| HTML Standard # dom-datatransfer-items-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* HTML 드래그 앤 드롭 API
* Drag Operations (en-US)
* Recommended Drag Types (en-US)
* DataTransfer test - Paste or Drag


WebSocket.readyState - Web API

WebSocket.readyState
====================

**`WebSocket.readyState`** 읽기 전용 속성은 `WebSocket` 연결의 현재 상태를 반환합니다.

값
-

아래의 `unsigned short` 값 중 하나입니다.

| 값 | 상태 | 설명 |
| --- | --- | --- |
| `0` | `CONNECTING` | 소켓이 생성됐으나 연결은 아직 개방되지 않았습니다. |
| `1` | `OPEN` | 연결이 개방되어 통신할 수 있습니다. |
| `2` | `CLOSING` | 연결을 닫는 중입니다. |
| `3` | `CLOSED` | 연결이 닫혔거나, 개방할 수 없었습니다. |

명세
--

| Specification |
| --- |
| WebSockets Standard # ref-for-dom-websocket-readystate① |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


WebSocket() - Web API

WebSocket()
===========

**`WebSocket()`** 생성자는 새로운 `WebSocket` 객체를 반환합니다.

구문
--

```js
new WebSocket(url);
new WebSocket(url, protocols);
```

### 매개변수

`url`

연결할 URL입니다. WebSocket 서버가 응답할 URL이어야 합니다.

`protocols` Optional

하나의 프로토콜 문자열, 또는 프로토콜 문자열로 구성된 배열입니다. 하나의 서버가 다수의 WebSocket 하위 프로토콜을 구현할 수 있도록, 사용할 하위 프로토콜을 가리키기 위한 값입니다. (예를 들어, 지정한 `protocol`에 따라 하나의 서버로 서로 다른 유형의 상호작용을 처리하길 바랄 때 사용할 수 있습니다)

생략할 때의 기본 값은 빈 배열, 즉 `[]` 입니다.

### 예외

`SyntaxError` `DOMException` (en-US)

아래의 경우 발생합니다.

* `url`을 파싱할 수 없음
* `url`의 스킴이 `ws`와 `wss` 중 하나가 아님
* `url`에 프래그먼트가 있음
* `protocols` 배열의 값들이 고유하지 않거나, WebSocket Protocol 명세가 정의한 `Sec-WebSocket-Protocol` (en-US)의 필드 값을 구성하는 요소들의 요구사항에 맞지 않음

명세
--

| Specification |
| --- |
| WebSockets Standard # ref-for-dom-websocket-websocket① |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* RFC 6455 (WebSocket Protocol 명세)


EventTarget.addEventListener() - Web API

EventTarget.addEventListener()
==============================

`EventTarget` 인터페이스의 **`addEventListener()`** 메서드는 지정한 유형의 이벤트를 대상이 수신할 때마다 호출할 함수를 설정합니다.

일반적인 대상은 `Element` (en-US), `Document`, `Window`지만, `XMLHttpRequest`와 같이 이벤트를 지원하는 모든 객체가 대상이 될 수 있습니다.

**참고:** 이벤트 수신기는 다른 방법으로도 추가할 수 있지만, `addEventListener()` 메서드를 사용해서 다음의 장점을 누리는 것이 좋습니다.

* 하나의 이벤트 유형에 대해 다수의 수신기를 부착할 수 있습니다. 라이브러리, JavaScript 모듈 등, 다른 라이브러리나 확장 코드와 충돌하지 않고 동작해야 하는 코드에 특히 중요한 점입니다.
* `onXYZ` 속성과 달리, 수신기가 어떤 이벤트 전파 단계(캡처링 vs. 버블링)에 동작해야 하는지 조절할 수 있습니다.
* HTML과 SVG 요소가 아니어도 이벤트 대상이라면 사용할 수 있습니다.

`addEventListener()` 메서드는 지정한 이벤트 유형의`EventTarget` 수신기 리스트에 `EventListener`를 구현한 함수 또는 객체를 추가하는 방식으로 동작합니다. 추가하려는 함수 또는 객체가 이미 수신기 리스트에 포함되어 있는 경우에는 추가하지 않으므로 수신기는 중복으로 등록되지 않습니다.

추가한 수신기를 반드시 `removeEventListener()`로 직접 제거해야 하는 것은 아닙니다.

**참고:** 두 개의 익명 함수는, 함수 본문이 동일하더라도, `addEventListener()`에 있어 항상 서로 다른 함수로 취급되므로 둘을 동시에 한 대상의 이벤트 수신기로 부착할 수 있습니다.

사실, 본문의 내용이 항상 동일하고 불변하더라도, 그 본문을 사용해 정의하는 각각의 익명 함수는 항상 서로를 구별할 수 있는 별도의 함수입니다. **반복문 내에서 정의하는 경우에도 마찬가지입니다.**

이와 같이 반복적으로 익명 함수를 정의하게 되면 문제가 될 수 있습니다. 아래의 메모리 문제를 확인하세요.

`EventTarget`에 부착된 이벤트 수신기가 이벤트 처리 중에 새로운 수신기를 등록하더라도, 현재 처리 중인 이벤트로는 그 수신기가 발동하지 않습니다. 그러나 버블링 단계처럼 이후의 이벤트 처리 흐름에서는 발동할 수 있습니다.

구문
--

```js
addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
```

### 매개변수

`type`

수신할 이벤트 유형 (en-US)을 나타내는 대소문자 구분 문자열입니다.

`listener`

지정한 이벤트(`Event` 인터페이스를 구현한 객체)를 수신할 객체입니다. `handleEvent()` 메서드를 포함하는 객체 또는 JavaScript 함수여야 합니다. 이벤트 수신기 콜백에서 콜백 자체에 대한 정보를 더 알아보세요.

`options` Optional

이벤트 수신기의 특징을 지정할 수 있는 객체입니다. 가능한 옵션은 다음과 같습니다.

`capture`

이벤트 대상의 DOM 트리 하위에 위치한 자손 `EventTarget`으로 이벤트가 전달되기 전에, 이 수신기가 먼저 발동돼야 함을 나타내는 불리언 값입니다. 명시하지 않을 경우 기본 값은 `false`입니다.

`once`

수신기가 최대 한 번만 동작해야 함을 나타내는 불리언 값입니다. `true`를 지정할 경우, 수신기가 발동한 후에 스스로를 대상에서 제거합니다. 명시하지 않을 경우 기본 값은 `false`입니다.

`passive`

`true`일 경우, 이 수신기 내에서 `preventDefault()`를 절대 호출하지 않을 것임을 나타내는 불리언 값입니다. 이 값이 `true`인데 수신기가 `preventDefault()`를 호출하는 경우, 사용자 에이전트는 콘솔에 경고를 출력하는 것 외에 아무런 동작도 하지 않습니다. 명시하지 않을 경우의 기본 값은 `false`지만, Safari와 Internet Explorer를 제외한 브라우저에서 `wheel` (en-US), `mousewheel` (en-US), `touchstart` (en-US), `touchmove` (en-US) 이벤트에서의 기본 값은 `true`입니다. 패시브 수신기로 스크롤 성능 향상에서 이 값에 대해 더 알아보세요.

`signal`

`AbortSignal`입니다. 지정한 `AbortSignal` 객체의 `abort()` 메서드를 호출하면 이 수신기가 제거됩니다. 명시하지 않을 경우 이벤트 수신기가 아무 `AbortSignal`에도 연결되지 않습니다.

`useCapture` Optional

이벤트 대상의 DOM 트리 하위에 위치한 자손 `EventTarget`으로 이벤트가 전달되기 전에, 이 수신기가 먼저 발동돼야 함을 나타내는 불리언 값입니다. 캡처 모드인 수신기는 DOM 트리의 위쪽으로 버블링 중인 이벤트에 의해선 발동하지 않습니다. 이벤트 버블링과 캡처링은 조상-자손 관계를 가진 두 개의 요소가 동일한 이벤트 유형에 대한 수신기를 가지고 있을 때, 두 요소에 이벤트가 전파되는 방법을 말합니다. 이벤트 전파 모드에 따라 두 요소 중 이벤트를 먼저 수신하는 쪽이 달라집니다. DOM Level 3 Events와 JavaScript Event 순서에서 자세한 설명을 확인하세요. 기본 값은 `false`입니다.

**참고:** 이벤트의 대상에 부착된 수신기의 경우 캡처링도, 버블링 단계도 아닌 별도의 단계에 발동합니다.
 캡처 모드의 수신기는 캡처 모드가 아닌 다른 모든 수신기보다 앞서 발동합니다.

`wantsUntrusted` Optional
비표준

Firefox(Gecko)에서만 사용할 수 있습니다. `true`일 경우 수신기가 웹 콘텐츠에서 발송하는 합성 이벤트를 수신할 수 있습니다. (기본 값은 브라우저 크롬에서는 `false`, 일반 웹 페이지에서는 `true`입니다.) 브라우저 확장, 또는 브라우저 자체 코드에서 유용하게 사용할 수 있습니다.

### 반환 값

없음 (`undefined`).

사용 일람
-----

### 이벤트 수신기 콜백

`addEventListener()`에 지정하는 이벤트 수신기는 콜백 함수거나, 콜백으로 작동할 `handleEvent()` 메서드를 포함하는 객체입니다.

콜백 함수 자체는 `handleEvent()` 메서드와 같은 매개변수, 같은 반환 값을 가집니다. 즉, 콜백 함수는 발생한 이벤트를 설명하는 `Event` 기반 객체를 유일한 매개변수로 받고, 아무것도 반환하지 않습니다.

다음은 `fullscreenchange` (en-US)와 `fullscreenerror` (en-US) 두 유형의 이벤트를 동시에 처리할 수 있는 이벤트 처리 콜백의 예제입니다.

```js
function eventHandler(event) {
  if (event.type == "fullscreenchange") {
    /\* 전체화면 여부 변화 처리 \*/
  } /\* fullscreenerror \*/ else {
    /\* 전체화면 오류 처리 \*/
  }
}
```

### 옵션 지원을 안전하게 감지하기

과거 DOM 명세에선 `addEventListener()`의 세 번째 매개 변수가 캡처 여부를 나타내는 불리언 값이었습니다. 그러나 시간이 지남에 따라 더 많은 옵션이 필요하다는 것이 분명해졌습니다. 메서드 매개변수를 계속 늘리면 선택적인 옵션을 나타내는 매개변수 처리가 어려워질 것이므로, 이제 세 번째 매개변수는 불리언 값이 아니라 이벤트 수신기의 다양한 성질을 설명하는 옵션 객체가 됐습니다.

구형 브라우저에서는 (그리고 일부 상대적으로 덜 오래된 브라우저에서도) 여전히 세 번째 매개변수에 오로지 불리언 값만 기대하고 있으므로 브라우저간 차이점을 처리할 수 있는 코드를 작성할 필요가 있습니다. 필요한 옵션에 대한 기능 감지 코드를 사용하세요.

예를 들어, `passive` 옵션의 지원 여부를 알아보고 싶다고 가정하겠습니다.

```js
let passiveSupported = false;

try {
  const options = {
    get passive() {
      // 브라우저가 passive 속성에 접근하려고 하면 이 함수가 실행됨
      passiveSupported = true;
      return false;
    },
  };

  window.addEventListener("test", null, options);
  window.removeEventListener("test", null, options);
} catch (err) {
  passiveSupported = false;
}
```

위의 코드는 `passive` 속성에 대한 접근자 함수를 가진 `options` 객체를 생성하고, `passive` 접근자는 자신이 호출되는 순간 `passiveSupported` 플래그 변수를 `true`로 설정합니다. 이 말은, `passiveSupported`가 `true`라면 브라우저가 `options` 객체의 `passive` 속성을 확인한다는 뜻이고, `false`면 확인하지 않는다는 뜻입니다. 그 아래에서는 `addEventListener()`를 사용해 가짜 이벤트 처리기를 등록, 브라우저가 세 번째 매개변수 객체를 인식할 수 있는지 확인하고, `removeEventListener()`로 정리합니다. (이벤트 수신기는 발동할 일이 없으므로 `null`을 지정해도 무방합니다.)

이 방법으로 모든 옵션에 대한 지원 여부를 확인할 수 있습니다. 위 코드와 비슷하게, 확인하려는 옵션에 대한 접근자를 추가하기만 하면 됩니다.

그 후, 실제로 플래그를 사용할 땐 아래와 같이 할 수 있습니다.

```js
someElement.addEventListener(
  "mouseup",
  handleMouseUp,
  passiveSupported ? { passive: true } : false,
);
```

위 코드에서는 {domxref("Element/mouseup\_event", "mouseup")}} 이벤트에 대한 수신기를 `someElement` 요소에 추가하고 있습니다. 세 번째 매개변수를 살펴보면, 만약 `passiveSupported`가 `true`일 경우 `passive`를 `true`로 지정한 옵션 객체를 지정하고 있으며, `passiveSupported`가 `false`일 경우, 세 번째 매개변수는 불리언 값이어야 할 것이므로 `useCapture`에 대한 값인 `false`를 지정하는 모습입니다.

직접 알아내는 대신 Modernizr나 Detect It 등 서드파티 라이브러리를 사용해 기능 감지를 할 수도 있습니다.

Web Incubator Community Group의 `EventListenerOptions`에 대한 글에서 더 자세한 정보를 알아보세요.

예제
--

### 간단한 수신기 추가하기

이 예제는 `addEventListener()`를 사용하여 요소에 대한 마우스 클릭을 감지하는 방법을 보여줍니다.

#### HTML

```html
<table id="outside">
  <tr>
    <td id="t1">one</td>
  </tr>
  <tr>
    <td id="t2">two</td>
  </tr>
</table>
```

#### JavaScript

```js
// t2의 콘텐츠를 바꾸는 함수
function modifyText() {
  const t2 = document.getElementById("t2");
  if (t2.firstChild.nodeValue == "three") {
    t2.firstChild.nodeValue = "two";
  } else {
    t2.firstChild.nodeValue = "three";
  }
}

// 표에 이벤트 수신기 추가
const el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
```

위 코드의 `modifyText()`가 `addEventListener()`로 추가하는 `click` 이벤트의 수신기입니다. 표 요소의 어디를 클릭하든, 클릭 이벤트는 버를링을 통해 전파되다가 `modifyText()`를 발동하게 됩니다.

#### 결과

### 중단 가능한 수신기 추가하기

이 예제는 `AbortSignal`로 중단할 수 있는 `addEventListener()`의 모습을 보입니다.

#### HTML

```html
<table id="outside">
  <tr>
    <td id="t1">one</td>
  </tr>
  <tr>
    <td id="t2">two</td>
  </tr>
</table>
```

#### JavaScript

```js
// 표에 중단 가능한 수신기 추가
const controller = new AbortController();
const el = document.getElementById("outside");
el.addEventListener("click", modifyText, { signal: controller.signal });

// t2의 콘텐츠를 바꾸는 함수
function modifyText() {
  const t2 = document.getElementById("t2");
  if (t2.firstChild.nodeValue == "three") {
    t2.firstChild.nodeValue = "two";
  } else {
    t2.firstChild.nodeValue = "three";
    controller.abort(); // 값이 "three"가 되면 수신기 제거
  }
}
```

이 예제는 앞선 첫 번째 예제를 수정해서, 표의 두 번째 행 내용이 "three"가 되면 `addEventListener()`의 옵션에 지정한 `AbortSignal`의 `abort()`를 호출합니다. 그 결과로 클릭 이벤트 수신기가 해제되고, 두 번째 행의 내용은 "three"에서 바뀌지 않게 됩니다.

#### 결과

### 익명 함수와 이벤트 수신기

여기선 익명 함수를 사용해 이벤트 수신기에 매개변수를 지정하는 방법을 보입니다.

#### HTML

```html
<table id="outside">
  <tr>
    <td id="t1">one</td>
  </tr>
  <tr>
    <td id="t2">two</td>
  </tr>
</table>
```

#### JavaScript

```js
// t2의 콘텐츠를 바꾸는 함수
function modifyText(new\_text) {
  const t2 = document.getElementById("t2");
  t2.firstChild.nodeValue = new_text;
}

// 표에 이벤트 수신기 추가
const el = document.getElementById("outside");
el.addEventListener(
  "click",
  function () {
    modifyText("four");
  },
  false,
);
```

`addEventListener()` 호출을 살펴보면, 실제로 이벤트에 반응할 `modifyText()` 함수에 매개변수를 제공하는 코드가 익명 함수에 의해 캡슐화된 모습을 볼 수 있습니다.

#### 결과

### 화살표 함수와 이벤트 수신기

이 예제는 화살표 함수를 사용해서 간단한 이벤트 수신기를 구현하는 모습을 보입니다.

#### HTML

```html
<table id="outside">
  <tr>
    <td id="t1">one</td>
  </tr>
  <tr>
    <td id="t2">two</td>
  </tr>
</table>
```

#### JavaScript

```js
// t2의 콘텐츠를 바꾸는 함수
function modifyText(new\_text) {
  const t2 = document.getElementById("t2");
  t2.firstChild.nodeValue = new_text;
}

// 화살표 함수를 사용한 이벤트 수신기를 표에 추가
const el = document.getElementById("outside");
el.addEventListener(
  "click",
  () => {
    modifyText("four");
  },
  false,
);
```

#### 결과

화살표 함수와 익명 함수는 비슷하지만, `this` 바인딩에 차이가 있다는 사실에 주의해야 합니다. 익명 함수(와 다른 모든 전통적인 JavaScript 함수)는 스스로의 `this` 바인딩을 생성하지만, 화살표 함수는 자신을 포함하고 있는 함수의 `this` 바인딩을 상속합니다.

따라서 화살표 함수를 포함한 함수의 변수와 상수를 이벤트 처리 코드에서도 사용할 수 있습니다.

### 옵션 객체 사용하기

#### HTML

```html
<div class="outer">
  바깥, 일회용 & 다회용
  <div class="middle" target="\_blank">
    중간, 캡처 & 비 캡처
    <a class="inner1" href="https://www.mozilla.org" target="\_blank">
      내부 1, 패시브 & (허용되지 않은) preventDefault
    </a>
    <a class="inner2" href="https://developer.mozilla.org/" target="\_blank">
      내부 2, 비 패시브 & preventDefault (새 페이지가 열리지 않음)
    </a>
  </div>
</div>
```

#### CSS

```css
.outer,
.middle,
.inner1,
.inner2 {
  display: block;
  width: 520px;
  padding: 15px;
  margin: 15px;
  text-decoration: none;
}
.outer {
  border: 1px solid red;
  color: red;
}
.middle {
  border: 1px solid green;
  color: green;
  width: 460px;
}
.inner1,
.inner2 {
  border: 1px solid purple;
  color: purple;
  width: 400px;
}
```

#### JavaScript

```js
const outer = document.querySelector(".outer");
const middle = document.querySelector(".middle");
const inner1 = document.querySelector(".inner1");
const inner2 = document.querySelector(".inner2");

const capture = {
  capture: true,
};
const noneCapture = {
  capture: false,
};
const once = {
  once: true,
};
const noneOnce = {
  once: false,
};
const passive = {
  passive: true,
};
const nonePassive = {
  passive: false,
};

outer.addEventListener("click", onceHandler, once);
outer.addEventListener("click", noneOnceHandler, noneOnce);
middle.addEventListener("click", captureHandler, capture);
middle.addEventListener("click", noneCaptureHandler, noneCapture);
inner1.addEventListener("click", passiveHandler, passive);
inner2.addEventListener("click", nonePassiveHandler, nonePassive);

function onceHandler(event) {
  alert("바깥, 일회용");
}
function noneOnceHandler(event) {
  alert("바깥, 다회용, 기본 값");
}
function captureHandler(event) {
  //event.stopImmediatePropagation();
  alert("중간, 캡처");
}
function noneCaptureHandler(event) {
  alert("중간, 비 캡처, 기본 값");
}
function passiveHandler(event) {
  // 패시브 수신기 내에서는 preventDefault 사용 불가
  event.preventDefault();
  alert("내부 1, 패시브, 새 페이지 열림");
}
function nonePassiveHandler(event) {
  event.preventDefault();
  //event.stopPropagation();
  alert("내부 2, 비 패시브, 기본 값, 새 페이지 열림");
}
```

#### 결과

바깥과 중간, 내부 컨테이너를 반복해서 클릭해 각각의 옵션이 어떻게 동작하는지 확인해보세요.

`options` 객체에 특정 값을 지정하기 전에, 옵션의 지원 범위는 브라우저와 브라우저 버전에 따라 다르므로 브라우저가 해당 옵션을 지원하는지 먼저 확인하는 것이 좋습니다. 옵션 지원을 안전하게 감지하기에서 방법을 알아보세요.

기타 일람
-----

### 이벤트 수신기 내부의 this 값

비슷한 요소 다수의 이벤트를 모두 처리할 수 있는 범용 수신기를 정의하는 경우, 부착된 요소의 참조를 가져와야 하는 상황이 종종 발생합니다.

`addEventListener()`를 사용해 요소에 수신기를 부착하게 되면 수신기 내부의 `this` 값은 대상 요소를 가리키게 되며, 이는 수신기가 매개변수로 받게 되는 이벤트 객체의 `currentTarget` 속성과 같습니다.

```js
my_element.addEventListener("click", function (e) {
  console.log(this.className); // my\_element의 className 기록
  console.log(e.currentTarget === this); // `true` 기록
});
```

다만 화살표 함수는 스스로의 `this` 맥락을 가지지 않는다는 점을 기억해야 합니다.

```js
my_element.addEventListener("click", (e) => {
  console.log(this.className); // 경고: `this`가 `my\_element`가 아님
  console.log(e.currentTarget === this); // `false` 기록
});
```

HTML 소스 코드에서 (`onclick` (en-US) 특성 등을 사용해) 요소의 이벤트 수신기를 지정하는 경우, 이벤트 수신기 특성 내의 JavaScript 코드는 가상의 함수로 감싸인 후, 감싼 함수의 `this`를 `addEventListener()`와 같은 방식으로 바인딩하는 것으로 생각할 수 있습니다. 즉, 해당 특성의 값에서 등장하는 `this`는 특성이 속한 요소를 가리킵니다.

```html
<table id="my\_table" onclick="console.log(this.id);">
  <!-- `this`가 표를 가리키므로 'my\_table'을 기록함 -->
  ...
</table>
```

참고로 이벤트 수신기 특성 안에서 호출한 함수의 `this`는 표준 규칙을 따라가며, 아래 코드로 확인할 수 있습니다.

```html
<script>
 function logID() {
 console.log(this.id);
 }
</script>
<table id="my\_table" onclick="logID();">
  <!-- 호출 시, logID의 `this`는 전역 객체를 가리키게 됨 -->
  ...
</table>
```

`logID()` 내의 `this`는 전역 객체 `Window`의 참조가 됩니다. (단, 엄격 모드에서는 `undefined`)

#### bind()로 this 설정하기

`Function.prototype.bind()` 메서드를 사용하면 모든 후속 호출의 `this` 맥락을 고정할 수 있으므로, `this`의 값이 호출 위치에 따라 달라져서 정확히 파악하기 힘들 때 발생하는 문제를 먼저 방지할 수 있습니다. 다만, 바인드한 함수 참조를 어딘가에 보관해놔야 나중에 수신기를 제거할 수 있습니다.

다음은 `bind()`를 사용하는 수신기와 사용하지 않는 수신기의 모습입니다.

```js
const Something = function (element) {
  // this는 Something 인스턴스
  this.name = "Something Good";
  this.onclick1 = function (event) {
    console.log(this.name); // undefined, this는 element임
  };

  this.onclick2 = function (event) {
    console.log(this.name); // 'Something Good', this는 Something 인스턴스
  };

  // bind가 onclick2의 this 맥락을 고정함
  this.onclick2 = this.onclick2.bind(this);

  element.addEventListener("click", this.onclick1, false);
  element.addEventListener("click", this.onclick2, false);
};
const s = new Something(document.body);
```

다른 방법으로는 `handleEvent()`라는 특별한 이름의 메서드를 구현해 모든 이벤트에 대응하는 것입니다.

```js
const Something = function (element) {
  // this는 Something 인스턴스
  this.name = "Something Good";
  this.handleEvent = function (event) {
    console.log(this.name); // 'Something Good', this는 Something 인스턴스로 바인딩 됨
    switch (event.type) {
      case "click":
        // 다른 코드...
        break;
      case "dblclick":
        // 다른 코드...
        break;
    }
  };

  // 수신기 매개변수가 this.handleEvent가 아니라 this인 것에 주의
  element.addEventListener("click", this, false);
  element.addEventListener("dblclick", this, false);

  // 수신기 제거도 가능
  element.removeEventListener("click", this, false);
  element.removeEventListener("dblclick", this, false);
};
const s = new Something(document.body);
```

`this` 참조를 처리할 수 있는 또 다른 방법으로는 객체의 필드에 접근해야 하는 메서드를 익명 함수로 감싸서 제공하는 것이 있습니다.

```js
class SomeClass {
  constructor() {
    this.name = "Something Good";
  }

  register() {
    const that = this;
    window.addEventListener("keydown", function (e) {
      that.someMethod(e);
    });
  }

  someMethod(e) {
    console.log(this.name);
    switch (e.keyCode) {
      case 5:
        // 다른 코드...
        break;
      case 6:
        // 다른 코드...
        break;
    }
  }
}

const myObject = new SomeClass();
myObject.register();
```

### 이벤트 수신기 안팎으로 데이터 옮기기

이벤트 수신기에 데이터를 제공하는 것은 물론, 수신기 실행이 끝난 후 결과를 가져오는 것 또한 매우 어려워보일 것입니다. 이벤트 수신기는 유일한 매개변수로 이벤트 객체만 받으며, 반환하는 모든 값은 무시됩니다. 그러면 어떻게 수신기에 데이터를 전달하거나, 그 밖으로 데이터를 가져올 수 있을까요? 몇 가지 좋은 방법을 소개합니다.

#### this로 이벤트 수신기에 데이터 전달하기

위에서 언급했듯, `Function.prototype.bind()`를 사용하면 `this` 참조 변수를 통해 수신기에 값을 전달할 수 있습니다.

```js
const myButton = document.getElementById("my-button-id");
const someString = "Data";

myButton.addEventListener(
  "click",
  function () {
    console.log(this); // 예상 값: 'Data'
  }.bind(someString),
);
```

이 방법은 수신기가 발동된 대상을 알 필요가 없을 때 적합하며, 매개변수를 통해 값을 제공하는 것과 크게 다르지 않은 방법을 제공한다는 점이 장점입니다.

#### 바깥 스코프 속성으로 이벤트 수신기에 데이터 전달하기

바깥 스코프에서 `const`, `let`을 사용해 변수를 선언할 경우, 해당 스코프 안의 모든 함수에서도 해당 변수에 접근할 수 있습니다(함수 문서에서 바깥/내부 함수에 대한 정보를, `var` 문서에서 변수 스코프에 대한 정보를 읽어보세요). 따라서, 이벤트 수신기가 바깥 데이터에 접근할 수 있는 가장 쉬운 방법은, 그 데이터를 이벤트 수신기가 선언된 스코프에서 접근할 수 있도록 하는 것입니다.

```js
const myButton = document.getElementById("my-button-id");
let someString = "Data";

myButton.addEventListener("click", function () {
  console.log(someString); // 예상 값: 'Data'

  someString = "Data Again";
});

console.log(someString); // 예상 값: 'Data' ('Data Again'을 출력하지 않음)
```

**참고:** 내부 스코프에서 바깥 스코프의 `const`와 `let` 변수에 접근할 수는 있지만, 수신기 내에서 변수의 값을 바꾸더라도 바깥 스코프에 그 변경점이 나타날 것으로 생각해서는 안됩니다. 이벤트 수신기가 발동하는 시점에는 이미 수신기가 속한 스코프가 실행을 마쳤을 것이기 때문입니다.

#### 객체를 사용해 이벤트 수신기 안팎으로 데이터 전달하기

자신을 참조하는 변수가 메모리에 남아있는 한, 객체는 메모리에서 사라지지 않습니다. 이 성질에 더해, 객체는 속성을 갖는다는 점, 그리고 참조로 전달된다는 점을 활용하면 스코프 안팎에서 데이터를 교환하는 창구로 객체를 이용할 수 있습니다.

**참고:** JavaScript에서는 함수도 사실 객체입니다. 따라서 함수도 속성을 가질 수 있고, 메모리에 유지되는 변수에 할당한 경우 실행 후에도 메모리에서 해제되지 않습니다.

객체를 참조하는 변수가 메모리에 존재하는 한 객체 속성을 사용해 메모리에 데이터를 저장할 수 있으니, 이벤트 수신기 내부로 데이터를 전달할 때도 사용할 수 있고, 이벤트 처리기 실행이 끝난 후 변화된 데이터를 가져와야 할 때도 사용할 수 있습니다. 아래 코드를 살펴보세요.

```js
const myButton = document.getElementById("my-button-id");
const someObject = { aProperty: "Data" };

myButton.addEventListener("click", function () {
  console.log(someObject.aProperty); // 예상 값: 'Data'

  someObject.aProperty = "Data Again"; // 값 변경
});

window.setInterval(function () {
  if (someObject.aProperty === "Data Again") {
    console.log("Data Again: True");
    someObject.aProperty = "Data"; // 다음 이벤트 실행을 기다리기 위해 값 초기화
  }
}, 5000);
```

위 예제를 보면, 이벤트 수신기와 인터벌 함수가 정의된 스코프는 `someObject.aProperty`가 바뀌기 전에 실행이 끝나겠지만, `someObject`의 참조가 수신기와 인터벌 함수 메모리에 계속 남아있기 때문에, 양쪽 모두에서 같은 데이터에 접근할 수 있습니다. 즉, 한 쪽이 데이터를 바꾸면 반대편에서도 바뀐 데이터를 볼 수 있습니다.

**참고:** 객체는 변수에 참조, 실제 데이터가 저장된 메모리의 주소로 할당됩니다. 이 말은 곧, 객체를 담고 있는 변수를 사용하면, 같은 객체를 담은 다른 변수에도 영향을 줄 수 있다는 뜻입니다. 두 개의 변수가 같은 객체를 참조(e.g. `let a = b = {aProperty: 'Yeah'};`)하면, 한 쪽의 변수 데이터만 바꿔도 다른 쪽 데이터가 같이 바뀝니다.

**참고:** 객체는 변수에 참조로 저장되기 때문에, 함수에서 객체를 반환하면 함수가 종료된 이후에도 계속 지속(데이터를 잃지 않도록 메모리에 유지)시킬 수 있습니다.

### 메모리 문제

```js
const els = document.getElementsByTagName("\*");

// 상황 1
for (let i = 0; i < els.length; i++) {
  els[i].addEventListener(
    "click",
    function (e) {
      /\*do something\*/
    },
    false,
  );
}

// 상황 2
function processEvent(e) {
  /\* do something \*/
}

for (let i = 0; i < els.length; i++) {
  els[i].addEventListener("click", processEvent, false);
}
```

위 코드에서 상황 1의 경우에는 반복문의 매 반복마다 새로운 익명 처리기 함수가 생성됩니다. 반면 상황 2에서는 사전에 정의한 함수를 이벤트 처리기로 사용하므로 처리기 함수를 하나만 사용하고, 따라서 더 작은 메모리 공간만 사용합니다. 더군다나 상황 1에서는, 익명 함수에 대한 참조를 유지하지 않으므로 `removeEventListener()`를 호출할 수 없습니다. 반면 상황 2에서는, `processEvent`가 처리기 함수를 가리키므로 `myElement.removeEventListener("click", processEvent, false)`를 할 수 있습니다.

사실, 메모리 소비와 관련하여, 함수 참조를 유지하지 못하는 것은 중요한 문제가 아닙니다. 진짜 문제는 함수 참조를 '정적으로' 유지하지 못하는 것입니다. 이 점을 보여주는 아래의 상황 3과 상황 4에서는 함수 참조를 유지하긴 하지만, 매 반복마다 재정의됩니다. 상황 3에서는 반복할 때마다 익명 함수에 대한 참조를 재할당하고, 상황 4에서는 함수 전체 정의는 변하지 않지만 매번 마치 새로운 함수처럼 반복적으로 재정의되므로 두 상황 모두 정적이지 않습니다. 따라서 코드를 보기엔 다수의 동일한 이벤트 수신기처럼 보이지만, 사실 각 반복마다 새로운 처리기를 참조하는 새로운 이벤트 수신기를 생성하고 있는 것입니다.

```js
const els = document.getElementsByTagName("\*");

function processEvent(e) {
  /\* do something \*/
}

// 시연을 위해 [i] 대신 [j]를 사용하는 실수를 한 것에 주의하세요. 반복문 내에서 정의한 수신기를 모두 첫 요소에 등록하고 있습니다.

// 상황 3
for (let i = 0, j = 0; i < els.length; i++) {
  els[j].addEventListener(
    "click",
    (processEvent = function (e) {
      /\* do something \*/
    }),
    false,
  );
}

// 상황 4
for (let i = 0, j = 0; i < els.length; i++) {
  function processEvent(e) {
    /\* do something \*/
  }
  els[j].addEventListener("click", processEvent, false);
}
```

또한 상황 3과 4에서는 함수 참조가 유지되긴 하지만 매번 `addEventListener()` 전에 재정의되므로, `removeEventListener("click", processEvent, false)`로 수신기를 제거할 수는 있으나 오직 마지막으로 정의된 수신기만 제거됩니다.

### 패시브 수신기로 스크롤 성능 향상

명세에 따르면, `passive` 옵션의 기본 값은 항상 `false` 입니다. 그러나 이 기본 값으로 인해 터치 이벤트 등 일부 이벤트의 수신기가 스크롤을 처리 중인 브라우저 메인 스레드를 블록할 가능성이 생기고, 따라서 스크롤 성능이 크게 저하될 수 있습니다.

이 문제를 방지하기 위해 일부 브라우저(Chrome과 Firefox 등)는 문서 레벨 노드인 `Window`, `Document`, `Document.body`의 `touchstart` (en-US)와 `touchmove` (en-US) 이벤트에 대해선 `passive`의 기본 값을 `true`로 바꿔 적용합니다. 패시브 이벤트 수신기는 이벤트를 취소할 수 없으므로 사용자가 스크롤할 때 브라우저의 렌더링을 방해하지 않습니다.

**참고:** 어떤 브라우저/버전에서 이렇게 동작하는지는 아래의 브라우저 호환성 표에 나와있습니다.

`passive` 옵션에 명시적으로 `false`를 지정해서 이 동작을 막을 수 있습니다.

```js
/\* 기능 감지 \*/
let passiveIfSupported = false;

try {
  window.addEventListener(
    "test",
    null,
    Object.defineProperty({}, "passive", {
      get: function () {
        passiveIfSupported = { passive: true };
      },
    }),
  );
} catch (err) {}

window.addEventListener(
  "scroll",
  function (event) {
    /\* do something \*/
    // event.preventDefault() 사용 불가
  },
  passiveIfSupported,
);
```

`addEventListener()`가 `options` 매개변수를 지원하지 않는 구형 브라우저에서 `options` 객체를 지정하면 `useCapture` 매개변수를 사용할 수 없으므로, 위와 같이 기능 감지를 거쳐야 합니다.

`scroll` (en-US) 이벤트에 대해서는 `passive` 옵션을 고려하지 않아도 됩니다. 어차피 취소할 수 없는 이벤트라서, 수신기가 페이지 렌더링을 막을 수도 없기 때문입니다.

명세
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-eventtarget-addeventlistener③ |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `EventTarget.removeEventListener()`
* 이벤트 생성 및 발동 (en-US)
* 이벤트 처리기의 `this`에 관한 더 자세한 정보


EventTarget() - Web API

EventTarget()
=============

**`EventTarget()`** 생성자는 새로운 `EventTarget` 객체 인스턴스를 생성합니다.

**참고:** 이 생성자를 명시적으로 사용하는 경우는 거의 없습니다. 보통은 `EventTarget`을 상속하는 객체의 생성자 내에서 `super` 키워드로 사용됩니다.

구문
--

```js
new EventTarget();
```

### 매개변수

없음.

### 반환 값

`EventTarget` 객체의 새로운 인스턴스.

예제
--

```js
class MyEventTarget extends EventTarget {
  constructor(mySecret) {
    super();
    this._secret = mySecret;
  }

  get secret() {
    return this._secret;
  }
}

let myEventTarget = new MyEventTarget(5);
let value = myEventTarget.secret; // == 5
myEventTarget.addEventListener("foo", function (e) {
  this._secret = e.detail;
});

let event = new CustomEvent("foo", { detail: 7 });
myEventTarget.dispatchEvent(event);
let newValue = myEventTarget.secret; // == 7
```

명세
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-eventtarget-eventtarget① |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `EventTarget`


EventTarget.removeEventListener() - Web API

EventTarget.removeEventListener()
=================================

`EventTarget` 인터페이스의 **`EventTarget.removeEventListener()`** 메서드는 `EventTarget.addEventListener()`로 이벤트 대상에 등록한 수신기를 제거합니다. 제거 대상 수신기의 식별은 이벤트 유형, 수신기 함수 참조, 그리고 식별에 관련된 다양한 옵션을 사용해 이뤄집니다. 제거할 이벤트 수신기의 식별을 참고하세요.

`removeEventListener()`를 호출할 때, `EventTarget`에 등록된 이벤트 수신기 중 어느 것에도 일치하지 않는 매개변수를 사용하면 아무 효과도 나타나지 않습니다.

`EventTarget`의 어느 수신기 중 하나가 이벤트를 처리하는 중에 다른 수신기를 제거할 경우, 제거된 이벤트 수신기는 현재 이벤트에 대해 발동하지 않습니다. 그러나 다시 부착할 수는 있습니다.

**경고:** 같은 수신기를 다른 캡처 플래그 값으로 두 번, 즉 한 번은 `capture` 플래그를 지정하고 한 번은 지정하지 않은 채 부착하는 경우 각각 따로따로 제거해야 합니다. 캡처 플래그를 지정한 수신기의 제거는 비 캡처 수신기에 영향을 주지 않고, 그 반대도 마찬가지입니다.

`addEventListener()`에 `AbortSignal`을 지정한 후, 나중에 `abort()` (en-US)를 호출해서 수신기를 제거하는 방법도 있습니다.

구문
--

```js
removeEventListener(type, listener);
removeEventListener(type, listener, options);
removeEventListener(type, listener, useCapture);
```

### 매개변수

`type`

이벤트 수신기를 제거할 이벤트 유형입니다.

`listener`

이벤트 대상에서 제거할 수신기 `EventListener` 함수입니다.

`options` Optional

제거할 이벤트 수신기의 특징을 나타내는 옵션 객체입니다.

가능한 옵션은 다음과 같습니다.

* `capture`: 제거할 `EventListener`가 캡처링 수신기로 등록됐는지에 대한 여부를 나타내는 불리언 값입니다. 기본 값은 `false`입니다.

`useCapture` Optional

제거할 `EventListener`가 캡처링 수신기로 등록됐는지에 대한 여부를 나타내는 불리언 값입니다. 기본 값은 `false`입니다.

### 반환 값

없음.

### 제거할 이벤트 수신기의 식별

`addEventListener()`로 이벤트 수신기를 추가했다면, 수신기를 제거해야 할 때가 오기도 합니다. 당연히 추가할 때 지정한 것과 같은 `type` 및 `listener`를 `removeEventListener()`의 매개변수로 지정해야 하겠지만, `options`나 `useCapture` 매개변수는 어떻게 해야 할까요?

`addEventListener()`는 옵션이 다르다면 이벤트 유형 하나에 같은 수신기를 여러 번 부착합니다. 그러나 `removeEventListener()`가 고려하는 옵션은 `capture`/`useCapture` 플래그 단 하나입니다. 따라서 `capture`/`useCapture` 값은 일치해야 하지만, 나머지 옵션은 일치하지 않아도 됩니다.

다음의 `addEventListener()` 호출을 예시로 보겠습니다.

```js
element.addEventListener("mousedown", handleMouseDown, true);
```

이제 아래의 두 `removeEventListener()` 호출을 살펴보세요.

```js
element.removeEventListener("mousedown", handleMouseDown, false); // 실패
element.removeEventListener("mousedown", handleMouseDown, true); // 성공
```

첫 번째 호출은 `useCapture`가 일치하지 않았기 때문에 실패하고, 두 번째 호출은 일치하기 때문에 성공합니다.

이제 다음 코드를 확인하세요.

```js
element.addEventListener("mousedown", handleMouseDown, { passive: true });
```

위 코드에서는 `passive`를 `true`로 지정한 `option` 객체를 지정했습니다. 다른 옵션은 기본 값인 `false`가 됩니다.

아래의 `removeEventListener()` 호출 각각을 살펴보세요. `capture`나 `useCapture`가 `true`인 호출은 실패하고, 나머지는 모두 성공합니다.

`removeEventListener()`에 영향을 주는 옵션은 `capture` 뿐입니다.

```js
element.removeEventListener("mousedown", handleMouseDown, { passive: true }); // 성공
element.removeEventListener("mousedown", handleMouseDown, { capture: false }); // 성공
element.removeEventListener("mousedown", handleMouseDown, { capture: true }); // 실패
element.removeEventListener("mousedown", handleMouseDown, { passive: false }); // 성공
element.removeEventListener("mousedown", handleMouseDown, false); // 성공
element.removeEventListener("mousedown", handleMouseDown, true); // 실패
```

다만 일부 브라우저 버전에서는 이 동작이 일관적이지 않으므로, 어쩔 수 없는 경우가 아니라면 `addEventListener()`에 지정한 옵션을 그대로 `removeEventListener()`에 제공하는 편이 낫습니다.

예제
--

이 예제는 `click` 이벤트 수신기를 제거하는 `mouseover` 이벤트 수신기를 보입니다.

```js
const body = document.querySelector("body");
const clickTarget = document.getElementById("click-target");
const mouseOverTarget = document.getElementById("mouse-over-target");

let toggle = false;
function makeBackgroundYellow() {
  if (toggle) {
    body.style.backgroundColor = "white";
  } else {
    body.style.backgroundColor = "yellow";
  }

  toggle = !toggle;
}

clickTarget.addEventListener("click", makeBackgroundYellow, false);

mouseOverTarget.addEventListener("mouseover", function () {
  clickTarget.removeEventListener("click", makeBackgroundYellow, false);
});
```

명세
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-eventtarget-removeeventlistener② |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `EventTarget.addEventListener()`


EventTarget.dispatchEvent() - Web API

EventTarget.dispatchEvent()
===========================

`EventTarget` 인터페이스의 **`dispatchEvent()`** 메서드는 `EventTarget` 객체로 `Event`를 발송해서, 해당 이벤트에 대해 등록된 `EventListener`들을 (동기적으로) 순서대로 호출합니다. `dispatchEvent()`를 통해 직접 발송한 이벤트에도 캡처링과 버블링 단계를 포함한 일반적인 이벤트 처리 규칙이 적용됩니다.

`dispatchEvent()` 호출은 이벤트 발송 과정의 마지막 단계입니다. 발송에 앞서 우선 `Event()` 생성자로 이벤트를 생성 및 초기화해야 합니다.

**참고:** `dispatchEvent()` 사용 시, `Event.target` 속성은 현재 `EventTarget`으로 초기화됩니다.

브라우저가 발송하는 "네이티브" 이벤트는 이벤트 루프를 통해 비동기적으로 처리기들을 호출하지만, `dispatchEvent()`로 발송된 이벤트는 처리기를 동기적으로 호출합니다. 즉, 모든 적합한 처리기의 호출과 반환이 끝나야 `dispatchEvent()` 역시 반환합니다.

구문
--

```js
dispatchEvent(event);
```

### 매개변수

`event`

발송할 `Event` 객체입니다. `Event.target`은 현재 `EventTarget`으로 설정됩니다.

### 반환 값

`event`가 취소 가능하고, 이벤트 처리기 중 하나에서 `Event.preventDefault()`를 호출한 경우 `false`, 그 외에는 `true`.

### 예외

`InvalidStateError` `DomException` (en-US)

이벤트 초기화 시 이벤트 유형을 지정하지 않은 경우 발생합니다.

**경고:** 이벤트 처리기에서 발생한 예외는 처리하지 않은 예외로 보고됩니다. 이벤트 처리기는 중첩 호출 스택에서 동작하므로, 실행이 끝날 때까지 호출자는 대기하지만, 예외는 호출자로 전파되지 않습니다.

예외
--

이벤트 생성과 발동를 확인하세요.

명세
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-eventtarget-dispatchevent③ |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* The Event object reference


NodeList.item() - Web API

NodeList.item()
===============

`NodeList` 의 node를 index로 돌려줍니다. 이 메서드는 인수(arguments)를 제공하는 한 exceptions 을 throw 하지 않습니다. index가 범위를 벗어나면 `null` 값이 반환되고, 인수가 제공되지 않으면 `TypeError` 가 throw 됩니다.

Syntax
------

```js
nodeItem = nodeList.item(index);
```

* `nodeList` 는 `NodeList` 입니다. 일반적으로 childNodes 와 같은 다른 DOM 속성(property) 또는 메서드에서 가져옵니다.
* `index` 는 fetch될 node의 index입니다. index는 0 에서 부터 시작합니다.
* `nodeItem` 은 `item` 메서드가 반환 한 `nodeList` 의 node `index` 입니다.
Alternate Syntax
----------------

JavaScript는 NodeList 에서 index를 얻기 위한, 배열과 같은 브라켓 문법([])을 제공합니다 :

```js
nodeItem = nodeList[index];
```

Example
-------

```js
var tables = document.getElementsByTagName("table");
var firstTable = tables.item(1); // or simply tables[1] - returns the second table in the DOM
```

Specification
-------------

DOM Level 1 Core: NodeList.item()

DOM Level 2 Core: NodeList.item()

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


NodeList.values() - Web API

NodeList.values()
=================

**`NodeList.values()`** 메서드는 이 객체에 포함된 모든 값을 통과할 수 있는 `iterator`를 반환합니다. 값은 `Node` (en-US) 객체 입니다.

Syntax
------

```js
nodeList.values();
```

### Return value

`iterator`를 반환합니다.

Example
-------

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// Using for..of
for (var value of list.values()) {
  console.log(value);
}
```

결과는 다음과 같습니다 :
```

<p>
#text "hey"
<span>
```

명세서
---

#### No specification found

No specification data found for `api.NodeList.values`.
Check for problems with this page or contribute a missing `spec_url` to mdn/browser-compat-data. Also make sure the specification is included in w3c/browser-specs.

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Node` (en-US)
* `NodeList`


element.length - Web API

element.length
==============

요약
--

`length`는 `NodeList`의 항목수를 반환합니다.

구문
--

```js
numItems = nodeList.length;
```

* `numItems`은 `NodeList`의 항목수를 나타내는 정수값입니다.
예
-

```js
// 문서의 모든 paragraph
var items = document.getElementsByTagName("p");
// 목록의 각 항목에,
// HTML의 문자열로 전체 요소를 추가
var gross = "";
for (var i = 0; i < items.length; i++) {
  gross += items[i].innerHTML;
}
// gross는 이제 모두 paragraph을 위한 HTML
```

주의
--

reference에서 이 페이지의 위치에도 불구하고, `length`는 Element의 프로퍼티가 아니고, `NodeList`의 프로퍼티입니다. NodeList 개체는 document.getElementsByTagName과 같은 많은 DOM 메소드에서 반환됩니다.

`length`는 DOM 프로그래밍에서 아주 흔한 프로퍼티입니다. 위 예에서처럼 목록의 길이(적어도 있는 지 보기 위해)를 조사하고 for 루프에서 훑개(반복자, iterator)로 널리 쓰입니다.

명세
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-nodelist-length① |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


NodeList.entries() - Web API

NodeList.entries()
==================

**`NodeList.entries()`** 메서드는 이 객체에 포함된 모든 key/value 쌍을 통과하는 `iterator` 를 반환합니다. 이 값(value)은 `Node` (en-US) 객체입니다.

Syntax
------

```js
list.entries();
```

### Return value

`iterator` 를 반환합니다.

Example
-------

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");
node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// Using for..of
for (var entry of list.entries()) {
  console.log(entry);
}
```

결과는 다음과 같습니다:
```

Array [ 0, <p> ]
Array [ 1, #text "hey" ]
Array [ 2, <span> ]
```

명세서
---

#### No specification found

No specification data found for `api.NodeList.entries`.
Check for problems with this page or contribute a missing `spec_url` to mdn/browser-compat-data. Also make sure the specification is included in w3c/browser-specs.

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Node` (en-US)
* `NodeList`


NodeList.prototype.forEach() - Web API

NodeList.prototype.forEach()
============================

`NodeList` 인터페이스의 **`forEach()`** 메서드는 리스트 내의 각각의 값 쌍에 대해 매개 변수에 지정된 콜백을 삽입 순서로 호출합니다.

문법Syntax
--------

```js
NodeList.forEach(callback[, thisArg]);
```

### Parameters

`callback`

 각각의 요소에 대해 실행하는 함수로, 3개의 인수(arguments)를 갖습니다:\_ *`currentValue`*
 \_ : NodeList에서 처리중인 현재 요소(element)입니다.

`currentIndex`

NodeList에서 처리중인 현재 요소의 인덱스입니다.

*`listObj`*

`forEach()` 가 적용되고 있는 NodeList 객체입니다.

`thisArg` Optional

`callback` 을 실행할 때 `this` 에 대입할 값입니다.

### Return value

`undefined`.

Exceptions
----------

*None*.

Example
-------

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

list.forEach(function (currentValue, currentIndex, listObj) {
  console.log(currentValue + ", " + currentIndex + ", " + this);
}, "myThisArg");
```

결과는 다음과 같습니다.
```

[object HTMLParagraphElement], 0, myThisArg
[object Text], 1, myThisArg
[object HTMLSpanElement], 2, myThisArg
```

Polyfill
--------

이 polyfill 은 ES5 를 지원하는 모든 브라우저에서 동작합니다:

```js
if (window.NodeList && !NodeList.prototype.forEach) {
  NodeList.prototype.forEach = function (callback, thisArg) {
    thisArg = thisArg || window;
    for (var i = 0; i < this.length; i++) {
      callback.call(thisArg, this[i], i, this);
    }
  };
}
```

또는

```js
if (window.NodeList && !NodeList.prototype.forEach) {
  NodeList.prototype.forEach = Array.prototype.forEach;
}
```

The above behavior is how many browsers actually implement NodeList.prototype.forEach (Chrome, for example).

명세서
---

| Specification |
| --- |
| DOM Standard # interface-nodelist |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Node` (en-US)
* `NodeList`


NodeList.keys() - Web API

NodeList.keys()
===============

**`NodeList.keys()`** 메서드는 이 객체에 포함된 모든 키를 통과할 수 있는 `iterator` 를 반환합니다. 이 키는 부호없는 정수형(`unsigned integer`) 입니다.

Syntax
------

```js
nodeList.keys();
```

### Return value

`iterator`를 반환합니다.

Example
-------

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// Using for..of
for (var key of list.keys()) {
  console.log(key);
}
```

결과는 다음과 같습니다 :
```

0
1
2
```

명세서
---

#### No specification found

No specification data found for `api.NodeList.keys`.
Check for problems with this page or contribute a missing `spec_url` to mdn/browser-compat-data. Also make sure the specification is included in w3c/browser-specs.

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Node` (en-US)
* `NodeList`


Geolocation.clearWatch() - Web API

Geolocation.clearWatch()
========================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`Geolocation.clearWatch()`** 메서드는 `Geolocation.watchPosition()`로 등록한 위치 변화 감지 콜백을 해제합니다.

구문
--

```js
navigator.geolocation.clearWatch(id);
```

### 매개변수

`id`

`Geolocation.watchPosition()` 메서드가 반환하는 콜백 ID.

예제
--

```js
var id, target, option;

function success(pos) {
  var crd = pos.coords;

  if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
    console.log("Congratulation, you reach the target");
    navigator.geolocation.clearWatch(id);
  }
}

function error(err) {
  console.warn("ERROR(" + err.code + "): " + err.message);
}

target = {
  latitude: 0,
  longitude: 0,
};

options = {
  enableHighAccuracy: false,
  timeout: 5000,
  maximumAge: 0,
};

id = navigator.geolocation.watchPosition(success, error, options);
```

명세
--

| Specification |
| --- |
| Geolocation API # clearwatch-method |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `Geolocation`
* `Geolocation.watchPosition()`
* `Geolocation.getCurrentPosition()`


Geolocation.watchPosition() - Web API

Geolocation.watchPosition()
===========================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

`Geolocation` 인터페이스의 **`watchPosition()`** 메서드는 장치의 위치가 바뀔 때마다 자동으로 호출할 처리기 함수를 등록할 때 사용합니다. 선택적으로 오류 발생 시 사용할 콜백 함수도 지정할 수 있습니다.

구문
--

```js
navigator.geolocation.watchPosition(success[, error[, options]])
```

### 매개변수

`success`

`GeolocationPosition` 객체를 입력 매개변수로 사용하는 콜백 함수.

`error` Optional

`GeolocationPositionError` (en-US) 객체를 입력 매개변수로 사용하는 콜백 함수.

`options` Optional

위치 감지 설정 옵션을 제공하는 `PositionOptions` 객체.

### 반환 값

등록한 처리기를 식별할 때 사용하는 정수 ID. ID를 `Geolocation.clearWatch()`에 전달해 등록을 해제할 수 있습니다.

예제
--

```js
var id, target, options;

function success(pos) {
  var crd = pos.coords;

  if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
    console.log("Congratulations, you reached the target");
    navigator.geolocation.clearWatch(id);
  }
}

function error(err) {
  console.warn("ERROR(" + err.code + "): " + err.message);
}

target = {
  latitude: 0,
  longitude: 0,
};

options = {
  enableHighAccuracy: false,
  timeout: 5000,
  maximumAge: 0,
};

id = navigator.geolocation.watchPosition(success, error, options);
```

명세
--

| Specification |
| --- |
| Geolocation API # watchposition-method |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* 메서드가 속한 `Geolocation` 인터페이스와, 접근하는 방법인 `NavigatorGeolocation.geolocation` (en-US).
* 처리기를 해제하는 `Geolocation.clearWatch()` 메서드
* 비슷한 `Geolocation.getCurrentPosition()` 메서드


Geolocation.getCurrentPosition() - Web API

Geolocation.getCurrentPosition()
================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**`Geolocation.getCurrentPosition()`** 메서드는 장치의 현재 위치를 가져옵니다.

구문
--

```js
navigator.geolocation.getCurrentPosition(success, error, [options]);
```

### 매개변수

`success`

`GeolocationPosition` 객체를 유일한 매개변수로 받는 콜백 함수입니다.

`error` Optional

`GeolocationPositionError` (en-US) 객체를 유일한 매개변수로 받는 콜백 함수입니다.

`options` Optional

다음을 포함하는 객체입니다.

`maximumAge`

캐시에 저장한 위치정보를 대신 반환할 수 있는 최대 시간을 나타내는 양의 `long` 값입니다. `0`을 지정한 경우 장치가 위치정보 캐시를 사용할 수 없으며 반드시 실시간으로 위치를 알아내려 시도해야 한다는 뜻입니다. `Infinity`를 지정한 경우 지난 시간에 상관없이 항상 캐시에 저장된 위치정보를 반환해야 함을 나타냅니다. 기본 값은 0입니다.

`timeout`

기기가 위치를 반환할 때 소모할 수 있는 최대 시간(밀리초)을 나타내는 양의 `long` 값입니다. 기본 값은 `Infinity`로, 위치를 알아내기 전에는 `getCurrentPosition()`이 반환하지 않을 것임을 나타냅니다.

`enableHighAccuracy`

위치정보를 가장 높은 정확도로 수신하고 싶음을 나타내는 불리언 값입니다. `true`를 지정했으면, 지원하는 경우 장치가 더 정확한 위치를 제공합니다. 그러나 응답 속도가 느려지며 전력 소모량이 증가하는 점에 주의해야 합니다. 반면 `false`를 지정한 경우 기기가 더 빠르게 반응하고 전력 소모도 줄일 수 있는 대신 정확도가 떨어집니다. 기본 값은 `false`입니다.

예제
--

```js
var options = {
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0,
};

function success(pos) {
  var crd = pos.coords;

  console.log("Your current position is:");
  console.log(`Latitude : ${crd.latitude}`);
  console.log(`Longitude: ${crd.longitude}`);
  console.log(`More or less ${crd.accuracy} meters.`);
}

function error(err) {
  console.warn(`ERROR(${err.code}): ${err.message}`);
}

navigator.geolocation.getCurrentPosition(success, error, options);
```

명세
--

| Specification |
| --- |
| Geolocation API # getcurrentposition-method |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* Geolocation API 사용하기
* `Navigator.geolocation`


CustomEvent() - Web API

CustomEvent()
=============

**`CustomEvent()`** 생성자는 새로운 `CustomEvent`를 생성합니다.

구문
--

```js
CustomEvent(typeArg);
CustomEvent(typeArg, options);
```

### 매개변수

`typeArg`

이벤트의 이름을 나타내는 문자열입니다.

`options` Optional

다음 속성을 포함하는 객체입니다.

* `"detail"`: 이 이벤트 내에 포함할, 이벤트의 세부 정보를 나타내는 값입니다. 기본 값은 `null`입니다. 처리기에서 `CustomEvent.detail` 속성을 통해 이 값에 접근할 수 있습니다.
* `Event()` 생성자의 옵션에 지정할 수 있는 모든 속성.

예제
--

```js
// CustomEvent 생성
const catFound = new CustomEvent("animalfound", {
  detail: {
    name: "cat",
  },
});
const dogFound = new CustomEvent("animalfound", {
  detail: {
    name: "dog",
  },
});

// 적합한 이벤트 수신기 부착
obj.addEventListener("animalfound", (e) => console.log(e.detail.name));

// 이벤트 발송
obj.dispatchEvent(catFound);
obj.dispatchEvent(dogFound);

// 콘솔에 "cat"과 "dog"가 기록됨
```

이벤트 생성 및 발동 문서에서 더 많은 예제를 확인하세요.

명세서
---

| Specification |
| --- |
| DOM Standard # ref-for-dom-customevent-customevent |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

함께 보기
-----

* `CustomEvent`
* 이벤트 생성 및 발동


CustomEvent.detail - Web API

CustomEvent.detail
==================

`CustomEvent` 인터페이스의 **`detail`** 읽기 전용 속성은 이벤트를 초기화할 때 제공한 데이터를 반환합니다.

값
-

이벤트를 초기화할 때 제공한 데이터입니다.

예제
--

```js
// CustomEvent 생성
const catFound = new CustomEvent("animalfound", {
  detail: {
    name: "cat",
  },
});
const dogFound = new CustomEvent("animalfound", {
  detail: {
    name: "dog",
  },
});

// 적합한 이벤트 수신기 부착
obj.addEventListener("animalfound", (e) => console.log(e.detail.name));

// 이벤트 발송
obj.dispatchEvent(catFound);
obj.dispatchEvent(dogFound);

// 콘솔에 "cat"과 "dog"가 기록됨
```

명세서
---

| Specification |
| --- |
| DOM Standard # ref-for-dom-customevent-detail② |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `CustomEvent`


FileReader.readAsText() - Web API

FileReader.readAsText()
=======================

**`readAsText()`** 메서드는 지정된 `Blob` 이나 `File` 의 컨텐츠를 읽기 위해 사용합니다. 읽기 연산이 끝나면, `readyState` (en-US) 가 `DONE`으로 바뀌고, `loadend (en-US)` 이벤트가 트리거 되고, `result` 프로퍼티는 파일의 내용을 텍스트 문자열로 가집니다.

**참고:** `Blob.text()` (en-US) 메서드는 파일을 텍스트로 읽는 프로미스 기반의 새 API 입니다.

문법
--

```js
instanceOfFileReader.readAsText(blob[, encoding]);
```

### 매개변수

`blob`

읽어 들일 `Blob` 이나 `File`

`encoding` Optional

반환 데이터에 사용될 문자열 인코딩을 지정. 매개변수가 지정되지 않으면 기본적으로 UTF-8이라고 가정합니다.

명세
--

| Specification |
| --- |
| File API # readAsDataText |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

함께 보기
-----

* `FileReader`


FileReader.result - Web API

FileReader.result
=================

파일의 내용을 반환한다. 이 속성은 읽기가 완료 된 후에만 사용 가능 하며 데이터의 형식은 읽기 작업에 어떤 함수가 사용되었는가에 의해 정해진다.

Syntax
------

```js
var file = instanceOfFileReader.result;
```

Value
-----

읽기 작업에 사용된 함수에 의해 string 또는 `ArrayBuffer` 가 된다.

명세서
---

| Specification |
| --- |
| File API # dom-filereader-result |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `FileReader`