다음과 같은 방법으로
Browser
에서Backend Server
로 요청 및 전달
현재 서버는 아래와 같이 Brower
, Front Server
, Backend Server
, Database
로 구성되어 있으며 하나의 포트는 개별 프로그램, 즉 Brower
와 Front Server
는 동일한 포트로 같은 프로그램이다.
다음과 같은 환경에서 User의 가입 정보를 Brower
에서 입력받아 Backend Server
로 요청, 응답하는 방법은 다음과 같다.
Brower
의 회원가입 Form
에서 User의 정보를 입력받은 뒤 Action
을 Dispatch
하여 Saga
로 이동
// value : {email: 'alsejr1004@gmail.com', password: 'dsa', nickname: 'sdad12'}
const onSubmitForm = useCallback((value) => {
dispatch(
type: SIGN_UP_REQUEST,
data: value,
);
}, []);
Rest.API
에서 get
, delete
와 달리 post
, put
, patch
는 데이터를 전달할 수 있다.
// sagas/user.js
function signUpAPI(data) {
// post는 데이터 전달 가능
// data는 Brower에서 전달한 User의 정보가 객체로 저장되어 전달
return axios.post('http://localhost:3065/user', data);
}
function* signUp(action) {
try {
const result = yield call(signUpAPI, action.data);
console.log(result);
yield put({
type: SIGN_UP_SUCCESS,
data: action.data,
})
} catch(err) {
yield put({
type: SIGN_UP_FAILURE,
error: err.response.data
})
}
}
function* watchSignUp() {
yield takeLatest(SIGN_UP_REQUEST, signUp);
}
export default function* userSaga() {
yield all([
fork(watchSignUp),
]);
}
다음과 같이 Backend Server
에서 userRouter
를 생성하여 기본 설정
// app.js
const express = require('express');
const userRouter = require('./routes/user'); // userRouter 불러오기
const db = require('./models')
const app = express();
db.sequelize.sync()
.then(() => {
console.log('db 연결 성공');
})
.catch(console.error);
// post에서 전달받은 데이터를 사용하기 위해 express서버 설정
app.use(express.json()); // json형태의 프론트 전달데이터를 req.body에 저장
app.use(express.urlencoded({ extended: true })); // form의 submit 프론트 전달데이터를 req.body에 저장
app.use('/user', userRouter); // userRouter 사용
app.listen(3065, () => {
console.log('서버 실행 중');
});
// routes/user.js
const express = require('express');
const { User } = require('../models'); // User DB 불러오기
const router = express.Router();
router.post('/', async(req, res) => { // POST /user/
// 테이블에 데이터를 생성
await User.create({
email: req.body.email, // req.body에 post에서 전달받은 데이터가 저장
password: req.body.password,
nickname: req.body.nickname,
});
res.send('OK');
});
module.exports = router;
Browser
에서 암호, 개인정보...등 보안 위협이 있는 데이터를 전달할 때 이를 Hashcode
로 변환하여 보안을 강화하는 비밀번호 암호화 라이브러리다.
Bcrypt
는 인자로 암호화 강도를 설정할 수 있는데 숫자가 높을수록 보안은 강화되지만, 변환과정이 복잡하여 서버 성능에 영향을 끼쳐 느려진다.
아래 npm명령어로 설치하며 사용방법은 다음과 같다.
npm i bcrypt
// routes/user.js
const express = require('express');
const bcrypt = require('bcrypt');
const { User } = require('../models');
const router = express.Router();
router.post('/', async(req, res) => {
// 보통 10 ~ 13사이 숫자를 사용
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await User.create({
email: req.body.email,
password: hashedPassword,
nickname: req.body.nickname,
});
res.send('OK');
});
module.exports = router;
생성한 userRouter
는 비동기이므로 try~catch
문을 사용하여 에러를 탐지한다.
// routes/user.js
const express = require('express');
const bcrypt = require('bcrypt');
const { User } = require('../models');
const router = express.Router();
router.post('/', async(req, res, next) => {
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await User.create({
email: req.body.email,
password: hashedPassword,
nickname: req.body.nickname,
});
res.send('OK');
} catch (error) {
console.error(error);
next(error); // next를 사용하면 에러 발생시 express가 한번에 표시
}
});
module.exports = router;
findOne
메서드를 사용하면 Browser
에서 전달받은 값과 DataBase
의 값을 비교하여 중복된 값을 다음과 같이 체크할 수 있다.
// routes/user.js
const express = require('express');
const bcrypt = require('bcrypt');
const { User } = require('../models');
const router = express.Router();
router.post('/', async(req, res, next) => {
try {
await User.findOne({
where: { // where에서 중복 조건을 설정
email: req.body.email, // Browser에서 전달받은 email값과 DB의 email을 비교
}
});
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await User.create({
email: req.body.email,
password: hashedPassword,
nickname: req.body.nickname,
});
res.send('OK');
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;
상태 코드는 클라이언트가 보낸 HTTP 요청에 대한 서버의 응답 코드로, 상태 코드에 따라 요청의 성공/실패 여부를 판단한다.
대표적인 상태 코드들은 아래와 같으며 이외에 코드는 해당 사이트를 참고하여 확인할 수 있다.
브랜치 | 용도 |
---|---|
200 | 성공 |
300 | 리다이렉트 |
400 | 클라이언트 에러 |
500 | 서버 에러 |
// routes/user.js
const express = require('express');
const bcrypt = require('bcrypt');
const { User } = require('../models');
const router = express.Router();
router.post('/', async(req, res, next) => {
try {
const exUser = await User.findOne({
where: {
email: req.body.email,
}
});
if (exUser) { // email값이 중복되었다면 브라우저 전달 데이터의 문제, 클라이언트 에러 응답
return res.status(403).send('이미 사용중인 아이디입니다.');
}
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await User.create({
email: req.body.email,
password: hashedPassword,
nickname: req.body.nickname,
});
res.status(200).send('OK'); // 정상적으로 요청 수행, 성공 응답
} catch (error) {
console.error(error);
next(error); // 서버쪽에서 발생한 에러, status(500)
}
});
module.exports = router;
Cors Error
는Browser
에서 서로 다른 도메인/포트의 서버로 요청할 때 발생하는 에러
완성된 코드를 실행하면 다음과 같은 에러가 발생하는 것을 확인할 수 있다.
해당 에러는 Browser
에서 다른 서버로 요청을 보내면, 즉 다른 도메인으로 요청을 보내면 브라우저가 이를 차단하는데 이것이 Cors Error
이다.
CORS
는 Cross-Origin Resource Sharing
의 약자로, Browser
에서 다른 출처의 리소스를 공유하는 방법이다.
위의 CORS policy
오류 메시지는 CORS
정책을 위반할 때 발생한다.
Cors Error
를 해결하기 위해서는 Access-Control-Allow-Origin
이라는 CORS
의 응답 헤더를 보내 특정 페이지 콘텐츠의 출처를 Browser
에게 알려야한다.
즉 Backend Server
의 요청이 Browser
에 액세스하기 위해서는 각 리소스 / 페이지
에 대해 Backend Server
는 응답 헤더와 함께 페이지를 제공하여 Browser
가 리소스에 접근할 수 있도록 허용해야한다.
다음과 같이 setHeader
를 사용하면 Backend Server
에서 직접적으로 Access-Control-Allow-Origin
헤더를 설정해 Cors Error
를 해결할 수 있다.
router.post('/', async(req, res, next) => {
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
await User.create({
email: req.body.email,
password: hashedPassword,
nickname: req.body.nickname,
});
// 모든 주소의 요청시 Access-Control-Allow-Origin 헤더를 설정하여 응답
res.setHeader('Access-Control-Allow-Origin', '*');
} catch (error) {
console.error(error);
next(error);
}
});
Cors
라이브러리는 위에서 설명한 Cors
문제를 해결하기 위해 사용되는 Middleware
, 즉 일종의 보안정책이다.
아래 npm명령어로 설치하며 사용방법은 다음과 같다.
npm i cors
// app.js
const cors = require('cors');
// 모든 요청에 res.setHeader('Access-Control-Allow-Origin', '*'); 설정을 적용
app.use(cors());
// '*'은 모든 주소를 허용, origin: true는 요청 주소가 자동 적용
app.use(cors({
// origin: '*',
origin: true,
}));
Cors
문제를 해결한 뒤 코드를 실행하면 다음과 같이 회원가입이 정상적으로 동작하여 테이블에 User의 정보가 등록된 것을 확인할 수 있다.
Node.js 공식문서
Node.js 교과서 - 조현영
React로 NodeBird SNS 만들기 - 제로초