์๋ฌธ
Jake Archibald, https://jakearchibald.com/2016/streams-ftw/
๊ทธ๋ ๋ค. ์ ๋ ์ด๋ถํฐ ํํด์ ์ผ์ ๋ํด ์ด์ผ๊ธฐํ๋ค๋ ๊ฒ์ ๊ฐ๋ฒผ์ด ์ผ์ ์๋๋ค. ๊ทธ๋ฌ๋ ์น ์คํธ๋ฆผ API์ ์ ์ฌ๋ ฅ์ ๋๋ฅผ ๋งค์ฐ ํฅ๋ถ ์์ผฐ๊ธฐ์ ๊ทธ๋ฐ ์ผ์ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
์์ฝํ๋ฉด, ์คํธ๋ฆผ์ "cloud"๋ผ๋ ๋จ์ด๋ฅผ "butt"๋ก ๋ณ๊ฒฝ ํ๊ฑฐ๋ MPEG๋ฅผ GIF๋ก ๋ณํ ํ๋ ์์ ๊ณผ ๊ฐ์ด ์ต์ํ ์ผ๋ค์ ํ ์ ์๋ค. ๊ทธ๋ฌ๋ ๊ฐ์ฅ ์ค์ํ๊ฒ์ ์ ๊ณต ๋ด์ฉ์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ์๋น์ค ์์ปค๋ก ๊ฒฐํฉ์ํฌ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์ข์ ๊ฒ์ ๋ถ๋ช ํ๋ฐ...
ํ๋ผ๋ฏธ์ค(promise)๋ ๋จ๋ ๊ฐ์ ๋ํ ๋น๋๊ธฐ ์ ์ก์ ๋์ฒดํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ๋ฌ๋ ์ฌ๋ฌ ๊ฐ์ ๋ํ ์ ์ก์ด๋ ํฐ๊ฐ์ ๋ถ์ฐํ์ฌ ์ ์กํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ์ข์ ๋ฐฉ๋ฒ์ด ์๋ ์ ์๋ค.
์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ค๊ณ ํํํ๊ธฐ ์ํ ๋์๋ ๋ค์์ ์ ์ฐจ๋๋ก ์งํ๋๋ค.
์ฐ๋ฆฌ๋ ํ๋ฒ์ ํ๋์ ์คํญ์ฉ ์คํํ๊ฑฐ๋ ์๋๋ฉด ์คํธ๋ฆผ์ ์ด์ฉํ ์ ์๋ค.
๋ง์ฝ ์ฐ๋ฆฌ๊ฐ ๋นํธ๋จ์๋ก ํธ๋ค๋งํ๊ณ ๋ณํํ๋ค๋ฉด, ์ด๋ฏธ์ง์ ์ผ๋ถ๋ฅผ ๋นจ๋ฆฌ ๋ ๋๋ง ํ๋ ๋ฐฉ๋ฒ์ ์ป์ ์ ์๋ค. ์ฌ์ง์ด ๋ฐ์ดํฐ๋ฅผ ๋ณ๋ ฌ๋ก ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์, ์ ์ฒด ์ด๋ฏธ์ง๋ฅผ ๋น ๋ฅด๊ฒ ๋ ๋๋งํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค. ์ด๊ฒ์ด ์คํธ๋ฆฌ๋ฐ์ด๋ค! ์ฐ๋ฆฌ๋ ๋คํธ์ํฌ๋ก๋ถํฐ ์คํธ๋ฆผ์ ์ฝ๊ณ ์์ถ๋ฐ์ดํฐ๋ฅผ ํฝ์ ๋ฐ์ดํฐ๋ก ๋ณํํ ํ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ฒ ๋๋ค.
๋น์ ์ ์ด๋ฒคํธ๋ก๋ ๋น์ทํ ์ฑ๊ณผ๋ฅผ ์ป์ ์ ์๋ค. ๊ทธ๋ฌ๋ ์คํธ๋ฆผ์ ๋๋ค๋ฅธ ์ด์ ์ ์ ๊ณตํ๋ค.
์์ ํญ๋ชฉ ์ค ๋ง์ง๋ง ํ๋๋ ์ ๋ง ์ค์ํ๋ค. ๋ค์ด๋ก๋์ ์์์ ํํํ๋๋ฐ ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๋ง์ฝ์ ์ด๋น 200 ํ๋ ์์ ๋น๋์ค๋ฅผ ๋ค์ด๋ก๋ํ๊ณ ๋์ฝ๋ฉ ํ๋ค๊ณ ํด๋ ํ๋ฉด์๋ ์ด๋น 24 ํ๋ ์๋ง ํํ๋ ๊ฒ์ด๋ฉฐ ๊ฒฐ๊ตญ ๋์ฝ๋ ํ๋ ์์ ๊ฑฐ๋ํ ๋ฐฑ๋ก๊ทธ์ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ์ ๊ฒฐ๊ณผ๋ง ์ป๊ฒ ๋ ๊ฒ์ด๋ค.
์ฌ๊ธฐ๊ฐ ํ๋ฆ์ ์ด๊ฐ ๋ค์ด์ค๋ ๊ณณ์ด๋ค. ๋ ๋๋ง์ ํธ๋ค๋งํ๋ ์คํธ๋ฆผ์ ์ด๋น 24ํ์ ๋์ฝ๋ ์คํธ๋ฆผ์ผ๋ก ๋ถํฐ ํ๋ ์์ ๊ฐ์ ธ์จ๋ค. ๋์ฝ๋๋ ์ฝํ์ง๊ฒ ๋ณด๋ค ํ๋ ์์ ์์ฑํ๋๊ฒ์ด ๋ ๋น ๋ฅด๋ฉฐ ์ ์ ๋๋ ค์ง๋ค๊ณ ์๋ ค์ ธ์๋ค. ๋คํธ์ํฌ ์คํธ๋ฆผ์ ๊ฐ์ ธ์ค๋ ๋ฐ์ดํฐ๊ฐ ๋์ฝ๋์์ํด ์ฝํ์ง๋ ๊ฒ ๋ณด๋ค ๋น ๋ฅด๋ฉฐ ๋๋ฆฌ๊ฒ ๋ค์ด๋ก๋ ๋๋ค๊ณ ์๋ ค์ ธ์๋ค.
์คํธ๋ฆผ๊ณผ ๋ฆฌ๋์ ํ์ดํธํ ๊ด๊ณ ๋๋ฌธ์ ์คํธ๋ฆผ์ ์ค๋ก์ง ํ๋์ ๋ฆฌ๋๋ง ๊ฐ์ง ์ ์๋ค. ๊ทธ๋ฌ๋ ์ฝ์ง ์์ ์คํธ๋ฆผ์ โํฐ๋(teed)โํ ์ ์๋๋ฐ, ์ด๋ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋๊ฐ์ ์คํธ๋ฆผ์ผ๋ก ๋๋๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ด ๊ฒฝ์ฐ ํฐ(tee)๋ ์ ๋ฆฌ๋ ๋ชจ๋๋ฅผ ํตํด ๋ฒํผ๋ฅผ ๊ด๋ฆฌํ๋ค.
์ข๋ค. ๊ทธ๊ฒ์ด ์ด๋ก ์ผ ๋ฟ์ด๊ณ ๋ด ์ด์ผ๊ธฐ์ ์ ์ ์ผ๋ก ๋์ํ๋ค๊ณ ๋ณผ ์๋ ์์ง๋ง, ๋น์ ์ด ๊ณ์ํด์ ์ด์ผ๊ธฐ๋ฅผ ๋ฃ๊ธธ ๋ฐ๋๋ค.
๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ง์ ๊ฒ์ ๋ก๋ํ๋ค. ๋ค์ด๋ก๋ํ๋ ๊ฒ ๊ฐ์ ํ์ด์ง/์ด๋ฏธ์ง/๋น๋์ค ๋ฑ์ ์ผ๋ถ๋ฅผ ํ์ํ๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋ณผ ๋๋ง๋ค ์คํธ๋ฆฌ๋ฐ ๋๋ถ์ด๋ผ๋ ๊ฒ์ ์์์ผ ํ๋ค. ๋ํ ๊ทธ๊ฒ์ ์ต๊ทผ์ ์คํธ๋ฆผ์ ์คํฌ๋ฆฝํธ์ ๋ ธ์ถ๋๊ฒ ํ ํ์คํ ๋ ธ๋ ฅ ๋๋ถ์ด๊ธฐ๋ ํ๋ค.
์๋ต(response) ๊ฐ์ฒด๋ ํจ์น ์คํ(fetch spec)์ ์ ์๋ ๋๋ก, ๋ค์ํ ํ์์ผ๋ก ์๋ต์ ์ฝ์ ์ ์๊ฒ ํ๋ค. ํ์ง๋ง response.body
๋ ๊ทผ๋ณธ์ ์ธ ์คํธ๋ฆผ์ ์์ธ์ค ํ ์ ์๊ฒ ํ๋ค.
response.body
๋ ํฌ๋กฌ์ ํ์ฌ ์์ ๋ฒ์ ์์ ์ง์ํ๋ค.
์คํธ๋ฆผ์ ์ด์ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฒด ์๋ต์ ์ ์งํ์ง ์๊ณ ํค๋์ ์์กดํ์ง ์์ผ๋ฉด์ ์๋ต ๋ด์ฉ์ ๊ธธ์ด๋ฅผ ์ป์ ์ ์๋ค.
// fetch() returns a promise that
// resolves once headers have been received
fetch(url).then(response => {
// response.body is a readable stream.
// Calling getReader() gives us exclusive access to
// the stream's content
var reader = response.body.getReader();
var bytesReceived = 0;
// read() returns a promise that resolves
// when a value has been received
reader.read().then(function processResult(result) {
// 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 (result.done) {
console.log("Fetch complete");
return;
}
// result.value for fetch streams is a Uint8Array
bytesReceived += result.value.length;
console.log('Received', bytesReceived, 'bytes of data so far');
// Read some more, and call this function again
return reader.read().then(processResult);
});
});
๋ฐ๋ชจ ๋ณด๊ธฐ (1.3mb)
๋ฐ๋ชจ์์๋ ์๋ฒ๋ก๋ถํฐ 1.3mb์ gzip์ผ๋ก ์์ถ๋(์์ถ์ 7.7mb) HTMLํ์ผ์ ๊ฐ์ ธ์๋ค. ๊ทธ๋ฌ๋, ๊ฒฐ๊ณผ๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋์ง ์๋๋ค. ๊ฐ ์กฐ๊ฐ์ ์ฌ์ด์ฆ๋ ๊ธฐ๋ก๋์ง๋ง ์กฐ๊ฐ๋ค ์์ฒด๋ GC(garbage collected)๋๋ค.
result.value๋ ์คํธ๋ฆผ์ด ์ ๊ณตํ๋ ๊ฒ์ ๋ฌด์์ด๋ (string, number, date, ImageData, DOM์๋ฆฌ๋จผํธ, โฆ) ๊ฐ์ ์์ฑํ์ง๋ง, ์ด๊ฒฝ์ฐ์ ๊ฐ์ ธ์ค๋ ์คํธ๋ฆผ์ ํญ์ ์ด์ง ๋ฐ์ดํฐ Unit8Array๋ค. ์ ์ฒด ์๋ต์ ๊ฐ๊ฐ์ Unit8Array๋ฅผ ๋ชจ์์ ๋ง์ถ ๊ฒ์ด๋ค. ํ ์คํธ๋ก ์๋ต์ ์ํ๋ค๋ฉด ์๋ ์์ ์ ๊ฐ์ด TextDecoder๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
var decoder = new TextDecoder();
var reader = response.body.getReader();
// read() returns a promise that resolves
// when a value has been received
reader.read().then(function processResult(result) {
if (result.done) return;
console.log(
decoder.decode(result.value, {stream: true})
);
// Read some more, and recall this function
return reader.read().then(processResult);
});
{stream: true}
๋ ๋์ฝ๋์ ๋ฒํผ ์ ์ง๋ฅผ ์๋ฏธํ๋ค. ๋ง์ฝ result.value
๊ฐ UTF-8 ์ฝ๋ ์ง์ ์ ํต๊ณผํด ์ค๊ฐ์์ ์ข
๋ฃ๋๋ค๋ฉด, โฅ
์ ๊ฐ์ ๋ฌธ์๋ 3 bytes([0xE2, 0x99, 0xA5]
)๋ง ๋ณด์ฌ์ฃผ๊ฒ ๋๋ค.
TextDecoder๋ ํ์ฌ๋ ์๊ณ ๋ณผํ์์ง๋ง ํฅํ์๋ ๋ณํ ์คํธ๋ฆผ(transform stream)์ด ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค(ํ๋ฒ์ ๋ณํ ์คํธ๋ฆผ์ ์ ์๋์ด์๋ค). ๋ณํ ์คํธ๋ฆผ์ .writable
์ ์ฐ๊ธฐ ๊ฐ๋ฅํ ์คํธ๋ฆผ๊ณผ .readable
์ ์ฝ์ ์ ์๋ ์คํธ๋ฆผ ๊ฐ์ฒด์ด๋ค. ๊ทธ๊ฒ์ ์กฐ๊ฐ๋ค์ ์ฐ๊ธฐ๊ฐ๋ฅํ๋ฉฐ ๊ฐ๊ณต๋ ์ํ๋ก ์ทจํด, ์ฝ๊ธฐ ๊ฐ๋ฅํ ์ํ๋ก ๋ด๋ณด๋ธ๋ค. ๋ณํ ์คํธ๋ฆผ์ ์ฌ์ฉ์์ ๋ ๋ค์๊ณผ ๊ฐ๋ค.
var reader = response.body
.pipeThrough(new TextDecoder()).getReader();
reader.read().then(result => {
// result.value will be a string
});
๋ธ๋ผ์ฐ์ ๋ ์์ ์ด ์์ ํ ์๋ต ์คํธ๋ฆผ๊ณผ TextDocoder ๋ณํ ์คํธ๋ฆผ ๋ชจ๋๋ฅผ ์ต์ ํํ ์ ์๋ค.
์คํธ๋ฆผ์ stream.cancel()
(ํจ์น์ ๊ฒฝ์ฐ response.body.cancel()
์ ์ฌ์ฉํ๋ค)์ด๋ reader.cancel()
์ ์ฌ์ฉํ์ฌ ์ทจ์ํ ์ ์๋ค. ๋ค์ด๋ก๋๋ฅผ ์ค์งํ์ฌ ๋ฐ์์ ์ป์ด๋ธ๋ค.
์ด ๋ฐ๋ชจ์์๋ ๊ฒ์์ด๋ก ํฐ ๋ฌธ์๋ฅผ ์ฐพ๋๋ฐ, ๋ฉ๋ชจ๋ฆฌ์์ ์์ ์ผ๋ถ๋ถ๋ง์ ์ ์งํ๋ฉฐ, ์ผ์นํ๋ ๋ถ๋ถ์ด ๋ฐ๊ฒฌ๋๋ฉด ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ๋ฉ์ถ๋ค.
์ด์จ๋ , ์ด ๋ชจ๋ ๊ฒ์ 2015๋ ๋ ๊ฐ๋ฅํ๋ ์ผ์ด๋ค. ์ด์ ๋ถํฐ ์ฌ๋ฏธ์๋ ์๋ก์ด ๊ฒ์ ๋ํด ์ด์ผ๊ธฐํ๊ฒ ๋ค.
ํฌ๋กฌ ์นด๋๋ฆฌ์(Chrome Canary, ๊ฐ๋ฐ์ ๋ฐ ์ผ๋ฆฌ์ด๋ตํฐ ์ฉ)์์ "์คํ์ ์ธ ์น ํ๋ซํผ ๊ธฐ๋ฅ(Experimental web platform features
)"์ ํ์ฑํ ์ํค๋ฉด ๋ฐ๋ก ์์ ์ ์คํธ๋ฆผ(your own stream)์ ์์ฑํ ์ ์๋ค.
var stream = new ReadableStream({
start(controller) {},
pull(controller) {},
cancel(reason) {}
}, queuingStrategy);
start
๋ ๊ณง๋ฐ๋ก ํธ์ถ๋๋ค. ์ด๋ ๋ชจ๋ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ค์ ํ๋๋ฐ ์ฌ์ฉ๋๋ค(๋ฌธ์์ด๊ณผ ๊ฐ์ ์ด๋ฒคํธ, ๋ค๋ฅธ ์คํธ๋ฆผ ๋๋ ๋ณ์ ๋ฑ์์ ๋ฐ์ดํฐ๋ฅผ ์ป์ ๋๋ฅผ ์๋ฏธํจ). ๋ง์ฝ ๋น์ ์ด ํ๋ผ๋ฏธ์ค(promise)๋ฅผ ๋ฐํํ๊ณ ๊ฑฐ์ ํ๋ค๋ฉด, ์คํธ๋ฆผ์ ํตํด ์๋ฌ ์ ํธ๋ฅผ ๋ณด๋ผ ๊ฒ์ด๋ค.pull
์ ์คํธ๋ฆผ์ ๋ฒํผ๊ฐ ๊ฐ๋์ฐจ์ง ์์์ ๋ ํธ์ถ๋๋ฉฐ, ๊ฐ๋์ฐฐ ๋ ๊น์ง ํธ์ถ๋๋ค. ๋ค์๋งํด ๋ง์ฝ ๋น์ ์ด ํ๋ผ๋ฏธ์ค๋ฅผ ๋ฐํํ๊ณ ๊ฑฐ์ ํ๋ค๋ฉด, ์คํธ๋ฆผ์ ํตํด ์๋ฌ ์ ํธ๋ฅผ ๋ณด๋ผ ๊ฒ์ด๋ค. ๋ํ, pull
์ ์์ ํ ํ๋ผ๋ฏธ์ค๊ฐ ๋ฐํ๋ ๋ ๊น์ง ๋ค์ ํธ์ถ๋์ง ์์ ๊ฒ์ด๋ค.cancel
์ ์คํธ๋ฆผ์ด ์ทจ์๋๋ฉด ํธ์ถ๋๋ค. ์ด๋ ๋ชจ๋ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ์์ค๋ฅผ ์ทจ์ ํ๋๋ฐ ์ฌ์ฉ๋๋ค.queuingStrategy
๋ ํ๋์ ์์ดํ
์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ๋ง๋ ๋ง์ ์คํธ๋ฆผ์ด ์ด๋ก ์ ์ผ๋ก ์์ด๋์ง๋ฅผ ์ ์ํ๋ค. - ๋ ์์ธํ ์ฌํญ์ ์ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ๊ธธ ๋ฐ๋๋ค.controller์ ๋ํด์๋
controller.enqueue(whatever)
- ์คํธ๋ฆผ ๋ฒํผ์ ํ ๋ฐ์ดํฐcontroller.close()
- ์คํธ๋ฆผ์ ์ข
๋ฃ ์ ํธcontroller.error(e)
- ํฐ๋ฏธ๋ ์๋ฌ ์ ํธcontroller.desiredSize
- ๋จ์์๋ ๋ฒํผ์ ์ด๋(๋ฒํผ๊ฐ ๊ฐ๋์ฐจ๋ฉด ์์์ผ ์ ์์). ์ด ์ซ์๋ queuingStrategy
๋ฅผ ์ฌ์ฉํ์ฌ ๊ณ์ฐ๋๋ค.๊ทธ๋์ ๋๋ ๋ค์ ์์ ์ ๊ฐ์ด 0.9๋ณด๋ค ํฐ ์ซ์๊ฐ ๋์ฌ๋ ๊น์ง ๋งค ์ด๋ง๋ค ๋๋ค ์ซ์๊ฐ ์์ฑ๋๋ ์คํธ๋ฆผ ์์ฑ์ ์ํ๋ค.
var interval;
var stream = new ReadableStream({
start(controller) {
interval = setInterval(() => {
var num = Math.random();
// Add the number to the stream
controller.enqueue(num);
if (num > 0.9) {
// Signal the end of the stream
controller.close();
clearInterval(interval);
}
}, 1000);
},
cancel() {
// This is called if the reader cancels,
//so we should stop generating numbers
clearInterval(interval);
}
});
์คํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์. (์๋ฆผ: ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ํฌ๋กฌ ์นด๋๋ฆฌ์์์ chrome://flags/#enable-experimental-web-platform-features
์ต์
์ ํ์ฑํ์์ผ์ผ ํจ)
controller.enqueue
๋ก ๋ฐ์ดํฐ๋ฅผ ํต๊ณผ ์ํค๋ ๊ฒ์ ์ง์ ํด๋ณด๊ธธ ๋ฐ๋๋ค. ์์ ์์ ์ ๊ฐ์ด ๋ณด๋ผ ๋ฐ์ดํฐ๊ฐ ์์ ๋, ์ง์ ๋ง๋ "push source" ์คํธ๋ฆผ์ ๋จ์ํ ํธ์ถํ ์ ์๋ค.
๊ทธ๋์ ์ pull
์ด ํธ์ถ๋ ๋ ๊น์ง ๊ธฐ๋ค๋ฆด ์ ์๋ค. ๊ทธ๋ฐ ํ ๊ธฐ๋ฐ ์์ค๋ก ๋ถํฐ ์์งํ ๋ฐ์ดํ ์ ํธ๋ฅผ ์ฌ์ฉํ ๋ค์ ์ง์ ๋ง๋ "pull source" ์คํธ๋ฆผ์ ๋๊ธฐ์ด์ ์ถ๊ฐํ๋ค. ๋๋ ์ํ๋ค๋ฉด, ๋๊ฐ์ง ๋ฐฉ๋ฒ์ ์กฐํฉํ ์ ๋ ์๋ค.
controller.desiredSize
๋ฅผ ๋ฐ๋ฅด๋ ๊ฒ์ ๊ฐ์ฅ ์คํธ๋ฆผ์ด ํจ์จ์ ์ธ ์๋์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ด๊ฒ์ "๋ฐฑํ๋ ์
(backpressure) ์ง์"๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ์๋ ค์ ธ ์๋๋ฐ, ์ด๋ ๋น์ ์ ์คํธ๋ฆผ์ด ๋ฆฌ๋์ ์ฝ๋ ์๋์ ๋ฐ์ํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค(์ด์ ์ ๋น๋์ค ๋์ฝ๋ฉ ์์ ์ ๋น์ทํจ). ๊ทธ๋ฌ๋ ๊ธฐ๊ธฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ค ์ฌ์ฉํ์ง ์๋ ํ ์ด๋ค ๊ฒ๋ desiredSize
๋ฅผ ๋ฌด์ํ๋ ๊ฒ์ ์ค๋จ์ํฌ ์ ์๋ค. ํด๋น ์คํ์ ๋ฐฑํ๋ ์
์ ์คํธ๋ฆผ ์์ฑํ๊ธฐ๋ผ๋ ์ข์ ์์ ๋ฅผ ๊ฐ๊ณ ์๋ค.
๊ทธ ์์ฒด๋ก ์คํธ๋ฆผ์ ์์ฑํ๋ ๊ฒ์ ํนํ๋ ์ฌ๋ฏธ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ฒ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ง์ํ๋ API๋ค๋ ๋ง์ง ์๋ค. ๊ทธ๋ฐ๊ฒ๋ค ์ค ํ๋๊ฐ ๋ค์์ Response
๋ค.
new Response(readableStream);
๋ณธ๋ฌธ(body
)์ด ์คํธ๋ฆผ์ธ HTTP ์๋ต ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๊ณ , ์๋น์ค ์์ปค๋ก ๋ถํฐ ์๋ต๋ฐ์ ๊ฒ์ ์ฌ์ฉํ ์ ์๋ค.
๋ฐ๋ชจ ๋ณด๊ธฐ (์๋ฆผ: ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ํฌ๋กฌ ์นด๋๋ฆฌ์์์ chrome://flags/#enable-experimental-web-platform-features
์ต์
์ ํ์ฑํ์์ผ์ผ ํจ)
๋น์ ์ HTML ํ์ด์ง๊ฐ ์ฒ์ฒํ(๊ณํ์ ์ผ๋ก) ๋ ๋๋ง ๋๋๊ฒ์ ๋ณผ ์ ์์ ๊ฒ์ด๋ค. ์ด ์๋ต์ ์ ์ ์ผ๋ก ์๋น์ค ์์ปค์์ ๋ฐ์์ํจ ๊ฒ์ด๋ค. ์ฌ๊ธฐ ๊ทธ ์ฝ๋๊ฐ ์๋ค.
// In the service worker:
self.addEventListener('fetch', event => {
var html = 'โฆhtml to serveโฆ';
var stream = new ReadableStream({
start(controller) {
var encoder = new TextEncoder();
// Our current position in `html`
var pos = 0;
// How much to serve on each push
var chunkSize = 1;
function push() {
// Are we done?
if (pos >= html.length) {
controller.close();
return;
}
// Push some of the html,
// converting it into an Uint8Array of utf-8 data
controller.enqueue(
encoder.encode(html.slice(pos, pos + chunkSize))
);
// Advance the position
pos += chunkSize;
// push again in ~5ms
setTimeout(push, 5);
}
// Let's go!
push();
}
});
return new Response(stream, {
headers: {'Content-Type': 'text/html'}
});
});
๋ธ๋ผ์ฐ์ ์์ ์๋ต ๋ณธ๋ฌธ์ ์ฝ์ ๋ Unit8Array
์ ์กฐ๊ฐ์ผ๋ก ์ป์ ๊ฒ์ ๊ธฐ๋ํ๋ค. ๋ง์ผ ํ๋ ์ธ ๋ฌธ์์ด๊ณผ ๊ฐ์ด ์ด๋ค ๋ค๋ฅธ๊ฒ์ด ํต๊ณผ ๋๋ค๋ฉด ๊ทธ๊ฒ์ ์คํจํ๋ค. ๊ณ ๋ง๊ฒ๋ TextEncoder
๋ ๋ฌธ์์ด์ ๊ฐ์ง๊ณ ๋ฌธ์์ด์ ํํํ๋ ๋ฐ์ดํธ์ Unit8Array
๋ก ๋ฐํํ ์ ์๋ค.
TextDecode
์ฒ๋ผ, TextEncoder
๋ ์์ผ๋ก ์คํธ๋ฆผ์ผ๋ก ๋ณํ ๋ ๊ฒ์ด๋ค.
์ด๋ฏธ ๋งํ๋ฏ์ด ๋ณํ ์คํธ๋ฆผ์ ์์ง ์ ์๋์ง ์์๋ค. ๊ทธ๋ฌ๋ ๋ค๋ฅธ ์คํธ๋ฆผ์์ ๋ฐ์ดํฐ ์์ค๋ฅผ ๊ณต๊ธํ๋ ์ฝ์ ์ ์๋ ์คํธ๋ฆผ(readable stream)์ ์์ฑํ์ฌ ๋น์ทํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ค.
๋ฐ๋ชจ ๋ณด๊ธฐ (์๋ฆผ: ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ํฌ๋กฌ ์นด๋๋ฆฌ์์์ chrome://flags/#enable-experimental-web-platform-features ์ต์ ์ ํ์ฑํ์์ผ์ผ ํจ)
๋น์ ์ด ๋ณผ ์ ์๋ ๊ฒ์ ์ด ํ์ด์ง(์ํคํผ๋์์์๋ ํด๋ผ์ฐ๋ ์ปดํจํ ์ํฐํด์์ ์ป์)์ด์ง๋ง, ๋ชจ๋ "cloud"๋ผ๋ ๋จ์ด๊ฐ "butt"๋ก ๋์ฒด๋ ๊ฒ์ด๋ค. ์คํธ๋ฆผ์ผ๋ก ํ๋ ๊ฒ์ ์ด์ ์ ์๋ณธ์ ๋ค์ด๋ก๋ ํ๋ ๋์ ํ๋ฉด์ ๋ณํ๋ ์ฝํ ์ธ ๋ฅผ ์ป์ ์ ์๋ค๋ ๊ฒ์ด๋ค.
๋ค์์ ์ฃ์ง ์ผ์ด์ค ์ผ๋ถ๋ถ์ ์์ธ๋ด์ฉ์ ํฌํจํ ์ฝ๋๊ฐ ์๋ ๋งํฌ๋ค. https://github.com/jakearchibald/isserviceworkerready/blob/master/src/demos/transform-stream/sw.js
๋น๋์ค ์ฝ๋ฑ์ ์ ๋ง ํจ์จ์ ์ด์ง๋ง, ๋ชจ๋ฐ์ผ์์ ์๋์คํ์ ๋ถ๊ฐ๋ฅํ๋ค. GIF๋ ์๋์คํ์ด ๊ฐ๋ฅํ์ง๋ง ๋น์ฉ์ด ํฌ๋ค. ์ง์ง๋ก ๋ฉ์ฒญํ ์๋ฃจ์ ์ด ์ฌ๊ธฐ์ ์๋ค.
๋ฐ๋ชจ ๋ณด๊ธฐ (์๋ฆผ: ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ํฌ๋กฌ ์นด๋๋ฆฌ์์์ chrome://flags/#enable-experimental-web-platform-features ์ต์ ์ ํ์ฑํ์์ผ์ผ ํจ)
MPEG ํ๋ ์ ๋์ฝ๋ฉ์ ํ๋ ๋์ GIF์ ์ฒซ๋ฒ์งธ ํ๋ ์์ด ํ์๋ ์ ์๋ ์ง๊ธ์ ์ํฉ์๋ ์คํธ๋ฆฌ๋ฐ์ ์ ์ฉํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒ์ด๋ค! 26mb GIF๋ฅผ ์ค๋ก์ง 0.9mb์ MPEG๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ฌํ๋๋ฐ, ์ค์๊ฐ์ผ๋ก ๋์ง ์๊ณ CPU๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค๋ ๊ฒ์ ์ ์ธํ๋ฉด ์๋ฒฝํ๋ค! ๋ธ๋ผ์ฐ์ ๋ ๋ชจ๋ฐ์ผ์์ ๋น๋์ค ์๋์คํ์ ์ ๋ง ํ์ฉํด์ผํ๋ค. ์์๊ฑฐ๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ๋ ํนํ๋ ๊ทธ๋ ๋ค. ์ด๊ฒ์ ํฌ๋กฌ์ด ์ง๊ธ ๋น์ฅ ๋ ธ๋ ฅํด์ผ ํธ๋ค.
์์ ๊ณต๊ฐ: ๋๋ ๋ฐ๋ชจ์์ ๊ผผ์๋ฅผ ๋ถ๋ ธ๋ค. ๋ชจ๋ MPEG๋ฅผ ์์์ ์ ๋ค์ด๋ก๋ ๋ฐ์๋จ๋ค. ๋๋ ๋คํธ์ํฌ๋ก ๋ถํฐ ์คํธ๋ฆฌ๋ฐ์ ์ป๊ธธ ์ํ์ง๋ง ์คํฌ์ด ๋ถ์กฑํ์ฌ(์๋ฌธ: OutOfSkillError
)๋ก ์คํํ ์ ์์๋ค. ๋ํ GIF๋ ์ ๋ง์ด์ง ๋ค์ด๋ก๋ ๋์ ๋ฐ๋ณตํ๋ฉด ์์์ผ ํ์ง๋ง, ์ง๊ธ์ ๋ฐ๋ณต๋๊ณ ์๋ค.
์ด๊ฒ์ ์๋ง๋ ์๋น์ค์ํฌ + ์คํธ๋ฆผ ์กฐํฉ์ ๊ฐ์ฅ ํ์ค์ ์ธ ์ ์ฉ์ด๋ค. ์ฅ์ ์ ์ฑ๋ฅ๋ฉด์์ ํฌ๋ค.
๋ช๊ฐ์ ์ ์ ๋๋ ์คํ๋ผ์ธ ์ฐ์ ์ํคํผ๋์์ ๋ฐ๋ชจ๋ฅผ ๊ฐ๋ฐํ๋ค. ๋๋ ๋น ๋ฅด๊ฒ ๋์ํ๋ฉฐ ํ๋์ ์ธ ํ์ฅ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋ ์ ๋ง ํ์ ์ ์ธ ์น์ฑ์ ๋ง๋ค๊ธฐ๋ฅผ ์ํ๋ค.
OSX ์ ๋คํธ์ํฌ ์ฐ๊ฒฐ ์ปจ๋์ ๋๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฎฌ๋ ์ด์ ํ ์์ค 3G ์ฐ๊ฒฐ ๊ธฐ๋ฐ์ ๋ํด ์ฑ๋ฅ๊ณผ ์์น์ ์ธ ๋ถ๋ถ์ ๋ํด์ ์ด์ผ๊ธฐํ๊ฒ ๋ค.
์๋น์ค ์์ปค ์์ด ํํ ์ปจํ ์ธ ๋ ์๋ฒ๋ก ๋ณด๋ด์ก๋ค. ์ฌ๊ธฐ ์ฑ๋ฅ์ ๋ง์ ๋ ธ๋ ฅ์ ๊ธฐ์ธ์๊ณ ๊ทธ์ ๋ํ ์ฑ๊ณผ๋ฅผ ์ป์๋ค.
๋์์ง๋ ์์๋ค. ์ฝ๊ฐ์ ์คํ๋ผ์ธ ์ฐ์ ์ ์ข์ ๋ถ๋ถ์ ํผํฉํ์ฌ ์ฑ๋ฅ ๋์ฑ์ ํฅ์์ํฌ ์ ์๋ ์๋น์ค ์์ปค๋ฅผ ์ถ๊ฐํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฒฐ๊ณผ๋?
์... ์ฐ์ ๋ ๋๋ง์ด ๋ ๋นจ๋ผ์ก๋ค. ๊ทธ๋ฌ๋ ์ปจํ ์ธ ๋ ๋๋ง ์์ ํฐ ํด๋ณด๊ฐ ๋ฐ์ํ๋ค.
๊ฐ์ฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ ์บ์ฌ์์ ์ง์ ํ์ด์ง๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ์ํคํผ๋์์ ๋ชจ๋ ์บ์ฑ์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค. ๊ทธ๋์ ์ ๋๋ CSS, ์๋ฐ์คํฌ๋ฆฝํธ ๊ทธ๋ฆฌ๊ณ ํค๋๋ฅผ ์ ๊ณตํ๋ค. ๋น ๋ฅธ ์ด๊ธฐ ๋ ๋๋ง์ ์ป๊ณ ๊ทธ๋ฐ ํ ๋ฌธ์์ ๋ด์ฉ์ ๊ฐ์ ธ์ค๋ ๊ฒ์ ๋ํ ํ์ด์ง์ ์๋ฐ์คํฌ๋ฆฝํธ ์ค์ ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ณณ์ด ๋ด๊ฐ ๋ชจ๋ ์ฑ๋ฅ์ ์์ด๋ฒ๋ฆฐ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง์ด๋ค.
์๋ฒ๋ก๋ถํฐ ์ง์ ์ ๊ณต๋ฐ๋ ์๋๋ฉด ์๋น์ค ์์ปค๋ฅผ ํตํ๋ , ๋ค์ด๋ก๋๋ HTML์ ๋ ๋๋ง ํ๋ค. ๊ทธ๋ฌ๋ ๋๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ ํ์ด์ง๋ก๋ถํฐ ๋ด์ฉ์ ๊ฐ์ ธ์๋ค. ๊ทธ๋ฐ ํ ์คํธ๋ฆฌ๋ฐ ํ์๋ฅผ ์ฐํํ์ฌ innerHTML
๋ก ์ถ๊ฐํ๋ค. ์ด ๋๋ฌธ์ ๋ด์ฉ์ด ํ์๋๊ธฐ ์ ์ ์์ ํ ๋ค์ด๋ก๋ ๋์๊ณ , 2์ด ๋ ๋๋ ค์ก๋ค. ๋ค์ด๋ก๋ํ๋ ์ปจํ
์ธ ๊ฐ ๋ง์์ง ์ ๋ก ์คํธ๋ฆฌ๋ฐ ์ฑ๋ฅ ์ํด๋ ๋ ๋์ด๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ ๋์๊ฒ๋ ๋ถํํ๊ฒ๋ ์ํคํผ๋์ ๋ฌธ์๋ ๋งค์ฐ ํฌ๋ค(๊ตฌ๊ธ ๋ฌธ์๋ 100k).
์ด๊ฒ์ด ๋ด๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ฐ์ ์น ์ฑ๊ณผ ํ๋ ์์ํฌ์ ๋ํด ํฌํธ๋๋ ์ด์ ๋ค - ๊ทธ๋ค์ 0๋จ๊ณ๋ก ์คํธ๋ฆฌ๋ฐ์ ๋ฒ๋ฆฌ๋ ๊ฒฝํฅ์ด ์๊ณ ๊ทธ ๊ฒฐ๊ณผ ์ฑ๋ฅ์ด ๋ ์์ข์์ง๋ค.
๋๋ ํ๋ฆฌํจ์นญ(prefetching)๊ณผ ๊ฐ์ง ์คํธ๋ฆฌ๋ฐ์ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ๋๋๋ฆฌ๊ธฐ ์ํด ๋
ธ๋ ธํ๋ค. ๊ฐ์ง ์คํธ๋ฆฌ๋ฐ์ ํนํ๋ ๋ํ ๊ผผ์๋ค. ํ์ด์ง๋ ๋ฌธ์์ ๋ด์ฉ์ ๊ฐ์ ธ์ ์คํธ๋ฆผ์ผ๋ก ์ฝ๋๋ค. ๋จผ์ ๋ด์ฉ์ 9k๋ฅผ ๋ฐ์ innerHTML
๋ก ์ถ๊ฐํ๊ณ ๋ค์ ๋๋จธ์ง ๋ด์ฉ์ ์ถ๊ฐํ๋ค. ์ด๊ฒ์ ์ผ๋ถ ์๋ฆฌ๋จผํธ๋ฅผ ๋๋ฒ ์์ฑํ๊ธฐ ๋๋ฌธ์ ๋์ฐํ์ง๋ง, ๊ทธ๋งํผ์ ๊ฐ์น๋ ์๋ค.
๊ผผ์๋ค์ ๋ฌธ์ ๋ด์ฉ์ด ๋ณด์ฌ์ง๋ ์๊ฐ์ ๊ฐ์ ํ์ง๋ง, ์ฌ์ ํ ๋ฉ๋ํ๊ธฐ ํ๋ค ๋งํผ ์๋ฒ ๋ ๋๋ง์ ๋นํด ๋ค์ณ์ง๋ค. ๋ฟ๋ง์๋๋ผ innerHTML
์ ์ฌ์ฉํ์ฌ ํ์ด์ง์ ์ถ๊ฐ๋ ๋ด์ฉ์ ์ผ๋ฐ์ ์ผ๋ก ๊ตฌ๋ฌธ ๋ถ์๋ ๋ด์ฉ๊ณผ ๋์ผํ๊ฒ ๋์ํ์ง ์๋๋ค. ํนํ ์ธ๋ผ์ธ <script>
๋ ์คํ๋์ง ์๋๋ค.
์ฌ๊ธฐ๊ฐ ์คํธ๋ฆผ์ด ๊ฐ์ ํ ๊ณณ์ด๋ค. ๋น ๊ป๋ฐ๊ธฐ๋ง ์ ๊ณตํ๊ณ JS์๊ฒ ๋ง๋๋ ์ญํ ์ ๋งก๊ธฐ๋ ๋์ ์บ์๋ก ๋ถํฐ ์จ ํค๋์์ ์๋น์ค ์์ปค์๊ฒ ์คํธ๋ฆผ์ ์์ฑํ๋๋ก ํ๋ค.๊ทธ๋ฌ๋ ๋ณธ๋ฌธ์ ๋คํธ์ํฌ๋ก ๋ถํฐ ์จ๋ค. ๊ทธ๊ฒ์ ์๋ฒ ๋ ๋๋ง๊ณผ ๊ฐ์ง๋ง ์๋น์ค ์์ปค๋ฅผ ์ด์ฉํ ๊ฒ์ด๋ค.
๋ฐ๋ชจ ๋ณด๊ธฐ (์๋ฆผ: ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ํฌ๋กฌ ์นด๋๋ฆฌ์์์ chrome://flags/#enable-experimental-web-platform-features ์ต์ ์ ํ์ฑํ์์ผ์ผ ํจ)
์๋น์ค ์ํฌ + ์คํธ๋ฆผ ์กฐํฉ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ฑฐ์ ์งง์ ์๊ฐ์ ์ฒซ๋ฒ์งธ ๋ ๋๋ง์ ํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ๊ทธ๋ฐ๋ค์ ๋คํธ์ํฌ๋ก ๋ถํฐ ๋ด์ฉ์ ์์์๋ง ํ์ดํํ์ฌ ์ผ๋ฐ ์๋ฒ ๋ ๋๋ง์ ์ํํ๋ฉด ๋๋ค.
๋ด์ฉ์ ์ผ๋ฐ์ ์ธ HTML ํ์๋ก ํต๊ณผ๋๋ค. ๊ทธ๋์ ๋น์ ์ ์คํธ๋ฆฌ๋ฐ์ ์ป๊ฒ๋๊ณ ๊ทธ๊ฒ์ ์๋์ผ๋ก DOM์ผ๋ก ๋ถํฐ ์ถ๊ฐ๋ ์ปจํ ์ธ ๋ฅผ ์ป๋ ๊ฒ๊ณผ ๋ค๋ฅด์ง ์๋ค.
๊ฒฐํฉ๋ ์คํธ๋ฆผ์ ํ์ดํ(piping)์ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์, ์คํธ๋ฆผ์ ๊ฒฐํฉํ๋ ๊ฒ์ ์กฐ๊ธ ์ง์ ๋ถํ๊ฒ ์๋์ผ๋ก ์ํํด์ผ ํ๋ค.
var stream = new ReadableStream({
start(controller) {
// Get promises for response objects for each page part
// The start and end come from a cache
var startFetch = caches.match('/page-start.inc');
var endFetch = caches.match('/page-end.inc');
// The middle comes from the network, with a fallback
var middleFetch = fetch('/page-middle.inc')
.catch(() => caches.match('/page-offline-middle.inc'));
function pushStream(stream) {
// Get a lock on the stream
var reader = stream.getReader();
return reader.read().then(function process(result) {
if (result.done) return;
// Push the value to the combined stream
controller.enqueue(result.value);
// Read more & process
return read().then(process);
});
}
// Get the start response
startFetch
// Push its contents to the combined stream
.then(response => pushStream(response.body))
// Get the middle response
.then(() => middleFetch)
// Push its contents to the combined stream
.then(response => pushStream(response.body))
// Get the end response
.then(() => endFetch)
// Push its contents to the combined stream
.then(response => pushStream(response.body))
// Close our stream, we're done!
.then(() => controller.close());
}
});
์ถ๋ ฅ๋ฌผ์ ์คํธ๋ฆผํ๊ณ ํ ํ๋ฆฟ ์์ ํฌํจ๋ ๊ฐ์ ์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ๋ฉฐ ๋ด์ฉ์ ๋ํ ํ์ดํ(piping)๊ณผ ์ฌ์ง์ด ์ฆ์์์ HTML ์ด์ค์ผ์ดํํ๋ Dust.js์ ๊ฐ์ ์ผ๋ถ ํ ํ๋ฆฟํ ์ธ์ด๊ฐ ์๋ค. ๋น ํธ๋ฆฐ ๊ฒ์ด๋ผ๊ณค ์น ์คํธ๋ฆผ ์ง์์ด๋ค.
์ฝ์ ์ ์๋ ์คํธ๋ฆผ ์ธ์ ์คํ์ด ์์ง ๊ฐ๋ฐ๋๊ณ ์์์๋, ์ด๋ฏธ ์ฌ์ฉํ ์ ์๋ค๋ ๊ฒ์ ๋งค์ฐ ๋๋ผ์ด ์ผ์ด๋ค. ๋ง์ฝ ์ปจํ ์ธ ๊ฐ ๋ง์ ์ฌ์ดํธ์ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ฉด์ ๊ตฌ์กฐ์ ๊ทผ๋ณธ์ ์ธ ๋ณํ ์๋ ์คํ๋ผ์ธ ์ฐ์ ๊ฒฝํ์ ์ ๊ณตํ๊ธฐ ์ํ๋ค๋ฉด, ์๋น์ค ์์ปค๋ก ์คํธ๋ฆผ์ ๊ตฌ์ฑํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด ๋ ๊ฒ์ด๋ค. ๊ทธ๊ฒ์ ์ด์จ๋ ๋ด๊ฐ ์๊ฐํ๋ ๋ธ๋ก๊ทธ๊ฐ ์คํ๋ผ์ธ ์ฐ์ ์์ ์ ํ๊ฒํ๋ ๋ฐฉ๋ฒ์ด๋ค!
์น์์ ์ด๊ธฐ์ ์คํธ๋ฆผ์ ๊ฐ๋ ๊ฒ์ ์ฐ๋ฆฌ๊ฐ ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ฏธ ๋ณด์ ํ๊ณ ์๋ ์คํธ๋ฆฌ๋ฐ ๋ฅ๋ ฅ์ ๋ํด ์คํฌ๋ฆฝํธ๋ก์ ์ ๊ทผ์ ์์ํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ๋ค์๊ณผ ๊ฐ์ดโฆ
์์ง์ ์ด๊ธฐ์ง๋ง ๋ง์ฝ ๋น์ ์ด ์คํธ๋ฆผ์ ๋ํด ์์ ์ API๋ฅผ ์ค๋นํ๊ธฐ ์์ํ๋ค๋ฉด, ์ผ๋ถ ๊ฒฝ์ฐ์ ๋ํด์ ํด๋ฆฌํ(polyfill)์ ์ฌ์ฉํ ์ ์๋ ์ฐธ์กฐ ๊ตฌํ(reference implementation)์ ์๋ค.
์คํธ๋ฆฌ๋ฐ์ ๋ธ๋ผ์ฐ์ ์ ๊ฐ์ฅ ํฐ ์์ฐ ์ค ํ๋๋ค. ๊ทธ๋ฆฌ๊ณ 2016๋ ์ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ํด ์ ๊ธ ํด์ ๋๋ ํด๋ค.