Guider/Backend/BackendDevGuide0001
Backend#01

BackendDevGuide0001

Node.js 완전 정복

Backend Developer Guide Series

BackendDevGuide0001

Node.js 완전 정복

비전공자도 현업에서 바로 쓸 수 있는 Node.js 완전 가이드

예상 학습시간: 20~30시간난이도: 입문~중급목표: 현업 투입 가능

학습 목차

01. Node.js란 무엇인가?
02. 설치와 개발환경 세팅
03. 모듈 시스템 완전 이해
04. 비동기 프로그래밍 마스터
05. 핵심 내장 모듈 정복
06. npm & 패키지 관리
07. 이벤트 루프 완전 이해
08. 스트림과 버퍼
09. 에러 처리 패턴
10. 실전 미니 프로젝트
11. 현업/면접 Q&A
12. 학습 로드맵
 

01. Node.js란 무엇인가?

핵심 한 줄 요약: Node.js는 브라우저 밖에서도 JavaScript를 실행할 수 있게 해주는 런타임 환경입니다.

피자 가게 비유로 이해하기

전통적인 서버(Apache 등)는 주문 하나당 직원 한 명을 배치합니다. 손님이 100명 오면 직원 100명이 필요하죠.

Node.js는 슈퍼 효율적인 혼자 일하는 요리사와 같습니다. 피자 반죽을 올려놓고(비동기 작업 시작), 기다리는 동안 다른 피자를 굽고(다른 요청 처리), 오븐 타이머 울리면 그때 마저 처리합니다.

이게 바로 이벤트 루프(Event Loop)논블로킹 I/O의 핵심입니다!

Node.js vs 기존 서버 비교표

비교 항목 기존 서버 (Apache/PHP) Node.js
처리 방식 멀티스레드 (동기) 싱글스레드 + 이벤트 루프 (비동기)
메모리 사용 요청마다 스레드 생성 이벤트 기반으로 적게 사용
동시 처리 스레드 수에 제한 수만 개 동시 처리 가능
언어 PHP, Java, Python 등 JavaScript (풀스택 가능!)
속도 (I/O 많을 때) 느림 매우 빠름

Node.js를 사용하는 유명 기업

Netflix (스트리밍), LinkedIn (모바일 백엔드), Uber (실시간 매칭), PayPal (결제 API), 카카오, 네이버, 쿠팡 등에서 활발히 사용 중입니다.

 

02. 설치와 개발환경 세팅

중요! 실무에서는 반드시 LTS(Long Term Support) 버전을 사용하세요. 짝수 버전이 LTS입니다 (예: 18.x, 20.x, 22.x)

nvm으로 설치하기 (강력 추천)

# macOS / Linux

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

$ source ~/.bashrc

$ nvm install --lts

$ nvm use --lts

$ nvm ls

# 설치 확인

$ node -v # v22.14.0 같은 버전 출력

$ npm -v # npm 버전 출력

nvm을 쓰는 이유: 프로젝트마다 다른 Node.js 버전을 사용할 수 있습니다. 프로젝트 A는 Node 18, 프로젝트 B는 Node 20처럼 버전 전환이 자유롭습니다!

필수 개발 도구 설정

도구 용도 설치
VS Code 코드 편집기 (가장 추천) code.visualstudio.com
ESLint 코드 품질 검사 npm install -D eslint
Prettier 코드 자동 포매팅 npm install -D prettier
Nodemon 파일 변경 자동 재시작 npm install -D nodemon
Postman API 테스트 도구 postman.com

첫 번째 Node.js 프로그램

// app.js

const os = require("os");

const greeting = (name) => {

  console.log(`안녕하세요, ${name}님! Node.js 세계에 오신걸 환영합니다!`);

  console.log(`운영체제: ${os.platform()}, CPU 코어: ${os.cpus().length}개`);

  console.log(`총 메모리: ${Math.round(os.totalmem() / 1024 / 1024 / 1024)}GB`);

};

greeting("개발자");

 

03. 모듈 시스템 완전 이해

구분 CommonJS (CJS) ES Modules (ESM)
불러오기 require() import
내보내기 module.exports export / export default
로딩 시점 동기 (런타임) 비동기 (파싱 타임)
현업 사용 레거시, 구형 라이브러리 최신 코드 권장

CommonJS 실전 코드

// math.js - 모듈 정의

const add = (a, b) => a + b;

const multiply = (a, b) => a * b;

const PI = 3.14159;

module.exports = { add, multiply, PI };

// app.js - 모듈 사용

const { add, PI } = require("./math");

console.log(add(3, 4)); // 7

console.log(PI); // 3.14159

 

04. 비동기 프로그래밍 완전 마스터

Node.js의 핵심 경쟁력! Callback - Promise - async/await 순서로 진화했습니다. 현업에서는 async/await을 주로 사용합니다.

Callback (콜백) - 기초

const fs = require("fs");

fs.readFile("data.txt", "utf8", (err, data) => {

  if (err) { console.error("에러:", err); return; }

  console.log(data);

});

async/await - 현업 표준

const getUserData = async (userId) => {

  try {

    const user = await fetchUser(userId);

    const posts = await fetchPosts(user.id);

    console.log({ user, posts });

  } catch (error) {

    console.error("데이터 로드 실패:", error.message);

  }

};

// 병렬 처리 (성능 최적화 핵심!)

const [user, settings] = await Promise.all([

  fetchUser(userId),

  fetchSettings(userId)

]);

 

05. 핵심 내장 모듈 정복

fs (File System) - 파일 읽기/쓰기

const fs = require("fs").promises; // Promise 버전 추천

const fileOperations = async () => {

  await fs.writeFile("hello.txt", "안녕하세요!", "utf8");

  const data = await fs.readFile("hello.txt", "utf8");

  await fs.mkdir("logs", { recursive: true });

  const files = await fs.readdir("./");

  await fs.unlink("hello.txt");

};

http 모듈 - 기본 서버 구축

const http = require("http");

const server = http.createServer((req, res) => {

  res.setHeader("Content-Type", "application/json; charset=utf-8");

  if (req.url === "/" && req.method === "GET") {

    res.writeHead(200);

    res.end(JSON.stringify({ message: "Hello Node.js!" }));

  } else {

    res.writeHead(404);

    res.end(JSON.stringify({ error: "Not Found" }));

  }

});

server.listen(3000, () => console.log("서버: http://localhost:3000"));

 

06. npm & 패키지 관리 완전 정복

필수 npm 명령어

$ npm init -y # 프로젝트 초기화

$ npm install express # 운영 의존성 설치

$ npm install -D nodemon # 개발 의존성 설치

$ npm install # package.json 기반 모두 설치

$ npm uninstall express # 패키지 제거

$ npm run dev # scripts의 dev 실행

$ npm audit # 보안 취약점 검사

현업 필수 패키지 TOP 15

패키지명 용도 중요도
express 웹 서버 프레임워크 필수
dotenv 환경변수 관리 필수
jsonwebtoken JWT 인증 필수
bcrypt 비밀번호 해시 필수
cors CORS 설정 필수
mongoose MongoDB ORM 중요
joi / zod 데이터 유효성 검사 필수
winston 로그 관리 중요
axios HTTP 클라이언트 중요
multer 파일 업로드 필요시
socket.io 실시간 통신 필요시
jest / vitest 테스트 프레임워크 필수
helmet 보안 헤더 설정 필수
ioredis Redis 캐싱 중요
sequelize SQL ORM 중요
 

07. 이벤트 루프 완전 이해

면접 단골 질문! 이벤트 루프는 Node.js가 싱글 스레드임에도 비동기 작업을 처리할 수 있는 핵심 메커니즘입니다.

이벤트 루프 구성 요소

Call Stack (주방): 현재 실행 중인 코드

Web APIs / libuv: setTimeout, HTTP 요청, 파일 I/O 등 백그라운드 처리

Callback Queue (대기줄): 완료된 작업들이 실행되길 기다리는 곳

Microtask Queue (VIP 대기줄): Promise.then() - 일반 콜백보다 먼저 처리!

Event Loop: Call Stack이 비면 Queue에서 꺼내서 실행

실행 순서 예측 - 핵심 테스트

console.log("1. 시작"); // 동기

setTimeout(() => console.log("4. setTimeout 0ms"), 0);

Promise.resolve().then(() => console.log("3. Promise"));

console.log("2. 끝"); // 동기

// 출력: 1.시작 - 2.끝 - 3.Promise - 4.setTimeout (Micro Task 우선!)

단계 처리 내용 예시
timers setTimeout, setInterval 콜백 setTimeout(fn, 100)
poll I/O 이벤트, 새 연결 수락 fs.readFile, HTTP 요청
check setImmediate 콜백 setImmediate(fn)
 

08. 스트림과 버퍼

대용량 데이터 처리의 핵심! 1GB 파일을 통째로 메모리에 올리면 서버가 다운됩니다. 스트림을 사용하면 조금씩 나눠서 처리하므로 메모리를 효율적으로 사용합니다.

// BAD: 통째로 읽기 (1GB 파일이면 1GB 메모리 사용)

fs.readFile("bigfile.mp4", (err, data) => fs.writeFile("copy.mp4", data, () => {}));

// GOOD: 스트림으로 처리 (메모리 효율적)

const fs = require("fs");

const readStream = fs.createReadStream("bigfile.mp4");

const writeStream = fs.createWriteStream("copy.mp4");

readStream.pipe(writeStream); // 조금씩 읽어서 쓰기

writeStream.on("finish", () => console.log("복사 완료!"));

 

09. 에러 처리 패턴 (현업 표준)

현업에서 에러 처리는 필수! 에러를 제대로 처리하지 않으면 서버가 갑자기 다운됩니다.

커스텀 에러 클래스 만들기

// errors/AppError.js

class AppError extends Error {

  constructor(message, statusCode = 500, code = "INTERNAL_ERROR") {

    super(message);

    this.statusCode = statusCode;

    this.code = code;

    this.isOperational = true;

  }

}

class NotFoundError extends AppError {

  constructor(resource = "Resource") {

    super(`${resource}을 찾을 수 없습니다`, 404, "NOT_FOUND");

  }

}

// catchAsync 래퍼 - try-catch 없이 에러 자동 처리!

const catchAsync = (fn) => (req, res, next) => {

  Promise.resolve(fn(req, res, next)).catch(next);

};

// 사용 예시

app.get("/users/:id", catchAsync(async (req, res) => {

  const user = await User.findById(req.params.id);

  if (!user) throw new NotFoundError("사용자");

  res.json({ success: true, data: user });

}));

 

10. 실전 미니 프로젝트 - REST API 서버

무엇을 만들까? Express + Node.js로 완전한 Todo API 서버를 만들어봅니다. 이것을 이해하면 어떤 REST API도 만들 수 있습니다!

프로젝트 구조

todo-api/

├── src/

│ ├── routes/todo.routes.js

│ ├── controllers/todo.controller.js

│ ├── middleware/errorHandler.js

│ └── app.js

├── .env

└── package.json

src/app.js - 메인 앱 설정

require("dotenv").config();

const express = require("express");

const cors = require("cors");

const helmet = require("helmet");

const app = express();

app.use(helmet()); // 보안 헤더

app.use(cors()); // CORS 허용

app.use(express.json()); // JSON 파싱

app.use("/api/todos", todoRoutes);

app.use(errorHandler); // 에러 핸들러 (마지막!)

app.listen(3000, () => console.log("서버: http://localhost:3000"));

Todo CRUD API 테스트

# 목록 조회

$ curl http://localhost:3000/api/todos

# Todo 생성

$ curl -X POST http://localhost:3000/api/todos -H "Content-Type: application/json" -d '{"title":"Node.js 공부"}'

# 완료 처리

$ curl -X PATCH http://localhost:3000/api/todos/UUID -H "Content-Type: application/json" -d '{"completed":true}'

 

11. 현업/면접 단골 Q&A

Q1. Node.js가 싱글 스레드인데 어떻게 빠를 수 있나요?

A: Node.js는 I/O 작업(파일 읽기, DB 조회, HTTP 요청)을 논블로킹 방식으로 처리합니다. 요청을 보내고 기다리는 대신, 다른 작업을 처리하다가 완료 신호가 오면 콜백을 실행합니다. 단, CPU를 많이 사용하는 작업(암호화, 이미지 처리)에는 worker_threads를 사용해야 합니다.

Q2. Promise와 async/await의 차이가 무엇인가요?

A: async/await은 Promise의 문법적 설탕(Syntactic Sugar)입니다. 비동기 코드를 동기 코드처럼 읽히게 해서 가독성이 좋습니다. 현업에서는 async/await을 주로 쓰고, 병렬 처리가 필요할 때는 Promise.all()과 함께 사용합니다.

Q3. 이벤트 루프에서 Microtask와 Macrotask의 차이는?

A: Microtask는 Promise.then() 등이며 현재 태스크가 끝난 직후 즉시 실행됩니다. Macrotask는 setTimeout, I/O 등이며 이벤트 루프의 다음 순회에서 실행됩니다. 실행 순서: 동기 코드 - Microtask 큐 - Macrotask 큐 순서입니다.

Q4. require()와 import의 차이점은?

A: require()(CJS)는 동기적으로 런타임에 로드되며 조건문 안에서도 사용 가능합니다. import(ESM)는 정적 분석이 되어 Tree-shaking이 가능합니다. 최신 Node.js 프로젝트는 ESM을 권장합니다.

Q5. 대용량 파일을 처리할 때 어떻게 하나요?

A: Stream을 사용합니다. fs.readFile()은 파일 전체를 메모리에 올리므로 대용량에는 부적합합니다. fs.createReadStream()으로 청크 단위로 읽어 처리하고 pipe()로 연결합니다. 수 GB 파일도 일정한 메모리로 처리 가능합니다.

 

12. 백엔드 개발자 학습 로드맵

단계 학습 내용 예상 기간 상태
1단계 JavaScript 기초 (ES6+) 2~3주 완료 (Guide0000)
2단계 Node.js 기초 + 비동기 2~3주 현재 (Guide0001)
3단계 Express.js + REST API 설계 3~4주 Guide0002 예정
4단계 데이터베이스 (MongoDB + PostgreSQL) 3~4주 Guide0003 예정
5단계 인증/보안 (JWT, OAuth, HTTPS) 2~3주 Guide0004 예정
6단계 테스트 코드 (Jest, Supertest) 2~3주 Guide0005 예정
7단계 배포 (Docker, AWS, CI/CD) 3~4주 Guide0006 예정

수고하셨습니다!

Node.js의 핵심 개념부터 실전 프로젝트까지 완주했습니다. 이 내용을 완전히 이해했다면 백엔드 개발자로서의 첫 발을 내딛은 것입니다!

다음 단계: BackendDevGuide0002
Express.js 완전 정복 - 미들웨어, 라우팅, MVC 패턴, REST API 설계

반응형