혼자 적어보는 노트

회원가입 구현하기 /NextJS, Express, MySQL 본문

기타

회원가입 구현하기 /NextJS, Express, MySQL

jinist 2022. 3. 18. 04:09

 

 

프론트서버(Next.js), 백엔드서버(Express), 데이터베이스(MySQL)를 연결하여

회원가입을 하는 기능을 구현해보자

 


 

📄 준비된 것

 

- 프론트에서 회원가입 submit시 데이터 전달을 하는 코드 작성

- Node Express를 이용한 서버 세팅

- Sequalize를 사용하여 모델 세팅

- MySQL으로 연결하여 데이터를 넣을 테이블 세팅

 

현재 Sequalize에서 만든 모델들을 MySQL에 연결해 놓은 상태이기 때문에

그 이후 과정을 진행해보겠다.

 


😇 연결 전 기억해야 할 것

 

1. 브라우저와 프론트서버의 포트는 동일하다.

프론트의 포트를 변경할 경우 front의 package.json에서 변경을 해준다.

 "scripts": {
    "dev": "next -p 3060"
  },

 

2. 백엔드 서버의 포트번호는 Node.js를 통해 설정한 app.js의 포트번호다.

 

3. MySQL의 포트번호는 워크벤치에서 확인할 수 있다.

 

총 3가지의 포트번호를 사용하고 있는 셈이다.

 


🚚 실제 회원가입 데이터 DB에 보내기

 

 

1. 임시 데이터 처리를 실제 데이터 요청으로 변경

 

회원가입 form에 submit후 dispatch 하는 부분에

임시로 데이터 요청을 해놓았던 부분을 실제 데이터 요청으로 변경

function signUpAPI(data) {
  return axios.post("http://localhost:8080/user", data); // api 요청
}

function* signUp(action) {
  try {
    const result = yield call(signUpAPI, action.data);
    // call의 인자로 API요청 함수와 API요청 함수에 보낼 매개변수를 적는다.
    console.log(result); //일단 데이터 요청 전 결과가 제대로 나오는지 console확인
    yield put({
      type: SIGN_UP_SUCCESS,
    });
  } catch (error) {
    yield put({
      type: SIGN_UP_FAILURE,
      error: error.response.data,
    });
  }
}

saga를 사용했고 임시로 작성해놓은 yield delay(1000); 코드를 지우고 call요청으로 변경했다.

* call은 비동기

 

 

2. 백엔드(express)의 app.js에 아래의 코드를 추가

//.. 생략

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use("/user", userRouter);

//.. 생략

 

app.use(express.json()) : 프론트 서버에서보낸 json형식의 데이터를 req.body에 넣어준다.
app.use(express.urlencoded()) : form submit의 방식의 경우 urlencoded형식으로 넘어오는데 그 데이터를 처리해줌.

 

express.json()과 express.urlencoded()을 설정해야 서버에 데이터 전달 시

데이터를 req.body로 받아올 수 있다.

 

❗ (중요) express.json()과 express.urlencoded()가 route를 설정하는 코드 위에 있어야한다. 

 

 

 

3. route 생성

 

[routes/user.js]

const express = require("express");
const router = express.Router();
const { User } = require("../models");
// models/index.js에서 내보낸 db안의 User을 가져온다 == User의 모델(table)을 가져오는 것

router.post("/", async (req, res) => {
  await User.create({
    // table안에 데이터 삽입 / 비동기함수기 때문에  await 사용
    email: req.body.email,
    nickname: req.body.nickname,
    password: req.body.password,
  });
  res.status(201).send("ok"); // 상태코드 전달
});

module.exports = router;

기존에 만들었던 models/user.js에서 db안의 User을 가져오고

Post요청을 작성한다. 비동기로 이루어지기 때문에 async를 사용한다.

2번에서 json형식으로 받은 데이터를 req.body에 넣어주었기 때문에

req.body에는 action.data가 들어있다.

 

4. 이메일 중복 검사

 

회원가입을 이메일로 진행을 했는데 해당 이메일이 DB에 있는지 확인을하고

이미 등록된 이메일이 있다면 회원가입을 막아주어야한다.

 

router.post("/", async (req, res, next) => {
  try {
    const exUser = await User.findOne({
      // 기존의 유저 중 해당 email을 사용하는 유저가 있는지 확인
      where: {
        email: req.body.email,
      },
    });
    if (exUser) {
      return res.status(403).send("이미 사용중인 이메일입니다.");
      // 이 부분이 saga의 error.response.data가 됨
    }

    await User.create({
      email: req.body.email,
      nickname: req.body.nickname,
      password: req.body.password,
    });
    res.status(201).send("ok");
  } catch (error) {
    console.log(error);
    next(error); // status(500)
  }
});

module.exports = router;

데이터를 찾을 땐 비동기로 조회해야 하기 때문에 async await을 사용해주어야 한다.

사용중인 이메일일 경우 return을 사용하여 에러 전달 후 끊어주어야 한다.

 

 

5. cors에러 처리

 

3번까지만 하고 서버를 실행해서 회원가입을 하면 cors에러가 발생하는데

브라우저에서 백엔드 서버에 요청을 보내면 port가 다르기 때문에 cors에러가 생긴다. (동일출처정책)

 

✅ 해결방법 1.

응답할 때(route) setHeader()사용

 

[routes/user.js]

const express = require("express");
const router = express.Router();
const { User } = require("../models");

router.post("/", async (req, res) => {
  await User.create({
    email: req.body.email,
    nickname: req.body.nickname,
    password: req.body.password,
  });
  res.setHeader("Access-Control-Allow-Origin", "http://localhost:3060"); // *
  
  res.status(201).send("ok");
});

module.exports = router;

서버에서 setHeader를 사용하여 브라우저의 도메인을 허용시켜준다.
두 번째 인자에 '*'을 넣을 경우 모든 요청을 허용해주게된다.

 

 

✅ 해결방법 2.

서버에서 미들웨어로 처리

 

[app.js]

const cors = require("cors");

app.use(
  cors({
    origin: "*",
  })
);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// .. route 코드들

use와 cors를 이용하여 미들웨어로 처리하면 각각의 route에서 처리하지 않고 한번에 처리를 할 수 있다.
origin에는 나중에 도메인이 생긴다면 허용하는 도메인을 넣으면 된다.

❗❗ (중요) cors는 route코드들 위에 적어주어야 한다.

 

 

5. 회원가입 완료

next와 node모두 서버를 실행한 상태에서 회원가입을 하면

상태코드 201과 함께 회원가입처리가 완료된 것을 확인할 수 있다.

 

 


✅ 데이터의 전달 순서

 

데이터가 전달이 꽤 많이 되는데 순서를 잘 파악을 해야 한다.

데이터가 전달되는 순서를 정리해보면,

 

1. 사용자에게서 이벤트 발생

 

2. 회원가입의 데이터를 action data로 dispatch

dispatch(signupRequestAction({ email, nickname, password }));
// dispatch(type: SIGN_UP_REQUEST, data : { email, nickname, password })); 이렇게도 전달 가능

 

3. watch하고 있던 saga로 전달하고 takeLatest의 두 번째 인자인 함수 실행

function* watchSignUp() {
  yield takeLatest(SIGN_UP_REQUEST, signUp);
}

 

4. 해당 함수에는 action이 들어있고 action.data를 api요청함수에 전달

function* signUp(action) {
  try {
    const result = yield call(signUpAPI, action.data);
    console.log(result);
    yield put({
      type: SIGN_UP_SUCCESS,
      data: action.data,
    });
  }   // ...생략

 

5. api를 요청하는 함수의 인자에는 data가 들어있고

post요청으로 만들어놓은 route에 데이터를 함께 전송

function signUpAPI(data) {
  return axios.post("http://localhost:8080/user", data);
}

 

6. 위의 data는 route에서 req.body에 전달되게된다.

router.post("/", async (req, res) => {
  await User.create({ // req.body == action.data
    email: req.body.email,
    nickname: req.body.nickname,
    password: req.body.password,
  });
});

 

 

회원가입 부분을 구현하면서 파일 이름들이 같고

어디서 하더라? 하면서 헷갈리던 부분들이 많았지만 조금은 이해가 된 것 같다.

몇번 더 해봐야 될 것 같다.

Comments