공부하기/React

[React] Koa를 이용한 개발 / 라우터 모듈화 / 컨트롤러 파일 모듈화

다섯자두 2022. 3. 9. 14:57

다음은 책 리액트를 다루는 기술을 읽고 공부한 내용을 바탕으로 작성된 글입니다.


 

기존 src/index.js 파일 하나에 모두 작성한 라우터들을 여러 파일에 분리시켜서 작성하여 모듈화 시킨 뒤, 다시 src/index.js 파일에서 불러와 적용하였다.

 

- 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 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());

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

 

localhost: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,
  };
  // 문자열이 아닌 JSON 객체를 반환, 현재 요청의 메서드, 경로, 파라미터를 담음
};

// posts 라우트에 여러 종류의 라우터를 설정한 후 모두 printInfo 함수를 호출하도록 설정
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 Router = require('koa-router');
const posts = require('./posts');

const api = new Router();

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

// 라우터를 내보낸다.
module.exports = api;

 

localhost:4000/api/posts 경로로 들어가면, 

{"method":"GET","path":"/api/posts","params":{}}

과 같이 뜨는 것을 확인 할 수 있다.

 

GET 메서드를 사용하는 API는 웹 브라우저에서 주소를 입력하여 테스팅할 수 있지만, 이외의 POST, DELETE, PUT, PATCH 메서드를 사용하는 API는 자바스크립트로 호출하여야 한다. 

REST API 요청 테스팅을 쉽게 할 수 있는 프로그램으로는 Postman이 있다.

 


컨트롤러 파일 작성 

라우트를 작성하는 과정에서 특정 경로에 미들웨어를 등록할 때는 다음과 같이 두 번째 인자에 함수를 선언해서 바로 넣어줄 수 있다.

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()); // api 라우트 적용

// 라우터 적용 전에 bodyParser 적용
app.use(bodyParser());

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

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

- posts/posts.ctrl.js

let postId = 1; // id의 초깃값

// posts 배열 초기 데이터
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; // 기존 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;
  }
  // index번째 아이템 제거
  posts.splice(index, 1);
  ctx.status = 204; //No Content
};

/* 포스트 수정
PUT /api/posts/:id
{ title, body }
*/
exports.replace = (ctx) => {
  // PUT 메서드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용됨
  const { id } = ctx.params;
  // 해당 id를 가진 포스트가 몇 번째인지 확인
  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;
  // 해당 id를 가진 포스트가 몇 번째인지 확인
  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];
};

 

위와 같이 

exports.이름 = ... 형식으로 함수를 내보냈다.

이는 다음 형식으로 다른 파일에서 불러올 수 있다.

const 모듈이름 = require('파일이름');

모듈이름.이름();

 

- src/api/posts/index.js

const Router = require('koa-router');
const postsCtrl = require('./posts.ctrl');
// 모듈 불러오기

const posts = new Router();

const printInfo = (ctx) => {
  ctx.body = {
    method: ctx.method,
    path: ctx.path,
    params: ctx.params,
  };
  // 문자열이 아닌 JSON 객체를 반환, 현재 요청의 메서드, 경로, 파라미터를 담음
};

// 모듈에서 exports 한 함수들 사용
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;