diff --git a/5-network/01-fetch/article.md b/5-network/01-fetch/article.md index a18c8bf6b4..3f6f3a9d49 100644 --- a/5-network/01-fetch/article.md +++ b/5-network/01-fetch/article.md @@ -27,11 +27,7 @@ let promise = fetch(url, [options]) - **`url`** -- 접근하고자 하는 URL - **`options`** -- 선택 매개변수, method나 header 등을 지정할 수 있음 -<<<<<<< HEAD `options`에 아무것도 넘기지 않으면 요청은 `GET` 메서드로 진행되어 `url`로부터 콘텐츠가 다운로드 됩니다. -======= -Without `options`, this is a simple GET request, downloading the contents of the `url`. ->>>>>>> upstream/master `fetch()`를 호출하면 브라우저는 네트워크 요청을 보내고 프라미스가 반환됩니다. 반환되는 프라미스는 `fetch()`를 호출하는 코드에서 사용됩니다. @@ -65,21 +61,12 @@ if (response.ok) { // HTTP 상태 코드가 200~299일 경우 `response` 에는 프라미스를 기반으로 하는 다양한 메서드가 있습니다. 이 메서드들을 사용하면 다양한 형태의 응답 본문을 처리할 수 있습니다. -<<<<<<< HEAD - **`response.text()`** -- 응답을 읽고 텍스트를 반환합니다, - **`response.json()`** -- 응답을 JSON 형태로 파싱합니다, - **`response.formData()`** -- 응답을 `FormData` 객체 형태로 반환합니다. `FormData`에 대한 자세한 내용은 [다음 챕터](info:formdata)에서 다루겠습니다. - **`response.blob()`** -- 응답을 [Blob](info:blob)(타입이 있는 바이너리 데이터) 형태로 반환합니다. - **`response.arrayBuffer()`** -- 응답을 [ArrayBuffer](info:arraybuffer-binary-arrays)(바이너리 데이터를 로우 레벨 형식으로 표현한 것) 형태로 반환합니다. - 이 외에도 `response.body`가 있는데, [ReadableStream](https://streams.spec.whatwg.org/#rs-class) 객체인 `response.body`를 사용하면 응답 본문을 청크 단위로 읽을 수 있습니다. 자세한 용례는 곧 살펴보겠습니다. -======= -- **`response.text()`** -- read the response and return as text, -- **`response.json()`** -- parse the response as JSON, -- **`response.formData()`** -- return the response as `FormData` object (explained in the [next chapter](info:formdata)), -- **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), -- **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level representation of binary data), -- additionally, `response.body` is a [ReadableStream](https://streams.spec.whatwg.org/#rs-class) object, it allows you to read the body chunk-by-chunk, we'll see an example later. ->>>>>>> upstream/master 지금까지 배운 내용을 토대로 GitHub에서 마지막 커밋을 JSON 객체 형태로 받아봅시다. @@ -205,21 +192,12 @@ let response = fetch(protectedUrl, { `GET` 이외의 요청을 보내려면 추가 옵션을 사용해야 합니다. -<<<<<<< HEAD - **`method`** -- HTTP 메서드(예: `POST`) - **`body`** -- 요청 본문으로 다음 항목 중 하나이어야 합니다. - 문자열(예: JSON 문자열) - `FormData`객체 -- `form/multipart` 형태로 데이터를 전송하기 위해 쓰입니다. - `Blob`나 `BufferSource` -- 바이너리 데이터 전송을 위해 쓰입니다. - [URLSearchParams](info:url) -- 데이터를 `x-www-form-urlencoded` 형태로 보내기 위해 쓰이는데, 요즘엔 잘 사용하지 않습니다. -======= -- **`method`** -- HTTP-method, e.g. `POST`, -- **`body`** -- the request body, one of: - - a string (e.g. JSON-encoded), - - `FormData` object, to submit the data as `multipart/form-data`, - - `Blob`/`BufferSource` to send binary data, - - [URLSearchParams](info:url), to submit the data in `x-www-form-urlencoded` encoding, rarely used. ->>>>>>> upstream/master 대부분은 JSON을 요청 본문에 실어 보내게 됩니다. @@ -318,7 +296,6 @@ fetch(url, options) .then(result => /* 결과 처리 */) ``` -<<<<<<< HEAD 응답 객체의 프로퍼티는 다음과 같습니다. - `response.status` -- 응답의 HTTP 코드 - `response.ok` -- 응답 상태가 200과 299 사이에 있는 경우 `true` @@ -330,19 +307,6 @@ fetch(url, options) - **`response.formData()`** -- 응답을 `FormData` 객체 형태로 반환(form/multipart 인코딩에 대한 내용은 다음 챕터에서 다룸) - **`response.blob()`** -- 응답을 [Blob](info:blob)(타입이 있는 바이너리 데이터) 형태로 반환 - **`response.arrayBuffer()`** -- 응답을 [ArrayBuffer](info:arraybuffer-binary-arrays)(바이너리 데이터를 로우 레벨로 표현한 것) 형태로 반환 -======= -Response properties: -- `response.status` -- HTTP code of the response, -- `response.ok` -- `true` if the status is 200-299. -- `response.headers` -- Map-like object with HTTP headers. - -Methods to get response body: -- **`response.text()`** -- return the response as text, -- **`response.json()`** -- parse the response as JSON object, -- **`response.formData()`** -- return the response as `FormData` object (`multipart/form-data` encoding, see the next chapter), -- **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), -- **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level binary data), ->>>>>>> upstream/master 지금까지 배운 `fetch` 옵션은 다음과 같습니다. - `method` -- HTTP 메서드 diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md index 197b77b8d2..a90d225ae8 100644 --- a/5-network/02-formdata/article.md +++ b/5-network/02-formdata/article.md @@ -47,11 +47,7 @@ HTML에 `form` 요소가 있는 경우, 위와 같은 코드를 작성하면 해 ``` -<<<<<<< HEAD -요청을 받아 처리하는 서버 측 코드는 튜토리얼 범위를 넘어서서 추가하진 않았는데, 서버는 POST 요청을 받아 '저장 성공'이라는 응답을 보내준다고 정도만 알고 계시면 됩니다. -======= -In this example, the server code is not presented, as it's beyond our scope. The server accepts the POST request and replies "User saved". ->>>>>>> upstream/master +이 예시에서 요청을 받아 처리하는 서버 코드는 튜토리얼 범위를 벗어나므로 제시하지 않습니다. 서버는 POST 요청을 받고 "저장 성공"이라고 응답을 보내준다는 정도만 알고 계시면 됩니다. ## FormData 메서드 @@ -172,11 +168,7 @@ formData.append("image", imageBlob, "image.png"); [FormData](https://xhr.spec.whatwg.org/#interface-formdata) 객체는 `fetch` 등의 네트워크 메서드를 통해 HTML 폼을 보내는데 사용됩니다. -<<<<<<< HEAD `FormData` 객체는 HTML 폼(`form`)을 직접 넘겨 `new FormData(form)`으로 만들 수도 있고, HTML 폼 없이 다음과 같은 메서드로 필드를 추가해 만들 수도 있습니다. -======= -We can either create `new FormData(form)` from an HTML form, or create an object without a form at all, and then append fields with methods: ->>>>>>> upstream/master - `formData.append(name, value)` - `formData.append(name, blob, fileName)` diff --git a/5-network/04-fetch-abort/article.md b/5-network/04-fetch-abort/article.md index a138531407..0ed70d88e7 100644 --- a/5-network/04-fetch-abort/article.md +++ b/5-network/04-fetch-abort/article.md @@ -1,87 +1,60 @@ -# Fetch: Abort +# Fetch: 요청 중단하기 -As we know, `fetch` returns a promise. And JavaScript generally has no concept of "aborting" a promise. So how can we cancel an ongoing `fetch`? E.g. if the user actions on our site indicate that the `fetch` isn't needed any more. +`fetch`는 프라미스를 반환합니다. 그런데 자바스크립트에는 일반적으로 프라미스를 '중단'한다는 개념이 없습니다. 그렇다면 진행 중인 `fetch`는 어떻게 취소할 수 있을까요? 예를 들어 사이트에서 사용자 행동을 보고 더 이상 `fetch`가 필요 없다고 판단한 경우처럼 말이죠. -There's a special built-in object for such purposes: `AbortController`. It can be used to abort not only `fetch`, but other asynchronous tasks as well. +이런 목적을 위해 만들어진 특별한 내장 객체가 있습니다. 바로 `AbortController`입니다. `AbortController`를 사용하면 `fetch`뿐만 아니라 다른 비동기 작업도 중단할 수 있습니다. -The usage is very straightforward: +사용법은 아주 간단합니다. -## The AbortController object +## AbortController 객체 -Create a controller: +컨트롤러를 하나 만듭니다. ```js let controller = new AbortController(); ``` -A controller is an extremely simple object. +컨트롤러는 아주 단순한 객체입니다. -- It has a single method `abort()`, -<<<<<<< HEAD -- And a single property `signal` that allows to set event liseners on it. -======= -- And a single property `signal` that allows to set event listeners on it. ->>>>>>> upstream/master +- 메서드는 `abort()` 하나뿐입니다. +- 이벤트 리스너를 설정할 수 있는 프로퍼티도 `signal` 하나뿐입니다. -When `abort()` is called: -- `controller.signal` emits the `"abort"` event. -- `controller.signal.aborted` property becomes `true`. +`abort()`가 호출되면 다음 일이 일어납니다. +- `controller.signal`에서 `"abort"` 이벤트가 발생합니다. +- `controller.signal.aborted` 프로퍼티 값이 `true`가 됩니다. -<<<<<<< HEAD -Generally, we have two parties in the process: -1. The one that performs an cancelable operation, it sets a listener on `controller.signal`. -2. The one one that cancels: it calls `controller.abort()` when needed. -======= -Generally, we have two parties in the process: -1. The one that performs a cancelable operation, it sets a listener on `controller.signal`. -2. The one that cancels: it calls `controller.abort()` when needed. ->>>>>>> upstream/master +보통 이 과정에는 두 주체가 있습니다. +1. 취소 가능한 작업을 수행하는 쪽은 `controller.signal`에 리스너를 설정합니다. +2. 취소하는 쪽은 필요할 때 `controller.abort()`를 호출합니다. -Here's the full example (without `fetch` yet): +전체 예시를 살펴봅시다. 아직 `fetch`는 사용하지 않습니다. ```js run let controller = new AbortController(); let signal = controller.signal; -<<<<<<< HEAD -// The party that performs a cancelable operation -// gets "signal" object -======= -// The party that performs a cancelable operation -// gets the "signal" object ->>>>>>> upstream/master -// and sets the listener to trigger when controller.abort() is called -signal.addEventListener('abort', () => alert("abort!")); +// 취소 가능한 작업을 수행하는 쪽은 +// "signal" 객체를 받고, +// controller.abort()가 호출되었을 때 실행될 리스너를 설정합니다. +signal.addEventListener('abort', () => alert("중단!")); -// The other party, that cancels (at any point later): -controller.abort(); // abort! +// 취소하는 쪽은 나중에 언제든 다음을 호출합니다. +controller.abort(); // 중단! -// The event triggers and signal.aborted becomes true +// 이벤트가 발생하고 signal.aborted가 true가 됩니다. alert(signal.aborted); // true ``` -<<<<<<< HEAD -As we can see, `AbortController` is just a means to pass `abort` events when `abort()` is called on it. +예시에서 볼 수 있듯이 `AbortController`는 `abort()`가 호출되었을 때 `abort` 이벤트를 전달하는 수단입니다. -We could implement same kind of event listening in our code on our own, without `AbortController` object at all. +`AbortController` 객체 없이도 코드에서 이런 이벤트 리스닝 방식을 직접 구현할 수 있습니다. -But what's valuable is that `fetch` knows how to work with `AbortController` object, it's integrated with it. +하지만 중요한 점은 `fetch`가 `AbortController` 객체와 함께 동작하는 방법을 알고 있다는 것입니다. `fetch`와 `AbortController`는 통합되어 있습니다. -## Using with fetch +## fetch와 함께 사용하기 -To become able to cancel `fetch`, pass the `signal` property of an `AbortController` as a `fetch` option: -======= -As we can see, `AbortController` is just a mean to pass `abort` events when `abort()` is called on it. - -We could implement the same kind of event listening in our code on our own, without the `AbortController` object. - -But what's valuable is that `fetch` knows how to work with the `AbortController` object. It's integrated in it. - -## Using with fetch - -To be able to cancel `fetch`, pass the `signal` property of an `AbortController` as a `fetch` option: ->>>>>>> upstream/master +`fetch`를 취소할 수 있게 하려면 `AbortController`의 `signal` 프로퍼티를 `fetch` 옵션으로 넘기면 됩니다. ```js let controller = new AbortController(); @@ -90,26 +63,22 @@ fetch(url, { }); ``` -The `fetch` method knows how to work with `AbortController`. It will listen to `abort` events on `signal`. +`fetch` 메서드는 `AbortController`와 함께 동작하는 방법을 알고 있습니다. `fetch`는 `signal`의 `abort` 이벤트를 감지합니다. -<<<<<<< HEAD -Now, to to abort, call `controller.abort()`: -======= -Now, to abort, call `controller.abort()`: ->>>>>>> upstream/master +이제 중단하려면 `controller.abort()`를 호출하면 됩니다. ```js controller.abort(); ``` -We're done: `fetch` gets the event from `signal` and aborts the request. +이게 전부입니다. `fetch`가 `signal`로부터 이벤트를 받아 요청을 중단합니다. -When a fetch is aborted, its promise rejects with an error `AbortError`, so we should handle it, e.g. in `try..catch`. +`fetch`가 중단되면 해당 프라미스는 `AbortError` 에러와 함께 거부됩니다. 따라서 `try..catch` 등으로 이를 처리해야 합니다. -Here's the full example with `fetch` aborted after 1 second: +다음은 1초 후 `fetch`를 중단하는 전체 예시입니다. ```js run async -// abort in 1 second +// 1초 후 중단 let controller = new AbortController(); setTimeout(() => controller.abort(), 1000); @@ -118,71 +87,62 @@ try { signal: controller.signal }); } catch(err) { - if (err.name == 'AbortError') { // handle abort() - alert("Aborted!"); + if (err.name == 'AbortError') { // abort() 처리 + alert("중단되었습니다!"); } else { throw err; } } ``` -## AbortController is scalable +## AbortController는 확장성이 좋습니다 -<<<<<<< HEAD -`AbortController` is scalable, it allows to cancel multiple fetches at once. -======= -`AbortController` is scalable. It allows to cancel multiple fetches at once. ->>>>>>> upstream/master +`AbortController`는 확장성이 좋습니다. 여러 `fetch`를 한꺼번에 취소할 수 있습니다. -Here's a sketch of code that fetches many `urls` in parallel, and uses a single controller to abort them all: +다음은 여러 `url`을 병렬로 가져오고, 하나의 컨트롤러로 모든 요청을 중단하는 코드 예시입니다. ```js -let urls = [...]; // a list of urls to fetch in parallel +let urls = [...]; // 병렬로 가져올 url 목록 let controller = new AbortController(); -// an array of fetch promises +// fetch 프라미스 배열 let fetchJobs = urls.map(url => fetch(url, { signal: controller.signal })); let results = await Promise.all(fetchJobs); -// if controller.abort() is called from anywhere, -// it aborts all fetches +// 어디서든 controller.abort()가 호출되면 +// 모든 fetch가 중단됩니다. ``` -If we have our own asynchronous tasks, different from `fetch`, we can use a single `AbortController` to stop those, together with fetches. +`fetch`와는 다른 자체 비동기 작업이 있다면 하나의 `AbortController`를 사용해 해당 작업과 `fetch`를 함께 멈출 수 있습니다. -We just need to listen to its `abort` event in our tasks: +작업 안에서 `abort` 이벤트를 감지하기만 하면 됩니다. ```js let urls = [...]; let controller = new AbortController(); -let ourJob = new Promise((resolve, reject) => { // our task +let ourJob = new Promise((resolve, reject) => { // 자체 작업 ... controller.signal.addEventListener('abort', reject); }); -let fetchJobs = urls.map(url => fetch(url, { // fetches +let fetchJobs = urls.map(url => fetch(url, { // fetch 작업들 signal: controller.signal })); -// Wait for fetches and our task in parallel +// fetch 작업과 자체 작업을 병렬로 기다립니다. let results = await Promise.all([...fetchJobs, ourJob]); -// if controller.abort() is called from anywhere, -// it aborts all fetches and ourJob +// 어디서든 controller.abort()가 호출되면 +// 모든 fetch와 ourJob이 중단됩니다. ``` -## Summary +## 요약 -<<<<<<< HEAD -- `AbortController` is a simple object that generates `abort` event on it's `signal` property when `abort()` method is called (and also sets `signal.aborted` to `true`). -- `fetch` integrates with it: we pass `signal` property as the option, and then `fetch` listens to it, so it becomes possible to abort the `fetch`. -======= -- `AbortController` is a simple object that generates an `abort` event on its `signal` property when the `abort()` method is called (and also sets `signal.aborted` to `true`). -- `fetch` integrates with it: we pass the `signal` property as the option, and then `fetch` listens to it, so it's possible to abort the `fetch`. ->>>>>>> upstream/master -- We can use `AbortController` in our code. The "call `abort()`" -> "listen to `abort` event" interaction is simple and universal. We can use it even without `fetch`. +- `AbortController`는 `abort()` 메서드가 호출되면 `signal` 프로퍼티에서 `abort` 이벤트를 발생시키는 단순한 객체입니다. 이때 `signal.aborted`도 `true`로 설정됩니다. +- `fetch`는 `AbortController`와 통합되어 있습니다. `signal` 프로퍼티를 옵션으로 넘기면 `fetch`가 이를 감지하므로 `fetch`를 중단할 수 있습니다. +- `AbortController`는 일반 코드에서도 사용할 수 있습니다. `abort()` 호출과 `abort` 이벤트 감지로 이어지는 상호작용은 단순하고 범용적입니다. `fetch` 없이도 사용할 수 있습니다. diff --git a/5-network/05-fetch-crossorigin/article.md b/5-network/05-fetch-crossorigin/article.md index 7bddfaa91e..7864fabcf1 100644 --- a/5-network/05-fetch-crossorigin/article.md +++ b/5-network/05-fetch-crossorigin/article.md @@ -28,11 +28,7 @@ CORS는 악의를 가진 해커로부터 인터넷을 보호하기 위해 만들 **과거 수 년 동안, 한 사이트의 스크립트에서 다른 사이트에 있는 콘텐츠에 접근할 수 없다는 제약이 있었습니다.** -<<<<<<< HEAD 이런 간단하지만 강력한 규칙은 인터넷 보안을 위한 근간이었습니다. 보안 규칙 덕분에 해커가 만든 웹 사이트 `hacker.com`에서 `gmail.com`에 있는 메일 박스에 접근할 수 없던 것이죠. 사람들은 이런 제약 덕분에 안전하게 인터넷을 사용할 수 있었습니다. -======= -That simple, yet powerful rule was a foundation of the internet security. E.g. an evil script from website `hacker.com` could not access the user's mailbox at website `gmail.com`. People felt safe. ->>>>>>> upstream/master 그런데 이 당시의 자바스크립트는 네트워크 요청을 보낼 수 있을 만한 메서드를 지원하지 않았습니다. 자바스크립트는 웹 페이지를 꾸미기 위한 토이 랭귀지 수준이었죠. @@ -48,11 +44,7 @@ That simple, yet powerful rule was a foundation of the internet security. E.g. a */!* -<<<<<<< HEAD - -======= - ->>>>>>> upstream/master + *!*
*/!* @@ -105,15 +97,10 @@ That simple, yet powerful rule was a foundation of the internet security. E.g. a 처음 네트워크 요청 메서드가 등장했을 때엔 크로스 오리진 요청이 불가능했습니다. 하지만 긴 논의 끝에 크로스 오리진 요청을 허용하기로 결정합니다. 대신 크로스 오리진 요청은 서버에서 명시적으로 크로스 오리진 요청을 '허가' 했다는 것을 알려주는 특별한 헤더를 전송받았을 때만 가능하도록 제약을 걸게 됩니다. -<<<<<<< HEAD ## 안전한 요청 -======= -## Safe requests ->>>>>>> upstream/master 크로스 오리진 요청은 크게 두 가지 종류로 구분됩니다. -<<<<<<< HEAD 1. 안전한 요청(safe request) 2. 그 외의 요청(안전한 요청이 아닌 요청) @@ -169,7 +156,6 @@ And, unless the server explicitly confirms that with headers, an unsafe request 이제 개괄적인 설명이 끝났으니 CORS에 대해 좀 더 자세히 알아봅시다. -<<<<<<< HEAD ## CORS와 안전한 요청 크로스 오리진 요청을 보낼 경우 브라우저는 항상 `Origin`이라는 헤더를 요청에 추가합니다. @@ -196,11 +182,6 @@ Origin: https://javascript.info 보시다시피 `Origin` 헤더엔 요청이 이뤄지는 페이지 경로(/page)가 아닌 오리진(도메인·프로토콜·포트) 정보가 담기게 됩니다. 서버는 요청 헤더에 있는 `Origin`를 검사하고, 요청을 받아들이기로 동의한 상태라면 특별한 헤더 `Access-Control-Allow-Origin`를 응답에 추가합니다. 이 헤더엔 허가된 오리진(위 예시에선 `https://javascript.info`)에 대한 정보나 `*`이 명시됩니다. 이때 응답 헤더 `Access-Control-Allow-Origin`에 오리진 정보나 `*`이 들어있으면 응답은 성공하고 그렇지 않으면 응답이 실패하게 됩니다. -======= -As you can see, the `Origin` header contains exactly the origin (domain/protocol/port), without a path. - -The server can inspect the `Origin` and, if it agrees to accept such a request, add a special header `Access-Control-Allow-Origin` to the response. That header should contain the allowed origin (in our case `https://javascript.info`), or a star `*`. Then the response is successful, otherwise it's an error. ->>>>>>> upstream/master 이 과정에서 브라우저는 중재인의 역할을 합니다. 1. 브라우저는 크로스 오리진 요청 시 `Origin`에 값이 제대로 설정, 전송되었는지 확인합니다. @@ -219,11 +200,7 @@ Access-Control-Allow-Origin: https://javascript.info ## 응답 헤더 -<<<<<<< HEAD 크로스 오리진 요청이 이뤄진 경우, 자바스크립트는 기본적으로 '안전한' 응답 헤더로 분류되는 헤더에만 접속할 수 있습니다. '안전한' 응답 헤더는 다음과 같습니다. -======= -For cross-origin request, by default JavaScript may only access so-called "safe" response headers: ->>>>>>> upstream/master - `Cache-Control` - `Content-Language` diff --git a/5-network/10-long-polling/article.md b/5-network/10-long-polling/article.md index 88a748a0fc..eaad676eb2 100644 --- a/5-network/10-long-polling/article.md +++ b/5-network/10-long-polling/article.md @@ -1,47 +1,39 @@ # 롱 폴링 -<<<<<<< HEAD -폴링(long polling)을 사용하면 웹소켓이나 server-sent event 같은 특정한 프로토콜을 사용하지 않아도 아주 간단히 서버와 지속적인 커넥션을 유지할 수 있습니다. -======= -Long polling is the simplest way of having persistent connection with server, that doesn't use any specific protocol like WebSocket or Server Sent Events. ->>>>>>> upstream/master +롱 폴링(long polling)은 웹소켓이나 서버 전송 이벤트(Server-Sent Events) 같은 특정 프로토콜을 사용하지 않고 서버와 지속적인 연결을 유지할 수 있는 가장 간단한 방법입니다. 폴링은 구현이 매우 쉽고 다양한 경우에 사용할 수 있습니다. -## Regular Polling +## 폴링 -<<<<<<< HEAD -폴링(regular polling)을 사용하면 서버에서 새로운 정보를 아주 간단히 받을 수 있습니다. 10초에 한 번씩 서버에 "안녕하세요. 저 클라이언트인데 새로운 정보 줄거 있나요?" 라고 요청을 보내는 식으로 말이죠. -======= -The simplest way to get new information from the server is periodic polling. That is, regular requests to the server: "Hello, I'm here, do you have any information for me?". For example, once every 10 seconds. ->>>>>>> upstream/master +주기적인 폴링(regular polling)을 통해 간단하게 서버에서 새로운 정보를 얻을 수 있습니다. 10초에 한 번씩 서버에 "안녕하세요. 저 클라이언트인데 저에게 줄 새로운 정보가 있나요?"라고 요청을 보내는 방식입니다. -In response, the server first takes a notice to itself that the client is online, and second - sends a packet of messages it got till that moment. +서버는 응답을 보내며 우선 클라이언트가 온라인 상태라는 것을 확인하고, 그다음 해당 시점까지 받은 메시지 묶음을 전송합니다. -That works, but there are downsides: -1. Messages are passed with a delay up to 10 seconds (between requests). -2. Even if there are no messages, the server is bombed with requests every 10 seconds, even if the user switched somewhere else or is asleep. That's quite a load to handle, speaking performance-wise. +이 방식은 잘 작동하지만 단점이 있습니다. +1. 메시지는 요청 간격만큼, 예시에선 최대 10초까지 지연되어 전달됩니다. +2. 메시지가 없어도 서버는 10초마다 계속 요청을 받습니다. 사용자가 다른 곳으로 이동했거나 아무 동작도 하지 않는 경우에도 마찬가지입니다. 성능 측면에서 꽤 큰 부하가 됩니다. 서비스 규모가 작은 경우 폴링은 꽤 괜찮은 방식입니다. 하지만 일반적인 경우엔 개선이 필요합니다. -## Long polling +## 롱 폴링 롱 폴링(long polling)은 일반 폴링보단 더 나은 방식입니다. 롱 폴링은 폴링과 마찬가지로 구현이 쉬운데, 지연 없이 메시지를 전달한다는 차이가 있습니다. -The flow: +흐름은 다음과 같습니다. -1. A request is sent to the server. -2. The server doesn't close the connection until it has a message to send. -3. When a message appears - the server responds to the request with it. -4. The browser makes a new request immediately. +1. 서버에 요청을 보냅니다. +2. 서버는 보낼 메시지가 생길 때까지 연결을 종료하지 않습니다. +3. 메시지가 생기면 서버는 해당 메시지를 담아 요청에 응답합니다. +4. 브라우저는 즉시 새 요청을 보냅니다. -This situation, where the browser has sent a request and keeps a pending connection with the server, is standard for this method. Only when a message is delivered, the connection is closed and reestablished. +브라우저가 요청을 보내고 서버와의 연결을 계속 유지하는 방식이 이 방식의 기본 동작입니다. 메시지가 전달된 경우 연결을 종료하고 다시 연결합니다. ![](long-polling.svg) -If the connection is lost, because of, say, a network error, the browser immediately sends a new request. +네트워크 에러 등으로 연결이 끊어지면 브라우저는 즉시 새 요청을 보냅니다. 다음과 같은 클라이언트 측 구독(`subscribe`) 함수는 롱 요청을 가능하게 해줍니다. @@ -50,22 +42,22 @@ async function subscribe() { let response = await fetch("/subscribe"); if (response.status == 502) { - // Status 502 is a connection timeout error, - // may happen when the connection was pending for too long, - // and the remote server or a proxy closed it - // let's reconnect + // 상태 코드 502는 연결 시간 초과 에러입니다. + // 연결이 너무 오래 대기 상태로 있으면 + // 원격 서버나 프락시가 연결을 닫을 수 있습니다. + // 다시 연결합니다. await subscribe(); } else if (response.status != 200) { - // An error - let's show it + // 에러가 발생하면 보여줍니다. showMessage(response.statusText); - // Reconnect in one second + // 1초 후 다시 연결합니다. await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else { - // Get and show the message + // 메시지를 받아 보여줍니다. let message = await response.text(); showMessage(message); - // Call subscribe() again to get the next message + // 다음 메시지를 받기 위해 subscribe()를 다시 호출합니다. await subscribe(); } } @@ -75,32 +67,32 @@ subscribe(); 롱 폴링을 구현한 함수 `subscribe`는 보시다시피 fetch를 사용해 요청을 보내고 응답이 올 때까지 기다린다음 응답을 처리하고 스스로 다시 요청을 보냅니다. -```warn header="Server should be ok with many pending connections" -The server architecture must be able to work with many pending connections. +```warn header="서버는 대기 중인 연결을 많이 처리할 수 있어야 합니다" +서버 아키텍처는 대기 중인 연결을 많이 처리할 수 있어야 합니다. -Certain server architectures run one process per connection, resulting in there being as many processes as there are connections, while each process consumes quite a bit of memory. So, too many connections will just consume it all. +일부 서버 아키텍처는 연결마다 프로세스 하나를 실행합니다. 이 경우 연결 수만큼 프로세스가 생기고, 각 프로세스는 상당한 메모리를 사용합니다. 따라서 연결이 너무 많으면 메모리를 전부 소모하게 됩니다. -That's often the case for backends written in languages like PHP and Ruby. +PHP나 Ruby 같은 언어로 작성한 백엔드에서 이런 경우가 종종 있습니다. -Servers written using Node.js usually don't have such problems. +Node.js로 작성한 서버에는 보통 이런 문제가 없습니다. -That said, it isn't a programming language issue. Most modern languages, including PHP and Ruby allow to implement a proper backend. Just please make sure that your server architecture works fine with many simultaneous connections. +그렇다고 프로그래밍 언어 자체의 문제는 아닙니다. PHP와 Ruby를 포함한 대부분의 모던 언어로도 적절한 백엔드를 구현할 수 있습니다. 서버 아키텍처가 동시에 많은 연결을 잘 처리할 수 있는지만 확인하면 됩니다. ``` -## Demo: a chat +## 데모: 채팅 -Here's a demo chat, you can also download it and run locally (if you're familiar with Node.js and can install modules): +다음은 채팅 데모입니다. Node.js에 익숙하고 모듈을 설치할 수 있다면 다운로드한 뒤 로컬 환경에서 실행할 수도 있습니다. [codetabs src="longpoll" height=500] -Browser code is in `browser.js`. +브라우저 코드는 `browser.js`에 있습니다. -## Area of usage +## 사용 영역 -Long polling works great in situations when messages are rare. +롱 폴링은 메시지가 드물게 발생하는 상황에 잘 맞습니다. -If messages come very often, then the chart of requesting-receiving messages, painted above, becomes saw-like. +메시지가 매우 빈번하게 온다면 위에서 본 요청-응답 흐름이 톱니 모양처럼 반복됩니다. -Every message is a separate request, supplied with headers, authentication overhead, and so on. +각 메시지가 별도 요청이 되며, 요청마다 헤더와 인증에 따른 오버헤드 등이 붙습니다. -So, in this case, another method is preferred, such as [Websocket](info:websocket) or [Server Sent Events](info:server-sent-events). +따라서 이런 경우엔 [웹소켓](info:websocket)이나 [서버 전송 이벤트](info:server-sent-events) 같은 다른 방법을 사용하는 편이 좋습니다. diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md index 08e76b8ffa..735da71a70 100644 --- a/5-network/11-websocket/article.md +++ b/5-network/11-websocket/article.md @@ -388,8 +388,8 @@ const wss = new ws.Server({noServer: true}); const clients = new Set(); http.createServer((req, res) => { - // here we only handle websocket connections - // in real project we'd have some other code here to handle non-websocket requests + // 여기서는 웹소켓 연결만 처리합니다. + // 실제 프로젝트라면 웹소켓이 아닌 요청을 처리하는 다른 코드도 있을 것입니다. wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect); }); @@ -397,7 +397,7 @@ function onSocketConnect(ws) { clients.add(ws); ws.on('message', function(message) { - message = message.slice(0, 50); // max message length will be 50 + message = message.slice(0, 50); // 메시지 최대 길이를 50으로 제한합니다. for(let client of clients) { client.send(message); @@ -411,34 +411,34 @@ function onSocketConnect(ws) { ``` -Here's the working example: +다음은 실제 동작하는 예시입니다. [iframe src="chat" height="100" zip] -You can also download it (upper-right button in the iframe) and run it locally. Just don't forget to install [Node.js](https://nodejs.org/en/) and `npm install ws` before running. +다운로드한 뒤 로컬 환경에서 실행할 수도 있습니다(iframe 오른쪽 위 버튼). 단, 실행 전에 [Node.js](https://nodejs.org/en/)를 설치하고 `npm install ws` 명령어도 꼭 실행해 주세요. ## Summary -WebSocket is a modern way to have persistent browser-server connections. +웹소켓은 브라우저와 서버 간에 지속적인 연결을 유지하기 위한 현대적인 방법입니다. -- WebSockets don't have cross-origin limitations. -- They are well-supported in browsers. -- Can send/receive strings and binary data. +- 웹소켓에는 크로스 오리진 제약이 없습니다. +- 브라우저에서 잘 지원됩니다. +- 문자열과 이진 데이터를 주고받을 수 있습니다. -The API is simple. +API는 간단합니다. -Methods: +메서드: - `socket.send(data)`, - `socket.close([code], [reason])`. -Events: +이벤트: - `open`, - `message`, - `error`, - `close`. -WebSocket by itself does not include reconnection, authentication and many other high-level mechanisms. So there are client/server libraries for that, and it's also possible to implement these capabilities manually. +웹소켓 자체에는 재연결, 인증 같은 고수준 메커니즘이 포함되어 있지 않습니다. 이런 기능을 제공하는 클라이언트/서버 라이브러리가 있고, 직접 구현하는 것도 가능합니다. -Sometimes, to integrate WebSocket into existing projects, people run a WebSocket server in parallel with the main HTTP-server, and they share a single database. Requests to WebSocket use `wss://ws.site.com`, a subdomain that leads to the WebSocket server, while `https://site.com` goes to the main HTTP-server. +기존 프로젝트에 웹소켓을 통합하기 위해 웹소켓 서버를 메인 HTTP 서버와 병렬로 실행하고, 두 서버가 하나의 데이터베이스를 공유하는 방식을 쓰기도 합니다. 웹소켓 요청은 웹소켓 서버로 연결되는 서브도메인인 `wss://ws.site.com`을 사용하고, `https://site.com`은 메인 HTTP 서버로 연결됩니다. -Surely, other ways of integration are also possible. +물론 다른 통합 방식도 가능합니다. diff --git a/README.md b/README.md index 95bc3a7758..63177a88b7 100755 --- a/README.md +++ b/README.md @@ -34,40 +34,30 @@ * 원문에는 없으나 독자의 이해를 돕기 위해 번역자가 추가하는 내용은 문장 중간이나 끝에 `(.....부가설명..... - 옮긴이)` 형태로 부가설명을 추가하도록 합니다. * '적∙의를 보이는 것∙들'에 대한 내용은 될 수 있으면 사용하지 않습니다.([링크](https://m.blog.naver.com/ojhnews/220840570533)) -<<<<<<< HEAD -잘못된 번역, 오타 및 기타 개선사항은 [이슈](https://github.com/javascript-tutorial/ko.javascript.info/issues)로 등록 부탁드립니다. +잘못된 번역, 오타, 빠진 주제 및 기타 개선사항은 [이슈](https://github.com/javascript-tutorial/ko.javascript.info/issues)로 등록 부탁드립니다. + +**텍스트는 어떤 편집기로든 수정할 수 있습니다.** 튜토리얼은 이해하기 쉬운 확장된 마크다운 형식을 사용합니다. 사이트에서 어떻게 보이는지 확인하고 싶다면 에서 튜토리얼을 로컬로 실행할 수 있는 서버를 확인하세요. ## 기여자 튜토리얼 원문에 기여하신 분들은 에서 확인할 수 있습니다. 한국어 번역에 기여해주신 분들은 에서 확인할 수 있습니다. ---- -by the Modern JavaScript Tutorial Project Owner, Ilya Kantor(@iliakan) - -모던 JavaScript 튜토리얼 한국어 프로젝트 오너, 이보라(@Violet-Bora-Lee) -======= -Something's wrong? A topic is missing? Explain it to people, add it as PR 👏 - -**You can edit the text in any editor.** The tutorial uses an enhanced "markdown" format, easy to grasp. And if you want to see how it looks on-site, there's a server to run the tutorial locally at . - -The list of contributors is available at . +## 구조 -## Structure +모든 챕터, 글, 과제는 각각 별도 폴더에 들어 있습니다. -Every chapter, article, or task has its folder. +폴더 이름은 `N-url` 형식입니다. 여기서 `N`은 정렬을 위한 숫자이고, `URL`은 자료 제목에 해당하는 URL 일부입니다. -The folder is named like `N-url`, where `N` is a number for the sorting purposes and `URL` is the URL part with the title of the material. +자료의 종류는 폴더 안의 파일로 구분합니다. -The type of the material is defined by the file inside the folder: + - `index.md`는 챕터를 의미합니다. + - `article.md`는 글을 의미합니다. + - `task.md`는 과제를 의미합니다. 과제에는 해답 파일인 `solution.md`도 함께 있어야 합니다. - - `index.md` stands for a chapter - - `article.md` stands for an article - - `task.md` stands for a task (solution must be provided in `solution.md` file as well) +각 파일은 최상위 제목(`# ...`)으로 시작합니다. -Each of these files starts from the `# Main header`. +새로운 내용을 쉽게 추가할 수 있습니다. -It's very easy to add something new. +--- +모던 JavaScript 튜토리얼 프로젝트 오너, Ilya Kantor(@iliakan) ---- -♥ -Ilya Kantor @iliakan ->>>>>>> upstream/master +모던 JavaScript 튜토리얼 한국어 프로젝트 오너, 이보라(@Violet-Bora-Lee)