JWT 사용방법

Node.js에서 제일 사용량이 많은 jsonwebtoken 라이브러리를 사용하자

 

# yarn을 이용해 프로젝트를 초기화합니다.
yarn init -y

# jsonwebtoken, express 라이브러리를 설치합니다.
yarn add jsonwebtoken express
  • yarn을 이용해 생성된 package.json 파일에서 type을 module로 변경하자 ( 프로젝트에서 ES6 모듈을 사용할수 있도록 설정해주는 옵션 )

JSON 데이터를 암호화

  • jsonwebtoken 라이브러리의 sign 메서드를 사용해 JWT를 생성한다.
import jwt from 'jsonwebtoken';

const token = jwt.sign({ myPayloadData: 1234 }, 'mysecretkey');
console.log(token); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0
  • sign 메서드는 첫 번째 인자로 Payload 데이터를, 두 번째 인자로 비밀 키를 받아 JWT를 생성한다. ( 여기서, Payload는 문자열 뿐만 아니라, 객체도 할당할 수 있다 )

JWT로 만든 토큰 모습

 

JSON 데이터를 복호화

  • jsonwebtoken 라이브러리의 decode 메서드를 사용해  JWT를 복호화한다.
import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValue = jwt.decode(token);

console.log(decodedValue); // { myPayloadData: 1234, iat: 1690873885 }
  • JWT는 누구나 복호화가 가능하다. 하지만 검증을 통해 변조가 되지 않은 데이터인지 알 수 있다.

복호화 된 JWT 출력

 

복호화가 아닌, 변조되지 않은 데이터 검증해보기

  • jsonwebtoken 라이브러리의 verify 메서드를 사용해 JWT를 검증한다.
import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValueByVerify = jwt.verify(token, "mysecretkey");

console.log(decodedValueByVerify); // { myPayloadData: 1234, iat: 1690873885 }
  • JWT가 변조되지 않았고, 올바른 비밀 키로 서명되었는지를 검증하는 중
  • 검증에 실패하면 에러가 발생한다.

잘못된 비밀키를 입력해서 데이터를 검증해보기

  • 잘못된 비밀 키를 이용해 JWT를 검증하면, 에러가 발생한다.
import jwt from 'jsonwebtoken';

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE2OTA4NzM4ODV9.YUmYY9aef9HOO8f2d6Umh2gtWRXJjDkzjm5FPhsQEA0";
const decodedValueByVerify = jwt.verify(token, "secretkey");

console.log(decodedValueByVerify);

// JsonWebTokenError: invalid signature
  • mysecretkey를 secretkey로 변경해서 실행해보자 ( 그러면 아래와 같은 에러가 발생하게되어, 검증에 실패했다는 것을 확인할 수 있다. )

Invalid Signature 에러 발생


JWT를 써야 하는 이유

JWT는 두 가지 중요한 특징을 가지고 있다.

  • JWT가 인증 서버에서 발급되었는지 위변조 여부를 확인할 수 있다.
  • 누구든지 JWT 내부에 들어있는 정보를 확인할 수 있다. ( 복호화 )

만약 JWT를 사용하지 않은 상태에서 사용자 로그인을 구현하려고 하면 어떻게 될까?

 

JWT를 적용하지 않은 로그인 API를 만들어보자

import express from 'express';
const app = express();

app.post('/login', function (req, res, next) {
  const user = { // 사용자 정보
    userId: 203, // 사용자의 고유 아이디 (Primary key)
    email: "archepro84@gmail.com", // 사용자의 이메일
    name: "이용우", // 사용자의 이름
  }

  res.cookie('sparta', user);  // sparta 라는 이름을 가진 쿠키에 user 객체를 할당합니다.
  return res.status(200).end();
});

app.listen(5002, () => {
  console.log(5002, "번호로 서버가 켜졌어요!");
});
  • 사용자의 정보가 sparta 이름을 가진 쿠키에 할당된다.
  • 쿠키의 속성값이나 만료 시간을 클라이언트가 언제든지 수정할 수 있다
  • 쿠키의 위변조 여부를 확인 할 수 없다.

 

JWT를 적용한 로그인 API를 만들어보자

import express from 'express';
import JWT from 'jsonwebtoken';

const app = express();

app.post('/login', (req, res) => {
  // 사용자 정보
  const user = {
    userId: 203,
    email: 'archepro84@gmail.com',
    name: '이용우',
  };

  // 사용자 정보를 JWT로 생성
  const userJWT = JWT.sign(
    user, // user 변수의 데이터를 payload에 할당
    'secretOrPrivateKey', // JWT의 비밀키를 secretOrPrivateKey라는 문자열로 할당
    { expiresIn: '1h' }, // JWT의 인증 만료시간을 1시간으로 설정
  );

  // userJWT 변수를 sparta 라는 이름을 가진 쿠키에 Bearer 토큰 형식으로 할당
  res.cookie('sparta', `Bearer ${userJWT}`);
  return res.status(200).end();
});

app.listen(5002, () => {
  console.log(5002, '번호로 서버가 켜졌어요!');
});
  • 사용자의 정보를 Payload에 저장한 JWT를 sparta 이름을 가진 쿠키에 할당된다.
  • JWT를 생성할 때 위변조 여부를 확인할 수 있는 비밀키를 사용했다.
  • 쿠키의 만료시간과 별개로 JWT의 만료시간을 설정했다.

 

암호화 된 데이터의 사용 방법

보통 암호화 된 데이터는 클라이언트 ( 브라우저 ) 가 전달받아 다양한 수단 ( 쿠키, 로컬스토리지 등 )을 통해 저장해 API 서버에 요청을 할 때 서버가 요구하는 HTTP 인증 양식에 맞게 보내주어 인증을 시도한다.

 

Prettier

 

Prettier는 코드 서식을 관리 해주는 패키지다

 

Prettier 사용 준비

# Prettier를 debDependency로 설치합니다.
yarn add prettier -D

 

위 명령어를 터미널에 입력해 prettier를 설치한다. ( D = 개발 환경 전용 )

 

Prettier 설정파일 추가

.prettierrc.json 기본 설정

{
  "printWidth": 80,
  "tabWidth": 2,
  "singleQuote": false,
  "trailingComma": "all",
}

 

  • printwidth : number ( 한 줄에 최대 글자수를 정의 )
  • tabWidth : number ( Tab에 대한 스페이스 수를 정의 )
  • singleQuote : bool ( 따옴표를 쌍 따옴표 (") 사용 여부 )
  • trailingComma : "all" ( 여러 줄을 사용할 때, 후행 콤마 사용 )

스크립트 추가

Prettier를 보다 편리하게 사용할 수 있도록 package.json 파일에 스크립트를 추가한다.

이를 통해 터미널에서 간단한 명령만으로 Prettier를 실행할 수 있다.

 

"scripts": {
  "format": "prettier --write *.js **/*.js"
},

 

위 코드를 package.json에 입력한다. 이 스크립트를 실행하면 프로젝트 내의 모든 .js 파일을 대상으로 .prettierrc.js에 설정된 값을 바탕으로 Prettier를 실행하게 된다.

 

Prettier 실행

# package.json에 작성된 format 스크립트를 실행합니다.
yarn run format

 

위 명령어를 터미널에 입력하면 yarn run이 package.json에 작성한 스크립트를 실행한다. ( format은 package.json에 추가한 스크립트 이름 )

Express.js의 에러 처리 미들웨어

  • 에러 처리 미들웨어는 Express.js가 공식적으로 제공하는 기능으로, 아래와 같은 구조를 가지고 있다.
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

 

 에러 처리 미들웨어의 항목

  • err : 이전 미들웨어에서 발생한 에러를 전달받은 객체
  • req, res : 일반적으로 사용하는 HTTP 요청과 응답을 관리 하는 객체
  • next : 다음 미들웨어를 실행하는 함수

에러를 인자로 전달받아 클라이언트에게 에러 응답을 반환하거나 다음 미들웨어로 에러를 전달하는 역할을 담당한다.

( Express.js에서 미들웨어나 라우터에서 에러가 발생하면, 해당 에러를 next 함수를 통해 다음 미들웨어로 전달한다. 그리고 Express.js는 등록된 미들웨어 중 에러를 매개변수로 받는 미들웨어를 찾아 실행하게 되는 것 )

 

에러 발생 시 에러를 처리하는 미들웨어 작성

export default function (err, req, res, next) {
  console.error(err);

  // Joi 검증에서 에러가 발생하면, 클라이언트에게 에러 메시지를 전달한다.
  if (err.name === 'ValidationError') {
    return res.status(400).json({ errorMessage: err.message });
  }

  // 그 외의 에러가 발생하면, 서버 에러로 처리한다.
  return res
    .status(500)
    .json({ errorMessage: '서버에서 에러가 발생하였습니다.' });
}

 

에러 처리 미들웨어 등록

import ErrorHandlerMiddleware from './middlewares/error-handler.middleware.js';

app.use('/api', [router, TodosRouter]);

app.use(ErrorHandlerMiddleware); // 에러 핸들링 미들웨어 등록

 

  • 에러 처리 미들웨어를 Router 하단에 등록해야 이전 미들웨어에서 발생한 에러에 대해 핸들링 할 수 있다. ( next()를 통해 에러 처리 미들웨어로 전달하기 때문에 )

Joi는 Javascript 유효성 검증을 위한 라이브러리다. Joi는 여러 타입과 규칙을 이용해 유효성을 검증할 수 있으며, 유효성 검증에 실패하면 오류를 발생시킨다.

 

유효성 검증 ( Validation )

function is1(value) {
	return value === 1;
}
  • 위 코드는 단순히 값이 1인지 아닌지 판단해서 Boolean 타입의 값을 반환하는 함수다. 이런 간단한 함수도 Validation. 즉, 검증을 위한 코드가 된다.
  • 이런 데이터를 검증하는 것을 더 쉽고 간결하게 작성하도록 도와주는 joi라는 라이브러리를 사용할 수 있다.

joi 설치

# yarn을 이용해 Joi를 설치합니다.
yarn add joi

 

  • yarn add joi를 터미널에 입력해 패키지를 설치한다.

 

joi를 이용해 유효성 검증 하기

  • 문자열 길이 검증
import Joi from 'joi';

// Joi 스키마를 정의합니다.
const schema = Joi.object({
  // name Key는 문자열 타입이고, 필수로 존재해야합니다.
  // 문자열은 최소 3글자, 최대 30글자로 정의합니다.
  name: Joi.string().min(3).max(30).required(),
});

// 검증할 데이터를 정의합니다.
const user = { name: 'Foo Bar' };

// schema를 이용해 user 데이터를 검증합니다.
const validation = schema.validate(user);

// 검증 결과값 중 error가 존재한다면 에러 메시지를 출력합니다.
if (validation.error) {
  console.log(validation.error.message);
} else {
  // 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
  console.log('Valid Data!');
}

 

  • 이메일 검증
import Joi from 'joi';

// Joi 스키마를 정의합니다.
const schema = Joi.object({
  // name Key는 문자열 타입이고, 필수로 존재해야합니다.
  // 문자열은 이메일 형식에 맞아야합니다.
  email: Joi.string().email().required(),
});

// 검증할 데이터를 정의합니다.
const user = { email: 'foo@example.com' };

// schema를 이용해 user 데이터를 검증합니다.
const validation = schema.validate(user);

// 검증 결과값 중 error가 존재한다면 에러 메시지를 출력합니다.
if (validation.error) {
  console.log(validation.error.message);
} else {
  // 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
  console.log('Valid Email User!');
}

 

https://joi.dev/api/?v=17.13.3  ( 더욱 상세한 joi 문법 사용 방법 )

 


joi를 이용해 유효성 검증하기 ( 비동기 )

  • 비동기로 문자열 길이 검증하기
import Joi from 'joi';

// Joi 스키마를 정의합니다.
const schema = Joi.object({
  // name Key는 문자열 타입이고, 필수로 존재해야합니다.
  // 문자열은 최소 3글자, 최대 30글자로 정의합니다.
  name: Joi.string().min(3).max(30).required(),
});

// 검증할 데이터를 정의합니다.
const user = { name: 'Foo Bar' };

try {
  // schema를 이용해 user 데이터를 검증합니다.
  const validation = await schema.validateAsync(user);
  // 검증 결과값 중 error가 존재하지 않는다면, 데이터가 유효하다는 메시지를 출력합니다.
  console.log('Valid Data!');
} catch (error) {
  // 검증에 실패한다면, 에러 메시지를 출력합니다.
  console.log(error.message);
}
  •  validateAsync()를 통해 데이터를 비동기적으로 검증한다.

미들웨어

 

  • 미들웨어는 서버의 요청 ( Request ) - 응답 ( Response ) 과정에서 중간에 위치해 특정 기능을 수행하는 함수라고 볼 수 있다.
  • 예를 들어, 모든 요청에 대해 로그를 남기거나, 특정 사용자만 API를 접근하게 하고 싶을 경우에도 미들웨어를 사용한다.

 

Express.js의 미들웨어 

// 전역으로 미들웨어 등록
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
  • urlencoded : form-urlencoded 라는 규격의 body 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어
  • json : JSON 규격의 body 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어

 

Express.js에서 미들웨어 작성

app.use((req, res, next) => {
  // 필요한 코드
});

위 형태에서 req, res, next는 각자 역할을 지니고 있는 인자다

  • req : 요청 ( Request )에 대한 정보가 담겨있는 객체다. ( HTTP Headers, Query Parameters, URL 등 브라우저가 서버로 보내는 정보들이 담겨 있다. )
  • res : 응답 ( Response )을 위한 기능이 제공된다. ( 어떤 HTTP Status Code로 응답 할지, 어떤 데이터 형식으로 응답 할지, 헤더는 어떤 값을 넣어 응답 할지 다양한 기능을 제공한다. )
  • next : 다음 스택으로 정의된 미들웨어를 호출한다.

 

미들웨어 작성해보기

app.use((req, res, next) => {
    console.log('Request URL:', req.originalUrl, ' - ', new Date());
    next();
});
  • 위 코드를 작성하고 서버를 실행한 후 메인페이지로 접속하면 터미널에 로그가 남겨지는 것을 확인할 수 있다.

 

미들웨어 동작 방식

다음 미들웨어로 진행하지 않고 중간에 응답할 경우의 상황

 

  • 미들웨어는 어디에 위치시키느냐에 따라 다르게 동작한다. 일반적으로는 어플리케이션에 등록된 순서대로 실행된다.
  • 여러개의 미들웨어가 겹치는 경우, 이는 첫번째 미들웨어부터 순차적으로 진입한다.
app.use((req, res, next) => {
    console.log('첫번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('두번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('세번째 미들웨어');
    next();
});

// print: 첫번째 미들웨어
// print: 두번째 미들웨어
// print: 세번째 미들웨어

 

예를 들어

  • 위 그림처럼 순차적으로 미들웨어를 통과하고 중간에 응답을해 종료가 되는 경우
  • 위 코드처럼 순차적으로 다음 미들웨어로 넘어가는 경우
  • 중간에 next()를 실행하지 않으면 다음 미들웨어는 실행되지 않는다.
  • 당연히 현재 미들웨어에서 응답을 보내는 경우, 즉 res.send() 나 res.json() 등의 메서드를 호출하는 경우 next()를 호출하면 안된다. 이렇게 하지 않으면 이미 요청이 종료된 상태에서 다른 미들웨어가 응답을 보내려고 해서 중복된 요청이 전달되는 문제가 발생한다.

 

라우터와 미들웨어 차이

  • 라우터와 미들웨어는 서로 다른 방식처럼 보이지만 라우터는 미들웨어 기반으로 구현된 객체라서 미들웨어와 동일한 방식으로 작동된다.
  • 라우터는 미들웨어 함수를 특정 경로에 바인딩하는 역할을 하고, 요청이 들어온 URL 경로에 따라 서로 다른 미들웨어를 실행시킬 수 있게 도와준다.
app.use(Middleware) : 모든 요청에서 미들웨어가 실행된다.
app.use('/api', Middleware) : /api로 시작하는 모든 요청에서 미들웨어를 실행한다.
app.post('/api', Middleware, (req,res)=>{}) : /api로 시작하는 POST 요청에서 미들웨어를 실행한다.

 

MongoDB 코드에서 연결

  • mongoose를 이용해 데이터베이스에 연결할 수 있다.

 

mongoose

  • mongoose는 MongoDB에 데이터를 쉽게 읽고 쓰게 해주는 JavaScript 라이브러리다.
  • Mongoose를 ODM ( Object Document Mapper ) 이라고도 부른다.
  • ODM ( Object Document Mapper ) 이란, JavaScript의 객체와 MongoDB의 문서 사이에서 매핑을 수행하는 도구다.
  • Javascript 코드에서 작업하는 객체 ( Object )를 MongoDB 데이터베이스의 문서 ( Document )로 쉽게 변환하거나, 반대로 문서를 개체로 변환해주는 작업을 수행한다.

 

mongoose 설치

  • 터미널에 yarn add mongoose 명령어를 통해 mongoose를 설치 할 수 있다.

https://www.npmjs.com/package/mongoose

 

mongoose

Mongoose MongoDB ODM. Latest version: 8.6.0, last published: 5 days ago. Start using mongoose in your project by running `npm i mongoose`. There are 18707 other projects in the npm registry using mongoose.

www.npmjs.com


mongoose의 문서 ( Document )

RDBMS 와 MongoDB 비교

 

  • MongoDB에서 가지고 있는 각 데이터 하나하나를 문서 ( Document )라고 정의한다.
  • 1개 이상의 Key-Value의 쌍으로 이루어져있다.
  • JSON 형식으로 구성되어있다.

mongoose의 컬렉션 ( Collection )

  • 컬렉션 ( Collection )은 여러개의 문서 ( Document )를 보유할 수 있는 MongoDB의 구성요소다.
  • JSON 형식의 여러가지 문서 ( Document )를 보유할 수 있다.
  • 컬렉션 ( Collection )은 고정된 구성요소가 존재하지 않고, 유연하게 구성할 수 있다.
  • 관계형 데이터 베이스 ( RDB ) 의 Table과 동일한 역할을 한다.

mongoose의 스키마 ( Schema )

  • 스키마 ( Schema )는 컬렉션 ( Collection )에 들어가는 문서 ( Document )가 어떤 종류의 값을 가질 것인지 정의하기 위해 사용한다.
  • 스키마 ( Schema )는 데이터의 구조와 어떤 제약 사항을 가지는지 정의하기 위해 사용하며, 일반적으로 데이터를 모델링할 때 사용한다.
  • 스키마 ( Schema )는 어떤 필드가 있어야 하는지, 필드는 어떤 데이터 타입을 가져야 하는지를 정의한다.
let UsersSchema = new mongoose.Schema({
  name: String, // 문자열 타입
  age: Number, // 숫자 타입
  favorites: [String], // 문자열 배열 타입
  createdAt: { type: Date, default: Date.now }, // 날짜 타입
  someId: mongoose.Schema.Types.ObjectId // ObjectId 타입
});

 

위는 사용자 정보를 정의한 스키마 ( Schema )의 예시다

 

mongoose의 모델

  • 모델 ( Model )은 데이터베이스에 데이터를 저장하고 읽어올 때 사용하는 데이터의 구조다
  • 스키마를 바탕으로 만들어지고, JavaScript의 객체와 MongoDB 간의 상호작용을 하기 위해 사용한다.
  • MongoDB의 실제 데이터를 다룰 수 있는 메서드를 지니고 있다.

 

 

  • MongoDB는 모든 데이터가 JSON 형태로 저장된다.
  • 복잡한 데이터 구조를 쉽게 저장할 수 있는 장점이 있다. ( 대용량 데이터를 손쉽게 처리할 수 있다 )
  • 데이터베이스 서버의 확장을 손쉽게 줄이고 늘일 수 있다.

 

MongoDB 대여

1. MongoDB Atlas

  • MongoDB Atlas는 MongoDB의 클라우드 버전이다. 이를 통해 어디에서나 쉽게 데이터베이스에 접근할 수 있다.

2. MongoDB Atlas 대여하기

  2.1 https://www.mongodb.com/ 접속

  2.2 MongoDB 사이트 회원가입 진행 

  

   2.3 M0 Free 선택

 

  2.4 Provider : AWS, Region : South Korea, Name : express-mongo 선택하고 Create

 

2.5 Username 과 Password를 입력하고 User 생성

  2.7 Go to Database를 클릭해 MongoDB 생성을 완료

 

3. MongoDB 주소 정보 조회하기

  • Connect 버튼을 클릭

 

  • Drivers를 선택

Drivers

 

  • 주소 정보를 확인

주소 정보

 

MongoDB의 주소 형식

대여한 MongoDB의 주소의 형식은 다음과 같다. 

mongodb+srv://<username>:<password>@express-mongo.iqizo.mongodb.net/?retryWrites=true&w=majority&appName=express-mongo

  • <username>, <password>: 여기에 생성한 사용자의 이름과 비밀번호를 입력야한다.

req 객체

  • req.app : req 객체를 통해 Express.js의 app객체에 접근한다.
  • req.ip : 요청한 클라이언트의 ip 주소가 담겨 있다.
  • req.body : Request를 호출할 때 body로 전달된 정보가 담긴 객체다. ( express.json() 미들웨어를 이용해야 해당 객체를 사용할 수 있다. ) 
  • req.params : 라우터 매개변수 ( Path Params )에 대한 정보가 담긴 객체다.
  • req.query : Request를 호출할 때 쿼리 스트링으로 전달된 정보가 담긴 객체다.
  • req.cookies : Request를 호출할 때 Cookie 정보가 담긴 객체다. ( cookie-parser 미들웨어를 이용해야 해당 객체를 사용할 수 있다. )
  • req.get(Header) : 헤더에 저장된 값을 가져오고 싶을 경우 사용한다.

req.body, req.params, req.query 차이

1. req.body

 1-1. JSON 등의 바디 데이터를 담을 때 사용

 1-2. 주로 POST로 유저의 정보 또는 파일 업로드를 보냈을 때 사용

 1-3. 요청 본문에 제출 된 키-값 데이터 쌍을 포함한다.

 

클라 코드

await axios.post('www.example.com/post/1/jun', { 
    name: 'nomad', // post 로 보낼 데이터
    age: 11,
    married: true
});

 

서버 코드

app.use(express.json()); // json 형식 폼 요청 들어오면 파싱

// 요청온 url : www.example.com/public/100/jun
router.post('/:id/:name', (req, res, next) => {

  // public/100 부분이 담기게 된다.
  console.log(req.params) // { id: '100', name: 'jun' }
  
  // post보낼때 담은 객체 부분이 담기게 된다.
  console.log(req.body) // { name: 'nomad', age: 11, married: true }
  
});

 

 

2. req.params

 2-1. 라우터의 매개변수

 2-2. 예를 들어 /:id/:name 경로가 있으면 ":id"속성과 ":name"속성을 req.params.id, req.params.name으로 사용할 수 있다.

 

예제 )

// 요청온 url : www.example.com/public/100/jun
router.get('/:id/:name', (req, res, next) => {

  //../100/jun 부분이 담기게 된다.
  console.log(req.params) // { id: '100', name: 'jun' }
});

 

 

3. req.query

 3-1. 경로의 각 쿼리 문자열 매개 변수에 대한 속성이 포함 된 개체 ( 주로 GET 요청에 대한 처리 )

 3-2. 예를 들어 www.example.com/post/1/jun?title=hello! 이면, title=hello! 부분을 객체로 매개변수의 값을 가져온다. 

 

예제) 클라 코드

// 클라이언트 단에서, 자바스크립트로 get요청을 보냄

// 방식 1
await axios.get(`www.example.com/post/1/jun?title=hello!`)

// 방식 2
await axios({
  method: "get",
  url: `www.example.com/post/1/jun`,
  params: { title: 'hello!' },
})

 

서버 코드

// 요청온 url : www.example.com/public/100/jun?title=hello!
app.use(express.urlencoded({ extended: false })); // uri 방식 폼 요청 들어오면 파싱

router.get('/:id/:name', (req, res, next) => {

  //../100/jun 부분이 담기게 된다.
  console.log(req.params) // { id: '100', name: 'jun' }
  
  // title=hello! 부분이 담기게 된다.
  console.log(req.query) // { title : 'hello!' }
});

 

res 객체

  • res.app : res 객체를 통해 Express.js의 app 객체에 접근할 수 있다.
  • res.status(코드) : Response에 HTTP 상태 코드를 지정한다.
  • res.send(데이터) : 데이터를 포함해 Response를 전달한다. ( 예 ) res.send('안녕');
  • res.json(JSON) : JSON 형식으로 Response를 전달한다. ( 예) res.json({ message: '안녕' });
  • res.end() : 데이터 없이 Response를 전달한다.
  • res.redirece(주소) : 리다이렉트할 주소화 함께 Response를 전달한다. ( 예 ) res.redirect('https://naver.com');
  • res.cookie(Key, Value, Option) : 쿠키를 설정할 때 사용한다.
  • res.clearCookie(Key, Value, Option) : 쿠키를 제거할 때 사용한다.

 

Express.js의 Response 살펴보기

서버에서 클라이언트에게 보내는 메시지를 응답(Response)라고 부른다.

status는 서버가 클라이언트에게 응답(Response)를 보낼 때 Http 상태 코드를 전송하는것을 나타내며, send, json은 서버가 클라이언트에게 응답(Response) 데이터를 전송하는 방법을 나열한 것이다.

 

res.status( 상태 코드 )

app.post('/', (req, res) => {
  return res.status(201).json({key: 'Value'});
});

 

  • 서버가 클라이언트에게 응답 ( Response )을 보냈을 때, 상태 코드를 전달할 때 사용한다.
  • HTTP 상태 코드는 HTTP 요청이 어떠한 상태로 처리 되었고, 완료되었는지를 나타낸다. ( 예 ) 200 은 요청이 성공적, 404는 요청한 리소스가 서버에 존재하지 않음 )
  • Express에서 상태 코드를 명시하지 않으면, 상태 코드는 200으로 자동 전달된다.
  • https://developer.mozilla.org/ko/docs/Web/HTTP/Status
 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 

res.json ( JSON ), res.send ( 데이터 )

app.post('/', (req, res) => {
  return res.status(201).json({key: 'Value'});
});

 

  • 서버가 클라이언트에게 응답 ( Response )을 보냈을 때, 데이터를 전달할 때 사용한다.
  • res.json() 메서드는 JSON 형식의 데이터만 보낼 수 있다. Response Header의 Content-Type이 'application/json으로 설정된다.
  • res.send() 메서드는 다양한 유형의 데이터를 보낼 수 있다. Response Header의 Content-Type을 데이터 유형에 따라 다르게 설정할 수 있다. ( Content-type은 서버가 클라이언트에게 전달하는 데이터의 타입을 지정할 때 사용한다. )
  • https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Type
 

Content-Type - HTTP | MDN

Content-Type 개체 헤더는 리소스의 media type을 나타내기 위해 사용됩니다.

developer.mozilla.org

 

Express.js의 Request 살펴보기

클라이언트에서 서버로 보내는 메시지를 요청 ( Request ) 이라고 부른다. body, params, query는 클라이언트가 서버에 요청 ( Request )을 보낼 때 데이터를 어떤 방식으로 전송하는지에 대한 여러가지 방법들을 나열한 것이다.

 

req.body: ( Body )

app.post('/', (req, res) => {
  // Request에서 body 데이터를 ReqBody 변수에 할당한다.
  const ReqBody = req.body;

  return res.status(201).json({});
});
  • 클라이언트가 요청 ( Request )을 보냈을 때, Body에 데이터를 삽입했을 때 사용한다.
  • req.body를 사용하기 위해서는 express.json() 미들웨어를 사용해야 한다.
  • Key-Value의 데이터 형식을 가지고 있고, 대표적으로 JSON 형태를 띄고 있다.
  • Body는 Query String, Path Variable(params)과 다르게, URL만을 가지고 어떤 데이터를 전달했는지 확인할 수 없는 특징을 가지고 있다.
  • 데이터를 생성하거나 수정이 필요한 데이터의 전달을 위해 사용한다. ( 예 ) 사용자의 ID, 사용자의 Password, 게시글 제목 등 )
  • POST, PUT 과 같은 HTTP Method에서 사용한다.

req.query : ( Query String )

app.get('/', (req, res) => {
  // Request에서 Query String 데이터를 ReqQuery 변수에 할당한다.
  const ReqQuery = req.query;

  return res.status(200).json({});
});
  • 클라이언트가 요청 ( Request ) 을 보냈을 때, URL에 원하는 Key-Vqlue를 삽입해 데이터를 전달한다.
  • URL의 마지막에 ? 기호를 이용해 Query String을 사용할 수 있다. ( 예 ) https://naver.com?name=얌생&gender=남자 )
  • 특정 컨텐츠의 위치를 표시하거나 웹 페이지에 특정한 옵션을 설정할 때 사용한다. ( 예 ) 게시글의 정렬, 특정 날짜의 게시글만 출력하는 옵션 설정 등 )
  • 주로 서버의 리소스를 필터링하거나 정렬하는 데 사용한다. ( 예 ) https://naver.com/posts?sort=desc&page=3&limit=20 )  limit=20 : 1 페이지당 20개의 게시글을 조회한다. page=3 : 3 페이지를 조회한다. sort=desc : 게시글을 내림차순으로 정렬한다.
  • GET 과 같은 HTTP method에서 사용한다.

req.params: ( Query String )

app.get('/:name', (req, res) => {
  // Request에서 Path Params 데이터의 name Key를 가진 Value를 name 변수에 할당한다.
  const { name } = req.params;

  return res.status(200).json({});
});
  • 클라이언트가 요청 ( Request )을 보냈을 때, URL에 원하는 데이터를 삽입해 전달한다.
  • URL 특정 경로를 매개 변수로써 사용한다.
  • 특정 게시글을 선택하거나 명확한 리소스를 지정해야할 때 사용한다. ( 예 ) 게시글의 상세 정보 조회, 사용자의 상세 정보 조회 )

Request, Response

Request

  • Request란 클라이언트가 서버에게 전달하려는 정보나 메세지를 담은 객체를 의미한다.
  • Request의 세부 사항에는 URL, http method, 헤더( header ), 쿼리 파라미터 ( query parameter ), 바이 데이터 ( body data ) 등이 포함 된다.

 

Response

  • Response란 서버에서 클라이언트로 응답 메세지를 전송시켜주는 객체다.
  • Response의 세부 사항에는 상태 코드 ( status code ), 응답 데이터 ( response data ), 응답 헤더 ( response header ) 등이 포함된다.

서버 모듈

  • Node.js의 서버 모듈에는 대표적으로 http 모듈과 Express.js가 존재한다. ( http 모듈은 Node.js에서 기본 제공하는 http 서버 모듈이다 )
  • Express.js는 http 모듈을 확장해 제공한다.
  • Express.js는 기존 http 모듈의 메서드도 사용할 수 있지만, Express.js가 추가 제공하는 메서드나 속성들을 사용할 수 있다.

 

Express.js 통신 흐름

  • 클라이언트는 특정 URL과 데이터를 담은 요청 ( Request )을 서버에 전송한다.
  • 서버는 받은 데이터에 따라 필요한 비즈니스 로직을 수행한다.
  • 서버는 처리된 결과를 클라이언트에게 응답 ( Response )으로 보내준다.

 

예제 ) 

 

app.js

// app.js

import express from 'express';
import goodsRouter from './routes/goods.js';
import newsRouter from './routes/news.js';

const app = express();
const PORT = 3000; // 서버를 열 때 사용할 포트 번호

app.get('/', (req, res) => {
  res.send('Hello World!');
});

// 1. Express.js의 서버를 엽니다.
app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

// localhost:3000/api -> goodsRouter
// localhost:3000/api -> newsRouter
// 2. 라우터를 등록 합니다.
// app.use 미들웨어를 사용해 /api로 접속하면 goodsRouter와 newsRouter를 연결(등록)한다 
app.use('/api', [goodsRouter, newsRouter]);

 

1. 서버시작

 app.listen을 통해 3000 포트로 웹 서버를 연다.

 

2. 라우터 등록

 app.use 미들웨어를 사용해 /api 주소에 goodsRouter와 newRouter를 연결(등록)한다.

 

news.js

// routes/news.js


import express from 'express';

const router = express.Router();

// req : 클라이언트가 전달한 Path Params
// res : 서버가 클라에게 전달할 데이터 ( json 형태로 전달 )

/** 뉴스 목록 조회 API **/
// 3. HTTP Method와 URL을 지정한 API를 정의합니다.
// 만약, localhost:3000/api/news 라는 URL로 GET 요청이 들어온다면 해당 코드를 실행합니다.
router.get('/news', (req, res) => {
  // 4. 사용자의 요청에 맞는 데이터를 반환합니다.
  return res // Express.js의 res 객체를 반환합니다.
    .status(200) // API의 상태 코드를 200번으로 전달합니다.
    .send('뉴스 목록 조회 API 입니다.'); // API의 결과값을 '뉴스 목록 조회 API 입니다.'로 전달합니다.
});

/** 뉴스 세부 조회 API **/
// 3. HTTP Method와 URL을 지정한 API를 정의합니다.
// 만약, localhost:3000/api/news/:newsId 라는 URL로 GET 요청이 들어온다면 해당 코드를 실행합니다.
router.get('/news/:newsId', (req, res) => {
  // 클라이언트가 전달한 Path Params 데이터를 받아옵니다.
  const params = req.params;

  // Path Params 데이터 중 newsId를 추출합니다.
  const newsId = params.newsId;

  // 서버 콘솔에 클라이언트가 전달한 newsId를 출력합니다.
  console.log('클라이언트로 부터 전달받은 뉴스 ID:', newsId);

  // 4. 사용자의 요청에 맞는 데이터를 json 형태로 반환합니다.
  return res.status(200).json({
    data: '뉴스 세부 조회 API 입니다.',
  });
});

// Express 라우터를 외부로 전달합니다.
export default router;

 

3. API 정의 

등록된 각 라우터를 순서대로 검토하고, HTTP Method와 URL이 일치하는 함수를 실행한다.

 -URL /api/news로 GET 요청이 들어오면 router.get('/new' ~ )을 실행한다.

   

 -URL /api/news/:newsId로 GET 요청이 들어오면 router.get('/new/:newsId' ~)을 실행한다.

  • 여기서 중요한 부분은 :newId에 해당하는 경로 변수다. 이는 req.params을 통해 들어온 변수를 읽어들일 수 있다. 
  • 위 코드에서는 req.params을 통해 들어온 변수를 params에 저장하고 params.newsId를 통해 해당 변수를 읽어오고 출력해주는 형태다

4. 결과 반환

API에서 모든 로직 처리가 완료된 후, 클라이언트에게 결과를 전달한다.

 

 

 

'Javascript' 카테고리의 다른 글

[Javascript][Node.js] Mongo DB  (0) 2024.09.02
[Javascript][Node.js] Express.js - req, res 객체  (0) 2024.08.30
[Javascript] API Client ( Insomnia API Client )  (0) 2024.08.30
[Javascript] API  (0) 2024.08.29
[Javascript] Module  (0) 2024.08.29

API Client란 개발단계에서 작성한 API의 요청을 확인하거나 테스팅 할 때 도움을 주는 툴이다.

API Client를 사용함으로 개발 속도를 높이거나 잘못된 API 요청으로 인한 치명적인 에러를 예방하는데 도움을 받을 수 있다.

  • 대표적인 API Client로 Postman, Insomnia가 있다. 이 두가지의 API Client는 API 요청 ( Request )을 손쉽게 구성할 수 있고, 응답 ( Response )을 직관적으로 확인할 수 있게 도와준다.
  • POST, PATCH, PUT, DELETE 등의 http method를 사용해 테스트를 도와주는 프로그램이라고 할 수 있다.

Insomnia API Client 설치

https://insomnia.rest/

 

The Collaborative API Development Platform

Leading Open Source API Development Platform for HTTP, REST, GraphQL, gRPC, SOAP, and WebSockets

insomnia.rest

 

1. Insomina 공식 사이트에서 Get Started for Free를 눌러준다.

 

 

2. Free 탭에 있는 Sign Up을 눌러 회원가입을 한다.

 

 

3. Download for Windows를 클릭한다.


 

Insomnia로 서버 응답을 확인하기

1. Insomnia API Client를 실행하고,+ 버튼을 눌러 새로운 프로젝트를 생성한다.

 

 

2. Create 탭의 Request Collection을 클릭하고, 새로운 Collection을 생성한다. ( Collection은 테스트하려는 프로젝트의 단위라고 생각하면 된다. )

 

 

3. + 버튼을 눌러 테스트 하려는 HTTP Request를 생성한다.

 

4. 테스트 메세지를 작성한 후, Send를 해서 결과를 확인한다.

'Javascript' 카테고리의 다른 글

[Javascript][Node.js] Express.js - req, res 객체  (0) 2024.08.30
[Javascript][Node.js] Request, Response  (0) 2024.08.30
[Javascript] API  (0) 2024.08.29
[Javascript] Module  (0) 2024.08.29
[Javascript] Express.js 프레임 워크  (0) 2024.08.28

+ Recent posts