Koa 기본 사용법 - 2

Jonghan·2020년 4월 27일

koa

목록 보기
2/3
post-thumbnail

koa-router

리액트의 경우 프론트엔드임에도 불구하고 주소에 따라 다른 화면을 보여줘야하는 라우팅이 반드시 필요하다. 왜냐하면 SPA이기 때문에, 주소에 따라 다른 화면을 보여주도록 하지 않으면 계속 동일한 HTML파일을 이용하고 내용물은 전부 JavaScript코드가 바꾸는 것이기 때문에 화면이 바뀌지 않는다.

원래 이런 라우팅의 개념은 사실 백엔드가 주로 쓰는 개념들이다.
사용자가 입력한 url에 따라 어떤 화면을 보여줄지를 원래 SPA등장 이전에는 전부 서버에서 관리하는 게 일반적이었으니까.

따라서 코아 역시 라우팅 라이브러리가 존재한다.
코아에서 지원하는건 미들웨어 뿐이므로, 라이브러리로 이런 기능들을 추가적으로 설치해줘야 한다고 했었다.

yarn add koa-router 와 같은 명령어로 간단히 설치할 수 있다.(npm이면 install사용)

지난 시간에 작성했던 코드를 다음과 같이 깔끔하게 남겨두고 나머지는 지우도록 해보자.

const Koa = require('koa');

const app = new Koa();

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

라우터 라이브러리를 설치했으니, 그것을 가져와야 한다. Koa밑에 require()문을 작성하자.

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

라우터 라이브러리를 사용하게되면 Express를 접해본 유저라면 쉽게 이해할 수 있는, HTTP METHOD를 사용할 수 있게 된다.

Router.get('/', ctx=>{});
다음과 같이 get, post, patch, deleteRouter.뒤에 HTTP METHOD를 사용할 수 있고, 매개변수로는 첫번째는 라우트의 경로, 두번째는 해당 라우트에 적용할 미들웨어 함수를 넣는다.

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

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

router.get('/', (ctx) => (ctx.body = 'hello world!'));
app.use(router.routes());
app.listen(4000, () => console.log('Listening to port 4000'));

다음과 같이 작성해보자.

문제 없이 결과가 출력이 되었는데, 다른 라우트의 경로를 집어넣어보자.
router.get('/genius', (ctx) => (ctx.body = 'Yes Genius!'));
위 문장을 router.get('/', ~);밑에 삽입해주자.

그리고 경로를 아래처럼 하면 아주 잘 출력이 된다.

라우트 파라미터와 쿼리

이 부분은 SPA에서 설명했던 경로와 동일한데, 파라미터의 경우는 /:value를 쓰면 되고, 값이 있을수도 있고 없을수도 있다면 (안녕하세요 안철ㅅ입니다.) /:value?이런식으로 쓰면 된다.

리액트 라우터에선 이렇게 넘겨진 경로를 match.params를 통해 조회할 수 있었는데, 코아 라우터에서는 ctx.params로 조회할 수 있다.

쿼리, 쿼리스트링의 경우는 ctx.query, ctx.querystring을 사용한다.
ctx로 쿼리와 파라미터를 통일한게 아주 마음에 든다. 리 뭐시기와는 달라서 좋다.

아까 예시로 들었던 /genius쪽을 다음과 같이 고쳐보자

router.get('/genius/:who', (ctx) => {
  const { who } = ctx.params;
  ctx.body = `Yes ${who} is Genius!`;
});

who를 비구조화 할당으로 받아왔고, literal template에 쓰이는 백틱을 사용했다.


그러자 url에 따라 아주 잘 받아오는 모습을 볼 수 있다.
만약 입력값이 없다면? 아까처럼 ?를 붙여줘야겠지.

router.get('/genius/:who?', (ctx) => {
  const { who } = ctx.params;
  ctx.body = who ? `Yes ${who} is Genius!` : 'I am Genius';
});

위와 같이 말이다.

입력값을 주지 않아도 잘 출력되는 모습

쿼리도 활용해보겠다.

router.get('/genius/:who?', (ctx) => {
  const { who } = ctx.params;
  const { age } = ctx.query;
  let string = who ? `Yes ${who} is Genius!` : 'I am Genius';
  string += age ? ` and I'm ${age}` : '';
  ctx.body = string;
});

이런식으로 작성해보고 직접 결과를 확인해보자.

용도는 리액트 라우터때도 적었지만,
파라미터는 카테고리, 고유 ID, 특정 데이터 조회등에 사용한다.

쿼리는 옵션에 관련된 정보를 받아올 때 사용한다.

예를 들면 리스트를 출력하는데 최대 몇 개까지 출력할건지, 정렬은 최신순으로 할건지 등을 위해서 사용한다. 동일한 페이지여도, 쿼리값에 의해 결과가 다르게 나올 수 있다.

REST API

SPA에서는 JSON을 통해 웹 페이지의 화면을 갱신하고 이 JSON을 서버로부터 받아오기 위해서는 AJAX를 사용해야 한다. 물론 AJAX를 고대로 사용하는 것은 아니고, 리액트의 경우는 axios같은 라이브러리가 준비되어 있다.

REST API는 진짜 딱 간단하게 한줄로 요약해보면 다음과 같다.

클라이언트로부터 오는 요청을 서버에서 처리하는데, 이 과정에서 URLHTTP METHOD를 이용한다.

예를 들면 get을 사용할 경우, 서버는 데이터베이스에 정보를 받아와 사용자에게 정보를 보내주고, post를 사용할 경우, 사용자가 보내는 정보를 데이터베이스에 저장해준다. 같은 URL을 사용할지라도 HTTP METHOD가 다르다면, 다른 동작을 한다. 이것이 REST API인 것이다.

애초에 웹 사이트들의 거의 대부분은 CRUD구조로 이뤄져 있기 때문에 이게 유용한것인데 CRUDCREATE, READ, UPDATE, DELETE의 약자이다.

게시물을 게시하기, 불러오기, 수정하기, 삭제하기
댓글을 달기, 불러오기, 수정하기, 삭제하기
아이디를 생성하기, 로그인하기, 회원정보를 수정하기, 삭제하기

전부다 이 CRUD의 패턴을 가진다.

Modularization

Express에서도 쓰는 모듈화인데, index.js에 모든 라우트 경로를 적다보면, 코드가 너무 길어져 유지보수가 힘들어진다. 따라서 라우트를 여러 파일에 나눠 작성하고, 불러와서 index.js에서 사용한다. 이것을 모듈화라고 보통 부른다.

보통은 api라는 폴더를 만들고, 안에 각종 라우트를 만들어준 다음 module.exports를 통해 내보내주게 된다. 그리고, index.js에서 이렇게 만들어진 모듈들을 require()를 통해 가져와서 사용하게 된다.

아까 라우터를 적용할때도 router.routes()라는 routes()함수를 사용했는데 apirequire()를 통해 가져온다고 하면 api.routes()를 해줘야 한다.

// src/api/index.js
const Router = require('koa-router');
const api = new Routes();

api.get('/test', ctx => ctx.body = 'hi');
module.exports = api;

// src/index.js
(...중략)
const api = require('./api');
(...중략)
router.use('/api', api.routes());

위 코드처럼 짜면 다음 URL입력시 hi가 출력된다.

localhost:4000까지는 모두다 동일한데, 뒤에 들어올 라우트에 따라 각각 모듈화 해서 관리할 수 있다. 그렇게하면 위처럼 localhost:4000/api에 관련된 /api/test 같이 뒤에 주소를 더 추가해서 붙이는 것들은 전부 src/api/index.js 쪽에서 처리하면 된다. 즉 src/index.js에서 모든걸 처리할 필요가 없어진다는 얘기다.

REST API 실습

어느 정도 koa에 감을 잡았으니 이제 REST API를 직접 구현해볼 차례다.

api 디렉토리 안에 posts를 만들어주고, 그 안에 index.js를 만들어준다.
그럼 이제 /api/postsURL에 들어가면 그에 관련된 모든 작업을 저기서 처리할 수 있다.

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

module.exports = posts;

항상 이거부터 먼저 작성하는 버릇을 들이면 좋을 것 같다.

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

const info = (ctx) => {
  ctx.body = ctx;
};

posts.get('/', info); // HTTP METHOD: GET
posts.post('/', info); // HTTP METHOD: POST
module.exports = posts;

라우트에 관련된 코드들을 추가해준다.
같은 미들웨어인 info를 실행시켜도 HTTP METHOD가 다르다.

localhost:4000/api/posts를 입력해야 결과가 뜨도록 하려면 src/api폴더에서 만들었던 index.js에서 이 라우트를 처리해줘야 한다.

src/index.js에서는 localhost:4000/이부분 즉 1단계를 처리해주고

src/api처럼 localhost:4000/api/이부분 2단계를 처리하려면 위처럼 api안에 또다른 폴더(여기선 posts)를 만들어주고, src/api/index.js에서 처리해주면 된다.

// src/api/index.js
const Router = require('koa-router');
const api = new Router();
const posts = require('./posts');

api.get('/test', (ctx) => (ctx.body = 'hi'));
api.use('/posts', posts.routes());

module.exports = api;

이렇게 해주면 localhost:4000/api/posts를 입력시 /src/index.js에서 시작해서 /src/api/index.js를 거쳐, src/api/posts/index.js까지 간다.

결과값은 아래와 같다.
{"request":{"method":"GET","url":"/api/posts","header":{"host":"localhost:4000","connection":"keep-alive","cache-control":"max-age=0","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","sec-fetch-site":"none","sec-fetch-mode":"navigate","sec-fetch-user":"?1","sec-fetch-dest":"document","accept-encoding":"gzip, deflate, br","accept-language":"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7","cookie":"access_token="}},"response":{"status":200,"message":"OK","header":{"content-type":"application/json; charset=utf-8"}},"app":{"subdomainOffset":2,"proxy":false,"env":"development"},"originalUrl":"/api/posts","req":"<original node req>","res":"<original node res>","socket":"<original node socket>"}

ctx.body를 통해 출력해보니 ctx에는 method, status, url, header등등 각종 웹 통신에 필요한 내용들이 전부 담겨있다.

근데 웹 브라우저에서는 post를 할 수 있는 조건이 까다롭다. form같은 걸로 데이터를 입력하고 제출해야지만 HTTP METHODpost가 된다. 그게 싫다면 자바스크립트를 사용해야 하는데, 번거로우니 그냥 POSTMAN을 깔자.

인터넷에 워낙 자세히 설명되있는데가 많으니 그냥 실행 결과만 보여주겠다.


형광펜으로 칠한 부분을 보면, methodPOST임을 알 수 있다.

profile
열정적으로 살고 싶다.

0개의 댓글