(Node.js) Backend Server Request

Mirrer·2022년 9월 6일
0

Node.js

목록 보기
5/12
post-thumbnail

Sign-Up Implement

다음과 같은 방법으로 Browser에서 Backend Server로 요청 및 전달

현재 서버는 아래와 같이 Brower, Front Server, Backend Server, Database로 구성되어 있으며 하나의 포트는 개별 프로그램, 즉 BrowerFront Server는 동일한 포트로 같은 프로그램이다.

다음과 같은 환경에서 User의 가입 정보를 Brower에서 입력받아 Backend Server로 요청, 응답하는 방법은 다음과 같다.


실행 과정

SignUp Dispatch

Brower의 회원가입 Form에서 User의 정보를 입력받은 뒤 ActionDispatch하여 Saga로 이동

// value : {email: 'alsejr1004@gmail.com', password: 'dsa', nickname: 'sdad12'}
const onSubmitForm = useCallback((value) => {
  dispatch(    
    type: SIGN_UP_REQUEST,    
    data: value,
  );
}, []);


Saga에서 Backend Server로 실제 데이터 요청

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),    
  ]);
}

userRouter 생성

다음과 같이 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;

Bcrypt

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;

try~catch문을 통해 에러 탐지

생성한 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

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

Cors ErrorBrowser에서 서로 다른 도메인/포트의 서버로 요청할 때 발생하는 에러

완성된 코드를 실행하면 다음과 같은 에러가 발생하는 것을 확인할 수 있다.

해당 에러는 Browser에서 다른 서버로 요청을 보내면, 즉 다른 도메인으로 요청을 보내면 브라우저가 이를 차단하는데 이것이 Cors Error이다.

CORSCross-Origin Resource Sharing의 약자로, Browser에서 다른 출처의 리소스를 공유하는 방법이다.

위의 CORS policy 오류 메시지는 CORS 정책을 위반할 때 발생한다.

Cors Error를 해결하기 위해서는 Access-Control-Allow-Origin이라는 CORS의 응답 헤더를 보내 특정 페이지 콘텐츠의 출처를 Browser에게 알려야한다.

Backend Server의 요청Browser에 액세스하기 위해서는 각 리소스 / 페이지에 대해 Backend Server는 응답 헤더와 함께 페이지를 제공하여 Browser가 리소스에 접근할 수 있도록 허용해야한다.


setHeader

다음과 같이 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 middleware

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 만들기 - 제로초

profile
memories Of A front-end web developer

0개의 댓글