본문으로 건너뛰기
김신건의 로그

[Koa] Koa vs Express

· 수정 · 📖 약 2분 · 859자/단어 #koa #express #comparison #framework
Koa vs Express, Express vs Koa, koa express 비교, koa express 차이, Node.js framework 비교

정의

Koa 와 Express 는 모두 Node.js 웹 프레임워크이지만 철학과 API 스타일이 다릅니다. Express 는 오래됐고 방대한 생태계 (2010~), Koa 는 async/await 우선 (2013~). 창시자는 같은 TJ Holowaychuk 이며 Koa 는 Express 의 후계 성격.

한눈에 비교

ExpressKoa
첫 릴리스20102013
크기500KB+ (deps)매우 작음 (~200KB)
비동기callback 기본, async/await 도 가능async/await first
미들웨어 모델linear (next())onion (await next())
Request/Responsereq, res 각각ctx (통합)
에러 처리try/catch 어려움 (callback)async try/catch 자연
라우팅내장 (app.get)별도 (@koa/router)
Body parser내장 (express.json())별도 (@koa/bodyparser)
템플릿내장 지원별도
정적 파일내장 (express.static)별도 (koa-static)
커뮤니티매우 큼중간
생태계방대 (10,000+ 미들웨어)작음 (koa- 프리픽스)
채택압도적 다수소수

미들웨어 스타일

Express

// callback 스타일
app.get('/users/:id', (req, res, next) => {
  User.findById(req.params.id, (err, user) => {
    if (err) return next(err);
    res.json(user);
  });
});

// async 스타일 (수동 try/catch)
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    res.json(user);
  } catch (err) {
    next(err);
  }
});

Express 는 async error 를 자동으로 안 잡음. express-async-errors 패키지 또는 wrapper 필요.

Koa

router.get('/users/:id', async (ctx) => {
  const user = await User.findById(ctx.params.id);
  ctx.body = user;
});

Async error 는 upstream 으로 자동 전파. 미들웨어에서 try/catch.

Middleware chain

Express (linear)

app.use((req, res, next) => {
  console.log('1');
  next();
  console.log('1 after');   // 이는 실행되지만 응답 이후 관측 어려움
});

app.use((req, res, next) => {
  console.log('2');
  res.send('done');
});

// 출력: 1, 2, 1 after (하지만 response 완료 이후)

Koa (onion)

app.use(async (ctx, next) => {
  console.log('1 in');
  await next();
  console.log('1 out');    // response 이후 확실히 실행
});

app.use(async (ctx) => {
  console.log('2');
  ctx.body = 'done';
});

// 출력: 1 in, 2, 1 out

Koa 의 onion 모델은 응답 시간 측정, 에러 catch, wrapping 이 자연스럽습니다.

Response 처리

Express

app.get('/', (req, res) => {
  res.status(201);
  res.set('X-Custom', 'value');
  res.json({ok: true});
  // 또는 chain
  res.status(201).set('X-Custom', 'value').json({ok: true});
});

res.send, res.json, res.render, res.redirect 등 method 다양.

Koa

router.get('/', async (ctx) => {
  ctx.status = 201;
  ctx.set('X-Custom', 'value');
  ctx.body = {ok: true};   // JSON 자동
});

ctx.body 하나로. Type 자동 감지 (string -> text/html, object -> json).

Error 처리

Express

app.use((err, req, res, next) => {
  console.error(err);
  res.status(err.status || 500).json({error: err.message});
});

// async error 자동 X (v4)
app.get('/', async (req, res, next) => {
  try {
    await doAsyncWork();
  } catch (err) {
    next(err);   // 수동
  }
});

Express 5 에서 async error 를 자동 처리 예정.

Koa

// 첫 미들웨어
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = {error: err.message};
    ctx.app.emit('error', err, ctx);
  }
});

// 그냥 throw
router.get('/', async (ctx) => {
  await doAsyncWork();   // 실패 시 자동 upstream
  ctx.body = 'ok';
});

성능

  • Express: baseline. 특별한 최적화 없음.
  • Koa: 조금 빠름 (코어 작음). 미들웨어 오버헤드 감소.
  • Fastify: 훨씬 빠름 (JSON 최적화, 스키마 컴파일). 성능이 최우선이면 Fastify.

언제 어느 것을?

Express 유지 / 선택

  • 기존 코드베이스
  • 광범위한 미들웨어 필요 (Passport strategies, 특수 통합)
  • 팀이 Express 익숙
  • 튜토리얼/문서/자료 방대 필요
  • Express 5+ async 개선 후

Koa 선택

  • 새 프로젝트 + async/await 자연스러움 원함
  • 가벼운 코어 + 필요한 것만 조합
  • Onion 모델 (트레이싱, error handling 통일) 이점
  • Express 미들웨어 안 필수

대안 고려

  • Fastify: 성능 최상, schema-first
  • NestJS: 대규모 앱 구조, TypeScript, DI
  • Hono: Cloudflare Workers / Edge 최적
  • Elysia: Bun native

코드 예시 나란히

Hello World

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

app.get('/', (req, res) => res.send('Hello'));
app.listen(3000);
// Koa
const Koa = require('koa');
const Router = require('@koa/router');

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

router.get('/', ctx => { ctx.body = 'Hello'; });

app.use(router.routes());
app.listen(3000);

JSON API

// Express
app.use(express.json());
app.post('/users', async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json(user);
  } catch (err) {
    next(err);
  }
});
// Koa
const bodyParser = require('@koa/bodyparser');
app.use(bodyParser());

router.post('/users', async ctx => {
  const user = await User.create(ctx.request.body);
  ctx.status = 201;
  ctx.body = user;
});

Error handler

// Express
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({error: err.message});
});
// Koa (최상단 미들웨어)
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = {error: err.message};
  }
});

File upload

// Express + multer
const multer = require('multer');
const upload = multer({dest: '/uploads/'});
app.post('/upload', upload.single('file'), (req, res) => {
  res.json({filename: req.file.filename});
});
// Koa + @koa/multer
const multer = require('@koa/multer');
const upload = multer({dest: '/uploads/'});
router.post('/upload', upload.single('file'), async ctx => {
  ctx.body = {filename: ctx.file.filename};
});

마이그레이션 (Express -> Koa)

주요 변환:

  1. req.method -> ctx.method, req.path -> ctx.path
  2. req.body -> ctx.request.body
  3. res.status(200).json(x) -> ctx.status = 200; ctx.body = x
  4. (req, res, next) -> (ctx, next)
  5. app.get(...) -> Router 로 이관
  6. Middleware 등록 순서 재검토 (onion model 로)
  7. 미들웨어 (express-session, helmet, …) -> koa 버전

대규모 앱은 마이그레이션 노력 큼. 새 프로젝트만 Koa 선택 후 기존은 유지가 관용.

함정

WARNING

Express middleware 는 Koa 에서 못 씀. Express 는 (req, res, next), Koa 는 (ctx, next). koa-connect 로 래핑 가능하지만 완전 X.

CAUTION

Express 5 는 async 자동 처리. Express 4 는 아님. 프로젝트 버전 확인.

WARNING

Koa 는 라우팅/body parser 없음. Express 에서 온 사람이 자주 실수.

IMPORTANT

미들웨어 표기법 다름. Express app.use(m), Koa app.use(m) 은 같아 보이지만 signature 다름.

CAUTION

성능은 프레임워크보다 코드가 지배적. 프레임워크 선택은 팀/생태계가 우선.

관련 위키

이 글의 용어 (12개)
[Framework] Koa.jsframeworks
정의 Koa.js 는 Express 창시자 TJ Holowaychuk 이 2013년 발표한 Node.js 웹 프레임워크 입니다. Express 의 후계자 성격이며, 미들웨어를 a…
[Framework] NestJSframeworks
정의 NestJS 는 Kamil Myśliwiec 이 2017년 발표한 Node.js 서버측 프레임워크 입니다. TypeScript first, Angular 에서 영감받은 모듈…
[Javascript] 콜백javascript
정의 콜백 (callback): 다른 함수의 인자로 넘겨져, 받은 쪽(caller)이 적절한 시점에 호출하는 함수. 특성이 있어야 가능한 패턴이다. JavaScript 에서는 함…
[Javascript] async/awaitjavascript
정의 / 는 기반 비동기 코드를 마치 동기 코드처럼 쓸 수 있게 해주는 ES2017 의 문법 설탕. 본질은 Promise 그 자체, 문법만 다르다. 전체 동작 메커니즘은 글 참조…
[Javascript] Promisejavascript
정의 Promise 는 미래의 어떤 시점에 결정될 값을 나타내는 객체다. ECMAScript 2015 (ES6) 에 도입되어 기반 비동기의 가독성과 에러 처리 문제를 해결했다. …
[Koa] Body Parsingframeworks
정의 Koa Body Parsing 은 HTTP 요청 body 를 파싱해 에 사용 가능한 형태로 만드는 미들웨어입니다. Koa 코어는 body parser 를 포함하지 않으므로 …
[Koa] Context (ctx)frameworks
정의 Koa Context ( ) 는 각 요청마다 생성되어 미들웨어와 route handler 에 전달되는 객체입니다. (Node 원본), (Koa 확장), (Koa 확장), (…
[Koa] Deployment (PM2, Docker, Kubernetes)frameworks
정의 Koa Deployment 는 Node.js 프로덕션 배포의 표준 도구 (Docker, Kubernetes, PM2) + Koa 특화 고려사항 (graceful shutdo…
[Koa] Error Handlingframeworks
정의 Koa Error Handling 은 미들웨어 chain 에서 발생한 예외를 catch 하여 응답을 정형화하는 패턴입니다. Koa 는 자동 error handler 를 제공…
[Koa] Middleware (Onion Model)frameworks
정의 Koa Middleware 는 요청/응답 사이에 실행되는 async 함수입니다. 각 미들웨어는 (context) 와 (다음 미들웨어를 실행하는 함수) 를 받고, 로 down…
[Koa] Router (@koa/router)frameworks
정의 @koa/router 는 Koa 의 공식 라우터입니다. Koa 코어는 라우팅을 제공하지 않으므로 별도 설치가 필요합니다. Express 스타일 라우팅 (HTTP method…
[Koa] Testing (Supertest, Jest, Vitest)frameworks
정의 Koa Testing 은 (HTTP 호출) + / (assertions/runner) 조합이 표준입니다. 을 Supertest 에 전달하면 실제 HTTP 서버 없이 요청/응…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기