230114 항해99 69일차 json-server

요니링 컴터 공부즁·2023년 2월 6일
0
post-thumbnail
post-custom-banner

도입 이유

항해99에서 프로젝트를 여러번 진행하면서 느낀 점은, 프론트에서 가능하다면 필수적으로 가짜 서버를 도입해야만 한다는 것이다.

미니 프로젝트, 클론코딩, 실전 프로젝트까지 백엔드와 협업을 한 프로젝트는 총 3번이다.

프로젝트 기간은 매번 정해져있는데, 그 프로젝트 기간 안에서 프론트가 api 관련 개발을 백엔드 개발 속도에 의존하게 된다는 점이 매우 불편했다.

미니 프로젝트 작업했을때 백엔드도 프론트와 첫 협업이었기 때문에 서버 배포 속도가 많이 늦었다,,
서버가 거의 발표 전날 배포를 해서, 이를 미리 걱정한 팀원분이 프로젝트 초반에 설정해놓은 json-server가 없었다면 아주 큰일 났을 것이라는 생각이 들었다.
미니 프로젝트 프론트 깃헙 바로가기

이번 실전 프로젝트에서 rest API 관련 개발을 담당하고 있는데, 디자인도 기다려야 하고, 서버 배포도 기다려야 하다보니 초반에 정말 할 일이 없었다 ㄷㄷ

시간이 흐를수록 내가 개발할 수 있는 기간은 줄어드니,, 초조해진 나머지 json-server 도입을 결정했다.

명세서도 이미 있고, 이전 프로젝트에서 팀원분이 json-server를 만들었던 소스도 있고, web socket 강의를 들으며 express 개발도 해봤으니 크게 어렵지 않을 것 같았다!

하지만 명세서가 수정되지 않으리란 보장이 없으니 계속해서 동기화해줘야 한다는 점!! 그래도 서버 배포 후에도 서버가 다시 내려가지 않으리란 보장이 없으니 json-server는 정말 유용하다. ㅎㅎ


서버 구축 방법

  1. root에 서버 폴더를 만들어준다.
  • 필수는 아니고 ㅎ 깔끔하자냥~
  1. 인증 요청을 처리할 파일을 만들어주고, 서버 파일도 만들어준다.
    파일 이름은 각각 authenticationHandler, index로 했다.
    소스에서 본대로 한다 ㅎ

  2. 인증 요청을 처리하는 파일은 다음과 같이 작성했다.

// authenticationHandler.js
// 헤더로 받은 authorization을 분리해준다.
const validAuthentication = (req, res) => {
  const authorization = req.headers.authorization;

  // json-server에서 토큰까지 발급할 필요는 없으니 토큰은 대충 token1, token2 <- 이런식으로
  발급했다. 토큰이 없을 경우 에러 핸들링을 해준다.
  if (authorization.split(' ')[1][0] !== 't') {
    return res.status(401).send({ errorMessage: '회원 인증에 실패했습니다.' });
  }
  
  // 토큰이 있을 경우 json-server의 id와 userId를 동일하게 입력해놨으니 숫자만 떼어놓는다!
  return authorization.split(' ')[1][5];
};

module.exports = {
  validAuthentication,
};
  1. 서버 파일은 다음과 같이 작성했다. 아래에서 뜯어보자!
// index.js
const express = require('express');
const jsonServer = require('json-server');
const { validAuthentication } = require('./authenticationHandler');
const fs = require('fs');

const server = jsonServer.create();
const router = jsonServer.router('./db.json');
const middlewares = jsonServer.defaults();
const port = 3001;

server.use(middlewares);
server.use(
  jsonServer.rewriter({
    '/api/users/me': '/me',
    '/api/users/me/keyword': '/me/keyword',
    '/api/users/logout': '/logout',
    '/api/admin/report': '/report',
  }),
);

// 카카오 소셜 로그인
server.get('/api/users/login/kakao', (req, res) => {
  res.redirect('http://localhost:3000/login?token=token1');
});

// 닉네임 중복검사
server.get(`/api/users/:nickname`, (req, res) => {
  const { nickname } = req.params;
  const isNicknameExists = router.db.__wrapped__.me.some((user) 
  => user.nickname === nickname);
  if (isNicknameExists) {
    return res.status(412).send({ result: false, 
    errorMessage: '이미 사용중인 닉네임입니다.' });
  }
  return res.status(200).send({ result: true, message: '사용가능한 닉네임입니다.' });
});

// 내 프로필 조회
server.get('/me', (req, res) => {
  const authenticatedUserId = validAuthentication(req, res);
  const userData = router.db.__wrapped__.me.find((user) 
  => user.userId == authenticatedUserId);
  const result = {
    data: userData,
  };

  res.jsonp(result);

  if (result.data.isFirstLogin) {
    const newData = router.db.__wrapped__;
    newData.me.map((user) => {
      if (user.userId == authenticatedUserId) {
        user.isFirstLogin = false;
      }
    });

    fs.writeFileSync('./db.json', JSON.stringify(newData));
  }

  return;
});

// 회원 키워드 조회
server.get('/me/keyword', (req, res) => {
  const authenticatedUserId = validAuthentication(req, res);
  const result = {
    data: router.db.__wrapped__.keyword.find((user) 
    => user.userId == authenticatedUserId),
  };
  return res.jsonp(result);
});

server.use(express.json());
// 회원정보 수정
server.patch('/me', (req, res) => {
  const authenticatedUserId = validAuthentication(req, res);
  const isUserExists = router.db.__wrapped__.me.some((user) 
  => user.userId == authenticatedUserId);
  if (!isUserExists) {
    return res.status(401).send({ result: false, 
    errorMessage: '해당 유저를 찾을 수 없습니다.' });
  }
  const result = router.db.__wrapped__;
  result.me.map((user) => {
    if (user.userId == authenticatedUserId) {
      user.nickname = req.body.nickname;
      user.profileImg = req.body.profileImg;
    }
  });

  fs.writeFileSync('./db.json', JSON.stringify(result));

  return res.status(200).send({ result: true, 
  message: '사용자 정보 변경에 성공하였습니다.' });
});

// 로그아웃
server.get('/logout', (req, res) => {
  return res.status(200).send({ result: true, message: '로그아웃 되었습니다.' });
});

server.use(router);
server.listen(port, () => {
  console.log('JSON Server is running');
});

module.exports = server;
  • 사실 기본적인 json-server를 사용하기 위해 서버 파일을 따로 작성할 필요는 없지만, 실제 서버는 json-server가 동작하는 방식과 다르게 동작하기 때문에 서버 파일을 따로 작성해 요청을 원하는대로 처리한다.
  • 기본적으로 get 요청을 원하는대로 처리하기 위해 필요한 코드는 다음과 같다.
// index.js
const jsonServer = require('json-server');

const server = jsonServer.create();
const router = jsonServer.router('./db.json');
const middlewares = jsonServer.defaults();
const port = 3001;

server.use(middlewares);
server.use(
  jsonServer.rewriter({
    '/api/users/me': '/me',
    '/api/users/me/keyword': '/me/keyword',
    '/api/users/logout': '/logout',
    '/api/admin/report': '/report',
  }),
);

server.use(router);
// 위에서 지정한 포트로 서버를 실행시키면, 콘솔로 자~알 실행된다고 알려주자
server.listen(port, () => {
  console.log('JSON Server is running');
});

module.exports = server;
  • db 파일을 읽을땐 아래 코드를 사용한다. db에 감싸져있는 데이터 중 필요한 데이터를 읽어서 써먹으면 된다.
  const isNicknameExists = router.db.__wrapped__.me.some((user) 
  => user.nickname === nickname);
  • 다음 코드는 body 값을 읽기 위해 필요한 코드다. 명세서가 json-server 작동 방식 처럼 db 수정할때 POST "/posts/1", POST "posts/2", 이렇게 되면 좋겠지만,, 안그러자냥
//index.js
const express = require('express');
const fs = require('fs');

// body 값을 읽도록 해준다!
server.use(express.json());
  • POST "posts/2" 이렇게 보내면 json-server가 알아서 데이터를 업데이트 해주지만,, 실제 서버는 다르니 서버 파일에서 직접 데이터를 다시 넣어주어야 한다. 데이터를 잘 조작해서, 다음과 같이 파일을 다시 써준다.
  • 찾아보면 굳이 모든 데이터를 불러와서 수정해 db 전체를 업데이트 하지 않아도 되는 방법이 있겠지만, mock-서버고 데이터도 많지 않으니 이렇게 한다. 완벽한 서버를 만들 필요는 업서!
fs.writeFileSync('./db.json', JSON.stringify(newData));
  • 이외 코드는 각자의 명세서에 맞춰 수정해주면 된다!

서버를 실행시키자

  • 터미널에 이렇게 파일명을 적어주면 서버는 잘 실행된다.
node server/index.js
  • 하지만 매번 저걸 다 적긴 귀찮으니 package.json 에서 server만 입력해도 실행되도록 scripts에 다음과 같이 설정해준다.
"server": "server/index.js",
  • 터미널에 다음과 같이 입력한다.
node server
  • 잘 실행되는구만~

그래서 가짜 서버로 효율이 올라갔음?!

  • 매우,, 명세서가 나오자마자 조금만 코드를 추가해주면 바로 프론트에서 api 관련 개발을 진행할 수 있었고, 서버가 배포된 시점에서 실제 서버를 연결해 테스트 해주면,, 끝!
  • 물론 내가 명세서를 제대로 이해하지 못했거나, 실제 서버를 구축하는 과정에서 명세서와 다르게 작성돼 바로 동작하지 않은 경우도 있지만, 그럴땐 수정해서 다음 프로젝트에 사용할 수 있는 소스를 더 쌓아갈 수 있는 것이니 매우 만족한다!

그래서 어느 프로젝트에서 썼냐규?! => 가치마인드 프론트 깃헙 바로가기

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 2월 7일

정말 유익한 정보네요^^ 훠훠~

답글 달기