TIL 22-05-14

thisisyjin·2022년 5월 14일
0

TIL 📝

목록 보기
48/113

React.js

Today I Learned ... react.js

🙋‍♂️ *Reference Lecture

🙋‍ My Dev Blog


CH 21. 백엔드 프로그래밍

  • Node.js의 Koa 프레임워크

Node.js

  • 구글의 V8 엔진을 기반으로 한 서버에서도 자바스크립트를 사용할 수 있는 런타임.

Koa 프레임워크

  • node.js 환경에서는 보통 Express, Hapi, Koa 등의 웹 프레임워크를 사용함.
  • Koa는 Express 개발 팀이 개선하여 새로 만든 프레임워크임.
  • Koa는 Express와는 달리 미들웨어 기능만 존재함.
    (Express는 미들웨어, 라우팅, 템플릿, 파일 호스팅 등 다양한 기능이 내장)
  • Koa는 필요한 기능만 붙여서 서버를 만들 수 있으므로 훨씬 가벼움.
  • 또한, async/await 문법을 정식으로 지원하여 더 편하게 관리 가능.

작업 환경 준비

yarn init

$ mkdir blog
$ cd blog
$ mkdir blog-backend
$ cd blog-backend
$ yarn init -y
  • 추후 진행할 프론트엔드 프로젝트인 블로그 서비스와 연동할 서버를 생성할 것임.
  • yarn init -y를 하면 기본값으로 저장된 package.json이 해당 디렉터리에 저장됨.

koa 라이브러리 설치

$ yarn add koa

ESLint, Prettier 설정

eslint 패키지 설치

$ yarn add --dev eslint
$ yarn run eslint --init
  • ESLint를 설치하고, yarn run eslint --init을 해준다.

위와 같이하고 나면, 디렉터리에 아래 파일이 생성된다.

.eslintrc.json

{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "rules": {
    }
}
  • 그 다음은 Prettier 설정을 해준다.
    (기존 방법대로 .prettierrc 파일 생성)
{
  "singleQuote": true,
  "semi": true,
  "useTabs": false,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 80
}

eslint-config-prettier 패키지 설치

  • prettier에서 관리하는 코드 스타일을 EsLint에서 관리하지 않도록 하는 패키지.
$ yarn add eslint-config-prettier

.eslintrc.json (수정)

{
  "env": {
    "commonjs": true,
    "es6": true,
    "node": true
  },
  "extends": ["eslint:recommended", "prettier"],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parserOptions": {
    "ecmaVersion": 2018
  },
  "rules": {}
}

src 디렉터리

blog-backend 디렉터리에 /src 디렉터리를 생성하고, index.js 파일에서 테스트 해보자.

const hello = "hello";

위와 같이 변수를 선언하고 사용하지 않으면, ESLint에서는 에러로 간주한다.

위와 같은 규칙을 끄기 위해서는 .eslintrc.json의 'rules' 필드를 아래와 같이 수정해주면 된다.

	...

  "rules": {
      "no-unused-vars": "warn",
      "no-console": "off"
  }

참고로, ESLint Rules 에 대한 설명은 공식문서를 참고하자.


Koa 기본 사용법

서버 열기

index.js

const Koa = require('koa');
const app = new Koa();

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

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

참고 - 왜 import - from이 아닌 require을 사용하는지>

  • 이전에는 CRA환경이므로 바벨이 설정이 되어있어 es6 문법인 import를 사용 가능했다.
    그러나 이번에는 nodeJS 환경에서 진행되므로, commonJS인 require을 사용해야 한다.
  • 서버를 포트 4000번으로 열고, 서버에 접속시 'hello world' 라는 문구를 반환하도록 함.
node src

-> 파일명이 index.js 이므로 폴더명만 적으면 알아서 실행됨.

-> app.listen()이 실행되어 두번째 인자인 콜백함수가 실행됨. (console.log)

이제 웹 브라우저로 localhost:4000에 접속해보면 아래와 같이 hello world 라는 문구가 보임.

미들웨어

Koa 어플리케이션은 미들웨어의 배열로 구성되어 있음.

위에서 사용했던 app.use() 함수는 미들웨어 함수를 어플리케이션에 등록하는 기능임.

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

미들웨어 함수

  • 미들웨어 함수는 두개의 파라미터를 받음.
  • 첫번째 파라미터 ctx는 웹 요청(request)과 응답(response)에 대한 정보를 지니고,
  • 두번째 파라미터 next는 현재 처리중인 미들웨어의 다음 미들웨어를 호출하는 함수임.

미들웨어를 등록하고 next 함수를 호출하지 않으면,그 다음 미들웨어를 처리하지 않음.

✅ 참고 - next를 사용하지 않을 때
미들웨어를 처리할 필요가 없는 라우트 미들웨어를 나중에 설정할 때 주로 next를 생략함.

  • 미들웨어는 app.use를 사용하여 등록되는 순서대로 처리됨.

다음은 현재 요청을 받은 주소와 숫자를 기록하는 두개의 미들웨어를 담은 코드임.

index.js (수정)

const Koa = require('koa');
const app = new Koa();

app.use((ctx, next) => {
  console.log(ctx.url);
  console.log(1);
  next();
});

app.use((ctx, next) => {
  console.log(2);
  next();
});

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

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

다시 node src를 실행해보면 아래와 같다.

크롬 브라우저는 사이트의 아이콘 파일인 /favicon.ico 파일을 서버에 요청하기 때문에, /경로와 더불어 /favicon.ico 경로도 나타나게 됨.

console.log(ctx.url);

이번에는 app.use()에서 첫번째 next()를 주석처리한 후 결과를 살펴보자.

이번에는 첫번째 미들웨어의 결과인 1까지만 출력이 되고, 두번째 미들웨어는 모두 무시된 것을 알 수 있다.


이와 같은 성질을 이용하여 조건부로 다음 미들웨어 처리를 무시하게 할 수 있다.

const Koa = require('koa');
const app = new Koa();

app.use((ctx, next) => {
  console.log(ctx.url);
  console.log(1);
  if (ctx.query.authorized !== '1') {
    ctx.status = 401; // Unauthorized
    return;
  }
  next();
});

...

request의 경로에 authorized=1 이라는 쿼리 파라미터가 없을 때는, 이후 미들웨어를 처리하지 않음.

-> return으로 use() 함수를 빠져나와서 다음 코드인 next()가 실행되지 않음.

  • 주소의 쿼리 파라미터를 authorized=1로 적어주면?
    (localhost:4000/?authorized=1)

❗️ 참고 - 나중에는 웹 요청(request)의 쿠키 or 헤더를 통해 처리함.


next()

next() 함수 호출시 Promise 를 반환한다.
-> Promise는 다음에 처리해야 할 미들웨어가 끝나야 완료된다.


next()를 호출한 다음에 then 메서드를 사용하여
Promise가 끝나면 콘솔에 end를 출력하도록 해보자.

✅ 참고 - Promise 객체 메소드 체이닝
.then은 Promise를 반환하므로 계속 연속해서 이을 수 있다.
next()도 Promise를 반환하므로 마찬가지임!

index.js

const Koa = require('koa');
const app = new Koa();

app.use((ctx, next) => {
  console.log(ctx.url);
  console.log(1);
  if (ctx.query.authorized !== '1') {
    ctx.status = 401; // Unauthorized
    return;
  }
    next().then(() => {
      console.log('end!');
  })
});

	...

async/await 적용

Express는 async/await을 적용할 수는 있지만, 제대로 에러를 잡아내지 못할 수도 있다.

하지만, Koa 는 async/await을 정식으로 지원하기 때문에 편하게 사용 가능!

index.js (수정)

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  console.log(ctx.url);
  console.log(1);
  if (ctx.query.authorized !== '1') {
    ctx.status = 401; // Unauthorized
    return;
  }
  await next();
  console.log('end!');
});

	...

-> 기존 코드와 동일하게 작동하지만, async-await 으로 작성한 코드이다.


nodemon 사용하기

  • 서버 코드를 변경할 때 마다 매번 ctrl+c로 빠져나온 후, node src를 입력하는 것은 번거로운 일이다.

  • nodemon 이라는 도구를 사용하면 코드를 변경할 때 마다 서버를 자동으로 재시작 할 수 있다.

nodemon 패키지 설치

$ yarn add --dev nodemon

scripts 추가

package.json

  "scripts": {
    "start": "node src",
    "start:dev": "nodemon --watch src/ src/index.js"
  }
  • start = 서버를 시작하는 명령어 (node src)
  • start:def = src 폴더를 보고 있다가, 어떤 파일이 변경되면 src/index.js 파일을 재시작함.

--watch 옵션은 해당 디렉터리를 관찰하라는 뜻.

start는 재시작이 필요 없을 때,
start:dev는 재시작이 필요할 때 사용하면 된다.

1. start:dev

$ yarn start:dev

파일을 저장할 때 마다 터미널에 아래와 같이 나옴.
즉, 수동으로 재시작 할 필요가 없어짐!


koa-router

이전에는 react-router-dom 라이브러리를 사용했으나,
Koa를 사용할때에도 다른 주소로 요청이 들어올 경우 다른 작업을 처리하도록 라우팅이 필요함.

Express 와는 달리 Koa에는 라우팅 기능이 내장되어 있지 않으므로, koa-router 모듈을 설치해야 함.

패키지 설치

yarn add koa-router

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');
});
  • / 경로로 들어오면 '홈' 문구를 띄우고,
    /about 경로로 들어오면 '소개' 문구를 띄우게 함.

  • app.use 의 인자에
    router.routes()).use(router.allowedMethods() 를 넣어줌.

  • .get 의 첫번째 인자로는 경로(path)를 넣고, 두번째 인자로는 미들웨어 함수를 넣는다.
    -> get 키워드는 HTTP 메서드를 의미함.


  • localhost:4000
  • localhost:4000/about

라우트 파라미터(params)

라우터의 파라미터 설정 시 /about/:name과 경로를 설정함.

+) 파라미터가 있을수도 있고, 없을수도 있다면
/about/:name? 과 같이 ?를 파라미터 뒤에 붙여줌.

-> 설정한 파라미터는 ctx.params 객체를 통해 조회할 수 있다.

index.js

	...

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

	...

ctx.params에 name이라는 필드로 저장되어 있을 것임.
즉, params가 존재하면 name 변수에 값이 있을 것이고, 없다면 (/about 이라면) 값이 존재하지 않을 것이다.

-> 삼항 연산자를 이용하여 ctx.body에 조건부로 값을 할당한다.

Result

  1. param이 없을 때 (/about)

  2. param이 있을 때 (/about/yjin)


라우트 쿼리(query)

  • params와 마찬가지로 쿼리를 통해서도 조건부로 다른 결과를 출력할 수 있다.
    -> ctx.query 객체의 값을 이용하면 된다.
router.get('/posts', (ctx) => {
  const { id } = ctx.query;
  ctx.body = id ? `포스트 #${id}` : '포스트 id가 없습니다.';
});

Result

  1. id가 없을 때 (/posts)

  2. id가 있을 때 (/posts?id=2)


다음 포스팅 - REST API

  • 라우트 모듈화
  • mongoDB / mongoose
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글