Node 시작하기: API, HTTP 및 ES6+ JavaScript 소개
게시 됨: 2022-03-10Node.js는 "Chrome의 V8 JavaScript 엔진을 기반으로 하는 비동기식 JavaScript 런타임"이며 "경량과 효율성을 제공하는 이벤트 중심의 비차단 I/O 모델을 사용합니다"라고 들어보셨을 것입니다. 그러나 어떤 사람들에게는 그것이 가장 위대한 설명이 아닙니다.
노드란 무엇입니까? 노드가 "비동기"라는 것은 정확히 무엇을 의미하며 "동기"와 어떻게 다릅니까? 어쨌든 "이벤트 기반" 및 "비차단"의 의미는 무엇이며 Node는 애플리케이션, 인터넷 네트워크 및 서버의 더 큰 그림에 어떻게 적합합니까?
Node의 내부 작동을 심층적으로 살펴보고 HyperText Transfer Protocol, API 및 JSON에 대해 배우고, MongoDB, Express, Lodash, Mocha 및 핸들바.
Node.js란?
노드는 브라우저 외부에서 일반 JavaScript(사소한 차이가 있음)를 실행할 수 있는 환경 또는 런타임일 뿐입니다. 이를 사용하여 데스크톱 애플리케이션(Electron과 같은 프레임워크 포함)을 구축하고 웹 또는 앱 서버를 작성하는 등의 작업을 수행할 수 있습니다.
차단/비차단 및 동기/비동기
사용자에 대한 속성을 검색하기 위해 데이터베이스를 호출한다고 가정합니다. 이 호출에는 시간이 걸리며 요청이 "차단"이면 호출이 완료될 때까지 프로그램 실행이 차단됩니다. 이 경우 스레드를 차단했기 때문에 "동기식" 요청을 했습니다.
따라서 동기 작업은 해당 작업이 완료될 때까지 프로세스 또는 스레드를 차단 하여 스레드를 "대기 상태"로 남깁니다. 반면에 비동기 작업은 차단되지 않습니다 . 작업을 완료하는 데 걸리는 시간이나 작업이 완료되는 데 걸리는 시간에 관계없이 스레드 실행이 계속 진행되도록 허용하고 스레드의 어떤 부분도 대기 상태에 빠지지 않습니다.
스레드를 차단 하는 동기 호출의 다른 예를 살펴보겠습니다. 두 날씨 API의 결과를 비교하여 온도 차이 백분율을 찾는 애플리케이션을 구축한다고 가정합니다. 차단 방식으로 Weather API One을 호출하고 결과를 기다립니다. 결과를 얻으면 Weather API Two를 호출하고 결과를 기다립니다. API에 익숙하지 않더라도 이 시점에서 걱정하지 마십시오. 다음 섹션에서 이에 대해 다룰 것입니다. 지금은 API를 두 대의 컴퓨터가 서로 통신할 수 있는 매체로 생각하십시오.
모든 동기 호출이 반드시 차단되는 것은 아니라는 점을 인식하는 것이 중요합니다. 스레드를 차단하거나 대기 상태를 일으키지 않고 동기 작업을 완료할 수 있으면 차단되지 않는 것입니다. 대부분의 경우 동기 호출이 차단되며 완료하는 데 걸리는 시간은 API 서버의 속도, 최종 사용자의 인터넷 연결 다운로드 속도 등과 같은 다양한 요인에 따라 달라집니다.
위 이미지의 경우 API One에서 첫 번째 결과를 가져오기까지 꽤 오랜 시간을 기다려야 했습니다. 그 후 우리는 API Two에서 응답을 받기까지 똑같이 기다려야 했습니다. 두 응답을 모두 기다리는 동안 사용자는 응용 프로그램이 멈추는 것을 알 수 있습니다. UI가 말 그대로 잠기며 이는 사용자 경험에 좋지 않습니다.
비차단 호출의 경우 다음과 같습니다.
얼마나 빨리 실행을 완료했는지 명확하게 알 수 있습니다. API 1을 기다린 다음 API 2를 기다리는 대신 두 가지가 동시에 완료되고 결과를 거의 50% 더 빠르게 달성할 수 있습니다. API One을 호출하고 응답을 기다리기 시작하면 API Two도 호출하고 One과 동시에 응답을 기다리기 시작했습니다.
이 시점에서 좀 더 구체적이고 실질적인 예로 들어가기 전에 편의상 "Synchronous" 는 일반적으로 "Sync"로, "Async " 는 일반적으로 "Async"로 줄인다는 점을 언급하는 것이 중요합니다. 메서드/함수 이름에 사용된 이 표기법을 볼 수 있습니다.
콜백 함수
"비동기식으로 호출을 처리할 수 있다면 호출이 완료되고 응답이 있을 때를 어떻게 알 수 있습니까?" 일반적으로 비동기 메서드에 대한 인수로 콜백 함수를 전달하고 해당 메서드는 나중에 응답으로 해당 함수를 "콜백"합니다. 여기서는 ES5 기능을 사용하고 있지만 나중에 ES6 표준으로 업데이트하겠습니다.
function asyncAddFunction(a, b, callback) { callback(a + b); //This callback is the one passed in to the function call below. } asyncAddFunction(2, 4, function(sum) { //Here we have the sum, 2 + 4 = 6. });
이러한 함수는 함수(콜백)를 인수로 취하기 때문에 "고차 함수"라고 합니다. 또는 콜백 함수가 오류 객체와 응답 객체를 인수로 받아 비동기 함수가 완료되면 이를 표시할 수 있습니다. 나중에 Express에서 이것을 볼 것입니다. asyncAddFunction(...)
을 호출했을 때 메서드 정의에서 콜백 매개변수에 대한 콜백 함수를 제공했음을 알 수 있습니다. 이 함수는 익명 함수(이름이 없음)이며 표현식 구문 을 사용하여 작성되었습니다. 반면에 메서드 정의는 함수 문입니다. 실제로 이름("asyncAddFunction")이 있기 때문에 익명이 아닙니다.
메서드 정의에서 "콜백"이라는 이름을 제공하기 때문에 일부 사람들은 혼동을 느낄 수 있습니다. 그러나 asyncAddFunction(...)
에 세 번째 매개 변수로 전달된 익명 함수는 이름을 알지 못하므로 익명으로 유지됩니다. 또한 나중에 이름으로 해당 함수를 실행할 수 없습니다. 이를 실행하려면 비동기 호출 함수를 다시 거쳐야 합니다.
동기 호출의 예로 Node.js readFileSync(...)
메서드를 사용할 수 있습니다. 다시 말하지만, 우리는 나중에 ES6+로 이동할 것입니다.
var fs = require('fs'); var data = fs.readFileSync('/example.txt'); // The thread will be blocked here until complete.
이 작업을 비동기식으로 수행하는 경우 비동기 작업이 완료될 때 실행되는 콜백 함수를 전달합니다.
var fs = require('fs'); var data = fs.readFile('/example.txt', function(err, data) { //Move on, this will fire when ready. if(err) return console.log('Error: ', err); console.log('Data: ', data); // Assume var data is defined above. }); // Keep executing below, don't wait on the data.
이전에 그런 방식으로 return
이 사용되는 것을 본 적이 없다면, 우리는 오류 개체가 정의된 경우 데이터 개체를 인쇄하지 않도록 함수 실행을 중지하라는 것입니다. else
절에 log 문을 래핑할 수도 있습니다.
asyncAddFunction(...)
과 마찬가지로 fs.readFile(...)
함수 뒤에 있는 코드는 다음과 같습니다.
function readFile(path, callback) { // Behind the scenes code to read a file stream. // The data variable is defined up here. callback(undefined, data); //Or, callback(err, undefined); }
비동기 함수 호출의 마지막 구현을 살펴보겠습니다. 이것은 나중에 실행되는 콜백 함수에 대한 아이디어를 확고히 하는 데 도움이 될 것이며 일반적인 Node.js 프로그램의 실행을 이해하는 데 도움이 될 것입니다.
setTimeout(function() { // ... }, 1000);
setTimeout(...)
메서드는 두 번째 인수로 지정된 시간(밀리초)이 발생한 후에 시작될 첫 번째 매개변수에 대한 콜백 함수를 사용합니다.
좀 더 복잡한 예를 살펴보겠습니다.
console.log('Initiated program.'); setTimeout(function() { console.log('3000 ms (3 sec) have passed.'); }, 3000); setTimeout(function() { console.log('0 ms (0 sec) have passed.'); }, 0); setTimeout(function() { console.log('1000 ms (1 sec) has passed.'); }, 1000); console.log('Terminated program');
우리가 받는 출력은 다음과 같습니다.
Initiated program. Terminated program. 0 ms (0 sec) have passed. 1000 ms (1 sec) has passed. 3000 ms (3 sec) have passed.
첫 번째 로그 문이 예상대로 실행되는 것을 볼 수 있습니다. 두 번째 setTimeout(...)
이후 0초가 초과되기 전에 마지막 로그 문이 즉시 화면에 인쇄됩니다. 그 직후에 두 번째, 세 번째, 첫 번째 setTimeout(...)
메서드가 실행됩니다.
Node.js가 비차단이 아닌 경우 첫 번째 로그 문을 보고 다음을 보기 위해 3초를 기다리며 즉시 세 번째(0초 setTimeout(...)
를 보고 한 번 더 기다려야 합니다. 두 번째로 마지막 두 개의 로그 문을 확인합니다. Node의 비차단 특성은 모든 타이머가 입력된 순서가 아니라 프로그램이 실행되는 순간부터 카운트다운을 시작하도록 합니다. Node API를 살펴보고 싶을 수도 있습니다. Node가 내부에서 작동하는 방식에 대한 자세한 내용은 Callstack 및 Event Loop를 참조하세요.
콜백 함수가 보인다고 해서 반드시 코드에 비동기 호출이 있다는 의미는 아니라는 점에 유의하는 것이 중요합니다. 서버 호출과 같이 작업을 완료하는 데 시간이 걸린다고 가정하기 때문에 "async" 위에 asyncAddFunction(…)
메서드를 호출했습니다. 실제로 두 개의 숫자를 더하는 과정은 비동기가 아니므로 실제로 스레드를 차단하지 않는 방식으로 콜백 함수를 사용하는 예가 될 것입니다.
콜백에 대한 약속
콜백은 JavaScript, 특히 다중 중첩 콜백에서 빠르게 지저분해질 수 있습니다. 우리는 함수에 대한 인수로 콜백을 전달하는 데 익숙하지만 약속을 사용하면 함수에서 반환된 객체에 콜백을 택하거나 첨부할 수 있습니다. 이렇게 하면 보다 우아한 방식으로 여러 비동기 호출을 처리할 수 있습니다.
예를 들어, API 호출을 하고 있고 ' makeAPICall(...)
'이라는 고유한 이름이 아닌 함수가 URL과 콜백을 사용한다고 가정합니다.
함수 makeAPICall(...)
은 다음과 같이 정의됩니다.
function makeAPICall(path, callback) { // Attempt to make API call to path argument. // ... callback(undefined, res); // Or, callback(err, undefined); depending upon the API's response. }
우리는 그것을 다음과 같이 부를 것입니다:
makeAPICall('/example', function(err1, res1) { if(err1) return console.log('Error: ', err1); // ... });
첫 번째 응답을 사용하여 다른 API 호출을 수행하려면 두 콜백을 모두 중첩해야 합니다. res1
객체의 userName
속성을 두 번째 API 호출 경로에 주입해야 한다고 가정합니다. 우리는해야:
makeAPICall('/example', function(err1, res1) { if(err1) return console.log('Error: ', err1); makeAPICall('/newExample/' + res1.userName, function(err2, res2) { if(err2) return console.log('Error: ', err2); console.log(res2); }); });
참고 : 문자열 연결 대신 res1.userName
속성을 삽입하는 ES6+ 방법은 "템플릿 문자열"을 사용하는 것입니다. 그렇게 하면 문자열을 따옴표( '
또는 "
)로 묶는 대신 키보드의 Escape 키 아래에 있는 역따옴표( `
)를 사용합니다. 그런 다음 ${}
표기법을 사용하여 JS 표현식을 내부에 포함합니다. 대괄호. 결국 이전 경로는 역따옴표로 /newExample/${res.UserName}
이 됩니다.
콜백을 중첩하는 이 방법은 소위 "JavaScript Pyramid of Doom"이라고 하는 매우 세련되지 않게 빠르게 될 수 있습니다. 콜백 대신 약속을 사용하는 경우 첫 번째 예제의 코드를 다음과 같이 리팩토링할 수 있습니다.
makeAPICall('/example').then(function(res) { // Success callback. // ... }, function(err) { // Failure callback. console.log('Error:', err); });
then()
함수의 첫 번째 인수는 성공 콜백이고 두 번째 인수는 실패 콜백입니다. 또는 .then()
에 대한 두 번째 인수를 잃어버리고 대신 .catch()
를 호출할 수 있습니다. .then()
에 대한 인수는 선택 사항이며 .catch()
호출은 .then(successCallback, null)
과 동일합니다.
.catch()
를 사용하여 다음을 수행합니다.
makeAPICall('/example').then(function(res) { // Success callback. // ... }).catch(function(err) { // Failure Callback console.log('Error: ', err); });
가독성을 위해 이것을 재구성할 수도 있습니다.
makeAPICall('/example') .then(function(res) { // ... }) .catch(function(err) { console.log('Error: ', err); });
.then()
호출을 어떤 함수에 붙이고 작동할 것으로 기대할 수는 없다는 점에 유의하는 것이 중요합니다. 우리가 호출하는 함수는 비동기 작업이 완료될 때 .then()
을 실행할 약속인 약속을 실제로 반환해야 합니다. 이 경우 makeAPICall(...)
은 완료되면 then()
블록이나 catch()
블록을 실행하여 제 역할을 수행합니다.
makeAPICall(...)
이 Promise를 반환하도록 하기 위해 변수에 함수를 할당합니다. 여기서 해당 함수는 Promise 생성자입니다. 약속은 이행 되거나 거부 될 수 있습니다. 여기서 이행은 약속과 관련된 작업이 성공적으로 완료되었음을 의미하고 거부는 반대를 의미합니다. 약속이 이행되거나 거부되면 우리는 그것이 해결 되었다고 말하고 아마도 비동기 호출 중에 약속이 해결되기를 기다리는 동안 우리는 약속이 보류 중이라고 말합니다.
Promise 생성자는 두 개의 매개변수를 받는 하나의 콜백 함수를 인수로 받습니다. resolve
와 reject
는 나중에 .then()
또는 .then()
실패에서 성공 콜백을 실행하기 위해 호출할 것입니다. 콜백 또는 .catch()
제공된 경우).
다음은 다음과 같은 예입니다.
var examplePromise = new Promise(function(resolve, reject) { // Do whatever we are going to do and then make the appropiate call below: resolve('Happy!'); // — Everything worked. reject('Sad!'); // — We noticed that something went wrong. }):
그런 다음 다음을 사용할 수 있습니다.
examplePromise.then(/* Both callback functions in here */); // Or, the success callback in .then() and the failure callback in .catch().
그러나 examplePromise
는 인수를 사용할 수 없습니다. 그러한 종류는 목적을 무효화하므로 대신 약속을 반환할 수 있습니다.
function makeAPICall(path) { return new Promise(function(resolve, reject) { // Make our async API call here. if (/* All is good */) return resolve(res); //res is the response, would be defined above. else return reject(err); //err is error, would be defined above. }); }
Promise는 "Promise Chaining" 개념으로 코드의 구조와 우아함을 개선하기 위해 정말 빛을 발합니다. 이렇게 하면 .then()
절 내에서 새 Promise를 반환할 수 있으므로 두 번째 Promise에서 적절한 콜백을 실행하는 두 번째 .then()
을 그 후에 첨부할 수 있습니다.
Promises를 사용하여 위의 다중 API URL 호출을 리팩토링하면 다음을 얻습니다.
makeAPICall('/example').then(function(res) { // First response callback. Fires on success to '/example' call. return makeAPICall(`/newExample/${res.UserName}`); // Returning new call allows for Promise Chaining. }, function(err) { // First failure callback. Fires if there is a failure calling with '/example'. console.log('Error:', err); }).then(function(res) { // Second response callback. Fires on success to returned '/newExample/...' call. console.log(res); }, function(err) { // Second failure callback. Fire if there is a failure calling with '/newExample/...' console.log('Error:', err); });
먼저 makeAPICall('/example')
을 호출합니다. 그러면 약속이 반환되므로 .then()
을 첨부합니다. then()
내부에서 makeAPICall(...)
에 대한 새 호출을 반환합니다. 이 호출은 앞에서 본 것처럼 그 자체로 약속을 반환하여 첫 번째 이후에 새로운 .then()
을 연결할 수 있도록 합니다.
위와 같이 가독성을 위해 이를 재구성하고 일반 catch()
all 절에 대한 실패 콜백을 제거할 수 있습니다. 그러면 DRY 원칙(Don't Repeat Yourself)에 따라 오류 처리를 한 번만 구현하면 됩니다.
makeAPICall('/example') .then(function(res) { // Like earlier, fires with success and response from '/example'. return makeAPICall(`/newExample/${res.UserName}`); // Returning here lets us chain on a new .then(). }) .then(function(res) { // Like earlier, fires with success and response from '/newExample'. console.log(res); }) .catch(function(err) { // Generic catch all method. Fires if there is an err with either earlier call. console.log('Error: ', err); });
.then()의 성공 및 실패 콜백은 .then()
.then()
해당하는 개별 Promise의 상태에 대해서만 실행됩니다. 그러나 catch
블록은 .then()
에서 발생하는 모든 오류를 포착합니다.
ES6 Const 대 Let
모든 예제에서 ES5 기능과 이전 var
키워드를 사용했습니다. 오늘날에도 수백만 줄의 코드가 이러한 ES5 방법을 사용하여 실행되고 있지만 현재 ES6+ 표준으로 업데이트하는 것이 유용하며 위의 코드 중 일부를 리팩터링할 것입니다. const
와 let
으로 시작합시다.
var
키워드로 변수를 선언하는 데 익숙할 수 있습니다.
var pi = 3.14;
ES6+ 표준을 사용하면 다음 중 하나를 만들 수 있습니다.
let pi = 3.14;
또는
const pi = 3.14;
여기서 const
는 "상수"를 의미합니다. 나중에 다시 할당할 수 없는 값입니다. (객체 속성을 제외하고 — 곧 다룰 것입니다. 또한 const
로 선언된 변수는 변경할 수 없으며 변수에 대한 참조만 변경 가능합니다.)
이전 JavaScript에서는 if
, while
, {}
와 같은 범위를 차단합니다. for
등은 어떤 식으로든 var
에 영향을 미치지 않았으며 이는 Java 또는 C++와 같은 정적으로 유형이 지정된 언어와 상당히 다릅니다. 즉, var
의 범위는 둘러싸는 전체 함수이며 전역(함수 외부에 배치된 경우) 또는 로컬(함수 내에 배치된 경우)일 수 있습니다. 이를 시연하려면 다음 예를 참조하십시오.
function myFunction() { var num = 5; console.log(num); // 5 console.log('--'); for(var i = 0; i < 10; i++) { var num = i; console.log(num); //num becomes 0 — 9 } console.log('--'); console.log(num); // 9 console.log(i); // 10 } myFunction();
산출:
5 --- 0 1 2 3 ... 7 8 9 --- 9 10
여기서 주목해야 할 중요한 점은 for
범위 내에서 새 var num
을 정의하면 for 외부와 위의 var num
에 직접적인 영향을 미친다는 것 for
. 이는 var
의 범위가 항상 블록이 아니라 둘러싸는 함수의 범위이기 때문입니다.
다시 말하지만, 기본적으로 for()
내부의 var i
는 기본적으로 myFunction
의 범위이므로 루프 외부에서 i
에 액세스하여 10을 얻을 수 있습니다.
변수에 값을 할당한다는 점에서 let
은 var
와 동일하며 let
에는 블록 범위가 있으므로 위의 var
에서 발생한 예외는 발생하지 않습니다.
function myFunction() { let num = 5; console.log(num); // 5 for(let i = 0; i < 10; i++) { let num = i; console.log('--'); console.log(num); // num becomes 0 — 9 } console.log('--'); console.log(num); // 5 console.log(i); // undefined, ReferenceError }
const
키워드를 보면 재할당하려고 하면 오류가 발생함을 알 수 있습니다.
const c = 299792458; // Fact: The constant "c" is the speed of light in a vacuum in meters per second. c = 10; // TypeError: Assignment to constant variable.
객체에 const
변수를 할당하면 상황이 흥미로워집니다.
const myObject = { name: 'Jane Doe' }; // This is illegal: TypeError: Assignment to constant variable. myObject = { name: 'John Doe' }; // This is legal. console.log(myObject.name) -> John Doe myObject.name = 'John Doe';
보시다시피, const
객체에 할당된 객체에 대한 메모리의 참조만 변경할 수 없으며 값 자체는 변경할 수 없습니다.
ES6 화살표 기능
다음과 같은 함수를 만드는 데 익숙할 수 있습니다.
function printHelloWorld() { console.log('Hello, World!'); }
화살표 기능을 사용하면 다음과 같이 됩니다.
const printHelloWorld = () => { console.log('Hello, World!'); };
숫자의 제곱을 반환하는 간단한 함수가 있다고 가정합니다.
const squareNumber = (x) => { return x * x; } squareNumber(5); // We can call an arrow function like an ES5 functions. Returns 25.
ES5 함수와 마찬가지로 괄호를 사용하여 인수를 사용할 수 있고 일반적인 반환 문을 사용할 수 있으며 다른 함수와 마찬가지로 함수를 호출할 수 있음을 알 수 있습니다.
위의 printHelloWorld()
와 같이 함수가 인수를 사용하지 않는 경우 괄호가 필요하지만 하나만 사용하는 경우 괄호를 삭제할 수 있으므로 이전의 squareNumber()
메서드 정의를 다음과 같이 다시 작성할 수 있습니다.
const squareNumber = x => { // Notice we have dropped the parentheses for we only take in one argument. return x * x; }
단일 인수를 괄호 안에 캡슐화할지 여부는 개인 취향의 문제이며 개발자가 두 가지 방법을 모두 사용하는 것을 볼 수 있습니다.
마지막으로 위의 squareNumber(...)
와 같이 하나의 표현식만 암시적으로 반환하려는 경우 메서드 서명과 함께 return 문을 넣을 수 있습니다.
const squareNumber = x => x * x;
그건,
const test = (a, b, c) => expression
와 같다
const test = (a, b, c) => { return expression }
묵시적으로 객체를 반환하기 위해 위의 속기를 사용할 때 사물이 모호해집니다. JavaScript가 객체를 캡슐화하는 데 필요한 괄호가 함수 본문이 아니라고 믿게 하는 이유는 무엇입니까? 이 문제를 해결하기 위해 개체의 대괄호를 괄호로 묶습니다. 이것은 우리가 실제로 객체를 반환하고 있으며 본문을 정의하는 것이 아님을 JavaScript에 명시적으로 알려줍니다.
const test = () => ({ pi: 3.14 }); // Spaces between brackets are a formality to make the code look cleaner.
ES6 함수의 개념을 확고히 하기 위해 이전 코드 중 일부를 리팩토링하여 두 표기법 간의 차이점을 비교할 수 있습니다.
asyncAddFunction(...)
은 위에서 리팩토링할 수 있습니다.
function asyncAddFunction(a, b, callback){ callback(a + b); }
에게:
const aysncAddFunction = (a, b, callback) => { callback(a + b); };
또는 다음을 수행할 수도 있습니다.
const aysncAddFunction = (a, b, callback) => callback(a + b); // This will return callback(a + b).
함수를 호출할 때 콜백에 화살표 함수를 전달할 수 있습니다.
asyncAddFunction(10, 12, sum => { // No parentheses because we only take one argument. console.log(sum); }
이 방법이 코드 가독성을 향상시키는 방법은 분명합니다. 한 가지 사례를 보여주기 위해 위의 이전 ES5 Promise 기반 예제를 가져와 화살표 기능을 사용하도록 리팩토링할 수 있습니다.
makeAPICall('/example') .then(res => makeAPICall(`/newExample/${res.UserName}`)) .then(res => console.log(res)) .catch(err => console.log('Error: ', err));
이제 화살표 기능에 몇 가지 주의 사항이 있습니다. 하나는 this
키워드를 바인딩하지 않습니다. 다음 객체가 있다고 가정합니다.
const Person = { name: 'John Doe', greeting: () => { console.log(`Hi. My name is ${this.name}.`); } }
Person.greeting()
호출이 "안녕하세요. 제 이름은 존 도입니다.” 대신 우리는 "안녕하세요. 내 이름은 정의되지 않았습니다." 화살표 함수에는 this
가 없기 때문에 화살표 함수 내에서 this
사용하려고 하면 기본적으로 둘러싸는 범위의 this
가 설정되고 Person
객체의 바깥쪽 범위는 브라우저에서 window
, 또는 module.exports
에 있기 때문입니다. 마디.
이를 증명하기 위해 동일한 객체를 다시 사용하지만 전역 this
의 name
속성을 'Jane Doe'와 같은 것으로 설정하면 화살표 함수의 this.name
은 'Jane Doe'를 반환 this
. 범위를 둘러싸거나 Person
개체의 부모입니다.
this.name = 'Jane Doe'; const Person = { name: 'John Doe', greeting: () => { console.log(`Hi. My name is ${this.name}.`); } } Person.greeting(); // Hi. My name is Jane Doe
이것은 'Lexical Scoping'으로 알려져 있으며, 우리는 콜론과 화살표를 잃는 소위 'Short Syntax'를 사용하여 이 문제를 해결할 수 있습니다.
const Person = { name: 'John Doe', greeting() { console.log(`Hi. My name is ${this.name}.`); } } Person.greeting() //Hi. My name is John Doe.
ES6 클래스
JavaScript는 클래스를 지원하지 않았지만 항상 위와 같은 객체로 클래스를 에뮬레이트할 수 있습니다. EcmaScript 6은 class
및 new
키워드를 사용하여 클래스를 지원합니다.
class Person { constructor(name) { this.name = name; } greeting() { console.log(`Hi. My name is ${this.name}.`); } } const person = new Person('John'); person.greeting(); // Hi. My name is John.
생성자 함수는 new
키워드를 사용할 때 자동으로 호출되며, 초기에 객체를 설정하기 위해 인수를 전달할 수 있습니다. 이것은 Java, C++ 및 C#과 같은 정적으로 유형이 지정된 객체 지향 프로그래밍 언어에 대한 경험이 있는 모든 독자에게 친숙할 것입니다.
OOP 개념에 대해 너무 자세히 설명하지 않고 그러한 또 다른 패러다임은 한 클래스가 다른 클래스에서 상속할 수 있도록 하는 "상속"입니다. 예를 들어 Car
라는 클래스는 모든 자동차가 필요로 하는 "stop", "start" 등과 같은 메서드를 포함하는 매우 일반적입니다. 그러면 SportsCar
라는 클래스의 하위 집합이 Car
에서 기본 작업을 상속하고 사용자 지정이 필요한 모든 것을 재정의할 수 있습니다. 이러한 클래스를 다음과 같이 나타낼 수 있습니다.
class Car { constructor(licensePlateNumber) { this.licensePlateNumber = licensePlateNumber; } start() {} stop() {} getLicensePlate() { return this.licensePlateNumber; } // … } class SportsCar extends Car { constructor(engineRevCount, licensePlateNumber) { super(licensePlateNumber); // Pass licensePlateNumber up to the parent class. this.engineRevCount = engineRevCount; } start() { super.start(); } stop() { super.stop(); } getLicensePlate() { return super.getLicensePlate(); } getEngineRevCount() { return this.engineRevCount; } }
super
키워드를 사용하면 상위 또는 상위 클래스의 속성 및 메서드에 액세스할 수 있음을 분명히 알 수 있습니다.
자바스크립트 이벤트
이벤트는 사용자가 응답할 수 있는 발생하는 작업입니다. 응용 프로그램에 대한 로그인 양식을 작성한다고 가정합니다. 사용자가 "제출" 버튼을 누르면 코드의 "이벤트 핸들러"(일반적으로 함수)를 통해 해당 이벤트에 반응할 수 있습니다. 이 함수가 이벤트 핸들러로 정의될 때 우리는 "이벤트 핸들러 등록"이라고 말합니다. 제출 버튼 클릭에 대한 이벤트 처리기는 사용자가 제공한 입력의 형식을 확인하고 SQL 주입 또는 교차 사이트 스크립팅과 같은 공격을 방지하기 위해 삭제합니다(클라이언트 측의 코드는 고려될 수 없음에 유의하십시오. 항상 서버의 데이터를 삭제하십시오. 브라우저의 어떤 것도 신뢰하지 마십시오. 그런 다음 해당 사용자 이름과 암호 조합이 사용자를 인증하고 토큰을 제공하기 위해 데이터베이스 내에서 종료되는지 확인하십시오.
이것은 노드에 대한 기사이므로 노드 이벤트 모델에 초점을 맞출 것입니다.
Node의 events
모듈을 사용하여 특정 이벤트를 내보내고 반응할 수 있습니다. 이벤트를 발생시키는 모든 객체는 EventEmitter
클래스의 인스턴스입니다.
emit()
메서드를 호출하여 이벤트를 발생시킬 수 있고 on()
메서드를 통해 해당 이벤트를 수신합니다. 둘 다 EventEmitter
클래스를 통해 노출됩니다.
const EventEmitter = require('events'); const myEmitter = new EventEmitter();
이제 myEmitter
를 사용하여 EventEmitter
클래스의 인스턴스를 사용하여 emit()
및 on()
액세스할 수 있습니다.
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('someEvent', () => { console.log('The "someEvent" event was fired (emitted)'); }); myEmitter.emit('someEvent'); // This will call the callback function above.
myEmitter.on()
의 두 번째 매개변수는 이벤트가 발생할 때 실행될 콜백 함수입니다. 이것이 이벤트 핸들러입니다. 첫 번째 매개변수는 이벤트의 이름으로, 우리가 원하는 대로 지정할 수 있지만, camelCase 명명 규칙이 권장됩니다.
또한 이벤트 핸들러는 이벤트가 발생할 때 전달되는 인수를 원하는 만큼 사용할 수 있습니다.
const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('someEvent', (data) => { console.log(`The "someEvent" event was fired (emitted) with data: ${data}`); }); myEmitter.emit('someEvent', 'This is the data payload');
상속을 사용하여 'EventEmitter'의 emit()
및 on()
메서드를 모든 클래스에 노출할 수 있습니다. 이것은 Node.js 클래스를 생성하고 EventEmitter
에서 사용 가능한 속성을 상속하기 위해 extends
예약 키워드를 사용하여 수행됩니다.
const EventEmitter = require('events'); class MyEmitter extends EventEmitter { // This is my class. I can emit events from a MyEmitter object. }
자동차 선체의 자이로스코프, 가속도계 및 압력계에서 데이터를 수신하는 차량 충돌 알림 프로그램을 구축한다고 가정합니다. 차량이 물체와 충돌하면 외부 센서가 충돌을 감지하여 collide(...)
함수를 실행하고 집계된 센서 데이터를 멋진 JavaScript 개체로 전달합니다. 이 함수는 collision
이벤트를 내보내 공급업체에 충돌을 알립니다.
const EventEmitter = require('events'); class Vehicle extends EventEmitter { collide(collisionStatistics) { this.emit('collision', collisionStatistics) } } const myVehicle = new Vehicle(); myVehicle.on('collision', collisionStatistics => { console.log('WARNING! Vehicle Impact Detected: ', collisionStatistics); notifyVendor(collisionStatistics); }); myVehicle.collide({ ... });
이것은 우리가 클래스의 collide 함수 안에 있는 이벤트 핸들러 내에 코드를 넣을 수 있는 복잡한 예이지만 그럼에도 불구하고 노드 이벤트 모델이 어떻게 작동하는지 보여줍니다. 일부 자습서에서는 개체가 이벤트를 내보내도록 허용하는 util.inherits()
메서드를 보여줍니다. ES6 클래스 및 extends
을 위해 더 이상 사용되지 않습니다.
노드 패키지 관리자
Node 및 JavaScript로 프로그래밍할 때 npm
에 대해 듣는 것은 매우 일반적입니다. Npm은 바로 그 일을 하는 패키지 관리자입니다. JavaScript의 일반적인 문제를 해결하는 타사 패키지의 다운로드를 허용합니다. Yarn, Npx, Grunt 및 Bower와 같은 다른 솔루션도 존재하지만 이 섹션에서는 npm
과 이를 사용하여 간단한 명령줄 인터페이스(CLI)를 통해 애플리케이션에 대한 종속성을 설치하는 방법에만 초점을 맞춥니다.
npm
으로 간단하게 시작해 보겠습니다. NPM에서 사용 가능한 모든 패키지를 보려면 NpmJS 홈페이지를 방문하십시오. NPM 패키지에 의존하는 새 프로젝트를 시작할 때 프로젝트의 루트 디렉토리에 있는 터미널을 통해 npm init
를 실행해야 합니다. package.json
파일을 만드는 데 사용할 일련의 질문을 받게 됩니다. 이 파일은 모든 종속성(응용 프로그램이 기능에 의존하는 모듈, 스크립트)을 저장합니다. 테스트 실행, 프로젝트 빌드, 개발 서버 시작 등을 위한 사전 정의된 터미널 명령 등입니다.
패키지를 설치하려면 npm install [package-name] --save
를 실행하기만 하면 됩니다. save
플래그는 패키지와 해당 버전이 package.json
파일에 기록되도록 합니다. npm
버전 5부터 기본적으로 종속성이 저장되므로 --save
를 생략할 수 있습니다. 또한 방금 설치한 패키지에 대한 코드가 포함된 새 node_modules
폴더도 볼 수 있습니다. 이것은 npm i [package-name]
으로 단축할 수도 있습니다. 참고로 node_modules
폴더는 크기 때문에 GitHub 리포지토리에 포함되어서는 안 됩니다. GitHub(또는 다른 버전 관리 시스템)에서 리포지토리를 복제할 때마다 npm install
명령을 실행하여 package.json
파일에 정의된 모든 패키지를 가져와 자동으로 node_modules
디렉토리를 생성해야 합니다. 예를 들어 npm i [package-name]@1.10.1 --save
와 같은 특정 버전에서 패키지를 설치할 수도 있습니다.
패키지를 제거하는 것은 npm remove [package-name]
을 설치하는 것과 비슷합니다.
패키지를 전역으로 설치할 수도 있습니다. 이 패키지는 작업 중인 프로젝트뿐만 아니라 모든 프로젝트에서 사용할 수 있습니다. npm i [package-name]
다음에 -g
플래그를 사용하여 이 작업을 수행합니다. 이것은 Google Firebase 및 Heroku와 같은 CLI에 일반적으로 사용됩니다. 이 방법이 제공하는 용이성에도 불구하고 패키지를 전역적으로 설치하는 것은 일반적으로 좋지 않은 방법으로 간주됩니다. 패키지는 package.json
파일에 저장되지 않고 다른 개발자가 프로젝트를 사용하려고 시도하면 필요한 모든 종속성을 얻지 못할 것이기 때문입니다. npm install
.
API 및 JSON
API는 프로그래밍에서 매우 일반적인 패러다임이며, 개발자로서의 경력을 막 시작하는 경우에도 특히 웹 및 모바일 개발에서 API와 그 사용법이 자주 등장할 것입니다.
API는 응용 프로그래밍 인터페이스 이며 기본적으로 분리된 두 시스템이 서로 통신할 수 있는 방법입니다. 보다 기술적인 용어로 API는 시스템 또는 컴퓨터 프로그램(일반적으로 서버)이 요청을 수신하고 적절한 응답(호스트라고도 하는 클라이언트에)을 보낼 수 있도록 합니다.
날씨 애플리케이션을 구축한다고 가정해 보겠습니다. 사용자의 주소를 위도와 경도로 지오코딩하는 방법이 필요합니다. 그런 다음 해당 특정 위치의 현재 또는 예상 날씨를 얻을 수 있는 방법이 필요합니다.
As a developer, you want to focus on building your app and monetizing it, not putting the infrastructure in place to geocode addresses or placing weather stations in every city.
Luckily for you, companies like Google and OpenWeatherMap have already put that infrastructure in place, you just need a way to talk to it — that is where the API comes in. While, as of now, we have developed a very abstract and ambiguous definition of the API, bear with me. We'll be getting to tangible examples soon.
Now, it costs money for companies to develop, maintain, and secure that aforementioned infrastructure, and so it is common for corporations to sell you access to their API. This is done with that is known as an API key, a unique alphanumeric identifier associating you, the developer, with the API. Every time you ask the API to send you data, you pass along your API key. The server can then authenticate you and keep track of how many API calls you are making, and you will be charged appropriately. The API key also permits Rate-Limiting or API Call Throttling (a method of throttling the number of API calls in a certain timeframe as to not overwhelm the server, preventing DOS attacks — Denial of Service). Most companies, however, will provide a free quota, giving you, as an example, 25,000 free API calls a day before charging you.
Up to this point, we have established that an API is a method by which two computer programs can communicate with each other. If a server is storing data, such as a website, and your browser makes a request to download the code for that site, that was the API in action.
Let us look at a more tangible example, and then we'll look at a more real-world, technical one. Suppose you are eating out at a restaurant for dinner. You are equivalent to the client, sitting at the table, and the chef in the back is equivalent to the server.
Since you will never directly talk to the chef, there is no way for him/her to receive your request (for what order you would like to make) or for him/her to provide you with your meal once you order it. We need someone in the middle. In this case, it's the waiter, analogous to the API. The API provides a medium with which you (the client) may talk to the server (the chef), as well as a set of rules for how that communication should be made (the menu — one meal is allowed two sides, etc.)
Now, how do you actually talk to the API (the waiter)? You might speak English, but the chef might speak Spanish. Is the waiter expected to know both languages to translate? What if a third person comes in who only speaks Mandarin? 그럼? Well, all clients and servers have to agree to speak a common language, and in computer programming, that language is JSON, pronounced JAY-sun, and it stands for JavaScript Object Notation.
At this point, we don't quite know what JSON looks like. It's not a computer programming language, it's just, well, a language, like English or Spanish, that everyone (everyone being computers) understands on a guaranteed basis. It's guaranteed because it's a standard, notably RFC 8259 , the JavaScript Object Notation (JSON) Data Interchange Format by the Internet Engineering Task Force (IETF).
Even without formal knowledge of what JSON actually is and what it looks like (we'll see in an upcoming article in this series), we can go ahead introduce a technical example operating on the Internet today that employs APIs and JSON. APIs and JSON are not just something you can choose to use, it's not equivalent to one out of a thousand JavaScript frameworks you can pick to do the same thing. It is THE standard for data exchange on the web.
Suppose you are building a travel website that compares prices for aircraft, rental car, and hotel ticket prices. Let us walk through, step-by-step, on a high level, how we would build such an application. Of course, we need our User Interface, the front-end, but that is out of scope for this article.
We want to provide our users with the lowest price booking method. Well, that means we need to somehow attain all possible booking prices, and then compare all of the elements in that set (perhaps we store them in an array) to find the smallest element (known as the infimum in mathematics.)
How will we get this data? Well, suppose all of the booking sites have a database full of prices. Those sites will provide an API, which exposes the data in those databases for use by you. You will call each API for each site to attain all possible booking prices, store them in your own array, find the lowest or minimum element of that array, and then provide the price and booking link to your user. We'll ask the API to query its database for the price in JSON, and it will respond with said price in JSON to us. We can then use, or parse, that accordingly. We have to parse it because APIs will return JSON as a string, not the actual JavaScript data type of JSON. This might not make sense now, and that's okay. We'll be covering it more in a future article.
Also, note that just because something is called an API does not necessarily mean it operates on the web and sends and receives JSON. The Java API, for example, is just the list of classes, packages, and interfaces that are part of the Java Development Kit (JDK), providing programming functionality to the programmer.
괜찮아. We know we can talk to a program running on a server by way of an Application Programming Interface, and we know that the common language with which we do this is known as JSON. But in the web development and networking world, everything has a protocol. What do we actually do to make an API call, and what does that look like code-wise? That's where HTTP Requests enter the picture, the HyperText Transfer Protocol, defining how messages are formatted and transmitted across the Internet. Once we have an understanding of HTTP (and HTTP verbs, you'll see that in the next section), we can look into actual JavaScript frameworks and methods (like fetch()
) offered by the JavaScript API (similar to the Java API), that actually allow us to make API calls.
HTTP And HTTP Requests
HTTP is the HyperText Transfer Protocol. It is the underlying protocol that determines how messages are formatted as they are transmitted and received across the web. Let's think about what happens when, for example, you attempt to load the home page of Smashing Magazine in your web browser.
You type the website URL (Uniform Resource Locator) in the URL bar, where the DNS server (Domain Name Server, out of scope for this article) resolves the URL into the appropriate IP Address. The browser makes a request, called a GET Request, to the Web Server to, well, GET the underlying HTML behind the site. The Web Server will respond with a message such as “OK”, and then will go ahead and send the HTML down to the browser where it will be parsed and rendered accordingly.
There are a few things to note here. First, the GET Request, and then the “OK” response. Suppose you have a specific database, and you want to write an API to expose that database to your users. Suppose the database contains books the user wants to read (as it will in a future article in this series). Then there are four fundamental operations your user may want to perform on this database, that is, Create a record, Read a record, Update a record, or Delete a record, known collectively as CRUD operations.
Let's look at the Read operation for a moment. Without incorrectly assimilating or conflating the notion of a web server and a database, that Read operation is very similar to your web browser attempting to get the site from the server, just as to read a record is to get the record from the database.
이것을 HTTP 요청이라고 합니다. 데이터를 가져오기 위해 어딘가의 서버에 요청을 하고 있으며, 따라서 요청의 이름은 적절하게 "GET"이며 대문자는 이러한 요청을 나타내는 표준 방법입니다.
CRUD의 Create 부분은 어떻습니까? 글쎄, HTTP 요청에 대해 이야기할 때, 그것은 POST 요청으로 알려져 있습니다. 소셜 미디어 플랫폼에 메시지를 게시 하는 것처럼 데이터베이스에 새 기록을 게시 할 수도 있습니다.
CRUD의 업데이트를 사용하면 리소스를 업데이트하기 위해 PUT 또는 PATCH 요청을 사용할 수 있습니다. HTTP의 PUT은 새 레코드를 생성하거나 이전 레코드를 업데이트/교체합니다.
조금 더 자세히 살펴보고 PATCH에 대해 알아보겠습니다.
API는 일반적으로 URL의 특정 경로에 대한 HTTP 요청을 수행하여 작동합니다. 사용자의 책 목록이 포함된 DB와 통신하는 API를 만든다고 가정합니다. 그러면 URL .../books
에서 해당 책을 볼 수 있습니다. .../books에 대한 POST 요청은 .../books
.../books
에서 정의한 모든 속성(ID, 제목, ISBN, 저자, 출판 데이터 등)으로 새 책을 생성합니다. 현재 모든 책을 .../books
에 저장하는 기본 데이터 구조가 무엇인지는 중요하지 않습니다. API가 데이터를 조작하기 위해 해당 엔드포인트(경로를 통해 액세스)를 노출한다는 점만 유의하면 됩니다. 이전 문장이 핵심이었습니다. POST 요청은 ...books/
경로에 새 책을 만듭니다 . PUT과 POST의 차이점은 PUT이 그런 책이 없으면 새 책을 생성하고(POST와 마찬가지로), 책이 앞서 언급한 데이터 구조 내에 이미 존재하는 경우 기존 책을 대체한다는 것 입니다.
각 책에 id, title, ISBN, 저자, hasRead(부울) 속성이 있다고 가정합니다.
그런 다음 앞에서 본 것처럼 새 책을 추가하기 위해 .../books
에 대한 POST 요청을 수행합니다. 책을 완전히 업데이트하거나 교체하려면 .../books/id
에 대한 PUT 요청을 수행합니다. 여기서 id
는 교체하려는 책의 ID입니다.
PUT이 기존 책을 완전히 대체하는 동안 PATCH는 위에서 정의한 hasRead
부울 속성을 수정하여 특정 책과 관련된 것을 업데이트하므로 …/books/id
에 대한 PATCH 요청을 새 데이터와 함께 전송합니다.
지금은 이것의 의미를 파악하기 어려울 수 있습니다. 지금까지는 이론상으로 모든 것을 설정했지만 실제로 HTTP 요청을 만드는 유형의 코드를 본 적이 없기 때문입니다. 그러나 우리는 이 기사에서 GET을 다루고 나머지는 다음 기사에서 추가하여 곧 다루게 될 것입니다.
마지막 기본 CRUD 작업이 하나 있으며 이를 삭제라고 합니다. 예상대로 이러한 HTTP 요청의 이름은 "DELETE"이고 PATCH와 거의 동일하게 작동하므로 경로에 책의 ID를 제공해야 합니다.
우리는 지금까지 라우트가 HTTP 요청을 하는 특정 URL이고 엔드포인트가 API가 제공하는 기능이라는 것을 배웠습니다. 즉, 끝점은 경로의 다른 끝에 있는 프로그래밍 언어 기능이며 지정한 HTTP 요청을 수행합니다. 또한 API에 대한 요청을 실제로 지정하는 POST, GET, PUT, PATCH, DELETE 등과 같은 용어(HTTP 동사라고도 함)가 있다는 것도 배웠습니다. JSON과 마찬가지로 이러한 HTTP 요청 방법은 IETF(Internet Engineering Task Force)에서 정의한 인터넷 표준입니다. 특히 RFC 7231, 섹션 4: 요청 방법 및 RFC 5789, 섹션 2: 패치 방법이 있습니다. 여기서 RFC는 의견 요청.
따라서 전달된 ID를 매개변수라고 하는 URL .../books/id
에 GET 요청을 할 수 있습니다. POST, PUT 또는 PATCH 요청을 .../books
에 만들어 리소스를 생성하거나 .../books/id
에 리소스를 수정/교체/업데이트할 수 있습니다. 또한 특정 책을 삭제하기 위해 .../books/id
에 DELETE 요청을 할 수도 있습니다.
HTTP 요청 방법의 전체 목록은 여기에서 찾을 수 있습니다.
HTTP 요청을 한 후 응답을 받게 된다는 점에 유의하는 것도 중요합니다. 특정 응답은 API 구축 방식에 따라 결정되지만 항상 상태 코드를 수신해야 합니다. 앞서 우리는 웹 브라우저가 웹 서버에서 HTML을 요청할 때 "OK"로 응답할 것이라고 말했습니다. 이는 HTTP 상태 코드, 보다 구체적으로 HTTP 200 OK로 알려져 있습니다. 상태 코드는 끝점에 지정된 작업 또는 작업(모든 작업을 수행하는 우리의 기능임을 기억하십시오)이 완료되는 방법을 지정합니다. HTTP 상태 코드는 서버에서 다시 전송되며 404 Not Found(리소스 또는 파일을 찾을 수 없습니다. 이는 .../books/id
에 GET 요청을 하는 것과 같습니다. 해당 ID가 존재하지 않는 경우 .../books/id
입니다.)
HTTP 상태 코드의 전체 목록은 여기에서 찾을 수 있습니다.
몽고DB
MongoDB는 Firebase 실시간 데이터베이스와 유사한 비관계형 NoSQL 데이터베이스입니다. MongoDB Native Driver 또는 Mongoose와 같은 Node 패키지를 통해 데이터베이스와 통신합니다.
MongoDB에서 데이터는 JSON으로 저장되는데, 이는 MySQL, PostgreSQL, SQLite와 같은 관계형 데이터베이스와 상당히 다릅니다. 둘 다 데이터베이스라고 하며 SQL 테이블은 컬렉션, SQL 테이블 행은 문서, SQL 테이블 열은 필드입니다.
첫 번째 Bookshelf API를 만들 때 이 시리즈의 다음 기사에서 MongoDB 데이터베이스를 사용할 것입니다. 위에 나열된 기본 CRUD 작업은 MongoDB 데이터베이스에서 수행할 수 있습니다.
Atlas 클러스터에서 라이브 데이터베이스를 생성하고 MongoDB 네이티브 드라이버로 CRUD 작업을 수행하는 방법을 배우려면 MongoDB 문서를 읽는 것이 좋습니다. 이 시리즈의 다음 기사에서는 로컬 데이터베이스와 클라우드 프로덕션 데이터베이스를 설정하는 방법을 배웁니다.
명령줄 노드 애플리케이션 빌드
응용 프로그램을 구축할 때 많은 작성자가 기사 시작 부분에서 전체 코드 기반을 버리고 각 줄을 설명하려고 시도하는 것을 볼 수 있습니다. 이 텍스트에서는 다른 접근 방식을 취할 것입니다. 내 코드를 한 줄씩 설명하면서 진행하면서 앱을 빌드하겠습니다. 모듈성이나 성능에 대해 걱정하지 않을 것이며 코드베이스를 별도의 파일로 분할하지 않을 것이며 DRY 원칙을 따르거나 코드를 재사용 가능하게 만들지 않을 것입니다. 그냥 배울 때는 가능한 한 간단하게 만드는 것이 좋습니다. 그래서 여기에서 제가 취할 접근 방식입니다.
우리가 무엇을 만들고 있는지 명확히 합시다. 우리는 사용자 입력에 대해 걱정하지 않을 것이므로 Yargs와 같은 패키지를 사용하지 않을 것입니다. 우리는 또한 우리 자신의 API를 구축하지 않을 것입니다. 이는 Express Web Application Framework를 사용할 때 이 시리즈의 뒷부분에 나올 것입니다. 나는 대부분의 튜토리얼이 그러하기 때문에 Node.js를 Express 및 API의 힘과 병합하지 않기 위해 이 접근 방식을 취합니다. 그보다는 타사 JavaScript 라이브러리를 사용하는 외부 API에서 데이터를 호출하고 수신하는 하나의 방법(많은 것 중)을 제공하겠습니다. 우리가 호출할 API는 날씨 API로, 노드에서 액세스하여 출력을 터미널에 덤프합니다. 아마도 "pretty-printing"으로 알려진 일부 형식이 지정되었을 것입니다. 2019년 1월 현재 올바른 결과를 제공하는 단계에서 API를 설정하고 API 키를 얻는 방법을 포함하여 전체 프로세스를 다룰 것입니다.
이 프로젝트에 OpenWeatherMap API를 사용할 것이므로 시작하려면 OpenWeatherMap 등록 페이지로 이동하여 해당 양식으로 계정을 만드십시오. 로그인하면 대시보드 페이지(여기에 있음)에서 API 키 메뉴 항목을 찾습니다. 방금 계정을 만든 경우 API 키의 이름을 선택하고 "생성"을 눌러야 합니다. 새 API 키가 작동하고 계정과 연결되는 데 최소 2시간이 걸릴 수 있습니다.
애플리케이션 구축을 시작하기 전에 API 문서를 방문하여 API 키 형식을 지정하는 방법을 알아보겠습니다. 이 프로젝트에서는 해당 위치의 날씨 정보를 얻기 위해 우편번호와 국가 코드를 지정합니다.
문서에서 이를 수행하는 방법은 다음 URL을 제공하는 것임을 알 수 있습니다.
api.openweathermap.org/data/2.5/weather?zip={zip code},{country code}
데이터를 입력할 수 있는 항목:
api.openweathermap.org/data/2.5/weather?zip=94040,us
이제 이 API에서 관련 데이터를 실제로 얻으려면 새 API 키를 쿼리 매개변수로 제공해야 합니다.
api.openweathermap.org/data/2.5/weather?zip=94040,us&appid={YOUR_API_KEY}
지금은 해당 URL을 웹 브라우저의 새 탭에 복사하여 {YOUR_API_KEY}
자리 표시자를 이전에 계정에 등록할 때 얻은 API 키로 바꾸십시오.
당신이 볼 수 있는 텍스트는 실제로 JSON(앞서 논의한 바와 같이 웹에서 합의된 언어)입니다.
이를 더 자세히 조사하려면 Chrome에서 Ctrl + Shift + I 을 눌러 Chrome 개발자 도구를 연 다음 네트워크 탭으로 이동합니다. 현재 여기에는 데이터가 없어야 합니다.
실제로 네트워크 데이터를 모니터링하려면 페이지를 새로고침하고 탭에 유용한 정보가 채워지는지 확인하세요. 아래 이미지와 같이 첫 번째 링크를 클릭합니다.
해당 링크를 클릭하면 헤더와 같은 HTTP 관련 정보를 실제로 볼 수 있습니다. 헤더는 API의 응답으로 전송됩니다(경우에 따라 고유한 헤더를 API에 보낼 수도 있고, 고유한 API를 빌드할 때 다시 보낼 사용자 지정 헤더(종종 x-
접두사)를 생성할 수도 있습니다. ), 클라이언트나 서버에 필요할 수 있는 추가 정보만 포함합니다.
이 경우 API에 HTTP GET 요청을 했고 HTTP 상태 200 OK로 응답한 것을 볼 수 있습니다. 또한 "응답 헤더" 섹션에 나열된 것처럼 다시 전송된 데이터가 JSON으로 된 것을 볼 수 있습니다.
미리보기 탭을 누르면 실제로 JSON을 JavaScript 개체로 볼 수 있습니다. 브라우저에서 볼 수 있는 텍스트 버전은 문자열입니다. JSON은 항상 웹을 통해 문자열로 전송 및 수신되기 때문입니다. 이것이 우리가 코드에서 JSON을 구문 분석하여 더 읽기 쉬운 형식으로 가져와야 하는 이유입니다. 이 경우(그리고 거의 모든 경우에) JavaScript 객체입니다.
Google 크롬 확장 프로그램 "JSON View"를 사용하여 자동으로 수행할 수도 있습니다.
애플리케이션 구축을 시작하기 위해 터미널을 열고 새 루트 디렉터리를 만든 다음 cd
를 입력합니다. 안으로 들어가면 새 app.js
파일을 만들고 npm init
를 실행하여 기본 설정으로 package.json
파일을 생성한 다음 Visual Studio Code를 엽니다.
mkdir command-line-weather-app && cd command-line-weather-app touch app.js npm init code .
그런 다음 Axios를 다운로드하고 package.json
파일에 추가되었는지 확인하고 node_modules
폴더가 성공적으로 생성되었는지 확인합니다.
브라우저에서 URL 표시줄에 적절한 URL을 수동으로 입력하여 GET 요청을 직접 만든 것을 볼 수 있습니다. Axios를 사용하면 Node.js 내에서 그렇게 할 수 있습니다.
지금부터 다음 코드는 모두 app.js
파일 내부에 위치하며 각 스니펫은 차례로 배치됩니다.
가장 먼저 할 일은 이전에 설치한 Axios 패키지가 필요합니다.
const axios = require('axios');
이제 Axios에 액세스할 수 있으며 axios
상수를 통해 관련 HTTP 요청을 만들 수 있습니다.
일반적으로 API 호출은 동적입니다. 이 경우 URL에 다른 우편 번호와 국가 코드를 삽입할 수 있습니다. 따라서 URL의 각 부분에 대해 상수 변수를 만든 다음 ES6 템플릿 문자열과 함께 넣습니다. 먼저 API 키와 함께 절대 변경되지 않는 URL 부분이 있습니다.
const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here';
우편번호와 국가 코드도 지정하겠습니다. 우리는 사용자 입력을 기대하지 않고 데이터를 하드 코딩하기 때문에 많은 경우에 let
을 사용하는 것이 더 유용하지만 이 값도 상수로 만들 것입니다.
const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us';
이제 이러한 변수를 Axios를 사용하여 GET 요청을 수행할 수 있는 하나의 URL에 넣어야 합니다.
const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`;
다음은 이 시점까지의 app.js
파일 내용입니다.
const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`;
남은 일은 실제로 axios
를 사용하여 해당 URL에 대한 GET 요청을 만드는 것입니다. 이를 위해 axios
에서 제공하는 get(url)
메서드를 사용합니다.
axios.get(ENTIRE_API_URL)
axios.get(...)
은 실제로 약속을 반환하고 성공 콜백 함수는 응답 인수를 받아 API의 응답에 액세스할 수 있도록 합니다. 브라우저에서 본 것과 동일합니다. 또한 오류를 포착하기 위해 .catch()
절을 추가합니다.
axios.get(ENTIRE_API_URL) .then(response => console.log(response)) .catch(error => console.log('Error', error));
이제 터미널에서 node app.js
를 사용하여 이 코드를 실행하면 반환되는 전체 응답을 볼 수 있습니다. 그러나 해당 우편번호의 온도만 보고 싶다고 가정하면 응답에 있는 대부분의 데이터는 유용하지 않습니다. Axios는 실제로 응답의 속성인 데이터 개체의 API에서 응답을 반환합니다. 즉, 서버의 응답은 실제로 response.data
에 있으므로 대신 콜백 함수인 console.log(response.data)
에 인쇄해 보겠습니다.
이제 우리는 웹 서버가 항상 JSON을 문자열로 처리한다고 말했고 그것은 사실입니다. 그러나 response.data
는 이미 객체( console.log(typeof response.data)
를 실행하면 알 수 있음)입니다. JSON.parse()
로 구문 분석할 필요가 없습니다. Axios가 이미 뒤에서 이를 처리하고 있기 때문입니다.
console.log(response.data)
실행의 터미널 출력은 console.log(JSON.stringify(response.data, undefined, 2))
를 실행하여 "예쁜 인쇄" 형식으로 지정할 수 있습니다. JSON.stringify()
는 JSON 개체를 문자열로 변환하고 개체, 필터 및 인쇄할 때 들여쓰기할 문자 수를 가져옵니다. 이것이 제공하는 응답을 볼 수 있습니다.
{ "coord": { "lon": -118.24, "lat": 33.97 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "base": "stations", "main": { "temp": 288.21, "pressure": 1022, "humidity": 15, "temp_min": 286.15, "temp_max": 289.75 }, "visibility": 16093, "wind": { "speed": 2.1, "deg": 110 }, "clouds": { "all": 1 }, "dt": 1546459080, "sys": { "type": 1, "id": 4361, "message": 0.0072, "country": "US", "sunrise": 1546441120, "sunset": 1546476978 }, "id": 420003677, "name": "Lynwood", "cod": 200 }
이제 우리가 찾고 있는 온도가 response.data
객체의 main
속성에 있음을 알 수 있으므로 response.data.main.temp
를 호출하여 액세스할 수 있습니다. 지금까지 응용 프로그램의 코드를 살펴보겠습니다.
const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`; axios.get(ENTIRE_API_URL) .then(response => console.log(response.data.main.temp)) .catch(error => console.log('Error', error));
우리가 얻는 온도는 실제로 켈빈 단위이며, 이는 모든 내부의 모든 열 운동이 일어나는 온도인 "절대 0"점을 제공한다는 사실 때문에 물리학, 화학 및 열역학에서 일반적으로 사용되는 온도 척도입니다. 입자가 멈춥니다. 아래 공식을 사용하여 이것을 화씨 또는 섭씨로 변환하기만 하면 됩니다.
F = K * 9/5 - 459.67
C = K - 273.15
이 변환으로 새 데이터를 인쇄하도록 성공 콜백을 업데이트해 보겠습니다. 또한 사용자 경험을 위해 적절한 문장을 추가할 것입니다.
axios.get(ENTIRE_API_URL) .then(response => { // Getting the current temperature and the city from the response object. const kelvinTemperature = response.data.main.temp; const cityName = response.data.name; const countryName = response.data.sys.country; // Making K to F and K to C conversions. const fahrenheitTemperature = (kelvinTemperature * 9/5) — 459.67; const celciusTemperature = kelvinTemperature — 273.15; // Building the final message. const message = ( `Right now, in \ ${cityName}, ${countryName} the current temperature is \ ${fahrenheitTemperature.toFixed(2)} deg F or \ ${celciusTemperature.toFixed(2)} deg C.`.replace(/\s+/g, ' ') ); console.log(message); }) .catch(error => console.log('Error', error));
message
변수 주위의 괄호는 필요하지 않습니다. 그냥 보기에 좋습니다. React에서 JSX로 작업할 때와 비슷합니다. 백슬래시는 템플릿 문자열이 새 줄의 형식을 지정하는 것을 중지하고 replace()
문자열 프로토타입 메서드는 정규식(RegEx)을 사용하여 공백을 제거합니다. toFixed()
Number 프로토타입 메서드는 부동 소수점을 특정 소수점 이하 자릿수(이 경우 2)로 반올림합니다.
이를 통해 최종 app.js
는 다음과 같이 보입니다.
const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`; axios.get(ENTIRE_API_URL) .then(response => { // Getting the current temperature and the city from the response object. const kelvinTemperature = response.data.main.temp; const cityName = response.data.name; const countryName = response.data.sys.country; // Making K to F and K to C conversions. const fahrenheitTemperature = (kelvinTemperature * 9/5) — 459.67; const celciusTemperature = kelvinTemperature — 273.15; // Building the final message. const message = ( `Right now, in \ ${cityName}, ${countryName} the current temperature is \ ${fahrenheitTemperature.toFixed(2)} deg F or \ ${celciusTemperature.toFixed(2)} deg C.`.replace(/\s+/g, ' ') ); console.log(message); }) .catch(error => console.log('Error', error));
결론
동기 및 비동기 요청의 차이점부터 콜백 함수, 새로운 ES6 기능, 이벤트, 패키지 관리자, API, JSON, HyperText Transfer Protocol, 비관계형 데이터베이스에 이르기까지 이 기사에서 Node가 작동하는 방식에 대해 많은 것을 배웠습니다. , 그리고 우리는 새로 발견한 대부분의 지식을 활용하여 자체 명령줄 응용 프로그램을 구축했습니다.
이 시리즈의 향후 기사에서는 호출 스택, 이벤트 루프 및 노드 API에 대해 자세히 살펴보고 CORS(Cross-Origin Resource Sharing)에 대해 이야기하고 전체 데이터베이스, 엔드포인트, 사용자 인증, 토큰, 서버 측 템플릿 렌더링 등을 활용하는 Stack Bookshelf API
여기에서 자신의 Node 애플리케이션 구축을 시작하고 Node 설명서를 읽고 흥미로운 API 또는 Node Module을 찾아 직접 구현하십시오. 세상은 당신의 굴이고 당신은 지구상에서 가장 큰 지식 네트워크인 인터넷에 손끝에서 접근할 수 있습니다. 유용하게 사용하세요.
SmashingMag에 대한 추가 정보:
- REST API 이해 및 사용
- Regex 작성 방식을 바꿀 새로운 JavaScript 기능
- Node.js를 빠르게 유지하기: 고성능 Node.js 서버를 만들기 위한 도구, 기술 및 팁
- Web Speech API와 Node.js로 간단한 AI 챗봇 만들기