728x90
반응형
1. Koa.js란
Koa.js는 Express.js 팀이 만든 차세대 웹 프레임워크입니다. async/await을 기본으로 사용하며, 더 작고 표현력 있는 미들웨어 구조를 제공합니다. Express보다 가벼우며 내장 기능이 적어 필요한 것만 선택적으로 추가합니다.
npm install koa
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
ctx.body = 'Hello Koa!';
});
app.listen(3000);
console.log('서버 실행 중: http://localhost:3000');
2. 컨텍스트(Context)
Koa는 요청과 응답을 하나의 ctx 객체로 캡슐화합니다.
2.1 ctx 객체
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
// 요청 정보
console.log(ctx.method); // GET
console.log(ctx.url); // /users?page=1
console.log(ctx.path); // /users
console.log(ctx.query); // { page: '1' }
console.log(ctx.host); // localhost:3000
console.log(ctx.protocol); // http
console.log(ctx.ip); // 127.0.0.1
console.log(ctx.headers); // 요청 헤더
// 응답 설정
ctx.status = 200;
ctx.type = 'application/json';
ctx.body = { message: 'Hello' };
// 헤더 설정
ctx.set('X-Custom-Header', 'value');
// 축약 접근
ctx.request; // Koa Request 객체
ctx.response; // Koa Response 객체
ctx.req; // Node.js request
ctx.res; // Node.js response
});
app.listen(3000);
2.2 요청 객체
app.use(async (ctx) => {
const { request } = ctx;
// 요청 정보
console.log(request.method);
console.log(request.url);
console.log(request.header);
console.log(request.query);
console.log(request.querystring);
// 헤더 접근
console.log(request.get('User-Agent'));
// Content-Type 확인
console.log(request.is('json'));
console.log(request.is('html'));
ctx.body = 'OK';
});
2.3 응답 객체
app.use(async (ctx) => {
const { response } = ctx;
// 상태 코드
response.status = 201;
// 응답 본문
response.body = { created: true };
// 헤더
response.set('Cache-Control', 'no-cache');
response.type = 'application/json';
// 리다이렉트
// response.redirect('/other');
});
3. 미들웨어
3.1 미들웨어 기본
Koa의 미들웨어는 양파 모델(Onion Model)을 따릅니다.
const Koa = require('koa');
const app = new Koa();
// 첫 번째 미들웨어
app.use(async (ctx, next) => {
console.log('1. 요청 시작');
await next(); // 다음 미들웨어 실행
console.log('6. 요청 종료');
});
// 두 번째 미들웨어
app.use(async (ctx, next) => {
console.log('2. 처리 전');
await next();
console.log('5. 처리 후');
});
// 세 번째 미들웨어
app.use(async (ctx) => {
console.log('3. 핸들러 시작');
ctx.body = 'Hello';
console.log('4. 핸들러 종료');
});
// 실행 순서: 1 → 2 → 3 → 4 → 5 → 6
app.listen(3000);
3.2 로깅 미들웨어
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
3.3 에러 처리 미들웨어
// 에러 핸들러 (첫 번째에 위치)
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
error: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
};
ctx.app.emit('error', err, ctx);
}
});
// 에러 이벤트 리스너
app.on('error', (err, ctx) => {
console.error('서버 에러:', err);
});
3.4 조건부 미들웨어
// 특정 경로에만 적용
function unless(paths, middleware) {
return async (ctx, next) => {
if (paths.includes(ctx.path)) {
return next();
}
return middleware(ctx, next);
};
}
const authMiddleware = async (ctx, next) => {
if (!ctx.headers.authorization) {
ctx.throw(401, 'Unauthorized');
}
await next();
};
app.use(unless(['/login', '/register'], authMiddleware));
4. 라우팅
Koa는 라우터가 내장되어 있지 않아 @koa/router를 사용합니다.
npm install @koa/router
4.1 기본 라우팅
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
router.get('/', (ctx) => {
ctx.body = 'Home';
});
router.get('/users', (ctx) => {
ctx.body = [{ id: 1, name: '홍길동' }];
});
router.get('/users/:id', (ctx) => {
ctx.body = { id: ctx.params.id, name: '홍길동' };
});
router.post('/users', (ctx) => {
ctx.status = 201;
ctx.body = { id: 1, ...ctx.request.body };
});
router.put('/users/:id', (ctx) => {
ctx.body = { id: ctx.params.id, ...ctx.request.body };
});
router.delete('/users/:id', (ctx) => {
ctx.status = 204;
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);
4.2 라우터 프리픽스와 중첩
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
// API 라우터
const apiRouter = new Router({ prefix: '/api' });
// 사용자 라우터
const userRouter = new Router({ prefix: '/users' });
userRouter.get('/', (ctx) => { ctx.body = []; });
userRouter.get('/:id', (ctx) => { ctx.body = {}; });
// 게시글 라우터
const postRouter = new Router({ prefix: '/posts' });
postRouter.get('/', (ctx) => { ctx.body = []; });
// 라우터 중첩
apiRouter.use(userRouter.routes(), userRouter.allowedMethods());
apiRouter.use(postRouter.routes(), postRouter.allowedMethods());
app.use(apiRouter.routes());
app.use(apiRouter.allowedMethods());
// 결과: /api/users, /api/users/:id, /api/posts
app.listen(3000);
4.3 라우트별 미들웨어
const authenticate = async (ctx, next) => {
if (!ctx.headers.authorization) {
ctx.throw(401);
}
await next();
};
router.get('/public', (ctx) => {
ctx.body = '공개 페이지';
});
router.get('/private', authenticate, (ctx) => {
ctx.body = '비공개 페이지';
});
// 여러 미들웨어 체인
router.post('/users',
authenticate,
validateBody,
async (ctx) => {
ctx.body = { created: true };
}
);
5. 요청 본문 파싱
npm install koa-bodyparser
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());
router.post('/users', (ctx) => {
const { name, email } = ctx.request.body;
ctx.body = { name, email };
});
app.use(router.routes());
app.listen(3000);
6. 정적 파일 서빙
npm install koa-static
const Koa = require('koa');
const serve = require('koa-static');
const path = require('path');
const app = new Koa();
// public 폴더의 정적 파일 서빙
app.use(serve(path.join(__dirname, 'public')));
app.listen(3000);
7. 템플릿 렌더링
npm install koa-views ejs
const Koa = require('koa');
const views = require('koa-views');
const path = require('path');
const app = new Koa();
app.use(views(path.join(__dirname, 'views'), {
extension: 'ejs'
}));
app.use(async (ctx) => {
await ctx.render('index', {
title: 'Koa 애플리케이션',
users: [{ name: '홍길동' }]
});
});
app.listen(3000);
8. 실전 API 서버
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');
const cors = require('@koa/cors');
const app = new Koa();
const router = new Router({ prefix: '/api' });
// 데이터 저장소
const users = new Map();
let nextId = 1;
// 미들웨어
app.use(cors());
app.use(bodyParser());
// 로깅
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} ${ctx.status} - ${ms}ms`);
});
// 에러 핸들러
app.use(async (ctx, next) => {
try {
await next();
if (ctx.status === 404) {
ctx.throw(404, 'Not Found');
}
} catch (err) {
ctx.status = err.status || 500;
ctx.body = { error: err.message };
}
});
// 라우트
router.get('/users', (ctx) => {
ctx.body = Array.from(users.values());
});
router.get('/users/:id', (ctx) => {
const user = users.get(Number(ctx.params.id));
if (!user) {
ctx.throw(404, 'User not found');
}
ctx.body = user;
});
router.post('/users', (ctx) => {
const { name, email } = ctx.request.body;
if (!name || !email) {
ctx.throw(400, 'Name and email required');
}
const user = { id: nextId++, name, email };
users.set(user.id, user);
ctx.status = 201;
ctx.body = user;
});
router.put('/users/:id', (ctx) => {
const id = Number(ctx.params.id);
if (!users.has(id)) {
ctx.throw(404, 'User not found');
}
const user = { ...users.get(id), ...ctx.request.body, id };
users.set(id, user);
ctx.body = user;
});
router.delete('/users/:id', (ctx) => {
const id = Number(ctx.params.id);
if (!users.delete(id)) {
ctx.throw(404, 'User not found');
}
ctx.status = 204;
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, () => {
console.log('Koa API 서버 실행 중');
});
9. Koa vs Express
| 특징 | Koa | Express |
|---|---|---|
| 미들웨어 | async/await 기반 | 콜백 기반 |
| 내장 기능 | 최소한 | 라우터, 정적 파일 등 포함 |
| 번들 크기 | 더 작음 | 더 큼 |
| 에러 처리 | try/catch로 간편 | next(err) 패턴 |
| 컨텍스트 | ctx 객체 | req, res 분리 |
결론
Koa.js는 async/await을 기본으로 사용하는 모던한 웹 프레임워크입니다. 양파 모델의 미들웨어 구조로 요청/응답 흐름을 직관적으로 제어할 수 있고, ctx 객체로 요청과 응답을 통합 관리합니다. Express보다 가볍고 유연하지만, 라우터와 바디 파서 같은 기본 기능도 별도 패키지로 설치해야 합니다. 모던 JavaScript를 선호하고 미니멀한 프레임워크를 원한다면 Koa가 좋은 선택입니다.
728x90
반응형
'Node.js' 카테고리의 다른 글
| Node.js의 HTTP 클라이언트 만들기 (0) | 2026.03.08 |
|---|---|
| Node.js의 HTTP 서버 만들기 (0) | 2026.03.08 |
| Node.js의 파일 스트림 처리 (0) | 2026.03.07 |
| Node.js의 디렉토리 생성 및 삭제 (0) | 2026.03.07 |
| Node.js의 파일 삭제 및 이동 (0) | 2026.03.02 |