[Framework] Koa.js
정의
Koa.js 는 Express 창시자 TJ Holowaychuk 이 2013년 발표한 Node.js 웹 프레임워크 입니다. Express 의 후계자 성격이며, 미들웨어를 async/await 로 자연스럽게 표현 하고 양파 (onion) 모델 로 요청/응답을 감쌉니다. 초경량 코어 (약 500 LOC), 라우팅과 body parsing 은 별도 패키지.
2024-2025 년 현재 Koa 2.15+ 또는 Koa 3.x (async/await native, Node 20+) 이 사용됩니다.
왜 Koa 인가
- 작고 명료: 코어에 라우팅 없음. 필요한 것만 조합.
- async/await 우선: Express 의 callback 지옥 없음.
- Onion middleware:
await next()로 downstream + upstream 를 자연스럽게. - Context (
ctx): request + response 를 감싼 하나의 객체. - Error 처리 단순: try/catch 로 미들웨어 예외 잡음.
- 경량: 저지연, 오버헤드 최소.
단점:
- 생태계가 Express 대비 좁음
- 큰 앱 구조는 스스로 설계 (NestJS 같은 opinionated 프레임워크 없음)
- 학습 자료 (블로그, 예시) 적음
설치 & 최소 예제
mkdir myapp && cd myapp
npm init -y
npm i koa
// server.js
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
node server.js
# curl http://localhost:3000 -> Hello Koa
Context (ctx)
Koa 의 핵심. request + response 통합.
app.use(async (ctx, next) => {
console.log(ctx.method); // 'GET'
console.log(ctx.path); // '/users/1'
console.log(ctx.query); // {page: '1'}
console.log(ctx.headers); // 헤더
console.log(ctx.request.body); // body (parser 필요)
ctx.status = 201;
ctx.body = {ok: true}; // 자동 JSON 직렬화
ctx.set('X-Custom', 'value'); // 응답 헤더
});
자세한 것은 Koa Context 참조.
Middleware (Onion 모델)
app.use(async (ctx, next) => {
console.log('A pre');
await next();
console.log('A post');
});
app.use(async (ctx, next) => {
console.log('B pre');
await next();
console.log('B post');
});
app.use(async (ctx) => {
console.log('C');
ctx.body = 'ok';
});
// 요청 시 출력:
// A pre
// B pre
// C
// B post
// A post
각 미들웨어의 await next() 이전은 downstream 방향, 이후는 upstream 방향. 요청 처리 완료를 순차 관측 가능.
자세한 것은 Koa Middleware 참조.
라우팅 (@koa/router)
npm i @koa/router
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = 'Home';
});
router.get('/users/:id', async (ctx) => {
ctx.body = { id: ctx.params.id };
});
router.post('/users', async (ctx) => {
ctx.body = ctx.request.body;
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
자세한 것은 Koa Router 참조.
Body Parser (@koa/bodyparser)
npm i @koa/bodyparser
const bodyParser = require('@koa/bodyparser');
app.use(bodyParser());
router.post('/echo', async (ctx) => {
ctx.body = ctx.request.body;
});
자세한 것은 Koa Body Parsing 참조.
흔한 조합 (스택)
| 목적 | 패키지 |
|---|---|
| 라우팅 | @koa/router |
| Body | @koa/bodyparser, koa-body (파일 업로드) |
| Multipart | @koa/multer |
| CORS | @koa/cors |
| Session | koa-session |
| Cookie | 내장 (ctx.cookies) |
| Compression | koa-compress |
| Logger | koa-logger, koa-pino-logger |
| Static | koa-static |
| JWT | koa-jwt, jose |
| Rate limit | koa-ratelimit |
| Views (Jinja) | @ladjs/koa-views |
| Error | 내장 + app.on('error', ...) |
Async / Promise 지원
Koa 는 처음부터 async 지원. 미들웨어가 Promise 를 리턴하거나 async 함수.
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
Error 처리
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
error: err.message,
...(process.env.NODE_ENV !== 'production' && {stack: err.stack}),
};
ctx.app.emit('error', err, ctx);
}
});
app.on('error', (err, ctx) => {
console.error('server error', err);
});
Koa 는 기본적으로 ctx.status = 4xx/5xx + ctx.throw(4xx, 'message') 지원. 자세한 것은 Koa Error Handling 참조.
응답 반환
ctx.body = 'string'; // text/plain (또는 text/html 자동 감지)
ctx.body = { a: 1 }; // JSON
ctx.body = buffer; // binary
ctx.body = stream; // streaming
ctx.body = null; // 204 no content
Status:
ctx.status = 201;
ctx.throw(404, 'Not found'); // 예외 발생
ctx.assert(user, 401, 'Login required');
Header:
ctx.set('Content-Type', 'application/json');
ctx.append('Set-Cookie', '...');
ctx.remove('X-Foo');
Redirect:
ctx.redirect('/login');
ctx.redirect('back', '/'); // Referer 기반
Config
Koa 자체는 config 없음. dotenv, config, zod 조합.
require('dotenv').config();
const app = new Koa();
app.proxy = process.env.NODE_ENV === 'production'; // X-Forwarded-* 신뢰
TypeScript 지원
@types/koa, @types/koa__router. 하지만 완전한 TS-first 는 아님. NestJS 처럼 완전한 타입 안전 원하면 그쪽.
import Koa from 'koa';
import Router from '@koa/router';
interface AppState { user?: User; }
interface AppContext { db: DatabaseService; }
const app = new Koa<AppState, AppContext>();
const router = new Router<AppState, AppContext>();
router.get('/me', async (ctx) => {
const user = ctx.state.user; // typed
const users = await ctx.db.query(...); // typed
ctx.body = user;
});
ctx.state 는 request 스코프 저장, ctx 확장은 앱 스코프.
프로젝트 구조 (실전)
myapp/
├── src/
│ ├── app.js # createApp
│ ├── server.js # 진입점 (listen)
│ ├── config/
│ │ └── index.js
│ ├── middleware/
│ │ ├── error.js
│ │ ├── logger.js
│ │ ├── auth.js
│ │ └── request-id.js
│ ├── routes/
│ │ ├── index.js # router combine
│ │ ├── users.js
│ │ └── posts.js
│ ├── services/
│ │ └── db.js
│ ├── models/
│ └── utils/
├── test/
├── package.json
└── .env
app.js:
const Koa = require('koa');
const bodyParser = require('@koa/bodyparser');
const cors = require('@koa/cors');
const helmet = require('koa-helmet');
const errorMiddleware = require('./middleware/error');
const loggerMiddleware = require('./middleware/logger');
const routes = require('./routes');
function createApp() {
const app = new Koa();
app.proxy = true;
app.use(errorMiddleware);
app.use(loggerMiddleware);
app.use(helmet());
app.use(cors({origin: 'https://app.example.com', credentials: true}));
app.use(bodyParser({jsonLimit: '1mb'}));
app.use(routes.routes()).use(routes.allowedMethods());
return app;
}
module.exports = createApp;
server.js:
const createApp = require('./app');
const app = createApp();
const server = app.listen(process.env.PORT || 3000, () => {
console.log(`Listening on ${process.env.PORT || 3000}`);
});
// Graceful shutdown
process.on('SIGTERM', async () => {
server.close(() => process.exit(0));
});
Fastify 와의 관계
Fastify 는 Koa 이후 등장한 성능 지향 프레임워크. Fastify 가 더 빠르고 (JSON stringify 캐싱, plugin 시스템), 스키마 지원 우수. Koa 는 가벼움 + async/await 초점, Fastify 는 성능 + 표준화.
Express 와의 관계
Koa 는 Express 의 후계자로 시작. TJ 가 Express 를 인수인계 후 Koa 를 만듦. 오늘날 두 프레임워크 병존. 자세한 것은 Koa vs Express 참조.
실전 예: JSON API
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('@koa/bodyparser');
const app = new Koa();
const router = new Router();
// Global error
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {error: err.message};
}
});
app.use(bodyParser());
// Routes
router.get('/users', async (ctx) => {
ctx.body = await getUsersFromDb();
});
router.get('/users/:id', async (ctx) => {
const user = await getUserById(ctx.params.id);
ctx.assert(user, 404, 'User not found');
ctx.body = user;
});
router.post('/users', async (ctx) => {
const {email, name} = ctx.request.body;
ctx.assert(email, 400, 'email required');
const user = await createUser({email, name});
ctx.status = 201;
ctx.body = user;
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
함정
WARNING
ctx.body 를 여러 번 할당 하면 마지막 값이 응답. 실수로 downstream 에서 override.
CAUTION
await next() 잊으면 hang. Middleware chain 이 다음 단계로 못 감.
WARNING
Prod 에서 app.proxy = true 안 하면 X-Forwarded-For 무시. ctx.ip 가 프록시 IP 반환.
IMPORTANT
Koa 는 라우팅 X, body parser X, cookie parser X 를 별도 설치. Express 에서 온 사람들이 자주 실수.
CAUTION
Middleware 순서. Error handler 를 첫 middleware 로. Body parser 를 라우터 앞에.
관련 위키
- Koa Context - ctx 심화
- Koa Middleware - Onion 모델
- Koa Router - 라우팅
- Koa Body Parsing
- Koa Error Handling
- Koa Testing
- Koa Deployment
- Koa vs Express
- NestJS - 대비 (opinionated)
- FastAPI - Python 대비
- Flask - Python 마이크로프레임워크 대비
- JavaScript async/await
- JavaScript Promise
이 글의 용어 (13개)
- [Framework] NestJSframeworks
- 정의 NestJS 는 Kamil Myśliwiec 이 2017년 발표한 Node.js 서버측 프레임워크 입니다. TypeScript first, Angular 에서 영감받은 모듈…
- [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] Koa vs Expressframeworks
- 정의 Koa 와 Express 는 모두 Node.js 웹 프레임워크이지만 철학과 API 스타일이 다릅니다. Express 는 오래됐고 방대한 생태계 (2010), Koa 는 as…
- [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 서버 없이 요청/응…
- [Python] FastAPIfastapi
- 정의 FastAPI 는 Python 3.8+ 을 위한 ASGI 기반 현대 웹/API 프레임워크 입니다. Sebastián Ramírez (tiangolo) 가 2018년 발표했고…
- [Python] Flaskflask
- 정의 Flask 는 Armin Ronacher 가 2010년 발표한 WSGI 기반 Python 마이크로프레임워크 입니다. Pallets Projects 가 유지관리하고, 2026…
이 개념을 다룬 위키 페이지 (11)
- wiki[Koa] Body Parsing
- wiki[Koa] Context (ctx)
- wiki[Koa] Deployment (PM2, Docker, Kubernetes)
- wiki[Koa] Error Handling
- wiki[Koa] Middleware (Onion Model)
- wiki[Koa] Router (@koa/router)
- wiki[Koa] Testing (Supertest, Jest, Vitest)
- wiki[Koa] Koa vs Express
- wiki[Framework] NestJS
- wiki[NestJS] Controllers
- wiki[Node.js] 런타임 개요
💬 댓글