Web Audio API 사용하기 - Web API

Web Audio API 사용하기
==================

Web Audio API 시작하기를 한번 봐 봅시다. 우리는 몇 가지 개념들을 간략하게 살펴보고, 그 다음에 오디오 트랙의 로드, 재생, 정지하고 볼륨 및 스테레오 패닝을 변경할 수 있는 간단한 카세트 플레이어 예제를 공부할 것입니다.

`<canvas>`가 `<img>` 요소와 나란히 공존하는 것처럼, Web Audio API는 `<audio>` (en-US) 미디어 요소를 대체하기보다는 보완합니다. 사용하는 경우에 따라 오디오를 구현하기 위해 무슨 도구를 사용해야 할 지가 결정될 것입니다. 단순히 오디오 트랙의 재생을 제어하려는 경우 `<audio>` 미디어 요소는 Web Audio API보다 더 빠르고 나은 해결책을 제공합니다. 더욱 복잡한 오디오 프로세싱을 수행하기를 원한다면, Web Audio API가 더욱 강한 기능과 제어를 제공합니다.

Web Audio API의 강력한 기능은 이것이 엄격한 "사운드 호출 제한(sound call limitation)"을 가지고 있지 않다는 것입니다. 예를 들어, 한 번에 32 또는 64개의 사운드 호출 제한이 없습니다. 일부 프로세서는 버벅거림 없이 1,000개 이상의 사운드를 동시에 재생할 수 있을 지도 모릅니다.

예제 코드
-----

우리의 카세트 플레이어는 다음과 같이 생겼습니다:

![재생, 패닝, 그리고 볼륨 제어가 있는 카세트 플레이어](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/boombox.png)

플레이 재생 버튼과, 음량과 스테레오 패닝을 조절할 수 있는 볼륨 그리고 패닝 슬라이더가 있는 복고풍의 카세트 덱에 주목해 주세요. 우리는 이 라디오를 더욱 복잡하게 만들 수도 있지만, 이 단계에서의 간단한 학습을 위해서는 이것은 이상적입니다.

Codepen에서 최종 데모를 확인하거나, GitHub에서 소스 코드를 확인하실 수 있습니다.

브라우저 지원
-------

현대의 브라우저들은 Web Audio API의 대부분의 기능들에 대해 좋은 지원을 가지고 있습니다. 이 API에는 많은 기능들이 있으므로, 더욱 정확한 정보를 위해서는 각 참조 페이지 하단의 브라우저 호환성 목록을 확인해야 할 것입니다.

오디오 그래프
-------

Web Audio API 내부의 모든 것은 오디오 그래프의 개념에 근거하고 있는데, 이는 노드로 구성되어 있습니다.

Web Audio API는 **오디오 컨텍스트**내에서 오디오 작업을 다루고, **모듈러 라우팅**(modular routing)을 허용하도록 설계되었습니다. 기본적인 오디오 작업은 **오디오 노드**와 함께 수행되는데, 이는 **오디오 라우팅 그래프**를 형성하기 위해 함께 연결되어 있습니다. 여러분은 조작할 사운드 소스인 입력 노드, 사운드를 원하는 대로 변경하는 수정 노드, 사운드를 저장하거나 듣게 허용하는 출력 노드(도착지)를 가지고 있습니다.

다른 채널 레이아웃을 가진 몇몇 오디오 소스들은 심지어 단일 컨텍스트 내에서도 지원됩니다. 이 모듈러 디자인 때문에, 여러분은 역동적인 효과를 가진 복잡한 오디오 기능을 생성할 수 있습니다.

오디오 컨텍스트
--------

Web Audio API로 무엇인가를 하기 위해서는 오디오 컨텍스트의 인스턴스를 생성할 필요가 있습니다. 이는 우리에게 API의 모든 기능에 접근할 수 있게 합니다.

```js
// 레거시 브라우저를 위해
const AudioContext = window.AudioContext || window.webkitAudioContext;

const audioContext = new AudioContext();
```

우리가 이것을 할 때 무슨 일이 벌어질까요? `BaseAudioContext`가 자동적으로 생성되고 온라인 오디오 컨텍스트로 확장됩니다. 우리는 라이브 사운드를 재생하기 때문에 이것을 원할 것입니다.

**참고:** 예를 들어, 만약 여러분이 단지 오디오 데이터를 처리하고, 그것을 버퍼링하고 스트리밍 하지만 재생하기를 원하지 않는다면, 여러분은 `OfflineAudioContext`를 만드는 방법을 살펴볼 수 있습니다.

사운드 로딩하기
--------

이제, 생성된 오디오 컨텍스트는 재생할 사운드가 필요합니다. API를 사용해 사운드를 재생하는 몇 가지 방법이 있습니다. 간단한 방법으로 시작해 봅시다 — 우리는 카세트 플레이어를 가지고 있으므로, 노래의 전체 트랙을 재생하기를 원할 것입니다. 또한, 접근성을 위해서, 트랙을 DOM에 노출시키는 것은 좋습니다. 우리는 `<audio>` (en-US) 요소를 사용하여 페이지에 노래를 노출시킬 것입니다.

```html
<audio src="myCoolTrack.mp3"></audio>
```

**참고:** 만약 로딩한 사운드 파일이 다른 도메인에 있다면 `crossorigin` 특성을 사용할 필요가 있습니다; 더 많은 정보를 보시려면 Cross Origin Resource Sharing (CORS)를 참고해 보세요.

Web Audio API로 얻을 수 있는 모든 것을 사용하려면 이 요소에서 소스를 가져와 우리가 만든 컨텍스트에 연결해야 합니다. 운 좋게도 이걸 할 수 있는 메서드가 있습니다 — `AudioContext.createMediaElementSource` (en-US)입니다:

```js
// 오디오 요소를 얻습니다
const audioElement = document.querySelector("audio");

// 오디오 요소를 오디오 컨텍스트에 전달합니다
const track = audioContext.createMediaElementSource(audioElement);
```

**참고:**

위의 `<audio>` 요소는 `HTMLMediaElement` 유형의 객체에 의해 DOM에 표현되는데, 이는 고유한 기능을 가지고 있습니다.

이것의 모든 것은 그대로 남아 있습니다. Web Audio API에서 사운드를 사용할 수 있도록 허용할 뿐입니다.

사운드 제어하기
--------

웹에서 사운드가 재생될 때, 사용자가 사운드를 제어할 수 있게 허용하는 것은 중요합니다. 사용하는 경우에 따라, 무수한 옵션이 있지만, 우리는 사운드를 재생/정지하고, 트랙의 볼륨을 변경하고, 그리고 좌에서 우로 이동하는 기능을 제공할 것입니다.

JavaScript 코드에서 프로그래밍적으로 사운드를 제어하는 것은 브라우저의 자동 재생 지원 정책에 의해 다뤄지는데, 그러한 것은 허가가 사용자 (또는 화이트리스트)에 의해 승인되지 않은 채로 차단될 가능성이 있습니다. 자동 재생 정책은 보통 스크립트가 오디오를 재생하게 할 수 있게 하기 전에 페이지에서 명시적인 허가나 사용자의 허락 중 하나를 요구합니다.

이 특별한 요구는 필수적으로 가동 중인데 그 이유는 예기치 않은 사운드는 짜증나고 거슬릴 수 있고, 접근성 문제를 유발할 수 있기 때문입니다. 미디어와 Web Audio API에 대한 자동 재생 가이드 문서에서 이것에 대해 더 배울 수 있습니다.

우리의 스크립트는 사용자의 입력 이벤트 (예를 들자면 재생 버튼의 클릭)에 응답하여 오디오를 재생하므로, 우리는 이상이 없고 자동 재생 방지로부터 문제가 없어야만 합니다. 그러므로, 우리의 재생과 정지 기능을 살펴봄으로써 시작해 봅시다. 우리는 트랙이 재생중일 때 정지 버튼으로 바뀌는 재생 버튼을 가지고 있습니다:

```html
<button data-playing="false" role="switch" aria-checked="false">
  <span>Play/Pause</span>
</button>
```

트랙을 재생하기 전에 우리는 오디오 소스/입력 노드에서 목적지로 오디오 그래프를 연결할 필요가 있습니다.

우리는 이미 오디오 요소를 API에 전달함으로써 입력 노드를 생성했습니다. 대개 여러분은 출력 노드를 생성할 필요가 없습니다, 여러분은 단지 다른 노드를, 여러분을 위해 상황을 다루는 `BaseAudioContext.destination` (en-US)에 연결할 수 있습니다.

```js
track.connect(audioContext.destination);
```

이 노드들을 시각화하는 좋은 방법은 오디오 그래프를 그리는 것입니다. 이것이 현재 우리의 오디오 그래프입니다:

![기본 목적지에 연결된 오디오 요소 소스를 가진 오디오 그래프](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graph1.jpg)

이제 우리는 재생과 정지 기능을 추가할 수 있습니다.

```js
// 재생 버튼을 선택합니다
const playButton = document.querySelector("button");

playButton.addEventListener(
  "click",
  function () {
    // 컨텍스트가 연기된(suspended) 상태에 있는지 검사합니다 (자동 재생 정책)
    if (audioContext.state === "suspended") {
      audioContext.resume();
    }

    // 상태에 따라 트랙을 재생하거나 정지합니다
    if (this.dataset.playing === "false") {
      audioElement.play();
      this.dataset.playing = "true";
    } else if (this.dataset.playing === "true") {
      audioElement.pause();
      this.dataset.playing = "false";
    }
  },
  false,
);
```

우리는 또한 트랙이 재생을 마쳤을 때 무엇을 할 지를 고려할 필요가 있습니다. `HTMLMediaElement`는 재생이 완료되었을 때 `ended` 이벤트를 한 번 발생시키므로, 우리는 그에 맞춰 코드를 실행시킬 수 있습니다.

```js
audioElement.addEventListener(
  "ended",
  () => {
    playButton.dataset.playing = "false";
  },
  false,
);
```

사운드 수정하기
--------

우리가 가지고 있는 사운드를 변경하기 위해, 몇몇 기본적인 수정 노드에 대해 공부해 봅시다. 여기가 Web Audio API가 정말 쓸모있어지기 시작하는 곳입니다. 우선, 볼륨을 변경해 봅시다. 이것은 `GainNode` (en-US)를 사용하여 구현될 수 있는데, 이는 음파가 얼마나 큰지를 표현합니다.

Web Audio API로 노드를 생성하는 두 가지 방법이 있습니다. 컨텍스트 그 자체에 있는 팩토리 메서드를 사용하거나 (예: `audioContext.createGain()`) 노드의 생성자(constructor)를 사용할 수 있습니다 (예: `new GainNode()`). 우리는 팩토리 메서드를 사용할 것입니다.

```js
const gainNode = audioContext.createGain();
```

이제 우리는 입력이 gain에 연결되고, 그리고 gain 노드가 목적지에 연결되도록 이전으로부터 우리의 오디오 그래프를 업데이트해야 합니다:

```js
track.connect(gainNode).connect(audioContext.destination);
```

이는 우리의 오디오 그래프를 다음과 같이 보이게 만들 것입니다:

![오디오 소스를 수정하는 gain 노드가 연결되고, 그리고 나서 기본 목적지로 가는 오디오 요소 소스를 가진 오디오 그래프](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graph2.jpg)

gain의 기본값은 1입니다; 이것은 현재 볼륨을 같게 유지할 것입니다. gain은 약 -3.4의 최소값과 약 3.4의 최대값으로 설정될 수 있습니다. 여기서 우리는 카세트 플레이어가 gain을 2까지 (기존 볼륨의 2배) 그리고 0까지 (이것은 효과적으로 사운드를 무음으로 만들 것입니다) 움직일 수 있게 허용할 것입니다.

사용자에게 이것을 하기 위한 제어 방법을 제공합시다 — 우리는 범위 입력 (en-US)을 사용할 것입니다:

```html
<input type="range" id="volume" min="0" max="2" value="1" step="0.01" />
```

**참고:**
 범위 입력은 오디오 노드의 값을 갱신하는 데 있어 정말로 유용한 입력 유형입니다.
 여러분은 범위의 값을 명시할 수 있고 오디오 노드의 파라미터와 함께 이것을 바로 사용할 수 있습니다.

그러므로 이 입력값을 취하고 입력 노드가 유저에 의해 바뀐 값을 가지고 있을 때 gain 값을 업데이트해 봅시다:

```js
const volumeControl = document.querySelector("#volume");

volumeControl.addEventListener(
  "input",
  function () {
    gainNode.gain.value = this.value;
  },
  false,
);
```

**참고:**
 노드 객체의 값 (예: `GainNode.gain`)은 단순한 값이 아닙니다.
 이 값은 실제로는 `AudioParam` 유형의 객체입니다 — 이것은 파라미터라고 불립니다.
 이것이 바로 `gain`에 직접 값을 설정하기보다, `GainNode.gain`의 `value` 속성을 설정해야 하는 이유입니다.
 이렇게 함은 이 객체가 좀 더 유연해 질 수 있게 하는데, 예를 들자면 파라미터에 주어진 시간 사이에서 변화시킬 값들의 집합을 전달하는 것을 고려해 볼 수 있겠습니다.

좋습니다, 이제 사용자는 트랙의 볼륨을 업데이트할 수 있습니다! gain 노드는 만약 여러분이 무음 기능을 추가하기를 원한다면 사용하기에 완벽한 노드입니다.

스테레오 패닝을 추가하기
-------------

우리가 방금 배운 것을 연습하기 위해 또 다른 수정 노드를 추가해 봅시다.

`StereoPannerNode` (en-US) 노드가 있는데, 이는 만약 사용자가 스테레오를 사용할 수 있다면, 좌측과 우측 스피커 사이의 소리의 균형을 변화시킵니다.

**참고:**

`StereoPannerNode`는 여러분이 단지 좌에서 우로 스테레오 패닝을 원하는 단순한 상황을 위한 것입니다.
 `PannerNode` (en-US)도 있는데, 이는 더욱 복잡한 효과를 생성하기 위해, 3D 공간, 즉 사운드 *공간화* 제어의 훌륭한 방법을 고려합니다.
 예를 들자면 이것은 머리 위를 나는 새나 사용자의 뒤로부터 오는 소리를 생성하기 위해 게임과 3D 앱에서 사용됩니다.

이것을 시각화하기 위해서, 우리는 우리의 오디오 그래프를 다음과 같이 보이게 만들 것입니다.

![입력 노드, 두 개의 수정 노드 (gain 노드와 stereo panner 노드) 그리고 도착지 노드를 보여주는 오디오 그래프를 보여주는 이미지](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/graphpan.jpg)

이번엔 노드 생성의 생성자 메서드를 사용해 봅시다. 이 방법으로 할 때에는, 이 특정한 노드가 취할지도 모르는 어떠한 옵션들과 컨텍스트를 전달해야만 합니다.

```js
const pannerOptions = { pan: 0 };
const panner = new StereoPannerNode(audioContext, pannerOptions);
```

**참고:**

노드 생성의 생성자 메서드는 이 시점에서 모든 브라우저에서 지원되지 않습니다. 오래된 팩토리 메서드는 더욱 널리 지원됩니다.

여기서 우리의 값은 범위가 -1 (극좌)에서 1 (극우)까지입니다. 이 파라미터를 달라지게 하기 위해 다시 범위 유형 입력을 사용합시다:

```html
<input type="range" id="panner" min="-1" max="1" value="0" step="0.01" />
```

패너의 값을 조정하기 위해 전에 했던 것과 같은 방법으로 우리는 이 입력으로부터의 값을 사용합니다:

```js
const pannerControl = document.querySelector("#panner");

pannerControl.addEventListener(
  "input",
  function () {
    panner.pan.value = this.value;
  },
  false,
);
```

모든 노드를 같이 연결하기 위해, 우리의 오디오 그래프를 다시 조정해 봅시다:

```js
track.connect(gainNode).connect(panner).connect(audioContext.destination);
```

유일하게 남은 것은 테스트해보는 것입니다. Codepen에서 최종 데모를 확인해 보세요.

요약
--

훌륭합니다! 우리는 우리의 '테이프'를 재생하는 카세트 플레이어를 가지고 있고, 볼륨과 스테레오 패닝을 조정할 수 있는데, 이는 우리에게 작동하는 상당히 기본적인 오디오 그래프를 제공합니다.

이것은 여러분의 웹사이트나 웹 앱에 오디오를 추가하기 위해 필요할 상당수의 기본을 구성합니다. Web Audio API에는 더 많은 기능이 있지만, 여러분이 한 번 노드의 개념을 잡고 오디오 그래프를 함께 합하면, 우리는 더욱 복잡한 기능을 보는 것으로 이동할 수 있습니다.

더 많은 예제
-------

Web Audio API에 대해 더 배우기 위해 이용해볼 수 있는 다른 예제들이 있습니다.

Voice-change-O-matic는 다른 효과와 시각화를 고를 수 있게 허용하는 재미있는 목소리 변조기와 소리 시각화 웹 앱입니다. 이 애플리케이션은 상당히 기본적이지만, 다수의 Web Audio API 기능의 동시 사용을 보여줍니다. (Voice-change-O-matic를 실행해 보세요).

![음파와, 목소리 효과와 시각화를 선택하기 위한 옵션이 보여지고 있는 UI](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/voice-change-o-matic.png)

Web Audio API를 보여주기 위해 명확하게 개발된 또 다른 애플리케이션은 Violent Theremin인데, 이는 마우스 포인터를 움직임으로써 음 높이(pitch)와 볼륨을 변경하는 것을 허용하는 간단한 웹 애플리케이션입니다. 이것은 또한 사이키델릭한 라이트 쇼를 제공합니다 (Violent Theremin 소스 코드를 확인해 보세요).

![화면 지우기와 음소거라고 써진 두 개의 버튼을 가진, 무지개 색으로 가득 찬 페이지 ](/ko/docs/Web/API/Web_Audio_API/Using_Web_Audio_API/violent-theremin.png)

또한 더 많은 예시를 보시려면 webaudio-examples repo를 참고해 보세요.


AudioWorklet을 사용하는 백그라운드 오디오 프로세싱 - Web API

AudioWorklet을 사용하는 백그라운드 오디오 프로세싱
=================================

Web Audio API가 처음 브라우저에 소개되었을 때, 이것은 실시간으로 오디오를 조작하는 사용자 정의 오디오 프로세서를 생성하기 위해 JavaScript 코드를 사용하고 있었습니다. `ScriptProcessorNode`의 문제점은 단순했습니다: 이것은 메인 스레드에서 실행되기 때문에 실행을 마치기 전까지 다른 모든 동작을 막았습니다. 이러한 방식은 이상적이지 않았습니다. 특히 오디오 프로세싱같이 계산적으로 비용이 많이 들 수 있는 무언가에 대해서 말입니다.

`AudioWorklet`이 도입되었습니다. 오디오 컨텍스트의 오디오 worklet은 메인 스레드에서 떨어져 실행되는 `Worklet` (en-US)인데, 이는 컨텍스트의 `audioWorklet.addModule()` (en-US) 메서드를 호출함으로써 이 worklet에 추가된 오디오 프로세싱 코드를 실행합니다. `addModule()`을 호출하면 명시된 JavaScript 파일을 로드하는데, 이 파일은 오디오 프로세서의 구현을 포함하고 있어야 합니다. 프로세서가 등록되었다면 여러분은 새로운 `AudioWorkletNode`를 생성할 수 있고, 이 노드가 다른 오디오 노드들과 함게 오디오 노드의 체인에 연결되었을 때 이 노드는 프로세서의 코드를 통해 오디오를 전달합니다.

JavaScript를 사용해 오디오 프로세서를 생성하고, 오디오 worklet 프로세서로서 그것을 등록하고, 그리고 나서 Web Audio 애플리케이션 내에서 그 프로세서를 사용하는 과정이 이 글의 주제입니다.

오디오 프로세싱이 종종 상당한 계산량을 수반하기 때문에, 여러분의 프로세서가 WebAssembly를 사용하여 개발된다면 큰 이점을 취할 수도 있을 것입니다. 그리고 이렇게 하는 것은 웹 앱에 네이티브에 가깝거나 완전히 네이티브인 성능을 가져다 준다는 것에 주목할 가치가 있습니다. WebAssembly를 사용하여 오디오 프로세싱 알고리즘을 구현하면 오디오 프로세싱이 대단히 뛰어나게 수행될 수 있게 만들 수 있습니다.

멀리서 바라본 개요
----------

단계별 기초를 밟아가며 AudioWorklet의 사용법을 알아보기 전에, 여기에 무엇이 관련되어 있는지 간략한 고수준의 개요를 살펴보며 시작합시다.

1. 하나 이상의 들어오는 소스에서 오디오를 취하고, 데이터에 대해 작업을 수행하고, 결과로 오디오 데이터를 출력하는 `AudioWorkletProcessor`를 상속받는 오디오 worklet 프로세서 클래스를 정의하는 모듈을 만듭니다.
2. 오디오 컨텍스트의 `audioWorklet` (en-US) 속성을 통해 오디오 컨텍스트의 `AudioWorklet`에 접근하고, 오디오 worklet의 `addModule()` (en-US) 메서드를 호출하여 오디오 worklet 프로세서 모듈을 설치합니다.
3. 필요한 대로, (모듈에 의해 정의된) 프로세서의 이름을 `AudioWorkletNode()` 생성자에 전달함으로써 오디오 프로세싱 노드를 생성합니다.
4. `AudioWorkletNode`가 필요로 하는, 또는 여러분이 설정하기를 원하는 오디오 파라미터들을 설정합니다. 이 파라미터들은 오디오 worklet 프로세서 모듈 내에 정의되어 있습니다.
5. 다른 노드처럼, 생성된 `AudioWorkletNode`를 오디오 프로세싱 파이프라인에 연결하고, 오디오 파이프라인을 평상시처럼 사용합니다.

이 글의 마지막까지 우리는 이 과정들을 예제와 함께 더욱 자세하게 살펴볼 것입니다 (직접 시도해볼 수 있는 작동하는 예제를 포함합니다).

이 페이지에서 찾을 수 있는 예제 코드는 MDN의 Web Audio 예제의 GitHub 레포지토리의 일부인 이 예제에서 왔습니다. 이 예제는 오실레이터 노드를 생성하고 결과로 발생된 사운드를 재생하기 전에 `AudioWorkletNode`를 사용하여 이 소리에 백색소음을 추가합니다. 또한, 슬라이더를 사용해 오실레이터와 오디오 worklet의 출력 gain을 제어할 수 있습니다.

**코드 보기**

**직접 시도해보기**

오디오 worklet 프로세서 생성하기
---------------------

기본적으로, 오디오 worklet 프로세서는 JavaScript 모듈을 사용하여 구현되고, 이 JavaScript 모듈은 사용자 정의 오디오 프로세서 클래스를 정의하고 등록합니다 (참고로, 우리는 이 글에서 오디오 worklet 프로세서를 "오디오 프로세서" 또는 "프로세서"로 나타낼 것입니다. 왜냐하면 그렇지 않을 경우 이 글은 두 배 가량 길어질 것이기 때문입니다).

### 오디오 worklet 프로세서의 구조

오디오 worklet 프로세서는 다음을 포함하는 JavaScript 모듈입니다:

* 오디오 프로세서를 정의하는 JavaScript 클래스. 이 클래스는 `AudioWorkletProcessor` 클래스를 상속받습니다(extends).
* 오디오 프로세서는 반드시 `process()` 메서드를 구현해야 하는데, 이 메서드는 들어오는 오디오 데이터를 받고 프로세서에 의해 조작된 데이터를 출력에 넣습니다.
* 이 모듈은 `registerProcessor()`를 호출함으로써 새로운 오디오 worklet 프로세서를 등록하는데, 등록할 때 오디오 프로세서의 이름과 프로세서를 정의하는 클래스를 인자로 전달합니다.

한 개의 오디오 worklet 프로세서 모듈이 다수의 프로세서 클래스를 정의하고 각 클래스에 대해 `registerProcessor()`를 호출해 클래스들을 등록할 수도 있을 것입니다. 각 클래스가 고유한 이름을 가지고 있는 한, 이것은 문제 없는 방법입니다. 이 방법은 또한 다수의 모듈을 네트워크나 심지어 사용자의 로컬 디스크로부터 로딩하는 것보다 더욱 효율적입니다.

### 기본 코드 프레임워크

오디오 프로세서 클래스의 가장 기본적인 프레임워크는 다음과 같습니다:

```js
class MyAudioProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
  }

  process(inputList, outputList, parameters) {
    /\* inputs를 사용하여 (혹은 필요하지 않다면 사용하지 않을 수도 있음),
 각 outputs의 내부에 있는 출력을 작성합니다 \*/

    return true;
  }
}

registerProcessor("my-audio-processor", MyAudioProcessor);
```

프로세서의 구현 이후에 전역 함수 `registerProcessor()`를 호출하는데, 이 함수는 오직 오디오 컨텍스트의 `AudioWorklet`의 스코프 내부에서만 사용 가능하고, 이 AudioWorklet은 여러분이 호출한 `audioWorklet.addModule()` (en-US)의 결과로써 호출자가 됩니다. 이 `registerProcessor()` 호출은 `AudioWorkletNode`들이 설정되었을 때, 생성된 모든 `AudioWorkletProcessor`에 대한 기초로써 여러분의 클래스를 등록합니다.

이것이 가장 기본적인 프레임워크이고 코드가 `process()`에 추가되어 입력과 출력을 가지고 무언가를 하기 전까지는 실제로는 아무런 효과도 없습니다. 그리고 이제 우리는 입력과 출력에 대해 살펴볼 준비가 되었습니다.

### 입력과 출력 리스트

입력과 출력 리스트가 어떻게 작동하는지 한 번 깨닫고 나면 이것들은 실제로 매우 단순하긴 하지만, 처음에는 조금 혼란스러울 수 있습니다.

안에서 시작해서 밖으로 나가는 전략을 취해 봅시다. 기본적으로, (예를 들자면 좌측 스피커나 서브우퍼같은) 하나의 오디오 채널에 대한 오디오는 `Float32Array`로 표현되는데 이것의 값은 개개의 오디오 샘플입니다. 명세에 따르면, `process()` 함수가 받는 각 오디오 블럭은 128개의 프레임을 포함하고 있지만 (즉, 각 채널에 대해 128개의 샘플), **이 값이 미래에 바뀔 것이라는 건** 예정되어 있고, 사실은 상황에 따라 다양할 수 있을 것이므로, 여러분은 특정한 크기를 추정하기보다는 **항상** 배열의 `length`를 확인해야 할 것입니다. 그러나, 입력과 출력이 같은 블럭 길이를 가질 것이라는 건 보장됩니다.

각각의 입력은 얼마간의 채널을 가지고 있습니다. 모노 입력은 하나의 채널을 가지고 있고, 스테레오 입력은 두 개의 채널을 가지고 있습니다. 서라운드 사운드는 여섯 개 혹은 그 이상의 채널을 가지고 있을 지도 모릅니다. 그래서 각 입력은, 결국엔, 채널의 배열입니다. 즉, `Float32Array` 객체의 배열입니다.

그렇다면, 다수의 입력이 있을 수 있으므로, `inputList`는 `Float32Array` 객체의 배열의 배열입니다. 각각의 입력은 각기 다른 수의 채널을 가지고 있을 지도 모르고, 각 채널은 채널마다의 샘플 배열을 가지고 있습니다.

따라서, 입력 리스트 `inputList`가 주어지면:

```js
const numberOfInputs = inputList.length;
const firstInput = inputList[0];

const firstInputChannelCount = firstInput.length;
const firstInputFirstChannel = firstInput[0]; // (또는 inputList[0][0])

const firstChannelByteCount = firstInputFirstChannel.length;
const firstByteOfFirstChannel = firstInputFirstChannel[0]; // (또는 inputList[0][0][0])
```

출력 리스트는 정확히 같은 방식으로 구성됩니다; 이것은 출력들의 배열인데, 이것의 각각은 채널들의 배열이고, 이것의 각각은 `Float32Array` 객체의 배열인데, 이는 그 채널에 대한 샘플을 포함합니다.

여러분이 어떻게 입력을 사용하고 어떻게 출력을 생성할 것인가는 여러분의 프로세서에 아주 달려 있습니다. 만약 여러분의 프로세서가 단지 생성기라면, 이것은 입력을 무시하고 단지 생성된 데이터로 출력의 내용을 대체할 수 있습니다. 또는 여러분은 각 입력의 각 채널의 들어오는 데이터에 알고리즘을 적용하고 그 결과를 해당하는 출력의 채널에 작성하며 (입력과 출력의 수는 다를지도 모르고, 채널은 또한 다를지도 모르는 이 입력과 출력에 기대고 있다는 것을 명심하십시오), 각각의 입력을 독립적으로 처리할 수 있습니다. 또는 여러분은 모든 입력을 취해서 믹싱이나 데이터로 채워진 하나의 출력 (또는 같은 데이터로 채워진 모든 출력) 을 생성하는 다른 계산을 수행할 수 있습니다.

이것은 전적으로 여러분에게 달려 있습니다. 이것은 오디오 프로그래밍 툴킷에서 매우 강력한 도구입니다.

### 다수의 입력 처리하기

해당하는 출력을 생성하기 위해 사용되는 각 입력을 가지고, 다수의 입력을 처리할 수 있는 `process()`의 구현을 살펴봅시다. 초과되는 입력은 무시됩니다.

```js
process(inputList, outputList, parameters) {
  const sourceLimit = Math.min(inputList.length, outputList.length);

  for (let inputNum = 0; inputNum < sourceLimit; inputNum++) {
    let input = inputList[inputNum];
    let output = outputList[inputNum];
    let channelCount = Math.min(input.length, output.length);

    for (let channelNum = 0; channelNum < channelCount; channelNum++) {
      let sampleCount = input[channelNum].length;

      for (let i = 0; i < sampleCount; i++) {
        let sample = input[channelNum][i];

        /\* 샘플 조작하기 \*/

        output[channelNum][i] = sample;
      }
    }
  };

  return true;
}
```

해당하는 출력에 보내고 처리할 소스의 수를 결정할 때, 우리는 `Math.min()`을 사용해 오직 출력 리스트에 있는 공간만큼의 채널만을 처리한다는 것을 확실히 합니다. 이것과 같은 확인은 현재 입력에서 얼마나 많은 채널을 처리할 지 결정할 때 수행됩니다; 우리는 오직 destination 출력에 있는 공간만큼만 처리합니다. 이렇게 함으로써 우리는 이 배열의 초과 때문에 발생하는 오류들을 방지할 수 있습니다.

### 입력 믹싱하기

많은 노드들은 입력들이 어떤 방법으로 하나의 출력으로 결합되는 **믹싱** 작업을 수행합니다. 이는 아래의 예제에서 시연됩니다.

```js
process(inputList, outputList, parameters) {
  const sourceLimit = Math.min(inputList.length, outputList.length);
   for (let inputNum = 0; inputNum < sourceLimit; inputNum++) {
     let input = inputList[inputNum];
     let output = outputList[0];
     let channelCount = Math.min(input.length, output.length);

     for (let channelNum = 0; channelNum < channelCount; channelNum++) {
       let sampleCount = input[channelNum].length;

       for (let i = 0; i < sampleCount; i++) {
         let sample = output[channelNum][i] + input[channelNum][i];

         if (sample > 1.0) {
           sample = 1.0;
         } else if (sample < -1.0) {
           sample = -1.0;
         }

         output[channelNum][i] = sample;
       }
     }
   };

  return true;
}
```

이것은 많은 점에서 이전 예제와 비슷한 코드이지만, 오직 첫번째 출력—`outputList[0]`—이 변경되었습니다. 값에 제한을 두어서 샘플들이 -1.0에서 1.0 사이의 올바른 범위를 초과하는 것을 방지하기 위해 작동하는 간단한 코드 조각과 함께, 각각의 샘플이 출력 버퍼의 해당하는 샘플에 추가되었습니다; 클리핑을 방지할 아마도 덜 왜곡될 경향이 있는 다른 방법들이 있겠지만, 이것은 아무것도 아닌 것 보다는 나은 간단한 예시를 보여줍니다.

오디오 worklet 프로세서의 생애 주기
-----------------------

여러분이 오디오 worklet 프로세서의 생애 주기에 영향을 줄 유일한 수단은 `process()`에 의해 반환된 값을 통해서인데, 이는 노드가 여전히 사용되고 있는지 아닌지에 대한 user agent의 의사 결정을 무시할지 말지를 나타내는 Boolean 값이어야만 합니다.

일반적으로, 모든 오디오 노드의 생애 정책은 단순합니다: 만약 노드가 여전히 활발히 오디오를 프로세싱하고 있는 것으로 여겨진다면, 노드는 계속 사용될 것입니다. `AudioWorkletNode`의 경우, 만약 이 노드의 `process()` 함수가 `true`를 반환하고 **또한** 이 노드가 오디오 데이터에 대해 소스로서 컨텐츠를 생성하거나 하나 이상의 입력으로부터 데이터를 받고 있다면 이 노드는 작동 중인 것으로 여겨집니다.

본질적으로 `process()` 함수로부터의 결과로써 `true`의 값을 명시한다는 것은 Web Audio API에게 심지어 API가 여러분을 위해 할 무언가가 남아있지 않다고 생각한다 할 지라도 여러분의 프로세서가 계속 호출될 필요가 있다고 말해주는 것입니다. 다른 말로 하자면, `true`는 API의 로직을 무시하고 여러분의 프로세서의 생애 주기 정책에 대한 제어를 제공합니다. 즉, API가 이 노드를 끄기로 결정했을 때일지라도 프로세서가 소유 중인 `AudioWorkletNode`의 실행을 유지합니다.

`process()` 메서드로부터 `false`를 반환하는 것은 API에게 보통의 로직을 따르고 만약 프로세서 노드를 종료하는 것이 적절하다고 여겨지면 그렇게 해야 한다고 말해주는 것입니다. 만약 API가 여러분의 노드가 더 이상 필요없다고 결정한다면, `process()`는 다시 호출되지 않을 것입니다.

**참고:** 이 시점에서, 불행하게도, Chrome은 어떤 의미로는 현재 표준에 일치하는 이 알고리즘을 구현하지 않았습니다. 대신, Chrome은 만약 여러분이 `true` 를 반환하면 노드를 살려두고 `false` 를 반환하면 끕니다. 따라서 호환성의 이유로 적어도 Chrome에서는 여러분은 항상 반드시 `process()` 에서 `true` 를 반환해야 합니다. 그러나, 이 Chrome 이슈가 수정되고 나면, 이것은 성능에 약간 부정적인 영향을 가질지도 모르므로 여러분은 만약 가능하다면 이 동작을 변경하고자 할 것입니다.

오디오 프로세서 worklet 노드 생성하기
------------------------

`AudioWorkletProcessor`를 통해 오디오 데이터 블럭을 채우는 오디오 노드를 생성하려면, 여러분은 다음의 간단한 단계를 따를 필요가 있습니다:

1. 오디오 프로세서 모듈을 로드하고 설치하기
2. 프로세서의 이름으로 사용할 오디오 프로세서 모듈을 명시하며, `AudioWorkletNode` 생성하기
3. `AudioWorkletNode`에 입력을 연결하고 이 노드의 출력을 적절한 destination에 연결하기 (이는 다른 노드거나 `AudioContext` 객체의 `destination` (en-US) 속성입니다).

오디오 worklet 프로세서를 사용하기 위해서, 여러분은 다음과 유사한 코드를 사용할 수 있습니다:

```js
let audioContext = null;

async function createMyAudioProcessor() {
  if (!audioContext) {
    try {
      audioContext = new AudioContext();
      await audioContext.resume();
      await audioContext.audioWorklet.addModule("module-url/module.js");
    } catch (e) {
      return null;
    }
  }

  return new AudioWorkletNode(audioContext, "processor-name");
}
```

이 `createMyAudioProcessor()` 함수는 여러분의 오디오 프로세서를 사용하기 위해 설정된 `AudioWorkletNode`의 새로운 인스턴스를 생성하고 반환합니다. 이 함수는 또한 만약 오디오 컨텍스트 생성이 이미 완료되지 않았다면 오디오 컨텍스트 생성을 다룹니다.

컨텍스트가 사용 가능함을 확실히 하기 위해서, 이 함수는 컨텍스트가 이미 사용 가능하지 않다면 컨텍스트를 생성함으로써 시작하고, 그리고 나서 프로세서를 포함하는 모듈을 worklet에 추가합니다. 이 작업이 완료되고 나면, 이 함수는 새로운 `AudioWorkletNode`를 인스턴스화하고 반환합니다. 반환된 노드를 얻었다면, 여러분은 이 노드를 다른 노드들에 연결할 수 있고 그게 아니라면 여타 다른 노드처럼 사용할 수 있습니다.

다음의 코드를 통해 여러분은 새로운 오디오 프로세서 노드를 생성할 수 있습니다:

```js
let newProcessorNode = createMyAudioProcessor();
```

만약 반환된 값 `newProcessorNode`가 `null`이 아니라면, 우리는 작동하고 사용될 준비가 된 백색소음 프로세서 노드와 함께 유효한 오디오 컨텍스트를 가지고 있는 것입니다.

오디오 파라미터 지원하기
-------------

다른 Web Audio 노드들처럼, `AudioWorkletNode`은 파라미터를 지원하는데, 이는 실제 작업을 하는 `AudioWorkletProcessor`와 공유됩니다.

### 프로세서에 파라미터 지원 추가하기

`AudioWorkletNode`에 파라미터를 추가하기 위해서, 여러분은 모듈 내의 `AudioWorkletProcessor` 기반의 프로세서 클래스 내에서 파라미터들을 정의할 필요가 있습니다. 이 작업은 static getter `parameterDescriptors`를 클래스에 추가함으로써 이루어질 수 있습니다. 이 함수는 `AudioParam` 객체들의 배열을 반환해야 하는데, 이 객체는 프로세서에 의해 지원되는 각 파라미터에 대해 하나씩입니다.

다음의 `parameterDescriptors()` 구현에서, 반환된 배열은 두 개의 `AudioParam` 객체를 가지고 있습니다. 첫번째 파라미터는 `gain`을 기본값이 0.5인 0과 1 사이의 값으로 정의했습니다. 두번째 파라미터의 이름은 `frequency`이고 기본값은 440.0인데, 그 범위는 전부 통틀어 27.5에서 4186.009까지입니다.

```js
static get parameterDescriptors() {
  return [
   {
      name: "gain",
      defaultValue: 0.5,
      minValue: 0,
      maxValue: 1
    },
    {
      name: "frequency",
      defaultValue: 440.0;
      minValue: 27.5,
      maxValue: 4186.009
    }
  ];
}
```

프로세서 노드의 파라미터들에 접근하는 것은 그것들을 `process()`의 구현에 전달된 `parameters` 객체에서 찾는 것만큼이나 간단합니다. `parameters` 객체 내부에 있는 것은 배열들인데, 각 파라미터 당 하나이고, 파라미터로서 같은 이름을 공유합니다.

A-rate 파라미터

a-rate 파라미터에 대해서—시간에 따라 값이 자동적으로 변하는 파라미터—`parameters` 객체에서 파라미터의 엔트리는 `AudioParam` 객체들의 배열인데, 처리되고 있는 블록의 각 프레임 당 하나입니다. 이 값들은 해당하는 프레임들에 적용될 것입니다.

K-rate 파라미터

반면에 K-rate 파라미터는, 블럭당 한 번만 변할 수 있어서, 파라미터의 배열은 오직 하나의 엔트리를 가집니다. 블럭의 모든 프레임에 대해 이 값을 사용하십시오.

아래의 코드에서, 우리는 a-rate 또는 k-rate 파라미터로 쓰일 수 있는 `gain` 파라미터를 다루는 `process()` 함수를 볼 수 있습니다. 우리의 노드는 오직 하나의 입력만을 지원하므로, 이 노드는 단지 리스트의 첫번째 입력만을 취하고, gain을 이 입력에 적용하고, 결과로 생성된 데이터를 첫번째 출력의 버퍼에 작성합니다.

```js
process(inputList, outputList, parameters) {
  const input = inputList[0];
  const output = outputList[0];
  const gain = parameters.gain;

  for (let channelNum = 0; channelNum < input.length; channel++) {
    const inputChannel = input[channel];
    const outputChannel = output[channel];

    // 만약 gain.length가 1이면, 이것은 k-rate 파라미터이므로,
    // 첫번째 엔트리를 모든 프레임에 적용합니다.
    // 그렇지 않으면, 각 엔트리를 해당하는 프레임에 적용합니다.

    if (gain.length === 1) {
      for (let i = 0; i < inputChannel.length; i++) {
        outputChannel[i] = inputChannel[i] \* gain[0];
      }
    } else {
      for (let i = 0; i < inputChannel.length; i++) {
        outputChannel[i] = inputChannel[i] \* gain[i];
      }
    }
  }

  return true;
}
```

여기서, 만약 `gain.length`가 `gain` 파라미터의 값들의 배열에 오직 하나의 값만이 있다는 것을 나타내면, 배열에 있는 첫번째 엔트리가 블럭의 모든 프레임에 적용됩니다. 그렇지 않으면, 블럭의 각 프레임에 대해, `gain[]`에 있는 해당하는 엔트리가 적용됩니다.

### 메인 스레드 스크립트에서 파라미터 접근하기

여러분의 메인 스레드 스크립트는 다른 노드들이 할 수 있는 것과 마찬가지로 파라미터에 접근할 수 있습니다. 이렇게 하기 위해서는, 첫째로 여러분은 `AudioWorkletNode`의 `parameters` 속성의 `get()` 메서드를 호출함으로써 파라미터에 대한 참조를 얻을 필요가 있습니다.

```js
let gainParam = myAudioWorkletNode.parameters.get("gain");
```

`gainParam`에 반환되고 저장된 값은 `gain` 파라미터를 저장하기 위해 사용된 `AudioParam`입니다. 여러분은 그리고 나서 `AudioParam` 메서드 `setValueAtTime()`을 사용하여 주어진 시간의 값을 효과적으로 바꿀 수 있습니다.

예를 들어, 여기서, 우리는 값을 `newValue`로, 효과적으로 즉시 설정했습니다.

```js
gainParam.setValueAtTime(newValue, audioContext.currentTime);
```

여러분은 유사하게 `AudioParam` 인터페이스에 있는 다른 모든 메서드를 시간에 따른 변화를 적용하기 위해, 예정된 변화를 취소하기 위해, 그리고 등등을 위해 사용할 수 있습니다.

파라미터의 값을 읽는 것은 `value` 속성을 보는 것 만큼이나 간단합니다:

```js
let currentGain = gainParam.value;
```

같이 보기
-----

* Web Audio API
* Enter Audio Worklet (Google Developers 블로그)


OffscreenCanvas.width - Web API

OffscreenCanvas.width
=====================

Baseline 2023
-------------

Newly availableSince March 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

**`width`** 프로퍼티는 `OffscreenCanvas` 객체에 할당된 너비를 반환합니다.

구문
--

```js
var pxl = offscreen.width;
offscreen.width = pxl;
```

예시
--

새로운 offscreen 캔버스를 생성하고 높이를 설정하거나 반환합니다.

```js
var offscreen = new OffscreenCanvas(256, 256);
offscreen.width; // 256
offscreen.width = 512;
```

명세서
---

| Specification |
| --- |
| HTML Standard # dom-offscreencanvas-width-dev |

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

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

더 보기
----

* `OffscreenCanvas`, 프로퍼티가 속한 인터페이스


OffscreenCanvas() - Web API

OffscreenCanvas()
=================

Baseline 2023
-------------

Newly availableSince March 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

**`OffscreenCanvas()`** 생성자는 새롭게 초기화된 `OffscreenCanvas` 객체를 반환합니다.

구문
--

```js
new OffscreenCanvas(width, height);
```

### 매개 변수

`width`

offscreen 캔버스의 너비

`height`

offscreen 캔버스의 높이

예시
--

이 예시는 `OffscreenCanvas()` 생성자를 이용해 새로운 offscreen 캔버스를 생성하고, WebGL 컨텍스트의 `getContext()` (en-US) 메소드를 사용해 초기화합니다.

```js
let offscreen = new OffscreenCanvas(256, 256);
let gl = offscreen.getContext("webgl");
```

명세서
---

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

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

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

더 보기
----

* `OffscreenCanvas`, 생성자가 속한 인터페이스


OffscreenCanvas.height - Web API

OffscreenCanvas.height
======================

Baseline 2023
-------------

Newly availableSince March 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

**`height`** 프로퍼티는 `OffscreenCanvas` 객체에 할당된 높이를 반환합니다.

구문
--

```js
var pxl = offscreen.height;
offscreen.height = pxl;
```

예시
--

새로운 offscreen 캔버스를 생성하고 높이를 설정하거나 반환합니다.

```js
var offscreen = new OffscreenCanvas(256, 256);
offscreen.height; // 256
offscreen.height = 512;
```

명세서
---

| Specification |
| --- |
| HTML Standard # dom-offscreencanvas-height-dev |

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

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

더 보기
----

* `OffscreenCanvas`, 프로퍼티가 속한 인터페이스


ServiceWorkerRegistration.active - Web API

ServiceWorkerRegistration.active
================================

**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

`ServiceWorkerRegistration` (en-US) 인터페이스의 **`active`** 속성은 `ServiceWorker.state` (en-US) 가 `activating` 또는 `activated` 상태인 서비스 워커를 반환한다. 이 속성은 `null` 로 초기 설정되어 있다.

클라이언트들의 URL이 등록 scope 내에 있을 경우 active 워커는 `ServiceWorkerClient` (en-US) 를 제어한다. (`ServiceWorkerContainer.register` (en-US) 가 처음으로 호출될 때 그 `scope` 옵션은 정의된다.)

**참고:** 이 기능은 Web Workers 에서 사용 가능하다.

Syntax
------

```js
sw = ServiceWorker.active;
```

### Value

현재 `activating` 또는 `activated` 상태에 있다면, `ServiceWorker` 객체.

명세서
---

| Specification |
| --- |
| Service Workers # navigator-service-worker-active |

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

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

See also
--------

* Using Service Workers (en-US)
* Service workers basic code example
* Is ServiceWorker ready?
* `Promise`
* Using web workers (en-US)


ServiceWorkerRegistration.installing - Web API

ServiceWorkerRegistration.installing
====================================

**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

`ServiceWorkerRegistration` (en-US) 인터페이스의 **`installing`** 속성은 `ServiceWorker.state` (en-US) 가 `installing` 인 서비스 워커를 반환한다. 이 속성은 `null` 로 초기 설정되어 있다.

**참고:** 이 기능은 Web Workers 에서 사용 가능하다.

Syntax
------

```js
sw = ServiceWorker.installing;
```

### Value

현재 `installing` 상태에 있다면, `ServiceWorker` 객체

명세서
---

| Specification |
| --- |
| Service Workers # navigator-service-worker-installing |

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

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

See also
--------

* Using Service Workers (en-US)
* Service workers basic code example
* Is ServiceWorker ready?
* `Promise`
* Using web workers (en-US)


ServiceWorkerRegistration.navigationPreload - Web API

ServiceWorkerRegistration.navigationPreload
===========================================

**실험적:** **이 기능은 실험적인 기능입니다.**
프로덕션 환경에서 사용하기 전에 브라우저 호환성 표를 주의 깊게 확인하세요.

`ServiceWorkerRegistration` (en-US) 인터페이스의 **`navigationPreload`** 읽기 전용 속성은 현재의 서비스 워커 등록와 관련된 `NavigationPreloadManager` (en-US) 를 반환한다.

Syntax
------

```js
var navigationPreloadManager = serviceWorkerRegistration.navigationPreload;
```

### Value

`NavigationPreloadManager` (en-US) 의 객체.

명세서
---

| Specification |
| --- |
| Service Workers # service-worker-registration-navigationpreload |

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

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


accessKeyLabel - Web API

accessKeyLabel
==============

**`HTMLElement.accessKeyLabel`** 읽기 전용 속성은 엘리먼트의 할당된 접근키를 나타내는 `String` 를 반환합니다. 존재하지 않는 경우 빈 문자열을 반환합니다.

구문
--

```js
label = element.accessKeyLabel;
```

예시
--

### JavaScript

```js
var node = document.getElementById("btn1");
if (node.accessKeyLabel) {
  node.title += " [" + node.accessKeyLabel + "]";
} else {
  node.title += " [" + node.accessKey + "]";
}

node.onclick = function () {
  var p = document.createElement("p");
  p.textContent = "Clicked!";
  node.parentNode.appendChild(p);
};
```

### HTML

```html
<button accesskey="h" title="Caption" id="btn1">Hover me</button>
```

### Result

명세
--

| Specification |
| --- |
| HTML Standard # dom-accesskeylabel |

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

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

함께 보기
-----

* `element.accessKey`
* accesskey 전역 속성.


Element.accessKey - Web API

Element.accessKey
=================

**`Element.accessKey`** 속성은 주어진 사용자가 눌러 주어진 엘리먼트로 이동할 수 있는 키 입력 세트입니다.

**참고:** `Element.accessKey` 속성은 브라우저에 이미 바인딩 된 키와의 여러 충돌로 인해 거의 사용되지 않습니다. 이를 해결하기 위해, 브라우저는 다른 "적절한" 키(예, `Alt` + accesskey)와 함께 키가 눌렸을 때동작하도록 구현하였습니다.

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

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

함께 보기
-----

* HTML 전역 속성: accesskey


HTMLElement.outerText - Web API

HTMLElement.outerText
=====================

**비표준:** 이 기능은 비표준이므로 실제 프로덕션에서 사용하지 마세요. 모든 사용자 환경에서 작동하지 않을 수 도 있으며, 미래에 호환성 문제가 생길 수 있습니다.

**`HTMLElement.outerText`** 는 비표준 속성(property)입니다. getter로써`Node.innerText`의 값을 반환합니다. setter로써는 현재 노드를 삭제하고 주어진 텍스트값으로 대체합니다.

Example
-------

StackOverflow의 이 답변을 보세요.

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

표준이 아니므로 스펙이 없습니다. 표준 논의가 다음에서 진행중입니다: whatwg/html#668.

Microsoft has a description on MSDN.

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

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

See also
--------

* `Node.innerText`
* `Element.outerHTML`


Document: dragstart 이벤트 - Web API

Document: dragstart 이벤트
=======================

`dragstart` 이벤트는 사용자가 요소나 선택한 텍스트를 드래그하기 시작할 때 발생합니다.

|  |  |
| --- | --- |
| 버블링 | 예 |
| 취소 가능 | 예 |
| 기본 동작 | 드래그 앤 드롭 작업 시작을 시작합니다. |
| 인터페이스 | `DragEvent` (en-US) |
| 이벤트 처리기 속성 | `ondragstart` (en-US) |

예제
--

### 드래그 시작 시 투명도 설정

이 예제에서는 컨테이너 안에 드래그 가능한 요소를 배치했습니다. 이 요소를 잡아 드래그한 후 놓아보세요.

`dragstart` 이벤트를 수신해서, 요소가 드래그 되기 시작하면 반투명하게 설정하는 예제입니다.

`drag` 이벤트 문서에서 드래그 앤 드롭의 더 완전한 예제를 확인할 수 있습니다.

#### HTML

```html
<div id="container">
  <div id="draggable" draggable="true">드래그 가능</div>
</div>
<div class="dropzone"></div>
```

#### CSS

```css
body {
  /\* 사용자가 예제의 텍스트를 선택하지 못하도록 \*/
  user-select: none;
}

#draggable {
  text-align: center;
  background: white;
}

#container {
  width: 200px;
  height: 20px;
  background: blueviolet;
  padding: 10px;
}

.dragging {
  opacity: 0.5;
}
```

#### JavaScript

```js
document.addEventListener("dragstart", (event) => {
  // 반투명하게 만들기
  event.target.classList.add("dragging");
});

document.addEventListener("dragend", (event) => {
  // 불투명하게 초기화
  event.target.classList.remove("dragging");
});
```

#### 결과

명세
--

| Specification |
| --- |
| HTML Standard # ix-handler-ondragstart |
| HTML Standard # event-dnd-dragstart |

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

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

같이 보기
-----

* 다른 드래그 앤 드롭 이벤트:
    + `drag`
    + `dragend`
    + `dragover` (en-US)
    + `dragenter` (en-US)
    + `dragleave` (en-US)
    + `drop` (en-US)
* 이 이벤트의 다른 대상:
    + `Window`: `dragstart` 이벤트
    + `HTMLElement`: `dragstart` 이벤트
    + `SVGElement` (en-US): `dragstart` 이벤트


Document: dragend 이벤트 - Web API

Document: dragend 이벤트
=====================

`dragend` 이벤트는 마우스 버튼을 놓거나 이스케이프 키를 눌러 드래그 작업이 끝나게 되면 발생합니다.

|  |  |
| --- | --- |
| 버블링 | 예 |
| 취소 가능 | 아니오 |
| 기본 동작 | 다양함 |
| 인터페이스 | `DragEvent` (en-US) |
| 이벤트 처리기 속성 | `ondragend` (en-US) |

예제
--

### 드래그 종료 시 투명도 초기화

이 예제에서는 컨테이너 안에 드래그 가능한 요소를 배치했습니다. 이 요소를 잡아 드래그한 후 놓아보세요.

요소가 드래그 되기 시작하면 반투명하게 설정했다가, `dragend` 이벤트를 수신해서 드래그가 끝나면 다시 불투명하게 만드는 예제입니다.

`drag` 이벤트 문서에서 드래그 앤 드롭의 더 완전한 예제를 확인할 수 있습니다.

#### HTML

```html
<div id="container">
  <div id="draggable" draggable="true">드래그 가능</div>
</div>
<div class="dropzone"></div>
```

#### CSS

```css
body {
  /\* 사용자가 예제의 텍스트를 선택하지 못하도록 \*/
  user-select: none;
}

#draggable {
  text-align: center;
  background: white;
}

#container {
  width: 200px;
  height: 20px;
  background: blueviolet;
  padding: 10px;
}

.dragging {
  opacity: 0.5;
}
```

#### JavaScript

```js
document.addEventListener("dragstart", (event) => {
  // 반투명하게 만들기
  event.target.classList.add("dragging");
});

document.addEventListener("dragend", (event) => {
  // 불투명하게 초기화
  event.target.classList.remove("dragging");
});
```

#### 결과

명세
--

| Specification |
| --- |
| HTML Standard # ix-handler-ondragend |
| HTML Standard # event-dnd-dragend |

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

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

같이 보기
-----

* 다른 드래그 앤 드롭 이벤트:
    + `drag`
    + `dragstart`
    + `dragover` (en-US)
    + `dragenter` (en-US)
    + `dragleave` (en-US)
    + `drop` (en-US)
* 이 이벤트의 다른 대상:
    + `Window`: `dragend` 이벤트
    + `HTMLElement`: `dragend` 이벤트
    + `SVGElement` (en-US): `dragend` 이벤트


HTMLElement.click() - Web API

HTMLElement.click()
===================

**`HTMLElement.click()`** 메소는 엘리먼트에 마우스 클릭을 시뮬레이션합니다.

지원하는 엘리먼트(`<input>` (en-US) 등)에서 `click()` 이 사용될 때, 엘리먼트의 클릭 이벤트가 실행됩니다. 그 다음, 다큐먼트 트리(또는 이벤트 체인)에서 더 상위의 엘리먼트로 버블링되며 상위 엘리먼트의 클릭 이벤트를 실행합니다.

구문
--

```js
element.click();
```

명세
--

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

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

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


HTMLElement: offsetLeft 속성 - Web API

HTMLElement: offsetLeft 속성
==========================

**`HTMLElement.offsetLeft`** 읽기 전용 속성은 현재 요소의 왼쪽 상단 모서리가 `HTMLElement.offsetParent` 노드 내에서 왼쪽으로 얼마나 떨어져 있는지를 픽셀 수로 반환합니다.

블록 레벨 요소의 경우 `offsetTop`, `offsetLeft`, `offsetWidth`, `offsetHeight`는 `offsetParent`를 기준으로 요소의 테두리 상자를 설명합니다.

하지만 **span** 같이 한 줄에서 다음 줄로 감싸질 수 있는 인라인 레벨 요소의 경우 `offsetTop`과 `offsetLeft`는 첫 번째 테두리 상자의 위치를 설명하고 (너비와 높이를 가져오기 위해 `Element.getClientRects()` 사용), `offsetWidth`와 `offsetHeight`는 경계 테두리 상자의 치수를 설명합니다 (위치를 가져오기 위해 `Element.getBoundingClientRect()` 사용). 따라서 왼쪽, 위쪽, 너비, 높이가 `offsetLeft`, `offsetTop`, `offsetWidth`, `offsetHeight`인 상자는 텍스트로 감싸진 span의 경계 상자가 되지 않습니다.

값
-

정수입니다.

예제
--

```js
const colorTable = document.getElementById("t1");
const tOLeft = colorTable.offsetLeft;

if (tOLeft > 5) {
  // 큰 왼쪽 오프셋: 여기서 무언가를 하세요.
}
```

이 예제는 파란색 테두리가 있는 div 안에 둘러싸인 'long' 문장과 span의 경계를 설명해야 할 것으로 생각하는 빨간색 상자를 보여줍니다.

![Short span이라고 읽는 문장입니다. 이 텍스트는 완전히 파란색 테두리가 있는 div 안에 있습니다. 이 div 내에서 감싸는 Long span이라고 읽는 문장입니다. "long span that wraps"라는 단어가 빨간색 테두리가 있는 상자 안에 있습니다. "within this div"라는 단어가 파란색 테두리가 있는 div 안에 있습니다.](/ko/docs/Web/API/HTMLElement/offsetLeft/offsetleft.jpg)

```html
<div
 style="width: 300px; border-color:blue; border-style:solid; border-width:1;">
  <span>Short span. </span>
  <span id="longspan">Long span that wraps within this div.</span>
</div>

<div
 id="box"
 style="position: absolute; border-color: red; border-width: 1; border-style: solid; z-index: 10"></div>

<script>
 const box = document.getElementById("box");
 const longspan = document.getElementById("longspan");
 box.style.left = longspan.offsetLeft + document.body.scrollLeft + "px";
 box.style.top = longspan.offsetTop + document.body.scrollTop + "px";
 box.style.width = longspan.offsetWidth + "px";
 box.style.height = longspan.offsetHeight + "px";
</script>
```

명세서
---

| Specification |
| --- |
| CSSOM View Module # dom-htmlelement-offsetleft |

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

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

같이 보기
-----

* `HTMLElement.offsetParent`, `HTMLElement.offsetTop` (en-US), `HTMLElement.offsetWidth` (en-US), `HTMLElement.offsetHeight`


HTMLElement.offsetParent - Web API

HTMLElement.offsetParent
========================

**`HTMLElement.offsetParent`** 읽기전용 프라퍼티는 가장 가까운 (포함 계층에서 가장 가까운) 위치가 정해진 포함하는 엘리먼트 객체에 대한 참조를 반환한다. `offsetParent`는 엘리먼트의 위치가 정해지지 않으면 가장 가까운 테이블 또는 테이블 셀, 루트 엘리먼트 (표준 준수 모드에서는 `html`; quirks 렌더링 모드에서는 `body`) 를 반환한다.
 엘리먼트의 `style.display`가 "none"으로 설정되면 null을 반환한다. `offsetParent`는 `offsetTop` (en-US)과 `offsetLeft`가 그것의 패딩 모서리에 상대적이기 때문에 유용하다.

문법
--

```js
parentObj = element.offsetParent;
```

* \_parentObj는 현재 엘리먼트가 그 안에서 offset된 엘리먼트의 객체 참조이다. \_is an object reference to the element in which the current element is offset.
명세
--

| Specification |
| --- |
| CSSOM View Module # dom-htmlelement-offsetparent |

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

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


HTMLElement: change 이벤트 - Web API

HTMLElement: change 이벤트
=======================

`change` 이벤트는 사용자가 `<input>` (en-US), `<select>` (en-US), `<textarea>` (en-US) 요소의 값을 변경할 때 발생합니다. `input` 이벤트와 달리 `change`는 요소의 `value`값이 변경될 때마다 매번 발생하지는 않을 수 있습니다.

값이 바뀐 요소와 사용자가 값을 바꾼 방법에 따라 `change` 이벤트가 발생하는 시점은 달라집니다.

* `<input type="checkbox">` 요소를 클릭하거나 키보드를 사용해서 체크하거나 해제할 때
* `<input type="radio"> (en-US)` 요소가 체크될 때 (해제될 때는 발생하지 않음)
* 사용자가 변경사항을 직접 반영할 때 (`<select>` (en-US)의 드롭다운에서 값을 클릭, `<input type="date">`의 달력에서 날짜 선택, `<input type="file">`의 파일 선택 창에서 파일을 선택할 때 등)
* `<textarea>` (en-US)와 `<input>` (en-US)의 `text (en-US)`, `search (en-US)`, `url (en-US)`, `tel (en-US)`, `email (en-US)`, `password (en-US)` 유형처럼, 사용자 상호작용이 값 선택이 아니라 타이핑인 요소의 값이 바뀐 뒤 포커스를 상실했을 때

HTML 명세서에 `change` 이벤트가 발생해야 하는 `<input>` 유형들이 나열되어 있습니다.

구문
--

이벤트 이름을 `addEventListener()` 등의 메서드에 제공하거나, 이벤트 처리기 속성을 사용하세요.

```js
addEventListener("change", (event) => {});

onchange = (event) => {};
```

이벤트 유형
------

일반 `Event`.

예제
--

### <select> 요소

#### HTML

```html
<label>
  아이스크림 맛을 선택하세요.
  <select class="ice-cream" name="ice-cream">
    <option value="">선택…</option>
    <option value="초콜릿">초콜릿</option>
    <option value="정어리">정어리</option>
    <option value="바닐라">바닐라</option>
  </select>
</label>

<div class="result"></div>
```

```
body {
  display: grid;
  grid-template-areas: "select result";
}

select {
  grid-area: select;
}

.result {
  grid-area: result;
}
```

#### JavaScript

```js
const selectElement = document.querySelector(".ice-cream");

selectElement.addEventListener("change", (event) => {
  const result = document.querySelector(".result");
  result.textContent = `${event.target.value} 맛을 좋아하시는군요`;
});
```

#### 결과

### 텍스트 입력 요소

`<input type="text">`를 포함한 일부 요소들에서는 컨트롤이 포커스를 잃을 때까지 `change` 이벤트가 발생하지 않습니다. 아래 입력 필드에 텍스트를 입력하고, 다른 아무 곳이나 클릭해서 이벤트를 발생시켜보세요.

#### HTML

```html
<input placeholder="아무 텍스트나 입력" name="name" />
<p id="log"></p>
```

#### JavaScript

```js
const input = document.querySelector("input");
const log = document.getElementById("log");

input.addEventListener("change", updateValue);

function updateValue(e) {
  log.textContent = e.target.value;
}
```

#### 결과

명세서
---

| Specification |
| --- |
| HTML Standard # event-change |
| HTML Standard # handler-onchange |

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

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

어떤 상호작용들은 브라우저마다 `change` 이벤트의 발생 여부가 다를 수도 있습니다. 예를 들어, Gecko에서는 `<select>` (en-US) 요소의 키보드 탐색으로 `change` 이벤트가 발생하지 않았습니다(Firefox 버그 126379). 하지만 Firefox 63 (Quantum) 이후로 이 동작은 모든 주요 브라우저에서 동일합니다.


HTMLElement: lang 속성 - Web API

HTMLElement: lang 속성
====================

**`HTMLElement.lang`** 속성은 요소의 특성 값과 텍스트 내용의 기본 언어를 가져오거나 설정합니다.

이 속성에 의해 반환되는 언어 코드는 RFC 5646: BCP 47로도 알려진 언어 식별용 태그에 정의되어 있습니다. 일반적인 예로 영어의 "en", 일본어의 "ja", 스페인어의 "es" 등을 포함합니다. 이 특성의 기본값은 `unknown`입니다. 이 특성은 여기에 설명된 개별 요소 수준에서 유효하지만 문서의 루트 요소에 지정되는 경우가 가장 많습니다.

또한 `lang` 특성에서만 작동하고 `xml:lang`에서는 작동하지 않습니다.

값
-

문자열입니다.

예제
--

```js
// 이 스니펫은 기본 언어를 비교하고 언어에 기반하여 다른 URL로 리다이렉션합니다.
if (document.documentElement.lang === "en") {
  window.location.href = "Some\_document.html.en";
} else if (document.documentElement.lang === "ru") {
  window.location.href = "Some\_document.html.ru";
}
```

명세서
---

| Specification |
| --- |
| HTML Standard # dom-lang |

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

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


HTMLElement.contentEditable - Web API

HTMLElement.contentEditable
===========================

`HTMLElement` 요소의 **`contenteditable`** 속성은 요소의 편집 가능 여부를 나타냅니다. 열거형 속성으로, 다음 중 하나의 값을 가질 수 있습니다.

* `'true'`는 요소가 `contenteditable`임을 의미합니다.
* `'false'`는 요소를 편집할 수 없음을 의미합니다.
* `'inherit'`은 요소가 부모의 편집 가능 여부를 상속함을 의미합니다.

`HTMLElement.isContentEditable` (en-US) 속성으로 이 속성의 `Boolean` 계산값을 얻을 수 있습니다.

명세
--

| Specification |
| --- |
| HTML Standard # contenteditable |

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

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

Internet Explorer에서 `contenteditable`은 `<table>` (en-US), `<col>` (en-US), `<colgroup>` (en-US), `<tbody>` (en-US), `<td>` (en-US), `<tfoot>`, `<th>` (en-US), `<thead>`, `<tr>` (en-US) 요소에 바로 적용할 수 없습니다. 대신 편집 가능한 `<span>` 또는 `<div>` (en-US) 요소를 표의 각 칸에 배치할 수 있습니다.

같이 보기
-----

* 콘텐츠를 편집 가능하게 만들기
* `HTMLElement.isContentEditable` (en-US)
* `contenteditable` 전역 속성


Document: drag 이벤트 - Web API

Document: drag 이벤트
==================

`drag` 이벤트는 사용자가 요소 또는 텍스트를 드래그하는 동안 매 수백 밀리초마다 발생합니다.

|  |  |
| --- | --- |
| 버블링 | 예 |
| 취소 가능 | 예 |
| 기본 동작 | 드래그 앤 드롭 작업을 지속합니다. |
| 인터페이스 | `DragEvent` (en-US) |
| 이벤트 처리기 속성 | `ondrag` (en-US) |

예제
--

### 드래그 앤 드롭 예제

#### HTML

```html
<div class="dropzone">
  <div id="draggable" draggable="true">드래그 가능</div>
</div>
<div class="dropzone"></div>
```

#### CSS

```css
body {
  /\* 사용자가 예제의 텍스트를 선택하지 못하도록 \*/
  user-select: none;
}

#draggable {
  text-align: center;
  background: white;
}

.dropzone {
  width: 200px;
  height: 20px;
  background: blueviolet;
  margin: 10px;
  padding: 10px;
}

.dropzone.dragover {
  background-color: purple;
}

.dragging {
  opacity: 0.5;
}
```

#### JavaScript

```js
let dragged;

/\* 드래그 가능한 대상에서 발생하는 이벤트 \*/
document.addEventListener("drag", (event) => {
  console.log("dragging");
});

document.addEventListener("dragstart", (event) => {
  // 드래그한 요소에 대한 참조 저장
  dragged = event.target;
  // 반투명하게 만들기
  event.target.classList.add("dragging");
});

document.addEventListener("dragend", (event) => {
  // 투명도 초기화
  event.target.classList.remove("dragging");
});

/\* 드롭 대상에서 발생하는 이벤트 \*/
document.addEventListener(
  "dragover",
  (event) => {
    // 드롭을 허용하기 위해 기본 동작 취소
    event.preventDefault();
  },
  false,
);

document.addEventListener("dragenter", (event) => {
  // 드래그 가능한 요소가 대상 위로 오면 강조
  if (event.target.classList.contains("dropzone")) {
    event.target.classList.add("dragover");
  }
});

document.addEventListener("dragleave", (event) => {
  // 드래그 가능한 요소가 대상 밖으로 나가면 강조 제거
  if (event.target.classList.contains("dropzone")) {
    event.target.classList.remove("dragover");
  }
});

document.addEventListener("drop", (event) => {
  // 일부 요소의 링크 열기와 같은 기본 동작 취소
  event.preventDefault();
  // 드래그한 요소를 선택한 드롭 대상으로 이동
  if (event.target.classList.contains("dropzone")) {
    event.target.classList.remove("dragover");
    dragged.parentNode.removeChild(dragged);
    event.target.appendChild(dragged);
  }
});
```

#### 결과

명세
--

| Specification |
| --- |
| HTML Standard # ix-handler-ondrag |
| HTML Standard # event-dnd-drag |

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

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

같이 보기
-----

* 다른 드래그 앤 드롭 이벤트:
    + `dragstart`
    + `dragend`
    + `dragover` (en-US)
    + `dragenter` (en-US)
    + `dragleave` (en-US)
    + `drop` (en-US)
* 이 이벤트의 다른 대상:
    + `Window`: `drag` 이벤트
    + `HTMLElement`: `drag` 이벤트
    + `SVGElement` (en-US): `drag` 이벤트


HTMLElement.innerText - Web API

HTMLElement.innerText
=====================

`HTMLElement` 인터페이스의 **`innerText`** 속성은 요소와 그 자손의 렌더링 된 텍스트 콘텐츠를 나타냅니다. `innerText`는 사용자가 커서를 이용해 요소의 콘텐츠를 선택하고 클립보드에 복사했을 때 얻을 수 있는 텍스트의 근삿값을 제공합니다.

**참고:** `innerText`는 `Node.textContent`와 혼동하기 쉬우나 중요한 차이점을 가지고 있습니다. 기본적으로, `innerText`는 텍스트의 렌더링 후 모습을 인식할 수 있지만 `textContent`는 그렇지 않습니다.

구문
--

```js
const renderedText = htmlElement.innerText;
htmlElement.innerText = string;
```

### 값

요소의 렌더링 된 텍스트 콘텐츠를 나타내는 `DOMString`. 요소 자체가 렌더링 중이 아니라면 `Node.textContent` 속성의 값과 동일합니다.

예제
--

다음 예제는 `innerText`와 `Node.textContent`를 비교합니다. `innerText`가 `<br>` 태그를 인식하고, 숨겨진 요소를 무시하는 점에 주목하세요.

### HTML

```html
<h3>원본 요소:</h3>
<p id="source">
  <style>
 #source {
 color: red;
 }
 </style>
  아래에서<br />이 글을<br />어떻게 인식하는지 살펴보세요.
  <span style="display:none">숨겨진 글</span>
</p>
<h3>textContent 결과:</h3>
<textarea id="textContentOutput" rows="6" cols="30" readonly>...</textarea>
<h3>innerText 결과:</h3>
<textarea id="innerTextOutput" rows="6" cols="30" readonly>...</textarea>
```

### JavaScript

```js
const source = document.getElementById("source");
const textContentOutput = document.getElementById("textContentOutput");
const innerTextOutput = document.getElementById("innerTextOutput");

textContentOutput.innerHTML = source.textContent;
innerTextOutput.innerHTML = source.innerText;
```

### 결과

명세
--

| Specification |
| --- |
| HTML Standard # the-innertext-idl-attribute |

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

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

같이 보기
-----

* `HTMLElement.outerText`
* `Element.innerHTML`


HTMLElement: offsetHeight 속성 - Web API

HTMLElement: offsetHeight 속성
============================

**`HTMLElement.offsetHeight`** 읽기 전용 속성은 수직의 패딩과 테두리를 포함하여 요소의 높이를 정수로 반환합니다.

일반적으로 `offsetHeight`은 테두리, 패딩, 가로 스크롤바(렌더링된 경우)을 포함한 요소의 CSS 높이를 픽셀 단위로 측정한 값입니다. `::before` 혹은 `::after`와 같은 의사 요소의 높이는 포함하지 않습니다. 문서 바디 객체에 대해서는 요소의 CSS 높이 대신 전체 선형 콘텐츠 높이를 측정에 포함합니다. 다른 선형 콘텐츠 아래로 확장하여 부동된 요소는 무시됩니다.

요소가 숨겨져 있다면 (요소 또는 상위 요소 중 하나에서 `style.display`를 `"none"`으로 설정하는 경우), `0`이 반환됩니다.

**참고:** 이 속성은 값을 정수로 반올림합니다. 분수값이 필요하면 `element.getBoundingClientRect()`를 사용합니다.

값
-

숫자입니다.

예제
--

![패딩, 테두리, 바깥 여백이 큰 요소의 예제입니다. offsetHeight은 패딩과 테두리를 포함하고 바깥 여백을 제외한 요소의 레이아웃 높이입니다.](/ko/docs/Web/API/HTMLElement/offsetHeight/dimensions-offset.png)

위의 예제 이미지는 윈도우에 맞는 스크롤바와 `offsetHeight` 보여줍니다. 하지만 스크롤할 수 없는 요소는 보이는 콘텐츠보다 훨씬 큰 `offsetHeight` 값을 가질 수 있습니다. 이러한 요소는 일반적으로 스크롤할 수 있는 요소 안에 포함됩니다. 따라서 스크롤할 수 있는 컨테이너의 `scrollTop` 설정에 따라 이러한 스크롤할 수 없는 요소가 완전히 혹은 부분적으로 보이지 않을 수 있습니다.

명세서
---

| Specification |
| --- |
| CSSOM View Module # dom-htmlelement-offsetheight |

### 참고

`offsetHeight`은 MSIE에 의해 처음 소개된 DHTML 객체 모델의 속성입니다. 때로 요소의 물리적/그래픽적 차원 혹은 요소의 테두리 박스 높이라고도 합니다.

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

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

같이 보기
-----

* `Element.clientHeight`
* `Element.scrollHeight`
* `HTMLElement.offsetWidth` (en-US)
* Determining the dimensions of elements
* MSDN Measuring Element Dimension and Location


WebGLRenderingContext: drawArrays() 메서드 - Web API

WebGLRenderingContext: drawArrays() 메서드
=======================================

WebGL API 의 **`WebGLRenderingContext.drawArrays()`** 메서드는 배열 데이터로부터 기본 그래픽 형상을 렌더링합니다.

구문
--

```js
drawArrays(mode, first, count)
```

### 매개변수

`mode`

렌더링할 기본 그래픽 형상의 타입을 지정하는 `GLenum` (en-US) 입니다. 가능한 값은 다음과 같습니다.

* `gl.POINTS`: 한 점을 그립니다.
* `gl.LINE_STRIP`: 다음 정점까지 직선을 그립니다.
* `gl.LINE_LOOP`: 다음 정점까지 직선을 그리고, 마지막 정점을 처음 정점에 연결합니다.
* `gl.LINES`: 정점 쌍 사이에 선을 그립니다.
* `gl.TRIANGLE_STRIP`
* `gl.TRIANGLE_FAN`
* `gl.TRIANGLES`: 세 개의 정점 그룹에 대한 삼각형을 그립니다.

`first`

벡터 포인트 배열에서의 시작 인덱스를 지정하는 `GLint` (en-US) 입니다.

`count`

렌더링 될 인덱스의 개수를 지정하는 `GLsizei` (en-US) 입니다.

### 반환 값

없음 (`undefined`).

### 예외

* `mode` 가 허용된 값 중 하나가 아닌 경우,
 `gl.INVALID_ENUM` 오류가 발생합니다.
* `first` 또는 `count` 가 음수인 경우,
 `gl.INVALID_VALUE` 오류가 발생합니다.
* `gl.CURRENT_PROGRAM` 이 `null`인 경우,
 `gl.INVALID_OPERATION` 오류가 발생합니다.
예제
--

```js
gl.drawArrays(gl.POINTS, 0, 8);
```

명세서
---

| Specification |
| --- |
| WebGL Specification # 5.14.11 |

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

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

같이 보기
-----

* `WebGLRenderingContext.drawElements()` (en-US)
* `ext.drawArraysInstancedANGLE()` (en-US)
* `ext.drawElementsInstancedANGLE()` (en-US)
* `ext.vertexAttribDivisorANGLE()` (en-US)
* `WebGL2RenderingContext.drawArraysInstanced()` (en-US)
* `WebGL2RenderingContext.drawElementsInstanced()` (en-US)
* `WebGL2RenderingContext.vertexAttribDivisor()` (en-US)
* `WEBGL_multi_draw.multiDrawArraysWEBGL()` (en-US)


WebGLRenderingContext: bindBuffer() 메서드 - Web API

WebGLRenderingContext: bindBuffer() 메서드
=======================================

WebGL API의 **`WebGLRenderingContext.bindBuffer()`** 메서드는 주어진 `WebGLBuffer` (en-US) target에 바인딩합니다.

구문
--

```js
bindBuffer(target, buffer)
```

### 매개변수

`target`

바인딩 포인트(target)을 지정하는 `GLenum` (en-US) 입니다. 가능한 값들은 다음과 같습니다.

`gl.ARRAY_BUFFER`

정점 좌표, 텍스처 좌표 데이터 또는 정점 색상 데이터 등의 정점 속성을 포함하는 버퍼입니다.

`gl.ELEMENT_ARRAY_BUFFER`

요소 인덱스를 위한 버퍼입니다.

WebGL 2 context (en-US) 를 사용하면 추가로 다음의 값들이 가능합니다.

`gl.COPY_READ_BUFFER`

한 버퍼 객체에서 다른 버퍼 객체로 복사하기 위한 버퍼입니다.

`gl.COPY_WRITE_BUFFER`

한 버퍼 객체에서 다른 버퍼 객체로 복사하기 위한 버퍼입니다.

`gl.TRANSFORM_FEEDBACK_BUFFER`

변환 피드백 작업을 위한 버퍼입니다.

`gl.UNIFORM_BUFFER`

유니폼 블록을 저장하기 위한 버퍼입니다.

`gl.PIXEL_PACK_BUFFER`

픽셀 전송 작업을 위한 버퍼입니다.

`gl.PIXEL_UNPACK_BUFFER`

픽셀 전송 작업을 위한 버퍼입니다.

`buffer`

바인딩할 `WebGLBuffer` (en-US) 입니다.

### 반환 값

없음 (`undefined`).

### 예외

주어진 `WebGLBuffer` (en-US)에는 한 target만 바인딩할 수 있습니다. 버퍼를 다른 타겟에 바인딩하려는 시도는 `INVALID_OPERATION` 오류를 던지고 현재 버퍼 바인딩은 유지됩니다.

`deleteBuffer` (en-US)로 삭제로 표시된 `WebGLBuffer` (en-US)는 (재)바인딩할 수 없습니다. 시도할 경우 `INVALID_OPERATION` 오류를 발생시키고, 현재 바인딩은 그대로 유지됩니다.

예제
--

### 버퍼를 타겟에 바인딩하기

```js
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const buffer = gl.createBuffer();

gl.bindBuffer(gl.ARRAY\_BUFFER, buffer);
```

### 현재 바인딩 확인하기

 현재 버퍼 바인딩을 확인하려면,`ARRAY_BUFFER_BINDING`
 과 `ELEMENT_ARRAY_BUFFER_BINDING` 상수를 조회하십시오.

```js
gl.getParameter(gl.ARRAY\_BUFFER\_BINDING);
gl.getParameter(gl.ELEMENT\_ARRAY\_BUFFER\_BINDING);
```

명세서
---

| Specification |
| --- |
| WebGL Specification # 5.14.5 |
| WebGL 2.0 Specification # 3.7.1 |

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

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

같이 보기
-----

* `WebGLRenderingContext.createBuffer()` (en-US)
* `WebGLRenderingContext.deleteBuffer()` (en-US)
* `WebGLRenderingContext.isBuffer()` (en-US)
* Other buffers: `WebGLFramebuffer` (en-US), `WebGLRenderbuffer`


WebGLRenderingContext: bufferData() 메서드 - Web API

WebGLRenderingContext: bufferData() 메서드
=======================================

WebGL API 의 **`WebGLRenderingContext.bufferData()`** 메서드는 버퍼 객체의 데이터 저장소를 초기화하고 생성합니다.

구문
--

```js
// WebGL1
bufferData(target, usage)
bufferData(target, size, usage)
bufferData(target, srcData, usage)

// WebGL2
bufferData(target, usage, srcOffset)
bufferData(target, srcData, usage, srcOffset)
bufferData(target, srcData, usage, srcOffset, length)
```

### 매개 변수

`target`

바인딩 지점(target)을 지정하는`GLenum` (en-US). 가능한 값들은 다음과 같습니다.

`gl.ARRAY_BUFFER`

정점 좌표, 텍스처 좌표 데이터 또는 정점 색상 데이터와 같은 정점 속성을 포함하는 버퍼.

`gl.ELEMENT_ARRAY_BUFFER`

요소 인덱스를 위한 버퍼.

WebGL 2 context (en-US)를 사용하는 경우, 추가적으로 아래 값들이 가능합니다.

`gl.COPY_READ_BUFFER`

한 버퍼 객체에서 다른 버퍼 객체로 복사하기 위한 버퍼.

`gl.COPY_WRITE_BUFFER`

한 버퍼 객체에서 다른 버퍼 객체로 복사하기 위한 버퍼.

`gl.TRANSFORM_FEEDBACK_BUFFER`

변환 피드백 작업을 위한 버퍼.

`gl.UNIFORM_BUFFER`

유니폼 블록을 저장하기 위한 버퍼.

`gl.PIXEL_PACK_BUFFER`

픽셀 전송 작업을 위한 버퍼.

`gl.PIXEL_UNPACK_BUFFER`

픽셀 전송 작업을 위한 버퍼.

`size`

버퍼 객체의 데이터 저장소의 크기를 바이트 단위로 설정하는 `GLsizeiptr` (en-US).

`srcData` Optional

데이터 저장소로 복사될 `ArrayBuffer`, `SharedArrayBuffer`, `TypedArray` 혹은 `DataView`. `null`인 경우, 데이터 저장소는 여전히 생성되지만, 그 내용은 초기화되지 않고 정의되지 않습니다.

`usage`

최적화 목적으로 데이터 저장소의 예정된 사용 패턴을 지정하는 `GLenum` (en-US) 가능한 값들은 다음과 같습니다.

* `gl.STATIC_DRAW`
    + : 콘텐츠는 응용 프로그램에 의해 한 번 지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 여러 번 사용됩니다.
* `gl.DYNAMIC_DRAW`
    + : 콘텐츠는 응용 프로그램에 의해 반복적으로 재지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 여러 번 사용됩니다.
* `gl.STREAM_DRAW`
    + : 콘텐츠는 응용 프로그램에 의해 한 번 지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 몇 번 사용됩니다.
* WebGL 2 context (en-US)를 사용할 때 추가적으로 사용할 수 있는 값들은 다음과 같습니다.

`gl.STATIC_READ`

콘텐츠는 WebGL에서 데이터를 읽어 한 번 지정되며, 응용 프로그램에 의해 여러 번 조회됩니다.

`gl.DYNAMIC_READ`

콘텐츠는 WebGL에서 데이터를 반복적으로 읽어 재지정되며, 응용 프로그램에 의해 여러 번 조회됩니다.

`gl.STREAM_READ`

콘텐츠는 WebGL에서 데이터를 읽어 한 번 지정되며, 응용 프로그램에 의해 몇 번 조회됩니다.

`gl.STATIC_COPY`

콘텐츠는 WebGL에서 데이터를 읽어 한 번 지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 여러 번 사용됩니다.

`gl.DYNAMIC_COPY`

콘텐츠는 WebGL에서 데이터를 반복적으로 읽어 재지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 여러 번 사용됩니다.

`gl.STREAM_COPY`

콘텐츠는 WebGL에서 데이터를 읽어 한 번 지정되며, WebGL 그리기와 이미지 명세 명령의 소스로서 몇 번 사용됩니다.

`srcOffset`

버퍼를 어디서부터 읽기 시작할 지 결정하는 원소 인덱스 오프셋을 지정하는 `GLuint` (en-US).

`length` Optional

기본적으로 0이 지정된 `GLuint` (en-US).

### 반환 값

없음 (`undefined`).

### 예외

* 컨텍스트가 주어진 `size`로 데이터 저장소를 생성할 수 없는 경우 `gl.OUT_OF_MEMORY` 오류가 발생합니다.
* `size`가 음수인 경우 `gl.INVALID_VALUE` 오류가 발생합니다.
* `target` 또는 `usage`가 허용된 열거형 중 하나가 아닌 경우 `gl.INVALID_ENUM` 오류가 발생합니다.
예제
--

### bufferData 사용하기

```js
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY\_BUFFER, buffer);
gl.bufferData(gl.ARRAY\_BUFFER, 1024, gl.STATIC\_DRAW);
```

### 버퍼 정보 가져오기

 현재 버퍼의 사용량과 버퍼 크기를 확인하려면
 `WebGLRenderingContext.getBufferParameter()` (en-US) 메서드를 사용합니다.

```js
gl.getBufferParameter(gl.ARRAY\_BUFFER, gl.BUFFER\_SIZE);
gl.getBufferParameter(gl.ARRAY\_BUFFER, gl.BUFFER\_USAGE);
```

### 형식화 배열의 크기 가져오기

타입 배열의 크기 매개변수를 계산합니다.

```js
const dataArray = new Float32Array([1, 2, 3, 4]);
const sizeInBytes = dataArray.length \* dataArray.BYTES\_PER\_ELEMENT;
```

명세서
---

| Specification |
| --- |
| WebGL Specification # 5.14.5 |

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

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

같이 보기
-----

* `WebGLRenderingContext.createBuffer()` (en-US)
* `WebGLRenderingContext.bufferSubData()` (en-US)
* 기타 버퍼: `WebGLFramebuffer` (en-US), `WebGLRenderbuffer`


History.state - Web API

History.state
=============

**`History.state`** 속성은 현 history에 해당하는 state값을 나타냅니다.

`popstate` 이벤트가 트리거될때가 아닌 상태에서 state값을 볼 수 있는 방법입니다.

구문
--

```js
const currentState = history.state;
```

### 값

현 history에 위치한 값입니다. 이 값은 `pushState()` 또는 `replaceState()`을 사용할때까지 `null` 값을 가집니다.

예제
--

`history.state` 로 초기값을 보여준 후 `pushState()`를 사용하여 State를 푸시합니다.

다음 코드 줄은 `history.state` 를 사용하여 콘솔에다 값이 푸시되었음을 보여줍니다.

```js
// Should be null because we haven't modified the history stack yet
console.log(`History.state before pushState: ${history.state}`);

// Now push something on the stack
history.pushState({ name: "Example" }, "pushState example", "page3.html");

// Now state has a value.
console.log(`History.state after pushState: ${history.state}`);
```

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-state-dev |

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

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

같이 보기
-----

* Working with the History API (en-US)


History: pushState() 메서드 - Web API

History: pushState() 메서드
========================

HTML 문서에서, **`history.pushState()`** 메서드는 브라우저의 세션 기록 스택에 항목을 추가합니다.

이 메서드는 비동기로 동작합니다. 네비게이션이 언제 완료되었는지 결정하기 위해 `popstate` 이벤트에 대한 수신기를 추가합니다. 이때 `state` 매개변수를 사용할 수 있습니다.

구문
--

```js
pushState(state, unused)
pushState(state, unused, url)
```

### 매개변수

`state`

`state` 객체는 `pushState()`에 의해 생성되어 새로운 기록 항목과 관련된 JavaScript 객체입니다. 사용자가 새로운 `state`로 이동할 때마다 `popstate` 이벤트가 발생되고 이벤트 `state` 속성은 기록 항목의 `state` 객체 복사본을 포함합니다.

`state` 객체는 직렬화될 수 있는 모든 것일 수 있습니다. Firefox는 사용자가 브라우저를 다시 시작한 후 복원될 수 있도록 사용자의 디스크에 `state` 객체를 저장하기 때문에, `state` 객체의 직렬화된 표현에 16MiB의 크기 제한을 적용합니다. 이보다 더 큰 직렬화된 표현 `state` 객체를 `pushState()`에 전달하면 메서드는 예외를 발생시킵니다. 이보다 더 많은 공간이 필요하다면 `sessionStorage` 혹은 `localStorage`를 사용하는 것이 좋습니다.

`unused`

이 매개변수는 역사적인 이유로 존재해서 생략할 수 없습니다. 빈 문자열을 전달하는 것이 나중에 메서드가 변경될 경우에도 안전합니다.

`url` Optional

새로운 기록 항목의 URL입니다. 브라우저는 `pushState()` 호출 후에는 이 URL을 로드하려고 시도하지 않습니다. 하지만, 예를 들어 사용자가 브라우저를 다시 시작한 후에는 나중에 URL을 로드하려고 시도할 수 있습니다. 새로운 URL은 절대 경로일 필요가 없습니다. 상대 경로인 경우 현재 URL을 기준으로 해결됩니다. 새로운 URL은 현재 URL과 같은 출처여야 합니다. 그렇지 않으면, `pushState()`는 예외를 발생시킵니다. 이 매개변수가 지정되지 않으면, 문서의 현재 URL로 설정됩니다.

### 반환 값

없음 (`undefined`).

설명
--

어떤 의미에서 `pushState()`를 호출하는 것은 `window.location = "#foo"`를 설정하는 것과 유사합니다. 둘 다 현재 문서와 연결된 다른 기록 항목을 생성하고 활성화합니다. 그러나 `pushState()`에는 몇 가지 이점이 있습니다.

* 새로운 URL은 현재 URL과 같은 출처에서 모든 URL이 될 수 있습니다. 반면, `window.location` (en-US)을 설정하는 것은 오직 해시를 수정하는 경우에만 동일한 문서를 유지할 수 있습니다.
* 페이지의 URL 변경은 선택 사항입니다. 반면, `window.location = "#foo";`를 설정하는 것은 현재 해시가 `#foo`가 아닌 경우에만 새로운 기록 항목을 생성합니다.
* 임의 데이터를 새로운 기록 항목과 연결할 수 있습니다. 해시 기반 접근에서는 모든 관련 데이터를 짧은 문자열로 인코딩할 필요가 있습니다.

새로운 URL이 오래된 URL과 오직 해시만 다를지라도 `pushState()`는 `hashchange` 이벤트를 발생시키지 않습니다.

예제
--

다음 코드는 상태와 url를 설정하는 새로운 브라우저 기록 항목을 생성합니다.

### JavaScript

```js
const state = { page\_id: 1, user\_id: 5 };
const url = "hello-world.html";

history.pushState(state, "", url);
```

### 쿼리 매개변수 변경

```js
const url = new URL(location);
url.searchParams.set("foo", "bar");
history.pushState({}, "", url);
```

명세서
---

| Specification |
| --- |
| HTML Standard # dom-history-pushstate-dev |

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

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

같이 보기
-----

* Working with the History API (en-US)
* Window: popstate event


History.go() - Web API

History.go()
============

**`History.go()`** 메서드는 history 세션에서 특정한 페이지를 로딩합니다. 인자로 전달하는 파라미터 값에 따라 history를 통해서 페이지를 앞 뒤로 이동할 수 있습니다.

이 메서드는 asynchronous(비동기)로 동작합니다. 페이지 앞, 뒤 이동이 언제 이뤄지는지 알려면 `popstate` event에 대한 listener를 등록합니다.

구문
--

```js
history.go([delta]);
```

### Parameters

`delta` Optional

현재 페이지에서 상대적으로 이동하려고 하는 history의 위치 값. 음수 값은 뒤로 이동하고, 양수 값은 앞으로 이동합니다. 예를 들면 `history.go(2)` 는 현재 페이지에서 2 페이지 앞으로 이동하고, `history.go(-2)` 는 현재 페이지에서 2 페이지 뒤로 이동합니다. 만약 값을 전달하지 않거나, `delta` 값을 0으로 전달한다면, 이는 `location.reload()`를 동작시켰을 때와 동일한 결과를 보입니다. (새로고침)

예제
--

한 페이지 뒤로 가기 (`back()`를 호출한 것과 동일):

```js
history.go(-1);
```

`forward()`와 동일한 한 페이지 앞으로 가기:

```js
history.go(1);
```

두 페이지 앞으로 가기:

```js
history.go(2);
```

두 페이지 뒤로 가기:

```js
history.go(-2);
```

마지막으로, 아래 구문들은 현재 페이지를 새로고침 합니다:

```js
history.go();
history.go(0);
```

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-go-dev |

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

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

See also
--------

* `History`
* `back()`
* `forward()`
* `popstate` event
* Working with the History API (en-US)


History.replaceState() - Web API

History.replaceState()
======================

**`History.replaceState()`** 메서드는 현재 history를 수정해 메소드의 매개 변수에 전달 된 `stateObj`, `title`, `URL`로 대체합니다. 이 방법은 특히 일부 유저의 동작에 대한 응답으로 history 객체의 상태나 현재 history의 URL을 업데이트하려는 경우에 유용합니다.

이 메서드는 비동기로 동작합니다. 네비게이션이 언제 완료되었는지 결정하기 위해 `popstate` 이벤트에 대한 수신기를 추가합니다. 이때 `state` 매개변수를 사용할 수 있습니다.

구문
--

```js
history.replaceState(stateObj, title[, url])
```

### Parameters

`stateObj`

state 객체는 `replaceState`에 전달된 history 항목과 연관된 JavaScript 객체입니다. state object는 `null`일 수 있습니다.

`title`

나중에는 사용할 수도 있지만, 대부분의 브라우저는 현재 이 파라미터를 무시하고 있습니다. 이 부분에 빈 String을 전달하면 나중에 메소드가 변화하더라도 안전합니다. 또는, state에 짧은 title을 전달할 수도 있습니다.

`url` Optional

history 항목의 URL 입니다. 새 URL은 현재 URL과 출처가 동일해야(same origin)합니다. 그렇지 않으면 replaceState에서 예외가 발생합니다.

예제
--

`http://www.mozilla.org/` 에서 아래 JavaScript를 실행한다고 가정합시다:

```js
const stateObj = { foo: "bar" };
history.pushState(stateObj, "", "bar.html");
```

위 두 줄에 대한 설명은 Working with the History API (en-US) 문서의 Example of `pushState()` method (en-US)에서 확인할 수 있습니다. 그 다음, `http://www.mozilla.org/bar.html` 에서 아래와 같은 JavaScript를 실행한다고 가정해보세요:

```js
history.replaceState(stateObj, "", "bar2.html");
```

이렇게하면 URL 표시줄에 `http://www.mozilla.org/bar2.html` 이라고 표시되지만, 브라우저가 `bar2.html` 을 로드하거나 `bar2.html`파일이 있는지 확인하지는 않습니다.

이제 사용자가 `http://www.microsoft.com` 으로 이동한 다음, 뒤로가기 버튼을 누른다고 가정해봅시다. 이 때, URL 표시줄에는 `http://www.mozilla.org/bar2.html` 이 표시됩니다. 사용자가 다시 뒤로가기 버튼을 누르면, URL 표시줄에는 `http://www.mozilla.org/foo.html` 이 표시되고, bar.html은 완전히 무시되어 표시되지 않습니다.

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-replacestate-dev |

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

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


History.scrollRestoration - Web API

History.scrollRestoration
=========================

**`History.scrollRestoration`** 속성을 사용하면 기록 탐색 시 사용할 스크롤 위치 복원 기능의 기본값을 웹 애플리케이션이 지정할 수 있습니다.

구문
--

```js
let scrollRestore = history.scrollRestoration;
```

### 값

`"auto"`

페이지 내에서 사용자의 스크롤이 위치했던 장소로 복원합니다.

`"manual"`

스크롤을 복원하지 않습니다. 사용자가 직접 스크롤해야 합니다.

예제
--

### 현재 스크롤 복원 여부 알아내기

```js
const scrollRestoration = history.scrollRestoration;
if (scrollRestoration === "manual") {
  console.log(
    "The location on the page is not restored, user will need to scroll manually.",
  );
}
```

### 스크롤 복원 비활성화

```js
if (history.scrollRestoration) {
  window.history.scrollRestoration = "manual";
}
```

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-scroll-restoration-dev |

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

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


History.length - Web API

History.length
==============

**`History.length`** 읽기 전용 속성은 현재 페이지를 포함해, 세션 기록의 길이를 나타내는 정수를 반환합니다. 예를 들어, 새로운 탭에 막 불러온 페이지의 세션 기록 길이는 `1`입니다.

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-length-dev |

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

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

같이 보기
-----

* 속성이 속한 `History` 인터페이스


History.back() - Web API

History.back()
==============

**`History.back()`** 메서드는 브라우저가 세션 기록의 바로 뒤 페이지로 이동하도록 지시합니다. `history.go(-1)`와 같습니다. 이전 페이지가 없는 경우 아무것도 하지 않습니다.

이 메서드는 비동기적입니다. `popstate` 이벤트 처리기를 통해 탐색 완료 시점을 알 수 있습니다.

구문
--

```js
history.back();
```

예제
--

다음 예제는 클릭했을 때 뒤로 가는 버튼을 생성합니다.

### HTML

```html
<button id="go-back">뒤로 가기!</button>
```

### JavaScript

```js
document.getElementById("go-back").addEventListener("click", () => {
  window.history.back();
});
```

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-back-dev |

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

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

같이 보기
-----

* `History`


History.forward() - Web API

History.forward()
=================

**`History.forward()`** 메서드는 브라우저가 세션 기록의 바로 앞 페이지로 이동하도록 지시합니다. `history.go(1)`과 같습니다. 다음 페이지가 없는 경우 아무것도 하지 않습니다.

이 메서드는 비동기적입니다. `popstate` 이벤트 처리기를 통해 탐색 완료 시점을 알 수 있습니다.

구문
--

```js
history.forward();
```

예제
--

다음 예제는 클릭했을 때 앞으로 가는 버튼을 생성합니다.

### HTML

```html
<button id="go-forward">앞으로 가기!</button>
```

### JavaScript

```js
document.getElementById("go-forward").addEventListener("click", (e) => {
  window.history.forward();
});
```

명세
--

| Specification |
| --- |
| HTML Standard # dom-history-forward-dev |

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

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

같이 보기
-----

* `History`


캔버스(canvas)를 이용한 비디오 조작하기 - Web API

캔버스(canvas)를 이용한 비디오 조작하기
=========================

비디오에서 다양한 시각적 효과를 보여주기 위해,`캔버스`와 `비디오`의 기능을 결합하여 실시간으로 비디오 데이터를 조작할 수 있습니다. 이 튜토리얼에서는 JavaScript 코드로 어떻게 크로마 키잉(chroma-keying, 또한 "녹색 스크린 효과, green screen effect")을 구현할 수 있는지 보여줍니다.

문서(document) 내용
---------------

이 내용을 보여주기 위한 XHTML 문서는 아래와 같습니다.

```html
<!doctype html>
<html>
  <head>
    <style>
 body {
 background: black;
 color: #cccccc;
 }
 #c2 {
 background-image: url(foo.png);
 background-repeat: no-repeat;
 }
 div {
 float: left;
 border: 1px solid #444444;
 padding: 10px;
 margin: 10px;
 background: #3b3b3b;
 }
 </style>
    <script type="text/javascript" src="main.js"></script>
  </head>

  <body onload="processor.doLoad()">
    <div>
      <video id="video" src="video.ogv" controls="true" />
    </div>
    <div>
      <canvas id="c1" width="160" height="96"></canvas>
      <canvas id="c2" width="160" height="96"></canvas>
    </div>
  </body>
</html>
```

여기에서 중요한 요소는:

1. 이 문서에는 ID가 c1, c2인 두 개의 `캔버스`가 있습니다. 캔버스 c1은 비디오 원본의 현재 프레임을 보여주기 위해 사용되고, c2는 크로마 키잉 효과를 수행한 결과를 보여줍니다. c2에서 비디오의 녹색 배경을 대체할 정지 이미지를 미리 로드합니다.
2. JavaScript 코드는 main.js에서 가져옵니다. 이 스크립트는 JavaScript 1.8 기능을 사용했기 때문에 스크립트를 가져오는 22번째 줄에서 버전이 명시됩니다
3. 문서가 로드되면, processor.doLoad() 메서드가 실행됩니다.
JavaScript 코드
-------------

main.js에 있는 JavaScript 코드는 3개의 메서드로 구성됩니다.

### 크로마 키잉 플레이어 초기화

`doLoad()` 메서드는 문서가 최초에 로드될 때 호출됩니다. 이 메서드가 하는 일은 크로마 키잉 처리에서 쓰일 변수를 준비하고, 이벤트 리스너를 등록함으로써 사용자가 비디오 재생을 시작할 때 감지할 수 있도록 해줍니다.

```js
  var processor;

  processor.doLoad = function doLoad() {
    this.video = document.getElementById('video');
    this.c1 = document.getElementById('c1');
    this.ctx1 = this.c1.getContext('2d');
    this.c2 = document.getElementById('c2');
    this.ctx2 = this.c2.getContext('2d');
    let self = this;
    this.video.addEventListener('play', function() {
        self.width = self.video.videoWidth / 2;
        self.height = self.video.videoHeight / 2;
        self.timerCallback();
      }, false);
  },
```

이코드는 XHTML에서 중요한 요소인 비디오와 캔버스의 참조를 가져옵니다. 두 개의 캔버스에 대한 그래픽 컨텍스트의 참조도 가져옵니다. 이 참조들은 뒤에서 크로마 키잉 효과를 구현할 때 사용됩니다.

그리고 `addEventListener()`는 비디오가 재생을 시작하기 위해 호출되기 때문에 사용자가 재생 버튼을 누를 때 알림을 받습니다. 재생이 시작되면 이 코드는 비디오의 가로, 세로를 이등분 한 값을 가져오고(크로마 키잉 효과를 수행할 때 이등분 함), `timerCallback()` 메서드를 호출하여 비디오를 보고 시각적 효과를 계산하기 시작합니다.

### 타이머 콜백

타이머 콜백은 비디오가 재생되기 시작("재생" 이벤트가 발생)할 때 호출되는데, 매 프레임마다 키잉 효과를 주기 위해 주기적으로 호출 될 수 있도록 설정해 주어야 합니다.

```js
  processor.timerCallback = function timerCallback() {
    if (this.video.paused || this.video.ended) {
      return;
    }
    this.computeFrame();
    let self = this;
    setTimeout(function() {
        self.timerCallback();
      }, 0);
  },
```

콜백에서 하는 첫 번 째 일은 비디오가 재생되고 있는지 확인하는 것인데, 만약 그렇지 않다면 콜백은 아무 일도 하지 않고 즉시 반환됩니다.

그 후에 현재 비디오 프레임에서 크로마 키잉 효과를 주기 위한 `computeFrame()` 메서드를 호출합니다.

콜백에서 마지막으로 하는 일은 `setTimeout()`을 호출하여 가능한 한 빨리 `timerCallback()` 메서드를 다시 호출할 수 있도록 하는 것입니다. 실제로는, 비디오 프레임 속도에 대한 기반 지식으로 호출할 수 있도록 합니다.

### 비디오 프레임 데이터 조작

아래의 `computeFrame()` 메서드는 프레임 데이터를 가져와서 크로마 키잉 효과를 수행하는 역할을 합니다.

```js
processor.computeFrame = function computeFrame() {
  this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
  let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
  let l = frame.data.length / 4;

  for (let i = 0; i < l; i++) {
    let r = frame.data[i \* 4 + 0];
    let g = frame.data[i \* 4 + 1];
    let b = frame.data[i \* 4 + 2];
    if (g > 100 && r > 100 && b < 43) frame.data[i \* 4 + 3] = 0;
  }
  this.ctx2.putImageData(frame, 0, 0);
  return;
};
```

위 과정이 계속 호출 되면, 아래와 같이 비디오 요소에 가장 최근 프레임의 비디오 데이터가 표출됩니다.

![검은색 티셔츠를 입은 사람이 있고 배경색은 노란색인 비디오 요소의 한 프레임.](/ko/docs/Web/API/Canvas_API/Manipulating_video_using_canvas/video.png)

2번째 줄에서, 첫 번째 캔버스의 그래픽 컨텍스트 ctx1에 비디오 프레임이 복사 되는데, 원본의 절반 크기로 프레임을 그리기 위해 이전에 저장한 가로, 세로 값으로 지정합니다. 컨텍스트의 `drawImage()` 메서드에 비디오 요소를 전달하기만 하면 현재 비디오 프레임을 그릴 수 있습니다. 결과는 아래와 같습니다:

![위 사진을 축소한 사진입니다. 검은색 티셔츠를 입은 사람이 있고 배경색은 노란색인 비디오 요소의 한 프레임.](/ko/docs/Web/API/Canvas_API/Manipulating_video_using_canvas/sourcectx.png)

3번째 줄에서는 첫 번째 컨텍스트의 `getImageData()` 메서드를 호출해서 현재 비디오 프레임의 원시 그래픽 데이터 복사본을 가져옵니다. 이것은 조작할 수 있는 원시 32비트 픽셀 이미지 데이터를 제공합니다. 4번째 줄에서는 프레임의 이미지 데이터 전체 크기를 4로 나누어 이미지의 픽셀 수를 계산합니다.

6번째 줄에서 시작하는 `for` 문은 프레임의 픽셀을 스캔하여, 빨간색, 녹색, 파란색 값을 추출하여 사전에 정의된 숫자와 비교합니다. 이 숫자는 `foo.png`에서 가져온 배경 이미지로 대체될 녹색 스크린 영역을 감지합니다.

녹색 스크린이라고 간주된 매개변수 내의 프레임 이미지 데이터의 모든 픽셀은 투명해질 수 있도록 알파값이 0으로 대체됩니다. 결과적으로 최종 이미지는 100% 투명해진 녹색 스크린 영역을 갖게 되고, 13번째 줄에서 대상 컨텍스트에 고정된 배경 위로 올려져 그려집니다.

결과 이미지는 아래와 같습니다:

![비디오 요소의 단일 프레임은 위의 사진과 같은 사람이 검은색 티셔츠를 입고 있는 것을 보여주지만 배경은 Firefox 로고입니다.](/ko/docs/Web/API/Canvas_API/Manipulating_video_using_canvas/output.png)

이 과정은 비디오가 재생될 때마다 반복되므로, 매 프레임마다 처리되어 크로마 키잉 효과가 나타나는 것입니다.

전체 소스코드 보기

더 보기
----

* 오디오와 비디오 사용하기 (en-US)


캔버스 튜토리얼 - Web API

캔버스 튜토리얼
========

![](/ko/docs/Web/API/Canvas_API/Tutorial/canvas_tut_examples.jpg)
**`<canvas>`**는 HTML 요소 중 하나로서, 스크립트(보통은 JavaScript)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은 복잡할 수도 있는) 애니메이션을 만드는 데에 사용될 수 있습니다. 오른쪽에 보이는 이미지들은 앞으로 설명하게 될 **`<canvas>`**를 사용하여 만들 수 있는 것들의 일부입니다.

이 튜토리얼은 `<canvas>` 요소를 사용하여 2D 그래픽을 어떻게 그리는지 기초부터 설명합니다. 예제들을 통하여 캔버스로 할 수 있는 것이 무엇인지 알려주며, 바로 사용할 수 있도록 코드도 제공합니다.

`<canvas>`는 Apple의 Webkit에 처음 도입되어 Mac OS X 대시보드(Dashboard)에 사용되었고, 이후 다른 브라우저에도 구현되어 왔습니다. 현재 대부분의 주요 브라우저에서 지원됩니다.

시작하기 전 알아두어야 할 것
----------------

`<canvas>` 요소를 사용하는 것이 어려운 일은 아니지만, HTML과 JavaScript에 대한 기본 지식을 갖추고 있어야 합니다. 몇몇 오래된 브라우저는 `<canvas>` 요소를 지원하지 않지만, 최근 버전의 주요 브라우저들은 모두 지원하고 있습니다. 캔버스의 크기는 300px \* 150px (너비 \* 높이)가 초기 설정값이며, HTML `height`와 `width` 속성을 사용하여 바꿀 수 있습니다. 캔버스에 그림을 그리려면 JavaScript 컨텍스트 오브젝트를 사용하며, 즉석에서 그림을 생성할 수 있습니다.

튜토리얼 내용
-------

* 캔버스(Canvas) 기본 사용법
* 캔버스에 도형 그리기
* 스타일과 색 적용하기
* 텍스트 그리기
* 이미지 사용하기 (en-US)
* 모양 변환
* 도형 합성
* 애니메이션 기본
* 애니메이션 고급
* 픽셀 다루기 (en-US)
* Hit 영역과 accessibility
* 캔버스 최적화
* 마무리
같이 보기
-----

* 캔버스 주제 페이지
* Adobe 일러스트레이터와 캔버스 플러그인
* HTML5 캔버스 튜토리얼
* 다음 »


발전된 애니메이션 - Web API

발전된 애니메이션
=========

* « 이전
* 다음 » (en-US)

마지막 챕터에서 우리는 몇가지 간단한 애니메이션들을 만들었고 이제 이것들을 어떻게 움직이게 하는지 안다. 이 챕터에서 우리는 각각의 모션들을 자세히 살펴보고 애니메이션을 더 발전시키기 위해 몇가지 물리 동작을 추가할 것이다.

공 그리기
-----

우리는 애니메이션 공부를 위해 공을 사용할 것이다. 먼저 캔버스에 공을 그려보자. 다음 코드를 통해 준비해보자.

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

언제나처럼, 우리는 context를 먼저 그려야 한다. 공을 그리기 위해 우리는 캔버스에 그림을 그리기 위한 프로퍼티와 `draw()` 메소드를 가진 `ball` 오브젝트를 생성할 것이다.

```js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var ball = {
  x: 100,
  y: 100,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

ball.draw();
```

특이할 건 없다. 이공은 사실상 간단한 원이고 그리는 방법은 다음`arc()` (en-US) 메소드에서 참고할 수 있다.

속도 추가하기
-------

우리한테는 이제 공이 있다. 이제 이 튜토리얼 마지막 챕터에서 배웠던 것과 같은 기본 애니메이션을 추가할 준비가 되었다. 다시 한 번, 애니메이션 컨트롤은 `window.requestAnimationFrame()`가 도와주 것이다. 공은 위치에 속도 벡터를 추가하여 움직일 수 있게 된다. 각각의 프레임에, 우리는clear (en-US)를 캔버스에 주어 오래된 원을 이전 프래임에서 지운다.

```js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;

var ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", function (e) {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
});

ball.draw();
```

경계
--

경계 충돌 테스트의 필요 없이 우리가 만든 공은 캔버스 밖으로 빠르게 빠져나갈 것입니다. 우리는 공의 `x` 와 `y` 위치가 캔버스 차원을 빠져나갔는지 체크해서 방향과 속도를 바꿔주어야 합니다. 그러기 위해서 우리는 `draw` 메소드에 다음 확인사항을 추가할 것입니다.:

```js
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
  ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
  ball.vx = -ball.vx;
}
```

### First demo

이제 동작을 확인해 봅시다. 시작하려먼 마우스를 캔버스 안으로 움직여 주세요.
```

<canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas>
```

```
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;

var ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", function (e) {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
});

ball.draw();
```

가속
--

움직임을 좀 더 리얼하게 만들기 위해, 우리는 속도를 다음과 같이 줄 겁니다. 예를 들어:

```js
ball.vy \*= 0.99;
ball.vy += 0.25;
```

이것은 각 프레임의 세로 속도를 줄여주어, 공이 결국 바닥에서 튀게 만듭니다.
```

<canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas>
```

```
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;

var ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  ball.vy \*= 0.99;
  ball.vy += 0.25;

  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", function (e) {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
});

ball.draw();
```

후행 효과
-----

지금까지 우리는 `clearRect` (en-US)메소드를 사용해서 이전 프레임을 지웠다. 만약 당신이 `fillRect` (en-US)르 사용하여 약간 투명도를 준다면, 쉽게 후행 효과(Trailing effect)를 만들 수 있을 것이다.

```js
ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
```

```
<canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas>
```

```
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;

var ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  ball.vy \*= 0.99;
  ball.vy += 0.25;

  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", function (e) {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
});

ball.draw();
```

마우스 컨트롤 추가하기
------------

공을 컨트롤 하기 위해, 우리는 `mousemove` (en-US) 이벤트를 사용하여 마우스를 따라오게 할 것이다. `click` 이벤트를 통해 공을 놓으면 다시 공이 튀도록 할 것이다.
```

<canvas id="canvas" style="border: 1px solid" width="600" height="300"></canvas>
```

```js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var raf;
var running = false;

var ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 1,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI \* 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function clear() {
  ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
}

function draw() {
  clear();
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mousemove", function (e) {
  if (!running) {
    clear();
    ball.x = e.clientX;
    ball.y = e.clientY;
    ball.draw();
  }
});

canvas.addEventListener("click", function (e) {
  if (!running) {
    raf = window.requestAnimationFrame(draw);
    running = true;
  }
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
  running = false;
});

ball.draw();
```

마우스로 공을 움직이고, 클릭을 통해 놓아보자.

Breakout
--------

이 짧은 챕터는 발전된 애니메이션을 만들기 위한 조금의 기술을 설명했다. 여기에 더 많은 것들이 있다! 노나 벽돌을 추가해서 이 튜토리얼을 Breakout 으로 발전시키는 건 어떨까? Game development에서 게임에 관련된 글들을 찾아보자.

더보기
---

* `window.requestAnimationFrame()`
* Efficient animation for web games (en-US)
* « 이전
* 다음 » (en-US)


캔버스 최적화 - Web API

캔버스 최적화
=======

* « 이전
* 다음 »

`<canvas>` 엘리먼트는 웹에서 2D 그래픽을 렌더링하는 데 가장 널리 사용되는 도구 중 하나입니다. 그러나 웹 사이트와 앱이 Canvas API를 최대한으로 밀면 성능이 저하되기 시작합니다. 그러나 웹 사이트 및 앱이 Canvas API를 한계치까지 사용하면 성능이 저하되기 시작합니다. 이 글에서는 그래픽이 잘 동작하도록 보장하기 위해 캔버스 엘리먼트의 사용을 최적화하기위한 제안 사항을 제공합니다.

성능 팁
----

다음은 캔버스 성능을 개선하기 위한 팁 모음입니다.

### 캔버스에 표시되지 않는 비슷한 원시 혹은 반복 객체를 미리 그려라

만약 당신이 캔버스에 애니메이션 프레임을 그리면서 반복적인 작업이 발견된다면, 눈에 보이지 않는 숨겨진 캔버스 요소를 새로 만들고 그 캔버스에 미리 그려 넣는 방법을 고려하세요. 그렇게 하면 필요한 순간에 숨긴 캔버스에 그려진 이미지를 다시 주 캔버스 이미지에 그려넣어, 불필요한 렌더링 반복 작업을 줄여 성능 향상을 꾀할 수 있습니다.

```js
myCanvas.offscreenCanvas = document.createElement("canvas");
myCanvas.offscreenCanvas.width = myCanvas.width;
myCanvas.offscreenCanvas.height = myCanvas.height;

myCanvas.getContext("2d").drawImage(myCanvas.offScreenCanvas, 0, 0);
```

### 부동 소수점 좌표를 피하고 대신 정수를 사용하라

정수값들 없이 캔버스 상의 객체를 렌더링할 때 부가적인 픽셀 렌더링이 발생합니다.

```js
ctx.drawImage(myImage, 0.3, 0.5);
```

이렇게하면 앤티 앨리어싱(anti-aliasing) 효과를 만들기 위해 브라우저에서 추가 연산을 수행해야합니다. 예제에서 이를 방지하려면 `Math.floor()`를 사용하여 `drawImage()` (en-US) 호출에 사용된 모든 좌표를 반올림해야합니다.

### `drawImage`에서 이미지 크기를 조정하지 마라

`drawImage()` (en-US)에서 즉시 크기를 조정하지 말고 다양한 이미지 크기를 오프스크린(offscreen) 캔버스에 캐시하십시오.

### 복잡한 장면에 여러 개의 레이어 캔버스를 사용하라

어플리케이션에서 일부 객체는 자주 이동하거나 변경해야하지만 다른 객체는 상대적으로 고정 위치에 남아야 합니다. 이런 상황에서 대응 가능한 최적화는 여러 `<canvas>` 엘리먼트를 사용하여 항목을 겹쳐서 만드는 것입니다.

예를 들어 상단에 UI, 중간에 게임 플레이 액션, 하단에 정적 배경이있는 게임이 있다고 가정 해 보겠습니다. 이 경우 게임을 세 개의 `<canvas>` 레이어로 나눌 수 있습니다. UI는 사용자 입력시에만 변경되며 게임 플레이 레이어는 모든 새 프레임마다 변경되며 배경은 일반적으로 변경되지 않습니다.

```html
<div id="stage">
  <canvas id="ui-layer" width="480" height="320"></canvas>
  <canvas id="game-layer" width="480" height="320"></canvas>
  <canvas id="background-layer" width="480" height="320"></canvas>
</div>

<style>
 #stage {
 width: 480px;
 height: 320px;
 position: relative;
 border: 2px solid black;
 }

 canvas {
 position: absolute;
 }
 #ui-layer {
 z-index: 3;
 }
 #game-layer {
 z-index: 2;
 }
 #background-layer {
 z-index: 1;
 }
</style>
```

### 큰 배경 이미지는 일반 CSS를 사용하라

정적 배경 이미지가있는 경우 CSS `background` 속성을 사용하여 일반 `<div>` (en-US) 요소에 그릴 수 있으며 캔버스 아래에 배치 할 수 있습니다. 이렇게하면 모든 틱 마다 배경을 캔버스에 렌더링 할 필요가 없어집니다.

### CSS 변환(transform)을 사용하여 캔버스 크기 조정하라

CSS 변환(transform) (en-US)은 GPU를 사용하기 때문에 더 빠릅니다. 가장 좋은 경우는 캔버스를 스케일링하지 않거나, 큰 캔버스를 축소하기보다 작은 캔버스를 확대하는 것입니다.

```js
var scaleX = window.innerWidth / canvas.width;
var scaleY = window.innerHeight / canvas.height;

var scaleToFit = Math.min(scaleX, scaleY);
var scaleToCover = Math.max(scaleX, scaleY);

stage.style.transformOrigin = "0 0"; //scale from top left
stage.style.transform = "scale(" + scaleToFit + ")";
```

### 투명도를 사용하지 마라

응용 프로그램이 캔버스를 사용하고 투명 배경을 필요로하지 않는 경우 `HTMLCanvasElement.getContext()`를 사용하여 드로잉 컨텍스트를 만들 때 alpha 옵션을 false로 설정합니다. 이 정보는 렌더링을 최적화하기 위해 브라우저에서 내부적으로 사용할 수 있습니다.

```js
var ctx = canvas.getContext("2d", { alpha: false });
```

### 추가 팁들

* 배치 캔버스를 함께 호출합니다. 예를 들어 여러 개의 개별 선 대신 다각형 선을 그립니다.
* 불필요한 캔버스 상태 변경을 피하십시오.
* 화면의 차이만 렌더링하고 완전히 새로운 상태로 렌더링하지 마십시오.
* 가능하면 `shadowBlur` (en-US) 속성을 사용하지 마십시오.
* 가능하면 텍스트 렌더링을 피하십시오.
* 캔버스를 지우는 여러 가지 방법을 시도해보십시오 (`clearRect()` (en-US) vs. `fillRect()` (en-US) vs. 캔버스 크기 조정).
* 애니메이션에서는 `window.setInterval()` (en-US) 대신 `window.requestAnimationFrame()`을 사용하십시오.
* 무거운 물리 연산 라이브러리를 주의하십시오.
See also
--------

* Improving HTML5 Canvas Performance – HTML5 Rocks
* Optimizing your JavaScript game for Firefox OS – Mozilla Hacks
* « 이전
* 다음 »


변형 (transformations) - Web API

변형 (transformations)
====================

* « 이전 (en-US)
* 다음 »

이 튜토리얼에 앞서 canvas 그리드와 **좌표 공간**에 대해 알아 보았습니다. 지금까지는 기본적인 그리드를 사용해 요구에 맞추어 전체 canvas의 크기를 바꾸기만 했습니다. Transformation(변형)에는 그리드를 원점에서 다른 위치로 옮기고, 회전하며, 확대·축소까지 하는 더 강력한 방법들이 있습니다.

상태(state)의 저장과 복원
-----------------

변형(transformation) 메소드을 살펴보기 전에 두 가지 다른 메소드를 보도록 하지요. 일단 여러분이 더 복잡한 그림(drawings)을 그리기 시작하면 반드시 있어야 하는 메소드들입니다.

`save()` (en-US)

canvas의 모든 상태를 저장합니다.

`restore()` (en-US)

가장 최근에 저장된 canvas 상태를 복원합니다.

Canvas 상태는 스택(stack)에 쌓입니다. `save()` 메소드가 호출될 때마다 현재 drawing 상태가 스택 위로 푸시됩니다. drawing 상태는 다음과 같이 이루어집니다.

* 이전부터 적용된 변형(가령, `translate`과 `rotate`와 `scale` 같은 – 아래의 내용을 보세요).
* 다음 속성(attributes)의 현재 값:
 `strokeStyle` (en-US), `fillStyle` (en-US), `globalAlpha` (en-US), `lineWidth` (en-US), `lineCap` (en-US), `lineJoin` (en-US), `miterLimit` (en-US), `lineDashOffset` (en-US), `shadowOffsetX` (en-US), `shadowOffsetY` (en-US), `shadowBlur` (en-US), `shadowColor` (en-US), `globalCompositeOperation`, `font` (en-US), `textAlign` (en-US), `textBaseline` (en-US), `direction` (en-US), `imageSmoothingEnabled` (en-US).
* 현재의 clipping path, 이것은 다음 섹션에서 다루겠습니다.

여러분은 원하는 만큼 `save()` 메소드를 많이 호출할 수 있습니다. `restore()` 메소드를 호출할 때마다 마지막으로 저장된 상태가 스택에서 튀어나와 저장된 설정들을 모두 복원시킵니다.

### `save`와 `restore` canvas 상태(state) 예제

사각형 세트를 연이어 그려서 drawing 상태를 가진 스택이 어떻게 기능하는지를 이 예제에서 설명하고자 합니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  ctx.fillRect(0, 0, 150, 150); // 기본 설정으로 사각형을 그리기
  ctx.save(); // 기본 상태를 저장하기

  ctx.fillStyle = "#09F"; // 설정 변경하기
  ctx.fillRect(15, 15, 120, 120); // 새로운 설정으로 사각형 그리기

  ctx.save(); // 현재 상태 저장하기
  ctx.fillStyle = "#FFF"; // 설정 변경하기
  ctx.globalAlpha = 0.5;
  ctx.fillRect(30, 30, 90, 90); // 새로운 설정으로 사각형 그리기

  ctx.restore(); // 이전 상태 복원하기
  ctx.fillRect(45, 45, 60, 60); // 복원된 설정으로 사각형 그리기

  ctx.restore(); // 초기 상태를 복원하기
  ctx.fillRect(60, 60, 30, 30); // 복원된 설정으로 사각형 그리기
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

첫 단계로 기본 설정으로 커다란 사각형을 그립니다. 그다음에는 이 상태를 저장하고 fill color를 바꿉니다. 그런 후에 두 번째이자 크기가 더 작은 파란 사각형을 그리고 그 상태를 저장합니다. 다시 한번 일부 drawing 설정을 바꾸고 세 번째 반투명한 흰 사각형을 그립니다.

여기까지는 이전 섹션에서 했던 작업과 매우 유사합니다. 하지만 일단 첫 번째 `restore()` 문을 호출하면 스택에서 맨 위의 drawing 상태가 지워지고 설정이 복원됩니다. 만일 `save()`로 저장하지 않았다면, 이전 상태로 되돌리기 위해 fill color와 투명도를 일일이 바꿔주어야 했을 것입니다. 두 속성이라서 간단했을 테지만 그보다 더 많았으면 코드가 급속히 길어졌겠지요.

두 번째 `restore()`문이 호출될 때, 초기 상태( 처음으로 `save`를 호출하기 전에 설정한 상태)가 복원되고 마지막 사각형은 한번 더 검게 그려집니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

이동(translating)
---------------

![](/ko/docs/Web/API/Canvas_API/Tutorial/Transformations/canvas_grid_translate.png)우리가 살펴볼 첫 번째 변형 메소드는 `translate()`입니다. 이 메소드는 그리드에서 canvas를 원점에서 다른 점으로 옮기는 데 사용됩니다.

`translate(x, y)` (en-US)

그리드에서 canvas와 그 원점을 이동합니다. `x`는 이동시킬 수평 거리를 가리키고, `y`는 그리드에서 수직으로 얼마나 멀리 떨어지는지를 표시합니다.

변형하기 전에 canvas 상태를 저장하는 것이 좋습니다. 대다수의 경우, 원래 상태로 되돌리려고 역이동(reverse translation)을 시키는 것보다 `restore` 메소드를 호출하는 것이 더욱 간편합니다. 게다가 루프(loop) 안에서 이동하는 거라면 canvas 상태를 저장하고 복원하지 마세요. canvas 모서리 밖에서 그려지는 바람에 drawing의 일부를 잃어버리게 될지 모릅니다.

### `translate` 예제

이 예제에서 canvas 원점의 이동에 관한 좋은 점을 일부 보여드리겠습니다. `translate()` 메소드를 쓰지 않으면 모든 사각형은 같은 위치 (0, 0)에 그려집니다. 또한, `translate()` 메소드는 사각형을 `fillRect()` function에서 좌표를 일일이 적으며 바꾸지 않아도 어디에나 위치할 수 있게 해줍니다. 이렇게 하면 이해하고 사용하기가 좀 더 쉽습니다.

`draw()` function에서 두 개의 루프(loops)를 이용해 `fillRect()` function을 아홉 번 호출합니다. 루프마다 canvas가 이동하고 사각형이 그려지며, canvas는 원래 상태로 되돌아 갑니다. `fillRect()`로의 호출이 `translate()`에 의지해 drawing 위치를 바꾸는데 어떻게 매번 같은 좌표를 사용하는지 눈여겨 보세요.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  for (var i = 0; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
      ctx.save();
      ctx.fillStyle = "rgb(" + 51 \* i + ", " + (255 - 51 \* i) + ", 255)";
      ctx.translate(10 + j \* 50, 10 + i \* 50);
      ctx.fillRect(0, 0, 25, 25);
      ctx.restore();
    }
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

회전(rotating)
------------

![](/ko/docs/Web/API/Canvas_API/Tutorial/Transformations/canvas_grid_rotate.png)두 번째 변형 메소드는 `rotate()`입니다. canvas를 현재의 원점 둘레로 회전하는 데 사용합니다.

`rotate(angle)` (en-US)

canvas를 현재 원점을 기준으로 라디안의 `angle` 숫자만큼 시계방향으로 회전시킵니다.

회전의 중심점은 언제나 canvas 원점입니다. 중심점을 바꾸려면 `translate()` 메소드를 써서 canvas를 이동해야 합니다.

### `rotate` 예제

이 예제는 사각형을 canvas 원점에서 먼저 회전하고 그다음에 `translate()`의 도움을 받아 사각형 자체의 중심에서 회전하는 데 `rotate()`를 사용합니다.

**참고:** **주의**: 각도의 단위는 도(degree)가 아닌 라디안(radian)입니다. 변환하려면 `radians = (Math.PI/180)*degrees`.를 사용합니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 좌측 사각형, canvas 원점에서 회전하기
  ctx.save();
  // 파란 사각형
  ctx.fillStyle = "#0095DD";
  ctx.fillRect(30, 30, 100, 100);
  ctx.rotate((Math.PI / 180) \* 25);
  // 회색 사각형
  ctx.fillStyle = "#4D4E53";
  ctx.fillRect(30, 30, 100, 100);
  ctx.restore();

  // 우측 사각형, 사각형 중심에서 회전하기
  // 파란 사각형 그리기
  ctx.fillStyle = "#0095DD";
  ctx.fillRect(150, 30, 100, 100);

  ctx.translate(200, 80); // 사각형 중심으로 이동하기
  // x = x + 0.5 \* width
  // y = y + 0.5 \* height
  ctx.rotate((Math.PI / 180) \* 25); // 회전
  ctx.translate(-200, -80); // 예전 위치로 이동하기

  // 회색 사각형 그리기
  ctx.fillStyle = "#4D4E53";
  ctx.fillRect(150, 30, 100, 100);
}
```

사각형 자체의 중심 둘레로 회전하려면 사각형의 중심으로 canvas를 옮기세요. 그런 후에 canvas를 회전하고, 그 canvas를 0, 0로 되돌려 이동합니다. 그다음에 사각형을 그리세요.
```

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

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

확대·축소(scaling)
--------------

다음 변형 메소드는 확대·축소(scaling)입니다. canvas 그리드에서 단위(units)를 키우거나 줄이는 데 사용합니다. 이는 벡터 모양과 비트맵(bitmaps) 요소를 축소하거나 확대해서 그리는 데 사용될 수 있습니다.

`scale(x, y)` (en-US)

canvas 단위를 수평으로 x만큼, 수직으로 y만큼 크기를 확대·축소합니다. 둘의 매개 변수는 실수입니다. 1.0보다 작은 값이면 단위의 크기를 축소하고, 1.0보다 큰 값이면 단위의 크기를 확대합니다. 값이 1.0이면 단위의 크기는 그대로입니다.

음수를 이용해서 축을 대칭 시킬 수 있습니다(가령 `translate(0,canvas.height); scale(1,-1);`로 쓰는 것처럼 말이죠. 좌측 하단 모서리에 있는 원점을 이용한, 잘 알려진 카르테시안 좌표계(Cartesian coordinate)인 것이지요.

기본적으로 canvas에서 하나의 단위는 정확히 1픽셀입니다. 예를 들어 0.5라는 확대·축소 비율을 적용한다면, 결과로 나오는 단위는 0.5 픽셀이 될 것이고, 고로 모양도 절반 크기로 그려질 것입니다. 이런 방식으로 크기 비율을 2.0으로 잡으면 단위 크기가 확대되어 하나의 단위는 이제 2픽셀이 되겠지요. 이 결과로 모양은 그만큼 2배로 커집니다.

### `scale` 예제

마지막 예제로 다양한 확대·축소 비율을 이용해 모양을 그려보겠습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 간단하지만 확대·축소 비율을 적용한 사각형 그리기
  ctx.save();
  ctx.scale(10, 3);
  ctx.fillRect(1, 10, 10, 10);
  ctx.restore();

  // 수평으로 대칭하기
  ctx.scale(-1, 1);
  ctx.font = "48px serif";
  ctx.fillText("MDN", -135, 120);
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

변형(transforms)
--------------

마지막으로, 다음의 변형(transform) 메소드들은 변환 행렬(transformation matrix)로 변경할 사항을 즉시 적용할 수 있습니다.

`transform(a, b, c, d, e, f)` (en-US)

인수(arguments)에 표시된 행렬을 이용해 현재 변환 행렬을 곱합니다. 변환 행렬은 다음과 같이 작성됩니다.

[

a

c

e

b

d

f

0

0

1

]

\left[ \begin{array}{ccc} a & c & e \ b & d & f \ 0 & 0 & 1 \end{array} \right]

만일 인수 중에 `Infinity`가 있다면, 변환 행렬은 예외 처리하는 메소드 대신에 반드시 infinite로 표시되어야 합니다.

이 function의 매개 변수들은 다음과 같습니다.

`a (m11)`

수평으로 확대·축소하기

*`b (m12)`*

수평으로 비스듬히 기울이기

`c (m21)`

수직으로 비스듬히 기울이기

`d (m22)`

수직으로 확대·축소하기

`e (dx)`

수평으로 이동하기

`f (dy)`

수직으로 이동하기

`setTransform(a, b, c, d, e, f)` (en-US)

현재 변형 상태를 단위 행렬로 재설정하고 나서 동일한 인수로 `transform()` 메소드를 적용합니다. 이는 기본적으로 현재의 변형을 무효로 한 후에 명시된 변형으로 바뀌는데, 한번에 모든 게 진행됩니다.

`resetTransform()` (en-US)

현재 변형 상태를 단위 행렬로 재설정합니다. 이는 `ctx.setTransform(1, 0, 0, 1, 0, 0);` 호출과 같습니다.

### `transform`과 `setTransform` 예제

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  var sin = Math.sin(Math.PI / 6);
  var cos = Math.cos(Math.PI / 6);
  ctx.translate(100, 100);
  var c = 0;
  for (var i = 0; i <= 12; i++) {
    c = Math.floor((255 / 12) \* i);
    ctx.fillStyle = "rgb(" + c + ", " + c + ", " + c + ")";
    ctx.fillRect(0, 0, 100, 10);
    ctx.transform(cos, sin, -sin, cos, 0, 0);
  }

  ctx.setTransform(-1, 0, 0, 1, 100, 100);
  ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
  ctx.fillRect(0, 50, 100, 100);
}
```

```
<canvas id="canvas" width="200" height="250"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

* « 이전 (en-US)
* 다음 »