본문 바로가기
BE/node

node Koa 프레임워크 알아보기

by livemehere 2023. 1. 24.

nestjs 를 사용해보면서 엔터프라이즈에는 적합하다고 생각하지만, 기본 패키지가 무겁다는 것은 부정할 수 없었습니다.

그에 비하면 express, koa, fasfity 는 경량의 프레임워크 입니다. 10줄 안팎으로 서버를 구축할 수 있습니다.

express 는 가장 큰 커뮤니티가 있다는 장점이있지만, koa 와 fasfity 와는 비교적 오래되었기 때문에 후자가 사용경험이 꽤나 괜찮았습니다.

한번쯤 공식문서를 정독해보고 싶어서, 학습하면서 글을 남겨봅니다.

 

Koa 란

koa 는 express 와 동일하게 요청에 대해서 미들웨어를 배열과 같이 순차적으로 적용시켜 처리합니다.

express 는 기본적인 미들웨어를 번들로 가지고있지만, koa 는 어떤 미들웨어도 번들로 제공하지 않기 때문에 훨씬 더 경량입니다.

express 의 경우에는 미들웨어를 콜백으로 구현하였지만, koa 는 비동기를 사용해 미들웨어를 구현하면서,

하나의 return 이 이루어지면 제어흐름이 끝나는 것 과 달리 downstream 의 호출이 끝나면 upstream 으로 다시 제어할 수 있습니다.

 

아래 코드는 요청마다 응답 시간을 측정해 응답 헤더에 추가하고, 다른 미들웨어에서는 그것을 받아 로그를 남기는 예시입니다.

import Koa from 'koa';

const app = new Koa();

// logger
app.use(async (ctx,next)=>{
    await next();
    const time = ctx.response.get('RESPONSE-TIME')
    console.log(`${ctx.request.method} ${ctx.url} - ${time}`)
})

// response time
app.use(async (ctx,next)=>{
    const start = Date.now();
    await next()
    ctx.set('RESPONSE-TIME',`${Date.now() - start}ms`)
})

app.use(async (ctx,next)=>{
    ctx.body = 'hello world';
})

app.listen(3000);

 

Settings

Koa App 에 대한 세팅은 app 인스턴스에 내장되어있고, 생성자 혹은 property로 접근, 수정할 수 있습니다.

자주 쓰일 두개만 예시로,

app.env 는 default 값으로 development 이고, NODE_ENV 값이 있다면 반영합니다.

app.keys 는 signed cookie 의 키들의 배열입니다.

console.log(app.env)
console.log(app.keys)

Signed Cookie 예시

https://github.com/pillarjs/cookies

const app = new Koa();
app.keys = ['hi']
app.use(async (ctx,next)=>{
    await next()
    ctx.cookies.set('nn','1234',{signed:true})
    console.log(ctx.cookies.get('nn'))
})

app.context

app.context 프로퍼티는 ctx 의 prototype 입니다.

전체 앱에서 접근할 수 있는 전역 프로퍼티인 샘이고, 안티패턴이라고들 하지만 아래와 같이 사용할 수 있습니다.

app.context.db = 'db'

app.use(async (ctx,next)=>{
    console.log(app.context.db)
    ctx.body = 'hello world';
})

 

ctx 의 대부분의 프로퍼티는 getter, setter 로 정의되어 있기 때문에, 수정하려면 app.context 에다가 Object.definedproperty() 를 사용해야합니다.

Error Handling

app.slient = true

err.status == 404

인 경우를 제외하고는 error 이벤트 핸들러에 전달되게 됩니다.

ctx 또한 두번째 인자로 전달 합니다.

app.on('error',(error,ctx)=>{
    console.log(error,ctx)
})
node 가 죽지않고 응답할 수 있는 경우 클라이언트에게는 500 응답을 전달하고, node 앱에서는 error 이벤트를 emit 합니다.

ctx

ctx 는 요청마다 생성되기 때문에 미들웨어간에 데이터 전달 용도로 사용될 수 도 있습니다.

예를들면 아래처럼 다른 미들웨어에서 어떤 상태값을 저장하고 다음 미들웨어로 넘기고, 그 미들웨어에서는 그 상태값을 참조할 수 있습니다.

app.use(async(ctx,next)=>{
    await next()
    console.log(ctx.state)
})

app.use(async (ctx,next)=>{
    ctx.state = 'hi'
    ctx.body = 'hello world';
})
미들웨어의 순서는 후 순위 일 수록 먼저 실행 됩니다

 

ctx.app.emit

내장된 EventEmitter 를 사용할 수 있습니다.

app.use(async (ctx,next)=>{
    ctx.app.emit('logger','HI')
})

app.on('logger',(d)=>{
    console.log(d)
})

의도적 error trhow

일반 Error 객체에 status 프로퍼티만 있다면 koa 에서 제공하는 에러 응답과 같아 집니다.

app.use(async (ctx,next)=>{
    // 1
    ctx.throw(404, 'not found')

    // 2
    const err = new Error('BAD')
    err.status = 400;
    err.expose = {
        user:'kong'
    };
    throw err

})

alias

request

  • ctx.header
  • ctx.headers
  • ctx.method
  • ctx.method=
  • ctx.url
  • ctx.url=
  • ctx.originalUrl
  • ctx.origin
  • ctx.href
  • ctx.path
  • ctx.path=
  • ctx.query
  • ctx.query=
  • ctx.querystring
  • ctx.querystring=
  • ctx.host
  • ctx.hostname
  • ctx.fresh
  • ctx.stale
  • ctx.socket
  • ctx.protocol
  • ctx.secure
  • ctx.ip
  • ctx.ips
  • ctx.subdomains
  • ctx.is()
  • ctx.accepts()
  • ctx.acceptsEncodings()
  • ctx.acceptsCharsets()
  • ctx.acceptsLanguages()
  • ctx.get()

response

  • ctx.body
  • ctx.body=
  • ctx.status
  • ctx.status=
  • ctx.message
  • ctx.message=
  • ctx.length=
  • ctx.length
  • ctx.type=
  • ctx.type
  • ctx.headerSent
  • ctx.redirect()
  • ctx.attachment()
  • ctx.set()
  • ctx.append()
  • ctx.remove()
  • ctx.lastModified=
  • ctx.etag=

@koa/router 

express 에서 기본적으로 제공해주는 router 조차 제공해주지 않기 때문에, 해당 모듈을 설치해야 라우터를 편하게 구성할 수 있습니다.

body 파싱을 위해 koa-body 모듈도 미들웨어로 추가해줍니다

 

router/userRouter.js

import Router from "@koa/router";

const userRouter = new Router()

const user = [];

userRouter.get('/',async (ctx,next)=>{
    ctx.body = user;
})

userRouter.post('/',async ctx =>{
    user.push(ctx.request.body)
    ctx.body = 'ok'
})

export default userRouter;

index.js

import Router from "@koa/router";
import {koaBody} from 'koa-body'

app.use(koaBody())
router.use('/users',auth,userRouter.routes())

koa 를 express 개발자분들이 만들었기 때문에 95퍼센트 동일한 느낌을 받았습니다.

다반 비동기 기반이라는 koa 만의 장점과 번들 소스가 작아 필요한 대로 가져다 쓰는 점 등이 사용성은 훨 씬 좋았습니다.

미들웨어라는 개념만 이해한다면 크게 어려움 없이 접근할 수 있는 가벼운 프레임워크라 생각이 듭니다.

할 말이 없을 정도로.. 아주 가볍고 좋네요! :)

 

2023.02.23

- koa 가 번들 미들웨어를 포함하지 않음으로써, 이것저것 필요한걸 추가로 설치하는데.. 이러다가 express 되겠다는 생각?.. 미들웨어 비동기 제어말고는 경량이라는 것이 장점이 될 수 있을까? 싶은생각이 든다..!

반응형