Node.js

Node.js 사전복습

데이터_박과장 2023. 11. 26. 18:40

Node.js에서의 이벤트 기반 모델은 매우 중요한 개념입니다. 이 모델은 비동기 방식으로 작동하며, 특정 이벤트가 발생할 때 실행될 콜백 함수를 미리 등록하는 방식으로 작동합니다. 이를 통해 Node.js는 효율적인 비동기 I/O 처리를 할 수 있습니다.

 

  • 이벤트 기반(Event-Driven) 모델: Node.js는 이벤트가 발생할 때마다 미리 지정된 작업(콜백 함수)을 수행합니다. 이러한 방식은 서버가 여러 클라이언트의 요청을 효율적으로 처리할 수 있도록 도와줍니다.
  • 이벤트 리스너/콜백 함수: 특정 이벤트에 반응하여 실행될 함수입니다. 예를 들어, HTTP 서버가 클라이언트의 요청을 받았을 때 특정 작업을 수행하는 함수를 등록할 수 있습니다.
  • 호출 스택(Call Stack): JavaScript에서 함수 호출은 호출 스택에 기록됩니다. 함수가 호출되면 스택에 추가(push)되고, 작업이 완료되면 스택에서 제거(pop)됩니다. Node.js는 이 호출 스택을 사용하여 실행 중인 코드를 관리합니다.
  • 백그라운드(Background): Node.js는 I/O 작업 같은 시간이 오래 걸리는 작업을 백그라운드에서 처리합니다. 예를 들어, 파일 읽기/쓰기, 네트워크 요청 등이 이에 해당합니다.
  • 태스크 큐(Task Queue): 이벤트 루프(Event Loop)는 호출 스택이 비어 있을 때 태스크 큐에 있는 작업을 호출 스택으로 옮겨 실행합니다. 태스크 큐는 비동기 작업의 결과(예: I/O 작업 완료, 타이머 만료)와 관련된 콜백 함수를 순서대로 저장합니다.

 

 


아래는 Node.js에서 이벤트 리스너를 사용하는 간단한 예시입니다. 여기서는 HTTP 서버를 생성하고, 서버가 클라이언트의 요청을 받을 때마다 특정 작업(여기서는 "Hello, World!" 메시지를 보내는 것)을 수행합니다.

 

const http = require('http');

// HTTP 서버 생성
const server = http.createServer((req, res) => {
    // 클라이언트 요청에 대한 처리
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello, World!\n');
});

// 서버가 3000번 포트에서 듣도록 설정
server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

 

이 코드에서 createServer 메소드는 HTTP 서버를 생성하며, 이 서버에 대한 요청이 들어올 때마다 실행될 콜백 함수를 정의합니다. listen 메소드는 서버가 특정 포트에서 클라이언트의 요청을 기다리도록 설정합니다.

이 예시는 Node.js의 이벤트 기반 모델과 비동기 처리 방식을 잘 보여줍니다. 클라이언트의 요청은 이벤트로 간주되며, 이에 대한 응답으로 콜백 함수가 실행됩니다.

 

 

위 코드에 대한 추가설명 

 

http.createServer(callback):
http.createServer 메소드는 HTTP 서버를 생성합니다.
callback 함수는 서버가 클라이언트로부터 요청을 받을 때마다 호출됩니다. 이 함수는 요청(req) 객체와 응답(res) 객체 두 개의 매개변수를 가집니다.
req 객체는 클라이언트의 요청에 대한 정보를 담고 있으며, res 객체를 사용하여 클라이언트에 응답을 보냅니다.

 

 

res.writeHead(statusCode, [statusMessage], [headers]):
res.writeHead 메소드는 응답의 헤더를 작성합니다.
statusCode는 HTTP 상태 코드를 나타내며, 예를 들어 200은 요청이 성공적으로 처리되었음을 의미합니다.
statusMessage는 상태 코드의 텍스트 설명입니다. 이는 선택적 매개변수이며 생략 가능합니다.
headers는 응답에 포함될 HTTP 헤더를 객체 형태로 지정합니다. 예를 들어, {'Content-Type': 'text/plain'}은 응답 본문이 일반 텍스트임을 나타냅니다.

 

 

res.end([data], [encoding], [callback]):
res.end 메소드는 서버 응답을 완료합니다.
data는 클라이언트에게 보낼 응답 본문입니다. 이 예제에서는 'Hello, World!\n'라는 문자열을 보냅니다.
encoding은 data의 인코딩 방식을 지정합니다. 이는 선택적 매개변수이며, 일반적으로 생략됩니다.
callback은 응답이 전송되고 종료된 후 호출될 함수입니다. 이 역시 선택적 매개변수입니다.

 

 

server.listen(port, [hostname], [backlog], [callback]):
server.listen 메소드는 서버가 클라이언트의 요청을 듣기 위해 특정 포트(이 예제에서는 3000)에서 대기하도록 설정합니다.
hostname은 서버가 대기할 호스트 이름입니다. 이는 선택적 매개변수이며, 생략할 경우 모든 인터페이스에서 서버가 수신됩니다.
backlog는 동시에 대기할 수 있는 최대 연결 수를 지정합니다. 이것도 선택적 매개변수입니다.
callback 함수는 서버가 시작되고 대기 상태가 되면 호출됩니다. 이 예제에서는 서버가 시작되면 콘솔에 메시지를 출력합니다.

 

 

리터럴이란?

자바스크립트에서 "리터럴(literal)"이란, 소스 코드 내에서 고정된 값을 직접 표현하는 표기법을 말합니다. 리터럴은 변수나 상수에 할당되거나, 함수의 매개변수로 전달되는 등의 방식으로 사용됩니다. 주요 리터럴 유형에는 다음과 같은 것들이 있습니다:

숫자 리터럴: 숫자 값을 직접 나타냅니다. 예를 들어, 10, 3.14, -5 등이 숫자 리터럴입니다.

문자열 리터럴: 텍스트를 표현합니다. 작은따옴표(' '), 큰따옴표(" "), 또는 백틱( )으로 둘러싸인 문자들의 집합입니다. 예: '안녕하세요', "Hello, world!", `Hello ${name}` (템플릿 리터럴).

불리언 리터럴: true 또는 false 값입니다. 조건문 또는 논리적 표현에서 많이 사용됩니다.

객체 리터럴: 중괄호 {}를 사용하여 객체를 직접 선언합니다. 예: { name: 'Alice', age: 25 }. 이는 키-값 쌍으로 구성되며, 객체를 생성하는 간편한 방법입니다.

배열 리터럴: 대괄호 []를 사용하여 배열을 선언합니다. 예: [1, 2, 3], ['apple', 'banana', 'cherry']. 배열 리터럴은 여러 값을 순차적으로 나열할 때 사용됩니다.

함수 리터럴: 함수 표현식을 통해 정의된 함수입니다. 예: function() { return true; }. 이는 익명 함수를 선언하고 변수에 할당할 수 있게 해줍니다.

정규 표현식 리터럴: /pattern/flags 형태로 정규 표현식을 나타냅니다. 예: /[a-z]/gi.

 


구조 분해 할당(Destructuring Assignment)

자바스크립트의 구조 분해 할당(Destructuring Assignment)은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 쉽게 할당할 수 있게 해주는 표현식입니다. 이 기능은 코드를 더 깔끔하고 읽기 쉽게 만들어줍니다.

객체 구조 분해


객체 구조 분해는 객체의 속성을 변수로 추출할 수 있게 해줍니다. 예를 들어, 객체 person에 name과 age 속성이 있다면, 이 두 속성을 두 개의 변수로 쉽게 할당할 수 있습니다.

const person = {
    name: 'Alice',
    age: 25
};

// 구조 분해를 사용하여 객체의 속성을 변수로 할당
const { name, age } = person;

console.log(name); // 출력: Alice
console.log(age);  // 출력: 25

 



배열 구조 분해
배열 구조 분해는 배열의 각 요소를 변수로 추출하는 데 사용됩니다. 배열의 순서대로 변수에 할당됩니다.

const numbers = [1, 2, 3];

// 구조 분해를 사용하여 배열의 요소를 변수로 할당
const [first, second, third] = numbers;

console.log(first);  // 출력: 1
console.log(second); // 출력: 2
console.log(third);  // 출력: 3



기본값과 나머지 패턴
구조 분해 할당에서는 기본값을 설정할 수 있으며, '나머지(rest)' 패턴을 사용하여 여러 요소를 한 변수에 그룹화할 수도 있습니다.

const [a, b, c = 3] = [1, 2];
console.log(a, b, c); // 출력: 1 2 3

const [x, ...y] = [1, 2, 3, 4];
console.log(x); // 출력: 1
console.log(y); // 출력: [2, 3, 4]



중첩 구조 분해
객체나 배열이 중첩된 경우에도 구조 분해 할당을 사용할 수 있습니다.

const options = {
    size: {
        width: 100,
        height: 200
    },
    items: ["Cake", "Donut"]
};

let {
    size: {
        width,
        height
    },
    items: [item1, item2],
    title = "Menu" // 기본값 설정
} = options;

console.log(title);  // 출력: Menu
console.log(width);  // 출력: 100
console.log(height); // 출력: 200
console.log(item1);  // 출력: Cake
console.log(item2);  // 출력: Donut



구조 분해 할당은 코드를 간결하게 만들고, 변수의 선언 및 할당을 더 직관적으로 표현할 수 있게 해줍니다. 이는 특히 함수의 매개변수 처리에 매우 유용합니다.

 

프로미스(Promise)

자바스크립트의 프로미스(Promise)는 비동기 작업의 최종 완료(또는 실패)와 그 결과값을 나타내는 객체입니다. 프로미스는 비동기적인 작업을 더 쉽고 관리하기 좋은 방법으로 처리할 수 있게 도와줍니다.

프로미스는 세 가지 상태를 가집니다:

  • 대기(Pending): 비동기 처리 로직이 아직 완료되지 않은 상태
  • 이행(Fulfilled): 비동기 처리가 성공적으로 완료되어 프로미스가 결과값을 반환한 상태
  • 거부(Rejected): 비동기 처리가 실패하거나 오류가 발생한 상태


프로미스의 기본 사용법


프로미스는 new Promise() 생성자를 통해 생성됩니다. 이 생성자는 비동기 작업을 수행할 함수를 인자로 받으며, 이 함수는 다시 두 개의 인자, resolve와 reject를 받습니다. resolve 함수는 비동기 작업이 성공했을 때 호출되며, reject는 비동기 작업이 실패했을 때 호출됩니다.

const myPromise = new Promise((resolve, reject) => {
    // 비동기 작업을 수행하는 코드
    const condition = true; // 예시 조건

    if (condition) {
        resolve('성공'); // 성공 시 resolve 호출
    } else {
        reject('실패'); // 실패 시 reject 호출
    }
});

myPromise
    .then((message) => {
        console.log(message); // 성공(이행) 경우 실행
    })
    .catch((error) => {
        console.error(error); // 실패(거부) 경우 실행
    });



프로미스 체이닝
프로미스는 .then(), .catch(), .finally() 메서드를 통해 체이닝될 수 있습니다. 이를 통해 여러 비동기 작업을 순차적으로 처리하거나, 예외 처리를 할 수 있습니다.

function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve("some data"), 1000); // 1초 후에 "some data" 반환
    });
}

getData()
    .then((data) => {
        console.log(data);  // "some data" 출력
        return 'more data';
    })
    .then((moreData) => {
        console.log(moreData);  // "more data" 출력
    })
    .catch((error) => {
        console.error(error);
    })
    .finally(() => {
        console.log('작업 완료');
    });



프로미스를 사용하면 콜백 지옥(callback hell)을 피하고, 코드를 보다 명확하고 관리하기 쉽게 만들 수 있습니다. 또한, Promise.all과 같은 메서드를 사용하여 여러 비동기 작업을 병렬로 처리하는 것도 가능합니다.

 

this

자바스크립트에서 this 키워드는 현재 실행 컨텍스트에 대한 참조를 제공합니다. 즉, this는 그것이 사용된 위치에 따라 다른 값을 가집니다. this의 값은 함수를 어떻게 호출하느냐에 따라 결정되며, 이는 종종 초보 개발자들에게 혼란을 줄 수 있는 부분입니다.

this의 사용 예시
전역 컨텍스트: 전역 실행 컨텍스트에서 this는 전역 객체를 참조합니다. 브라우저에서는 window 객체가 됩니다.

console.log(this === window); // 브라우저에서 true를 반환



함수 컨텍스트: 일반 함수에서 this의 값은 함수를 호출하는 방식에 따라 달라집니다.

일반 함수 호출: this는 전역 객체를 참조합니다.

function myFunction() {
  return this;
}

console.log(myFunction() === window); // 브라우저에서 true를 반환



메서드 호출: 메서드를 호출한 객체를 참조합니다.

const myObject = {
  method() {
    return this;
  }
};

console.log(myObject.method() === myObject); // true를 반환



생성자 함수 컨텍스트: 생성자 함수에서 this는 새로 생성되는 객체를 참조합니다.

function Person(name) {
  this.name = name;
}

const alice = new Person('Alice');
console.log(alice.name); // "Alice"



화살표 함수: 화살표 함수에서 this는 화살표 함수를 둘러싼 외부 함수의 this 값을 가집니다. 즉, 화살표 함수는 자신만의 this를 가지지 않습니다.

const myObject = {
  method() {
    console.log(this); // myObject를 참조
    setTimeout(() => {
      console.log(this); // 여전히 myObject를 참조
    }, 1000);
  }
};

myObject.method();



명시적 this 바인딩: Function.prototype의 call, apply, bind 메소드를 사용하여 함수의 this 값을 명시적으로 설정할 수 있습니다.

function greet() {
  console.log(`Hello, ${this.name}`);
}

const person = { name: 'Alice' };
greet.call(person); // "Hello, Alice" 출력



this는 자바스크립트에서 매우 유연하지만, 때로는 예측하기 어렵게 만들 수 있는 개념입니다. 그래서 이를 이해하는 것은 중요하며, 다양한 상황에서 this가 어떻게 동작하는지 이해하는 것이 필수적입니다.

 

 

'Node.js' 카테고리의 다른 글

Node.js 시작  (0) 2023.11.26