[Node.js] koa-router 사용하기

🌊·2021년 12월 30일
0

Node.js

목록 보기
2/3

다른 주소로 요청이 들어올 경우 다른 작업을 처리할 수 있도록 라우터를 사용해야 한다.
Koa 자체에 이 기능이 내장되어 있지 않으므로 koa-router 모듈을 설치해야 한다.

설치

$ yarn add koa-router


기본 사용법

index.js

const Koa = require('koa');
const Router = require('koa-router');

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

// 라우터 설정
router.get('/', (ctx) => {
  ctx.body = '홈';
});

router.get('/about', (ctx) => {
  ctx.body = '소개';
});

// app 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());

app.listen(4000, () => {
  console.log('Listening to port 4000');
});

router.get의 첫 번째 파라미터에는 라우트의 경로가 들어가고, 두 번째 파라미터에는 해당 라우트에 적용할 미들웨어 함수를 넣는다.


라우트 파라미터와 쿼리

라우트 파라미터

라우터의 파라미터를 설정할 때는 /about/:name 형식으로 콜론(:)을 사용하여 라우트 경로를 설정한다.

파라미터가 있을수도 있고 없을 수도 있다면 /about/:name? 같은 형식으로 파라미터 뒤에 물음표를 사용한다.

ctx.params 객체에서 조회할 수 있다.

router.get('/about/:name?', (ctx) => {
  const { name } = ctx.params;
  ctx.body = name ? `${name}의 소개` : '소개';
});

라우트 파라미터 : /about/react

라우트 쿼리

URL 쿼리의 경우에는 /posts/?id=10 같은 형식으로 요청했다면 해당 값을 ctx.query에서 조회할 수 있다.

쿼리 문자열을 자동으로 객체 형태로 파싱해 주므로 별도로 파싱 함수를 돌릴 필요가 없다.

router.get('/posts', (ctx) => {
  const { id } = ctx.query;
  ctx.body = id ? `포스트 #${id}` : '포스트 아이디가 없습니다.';
});

라우트 쿼리 : /posts?id=10

파라미터와 쿼리 둘 다 주소를 통해 특정 값을 받아 올 때 사용하지만, 용도가 조금씩 다르다.

파라미터 : 처리할 작업의 카테고리를 받아 오거나 고유 ID 혹은 이름으로 특정 데이터 조회

쿼리 : 옵션에 관련된 정보를 받아온다. 여러 항목을 리스팅하는 API라면 어떤 조건을 만족하는 항목을 보여줄지 또는 어떤 기준으로 정렬할지를 정해야 할 때 쿼리를 사용한다.


REST API

브라우저에서 데이터베이스에 직접 접속하여 데이터를 변경한다면 보안상에 문제가 될 수 있다.
그래서 REST API를 만들어서 사용한다.

REST API는 요청 종류에 따라 다른 HTTP 메서드를 사용한다.

HTTP 메서드의 종류

  • GET : 데이터를 조회할 때 사용
  • POST : 데이터를 등록할 때 사용, 인증 작업을 거칠 때 사용하기도 한다.
  • DELETE : 데이터를 지울 때 사용
  • PUT : 데이터를 새 정보로 통째로 교체할 때 사용
  • PATCH : 데이터의 특정 필드를 수정할 때 사용

REST API 예시

POST /posts : 포스트 작성
GET /posts : 포스트 목록 조회
GET /posts/:id : 특정 포스트 조회
POST /posts/:id/commnets : 특정 포스트에 덧글 등록


라우트 모듈화

라우터를 분리시켜서 작성하고, 불러와서 적용하는 방법을 알아보자.

src/api/index.js

const Router = require('koa-router');
const api = new Router();

api.get('/test', (ctx) => {
  ctx.body = '성공';
});

// 라우터 내보내기
module.exports = api;

src/index.js

const Koa = require('koa');
const Router = require('koa-router');

const api = require('./api');

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

router.use('/api', api.routes()); // api 라우트 적용

app.use(router.routes()).use(router.allowedMethods());

app.listen(4000, () => {
  console.log('Listening to port 4000');
});

/api/test 경로로 요청하면 성공이라는 문자열이 나타난다.

posts 라우트 생성

src/api/posts/index.js

const Router = require('koa-router');
const posts = new Router();

const printInfo = (ctx) => {
  ctx.body = {
    method: ctx.method,
    path: ctx.path,
    params: ctx.params,
  };
};

posts.get('/', printInfo);
posts.post('/', printInfo);
posts.get('/:id', printInfo);
posts.delete('/:id', printInfo);
posts.put('/:id', printInfo);
posts.patch('/:id', printInfo);

module.exports = posts;

src/api/index.js

const posts = require('./posts');

api.use('/posts', posts.routes());

api/posts 형식으로 요청을 하면 body에 해당 요청에 대한 값을 보여준다.

컨트롤러 파일 작성

router.get('/', ctx => {
}

함수를 선언하지 않고 두 번째 인자에 바로 넣어줄 수 있다.
처리 함수의 코드가 길면 라우터 설정을 한 눈에 보기 힘들다.
라우트 처리 함수들을 다른 파일로 따로 분리해서 관리할 수 있다.

koa-bodyparser 미들웨어 적용

POST, PUT, PATCH 같은 메서드의 Request Body에 JSON 형식으로 데이터를 넣어주면, 이를 파싱하여 서버에서 사용할 수 있게 한다.

$ yarn add koa-bodyparser

src/index.js

const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');

const api = require('./api');

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

router.use('/api', api.routes());

app.use(bodyParser());
app.use(router.routes()).use(router.allowedMethods());

app.listen(4000, () => {
  console.log('Listening to port 4000');
});

주의할 점은 router를 적용하는 코드의 윗부분에서 bodyparser를 적용해야 한다.

컨트롤러 함수 작성

src/api/posts/posts.ctrl.js

let postId = 1;

const posts = [
  {
    id: 1,
    title: '제목',
    body: '내용',
  },
];

// 포스트 작성
// POST /api/posts
// { title, body }
exports.write = (ctx) => {
  // REST API의 Request Body는 ctx.request.body에서 조회할 수 있다.
  const { title, body } = ctx.request.body;
  postId += 1;
  const post = { id: postId, title, body };
  posts.push(post);
  ctx.body = post;
};

// 포스트 목록 조회
// GET /api/posts
exports.list = (ctx) => {
  ctx.body = posts;
};

// 특정 포스트 조회
// GET /api/posts/:id
exports.read = (ctx) => {
  const { id } = ctx.params;
  // 주어진 id 값으로 포스트를 찾는다.
  // 파라미터로 받아 온 값은 문자열 형식이므로
  // 파라미터를 숫자로 변환하거나 비교할 p.id 값을 문자열로 변경해야 한다.
  const post = posts.find((p) => p.id.toString() === id);

  if (!post) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다.',
    };
    return;
  }
  ctx.body = post;
};

// 특정 포스트 제거
// DELETE /api/posts/:id
exports.remove = (ctx) => {
  const { id } = ctx.params;
  // 해당 id를 가진 post가 몇 번째인지 확인.
  const index = posts.findIndex((p) => p.id.toString() === id);

  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다.',
    };
    return;
  }
  posts.slice(index, 1);
  ctx.status = 204;
};

// 포스트 수정(교체)
// PUT /api/posts/:id
// { title, body }
exports.replace = (ctx) => {
  // PUT 메서드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용한다.
  const { id } = ctx.params;
  const index = posts.findIndex((p) => p.id.toString() === id);

  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다.',
    };
    return;
  }
  // 전체 객체를 덮어 씌운다.
  // 따라서 id를 제외한 기존 정보를 날리고 객체를 새로 만든다.
  posts[index] = {
    id,
    ...ctx.request.body,
  };
  ctx.body = posts[index];
};

// 포스트 수정(특정 필드 변경)
// PATCH /api/posts/:id
// { title, body }

exports.update = (ctx) => {
  // PATCH 메서드는 주어진 필드만 교체
  const { id } = ctx.params;
  const index = posts.findIndex((p) => p.id.toString() === id);
  if (index === -1) {
    ctx.status = 404;
    ctx.body = {
      message: '포스트가 존재하지 않습니다.',
    };
    return;
  }
  // 기존 값에 정보를 덮어 씌웁니다.
  posts[index] = {
    ...posts[index],
    ...ctx.request.body,
  };
  ctx.body = posts[index];
};

컨트롤러 함수 라우트 연결

src/api/posts/index.js

const Router = require('koa-router');

const postsCtrl = require('./posts.ctrl');

const posts = new Router();

posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.read);
posts.delete('/:id', postsCtrl.remove);
posts.put('/:id', postsCtrl.replace);
posts.patch('/:id', postsCtrl.update);

module.exports = posts;

Postman을 통해서 Request Body를 넣을 수 있다.
update(PATCH)는 기존 값은 유지하면서 새로 값을 덮어 씌운다.
replace(PUT)은 Request Body로 받은 값이 id를 제외한 모든 값을 대체한다.

0개의 댓글