diff options
Diffstat (limited to 'files/ko/web/api/streams_api')
-rw-r--r-- | files/ko/web/api/streams_api/index.html | 152 | ||||
-rw-r--r-- | files/ko/web/api/streams_api/using_readable_streams/index.html | 317 | ||||
-rw-r--r-- | files/ko/web/api/streams_api/컨셉/index.html | 115 |
3 files changed, 584 insertions, 0 deletions
diff --git a/files/ko/web/api/streams_api/index.html b/files/ko/web/api/streams_api/index.html new file mode 100644 index 0000000000..96009dad26 --- /dev/null +++ b/files/ko/web/api/streams_api/index.html @@ -0,0 +1,152 @@ +--- +title: Streams API +slug: Web/API/Streams_API +tags: + - API + - Experimental + - Landing + - Reference + - Streams + - TopicStub +translation_of: Web/API/Streams_API +--- +<div>{{SeeCompatTable}}{{APIRef("Streams")}}</div> + +<p class="summary">Streams API는 Javascript를 이용해 네트워크를 통해 전송된 데이터 스트림에 접근하여 원하는 대로 처리가 가능한 API를 제공합니다.</p> + +<h2 id="개념과_사용법">개념과 사용법</h2> + +<p>Streaming은 네트워크를 통해 받은 리소스를 작은 조각으로 나누어, Bit 단위로 처리합니다. 이는 브라우저가 수신한 자원을 웹페이지에 표현할 때 주로 사용하는 방법입니다. — Video buffer는 재생되기 전 천천히 채워지며 가끔 이미지도 천천히 로딩되는 것을 보실 수 있을 겁니다..</p> + +<p>하지만 Javascript에서는 지금까지 불가능했습니다. 이전에는 (비디오나 텍스트 파일 등의) 리소스를 처리하기 위해서 우선, 전체 파일을 다운로드 받은 후 알맞은 포맷으로 파싱된 후에야, 전송된 전체 데이터를 처리할 수 있었습니다.</p> + +<p>With Javascript에 Stream이 도입된 후에는 모든 것이 바뀌었는데, 이제 Buffer, String 또는 blob 없이도 Javascript를 통해 Raw Data를 비트 단위로 처리할 수 있습니다.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15817/Concept.png" style="display: block; height: 382px; margin: 0px auto; width: 1000px;"></p> + +<p>장점은 또 있습니다 — Stream의 시작 또는 종료를 감지할 수 있으며, 여러 stream을 엮어서 에러를 처리하거나 필요한 경우 stream을 취소할 수도 있습니다. 또한 stream이 읽어들이는 속도에 따라 반응할 수도 있지요.</p> + +<p>Stream의 주요한 기본 사용법은 응답 데이터를 stream으로 만드는 것입니다. <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch request</a>를 통해 정상적으로 전송된 응답 {{domxref("Body")}}는 {{domxref("ReadableStream")}}로 표현 가능합니다. 또한 {{domxref("ReadableStream.getReader()")}}를 통해 Reader 객체를 얻어 데이터를 읽을 수도 있으며, {{domxref("ReadableStream.cancel()")}}로 Stream을 취소하는 것 등이 가능합니다.</p> + +<p>조금 더 복잡한 사용법은 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}} 생성자를 통해 사용자가 직접 Stream을 생성하는 것입니다. 예를 들자면 <a href="/en-US/docs/Web/API/Service_Worker_API">service worker</a>에 전달할 데이터를 Stream으로 만들 수도 있습니다.</p> + +<p>{{domxref("WritableStream")}}을 사용하면 Stream에 데이터를 쓰는 것도 가능합니다..</p> + +<div class="note"> +<p><strong>Note</strong>: <a href="/en-US/docs/Web/API/Streams_API/Concepts">Streams API concepts</a>, <a href="/en-US/docs/Web/API/Streams_API/Using_readable_streams">Using readable streams</a>, <a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">Using writable streams</a> — 페이지에서 stream에 관한 더 자세한 이론과 예제를 찾을 수 있습니다.</p> +</div> + +<h2 id="스트림_인터페이스">스트림 인터페이스</h2> + +<h3 id="읽기_스트림(Readable_streams)">읽기 스트림(Readable streams)</h3> + +<dl> + <dt>{{domxref("ReadableStream")}}</dt> + <dd>데이터 읽기 스트림을 나타냅니다. <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a>의 결과 스트림이나 개발자가 정의한 스트림(예, 커스텀 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}} 클래스)등을 핸들링할 수 있습니다.</dd> + <dt>{{domxref("ReadableStreamDefaultReader")}}</dt> + <dd>네트워크(예, fetch 요청)등에서 전달된 스트림 데이터를 처리하는 기본 Reader를 반환합니다.</dd> + <dt>{{domxref("ReadableStreamDefaultController")}}</dt> + <dd>{{domxref("ReadableStream")}}의 상태나 내부 큐를 컨트롤 할 수 있는 기본 컨트롤러를 반환합니다. 기본 컨트롤러는 Byte 스트림 외의 스트림에만 해당합니다.</dd> +</dl> + +<h3 id="쓰기_스트림(writable_streams)">쓰기 스트림(writable streams)</h3> + +<dl> + <dt>{{domxref("WritableStream")}}</dt> + <dd>목적지 스트림에 데이터를 쓰기 위한 표준 추상 인터페이스를 제공하는 객체입니다. 이 객체는 내장 백프레셔와 큐잉을 구현하고 있습니다.</dd> + <dt>{{domxref("WritableStreamDefaultWriter")}}</dt> + <dd>쓰기 스트림에 데이터 조각들을 쓰기 위한 기본 Writer 객체를 반환한다.</dd> + <dt>{{domxref("WritableStreamDefaultController")}}</dt> + <dd>{{domxref("WritableStream")}} 상태를 컨트롤하는 기본 컨트롤러를 반환한다. <code>WritableStream</code>을 생성하면 해당 스트림을 컨트롤 하기 위해 <code>WritableStreamDefaultController</code> 인스턴스가 내부적으로 생성된다.</dd> +</dl> + +<h3 id="관련_스트림_API와_기능">관련 스트림 API와 기능</h3> + +<dl> + <dt>{{domxref("ByteLengthQueuingStrategy")}}</dt> + <dd>스트림을 생성할 때 기본으로 사용 할 내장 byte length queuing strategy를 제공합니다.</dd> + <dt>{{domxref("CountQueuingStrategy")}}</dt> + <dd>스트림을 생성할 때 기본적으로 사용 할 내장 chunk counting queuing strategy를 제공합니다..</dd> +</dl> + +<h3 id="외부_API_확장">외부 API 확장</h3> + +<dl> + <dt>{{domxref("Request")}}</dt> + <dd>새 <code>Request</code> 객체가 생성될 때 <code>RequestInit</code> 딕셔너리의 <code>body</code>에 {{domxref("ReadableStream")}} 를 전달할 수 있습니다. 이 <code>Request</code>는 {{domxref("WindowOrWorkerGlobalScope.fetch()")}} 에 전달되에서 스트림을 fetch하는데 사용됩니다.</dd> + <dt>{{domxref("Body")}}</dt> + <dd><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch request</a> 성공 시 기본적으로 {{domxref("Body")}} {{domxref("ReadableStream")}}로 제공되며, reader를 붙여 데이터를 읽어들일 수 있습니다.</dd> +</dl> + +<h3 id="ByteStream_관련_인터페이스">ByteStream 관련 인터페이스</h3> + +<div class="warning"> +<p><strong>중요</strong>: 아래 항목들은 아직 구현된 곳이 없으며, 스펙의 세부 사항이 구현할 수 있을 만큼 충분한 지 논의가 진행중입니다. 추후 변경될 수 있습니다.</p> +</div> + +<dl> + <dt>{{domxref("ReadableStreamBYOBReader")}}</dt> + <dd>개발자가 직접 제공하여 stream data를 읽을 수 있는 BYOB ("bring your own buffer") reader를 표현합니다. (예 커스텀 {{domxref("ReadableStream.ReadableStream", "ReadableStream()")}} 생성자).</dd> + <dt>{{domxref("ReadableByteStreamController")}}</dt> + <dd>{{domxref("ReadableStream")}}의 상태와 내부 큐를 컨트롤 하는 컨트롤러 객체를 표현합니다. Byte stream 컨트롤러는 byte stream을 위한 컨트롤러입니다.</dd> + <dt>{{domxref("ReadableStreamBYOBRequest")}}</dt> + <dd>{{domxref("ReadableByteStreamController")}}의 request를 표현합니다.</dd> +</dl> + +<h2 id="예제">예제</h2> + +<p>Streams API 문서와 참조할 만한 예제를 함께 작성하였습니다 — <a href="https://github.com/mdn/dom-examples/tree/master/streams">mdn/dom-examples/streams</a> 를 참조하세요. 예제는 아래와 같습니다.:</p> + +<ul> + <li><a href="http://mdn.github.io/dom-examples/streams/simple-pump/">Simple stream pump</a>: ReadableStream에서 어떻게 데이터를 읽어들여 다른 곳으로 전달하는지 보여줍니다.</li> + <li><a href="http://mdn.github.io/dom-examples/streams/grayscale-png/">Grayscale a PNG</a>: PNG file의 ReadableStream을 통해 grayscale로 변경하는 방법을 보여줍니다..</li> + <li><a href="http://mdn.github.io/dom-examples/streams/simple-random-stream/">Simple random stream</a>: 커스텀 스트림을 통해 무작위 문자열을 생성하고, 데이터 청크로 큐잉한 뒤, 다시 읽어들이는 방법에 대해 설명합니다.</li> + <li><a href="http://mdn.github.io/dom-examples/streams/simple-tee-example/">Simple tee example</a>: 이 예제는 simple random stream 예제를 확장하여, 스트림을 분할하고 각 스트림이 독립적으로 데이터를 읽는 방법을 보여줍니다.</li> + <li><a href="http://mdn.github.io/dom-examples/streams/simple-writer/">Simple writer</a>: Writable stream에 데이터를 쓰는 방법을 설명하고, 스트림 데이터를 디코드하여 UI로 표현하는 방법을 보옂부니다..</li> + <li><a href="http://mdn.github.io/dom-examples/streams/png-transform-stream/">Unpack chunks of a PNG</a>: <a href="https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeThrough"><code>pipeThrough()</code></a> 을 통해 PNG file을 PNG 청크 스트림으로 변환하는 방식으로 ReadableStream을 다른 데이터 타입 스트림으로 전환하는 방법을 설명합니다.</li> +</ul> + +<p>다른 개발자의 예제:</p> + +<ul> + <li><a href="https://fetch-progress.anthum.com/">Progress Indicators with Streams, Service Workers, & Fetch</a>.</li> +</ul> + +<h2 id="Specifications">Specifications</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('Streams')}}</td> + <td>{{Spec2('Streams')}}</td> + <td>최초 정의.</td> + </tr> + </tbody> +</table> + +<h2 id="브라우저_호환성">브라우저 호환성</h2> + +<div class="hidden"> +<p>The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</p> +</div> + +<h3 id="ReadableStream">ReadableStream</h3> + +<p>{{Compat("api.ReadableStream")}}</p> + +<h3 id="WritableStream">WritableStream</h3> + +<p>{{Compat("api.WritableStream")}}</p> + +<h2 id="더_보기">더 보기</h2> + +<ul> + <li><a href="/en-US/docs/Web/API/Streams_API/Concepts">Streams API 개</a>념</li> + <li><a href="/en-US/docs/Web/API/Streams_API/Using_readable_streams">Readable stream 사용하기</a></li> + <li><a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">Writable stream 사용하기</a></li> +</ul> diff --git a/files/ko/web/api/streams_api/using_readable_streams/index.html b/files/ko/web/api/streams_api/using_readable_streams/index.html new file mode 100644 index 0000000000..719cd6653f --- /dev/null +++ b/files/ko/web/api/streams_api/using_readable_streams/index.html @@ -0,0 +1,317 @@ +--- +title: Using readable streams +slug: Web/API/Streams_API/Using_readable_streams +translation_of: Web/API/Streams_API/Using_readable_streams +--- +<div>{{apiref("Streams")}}</div> + +<p class="summary">자바스크립트 개발자로서, 프로그래밍적으로 네트워크로부터 받은 데이터 스트림을 Chunk단위로 읽고 다루는 것은 매우 유용합니다! 그러나 어떻게 스트림 API의 Readable stream을 잘 사용할수 있을까요. 이번 내용은 그것을 설명하고 있습니다.</p> + +<div class="note"> +<p><strong>Note</strong>: This article assumes that you understand the use cases of readable streams, and are aware of the high-level concepts. If not, we suggest that you first read the <a href="/en-US/docs/Web/API/Streams_API#Concepts_and_usage">Streams concepts and usage overview</a> and dedicated <a href="/en-US/docs/Web/API/Streams_API/Concepts">Streams API concepts</a> article, then come back.</p> +</div> + +<div class="note"> +<p><strong>Note</strong>: If you are looking for information on writable streams try <a href="/en-US/docs/Web/API/Streams_API/Using_writable_streams">Using writable streams</a> instead.</p> +</div> + +<h2 id="Browser_support">Browser support</h2> + +<p>파이어폭스 65+ 와 크롬 42+ 에서 Fetch Body 객체를 스트림으로서 사용 할수 있고, custom readable 스트림을 만들수 있습니다. 현재 <a href="/en-US/docs/Web/API/Streams_API/Concepts#Pipe_chains">Pipe chains</a>의 경우 오직 크롬에서만 지원하고 있고 그 기능은 변경될 수 있습니다.</p> + +<h2 id="Finding_some_examples">Finding some examples</h2> + +<p>이번 내용과 관련한 많은 예제를 <a href="https://github.com/mdn/dom-examples/tree/master/streams">dom-examples/streams</a> 에서 살펴볼수 있습니다. 이곳에서 모든 소스를 확인할수 있을 뿐만 아니라 예제 사이트 링크도 확인할수 있습니다.</p> + +<h2 id="Consuming_a_fetch_as_a_stream">Consuming a fetch as a stream</h2> + +<p><a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> 는 네트워크를 통해 리소스를 가져오는 <a href="/en-US/docs/Web/API/XMLHttpRequest">XHR</a>의 현대적인 대안책 입니다. Fetch API의 수많은 이점 가운데 가장 훌륭한점은 최근 브라우저들이 Fetch Response를 Readable 스트림으로 이용할수 있게 하는 기능을 추가한것 입니다.</p> + +<p>{{domxref("Body")}} 믹스인은 {{domxref("Body.body","body")}} 속성을 포함하고 있습니다. 그리고 이 {{domxref("Body.body","body")}} 속성은 {{domxref("Body.body","body")}}의 내용을 Readable 스트림으로 노출시키는 간단한 getter 입니다. 이 {{domxref("Body")}} 믹스인은 {{domxref("Request")}}와 {{domxref("Response")}} 인터페이스로부터 구현 되며, 따라서 두 경우 모두 사용 할수 있습니다. 다만 Response body의 스트림을 사용하는 것이 조금더 명확합니다.</p> + +<p>우리의 <a href="https://github.com/mdn/dom-examples/tree/master/streams/simple-pump">Simple stream pump</a>예시에서 보여주듯(<a href="https://mdn.github.io/dom-examples/streams/simple-pump/">see it live also</a>), Readable 스트림을 노출시키기 위해서는 단지 Response의 body 속성에 접근하기만 하면 됩니다.</p> + +<pre class="brush: js">// 오리지널 이미지를 Fetch 함 +fetch('./tortoise.png') +// body 를 ReadableStream으로 가공함 +.then(response => response.body) +</pre> + +<p>이것은 우리에게 {{domxref("ReadableStream")}} 객체를 제공해 줍니다.</p> + +<h3 id="Attaching_a_reader">Attaching a reader</h3> + +<p>이제 우리는 스트림화된 body를 가지고 있으며, 이 스트림을 읽기 위해서는 리더기를 붙여야 합니다. 이 작업은 {{domxref("ReadableStream.getReader()")}} 메서드를 사용하여 처리합니다.</p> + +<pre class="brush: js">// 오리지널 이미지를 Fetch 함 +fetch('./tortoise.png') +// body 를 ReadableStream으로 가공함 +.then(response => response.body) +.then(body => { + const reader = body.getReader();</pre> + +<p>이 메서드를 실행하면 리더기가 생성되고 이 리더기를 스트림에 고정(locks) 시킵니다. {{domxref("ReadableStreamDefaultReader.releaseLock()")}} 메서드를 사용하는등 이 고정(locks)을 풀기전 까지는(released), 그 어떤 다른 리더기들도 이 스트림을 읽을수 없습니다. </p> + +<p>또한 <code>response.body</code> 는 동기이므로 굳이 프로미스를 사용할 필요가 없으며, 위의 예시를 한번의 스텝으로 줄일수 있다는 점에 주목해 주십시요</p> + +<pre class="brush: js">// 오리지널 이미지를 Fetch 함 + fetch('./tortoise.png') + // body 를 ReadableStream으로 가공함 + .then(response => { + const reader = response.body.getReader();</pre> + +<h3 id="Reading_the_stream">Reading the stream</h3> + +<p>이제 우리는 우리의 리더기를 추가하였습니다. 우리는 리더기의 {{domxref("ReadableStreamDefaultReader.read()")}} 메서드를 사용하여 스트림으로 부터 data chunk들을 읽을수 있게 되었습니다.</p> + +<p>정확하게는 이 메서드는 각 스트림으로부터 하나의 data chunk를 읽습니다. 그리고 이러한 data chunk는 우리가 원하는대로 사용할 수 있습니다.</p> + +<p>예를 들어 우리의 Simple stream pump example에서는 {{domxref("ReadableStreamDefaultReader.read()")}} 사용하여 Data Chunk를 새로운 커스텀 <code>ReadableStream</code>에 집어 넣고 있습니다. 그리고 만약 읽을수 있는 다음 Data Chunk가 있다면, {{domxref("ReadableStreamDefaultReader.read()")}} 를 다시 사용하여 다음 Data Chunk를 커스텀 <code>ReadableStream</code>에 집어 넣습니다. 더이상 읽을수 있는 Data Chunk가 없다면, 그 커스텀 <code>ReadableStream</code>(우리는 이 새로운 Readable 스트림에 대해 다음 섹션에서 다 자세히 살펴 볼것 입니다.)을 통해 새로운 {{domxref("Response")}} 객체를 생성합니다. 그 다음 이 {{domxref("Response")}} 객체를 {{domxref("Blob")}} 형태로 변환하고 이 {{domxref("Blob")}} 으로 부터 {{domxref("URL.createObjectURL()")}} 메서드를 사용하여 URL 객체를 생성합니다. 마지막으로 이 URL객체를 {htmlelement("img")}} 에 적용하여 이미지를 보여줌으로서 fetch된 오리지널 이미지를 효과적으로 복사하는 것입니다.</p> + +<pre class="brush: js"> return new ReadableStream({ + start(controller) { + return pump(); + function pump() { + // 스트림의 다음 Chunk에 대한 액세스를 제공하는 psomise를 리턴한다. + return reader.read().then(({ done, value }) => { + // 더이상 읽을 데이터 조각이 없을때 스트림을 닫는다 + if (done) { + controller.close(); + return; + } + // 데이터 조각을 새로운 스트림(새로 만드는 커스텀 스트림)에 넣는다. + controller.enqueue(value); + return pump(); + }); + } + } + }) +}) +.then(stream => new Response(stream)) +.then(response => response.blob()) +.then(blob => URL.createObjectURL(blob)) +.then(url => console.log(image.src = url)) +.catch(err => console.error(err));</pre> + +<p>어떻게 <code>read()</code> 가 사용되었는지 자세히 들여다 봅시다. 위 예제의 <code>pump()</code> 함수는 제일먼저 <code>read()</code> 를 실행하였습니다. 그리고 이 <code>read()</code> 는 스트림으로부터 읽어 들인 내용의 결과를 <code>{ done, value }</code> 의 형식으로 가지고 있는 pomise를 반환합니다. </p> + +<pre class="brush: js">return reader.read().then(({ done, value }) => {</pre> + +<p>스트림으로부터 읽어 들인 내용은 아래 3가지 타입이 있습니다. </p> + +<ul> + <li>chunk를 아직 읽을수 있는 경우에 프로미스는 <code>{ value: theChunk, done: false }</code> 객체와 함께 fulfill 됩니다. </li> + <li>스트림이 닫힌 경우에 프로미스는 <code>value: undefined, done: true }</code> 객체와 함께 fulfill 됩니다.</li> + <li>스트림에서 애러가 발생한 경우 promise는 관련 애러와 함께 reject 됩니다. </li> +</ul> + +<p>다음으로 우리는 <code>done</code> 이 <code>true</code> 인지 아닌지 확인해 봐야 합니다.</p> + +<p>만약 <code>done</code> 이 <code>true</code> 라면 더이상 읽어들일 chunk가 없다는 뜻입니다. 따라서 우리는 함수 밖으로 빠져 나가야 하고 {{domxref("ReadableStreamDefaultController.close()")}} 를 통하여 커스텀 스트림을 닫아야 합니다. </p> + +<pre class="brush: js">if (done) { + controller.close(); + return; +}</pre> + +<div class="note"> +<p><strong>Note</strong>: 여기서 사용한 <code>close()</code> 는 새로만든 커스텀 스트림의 일부이며 오리지널 스트림의 것이 아닙니다. 커스텀 스트림에 대해서는 다음섹션에서 더 자세히 살펴 볼 예정입니다. </p> +</div> + +<p>만약 <code>done</code> 이 <code>true</code> 가 아니라면, 우선 읽어 드린 Chunk를 처리하고 (<code>value</code> 속성), <code>pump()</code> 함수를 재귀적으로 다시 호출 함으로서 다음 chunk를 읽어 드립니다. </p> + +<pre class="brush: js">// 다음 data chunk를 새로운 readable 스트림에 집어 넣음 +controller.enqueue(value); +return pump();</pre> + +<p>다음은 스트림 리더기를 사용할때의 기본적인 패턴 입니다.</p> + +<ol> + <li>스트림을 읽음으로서 실행되는 함수를 작성합니다.</li> + <li>만약 읽을수 있는 스트림이 더이상 없다면, 함수를 리턴 시킵니다.</li> + <li>만약 읽을수 있는 스트림이 아직 남아 있다면, 우선 읽어 드린 chunk를 처리하고, 다음 chunk를 읽어 드리기 위해 함수를 다시 실행합니다. </li> + <li>더이상 읽을수 있는 스트림이 없을때까지 함수를 재귀적으로 실행하고, 최종적으로 읽을수 있는 스트림이 없을경우 2번 Step을 따릅니다. </li> +</ol> + +<h2 id="Creating_your_own_custom_readable_stream">Creating your own custom readable stream</h2> + +<p>두번째 파트에서 사용했던 Simple stream pump example 예제에서 우리는 fetch body로 부터 읽어드린 이미지에 대한 data를 우리가 자체적으로 만든 커스텀 readable 스트림에 다시 옮겨 심었습니다. 그럼 어떻게 스트림을자체적으로 만들수 있을까요? 우리는 <code>ReadableStream()</code>생성자 함수를 통해 커스텀 readable 스트림을 만들 수 있습니다. </p> + +<h3 id="The_ReadableStream_constructor">The ReadableStream() constructor</h3> + +<p>Fetch와 같이 브라우저에서 스트림을 제공할때 그것을 읽어 들이는 일은 쉬운 일입니다. 그러나 때때로 새로운 커스텀 스트림을 만들고 이것을 data chunk들로 채워넣어야 하는 경우가 있습니다. {{domxref("ReadableStream.ReadableStream","ReadableStream()")}}을 아래의 구문과 같이 사용함으로서 이것을 가능하게 할수 있습니다. 구문이 처음에는 다소 복잡해 보일수 있습니다만, 실제로는 그렇게 복잡하지 않습니다. </p> + +<p>기본적인 핵심 구문은 다음과 같습니다.</p> + +<pre class="brush: js">const stream = new ReadableStream({ + start(controller) { + + }, + pull(controller) { + + }, + cancel() { + + }, + type, + autoAllocateChunkSize +}, { + highWaterMark, + size() +});</pre> + +<p>생성자 함수는 두개의 객체를 인자로 받습니다. 첫번째 인자는 필수 값이며, 이것은 우리가 읽어 들일 기본 소스의 모델을 JavasScrip 적으로 생성 합니다. 두번째 인자는 옵션 값이며, 이것은 커스텀 스트림에 사용할 커스텀 queuing 전략을 수립하게 합니다. 두번째 인자의 경우 매우 드물게 설정하는 값이므로 지금은 첫번째 인자에 집중하도록 하겠습니다.</p> + +<p>첫번째 인자인 객체는 5개의 맴버를 가질수 있으며, 제일 첫번째 맴버만 필수 입니다.</p> + +<ol> + <li><code>start(controller)</code> — <code>ReadableStream</code> 이 생성되자 마자 딱 한번만 호출 되는 메서드 입니다. 이 메서드에는 스트림을 기능을 설정할 수 있는 코드가 포함되어야 합니다. 예를 들면 데이터 생성을 시작한다거나 아니면 소스에 접근하여 데이터를 가져오는 코드등이 들어가게 될것입니다.</li> + <li><code>pull(controller)</code> — 이 메서드는 스트림 내부의 queue가 가득 찰때까지 반복적으로 호출 됩니다. 더 많은 청크가 큐에 들어갈 때 스트림을 제어하는 데 사용할 수 있습니다.</li> + <li><code>cancel()</code> — 이 메서드는 스트림이 캔슬될때 호출 됩니다 (예를 들어 {domxref("ReadableStream.cancel()")}} 이 호출 되었을때). 메서드의 내용은 스트림 소스에 대한 액세스를 해제하는 데 필요한 모든 것을 수행해야합니다.</li> + <li><code>type</code> and <code>autoAllocateChunkSize</code> — 스트림이 바이트 스트림임을 나타 내기 위해 사용됩니다. 바이트 스트림은 목적과 사용 사례가 일반 (기본) 스트림과 약간 다르므로 향후 자습서에서 별도로 다룰 것입니다. 또한 아직 어느 곳에서도 구현되지 않았습니다.</li> +</ol> + +<p>simple example code 를 다시한번 살펴보면, <code>ReadableStream()</code> 생성자가 <code>start()</code> 메서드 단 하나만 가지고 있다는 것을 알아챌수 있을 것 입니다. 이 <code>start()</code> 메서드 fetch된 스트림으로 부터 데이터를 읽어 들이고 있습니다.</p> + +<pre class="brush: js"> return new ReadableStream({ + start(controller) { + return pump(); + function pump() { + return reader.read().then(({ done, value }) => { + // 더이상 읽을수 있는 data가 없다면 스트림을 닫는다 + if (done) { + controller.close(); + return; + } + // 데이터 조각을 새로운 스트림(새로 만드는 커스텀 스트림)에 넣는다. + controller.enqueue(value); + return pump(); + }); + } + } + }) +}) +</pre> + +<h3 id="ReadableStream_controllers">ReadableStream controllers</h3> + +<p>ReadableStrem() 생성자에 인자로 전달된 객체안의 <code>start()</code> 와 <code>pull()</code> 메서드에 controller라는 인자가 전달되는 것을 볼수 있습니다. 이것은 {{domxref("ReadableStreamDefaultController")}} 클래스의 인스턴스이며 우리의 스트림을 제어하는데 사용 됩니다.</p> + +<p>우리의 예제에서, 우리는 fetch된 body로부터 chunk의 값을 읽은 뒤 그 값을 커스텀 스트림에 집어 넣기 위해 Controller의 {{domxref("ReadableStreamDefaultController.enqueue","enqueue()")}} 메서드를 상용하고 있습니다.</p> + +<p>또한, fetch된 body를 읽어 들이는 것이 끝나면 컨트롤러의 {{domxref("ReadableStreamDefaultController.close","close()")}}를 사용하여 커스텀 스트림을 닫습니다. 이때 이미 삽입된 chunk들은 여전히 읽을수 있지만 새로운 chunk는 집어 넣을수 없습니다. 그리고 읽는 것이 모두 긑나면 스트림은 닫힙니다. </p> + +<h3 id="Reading_from_custom_streams">Reading from custom streams</h3> + +<p>우리의 Simple stream pump example 에서, 우리는 {domxref("Response.Response", "Response")}} 생성자 함수에 우리가 만든 커스텀 readable 스트림을 인자로 전달하였으며 그렇게 생성된 response 인스턴스를 <code>blob()</code> 으로 사용 하였습니다.</p> + +<pre class="brush: js">.then(stream => new Response(stream)) +.then(response => response.blob()) +.then(blob => URL.createObjectURL(blob)) +.then(url => console.log(image.src = url)) +.catch(err => console.error(err));</pre> + +<p>그러나 커스텀 스트림은 여전히 <code>ReadableStream</code> 인스턴스입니다. 즉, 우리는 여전히 리더기를 커스텀 스트림에 붙일수 있다는 뜻입니다. 예를 들어 <a href="https://github.com/mdn/dom-examples/blob/master/streams/simple-random-stream/index.html">Simple random stream demo</a> (<a href="https://mdn.github.io/dom-examples/streams/simple-random-stream/">see it live also</a>) 를 살펴 보십시요. 이 예제에서는 커스텀 스트림을 생성한후, 랜덤 문자열을 생성된 커스텀 스트림에 집어 넣습니다. 그 후 문자열 생성 중지 버튼을 눌렀을때 커스텀 스트림에 집어 넣었던 랜덤 문자열을 커스텀 스트림으로 부터 다시 읽어 옵니다.</p> + +<p><strong>Note:</strong> {{domxref("FetchEvent.respondWith()")}} 메서드를 사용하여 스트림을 다루기 위해서는 스트림에 삽입된 내용이 만드시 {{jsxref("Uint8Array")}} 이어야 합니다. ({{domxref("TextEncoder")}} 등을 사용)</p> + +<p> <a href="https://github.com/mdn/dom-examples/blob/master/streams/simple-random-stream/index.html">Simple random stream demo</a> (<a href="https://mdn.github.io/dom-examples/streams/simple-random-stream/">see it live also</a>) 에서 커스텀 스트림 생성자 함수는 <code>start()</code> 메서드를 가지고 있으며, 이 메서드는 {{domxref("WindowTimers.setInterval()")}} 을 사용하여 매초마다 새로운 랜덤 문자열을 생성하고 이 문자열을 {{domxref("ReadableStreamDefaultController.enqueue()")}} 를 사용하여 스트림안에 넣습니다. 만약 문자열 생성 중지 버튼을 누른다면 이 interval 이 취소됨과 동시에 <code>readStream()</code> 함수를 호출하여 스트림으로 부터 스트림에 넣은 문자열 전부를 읽어 드립니다. 이때 스트림에 chunk data(여기서는 랜덤 문자열)를 넣는 것도 중단 했기때문에 스트림을 닫습니다. </p> + +<pre class="brush: js">const stream = new ReadableStream({ + start(controller) { + interval = setInterval(() => { + let string = randomChars(); + // Add the string to the stream + controller.enqueue(string); + // show it on the screen + let listItem = document.createElement('li'); + listItem.textContent = string; + list1.appendChild(listItem); + }, 1000); + button.addEventListener('click', function() { + clearInterval(interval); + readStream(); + controller.close(); + }) + }, + pull(controller) { + // We don't really need a pull in this example + }, + cancel() { + // This is called if the reader cancels, + // so we should stop generating strings + clearInterval(interval); + } +});</pre> + +<p>스트림에 넣은 문자열 전부를 읽고 다루기 위해 만든 커스텀 함수인 <code>readStream()</code> 함수를 살펴보면, 우선 {{domxref("ReadableStream.getReader()")}}를 사용하여 리더기를 스트림에 고정시키는 것을 볼수 있습니다. 그 후 앞서 살펴봤던 패턴과 마찬가지로 <code>read()</code> 를 사용하여 chunk를 읽어 들이고 <code>done</code> 이 <code>true</code> 인지 아닌지 확인 합니다. 만약 <code>true</code> 이면 <code>readStream()</code> 함수의 프로세스를 끝내버리고 그렇지 않으면 읽어드린 chunk를 후속처리한 후 <code>read()</code> 를 재귀적으로 실행합니다.</p> + +<pre class="brush: js">function readStream() { + const reader = stream.getReader(); + let charsReceived = 0; + + // read() returns a promise that resolves + // when a value has been received + reader.read().then(function processText({ done, value }) { + // Result objects contain two properties: + // done - true if the stream has already given you all its data. + // value - some data. Always undefined when done is true. + if (done) { + console.log("Stream complete"); + para.textContent = result; + return; + } + + charsReceived += value.length; + const chunk = value; + let listItem = document.createElement('li'); + listItem.textContent = 'Read ' + charsReceived + ' characters so far. Current chunk = ' + chunk; + list2.appendChild(listItem); + + result += chunk; + + // Read some more, and call this function again + return reader.read().then(processText); + }); +}</pre> + +<h3 id="Closing_and_cancelling_streams">Closing and cancelling streams</h3> + +<p>우리는 이미 앞서 스트림을 닫는 메서드인 {{domxref("ReadableStreamDefaultController.close()")}} 를 살펴보았습니다. 이미 언급했다 시피, 스트림이 닫혔다 하더라고 이미 들어가 있는 chunk는 읽을 수 있습니다.</p> + +<p>만약 스트림을 완벽하게 제거하고 삽입된 모든 chunk를 날리고 싶다면, {{domxref("ReadableStream.cancel()")}} 또는 {{domxref("ReadableStreamDefaultReader.cancel()")}} 메서드를 사용 하면 됩니다.</p> + +<h2 id="Teeing_a_stream">Teeing a stream</h2> + +<p>때로는 하나의 스트림을 동시 두번 읽어들여야 할 경우가 있습니다. {{domxref("ReadableStream.tee()")}} 메서드가 이를 가능하게 합니다. {{domxref("ReadableStream.tee()")}} 메서드는 두개의 독립적인 카피된 스트림을 가지고 있는 배열을 제공합니다. 이 카피된 두개의 스트림은 두개의 독립적인 리더기로 각각 읽어 들일 수 있습니다.</p> + +<p>이런 경우는 아마 <a href="/en-US/docs/Web/API/Service_Worker_API">ServiceWorker</a> 안에서 필요할 것입니다. 만약 서버로부터 fetch된 response를 브라우저에도 전달하고 서비스 워커 캐시에도 전달해야 하다면 하나의 스트림에 대해 두개의 카피본이 필요 할 것입니다. 왜냐하면 response body (Readablestream)는 단 한번만 사용될 수 있고 하나의 Readablestream은 하나의 리더기만 붙을수 있기 때문입니다. </p> + +<p>위 내용에 대한 예제를 <a href="https://github.com/mdn/dom-examples/blob/master/streams/simple-tee-example/index.html">Simple tee example</a> (<a href="https://mdn.github.io/dom-examples/streams/simple-tee-example/">see it live also</a>)에서 살펴 볼수 있습니다. 이 예제는 랜덤 문자열 생성 버튼에 대한 이벤트가 없다는 점과, 이 예제에서의 스트림은 teed되어 두개의 스트림이 두개의 리더기로 읽어진다는 점만 제외하면 앞서 살펴본 Simple random stream 예제와 매우 유사하게 동작합니다.</p> + +<pre class="brush: js">function teeStream() { + const teedOff = stream.tee(); + readStream(teedOff[0], list2); + readStream(teedOff[1], list3); + }</pre> + +<h2 id="Pipe_chains">Pipe chains</h2> + +<p>One very experimental feature of streams is the ability to pipe streams into one another (called a <a href="/en-US/docs/Web/API/Streams_API/Concepts#Pipe_chains">pipe chain</a>). This involves two methods — {{domxref("ReadableStream.pipeThrough()")}}, which pipes a readable stream through a writer/reader pair to transform one data format into another, and {{domxref("ReadableStream.pipeTo()")}}, which pipes a readable stream to a writer acting as an end point for the pipe chain.</p> + +<p>This functionality is at a very experimental stage and is subject to change, so we have no explored it too deeply as of yet.</p> + +<p>We have created an example called <a href="https://github.com/mdn/dom-examples/tree/master/streams/png-transform-stream">Unpack Chunks of a PNG</a> (<a href="https://mdn.github.io/dom-examples/streams/png-transform-stream/">see it live also</a>) that fetches an image as a stream, then pipes it through to a custom PNG transform stream <span class="pl-c">that retrieves PNG chunks out of a binary data stream.</span></p> + +<pre class="brush: js">// Fetch the original image +fetch('png-logo.png') +// Retrieve its body as ReadableStream +.then(response => response.body) +// Create a gray-scaled PNG stream out of the original +.then(rs => logReadableStream('Fetch Response Stream', rs)) +.then(body => body.pipeThrough(new PNGTransformStream())) +.then(rs => logReadableStream('PNG Chunk Stream', rs))</pre> + +<h2 id="Summary">Summary</h2> + +<p>That explains the basics of “default” readable streams. We’ll explain bytestreams in a separate future article, once they are available in browsers.</p> diff --git a/files/ko/web/api/streams_api/컨셉/index.html b/files/ko/web/api/streams_api/컨셉/index.html new file mode 100644 index 0000000000..9c993b81a3 --- /dev/null +++ b/files/ko/web/api/streams_api/컨셉/index.html @@ -0,0 +1,115 @@ +--- +title: Streams API 컨셉 +slug: Web/API/Streams_API/컨셉 +translation_of: Web/API/Streams_API/Concepts +--- +<div>{{apiref("Streams")}}</div> + +<p class="summary">The <a href="/en-US/docs/Web/API/Streams_API">Streams API</a> adds a very useful set of tools to the web platform, providing objects allowing JavaScript to programmatically access streams of data received over the network and process them as desired by the developer. Some of the concepts and terminology associated with streams might be new to you — this article explains all you need to know.</p> + +<h2 id="Readable_streams">Readable streams</h2> + +<p>A readable stream is a data source represented in JavaScript by a {{domxref("ReadableStream")}} object that flows from an <strong>underlying source</strong> — this is a resource somewhere on the network or elsewhere on your domain that you want to get data from.</p> + +<p>There are two types of underlying source:</p> + +<ul> + <li><strong>Push sources</strong> constantly push data at you when you’ve accessed them, and it is up to you to start, pause, or cancel access to the stream. Examples include video streams and TCP/<a href="/en-US/docs/Web/API/WebSockets_API">Web sockets</a>.</li> + <li><strong>Pull sources</strong> require you to explicitly request data from them once connected to. Examples include a file access operation via a <a href="/en-US/docs/Web/API/Fetch_API">Fetch</a> or <a href="/en-US/docs/Web/API/XMLHttpRequest/XMLHttpRequest">XHR</a> call.</li> +</ul> + +<p>컨The data is read sequentially in small pieces called <strong>chunks</strong>. A chunk can be a single byte, or it can be something larger such as a <a href="/en-US/docs/Web/JavaScript/Typed_arrays">typed array</a> of a certain size. A single stream can contain chunks of different sizes and types.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15819/Readable%20streams.png" style="height: 452px; width: 1000px;"></p> + +<p>The chunks placed in a stream are said to be <strong>enqueued</strong> — this means they are waiting in a queue ready to be read. An <strong>internal queue</strong> keeps track of the chunks that have not yet been read (see the Internal queues and queueing strategies section below).</p> + +<p>The chunks inside the stream are read by a <strong>reader</strong> — this processes the data one chunk at a time, allowing you to do whatever kind of operation you want to do on it. The reader plus the other processing code that goes along with it is called a <strong>consumer</strong>.</p> + +<p>There is also a construct you’ll use called a <strong>controller</strong> — each reader has an associated controller that allows you to control the stream (for example, to cancel it if wished).</p> + +<p>Only one reader can read a stream at a time; when a reader is created and starts reading a stream (an <strong>active reader</strong>), we say it is <strong>locked</strong> to it. If you want another reader to start reading your stream, you typically need to cancel the first reader before you do anything else (although you can <strong>tee</strong> streams, see the Teeing section below)</p> + +<p>Note that there are two different types of readable stream. As well as the conventional readable stream there is a type called a byte stream — this is an extended version of a conventional stream for reading underlying byte sources (otherwise known as BYOB, or “bring your own buffer”) sources. These allow streams to be read straight into a buffer supplied by the developer, minimizing the copying required. Which underlying stream (and by extension, reader and controller) your code will use depends on how the stream was created in the first place (see the {{domxref("ReadableStream.ReadableStream()")}} constructor page).</p> + +<div class="warning"> +<p><strong>Important</strong>: Byte streams are not implemented anywhere as yet, and questions have been raised as to whether the spec details are in a finished enough state for them to be implemented. This may change over time.</p> +</div> + +<p>You can make use of ready-made readable streams via mechanisms like a {{domxref("Response.body")}} from a fetch request, or roll your own streams using the {{domxref("ReadableStream.ReadableStream()")}} constructor.</p> + +<h2 id="Teeing">Teeing</h2> + +<p>Even though only a single reader can read a stream at once, it is possible to split a stream into two identical copies, which can then be read by two separate readers. This is called <strong>teeing</strong>.</p> + +<p>In JavaScript, this is achieved via the {{domxref("ReadableStream.tee()")}} method — it outputs an array containing two identical copies of the original readable stream, which can then be read independently by two separate readers.</p> + +<p>You might do this for example in a <a href="/en-US/docs/Web/API/Service_Worker_API">ServiceWorker</a> if you want to fetch a response from the server and stream it to the browser, but also stream it to the ServiceWorker cache. Since a response body cannot be consumed more than once, and a stream can't be read by more than one reader at once, you’d need two copies to do this.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15820/tee.png" style="height: 527px; width: 1000px;"></p> + +<h2 id="Writable_streams">Writable streams</h2> + +<p>A <strong>writable stream</strong> is a destination into which you can write data, represented in JavaScript by a {{domxref("WritableStream")}} object. This serves as an abstraction over the top of an <strong>underlying sink</strong> — a lower-level I/O sink into which raw data is written.</p> + +<p>The data is written to the stream via a <strong>writer</strong>, one chunk at a time. A chunk can take a multitude of forms, just like the chunks in a reader. You can use whatever code you like to produce the chunks ready for writing; the writer plus the associated code is called a <strong>producer</strong>.</p> + +<p>When a writer is created and starts writing to a stream (an <strong>active writer</strong>), it is said to be <strong>locked</strong> to it. Only one writer can write to a writable stream at one time. If you want another writer to start writing to your stream, you typically need to abort it before you then attach another writer to it.</p> + +<p>An <strong>internal queue</strong> keeps track of the chunks that have been written to the stream but not yet been processed by the underlying sink.</p> + +<p>There is also a construct you’ll use called a controller — each writer has an associated controller that allows you to control the stream (for example, to abort it if wished).</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15821/writable%20streams.png" style="height: 452px; width: 1000px;"></p> + +<p>You can make use of writable streams using the {{domxref("WritableStream.WritableStream()")}} constructor. These currently have very limited availability in browsers.</p> + +<h2 id="Pipe_chains">Pipe chains</h2> + +<p>The Streams API makes it possible to pipe streams into one another (or at least it will do when browsers implement the relevant functionality) using a structure called a <strong>pipe chain</strong>. There are two methods available in the spec to facilitate this:</p> + +<ul> + <li>{{domxref("ReadableStream.pipeThrough()")}} — pipes the stream through a <strong>transform stream</strong>, which is a pair comprised of a writable stream that has data written to it, and a readable stream that then has the data read out of it — this acts as a kind of treadmill that constantly takes data in and transforms it to a new state. Effectively the same stream is written to, and then the same values are read. A simple example is a text decoder, where raw bytes are written, and then strings are read. You can find more useful ideas and examples in the spec — see <a href="https://streams.spec.whatwg.org/#ts-model">Transform streams</a> for ideas, and <a href="https://streams.spec.whatwg.org/#example-both">this web sockets example</a>.</li> + <li>{{domxref("ReadableStream.pipeTo()")}} — pipes to a writable stream that acts as the end point of the pipe chain.</li> +</ul> + +<p>The start of the pipe chain is called the <strong>original source</strong>, and the end is called the <strong>ultimate sink</strong>.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/15818/PipeChain.png" style="height: 382px; width: 1000px;"></p> + +<div class="note"> +<p><strong>Note</strong>: This functionality isn't fully thought through yet, or available in many browsers. At some point the spec writers hope to add something like a <code>TransformStream</code> class to make creating transform streams easier.</p> +</div> + +<h2 id="Backpressure">Backpressure</h2> + +<p>An important concept in streams is <strong>backpressure</strong> — this is the process by which a single stream or a pipe chain regulates the speed of reading/writing. When a stream later in the chain is still busy and isn't yet ready to accept more chunks, it sends a signal backwards through the chain to tell earlier transform streams (or the original source) to slow down delivery as appropriate so that you don't end up with a bottleneck anywhere.</p> + +<p>To use backpressure in a ReadableStream, we can ask the controller for the chunk size desired by the consumer by querying the {{domxref("ReadableStreamDefaultController.desiredSize")}} attribute on the controller. If it is too low, our ReadableStream can tell its underlying source to stop sending data, and we backpressure along the stream chain.</p> + +<p>If later on the consumer again wants to receive data, we can use the pull method in the stream creation to tell our underlying source to feed our stream with data.</p> + +<h2 id="Internal_queues_and_queuing_strategies">Internal queues and queuing strategies</h2> + +<p>As mentioned earlier, the chunks in a stream that have not yet been processed and finished with are kept track of by an internal queue.</p> + +<ul> + <li>In the case of readable streams, these are the chunks that have been enqueued but not yet read</li> + <li>In the case of writable streams, these are chunks that have been written but not yet processed by the underlying sink.</li> +</ul> + +<p>Internal queues employ a <strong>queuing strategy</strong>, which dictates how to signal backpressure based on the <strong>internal queue state.</strong></p> + +<p>In general, the strategy compares the size of the chunks in the queue to a value called the <strong>high water mark</strong>, which is the largest total chunk size that the queue can realistically manage.</p> + +<p>The calculation performed is</p> + +<pre>high water mark - total size of chunks in queue = desired size</pre> + +<p>The <strong>desired size</strong> is the size of chunks the stream can still accept to keep the stream flowing but below the high water mark in size. After the calculation is performed, chunk generation will be slowed down/sped up as appropriate to keep the stream flowing as fast as possible while keeping the desired size above zero. If the value falls to zero (or below in the case of writable streams), it means that chunks are being generated faster than the stream can cope with, which results in problems.</p> + +<div class="note"> +<p><strong>Note</strong>: What happens in the case of zero or negative desired size hasn’t really been defined in the spec so far. Patience is a virtue.</p> +</div> + +<p>As an example, let's take a chunk size of 1, and a high water mark of 3. This means that up to 3 chunks can be enqueued before the high water mark is reached and backpressure is applied.</p> |