Post

[Day65]React + Express + MongoDB 회원가입 기능

[Day65]React + Express + MongoDB 회원가입 기능

🔄 구성 흐름

  1. [프론트엔드] 프론트엔드에서 입력값 유효성 검사를 통해 잘못된 값은 서버에 보내지 않음
  2. [백엔드] 백엔드는 Express로 서버를 구성하고, CORS 문제 해결
  3. [데이터베이스] MongoDB에 Mongoose를 통해 사용자 정보를 저장

1. 프론트엔드 (React)

✅ 정규표현식 이용한 유효성 검사

정규 표현식은 문자열에서 특정 패턴을 찾거나 검증, 대체하는 강력한 도구

  • 입력값이 올바른지 먼저 검사해서 잘못된 데이터는 서버에 보내지 않도록 처리
  • 에러 메시지를 setState를 통해 바로바로 UI에 반영
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 사용자명: 영문자로 시작 + 영문자/숫자 4자 이상
const validateUsername = value => {
  if (!value) return setErrUsername('')
  if (!/^[a-zA-Z][a-zA-Z0-9]{3,}$/.test(value)) {
    setErrUsername('사용자명은 영문자로 시작하는 4글자 이상의 영문자 또는 숫자여야 합니다.')
  } else {
    setErrUsername('')
  }
}

// 비밀번호: 4자 이상
const validatePassword = value => {
  if (!value) return setErrPassword('')
  if (value.length < 4) {
    setErrPassword('패스워드는 4자 이상이어야 합니다.')
  } else {
    setErrPassword('')
  }
}

// 비밀번호 확인: 위의 비밀번호와 일치해야 함
const validatePasswordCheck = (value, current = password) => {
  if (!value) return setErrPasswordOk('')
  if (value !== current) {
    setErrPasswordOk('패스워드가 일치하지 않습니다.')
  } else {
    setErrPasswordOk('')
  }
}

✅ 회원가입 핸들러

  1. 유효성 검사 실행
  2. 에러 존재 시 조기 리턴
  3. 문제 없을 경우 axios로 백엔드에 POST 요청
  4. 응답에 따라 상태 메시지 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const register = async e => {
  e.preventDefault()

  // 유효성 검사 실행
  validateUsername(username)
  validatePassword(password)
  validatePasswordCheck(passwordOk, password)

  // 에러가 있거나 입력값이 비어 있으면 요청 중단
  if (errUsername || errPassword || errPasswordOk || !username || !password || !passwordOk) return
  // 문제 없을 경우 axios로 백엔드에 POST 요청
  try {
    setRegisterState('등록중')
    const response = await axios.post('http://localhost:3000/register', { username, password })
    console.log('회원가입 성공', response)
    setRegisterState('등록완료')
  } catch (err) {
    console.log('회원가입 실패', err)
  }
}

✅ HTML 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<form className={css.container} onSubmit={register}>
  <input
    type="text"
    placeholder="사용자명"
    value={username}
    onChange={handleUsernameChange}
  />
  <strong>{errUsername}</strong>

  <input
    type="password"
    placeholder="패스워드"
    value={password}
    onChange={handlePasswordChange}
  />
  <strong>{errPassword}</strong>

  <input
    type="password"
    placeholder="패스워드 확인"
    value={passwordOk}
    onChange={handlePasswordOkChange}
  />
  <strong>{errPasswordOk}</strong>

  <button type="submit">가입하기</button>
</form>

2. 백엔드 (Express)

✅ Express 서버 설정

1
npm i express

package.json에 type 추가

1
"type": "module"

서버 기본 코드

1
2
3
4
5
6
7
8
9
10
11
import express from "express";
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`${port}번 포트에서 실행 중`);
});

서버 실행 UI

✅ CORS 에러

프론트와 백엔드를 연동했더니 CORS 에러 발생 !

CORS(Cross-Origin Resource Sharing)는 브라우저의 보안 정책 중 하나 !
서로 다른 출처(Origin)에서 리소스를 요청할 때 브라우저가 이를 차단할 수 있음

  • 프론트 포트(5173)와 백엔드 포트(3000)가 다르기 때문에 CORS 에러가 발생함
  • express.json()으로 body 데이터 파싱도 필요

해결방법 : cors 미들웨어 추가

특정 출처를 허용하면 브라우저가 요청을 허용하게 됨

1
npm i cors
1
2
3
4
5
import cors from "cors";
app.use(cors()); // 모든 출처 허용 (개발 단계에서 편함)
// 선택적으로 특정 출처만 허용하려면
// app.use(cors({ origin: 'http://localhost:5173' }))
app.use(express.json()); // 프론트에서 전송한 JSON 데이터를 Express가 해석하도록.
  • 브라우저가 보낸 JSON 데이터를 req.body로 받을 수 있고
  • 프론트엔드의 CORS 에러도 발생하지 않게 됨

React에서 Express로 보낸 값 확인 가능

3. MongoDB 연결 (mongoose 사용)

✅ Mongoose란?

  • MongoDB와 연결해주는 ODM(Object Data Modeling) 라이브러리
  • 스키마 정의 및 모델 생성, 데이터를 쉽게 읽고 쓰게 해줌
1
npm i mongoose

1. 모델 생성

1
2
3
4
5
6
7
8
9
10
11
12
import mongoose from "mongoose"
const { Schema, model } = mongoose

// username, password라는 필드를 갖는 users라는 컬렉션을 만든 것과 동일
const userSchema = new Schema({
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true }
}, {
  timestamps: true
})

export const userModel = model("user", userSchema)
  • mongoose.Schema()로 스키마를 정의
  • mongoose.model()로 컬렉션을 생성

2. DB 연결 및 회원가입 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import express from "express";
import cors from "cors";
import mongoose from "mongoose";
import { userModel } from "./model/user.js";

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

app.use(cors());
app.use(express.json());

mongoose.connect(`mongodb+srv://{username}:{password}@cluster0.mongodb.net/blog?retryWrites=true&w=majority`)
  .then(() => console.log("MongoDB 연결됨"))
  .catch(err => console.log("MongoDB 연결 안됨", err))

app.get("/", (req, res) => {
  res.send("Hello world");
});
app.post("/register", async (req, res) => {
  const { username, password } = req.body
  console.log("회원가입 요청", req.body)
  // 1. userModel(DB) 에서 이미 존재하는 사용자인지 확인
  const existingUser = await userModel.findOne({ username });
  if (existingUser) {
    return res.json({ message: "이미 존재하는 사용자입니다." });
  }
  // 2. 새 사용자 생성 및 저장
  const userDoc = new userModel({ username, password });
  const savedUser = await userDoc.save();
  // 3. 성공 응답
  res.status(201).json({
    user: {
      username: savedUser.username,
      _id: savedUser._id,
    },
  });
});
app.listen(port, () => {
  console.log(`${port} 포트에서 돌고 있음`);
});

MongoDB 연결된 모습

연결 후 콘솔에 “회원가입 성공”이 찍히는 모습

회원 정보가 MongoDB에 저장

connect 함수에서 ?retryWrites=true 뒤에 작성한 DB 이름인 blog가 MongoDB에 생성됨

MongoDB에 “blog”라는 DB가 생기고 그 안에 데이터가 저장된 모습


END

This post is licensed under CC BY 4.0 by the author.