diff --git a/locale/ko/docs/es6.md b/locale/ko/docs/es6.md new file mode 100644 index 0000000000000..f39c21d044b90 --- /dev/null +++ b/locale/ko/docs/es6.md @@ -0,0 +1,104 @@ +--- +title: ECMAScript 2015(ES6)와 그 다음 +layout: docs.hbs +--- + + + +# ECMAScript 2015(ES6)와 그 다음 + +Node.js는 [V8](https://developers.google.com/v8/)의 최신 버전으로 만들었습니다. +V8을 최신 릴리스로 유지하기 때문에 Node.js 개발자에게 +[JavaScript ECMA-262 명세](http://www.ecma-international.org/publications/standards/Ecma-262.htm)의 +새로운 기능을 제때에 지원하면서 성능과 안정성 개선도 할 수 있습니다. + +모든 ECMAScript 2015(ES6) 기능은 **shipping**, **staged**, **in progress** 기능의 +세 가지 그룹으로 나뉩니다. + +* 모든 **shipping** 기능은 V8이 안정적이라고 간주한 것으로 + **Node.js에서는 기본적으로 켜져 있으므로** 런타임 플래그가 전혀 **필요 없습니다**. +* **Staged** 기능은 거의 완성되었지만, V8 팀이 안정적이라고 간주하지 않은 기능으로 + `--harmony` 런타임 플래그가 필요합니다. +* **In progress** 기능은 각 하모니 플래그로 개별적으로 활성화할 수 있습니다. 테스트 목적이 + 아니라면 활성화하지 않기를 강력하게 권장합니다. 주의: 이 플래그는 V8에서 제공한 것으로 + 폐기 공지 없이 변경될 수 있습니다. + + + +## Node.js 버전에 어떤 기능이 기본적으로 포함되나요? + +[node.green](http://node.green) 웹사이트에서 어떤 Node.js가 어떤 ECMAScript 기능을 +지원하는지 파악할 수 있습니다. 이는 kangax의 호환성 표를 기반으로 만들어졌습니다. + + + +## 어떤 기능이 진행 중입니까? + +V8 엔진에 계속해서 새로운 기능이 추가되고 있습니다. 정확한 시기는 알 수 없겠지만 +앞으로 릴리스 할 Node.js에는 V8에 추가된 새 기능이 대체로 포함될 것입니다. + +`--v8-options` 인자로 각 Node.js 릴리스에서 모든 *in progress* 기능의 리스트를 볼 수 +있습니다. 이 기능들은 완성되지 않았고 V8에서 제대로 돌아가지 않을 수도 있으므로 이 기능을 사용할 때는 +위험을 감수해야 함을 명심하세요. + +```bash +node --v8-options | grep "in progress" +``` + + + +## --harmony 플래그를 사용하는 환경이 있습니다. 이를 제거해야 하나요? + +Node.js에서 `--harmony` 플래그의 현재 동작은 **staged** 기능만 활성화하는 것입니다. 결국, +이는 `--es_staging`과 같은 의미입니다. 앞에서 말했듯이 이 기능은 완성되었지만, 아직 안정적이라고 +볼 수는 없습니다. 프로덕션 환경 등에서 안전하게 운영하고 싶다면 V8과 Node.js에서 기본적으로 +제공할 때까지 이 런타임 플래그를 제거하는 것을 고려해 보세요. 이 기능을 활성화한다면 차후 Node.js를 +업그레이드 할 때 V8이 이 기능의 의미를 표준에 더 가깝게 변경한 경우 코드가 깨질 수 있으므로 +대비해야 합니다. + + + +## Node.js의 특정 버전에 포함된 V8의 버전을 어떻게 알 수 있나요? + +Node.js에서는 `process` 전역 객체를 통해 특정 바이너리에 포함된 모든 의존성과 각 버전의 목록을 +쉽게 볼 수 있습니다. V8 엔진의 경우 터미널에서 다음 명령어를 실행하면 V8 버전을 볼 수 있습니다. + +```bash +node -p process.versions.v8 +``` diff --git a/locale/ko/docs/faq.md b/locale/ko/docs/faq.md new file mode 100644 index 0000000000000..e95694db5f15b --- /dev/null +++ b/locale/ko/docs/faq.md @@ -0,0 +1,51 @@ +--- +title: FAQ +layout: docs.hbs +--- + + +# FAQ + +## 어떻게 기여하나요? + +누구나 참여할 수 있습니다. Node.js는 +[행동 강령](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-of-conduct)을 +지키고 [오픈 거버넌스](https://github.com/nodejs/node/blob/master/GOVERNANCE.md#readme) +모델 하에 기여, 릴리스, 기여자를 운영합니다. + +우선은 [GitHub에서 공개적으로 이뤄지는 논의](https://github.com/nodejs/node/issues)에 +참여해보는 것도 좋습니다. 우리는 여러분의 피드백을 듣고 싶습니다. 논의에 참여하는 것은 무엇을 +도울 수 있는지 찾는 좋은 방법입니다. 기여할 수 있는 부분은 찾았다면 +[풀 리퀘스트를 올려주세요](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-contributions). + + + +## 논의는 어디서 이뤄지나요? + +Freenode IRC에 #node.js 채널이 있습니다. 채널의 대화 로그를 +[여기](http://logs.libuv.org/node.js/latest)에 저장해 두고 있습니다. + + + +## 오픈 소스 거버넌스가 무엇인가요? + +오픈 소스 거버넌스는 위키 문서처럼 관심 있는 사람이라면 누구나 최종 제품에 창작물을 추가할 수 있도록 +오픈 소스와 오픈 콘텐츠 운동의 철학을 지지합니다. 규칙은 모든 사람에게 민주적으로 열려있고 이들의 +집단 지성을 모아서 의사결정 과정을 돕고 민주주의를 개선합니다. +[[출처]](https://en.wikipedia.org/wiki/Open-source_governance) diff --git a/locale/ko/docs/guides/anatomy-of-an-http-transaction.md b/locale/ko/docs/guides/anatomy-of-an-http-transaction.md new file mode 100644 index 0000000000000..46d2a63c55313 --- /dev/null +++ b/locale/ko/docs/guides/anatomy-of-an-http-transaction.md @@ -0,0 +1,889 @@ +--- +title: HTTP 트랜잭션 해부 +layout: docs.hbs +--- + + + +# HTTP 트랜잭션 해부 + +이 문서의 목적은 Node.js HTTP 처리 과정을 잘 이해하게 하는 것입니다. 언어나 프로그래밍 환경과 +관계없이 상식선에서 HTTP 요청이 어떻게 동작하는지는 알고 있어야 합니다. Node.js의 +[`EventEmitters`][]와 [`Streams`][]에도 어느 정도 익숙해야 합니다. 익숙하지 않다면 +관련 API 문서를 미리 훑어보는 편이 좋습니다. + + + +## 서버 생성 + +모든 node 웹 서버 애플리케이션은 웹 서버 객체를 만들어야 합니다. +이 때 [`createServer`][]를 이용합니다. + +```javascript +var http = require('http'); + +var server = http.createServer(function(request, response) { + // 여기서 작업이 진행됩니다! +}); +``` + + + +이 서버로 오는 HTTP 요청마다 [`createServer`][]에 전달된 함수가 한 번씩 호출됩니다. +사실 [`createServer`][]가 반환한 [`Server`][] 객체는 [`EventEmitter`][]이고 여기서는 +`server` 객체를 생성하고 리스너를 추가하는 축약 문법을 사용한 것입니다. + +```javascript +var server = http.createServer(); +server.on('request', function(request, response) { + // 여기서 작업이 진행됩니다! +}); +``` + + + +HTTP 요청이 서버에 오면 node가 트랜잭션을 다루려고 `request`과 `response` 객체를 전달하며 +요청 핸들러 함수를 호출합니다. 곧 이 객체를 사용해 볼 것입니다. + +요청을 실제로 처리하려면 [`listen`][] 메소드가 `server` 객체에서 호출되어야 합니다. +대부분은 서버가 사용하고자 하는 포트 번호를 `listen`에 전달하기만 하면 됩니다. +몇 가지 다른 옵션도 있으므로 [API 문서][]를 참고하세요. + + + +## 메소드, URL, 헤더 + +요청을 처리할 때, 우선은 메소드와 URL을 확인한 후 이와 관련된 적절한 작업을 실행하려고 할 것입니다. +Node가 `request` 객체에 유용한 프로퍼티를 넣어두었으므로 이 작업은 비교적 쉽게 할 수 있습니다. + +```javascript +var method = request.method; +var url = request.url; +``` +> **주의:** `request` 객체는 [`IncomingMessage`][]의 인스턴스입니다. + + + +여기서 `method`는 항상 일반적인 HTTP 메소드/동사가 될 것입니다. `url`은 전체 URL에서 서버, +프로토콜, 포트를 제외한 것으로, 세 번째 슬래시 이후의 나머지 전부라고 볼 수 있습니다. + +헤더도 많이 다르지 않습니다. `request`에 `headers`라는 전용 객체가 있습니다. + +```javascript +var headers = request.headers; +var userAgent = headers['user-agent']; +``` + + + +클라이언트가 어떻게 헤더를 설정했는지에 관계없이 모든 헤더는 소문자로만 표현된다는 것을 기억해야 합니다. +이는 어떤 목적이든 헤더를 파싱하는 작업을 간편하게 해줍니다. + +일부 헤더를 반복해서 설정한다면 이 값은 헤더에 따라 덮어씌워지거나 콤마로 구분된 문자열로 합쳐집니다. +이게 문제가 될 경우에는 [`rawHeaders`][]를 사용할 수도 있습니다. + + + +## 요청 바디 + +`POST`나 `PUT` 요청을 받을 때 애플리케이션에 요청 바디는 중요할 것입니다. 요청 헤더에 접근하는 +것보다 바디 데이터를 받는 것은 좀 더 어렵습니다. 핸들러에 전달된 `request` 객체는 +[`ReadableStream`][] 인터페이스를 구현하고 있습니다. 이 스트림에 이벤트 리스너를 등록하거나 +다른 스트림에 파이프로 연결할 수 있습니다. 스트림의 `'data'`와 `'end'` 이벤트에 이벤트 리스너를 +등록해서 데이터를 받을 수 있습니다. + +각 `'data'` 이벤트에서 발생시킨 청크는 [`Buffer`][]입니다. 이 청크가 문자열 데이터라는 것을 +알고 있다면 이 데이터를 배열에 수집한 다음 `'end'` 이벤트에서 이어 붙인 다음 문자열로 +만드는 것이 가장 좋습니다. + + + +```javascript +var body = []; +request.on('data', function(chunk) { + body.push(chunk); +}).on('end', function() { + body = Buffer.concat(body).toString(); + // 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다. +}); +``` + +> **주의:** 이 코드가 약간 장황할 수도 있고 대부분은 실제로 그렇습니다. 다행히 [`npm`][]에 +[`concat-stream`][]나 [`body`][] 같은 모듈로 이 로직을 감출 수 있습니다. 이어서 읽기 전에 +어떤 작업이 이뤄지는지 잘 이해하는 것이 중요합니다. 그래서 이 글을 읽고 있는 것입니다. + + + +## 오류에 대한 간단한 설명 + +`request` 객체가 [`ReadableStream`][]이므로 [`EventEmitter`][]이기도 하고 +오류가 발생했을 때 [`EventEmitter`][]처럼 동작합니다. + +`request` 스트림의 오류가 발생하면 스트림에서 `'error'` 이벤트가 발생하면서 오류를 전달합니다. +**이벤트에 리스너가 등록되어 있지 않다면 Node.js 프로그램을 종료시킬 수도 있는 오류를 *던질* 것입니다.** +그러므로 단순히 오류를 로깅만 하더라도 요청 스트림에 `'error'` 리스너를 추가해야 합니다.(하지만 +HTTP 오류 응답을 보내는 것이 좋을 겁니다. 이에 대해는 뒤에서 더 자세히 살펴봅니다.) + + + +```javascript +request.on('error', function(err) { + // 여기서 `stderr`에 오류 메시지와 스택 트레이스를 출력합니다. + console.error(err.stack); +}); +``` + +별도의 추상화나 도구를 이용해서 [오류를 처리하는][] 다른 방법도 존재하지만, 항상 오류는 발생할 수 +있다는 것을 명심하고 오류를 처리해야 합니다. + + + +## 지금까지 살펴본 내용 + +지금까지 서버를 생성하고 요청의 메소드, UL, 헤더, 바디를 가져왔습니다. +이를 모두 사용하면 다음과 같이 될 것입니다. + +```javascript +var http = require('http'); + +http.createServer(function(request, response) { + var headers = request.headers; + var method = request.method; + var url = request.url; + var body = []; + request.on('error', function(err) { + console.error(err); + }).on('data', function(chunk) { + body.push(chunk); + }).on('end', function() { + body = Buffer.concat(body).toString(); + // 여기서 헤더, 메소드, url, 바디를 가지게 되었고 + // 이 요청에 응답하는 데 필요한 어떤 일이라도 할 수 있게 되었습니다. + }); +}).listen(8080); // 이 서버를 활성화하고 8080 포트로 받습니다. +``` + + + +이 예제를 실행하면 요청을 *받을 수* 있지만, 요청에 *응답*하지는 않습니다. 사실 웹 브라우저에서 +이 예제에 접근하면 클라이언트에 돌려보내는 것이 없으므로 요청이 타임아웃에 걸릴 것입니다. + +지금까지 `response` 객체는 전혀 건드리지 않았습니다. 이 객체는 [`ServerResponse`][]의 +인스턴스이면서 [`WritableStream`][]입니다. 여기에는 클라이언트에 데이터를 응답하기 위한 +여러 가지 유용한 메소드가 있습니다. 이제 이를 살펴볼 것입니다. + + + +## HTTP 상태 코드 + +따로 설정하지 않으면 응답의 HTTP 상태 코드는 항상 200입니다. 물론 모든 HTTP 응답이 이를 보장하는 +것은 아니고 어떤 경우에는 다른 상태 코드를 보내기를 원할 것입니다. +상태 코드를 변경하려면 `statusCode` 프로퍼티를 설정해야 합니다. + +```javascript +response.statusCode = 404; // 클라이언트에게 리소스를 찾을 수 없다고 알려줍니다. +``` + +이에 대한 단축 설정도 있는데 곧 살펴볼 것입니다. + + + +## 응답 헤더 설정 + +편리한 [`setHeader`][] 메소드로 헤더를 설정합니다. + +```javascript +response.setHeader('Content-Type', 'application/json'); +response.setHeader('X-Powered-By', 'bacon'); +``` + +응답에 헤더를 설정할 때 헤더 이름의 대소문자는 중요하지 않습니다. 헤더를 여러 번 설정한다면 마지막에 설정한 값을 보낼 것입니다. + + + +## 명시적인 헤더 데이터 전송 + +지금까지 설명한 헤더와 상태 코드를 설정하는 메소드는 "암묵적인 헤더"를 사용하고 있다고 가정합니다. 이는 +바디 데이터를 보내기 전 적절한 순간에 헤더를 보내는 일을 노드에 의존하고 있다는 의미입니다. + +원한다면 *명시적으로* 응답 스트림에 헤더를 작성할 수 있습니다. 헤더를 작성하는 [`writeHead`][] +메소드가 있습니다. 이 메소드는 스트림에 상태 코드와 헤더를 작성합니다. + +```javascript +response.writeHead(200, { + 'Content-Type': 'application/json', + 'X-Powered-By': 'bacon' +}); +``` + +(암묵적이든 명시적이든) 일단 헤더를 설정하고 나면 응답 데이터를 전송할 준비가 된 것입니다. + + + +## 응답 바디 전송 + +`response` 객체는 [`WritableStream`][]이므로 클라이언트로 보내는 응답 바디는 일반적인 +스트림 메소드를 사용해서 작성합니다. + +```javascript +response.write(''); +response.write(''); +response.write('

Hello, World!

'); +response.write(''); +response.write(''); +response.end(); +``` + +스트림의 `end` 함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있으므로 +위의 예제는 다음과 같이 간단하게 작성할 수 있습니다. + +```javascript +response.end('

Hello, World!

'); +``` + +> **주의:** 바디에 데이터 청크를 *작성하기 전에* 상태 코드와 헤더를 설정해야 합니다. +HTTP 응답에서 바디 전에 헤더가 있으므로 이는 이치에 맞습니다. + + + +## 오류에 대한 추가 설명 + +`response` 스트림도 `'error'` 이벤트를 발생시킬 수 있고 때로는 이 오류도 처리해야 합니다. +`request` 스트림 오류에 대한 모든 설명이 여기서도 똑같이 적용됩니다. + + + +## 지금까지 배운 내용을 사용한 예제 + +HTTP 응답 만드는 방법을 배웠으니 이제 모든 것을 함께 사용해 보겠습니다. 이전에 본 예제에서 사용자가 +서버에 보낸 모든 데이터를 다시 보내는 서버를 만들 것입니다. `JSON.stringify`를 사용해서 데이터를 +JSON으로 포매팅할 것입니다. + +```javascript + +var http = require('http'); + +http.createServer(function(request, response) { + var headers = request.headers; + var method = request.method; + var url = request.url; + var body = []; + request.on('error', function(err) { + console.error(err); + }).on('data', function(chunk) { + body.push(chunk); + }).on('end', function() { + body = Buffer.concat(body).toString(); + // 여기서부터 새로운 부분입니다. + + response.on('error', function(err) { + console.error(err); + }); + + response.statusCode = 200; + response.setHeader('Content-Type', 'application/json'); + // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다. + // response.writeHead(200, {'Content-Type': 'application/json'}) + + var responseBody = { + headers: headers, + method: method, + url: url, + body: body + }; + + response.write(JSON.stringify(responseBody)); + response.end(); + // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다. + // response.end(JSON.stringify(responseBody)) + + // 새로운 부분이 끝났습니다. + }); +}).listen(8080); +``` + + + +## 에코 서버 예제 + +위의 예제를 간략하게 바꾸어 간단한 에코 서버를 만들어보겠습니다. 에코 서버는 요청받은 데이터를 +그대로 응답으로 돌려보내는 서버입니다. 앞에서 했던 것처럼 요청 스트림에서 데이터를 가져와 +응답 스트림에 쓰기만 하면 됩니다. + +```javascript +var http = require('http'); + +http.createServer(function(request, response) { + var body = []; + request.on('data', function(chunk) { + body.push(chunk); + }).on('end', function() { + body = Buffer.concat(body).toString(); + response.end(body); + }); +}).listen(8080); +``` + + + +이제 약간의 변경을 해보겠습니다. 다음의 조건에서만 에코 응답을 보내려고 합니다. + +* 요청 메소드가 GET인 경우 +* URL이 `/echo`인 경우 + +위 조건이 아닌 경우에는 404를 응답합니다. + +```javascript +var http = require('http'); + +http.createServer(function(request, response) { + if (request.method === 'GET' && request.url === '/echo') { + var body = []; + request.on('data', function(chunk) { + body.push(chunk); + }).on('end', function() { + body = Buffer.concat(body).toString(); + response.end(body); + }) + } else { + response.statusCode = 404; + response.end(); + } +}).listen(8080); +``` + + + +> **주의:** 이 방법으로 URL을 검사함으로써 "라우팅"을 하고 있습니다. `switch` 문으로 간단히 +라우팅할 수도 있고 [`express`][]같은 프레임워크로 복잡한 라우팅을 할 수도 있습니다. +라우팅 처리를 하는 무언가를 찾고 있다면 [`router`][]를 사용해 보길 바랍니다. + +좋습니다! 이제 이를 간략화 해보겠습니다. `request` 객체는 [`ReadableStream`][]이고 +`response` 객체는 [`WritableStream`][]임을 명심하세요. 이 말은 데이터를 한 스트림에서 +다른 스트림으로 직접 연결하는 [`pipe`][]를 사용할 수 있음을 의미합니다. +에코 서버에서 하려는 것이 바로 이것입니다. + +```javascript +var http = require('http'); + +http.createServer(function(request, response) { + if (request.method === 'GET' && request.url === '/echo') { + request.pipe(response); + } else { + response.statusCode = 404; + response.end(); + } +}).listen(8080); +``` + + + +이게 스트림입니다! + +아직은 끝난 것이 아닙니다. 이 문서에서 여러 번 얘기했듯이 오류는 언제든 발생할 수 있고 +우리는 이를 처리해야 합니다. + +요청 스트림에서 오류를 처리하기 위해 오류를 `stderr`에 로깅하고 `Bad Request`를 의미하는 +400 상태 코드를 보낼 것입니다. 실제 애플리케이션에서는 적절한 상태 코드와 메시지가 무엇인지 찾아내기 +위해 오류를 검사하고자 할 것입니다. 언제나처럼 오류에 대해서는 [`Error` 문서][]를 살펴봐야 합니다. + +응답에서는 `stdout`에 오류를 로깅 할 것입니다. + +```javascript +var http = require('http'); + +http.createServer(function(request, response) { + request.on('error', function(err) { + console.error(err); + response.statusCode = 400; + response.end(); + }); + response.on('error', function(err) { + console.error(err); + }); + if (request.method === 'GET' && request.url === '/echo') { + request.pipe(response); + } else { + response.statusCode = 404; + response.end(); + } +}).listen(8080); +``` + + + +지금까지 HTTP 요청을 다루는 기본 내용을 거의 다 다루었습니다. 이제 다음을 할 수 있어야 합니다. + +* 요청 핸들러 함수로 HTTP 서버의 인스턴스를 생성하고 특정 포트로 서버를 열 수 있습니다. +* `request` 객체에서 헤더, URL, 메소드, 바디 데이터를 가져올 수 있습니다. +* URL이나 `request` 객체의 데이터에 기반을 둬서 라우팅을 할 수 있습니다. +* `response` 객체로 헤더, HTTP 상태 코드, 바디 데이터를 보낼 수 있습니다. +* `request` 객체에서 `response` 객체로 데이터를 파이프로 연결할 수 있습니다. +* `request`와 `response` 스트림 모두에서 스트림 오류를 처리할 수 있습니다. + +이 기본 내용을 바탕으로 많은 사용 사례를 위한 Node.js HTTP 서버를 구축할 수 있습니다. +API가 제공하는 그 외 많은 기능이 있으므로 [`EventEmitters`][], [`Streams`][], +[`HTTP`][]의 API 문서를 꼭 읽어보세요. + + + +[`EventEmitters`]: https://nodejs.org/api/events.html +[`Streams`]: https://nodejs.org/api/stream.html +[`createServer`]: https://nodejs.org/api/http.html#http_http_createserver_requestlistener +[`Server`]: https://nodejs.org/api/http.html#http_class_http_server +[`listen`]: https://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback +[API 문서]: https://nodejs.org/api/http.html +[`IncomingMessage`]: https://nodejs.org/api/http.html#http_class_http_incomingmessage +[`ReadableStream`]: https://nodejs.org/api/stream.html#stream_class_stream_readable +[`rawHeaders`]: https://nodejs.org/api/http.html#http_message_rawheaders +[`Buffer`]: https://nodejs.org/api/buffer.html +[`concat-stream`]: https://www.npmjs.com/package/concat-stream +[`body`]: https://www.npmjs.com/package/body +[`npm`]: https://www.npmjs.com +[`EventEmitter`]: https://nodejs.org/api/events.html#events_class_eventemitter +[오류를 처리하는]: https://nodejs.org/api/errors.html +[`domains`]: https://nodejs.org/api/domain.html +[`ServerResponse`]: https://nodejs.org/api/http.html#http_class_http_serverresponse +[`setHeader`]: https://nodejs.org/api/http.html#http_response_setheader_name_value +[`WritableStream`]: https://nodejs.org/api/stream.html#stream_class_stream_writable +[`writeHead`]: https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers +[`express`]: https://www.npmjs.com/package/express +[`router`]: https://www.npmjs.com/package/router +[`pipe`]: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options +[`Error` 문서]: https://nodejs.org/api/errors.html +[`HTTP`]: https://nodejs.org/api/http.html diff --git a/locale/ko/docs/guides/index.md b/locale/ko/docs/guides/index.md new file mode 100644 index 0000000000000..14054678ab20b --- /dev/null +++ b/locale/ko/docs/guides/index.md @@ -0,0 +1,6 @@ +--- +title: 가이드 +layout: guides-index.hbs +--- + +# 가이드 diff --git a/locale/ko/docs/guides/nodejs-docker-webapp.md b/locale/ko/docs/guides/nodejs-docker-webapp.md new file mode 100644 index 0000000000000..f1f128bb626df --- /dev/null +++ b/locale/ko/docs/guides/nodejs-docker-webapp.md @@ -0,0 +1,483 @@ +--- +title: Node.js 웹 앱의 도커라이징 +layout: docs.hbs +--- + + + +# Node.js 웹 앱의 도커라이징 + +이 예제에서는 Node.js 애플리케이션을 Docker 컨테이너에 넣는 방법을 보여줍니다. 이 가이드는 +개발 목적이지 프로덕션 배포용이 *아닙니다*. +[Docker가 설치](https://docs.docker.com/engine/installation/)되어 있고 +Node.js 애플리케이션을 구조화하는 방법에 관해 기본적인 지식이 있어야 합니다. + +먼저 간단한 Node.js 웹 애플리케이션을 만든 후에 이 애플리케이션을 위한 Docker 이미지를 +만들어서 컨테이너로 실행할 것입니다. + +Docker를 사용하면 애플리케이션과 모든 의존성을 소프트웨어 개발에서 컨테이너라고 부르는 표준화된 +단위로 패키징할 수 있습니다. 컨테이너는 리눅스 운영체제의 간단 버전입니다. + + + +## Node.js 앱 생성 + +우선, 모든 파일을 넣은 새로운 디렉터리를 만들겠습니다. 이 디렉터리 안에 애플리케이션과 의존성을 +알려주는 `package.json` 파일을 생성하겠습니다. + +```javascript +{ + "name": "docker_web_app", + "version": "1.0.0", + "description": "Node.js on Docker", + "author": "First Last ", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.13.3" + } +} +``` + + + +이제 [Express.js](http://expressjs.com/) 프레임워크로 웹앱을 정의하는 `server.js`를 만들겠습니다. + +```javascript +'use strict'; + +const express = require('express'); + +// 상수 +const PORT = 8080; + +// 앱 +const app = express(); +app.get('/', function (req, res) { + res.send('Hello world\n'); +}); + +app.listen(PORT); +console.log('Running on http://localhost:' + PORT); +``` + +다음 단계에서 공식 Docker 이미지를 사용해서 Docker 컨테이너 안에서 이 앱을 실행하는 방법을 +살펴보겠습니다. 먼저 앱의 Docker 이미지를 만들어야 합니다. + + + +## Dockerfile 생성 + +`Dockerfile`이라는 빈 파일을 생성합니다. + +```markup +touch Dockerfile +``` + +선호하는 텍스트 에디터로 `Dockerfile`을 엽니다. + +가장 먼저 해야 할 것은 어떤 이미지를 사용해서 빌드할 것인지를 정의하는 것입니다. 여기서는 +[Docker Hub](https://hub.docker.com/)에 있는 +`node`의 최신 LTS(장기 지원) 버전인 `argon`을 사용할 것입니다. + +```docker +FROM node:argon +``` + + + +다음으로 이미지 안에 애플리케이션 코드를 넣기 위해 디렉터리를 생성할 것입니다. +이 디렉터리가 애플리케이션의 워킹 디렉터리가 됩니다. + +```docker +# 앱 디렉터리 생성 +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app +``` + +이 이미지에는 이미 Node.js와 NPM이 설치되어 있으므로 `npm` 바이너리로 +앱의 의존성을 설치하기만 하면 됩니다. + +```docker +# 앱 의존성 설치 +COPY package.json /usr/src/app/ +RUN npm install +``` + + + +Docker 이미지 안에 앱의 소스코드를 넣기 위해 `COPY` 지시어를 사용합니다. + +```docker +# 앱 소스 추가 +COPY . /usr/src/app +``` + +앱이 `8080`포트에 바인딩 되어 있으므로 `EXPOSE` 지시어를 사용해서 `docker` 데몬에 매핑합니다. + +```docker +EXPOSE 8080 +``` + + + +마지막으로 런타임을 정의하는 `CMD`로 앱을 실행하는 중요 명령어를 정의해야 합니다. +여기서는 서버를 구동하도록 `node server.js`을 실행하는 기본 `npm start`을 사용할 것입니다. + +```docker +CMD [ "npm", "start" ] +``` + +`Dockerfile`은 다음과 같아야 합니다. + +```docker +FROM node:argon + +# 앱 디렉토리 생성 +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +# 앱 의존성 설치 +COPY package.json /usr/src/app/ +RUN npm install + +# 앱 소스 추가 +COPY . /usr/src/app + +EXPOSE 8080 +CMD [ "npm", "start" ] +``` + + + +## 이미지 빌드 + +작성한 `Dockerfile`이 있는 디렉토리로 가서 Docker 이미지를 빌드하는 다음 명령어를 실행하세요. +`-t` 플래그로 이미지에 태그를 추가하기 때문에 나중에 `docker images` 명령어로 +쉽게 찾을 수 있습니다. + +```bash +$ docker build -t /node-web-app . +``` + +Docker가 당신이 빌드한 이미지를 보여줄 것입니다. + +```bash +$ docker images + +# 예시 +REPOSITORY TAG ID CREATED +node argon 539c0211cd76 3 weeks ago +/node-web-app latest d64d3505b0d2 1 minute ago +``` + + + +## 이미지 실행 + +`-d`로 이미지를 실행하면 분리 모드로 컨테이너를 실행해서 백그라운드에서 컨테이너가 돌아가도록 합니다. +`-p` 플래그는 공개 포트를 컨테이너 내의 비밀 포트로 리다이렉트합니다. 앞에서 만들 이미지를 실행하세요. + +```bash +$ docker run -p 49160:8080 -d /node-web-app +``` + +앱의 로그를 출력하세요. + +```bash +# 컨테이너 아이디를 확인합니다 +$ docker ps + +# 앱 로그를 출력합니다 +$ docker logs + +# 예시 +Running on http://localhost:8080 +``` + +컨테이너 안에 들어가 봐야 한다면 `exec` 명령어를 사용할 수 있습니다. + +```bash +# 컨테이너에 들어갑니다 +$ docker exec -it /bin/bash +``` + + + +## 테스트 + +앱을 테스트하려면 Docker 매핑된 앱 포트를 확인합니다. + +```bash +$ docker ps + +# 예시 +ID IMAGE COMMAND ... PORTS +ecce33b30ebf /node-web-app:latest npm start ... 49160->8080 +``` + +위 예시에서 Docker가 컨테이너 내의 `8080` 포트를 머신의 `49160` 포트로 매핑했습니다. + +이제 `curl`로 앱을 호출할 수 있습니다.(필요하다면 `sudo apt-get install curl`로 설치하세요.) + +```bash +$ curl -i localhost:49160 + +HTTP/1.1 200 OK +X-Powered-By: Express +Content-Type: text/html; charset=utf-8 +Content-Length: 12 +Date: Sun, 02 Jun 2013 03:53:22 GMT +Connection: keep-alive + +Hello world +``` + + + +간단한 Node.js 애플리케이션을 Docker로 실행하는데 이 튜토리얼이 도움되었길 바랍니다. + +다음 링크에서 Docker와 Docker에서의 Node.js에 대한 정보를 더 자세히 볼 수 있습니다. + +* [공식 Node.js Docker 이미지](https://registry.hub.docker.com/_/node/) +* [Node.js Docker 사용사례 문서](https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md) +* [공시 Docker 문서](https://docs.docker.com/) +* [StackOverflow에 Docker 태그로 올라온 질문](http://stackoverflow.com/questions/tagged/docker) +* [Docker 레딧](https://reddit.com/r/docker) diff --git a/locale/ko/docs/guides/simple-profiling.md b/locale/ko/docs/guides/simple-profiling.md new file mode 100644 index 0000000000000..ea139ce95d7e8 --- /dev/null +++ b/locale/ko/docs/guides/simple-profiling.md @@ -0,0 +1,564 @@ +--- +title: Node.js 애플리케이션의 간단한 프로파일링 +layout: docs.hbs +--- + + + +# Node.js 애플리케이션의 간단한 프로파일링 + +Node.js 애플리케이션을 프로파일링하는 서드파티 도구들이 많이 있지만, 대부분은 Node.js에 내장된 +프로파일러를 사용하는 것인 가장 쉬운 방법입니다. 내장된 프로파일러는 프로그램을 실행하는 동안 주기적으로 +스택을 샘플링하는 [V8 내의 프로파일러][]를 사용합니다. 이 샘플링 결과를 기록하면서 jit 컴파일이나 +tick 시리즈처럼 중요한 최적화 작업을 기록합니다. + +``` +code-creation,LazyCompile,0,0x2d5000a337a0,396,"bp native array.js:1153:16",0x289f644df68,~ +code-creation,LazyCompile,0,0x2d5000a33940,716,"hasOwnProperty native v8natives.js:198:30",0x289f64438d0,~ +code-creation,LazyCompile,0,0x2d5000a33c20,284,"ToName native runtime.js:549:16",0x289f643bb28,~ +code-creation,Stub,2,0x2d5000a33d40,182,"DoubleToIStub" +code-creation,Stub,2,0x2d5000a33e00,507,"NumberToStringStub" +``` + + + +이전에는 tick을 해석하려면 V8 소스 코드가 필요했습니다. 다행히, 소스에서 V8 빌드를 따로 하지 않고 +이 정보를 사용할 수 있는 도구가 최근 Node.js 4.4.0에 도입되었습니다. 애플리케이션의 성능을 +볼 수 있는 내장된 프로파일러를 어떻게 사용하는지 살펴보겠습니다. + +tick 프로파일러의 사용방법을 설명하기 위해 간단한 Express 애플리케이션을 만들어 보겠습니다. +예제 애플리케이션은 2개의 핸들러를 가지고 있는데 하나는 새로운 사용자를 시스템에 추가할 것입니다. + + + +```javascript +app.get('/newUser', function (req, res) { + var username = req.query.username || ''; + var password = req.query.password || ''; + + username = username.replace(/[!@#$%^&*]/g, ''); + + if (!username || !password || users.username) { + return res.sendStatus(400); + } + + var salt = crypto.randomBytes(128).toString('base64'); + var hash = crypto.pbkdf2Sync(password, salt, 10000, 512); + + users[username] = { + salt: salt, + hash: hash + }; + + res.sendStatus(200); +}); +``` + +다른 핸들러는 사용자 인증의 유효성을 검사합니다. + + + +```javascript +app.get('/auth', function (req, res) { + var username = req.query.username || ''; + var password = req.query.password || ''; + + username = username.replace(/[!@#$%^&*]/g, ''); + + if (!username || !password || !users[username]) { + return res.sendStatus(400); + } + + var hash = crypto.pbkdf2Sync(password, users[username].salt, 10000, 512); + + if (users[username].hash.toString() === hash.toString()) { + res.sendStatus(200); + } else { + res.sendStatus(401); + } +}); +``` + + + +*Node.js 애플리케이션에서 사용자 인증을 처리하는 핸들러는 추천하지 않는 방법이고 여기서는 예시를 +보여주기 위해서 사용한 것뿐입니다. 보통은 직접 만든 인증 메커니즘을 설계하지 말아야 합니다. +기존에 존재하는 입증된 인증솔루션을 사용하는 것이 훨씬 낫습니다.* + +배포한 애플리케이션에서 사용자가 요청에 지연이 발생한다고 불평한다고 가정해 보겠습니다. +내장된 프로파일러로 애플리케이션을 간단히 실행할 수 있습니다. + +``` +NODE_ENV=production node --prof app.js +``` + +ab로 서버에 부하를 줄 수 있습니다. + +``` +curl -X GET "http://localhost:8080/newUser?username=matt&password=password" +ab -k -c 20 -n 250 "http://localhost:8080/auth?username=matt&password=password" +``` + +다음과 같은 결과가 나올 것입니다. + + + +``` +Concurrency Level: 20 +Time taken for tests: 46.932 seconds +Complete requests: 250 +Failed requests: 0 +Keep-Alive requests: 250 +Total transferred: 50250 bytes +HTML transferred: 500 bytes +Requests per second: 5.33 [#/sec] (mean) +Time per request: 3754.556 [ms] (mean) +Time per request: 187.728 [ms] (mean, across all concurrent requests) +Transfer rate: 1.05 [Kbytes/sec] received + +... + +Percentage of the requests served within a certain time (ms) + 50% 3755 + 66% 3804 + 75% 3818 + 80% 3825 + 90% 3845 + 95% 3858 + 98% 3874 + 99% 3875 + 100% 4225 (longest request) +``` + + + +이 결과를 보면 초당 5건의 요청만 처리할 수 있고 한 요청당 4초 미만의 시간이 걸리는 것을 알 수 있습니다. +실제 애플리케이션에서는 요청을 처리할 때 많은 함수에서 다량의 작업이 일어날 수 있지만, 이 간단한 +예시에서도 정규표현식을 해석하고 임의의 솔트를 만들고 사용자 비밀번호에서 유일한 해시값을 만들고 +Express 프레임워크 내부 작업을 진행하기 위해 시간이 소비됩니다. + +`--prof` 옵션으로 애플리케이션을 실행했으므로 애플리케이션을 실행한 디렉터리에 tick 파일이 생성됩니다. +파일명은 `isolate-0xnnnnnnnnnnnn-v8.log`(여기서 `n`은 숫자입니다.) 같은 형식일 것입니다. + +이 파일을 이해하려면 Node.js 바이너리에 포함된 tick 프로세서를 사용해야 합니다. +`--prof-process` 플래그로 tick 프로세서를 실행할 수 있습니다. + +``` +node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt +``` + + + +사용하는 텍스트 에디터에서 processed.txt를 열면 몇 가지 정보를 볼 수 있습니다. +파일은 언어로 구분된 단위로 나누어져 있습니다. 우선 요약 부분을 보겠습니다. + +``` + [Summary]: + ticks total nonlib name + 79 0.2% 0.2% JavaScript + 36703 97.2% 99.2% C++ + 7 0.0% 0.0% GC + 767 2.0% Shared libraries + 215 0.6% Unaccounted +``` + + + +이 부분을 보면 C++ 코드에서 수집된 샘플이 97%를 차지하는 것을 볼 수 있으므로 처리된 결과에서 +다른 부분을 볼 때 C++에서 이뤄진 작업에 대부분의 관심을 기울여야 합니다.(Javascript 대비) +그래서 C++ 함수가 대부분의 CPU 시간을 차지한 정보를 담고 있는 [C++] 부분을 찾아볼 것입니다. + +``` + [C++]: + ticks total nonlib name + 19557 51.8% 52.9% node::crypto::PBKDF2(v8::FunctionCallbackInfo const&) + 4510 11.9% 12.2% _sha1_block_data_order + 3165 8.4% 8.6% _malloc_zone_malloc +``` + + + +프로그램에서 72.1%의 CPU 시간을 차지한 3개의 작업을 보겠습니다. 이 결과를 보면 사용자 비밀번호에서 +해시를 생성하는 PBKDF2 함수 호출이 최소 51.8%의 CPU 시간을 차지한 것을 바로 눈치챌 수 있습니다. +하지만 더 낮은 비율을 가진 두 부분은 애플리케이션의 어떤 부분인지 바로 알 수 없습니다.(아니면 +예제를 위해서 그런 척 할 것입니다.) 이러한 함수 간의 관계를 더 이해하려면 각 함수의 주요 호출자 정보를 +제공하는 [Bottom up (heavy) profile] 부분을 봐야 합니다. +이 부분을 찾아보면 다음과 같이 나와 있습니다. + +``` + ticks parent name + 19557 51.8% node::crypto::PBKDF2(v8::FunctionCallbackInfo const&) + 19557 100.0% v8::internal::Builtins::~Builtins() + 19557 100.0% LazyCompile: ~pbkdf2 crypto.js:557:16 + + 4510 11.9% _sha1_block_data_order + 4510 100.0% LazyCompile: *pbkdf2 crypto.js:557:16 + 4510 100.0% LazyCompile: *exports.pbkdf2Sync crypto.js:552:30 + + 3165 8.4% _malloc_zone_malloc + 3161 99.9% LazyCompile: *pbkdf2 crypto.js:557:16 + 3161 100.0% LazyCompile: *exports.pbkdf2Sync crypto.js:552:30 +``` + + + +이 섹션을 분석하려면 위에 나온 tick 횟수보다는 약간 더 작업이 필요합니다. 위에 나온 각 +"호출 스택(call stacks)"에서 parent에 나온 퍼센티지는 현재 줄에 있는 함수가 호출한 +바로 윗 줄의 함수가 차지하는 비율을 알려줍니다. 예를 들어 중간에 있는 _sha1_block_data_order의 +"호출 스택"에서 _sha1_block_data_order가 샘플에서 11.9%를 차지한다는 것을 알 수 있고 +이는 횟수에서 나온 값이라는 것을 알 수 있습니다. 하지만 여기서 이 함수는 항상 Node.js crypto 모듈의 +pbkdf2 함수가 호출한다고도 할 수 있습니다. 비슷하게 _malloc_zone_malloc도 같은 pbkdf2 함수가 +거의 전적으로 호출한다는 것을 알 수 있습니다. 그러므로 이 관점으로 얻은 정보를 사용하면 +_sha1_block_data_order과 _malloc_zone_malloc가 pbkdf2 함수에 의해서 호출되었으므로 +사용자 비밀번호의 해시 계산이 앞에서 본 51.8%뿐 아니라 상위 3개의 모든 CPU 시간을 +차지한다는 것을 알 수 있습니다. + +이 관점으로 보면 비밀번호에 기반을 둔 해시 생성이 최적화 대상이 되어야 한다는 것이 아주 명확합니다. +고맙게도 [비동기 프로그래밍의 장점][]을 완전히 이해하고 있고 사용자 비밀번호에서 해시를 생성하는 과정이 +동기로 진행된다는 것을 알 수 있으므로 이를 이벤트 루프로 바꾸면 됩니다. +이는 해시를 계산하는 동안 들어오는 다른 요청을 처리할 수 있게 합니다. + + + +이 이슈를 처리하려면 pdkdf2 함수의 비동기 버전을 사용하도록 핸들러를 수정하면 됩니다. + +```javascript +app.get('/auth', function (req, res) { + var username = req.query.username || ''; + var password = req.query.password || ''; + + username = username.replace(/[!@#$%^&*]/g, ''); + + if (!username || !password || !users[username]) { + return res.sendStatus(400); + } + + crypto.pbkdf2(password, users[username].salt, 10000, 512, function(err, hash) { + if (users[username].hash.toString() === hash.toString()) { + res.sendStatus(200); + } else { + res.sendStatus(401); + } + }); +}); +``` + + + +애플리케이션의 비동기 버전으로 앞에서 진행한 ab 벤치마크를 실행하면 다음과 같은 결과를 볼 수 있습니다. + +``` +Concurrency Level: 20 +Time taken for tests: 12.846 seconds +Complete requests: 250 +Failed requests: 0 +Keep-Alive requests: 250 +Total transferred: 50250 bytes +HTML transferred: 500 bytes +Requests per second: 19.46 [#/sec] (mean) +Time per request: 1027.689 [ms] (mean) +Time per request: 51.384 [ms] (mean, across all concurrent requests) +Transfer rate: 3.82 [Kbytes/sec] received + +... + +Percentage of the requests served within a certain time (ms) + 50% 1018 + 66% 1035 + 75% 1041 + 80% 1043 + 90% 1049 + 95% 1063 + 98% 1070 + 99% 1071 + 100% 1079 (longest request) +``` + + + +애플리케이션이 이제 초당 20개의 요청을 처리할 수 있게 되었습니다. 이는 동기 해시 생성을 사용한 것보다 +대략 4배가 빨리진 것입니다. 게다가 평균 지연시간이 이전의 4초에서 1초 정도로 줄어들었습니다. + +이 예시의 성능 분석을 통해 V8 tick 프로세서가 Node.js 애플리케이션 성능을 이해하는데 +어떻게 도움이 되는지 알기 바랍니다. + +[V8 내의 프로파일러]: https://developers.google.com/v8/profiler_example +[비동기 프로그래밍의 장점]: https://nodesource.com/blog/why-asynchronous diff --git a/locale/ko/docs/guides/working-with-different-filesystems.md b/locale/ko/docs/guides/working-with-different-filesystems.md new file mode 100644 index 0000000000000..aa4222e92b636 --- /dev/null +++ b/locale/ko/docs/guides/working-with-different-filesystems.md @@ -0,0 +1,420 @@ +--- +title: 여러 파일 시스템에서 작업하기 +layout: docs.hbs +--- + + + +# 여러 파일 시스템에서 작업하기 + +Node는 파일 시스템에서 다양한 기능을 제공합니다. 하지만 모든 파일 시스템이 같은 것은 아닙니다. +여러 파일 시스템에서 동작할 때 코드를 간단하고 안전하게 유지하기 위한 모범 예제를 +아래에서 제시합니다. + + + +## 파일 시스템의 동작 + +파일 시스템을 사용하기 전에 파일 시스템이 어떻게 동작하는지 알아야 합니다. 다른 파일 시스템은 +다르게 동작하고 파일 시스템마다 기능이 더 많거나 적거나 합니다. 예를 들어 대소문자 구분/비구분, +대소문자 유지, 유니코드 형식 보존, 타임스탬프 처리방법, 속성 확장, 아이노드, 유닉스 권한, +데이터 스트림 대안 등이 있습니다. + +`process.platform`에서 파일 시스템의 동작을 추측하는 것을 주의해야 합니다. 예를 들어 사용자가 +대소문자를 구별하는 파일 시스템(HFSX)를 사용할 수도 있으므로 프로그램이 Darwin에서 동작하고 있다고 +대소문자를 구별하지 않는 파일 시스템(HFS+)을 사용한다고 가정해서는 안 됩니다. 유사하게 유닉스 권한이나 +아이노드를 지원하지 않는 외장 드라이브나 USB, 네트워크 드라이브를 사용할 수도 있으므로 Linux에서 +돌아가고 있다고 파일 시스템이 유닉스 권한이나 아이노드를 지원한다고 가정해서는 안 됩니다. + +운영체제로 파일 시스템의 동작을 쉽게 예상할 수 없지만 모두 필요 없는 것은 아닙니다. 알려진 모든 +파일 시스템과 동작의 목록을 관리하는 대신(절대 완료된 목록을 갖지 못할 것입니다.) 파일 시스템이 +실제로 어떻게 동작하는지 확인할 수 있습니다. 쉽게 확인할 수 있는 특정 기능의 존재 여부 만으로도 +확인하기 더 어려운 다른 기능의 동작을 예측하기에 대부분 충분합니다. + +일부 사용자는 워킹 트리의 다양한 경로에 여러 가지 파일 시스템을 마운트해서 +사용할 수도 있다는 것을 명심하세요. + + + +## 최소 공통분모 접근 피하기 + +모든 파일명을 대문자로 정규화하고 모든 파일명을 NFC 유니코드 형식으로 정규화하고 파일의 타임스탬프를 +1초 해상도로 정규화함으로써 파일 시스템의 최소 공통분모로 프로그램이 동작하게 하고 싶을 수도 있습니다. +이를 최소 공통분모 접근이라고 합니다. + +이렇게 하면 안 됩니다. 모든 부분에서 최소 공통분모의 특성과 정확히 같은 파일 시스템에서만 안전하게 +사용할 수 있을 것입니다. 더 향상된 파일 시스템에서는 사용자가 기대하는 방법으로 동작하지 않을 것이고 +파일 시스템이나 타임스탭프 충돌이 발생할 것입니다. 복잡하게 의존하는 이벤트 사이에서 사용자 데이터를 +잃어버리거나 훼손할 가능성이 아주 크고 해결할 수 없거나 어려운 버그를 만들 것입니다. + +2초나 24시간 타임스탬프 해상도만 가진 파일 시스템을 나중에 지원해야 한다면 어떻게 할 것입니까? +유니코드 표준이 정규화 알고리즘과 약간 다른 내용을 포함하게 된다면(이런 일은 과거에도 일어났습니다.) +어떻게 할 것입니까? + +최소 공통분모 접근은 "이식성 있는(portable)" 시스템 호출만 사용해서 이식성 있는 프로그램을 만들려고 +하는 경향이 있습니다. 이는 누출되기 쉽고 실제로는 이식성이 없는 프로그램이 됩니다. + + + +## 슈퍼셋 접근 도입 + +슈퍼셋 접근으로 지원하는 각 플랫폼을 최상으로 사용하게 하세요. 예를 들어, 이식성 있는 백업 프로그램은 +리눅스 시스템에서는 btimes을 지원하지 않더라도 윈도우 시스템에서 btimes(파일이나 폴더의 생성 시간)를 +제대로 동기화해야 하고 btimes를 없애거나 바꾸지 않아야 합니다. 같은 이식성 있는 백업 프로그램은 +리눅스 시스템에서 유닉스 권한을 제대로 동기화해야 하고 윈도우 시스템이 유닉스 권한을 지원하지 않더라도 +유닉스 권한을 없애거나 바꾸면 안 됩니다. + +더 진보된 파일 시스템처럼 동작하게 프로그램을 만들어서 여러 파일 시스템을 처리하세요. 대소문자 구별, +대소문자 유지, 유니코드 형식 구별, 유니코드 형식 보존, 유닉스 권한, 고행상도 나노초 타임스탬프, +확장 속성 등 가능한 모든 기능의 슈퍼셋을 지원하세요. + +프로그램이 대소문자를 보존하고 있다면 대소문자를 구별하지 않는 파일 시스템을 사용해야 할 때 항상 +대소문자를 구별하지 않도록 구현할 수 있습니다. 하지만 프로그램이 대소문자를 유지하지 않는다면 대소문자를 +유지하는 파일 시스템에서 안전하게 사용할 수 없을 것입니다. 유니코드 형식 보존과 타임스탬프 해상도 +보존에서도 마찬가지입니다. + +파일 시스템에 대소문자가 섞인 파일명을 준다면 받은 그대로의 파일명을 유지하세요. 파일 시스템이 +유니코드 형식이나 NFC, NFD(혹은 NFKC나 NFKD)가 섞인 파일명을 준다면 주어진 바이트 순서 +그대로의 파일명을 유지하세요. 파일 시스템이 밀리 초 단위의 타임스탬프를 준다면 밀리 초단위의 해상도로 +타임스탬프를 유지하세요. + +프로그램이 돌아가는 파일 시스템의 동작에서 필요로 하는 기능과 비교해서 기능이 더 부족한 파일 시스템에서 +동작할 때 언제나 적절하게 기능을 줄일 수 있습니다. 파일 시스템 유닉스 권한을 지원하지 않는 것을 알고 +있다면 작성한 유닉스 권한과 같은 권한을 읽으려고 하면 안 됩니다. 파일 시스템이 대소문자를 보존하지 +않는 것을 알고 있지만, 프로그램이 `ab`를 생성할 때 디렉터리 목록에서 `ABC`를 볼 대비를 해야 합니다. +하지만 파일 시스템이 대소문자를 유지하는 것을 알고 있다면 파일명 변경을 감지하거나 파일 시스템에 +대소문자를 구별 하는 경우 `ABC`와 `abc`를 다른 파일명으로 간주해야 합니다. + + + +## 대소문자 보존 + +`test/abc`라는 디렉터리를 생성한 뒤 `fs.readdir('test')`가 `['ABC']`를 반환할 때 놀랄 수도 +있습니다. 이는 Node의 버그가 아닙니다. Node는 파일 시스템이 저장한 파일명을 반환하고 모든 +파일 시스템이 대소문자를 보존하는 것은 아닙니다. 어떤 파일 시스템은 모든 파일명을 +대문자(혹은 소문자)로 바꿉니다. + + + +## 유니코드 형식 보존 + +*대소문자 보존과 유니코드 형식 보존은 비슷한 개념입니다. 유니코드 형식을 보존해야 하는 이유를 +이해하려면 먼저 왜 대소문자를 보존해야 하는지를 확실히 이해해야 합니다. 제대로 이해한다면 +유니코드 형식 보존은 아주 간단합니다.* + +유니코드는 여러 가지 다른 바이트 순서를 사용해서 같은 문자를 인코딩할 수 있습니다. 여러 가지 문자열이 +같아 보일 수 있지만 다른 바이트 순서를 가질 수 있습니다. UTF-8 문자열에서 줄과 관련해서 유니코드가 +동작하는 방식에 대해서 조심해야 합니다. 모든 UTF-8 문자를 하나의 바이트로 인코딩되기를 기대하면 +안 되듯이 사람 눈에는 같아 보이는 여러 가지 UTF-8 문자열이 같은 바이트 표현을 한다고 생각해서는 +안 됩니다. ASCII에서는 이런 기대를 해도 되지만 UTF-8에서는 안 됩니다. + +`test/café`라는 디렉터리(`<63 61 66 c3 a9>`의 바이트 순서와 `string.length === 5`를 가진 +NFC 유니코드 형식)를 만들고 `fs.readdir('test')`가 `['café']`(`<63 61 66 65 cc 81>`의 +바이트 순서와 `string.length === 6`를 가진 NFD 유니코드 형식)을 반환하면 놀랄 수도 있습니다. +이는 Node의 버그가 아닙니다. Node는 파일 시스템이 저장한 파일명을 반환하는데 모든 파일 시스템이 +유니코드 형식을 보존하는 것은 아닙니다. + +예를 들어 HFS+는 거의 항상 모든 파일명을 NFD 형식으로 정규화할 것입니다. HFS+가 NTFS나 EXT4처럼 +동작하기를 기대해도 안 되고 그 반대를 기대해도 안 됩니다. 파일 시스템마다 다른 유니코드를 감추려고 취약한 +추상화로 정규화함으로써 데이터를 항상 바꾸려고 하지 마세요. 이는 아무런 문제도 해결하지 못하고 문제를 +만들어 낼 것입니다. 대신 유니코드 형식을 보존하고 비교함수처럼 정규화만 사용하세요. + + + +## 유니코드 형식 비구별 + +유니코드 형식을 비구별과 유니코드 형식 보존은 종종 헷갈리는 파일 시스템의 다른 두 가지 동작입니다. +때로 대소문자 비구별을 파일명을 저장하고 전송할 때 항상 대문자로 정규화하게 잘못 구현하듯이 유니코드 +형식 비구별도 종종 파일명을 저장하고 전송할 때 파일명을 특정 유니코드 형식(HFS+의 경우 NFD)으로 +항상 정규화하도록 잘못 구현하곤 합니다. 비교할 때만 유니코드를 정규화함으로써 유니코드 형식은 +보존하면서도 유니코드 형식 비구별을 구현하는 것이 가능하고 이 방법이 훨씬 좋습니다. + + + +## 다른 유니코드 형식의 비교 + +Node는 UTF-8 문자열을 NFC나 NFD로 정규화하는 데 사용할 수 있는 +`string.normalize('NFC' / 'NFD')`를 제공합니다. 이 함수의 반환 값은 절대 저장하면 안 되고 +두 UTF-8 문자열이 사용자에게 같아 보이는지 확인하는 비교 함수에서만 사용해야 합니다. + +비교 함수로 `string1.normalize('NFC') === string2.normalize('NFC')`나 +`string1.normalize('NFD') === string2.normalize('NFD')`를 사용할 수 있습니다. +어느 방법을 사용하든 상관없습니다. + +정규화는 빠르지만 같은 문자열을 여러 번 정규화하는 것을 피하고자 비교함수의 입력값에 캐시를 +사용하려고 할 수 있습니다. 캐시에 문자열이 없다면 정규화하고 이를 저장합니다. 캐시를 저장하거나 +유지하지 않도록 조심하고 캐시로써만 사용해야 합니다. + +`normalize()`를 사용하려면 사용하는 Node 버전에 ICU를 포함해야 합니다.(그렇지 않으면 +`normalize()`가 원래의 문자열을 그냥 반환할 것입니다.) 웹사이트에서 최신 버전의 Node를 +다운로드 했다면 ICU가 포함되어 있습니다. + + + +## 타임스탬프 해상도 + +파일의 `mtime`(수정시간)을 `1444291759414`(밀리 초 해상도)로 설정했는데 `fs.stat`가 mtime을 +`1444291759000`(1초 해상도)나 `1444291758000`(2초 해상도)로 반환하는 것에 당황할 수도 +있습니다. 이는 Node의 버그가 아닙니다. Node는 파일 시스템이 저장한 타임스탬프를 반환하고 모든 +파일 시스템이 나노초, 밀리 초, 1초 타임스탬프 해상도를 지원하는 것은 아닙니다. 일부 파일 시스템은 +atime 타임스탬프에 아주 거친 해상도를 쓰기도 합니다.(예를 들어 일부 FAT 파일 시스템은 24시간입니다.) + + + +## 정규화로 파일명과 타임스탬프를 훼손시키지 마세요. + +파일명과 타임스탬프는 사용자 데이터입니다. 데이터를 대문자로 바꾸거나 `CRLF`나 `LF`같은 줄 끝 문자를 +정규화해서 사용자 파일 데이터를 자동으로 재작성하지 말아야 하듯이 대소문자 / 유니코드 형식 / 타임스탬프 +정규화로 파일명이나 타임스탬프를 절대 변경하거나 훼손시키지 않아야 합니다. 정규화는 비교할 때만 사용하고 +데이터를 바꾸면 안 됩니다. + +정규화는 효율적인 손실을 주는 해시 코드입니다. 어떤 종류든 동등한지 검사할 때 사용할 수 있지만(여러 +문자열이 다른 바이트 순서를 가지고 있더라도 같아 보이는지 등) 실제 데이터를 교체하는 데 사용할 수는 +없습니다. 프로그램은 있는 그대로의 파일명과 타임스탬프를 전달해야 합니다. + +프로그램이 NFC(또는 선호하는 어떤 유니코드 형식의 조합이더라도)에서, 혹은 소문자나 대문자 파일명으로, +혹은 2초 해상도 타임스탬프로 새로운 데이터를 만들 수 있지만, 대소문자 / 유니코드 형식 / 타임스탬프 +정규화를 적용해서 기존의 사용자 데이터를 훼손시키면 안됩니다. 대신 슈퍼셋 접근을 적용하고 프로그램에서 +대소문자, 유니코드 형식, 타임스탬프 해상도를 보존하세요. 이 방법을 적용하면 같은 동작을 하는 +파일 시스템을 안정하게 사용할 수 있습니다. + + + +## 정규화 비교 함수를 적절하게 사용하세요. + +대소문자 / 유니코드 형식 / 타임스탬프 비교 함수를 적절하게 사용하세요. 대소문자를 구별하는 +파일 시스템에서 동작한다면 대소문자를 구별하지 않는 파일명 비교 함수를 사용하지 말아야 합니다. 유니코드 +형식을 구별하는 파일 시스템에서 동작한다면(예를 들어 NFC와 NFD를 둘 다 보존하거나 혼합된 유니코드 +형식을 사용하는 NTFS와 대부분의 리눅스 파일 시스템) 유니코드 형식을 구별하지 않는 비교 함수를 +사용하지 마세요. 나노초 타임스탬프 해상도를 가진 파일 시스템에서 동작한다면 2초 해상도로 타임스탬프를 +비교하지 마세요. + + + +## 비교함수에 있는 약간의 차이점에 대비하세요. + +비교함수로 파일 시스템에서 일치 여부를 판단할 때는 주의해야 합니다.(또는 실제로 파일 시스템이 어떻게 +비교하는지 볼 수 있다면 파일 시스템을 탐구해야 합니다.) 예를 들어 대소문자 구분없이 비교하는 것은 단순한 +`toLowerCase()` 비교보다 훨씬 복잡합니다. 사실 `toUpperCase()`가 `toLowerCase()`보다 +보통 더 좋습니다.(`toLowerCase()`가 특정 외국어 문자를 다르게 다루기 때문입니다.) 하지만 모든 +파일 시스템은 자신만의 대소문자 비교 테이블을 가지고 있으므로 파일시스템을 탐구하는 것이 좋습니다. diff --git a/locale/ko/docs/index.md b/locale/ko/docs/index.md new file mode 100644 index 0000000000000..23bdc0f2e11ed --- /dev/null +++ b/locale/ko/docs/index.md @@ -0,0 +1,85 @@ +--- +title: 문서 +layout: docs.hbs +labels: + lts: LTS +--- + + + +# 문서에 관해서 + +이 웹사이트에는 세 가지 종류의 문서가 있습니다. + +* API 레퍼런스 문서 +* ES6 기능 +* 자주 묻는 질문 + + + +### API 레퍼런스 문서 + +[API 레퍼런스 문서](/api/)에는 Node.js의 함수나 객체에 대한 자세한 정보가 있습니다. +이 문서에서 메소드가 어떤 인자를 받고 어떤 값을 반환하는지 해당 메소드와 관련된 에러에는 어떤 것이 +있는지를 알려줍니다. 다양한 Node.js에서 어떤 메소드를 사용할 수 있는지도 알려줍니다. + + + +
+

이전 버전에 대한 API 문서가 필요한가요?

+ + +
+ + + +### ES6 기능 + +[ES6 부분](/ko/docs/es6/)에서는 세 가지 ES6 기능 그룹을 설명하고 Node.js에서 어떤 기능이 +기본적으로 활성화되어있는지 설명하면서 추가 링크를 제공합니다. 특정 Node.js 릴리스 버전에 +어떤 V8 버전이 포함되었는지 찾는 방법도 알려줍니다. + + + +### 자주 묻는 질문 + +[FAQ](/ko/docs/faq/)에서는 Node.js에 기여하는 방법, 행동강령과 거버넌스 모델, +GitHub과 IRC에서 연락을 취하는 방법, 선별된 이슈로 돕는 방법 등을 다룹니다. diff --git a/locale/ko/docs/meta/topics/dependencies.md b/locale/ko/docs/meta/topics/dependencies.md new file mode 100644 index 0000000000000..4fd9f5eb9438b --- /dev/null +++ b/locale/ko/docs/meta/topics/dependencies.md @@ -0,0 +1,213 @@ +--- +title: 의존성 +layout: docs.hbs +--- + + + +# 의존성 + +Node.js가 의존하고 있는 여러 의존성 + +- [라이브러리](#libraries) + - [V8](#v8) + - [libuv](#libuv) + - [http-parser](#http-parser) + - [c-ares](#c-ares) + - [OpenSSL](#openssl) + - [zlib](#zlib) +- [도구](#tools) + - [npm](#npm) + - [gyp](#gyp) + - [gtest](#gtest) + + + +## 라이브러리 + +### V8 + +V8 라이브러리는 Node.js가 V8 C++ API로 제어하는 JavaScript 엔진을 제공합니다. +V8은 구글이 관리하고 크롬에서 사용 중인 엔진입니다. + +- [문서](https://v8docs.nodesource.com/) + + + +### libuv + +또 하나의 중요한 의존성은 libuv입니다. libuv는 C 라이브러리로 논블로킹 I/O 작업을 지원하는 +모든 플랫폼에서 일관된 인터페이스로 추상화하는 데 사용됩니다. libuv는 파일 시스템, DNS, 네트워크, +자식 프로세스, 파이프, 신호 처리, 폴링, 스트리밍을 다루는 메커니즘을 제공하고 운영체제 수준에서 +비동기로 처리될 수 없는 작업을 위한 스레드 풀도 포함하고 있습니다. + +- [문서](http://docs.libuv.org/) + + + +### http-parser + +HTTP 파싱은 http-parser라는 경량 C 라이브러리가 처리합니다. 이는 시스템 호출이나 할당을 하려고 +만들어진 것이 아니므로 요청당 아주 작은 메모리 공간만 차지합니다. + +- [문서](https://github.com/joyent/http-parser/) + + + +### c-ares + +일부 비동기 DNS 요청을 위해서 Node.js는 c-ares라는 C 라이브러리를 사용합니다. c-ares는 +JavaScript DNS 모듈로 resolve() 류의 함수들을 노출합니다. 코어의 다른 부분에서 사용하는 +lookup() 함수는 libuv에서 스레드로 관리되는 getaddrinfo(3) 호출을 사용합니다. 이렇게 사용하는 +이유는 c-ares가 /etc/hosts, /etc/resolv.conf, /etc/svc.conf는 지원하지만 +mDNS 같은 것은 지원하지 않기 때문입니다. + +- [문서](http://c-ares.haxx.se/docs.html) + + + +### OpenSSL + +OpenSSL은 `tls`와 `crypto` 모듈에서 광범위하게 사용되고 있습니다. OpenSSL은 현대 웹이 +보안에서 사용하는 수많은 암호화 함수에 대한 검증된 구현체를 제공합니다. + +- [문서](https://www.openssl.org/docs/) + + + +### zlib + +빠른 압축과 압축 해제를 하기 위해 Node.js는 산업 표준인 zlib 라이브러리를 사용하고 zlib은 +gzip과 libpng를 사용한다고 알려져 있습니다. Node.js는 동기, 비동기, 스트리밍 압축과 +압축 해제 인터페이스에 zlib을 사용합니다. + +- [문서](http://www.zlib.net/manual.html) + + + +## 도구 + +### npm + +Node.js는 모든 것이 모듈화되어 있으므로 질 좋은 패키지 매니저가 필요해졌습니다. 이 목적 때문에 +npm이 만들어졌습니다. npm이 모든 프로그래밍 생태계에서 커뮤니티가 만든 커다란 패키지 선택권을 +제공해 주므로 Node.js를 빠르고 쉽게 만들 수 있습니다. + +- [문서](https://docs.npmjs.com/) + + + +### gyp + +V8에서 파생된 Python 기반의 프로젝트 제너레이터인 gyp가 빌드 시스템을 다룹니다. gyp는 다수의 +플랫폼에서 빌드 시스템을 사용하기 위한 프로젝트 파일을 생성할 수 있습니다. Node.js는 컴파일이 +필요한 언어로 작성된 부분이 많이 있으므로(혹은 의존성 라이브러리에서) 빌드 시스템이 필요합니다. + +- [문서](https://chromium.googlesource.com/external/gyp/+/master/docs/UserDocumentation.md) + + + +### gtest + +네이티브 코드는 Chromium의 gtest로 테스트할 수 있습니다. gtest로 C/C++를 시작하는 +기존의 node 실행 파일 없이 C/C++를 테스트할 수 있습니다. + +- [문서](https://code.google.com/p/googletest/wiki/V1_7_Documentation)