먼저 이번 프로젝트의 서버쪽 디렉토리 구성은 이렇다. (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 방식을 활용하는 방법은
upstream(원본 레포) 에 "dev" branch (사실 master branch 를 직접 이용하기보다는 개발하는 branch 를 따로 파는게 낫기 때문이다.) 를 git checkout -b <branchname>
을 통해 만든다. 참고로 git checkout -b <branchname>
를 하면 branch 생성과 동시에 해당 branch 로 이동한다.
해당 upstream 을 fork & clone 한다.
하나의 작업별로 branch 를 따서 작업한 후 merge 하는 방식이 좋다. 그러려면 나의 레포에서 git checkout -b <branchname>
을 통해 branch 를 판다. branchname 은 해당 작업과 관련된 이름으로 한다.
구현을 완료하면 git add & commit 후, git push origin <branchname>
을 한다.
upstream repo 를 Github에서 "Create pull request" 를 할 수 있다. 이 때 팀원들에게 내 코드를 리뷰받고 모두의 동의를 얻었으면 본인이 merge 를 진행한다. 그 이유는 merge 의 책임은 코드를 작성한 본인에게 있기 때문이다.
성공적으로 merge 했다면 작업했던 branch 는 제거해주는게 좋다.
다른 사람들도 merge 를 하고 업데이트된 버젼으로 작업을 하려면 원본 레포를 git remote add <upstream repo>
로 연결한 후 git pull pair <upstream repo>
으로 최신 버젼을 pull 한다.
3-7 번을 반복하며 프로젝트를 진행!
앞으로 할 진행은 /trail
관련 API 들을 작성하기, 이미지 업로드 기능 구현하기가 있다. 아직 갈 길이 멀다...