상세 컨텐츠

본문 제목

[Node.js 1팀] 3장. 노드 기능, 4장. http 모듈로 서버 만들기

25-26/Node.js 1

by a-rom 2025. 10. 31. 10:00

본문

728x90

 


 

3장 노드 기능

 

3-1. REPL 사용하기

- 자바 스크리브는 스크립트 언어이므로 미리 컴파일을 하지 않아도 즉석 코드 실행이 가능하다.

입력한 코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복한다(Loop)라고 해서 REPL(Read Eval Print Loop)라고 불린다.

 

 

3.3 모듈로 만들기

 

모듈: 특정한 기능을 하는 함수나 변수들의 집합

모듈을 만들어놓으면 여러 프로그램에 해당 모듈을 재사용할 수 있다.

이는, 자바스크립트에서 코드를 재사용하기 위해 함수로 만드는 것과 비슷하다. 

 

 

예시: 한 명의 사람에 대한 정보)

 

한 사람의 이름과 나이를 관리한다고 가정한다.

person.js 파일은 사람의 이름과 나이를 정의하고 내보내는 모듈이다. 마치 "이 파일에는 이런 정보가 들어있어요!"라고 말해주는 것과 같다.

// person.js

// 'name'이라는 변수를 다른 파일에서 사용할 수 있게 내보냄
export const name = '김민준';

// 'age'라는 변수를 다른 파일에서 사용할 수 있게 내보냄
export const age = 30;

 

main.js 파일은 person.js 모듈에서 필요한 정보를 가져와서 사용한다. 마치 "아까 그 파일에 있던 '이름'과 '나이'를 여기서 쓸게요!"라고 말하는 것과 같다.

// main.js

// 'person.js' 모듈에서 'name'과 'age'를 가져옴
import { name, age } from './person.js';

// 가져온 변수들을 사용함
console.log(`이름: ${name}`); // 출력: 이름: 김민준
console.log(`나이: ${age}`); // 출력: 나이: 30

 

여러 파일에 걸쳐 재사용 되는 함수나 변수를 모듈로 만들어두면 편리하다.

그러나 모듈이 많아지고 모듈 간의 관계가 얽히게 되면 구조를 파악하기 어렵다는 단점이 있다.

 

 

3-4. 노드 내장 객체 알아보기

 

3.4.1 global

브라우저의 window와 같은 전역 객체이다. 자바스크립트의 모든 요소가 연결되는 '가장 큰 공간' 이다.

*전역 객체이므로 모든 파일에서 접근 가능한 최상위 객체

 

 

예시: 아래의 코드는 같은 의미이다.)

 

// window 객체의 예

// 전역 변수 'message'를 선언했음
var message = '안녕하세요!';

// 이 변수는 사실 window 객체에 속하게 됨
// 따라서 window.message 로 접근해도 같은 값을 얻음
console.log(window.message); // 출력: 안녕하세요!

 

// Node.js에서 전역 변수 'user'를 선언함
var user = '관리자';

// 이 변수는 global 객체에 속하게 됨
// 따라서 global.user 로 접근해도 같은 값을 얻음
console.log(global.user); // 출력: 관리자

 

global은 이렇게 객체의 속성에 값을 대입해 파일 간의 데이터를 공유할 수 있다.

하지만 이를 남용할시 프로그램의 규모가 커져 어떤 파일에서 global 객체에 값을 대입했는지 찾기 어려워져 유지 보수에 어려움을 겪을 수 있다.

 

*추천: var 대신 const와 let을 사용하고, 모듈(Module)을 활용하여 전역 공간을 깨끗하게 유지하는 것을 권장

 

 

3.4.2 console

 

주로 디버깅을 위해 사용한다. 개발을 하면서 변수에 값이 제대로 들어 있는지 확인하기 위해 사용하고, 에러 발생 시 에러 내용을 콘솔에 표현하기 위해 사용하며, 코드 실행 시간을 알아보려고 할 때도 사용한다. 대표적으로 console.log 메소드가 있다.

 

const string = 'abc';
const number = 1;
const boolean = true;
const obj = {
    outside: {
        inside: {
            key: 'value'
        },
    },
};
console.time('전체 시간');
console.log('쉼표로 구분해 여러 값을 찍을 수 있습니다.');
console.log(string, number, boolean);
console.error('에러 메시지');

console.table([{
    name: '제로',
    birth: 1994
}, {
    name: 'hero',
    birth: 1998
}]);

console.dir(obj, {
    colors: false,
    depth: 2,
});
console.dir(obj, {
    colors: true,
    depth: 1,
});

console.time('시간 측정');
for (let i = 0; i < 100000; i++) { }
console.timeEnd('시간 측정');

function b() {
    console.trace('에러 위치 추적');
}
function a() {
    b();
}
a();

console.timeEnd('전체 시간');

 

  • console.time(레이블) / console.timeEnd(레이블) : 서로 대응되며, 같은 레이블을 가진 time과 timeEnd 사이 시간 측정
  • console.log(내용) : 로그들을 콘솔에 표시
  • console.error(에러 내용) : 에러를 콘솔에 표시
  • console.table(배열) : 배열 요소로 객체 리터럴을 넣으면 객체 속성들이 테이블 형식으로 표현
  • console.dir(객체, 옵션) : 객체를 콘솔에 표시할 때 사용
    • 첫 번째 인수 : 표시할 객체
    • 두 번째 인수 : 옵션
      • colors : 경우 true로 할 경우 콘솔에 색이 추가
      • depth : 객체 안의 객체를 몇 단계까지 보여줄 지 결정(기본값 : 2)
console.dir(user, { colors: true });
console.dir(user, { depth: 2 });
  • console.trace(레이블) : 에러가 어디서 발생했는지 추적

 

 

3.4.3 타이머

 

타이머 기능을 제공하는 함수인 setTimeout, setInterval, setImmediate는 노드에서 window 대신 global 객체 안에 들어있다.

 

 

밑의 세 함수는 웹 브라우저에서도 자주 사용되는익숙한 타이머 함수들이다.

  • setTimeout(콜백 함수, 밀리초) : 주어진 밀리초 이후에 콜백 함수 실행
  • setInterval(콜백 함수, 밀리초) : 주어진 밀리초마다 콜백 함수 반복 실행
  • setImmediate(콜백 함수) : 콜백 함수 즉시 실행

 

이 타이머 함수들은 모두 아이디를 반환한다, 아이디를 사용하여 타이머를 취소할 수 있다.

* 아이디(ID)는 타이머 함수(setTimeout, setInterval, setImmediate)가 반환하는 고유한 식별자이다.

  • clearTimeout(아이디) : setTimeout을 취소
  • clearInterval(아이디) : setInterval을 취소
  • clearImmediate(아이디) : setImmediate를 취소
const timeout = setTimeout(() => {
    console.log('1.5초 후 실행');
}, 1500);

const interval = setInterval(() => {
    console.log('1초마다 실행');
}, 1000);

const timeout2 = setTimeout(() => {
    console.log('실행되지 않습니다.');
}, 3000);

setTimeout(() => {
    clearTimeout(timeout2);
    clearInterval(interval);
}, 2500);

const immediate = setImmediate(() => {
    console.log('즉시 실행');
});

const immediate2 = setImmediate(() => {
    console.log('실행되지 않습니다.');
});

clearImmediate(immediate2);

 

# 실행 결과

즉시 실행
1초마다 실행
1.5초 후 실행
1초마다 실행

 

 

3.4.4 __filename, __dirname

 

노드에서는 파일 사이에 모둘 관계가 있는 경우가 많으므로 때로는 현재 파일의 경로나 파일명을 알아야 한다. 노드는 __filename, __dirname이라는 키워드로 경로에 대한 정보를 제공한다. 파일에 __filename과 __dirname을 넣어두면 실행 시 현재 파일명과 현재 파일 경로로 바뀐다.

 

# filename.js

console.log(__filename);
console.log(__dirname);
# 콘솔

c:\Users\사용자이름\filename.js
c:\Users\사용자이름

 

 

3.4.5 module, exports, require

 

this 특징

  • 최상위 스코프에 존재하는 this는 module.exports를 가리킴

*최상위 스코프 ex: global 스코프

  • 함수 선언문 내부 this는 global 객체를 가리킴
console.log(this);
console.log(this === module.exports);
console.log(this === exports);

function whatIsThis() {
    console.log('function', this === exports, this === global);
}

whatIsThis();
# 콘솔
# $ node idnex

{}
true
true
function false true

 

  • require: Node.js에서 모듈을 가져오는 함수
    • cache
      • 캐시 정보 확인: require.cache 객체를 통해 어떤 모듈들이 캐시되었는지 확인한다. *캐시되었다: 데이터를 임시 저장
        각 캐시된 모듈은 파일 경로를 키(key)로 가진다.
      • 재사용성: 같은 파일을 여러 번 require하더라도, 실제로는 캐시에 저장된 파일을 가져와 사용한다.
        이로 인해 불필요한 파일 읽기 작업을 줄일 수 있다.
      • 캐시 삭제: 만약 require를 통해 파일을 다시 불러와야 하는 경우(예: 개발 중 파일 내용이 변경되었을 때),
        delete require.cache[파일경로] 코드를 사용하여 캐시를 수동으로 제거해야 한다.
    • main: 현재 Node.js 프로세스를 실행시킨 첫 번째 모듈을 나타낸다.
      즉, 명령어로 실행한 .js 파일이 바로 require.main이 된다.
console.log("require가 가장 위에 올 필요가 없습니다.");
module.expports = "저를 찾아보세요.";

require('./var');

console.log('require.cache입니다.');
console.log(require.cache); // 파일명에 대한 정보 출력
console.log('require.main입니다.');
console.log(require.main === module); // 현재 파일이 첫 모듈인지 확인
console.log(require.main.filename); // 첫 모듈 파일명 확인

 

 

 

 

3.4.6 process

 

Process: 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고있다.

  • process.env: 서비스의 중요한 키를 저장하는 공간으로 사용된다.
  • process.nextTick(콜백): 이벤트 루프가 다른 콜백 함수들보다 nextTick의 콜백 함수를 우선으로 처리하게 만든다.
  • porcess.exit(코드): 실행 중인 노드 프로세스를 종료한다. 서버 환경에서 이 함수를 사용하면 서버가 멈추므로 특수한 경우를 제외하고는 서버에서 잘 사용하지 않는다. 하지만 서버 외의 독립적인 프로그램에서는 수동으로 노드를 멈추기 위해 사용한다.

 

 

3.5.1 os

 

웹 브라우저에 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다. 

 

  • os.arch() : process.arch와 동일, 프로세서 아키텍처 정보 (arm, ia32 등의 값)
  • os.platform() : process.platform과 동일, 운영체제 플랫폼, linux나 darwin, freebsd 등의 값
  • os.type() : 운영체제의 종류
  • os.uptime() : 운영체제 부팅 이후 흐른 시간(초), 노드의 실행 시간
  • os.hostname() : 컴퓨터의 이름
  • os.release() : 운영체제 버전

경로

  • os.homedir() : 홈 디렉터리 경로
  • os.tmpdir() : 임시 파일 저장 경로

 

  • CPU 정보
  • os.cpus() : 컴퓨터의 코어 정보
  • os.freemem() : 사용 가능한 메모리(RAM)
  • os.totalmem() : 전체 메모리 용량

3.5.2 path

 

 

path 모듈은 Node.js에서 파일과 폴더의 경로를 다루는 데 사용된다. 운영체제에 상관없이 경로를 안전하게 조작할 수 있도록 도와주는 역할을 한다.

 

경로 정보 확인

  • path.sep : 경로를 나누는 문자를 알려준다. 윈도우는 \를, 리눅스나 macOS는 /를 사용한다.
  • path.delimiter : 환경 변수들을 구분하는 문자를 알려준다.
  • path.dirname(경로) : 주어진 경로에서 폴더 이름만 가져온다.
  • path.extname(경로) : 파일의 확장자만 가져온다. (예: .js, .txt)
  • path.basename(경로, 확장자) : 파일 이름을 가져온다. 확장자를 함께 넣으면 파일 이름만 가져올 수 있다.
  • path.isAbsolute(경로) : 경로가 절대 경로인지 아닌지 true나 false로 알려준다.

 

경로 조작 및 변환

  • path.parse(경로) : 경로를 root, dir, base, ext, name으로 나누어 객체 형태로 보여준다.
  • path.format(객체) : path.parse()와 반대로, 나눠진 객체를 다시 하나의 경로로 합쳐준다.
  • path.normalize(경로) : 경로에 /나 \가 여러 번 반복되거나 섞여 있을 때 정상적인 경로로 고쳐준다.
  • path.relative(기준경로, 비교경로) : 두 경로를 넣으면, 첫 번째 경로에서 두 번째 경로로 어떻게 이동해야 하는지 알려준다.

경로 합치기

  • path.join(...경로들) : 여러 경로를 하나로 합친다. 상대 경로(., ..)도 올바르게 처리해 준다.
    • 예시: path.join('/a', 'b', 'c') → /a/b/c
  • path.resolve(...경로들) : path.join과 비슷하지만, 절대 경로로 합친다.
    • path.resolve는 path.join과 달리, 인자 중 /로 시작하는 경로가 있다면 그 앞의 모든 경로를 무시한다.
    • 예시: path.resolve('/a', '/b', 'c') → /b/c (/a가 무시됨)
      • path.join은 단순히 경로들을 합치는 반면, path.resolve는 마치 터미널에서 cd /b를 실행한 것처럼 작동한다.

 

3-6. 파일 시스템 접근하기

 

fs 모듈은 Node.js에서 파일을 읽고, 쓰고, 관리하는 데 사용되는 모듈이다. 이 모듈의 함수들은 대부분 비동기적으로 동작하기 때문에, 결과를 처리하기 위해 콜백이나 프로미스를 사용해야 한다.

 

 * 콜백(callback): 다른 코드의 실행이 끝난 후 다시 호출될 함수

 

1. 파일 읽기: fs.readFile()

파일의 내용을 읽어오는 함수다. 파일을 읽고 나면 결과는 Buffer라는 형식으로 반환된다. toString()을 사용하면 우리가 읽을 수 있는 문자열로 변환할 수 있다.

 

콜백 형식 (기본)

const fs = require('fs');

// readFile 함수: 콜백 함수로 결과를 처리함
fs.readFile('./readme.txt', (err, data) => {
    if (err) {
        // 에러 발생 시 여기서 처리하는 것임.
        throw err;
    }
    console.log(data);         // 버퍼(Buffer) 형태로 출력되는 것임.
    console.log(data.toString()); // 버퍼를 문자열로 변환해 출력하는 것임.
});

 

프로미스 형식 (최신)

const fs = require('fs').promises;

// readFile 함수: 프로미스를 반환하여 then()과 catch()로 결과를 처리함
fs.readFile('./readme.txt')
    .then((data) => {
        console.log(data.toString());
    })
    .catch((err) => {
        // 에러가 발생하면 여기서 처리함.
        console.error(err);
    });

 

 

2. 파일 쓰기: fs.writeFile()

지정한 경로에 파일 내용을 쓰는 함수다. 파일이 이미 존재하면 덮어쓰고, 없으면 새로 생성한다.

 

프로미스 형식

const fs = require('fs').promises;

// 1. writeme.txt 파일에 글을 작성함.
fs.writeFile('./writeme.txt', "파일에 글을 작성했음.")
    .then(() => {
        // 2. 파일 쓰기가 완료되면, 바로 이어서 파일을 읽는 것임.
        console.log('파일 쓰기 완료!');
        return fs.readFile('./writeme.txt');
    })
    .then((data) => {
        // 3. 읽은 파일 내용을 출력하는 것임.
        console.log(data.toString());
    })
    .catch((err) => {
        // 에러가 발생하면 여기서 처리함.
        console.error(err);
    });

 

 

3.6.1 동기 메서드와 비동기 메서드

 

동기 vs. 비동기: 파일 시스템 접근

파일 시스템에 접근하는 fs 모듈은 작업을 처리하는 방식에 따라 동기와 비동기 메서드로 나뉜다. 이 둘은 순서를 기다리는 방식에서 큰 차이가 있다.

 

1. 비동기 (논블로킹 방식) 

비동기 메서드는 작업이 끝날 때까지 기다리지 않고, 다음 코드를 바로 실행한다. 파일을 읽는 동안에도 다른 작업을 처리할 수 있어 효율적이다. 마치 여러 주문을 동시에 받고, 주문이 완성되는 대로 손님을 부르는 식당과 같다. 하지만 이 때문에 순서가 보장되지 않을 수 있다.

// 비동기 방식: 실행 순서가 뒤죽박죽
fs.readFile('./readme.txt', (err, data) => {
    // 이 코드가 가장 먼저 실행될 수 있음.
    console.log('1번', data.toString());
});
fs.readFile('./readme.txt', (err, data) => {
    // 이 코드가 1번보다 먼저 실행될 수도 있음.
    console.log('2번', data.toString());
});

 

2. 동기 (블로킹 방식) 

동기 메서드는 작업이 완료될 때까지 다음 코드를 실행하지 않고 기다린다. 마치 한 손님이 주문을 마치고 음식을 받을 때까지 다른 손님의 주문을 받지 않는 식당과 같다. 이 방식은 코드가 순서대로 실행되므로 결과를 예측하기 쉽지만, 동시에 많은 요청이 들어오면 모든 작업이 멈추는 블로킹(Blocking) 현상이 발생해 성능에 문제가 생긴다.

  • 특징: 동기 메서드는 이름 뒤에 Sync가 붙는다. (예: fs.readFileSync, fs.writeFileSync)
  • 예시: fs.readFileSync()를 사용하면 data 변수에 파일 내용이 완전히 저장될 때까지 다음 console.log가 실행되지 않는다.
// 동기 방식: 순서가 보장됨
let data1 = fs.readFileSync('./readme.txt');
console.log('1번', data1.toString()); // 이 코드가 실행되기 전까지 다음 줄로 넘어가지 않음

let data2 = fs.readFileSync('./readme.txt');
console.log('2번', data2.toString());

let data3 = fs.readFileSync('./readme.txt');
console.log('3번', data3.toString());

 

 

 

3.6.2 버퍼와 스트림 이해하기

 

 

 

버퍼(Buffer)와 스트림(Stream)은 Node.js에서 대용량 데이터를 처리할 때 메모리와 시간을 효율적으로 사용하게 해주는 중요한 개념이다. 이 둘은 데이터를 다루는 방식에서 차이가 있다.

 

 

버퍼(Buffer)

버퍼는 파일이나 네트워크로부터 데이터를 메모리에 일시적으로 저장하는 공간이다. 마치 택배를 트럭에 모두 실어 한 번에 보내는 것처럼, 데이터를 한 덩어리로 모아 전송한다.

  • from(문자열): 문자열을 버퍼로 바꾼다.
  • toString(버퍼): 버퍼를 다시 문자열로 바꾼다.
  • concat(배열): 여러 개의 버퍼를 하나로 합친다.
  • alloc(바이트): 지정한 크기의 빈 버퍼를 만든다.

readFile과 같은 함수는 내부적으로 버퍼를 사용한다. 파일 전체를 메모리에 올리기 때문에, 파일 크기가 클수록 메모리 사용량이 급증하여 비효율적일 수 있다.

 

 

스트림(Stream)

스트림은 데이터를 작은 조각으로 나누어 조금씩 전송하는 방식이다. 마치 물이 수도관을 통해 끊임없이 흘러가는 것처럼, 데이터가 버퍼를 통해 연속적으로 흐른다. 이 방식은 대용량 파일을 다룰 때 메모리를 훨씬 효율적으로 사용한다.

  • createReadStream(): 파일을 읽기 위한 스트림을 만든다. highWaterMark 옵션으로 한 번에 읽을 버퍼의 크기를 조절할 수 있다.
  • createWriteStream(): 데이터를 쓰기 위한 스트림을 만든다.
  • pipe(): 읽기 스트림과 쓰기 스트림을 연결하는 메서드. readStream.pipe(writeStream)처럼 사용해 데이터를 자동으로 전달한다.

pipe를 사용하면 파일 압축(zlib)과 같은 작업을 readStream과 writeStream 사이에 연결하여 데이터를 효율적으로 처리할 수 있다.

 

 

3-7. 이벤트 이해하기

 

Node.js에서 이벤트는 특정 상황이 발생했을 때 미리 정해둔 동작(콜백 함수)을 실행하는 메커니즘이다. events 모듈의 EventEmitter 클래스를 사용하면 이벤트를 직접 만들고 관리할 수 있다.

 

이벤트 리스너를 관리하는 주요 메서드들은 다음과 같다.

  • on(이벤트명, 콜백): 이벤트를 연결하는 가장 기본적인 방법이다. 이 함수는 특정 이름의 이벤트가 발생할 때마다 콜백 함수를 실행하도록 설정한다.
  • addListener(이벤트명, 콜백): on과 기능이 완전히 같다.
  • once(이벤트명, 콜백): 이벤트를 단 한 번만 실행하고 싶을 때 사용한다. 여러 번 이벤트가 발생해도, 처음에만 콜백이 실행된다.
  • emit(이벤트명): 등록된 이벤트를 호출하는 메서드다. 이 함수를 사용하면 해당 이벤트명에 연결된 모든 콜백 함수가 실행된다.
  • removeAllListeners(이벤트명): 특정 이벤트에 연결된 모든 리스너를 한 번에 제거한다.
  • removeListener(이벤트명, 리스너): 특정 콜백 함수를 지정해 이벤트 리스너를 하나씩 제거한다.
  • listenerCount(이벤트명): 특정 이벤트에 현재 몇 개의 리스너가 연결되어 있는지 숫자로 알려준다.

 

 


 

4장 http 모듈로 서버 만들기

 

4-1. 요청과 응답장 노드 기능

 

 

클라이언트(웹 브라우저)가 서버에 특정 정보를 달라고 요청하면, 서버는 그 요청을 처리한 후 결과를 응답으로 돌려준다. 이 통신은 대부분 HTTP(HyperText Transfer Protocol)라는 규칙을 따른다.

 

http 모듈 중 http.createServer() 함수는 서버를 만들고, 요청이 들어올 때마다 콜백 함수를 실행한다. 이 콜백 함수는 req(요청)와 res(응답) 두 개의 인수를 받는다.

  • req (요청 객체): 클라이언트가 보낸 정보(URL, 헤더, 쿠키 등)를 담고 있다.
  • res (응답 객체): 서버가 클라이언트에 보낼 정보(응답 헤더, 본문)를 담는다.

 

HTTP 상태 코드

서버가 요청을 어떻게 처리했는지 알려주는 3자리 숫자다. 이 번호만 봐도 성공인지, 오류인지 등을 바로 알 수 있다.

  • 1XX (정보): 서버가 요청을 받았고 계속 진행 중임을 알린다.
  • 2XX (성공): 요청을 성공적으로 처리했음을 알린다. (예: 200 OK)
  • 3XX (리다이렉션): 다른 URL로 이동해야 함을 알린다.
  • 4XX (클라이언트 오류): 요청 자체가 잘못되었음을 알린다. (예: 404 Not Found)
  • 5XX (서버 오류): 요청은 올바르지만, 서버에 문제가 발생했음을 알린다. (예: 500 Internal Server Error)

 

4-2. REST와 라우팅 사용하기

 

REST(REpresentational State Transfer)는 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법을 가리킨다. 일종의 약속이라고 봐도 무방하다. 자원이라고 해서 꼭 파일일 필요는 없고 서버가 행할 수 있는 것들을 통틀어서 의미한다고 보면 된다. 

 

REST에서는 주소 외에도 HTTP 요청 메서드라는 것을 사용한다. 

  • GET : 원하는 서버 자원을 가져오고자 할 때 사용
  • POST : 서버 자원을 새로 등록하고자 할 때 사용
  • PUT : 서버 자원을 요청에 들어 있는 자원으로 치환
  • PATCH : 서버 자원 일부만 수정할 때 사용
  • DELETE : 서버 자원을 삭제하고자 할 때 사용
  • HEAD : 헤더 부분만 받고 싶을 때 사용
  • OPTIONS : 요청을 하기 전에 통신 옵션을 설명하기 위해 사용

 

4-3.  쿠키와 세션 이해하기

 

사용자가 누구인지 기억하기 위해 서버는 요청에 대한 응답을 할 때는 쿠키라는 것을 보낸다. 쿠키는 유효 기간이 있으며 name=zerocho와같이 단순한 '키-값'의 쌍이다. 서버로부터 쿠키가 오면 웹 브라우저는 쿠키를 저장해두었다가 다음에 요청할 때마다 쿠키를 동봉해서 보낸다. 서버는 요청에 들어 있는 쿠키를 읽어서 사용자가 누군지 파악한다.

 

브라우저는 쿠키가 있다면 자동으로 동봉해서 보내주므로 따로 처리할 필요가 없다. 서버에서 브라우저로 쿠키를 보낼 때만 코드를 작성해 처리한다.

 

세션은 클라이언트(웹 브라우저)에 직접 저장되는 쿠키와 달리, 모든 정보가 서버에 저장되는 방식이다. 사용자가 웹사이트에 처음 접속하면, 서버는 그 사용자를 식별할 수 있는 고유한 세션 ID를 만든다. 그리고 이 세션 ID를 담은 작은 파일, 즉 '세션 쿠키'를 사용자 브라우저에 보낸다. 브라우저는 이 세션 쿠키를 메모리에 저장해 두었다가, 다음에 같은 서버에 요청할 때 자동으로 포함시켜 보낸다. 서버는 요청과 함께 온 세션 쿠키의 ID를 확인하고, 서버에 저장된 세션 정보를 찾아 해당 사용자의 상태를 파악한다. 이 덕분에 사용자는 매번 로그인 정보를 입력할 필요 없이 편리하게 웹 서비스를 이용할 수 있다. 세션은 보통 사용자가 브라우저를 닫거나 만료 시간이 지나면 자동으로 사라진다.

 

 

 

4-4. https와 http2

  • HTTP/1.1: 텍스트 기반으로 통신하고, 한 번에 하나의 파일만 주고받아 여러 파일을 요청할 때 비효율적이다.
  • HTTP/2: 바이너리 형태로 정보를 주고받아 빠르다. 멀티플렉싱(Multiplexing) 기능으로 여러 파일을 동시에 주고받을 수 있어 성능이 크게 향상되었다. 또한 HTTPS를 통해 암호화가 기본으로 적용되어 보안이 강화되었다.

 

4-5. cluster

 

 

cluster 모듈은 기본적으로 싱글 프로세스로 동작하는 노드가 CPU 코어를 모두 사용할 수 있게 해주는 모듈이다. 포트를 공유하는 노드 프로세스를 여러 개 둘 수도 있으므로, 요청이 많이 들어왔을 때 병렬로 실행된 서버의 개수만큼 요청이 분산되게 할 수 있다. 서버에 무리가 덜 가게 하는 것이다. 이렇게 하면 서버의 부하를 효과적으로 분산시켜 성능을 극대화할 수 있다.

 

cluster 모듈은 마스터(Master) 프로세스 워커(Worker) 프로세스라는 두 가지 개념을 사용한다. 마스터 프로세스는 사용자가 실행한 Node.js 파일이며, 이 마스터는 CPU 코어의 개수만큼 워커 프로세스를 생성하는 역할을 한다. 마치 한 명의 관리자가 여러 명의 일꾼을 고용하는 것과 같다.

워커 프로세스는 마스터가 생성한 일꾼들이다. 각각의 워커 프로세스는 독립적으로 Node.js 서버를 실행하며, CPU 코어를 하나씩 맡아 사용한다. 사용자로부터 웹 요청이 들어오면, 마스터 프로세스가 이 요청을 여러 워커 프로세스 중 하나에 분배해 처리하게 한다. 이렇게 하면 요청이 한 곳에 몰리지 않고 여러 코어로 분산되므로, 서버의 처리 속도 안정성이 크게 향상된다.

 

 

 


QUIZ

 

 

 

1) 파일 시스템 접근을 위해 fs 모듈의 동기 메서드를 사용하면, 작업이 완료될 때까지 다음 코드가 실행되지 않는 ( ) 현상이 발생할 수 있다.

 

2) path 모듈에서 여러 경로를 합치되, /로 시작하는 경로가 있다면 그 앞의 경로를 모두 무시하고 절대 경로로 합쳐주는 메서드는 path.( ) 이다.

 

3) http 모듈로 서버를 만들 때, 클라이언트가 보낸 요청 정보를 담고 있는 객체의 이름은 ( ) 이다.

 

4) 웹 브라우저에서 window 객체가 전역 객체인 것처럼, Node.js에서는 ( ) 객체가 전역 객체이다.

 

5) setTimeout, setInterval, setImmediate 같은 타이머 함수들은 모두 고유한 ( )를 반환하여 타이머를 취소할 수 있게 해준다.

 

 

6) 코드의 실행 순서를 올바르게 고르세요.

const fs = require('fs');

const readStream = fs.createReadStream('./readme.txt');

readStream.on('data', (chunk) => {
    // 파일의 일부 데이터가 읽힐 때마다 실행됨
    console.log('읽은 데이터:', chunk.length);
});

readStream.on('end', () => {
    // 파일 읽기가 모두 끝났을 때 실행됨
    console.log('파일 읽기 종료');
});

보기
A. A → C → B
B. B → A → C
C. C → B → A
D. A → B → C

 

7) 코드 뒤 빈칸에 올 내용을 고르세요.

console.log("Node 버전:", process._____);

보기
A. version
B. nodeVersion
C. env
D. info

 

8) 이벤트 루프의 옳은 순서를 고르세요.

setTimeout(() => console.log("timeout"), 0); 
Promise.resolve().then(() => console.log("promise")); 
console.log("sync");

보기
A. sync → timeout → promise
B. sync → promise → timeout
C. promise → sync → timeout
D. timeout → sync → promise

 

 

정답

더보기
1. 블로킹
2. resolve()
3. req
4. global
5. 아이디
6. A
해설: 
setImmediate()는 현재 이벤트 루프가 끝난 뒤 실행됨.

그래서 동기 코드(A, C)가 먼저 실행되고 B가 마지막에 나온다.
7. A
8. B  (3.4 타이머(timer), sync → promise → timeout)

 


출처 : 조현영,  Node.js 교과서 개정 3판, 길벗(2022),

Wallarm, "HTTP/2란 무엇이고 HTTP/1과 어떻게 다른가요?"

TCP School, "C++ 입출력 스트림과 버퍼"

 

Corner Node.js 1
Editor : GYEOMGYOEM

728x90

관련글 더보기