Koa

Koa는 Express.js 팀이 개발한 새로운 세대의 웹 프레임워크.
Node.js를 위한 더 가벼운 미들웨어 아키텍처를 제공하면서도, 현대적인 JavaScript 기능들을 활용할 수 있도록 설계되었다.
async/await를 기본적으로 지원하여 비동기 코드를 더 우아하게 작성할 수 있게 해주며, 더 작고 표현력 있는 기반을 제공한다.

주요 특징

  1. 비동기 함수 지원: Koa는 async/await를 사용하여 비동기 코드를 간결하게 작성할 수 있다.
  2. 미들웨어 기반 아키텍처: 요청 처리 흐름을 제어하는 미들웨어를 사용하여 유연한 구조를 제공한다.
  3. 경량화: Koa는 기본적으로 미들웨어를 포함하지 않으며, 필요한 기능을 플러그인 형태로 추가할 수 있다.
  4. 컨텍스트 객체: 각 요청에 대해 ctx 객체를 제공하여 요청 및 응답을 쉽게 처리할 수 있다.
  5. 모듈화된 구조: Koa는 다양한 기능을 모듈화하여 필요한 기능만 선택적으로 사용할 수 있다.

장점

  1. 높은 성능: Koa는 미니멀한 디자인 덕분에 빠른 성능을 제공한다.
  2. 개발자 친화적: 직관적인 API와 간결한 코드로 개발자 경험이 향상된다.
  3. 유연성: 필요에 따라 미들웨어를 추가하거나 제거할 수 있어 프로젝트 요구에 맞게 조정 가능하다.
  4. 최신 JavaScript 기능 활용: ES6와 async/await 문법을 통해 현대적인 코드 작성을 지원한다.

단점 및 한계

  1. 작은 커뮤니티: Express.js에 비해 상대적으로 작은 커뮤니티와 생태계를 가지고 있다.
  2. 미들웨어 부족: 기본적으로 제공되는 미들웨어가 없기 때문에 필요한 기능을 직접 구현하거나 외부 라이브러리를 찾아야 한다.
  3. 학습 곡선: 비동기 프로그래밍에 익숙하지 않은 개발자에게는 다소 복잡할 수 있다.

사용 방법

  1. 설치:

    1
    
    npm install koa
    
  2. 기본 서버 생성:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    const Koa = require('koa');
    const app = new Koa();
    
    app.use(async ctx => {
      ctx.body = 'Hello World';
    });
    
    app.listen(3000, () => {
      console.log('Server running on http://localhost:3000');
    });
    

주요 명령어

  • app.use(middleware): 미들웨어 등록
  • app.listen(port): 서버 시작
  • ctx.body: 응답 본문 설정
  • ctx.request: 요청 정보 접근
  • ctx.response: 응답 정보 접근

기본적인 Koa 애플리케이션의 설정과 실행 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');

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

// 에러 처리 미들웨어
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);
  }
});

// 로깅 미들웨어
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});

// body 파싱 미들웨어
app.use(bodyParser());

// 라우터 설정
router.get('/', async (ctx) => {
  ctx.body = { message: 'Welcome to Koa API' };
});

// 사용자 관리 API 예시
let users = new Map();

router.post('/users', async (ctx) => {
  const user = ctx.request.body;
  const id = users.size + 1;
  user.id = id;
  users.set(id, user);
  ctx.status = 201;
  ctx.body = user;
});

router.get('/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  const user = users.get(id);
  
  if (!user) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }
  
  ctx.body = user;
});

router.put('/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  const updatedUser = ctx.request.body;
  
  if (!users.has(id)) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }
  
  updatedUser.id = id;
  users.set(id, updatedUser);
  ctx.body = updatedUser;
});

router.delete('/users/:id', async (ctx) => {
  const id = parseInt(ctx.params.id);
  
  if (!users.has(id)) {
    ctx.status = 404;
    ctx.body = { error: 'User not found' };
    return;
  }
  
  users.delete(id);
  ctx.status = 204;
});

// 라우터 미들웨어 등록
app.use(router.routes());
app.use(router.allowedMethods());

// 서버 시작
app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Koa의 고급 기능

커스텀 미들웨어와 컨텍스트 확장

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const Koa = require('koa');
const app = new Koa();

// 커스텀 미들웨어: 인증 체크
const authMiddleware = async (ctx, next) => {
  const token = ctx.get('Authorization');
  
  if (!token) {
    ctx.status = 401;
    ctx.body = { error: 'Authentication required' };
    return;
  }
  
  // 토큰 검증 로직을 여기에 추가
  ctx.state.user = { id: 1, name: 'Test User' };
  await next();
};

// 컨텍스트 확장
app.context.sendError = function(message, status = 400) {
  this.status = status;
  this.body = { error: message };
};

app.context.sendSuccess = function(data, status = 200) {
  this.status = status;
  this.body = { success: true, data };
};

// 미들웨어 체이닝 예시
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// 보안 미들웨어 예시
app.use(async (ctx, next) => {
  ctx.set('X-XSS-Protection', '1; mode=block');
  ctx.set('X-Frame-Options', 'DENY');
  ctx.set('X-Content-Type-Options', 'nosniff');
  await next();
});

// 에러 처리
app.on('error', (err, ctx) => {
  console.error('server error', err);
});

파일 업로드 처리 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa');
const multer = require('@koa/multer');
const Router = require('@koa/router');

const app = new Koa();
const router = new Router();
const upload = multer({ 
  dest: 'uploads/',
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB 제한
  }
});

router.post('/upload', upload.single('file'), async (ctx) => {
  ctx.body = {
    filename: ctx.file.filename,
    size: ctx.file.size,
    mimetype: ctx.file.mimetype
  };
});

세션 처리 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const session = require('koa-session');

app.keys = ['some secret key']; // 쿠키 서명을 위한 키

const CONFIG = {
  key: 'koa.sess',
  maxAge: 86400000,
  autoCommit: true,
  overwrite: true,
  httpOnly: true,
  signed: true,
  rolling: false,
  renew: false,
};

app.use(session(CONFIG, app));

router.get('/session-test', async (ctx) => {
  // 세션 데이터 설정
  ctx.session.views = (ctx.session.views || 0) + 1;
  ctx.body = {
    views: ctx.session.views
  };
});

참고 및 출처

Koa - next generation web framework for node.js