프로그래밍/Node.js

Node.js 웹 개발: Express.js와 Koa.js 프레임워크 비교

shimdh 2025. 2. 18. 09:23
728x90

1. Express.js: Node.js 웹 애플리케이션 개발의 표준

1.1 Express.js란 무엇인가?

Express.js는 Node.js 위에서 작동하는 경량화된 웹 애플리케이션 프레임워크로, Node.js 웹 개발의 사실상 표준으로 자리 잡았습니다. 간결하고 직관적인 API를 제공하며, 라우팅, 미들웨어, 템플릿 엔진, 정적 파일 제공 등 웹 개발에 필요한 다양한 기능을 포함하고 있습니다.

1.2 Express.js의 주요 특징

  • 간편한 라우팅: Express.js는 URL 및 HTTP 메소드(GET, POST, PUT, DELETE 등)를 사용하여 요청을 처리할 수 있는 직관적인 라우팅 기능을 제공합니다. 이를 통해 특정 URL 경로에 대한 요청이 들어왔을 때 어떤 작업을 수행할지 쉽게 정의할 수 있습니다.
  • 미들웨어 지원: Express.js의 핵심 기능 중 하나는 미들웨어입니다. 미들웨어는 요청과 응답 사이에 여러 기능(예: 인증, 로깅, 에러 핸들링 등)을 체인 형태로 추가할 수 있는 메커니즘을 제공합니다. 이를 통해 애플리케이션의 로직을 모듈화하고 재사용할 수 있습니다.
  • 유연성과 확장성: Express.js는 최소한의 기능만 코어에 포함하고, 나머지 기능은 미들웨어나 외부 모듈을 통해 확장할 수 있도록 설계되었습니다. 이러한 유연성 덕분에 개발자는 필요한 기능만 선택적으로 사용하여 가볍고 효율적인 애플리케이션을 구축할 수 있습니다.
  • RESTful API 구축 용이: RESTful API 설계 원칙을 쉽게 구현할 수 있도록 돕는 다양한 기능을 제공합니다.
  • 템플릿 엔진 지원: EJS, Pug, Handlebars와 같은 다양한 템플릿 엔진과 쉽게 연동하여 동적인 HTML 페이지를 생성할 수 있습니다.
  • 정적 파일 제공: express.static() 미들웨어를 사용하여 HTML, CSS, JavaScript, 이미지와 같은 정적 파일을 쉽게 제공할 수 있습니다.

1.3 Express.js 예제: 라우팅

const express = require('express');
const app = express();

// GET / 요청 처리
app.get('/', (req, res) => {
  res.send('Hello, Express!');
});

// GET /users 요청 처리
app.get('/users', (req, res) => {
  res.send('사용자 목록');
});

// GET /posts 요청 처리
app.get('/posts', (req, res) => {
  res.send('게시글 목록');
});

// GET /products 요청 처리
app.get('/products', (req, res) => {
  res.send('상품 목록');
});

app.listen(3000, () => {
  console.log('서버가 3000번 포트에서 실행 중입니다.');
});

설명: 위 코드는 Express.js를 사용하여 간단한 웹 서버를 구축하는 예제입니다. express 모듈을 불러와 app 객체를 생성하고, app.get() 메서드를 사용하여 /, /users, /posts, /products 경로에 대한 GET 요청을 처리하는 라우트를 정의합니다. res.send()는 클라이언트에게 문자열 응답을 전송합니다. app.listen()은 서버를 3000번 포트에서 실행합니다.

1.4 Express.js 예제: 미들웨어

const express = require('express');
const app = express();

// 로깅 미들웨어
function logger(req, res, next) {
  console.log(`${req.method} ${req.url}`); // 요청 메서드와 URL 로깅
  next(); // 다음 미들웨어 또는 라우트 핸들러 호출
}

// 인증 미들웨어
function authenticator(req, res, next) {
  // 인증 로직 (예: 토큰 검사)
  const token = req.headers.authorization;
  if (token === 'valid_token') {
    next();
  } else {
    res.status(401).send('Unauthorized'); // 인증 실패 시 401 응답
  }
}

// 요청 시간 기록 미들웨어
function requestTime(req, res, next) {
  req.requestTime = Date.now(); // 요청 시간을 req 객체에 추가
  next();
}

app.use(logger); // 모든 요청에 대해 logger 미들웨어 실행
app.use(requestTime); // 모든 요청에 대해 requestTime 미들웨어 실행

// /users 경로에 대한 GET 요청에만 authenticator 미들웨어 실행
app.get('/users', authenticator, (req, res) => {
  console.log(`Request Time: ${req.requestTime}`);
  res.send('인증된 사용자 목록');
});

app.get('/', (req, res) => {
    console.log(`Request Time: ${req.requestTime}`);
    res.send('Hello from Express.js!');
  });

app.listen(3000, () => {
    console.log('Express 서버가 3000번 포트에서 실행중입니다.');
});

설명: logger, authenticator, requestTime 미들웨어를 정의합니다. logger는 요청 정보를 콘솔에 출력하고, authenticator는 인증을 처리하며, requestTime는 요청 시간을 req 객체에 추가합니다. app.use()를 통해 loggerrequestTime을 모든 요청에 적용하고, /users 경로에는 authenticator를 추가로 적용합니다.

1.5 Express.js 예제: 정적 파일 제공

const express = require('express');
const app = express();

// public 디렉토리의 파일들을 /static 경로로 제공
app.use('/static', express.static('public'));

// uploads 디렉토리의 파일들을 /uploads 경로로 제공
app.use('/uploads', express.static('uploads'));

app.listen(3000, () => {
    console.log('Express 서버가 3000번 포트에서 실행중입니다.');
});

설명: express.static() 미들웨어를 사용하여 public 디렉토리의 파일들을 /static 경로로, uploads 디렉토리의 파일들을 /uploads 경로로 제공합니다.

1.6 Express.js 예제: RESTful API

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json()); // JSON 요청 본문 파싱

let users = [];
let posts = [];

// 사용자 생성 (POST /users)
app.post('/users', (req, res) => {
  const user = req.body;
  user.id = Date.now().toString(); // 간단한 ID 생성
  users.push(user);
  res.status(201).send(user); // 201 Created 상태 코드 반환
});

// 사용자 목록 조회 (GET /users)
app.get('/users', (req, res) => {
  res.send(users);
});

// 특정 사용자 조회 (GET /users/:id)
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  const user = users.find(u => u.id === userId);
  if (user) {
    res.send(user);
  } else {
    res.status(404).send('User not found');
  }
});

// 게시글 생성 (POST /posts)
app.post('/posts', (req, res) => {
  const post = req.body;
  post.id = Date.now().toString(); // 간단한 ID 생성
  posts.push(post);
  res.status(201).send(post);
});

// 모든 게시글 조회 (GET /posts)
app.get('/posts', (req, res) => {
  res.send(posts);
});

app.listen(3000, () => {
  console.log('서버가 3000번 포트에서 실행 중입니다.');
});

설명: Express.js를 사용하여 사용자 정보와 게시글을 관리하는 RESTful API 서버를 구축합니다. body-parser 미들웨어를 사용하여 JSON 요청 본문을 파싱합니다. 사용자 생성, 조회, 특정 사용자 조회, 게시글 생성, 조회를 위한 라우트를 정의합니다.

2. Koa.js: Express.js 개발팀이 만든 차세대 프레임워크

2.1 Koa.js란 무엇인가?

Koa.js는 Express.js를 만든 개발팀이 새롭게 설계한 차세대 Node.js 웹 프레임워크입니다. Express.js보다 더 가볍고 표현력이 뛰어나며, async/await를 기반으로 비동기 프로그래밍을 더욱 간편하게 처리할 수 있습니다.

2.2 Koa.js의 주요 특징

  • 경량화: Koa.js는 코어에 최소한의 기능만 포함하고, 나머지 기능은 미들웨어를 통해 구현하도록 설계되었습니다. 이를 통해 매우 가볍고 빠른 애플리케이션을 구축할 수 있습니다.
  • Async/Await: Koa.js의 가장 큰 특징은 async/await를 사용하여 비동기 코드를 동기 코드처럼 작성할 수 있다는 것입니다. 이를 통해 콜백 지옥(callback hell)을 피하고 코드의 가독성과 유지보수성을 크게 향상시킬 수 있습니다.
  • 향상된 오류 처리: Koa.js는 try...catch 문을 사용하여 비동기 코드에서 발생하는 오류를 쉽게 처리할 수 있습니다.
  • 컨텍스트(Context) 객체: Koa.js는 요청과 응답을 하나의 객체로 묶은 컨텍스트(Context) 객체를 제공합니다. 이를 통해 요청과 응답에 대한 정보를 더 편리하게 다룰 수 있습니다.
  • 유연한 미들웨어 시스템: Koa.js의 미들웨어는 async 함수로 작성되며, await next()를 통해 다음 미들웨어의 실행을 제어합니다. 이를 통해 미들웨어의 실행 순서를 정교하게 조정할 수 있습니다.

2.3 Koa.js 예제: 기본 서버 및 미들웨어

const Koa = require('koa');
const app = new Koa();

// 첫 번째 미들웨어
app.use(async (ctx, next) => {
  console.log('첫 번째 미들웨어');
  await next(); // 다음 미들웨어 실행
  console.log('첫 번째 미들웨어 종료');
});

// 두 번째 미들웨어 (로깅)
app.use(async (ctx, next) => {
  const start = Date.now();
  await next(); // 다음 미들웨어 실행
  const ms = Date.now() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); // 요청 처리 시간 로깅
});

// 세 번째 미들웨어 (응답)
app.use(async (ctx) => {
  ctx.body = 'Hello, Koa!'; // 응답 본문 설정
});

app.listen(3000, () => {
    console.log('Koa 서버가 3000번 포트에서 실행 중입니다.');
});

설명: Koa.js를 사용하여 간단한 웹 서버를 구축하고 세 개의 미들웨어를 추가합니다. 첫 번째 미들웨어는 요청과 응답 전후에 로그를 남기고, 두 번째 미들웨어는 요청 처리 시간을 로깅하며, 세 번째 미들웨어는 Hello, Koa!라는 응답을 설정합니다. await next()는 다음 미들웨어의 실행이 완료될 때까지 기다리는 역할을 합니다.

2.4 Koa.js 예제: 라우팅

const Koa = require('koa');
const Router = require('@koa/router');

const app = new Koa();
const router = new Router();

// GET / 요청 처리
router.get('/', (ctx) => {
  ctx.body = 'Hello, Koa Router!';
});

// GET /users 요청 처리
router.get('/users', (ctx) => {
  ctx.body = '사용자 목록';
});

// GET /posts 요청 처리
router.get('/posts', (ctx) => {
    ctx.body = '게시글 목록';
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
    console.log('Koa 서버가 3000번 포트에서 실행 중입니다.');
});

설명: @koa/router를 사용하여 라우팅을 처리합니다. /, /users, /posts 경로에 대한 GET 요청을 처리하는 라우트를 정의합니다. app.use(router.routes()).use(router.allowedMethods())는 라우터 미들웨어를 Koa.js 애플리케이션에 적용합니다.

2.5 Koa.js 예제: RESTful API

const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');

const app = new Koa();
const router = new Router();

app.use(bodyParser()); // 요청 본문 파싱

let items = [];
let users = [];

// 아이템 목록 조회 (GET /items)
router.get('/items', (ctx) => {
  ctx.body = items;
});

// 아이템 추가 (POST /items)
router.post('/items', async (ctx) => {
  const item = ctx.request.body;
  item.id = Date.now().toString(); // 간단한 ID 생성
  items.push(item);
  ctx.status = 201; // 201 Created 상태 코드 반환
  ctx.body = item;
});

// 특정 아이템 조회 (GET /items/:id)
router.get('/items/:id', (ctx) => {
  const itemId = ctx.params.id;
  const item = items.find(i => i.id === itemId);
  if (item) {
    ctx.body = item;
  } else {
    ctx.status = 404;
    ctx.body = { error: 'Item not found' };
  }
});

// 사용자 추가 (POST /users)
router.post('/users', async (ctx) => {
  const user = ctx.request.body;
  user.id = Date.now().toString(); // 간단한 ID 생성
  users.push(user);
  ctx.status = 201;
  ctx.body = user;
});

// 사용자 목록 조회 (GET /users)
router.get('/users', (ctx) => {
  ctx.body = users;
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
  console.log('Koa API 서버가 3000번 포트에서 실행 중입니다.');
});

설명: Koa.js, @koa/router, koa-bodyparser를 사용하여 아이템 정보와 사용자 정보를 관리하는 RESTful API 서버를 구축합니다. 아이템과 사용자의 생성, 조회, 특정 아이템 조회를 위한 라우트를 정의합니다.

3. Express.js vs Koa.js: 비교 및 결론

3.1 비교

특징 Express.js Koa.js
개발 주체 StrongLoop (IBM의 자회사) Express.js 개발팀
성숙도 매우 성숙함, 넓은 사용자층 상대적으로 덜 성숙하지만 빠르게 성장 중
미들웨어 콜백 기반 Async/Await 기반
오류 처리 콜백 또는 별도의 오류 처리 미들웨어 필요 try...catch를 통한 간편한 오류 처리
컨텍스트 별도의 컨텍스트 객체 없음, reqres 사용 ctx 객체를 통해 요청과 응답을 통합 관리
라우팅 내장 라우터 제공 @koa/router와 같은 별도 모듈 사용
크기 상대적으로 큼 매우 작고 가벼움
성능 우수 매우 우수
학습 곡선 상대적으로 낮음 상대적으로 높음 (async/await에 대한 이해 필요)
유연성 높음 매우 높음
커뮤니티 매우 큼, 방대한 자료와 서드파티 모듈 성장 중, Express.js에 비해 상대적으로 작음

3.2 결론

Express.js와 Koa.js는 모두 훌륭한 Node.js 웹 프레임워크입니다.

Express.js는 다음과 같은 경우에 적합합니다.

  • Node.js 웹 개발을 처음 시작하는 경우: Express.js는 학습 곡선이 낮고, 방대한 자료와 커뮤니티 지원을 받을 수 있습니다.
  • 안정적이고 검증된 프레임워크를 선호하는 경우: Express.js는 오랜 시간 동안 널리 사용되어 왔으며, 안정성과 신뢰성이 검증되었습니다.
  • 다양한 미들웨어와 플러그인을 활용하고 싶은 경우: Express.js는 방대한 생태계를 보유하고 있어, 다양한 미들웨어와 플러그인을 쉽게 사용할 수 있습니다.

Koa.js는 다음과 같은 경우에 적합합니다.

  • Async/Await를 사용한 비동기 프로그래밍에 익숙한 경우: Koa.js는 async/await를 기반으로 설계되어, 비동기 코드를 간결하고 가독성 좋게 작성할 수 있습니다.
  • 가볍고 유연한 프레임워크를 선호하는 경우: Koa.js는 코어에 최소한의 기능만 포함하고 있어 매우 가볍고, 미들웨어를 통해 기능을 유연하게 확장할 수 있습니다.
  • 직접 미들웨어를 작성하고 제어하는 것을 선호하는 경우: Koa.js는 미들웨어의 실행 순서를 정교하게 제어할 수 있어, 애플리케이션의 로직을 세밀하게 조정할 수 있습니다.

결론적으로, 프로젝트의 요구 사항과 개발자의 선호도에 따라 적절한 프레임워크를 선택하는 것이 중요합니다. Express.js는 안정성과 편리함을, Koa.js는 최신 기술과 유연성을 제공합니다. 두 프레임워크 모두 장단점이 있으므로, 프로젝트의 규모, 복잡성, 성능 요구 사항, 개발팀의 숙련도 등을 고려하여 신중하게 선택해야 합니다. Node.js와 함께 Express.js 또는 Koa.js를 사용하면 현대적인 웹 애플리케이션을 효율적으로 구축할 수 있을 것입니다.

728x90