[프로젝트-Stroll] Devlog-3

김대연·2020년 1월 17일
0

Project Stroll

목록 보기
3/7

먼저 이번 프로젝트의 서버쪽 디렉토리 구성은 이렇다. (node_modules는 생략)

.
├── README.md
├── config
│   ├── config.js
│   └── jwt.js
├── controllers
│   ├── signin.js
│   ├── signup.js
│   └── trails
│       ├── tag.js
│       ├── trailId.js
│       └── trails.js
├── index.js
├── migrations
│   ├── 20200114050301-create-users.js
│   ├── 20200115075622-create-categories.js
│   ├── 20200115075901-create-images.js
│   ├── 20200115080332-create-locations.js
│   ├── 20200118051951-create-trails.js
│   └── 20200118053842-create-comments.js
├── models
│   ├── categories.js
│   ├── comments.js
│   ├── images.js
│   ├── index.js
│   ├── locations.js
│   ├── trails.js
│   └── users.js
├── package-lock.json
├── package.json
└── routes
    ├── signin.js
    ├── signup.js
    └── trails.js

먼저 index.js 에서 express와 middleware 및 여러 설정들을 하였다.

const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const morgan = require('morgan');

const app = express();
const PORT = 3000;

const signupRoute = require('./routes/signup');
const signinRoute = require('./routes/signin');
const trailsRoute = require('./routes/trails');

// middleware
app.use(
  bodyParser.urlencoded({
    extended: false,
  }),
);
app.use(bodyParser.json());
app.use(cookieParser());
// app.use( 
//   cors({
//     origin: ['http://localhost:3000'], 
//     methods: ['GET', 'POST'],
//     credentials: true,
//   }),
// ); //아직 프론트와 병합하지 않아 주석처리
app.use(cors());
app.use(morgan('dev')); //요청/응답 log를 남겨주는 모듈

// router
app.use('/signup', signupRoute);
app.use('/signin', signinRoute);
app.use('/trails', trailsRoute);

app.listen(PORT, () => {
  // eslint-disable-next-line no-console
  console.log(`app Listening on ${PORT}`);
});

각 URI 들은 /routes 를 통해 routing하여 /controllers 에서 각 요청의 메소드를 구현했다.

아래는 /controllers/signup.js

/* eslint-disable object-shorthand */
const { Users } = require('../models');

/**
 * 회원가입 요청을 받아 DB에 요청받은 정보를 저장한다.
 * 1. 먼저 이미 존재하는 email인지 확인 -> 존재하면 send 409 
 * 2. email이 존재하지 않으면 username 도 확인 -> 존재하면 send 409
 * 3. email, username 둘 다 사용가능하면 DB에 추가 -> send 201
 */
module.exports = async (req, res) => {
  // database에 들어온 바디를 넣어준다.
  const { email, password, username } = req.body;

  const checkEmail = await Users.findOne({
    where: {
      email: email,
    },
  }).catch((error) => {
    console.log(error);
    res.sendStatus(500);
  });

  if (checkEmail) {
    res.status(409).send('Account already exists');
  } else {
    Users.findOrCreate({
      where: {
        username: username,
      },
      defaults: {
        email: email,
        password: password,
      },
    }).spread((data, created) => {
      if (created) {
        res.status(201).send('Account has been successfully created');
      } else {
        res.status(409).send('Username already exists');
      }
    }).catch((error) => {
      console.log(error);
      res.sendStatus(500);
    });
  }
};

회원가입을 한 후 로그인 요청을 할 때를 위한 signin.js

/* eslint-disable object-shorthand */
// jsonwebtoken
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const secretObj = require('../config/jwt');
const { Users } = require('../models');

/**
 * 이미 회원가입이 완료된 사람(email 확인)인지 DB에서 조회 -> 아니라면 401 리턴
 * 존재한다면 password 도 확인 -> 아니라면 401 리턴
 * 패스워드도 일치하면, 토큰을 생성 & 지급 -> 이 때 추후 userId 조회를 위해 token 과 userId 도 쿠키에 추가
 */
module.exports = async (req, res) => {
  const { email, password } = req.body;
  const hashedPwd = crypto
    .pbkdf2Sync(password, email, 100000, 64, 'sha512')
    .toString('hex');

  const checkEmail = await Users.findOne({
    where: {
      email: email,
    },
  }).catch((error) => {
    console.error(error);
    res.sendStatus(500);
  });

  if (checkEmail) {
    if (checkEmail.dataValues.password === hashedPwd) {
      const token = jwt.sign(
        {
          userId: checkEmail.dataValues.id,
          email: email, // 토큰의 내용(payload)
        },
        secretObj.secret, // 비밀 키
        {
          expiresIn: '5m', // 유효 시간은 5분
        },
      );
      res.cookie('user', token); // cookie에 token 추가
      res.json({ token: token });
    } else {
      res.status(409).send('Incorrect password');
    }
  } else {
    res.status(404).send('Invalid user');
  }
};

여기서는 해당 유저를 식별할 수 있도록 토큰에 Users.id 를 추가해주었다.

그리고 signin.js 를 작업하면서 아래와 같은 에러가 발생해서 코드 초반부부터 실행을 할 수가 없었다.
TypeError: Cannot read property 'findOne' of undefined
코드 구현 자체에서 발생한 에러는 아닌 것 같아 열심히 검색을 하고 다른 백엔드 팀원분과 찾은 결과, 6번째 줄의
const { Users } = require('../models'); 부분이
const { Users } = require('../models/signin'); 으로 작성되어 있어 제대로 require 를 받아올 수 없었다. 항상 import 하려는 디렉토리를 잘 확인해야 한다!


프로젝트를 협업으로 진행하면서 Git flow 를 정리해놓으면 좋겠다는 생각이 들었다. 2인 정도의 페어로 프로그래밍을 할 때는 git remote 를 통해 서로 연결하여 번갈아 작업하고 push하면 되었지만 팀프로젝트로 작업하기엔 비효율적이다. 그래서 Git flow 방식을 통해 작업을 하면 분업과 소통에 더 효율적이다. Git flow 방식을 활용하는 방법은

  1. upstream(원본 레포) 에 "dev" branch (사실 master branch 를 직접 이용하기보다는 개발하는 branch 를 따로 파는게 낫기 때문이다.) 를 git checkout -b <branchname> 을 통해 만든다. 참고로 git checkout -b <branchname> 를 하면 branch 생성과 동시에 해당 branch 로 이동한다.

  2. 해당 upstream 을 fork & clone 한다.

  3. 하나의 작업별로 branch 를 따서 작업한 후 merge 하는 방식이 좋다. 그러려면 나의 레포에서 git checkout -b <branchname> 을 통해 branch 를 판다. branchname 은 해당 작업과 관련된 이름으로 한다.

  4. 구현을 완료하면 git add & commit 후, git push origin <branchname> 을 한다.

  5. upstream repo 를 Github에서 "Create pull request" 를 할 수 있다. 이 때 팀원들에게 내 코드를 리뷰받고 모두의 동의를 얻었으면 본인이 merge 를 진행한다. 그 이유는 merge 의 책임은 코드를 작성한 본인에게 있기 때문이다.

  6. 성공적으로 merge 했다면 작업했던 branch 는 제거해주는게 좋다.

  7. 다른 사람들도 merge 를 하고 업데이트된 버젼으로 작업을 하려면 원본 레포를 git remote add <upstream repo> 로 연결한 후 git pull pair <upstream repo> 으로 최신 버젼을 pull 한다.

  8. 3-7 번을 반복하며 프로젝트를 진행!


앞으로 할 진행은 /trail 관련 API 들을 작성하기, 이미지 업로드 기능 구현하기가 있다. 아직 갈 길이 멀다...

0개의 댓글