백엔드에서 회원가입과 로그인하는 기능을 구현하겠습니다.
기능 구현 하다가 막힌 부분이 있는데 아무리 찾아도 뭔가 해결을 못해서 다음에 계속 이어서 하겠습니다. 혹시 코드 보고 뭐가 문제인지 아시는 분은 댓글로 알려주시면 감사하겠습니다. ㅜㅜ
* 페이지를 넘어가서 하는거 말고 현재는 메인페이지에서 팝업창으로 회원가입 / 로그인 기능을 구현하려는 상황 *
CafeMain.js코드 (React)
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; // useHistory 훅 임포트
import styles from './CafeMain.module.css';
import { Link } from 'react-router-dom';
import axios from 'axios'; // Axios 임포트 추가
const images = [
{ id: 1, url: require('../img/slide1.jpg'), link: '/cafeList' },
{ id: 2, url: require('../img/slide2.jpg'), link: '/cafeList' },
{ id: 3, url: require('../img/slide3.jpg'), link: '/cafeList' },
];
export default function CafeMain() {
const [currentIndex, setCurrentIndex] = useState(0);
const [showSignupPopup, setShowSignupPopup] = useState(false); // 회원가입 팝업 상태 추가
const [showLoginPopup, setShowLoginPopup] = useState(false); // 로그인 팝업 상태 추가
const [showResetPasswordPopup, setShowResetPasswordPopup] = useState(false); // 비밀번호 재설정 팝업 상태 추가
const [emailText, setEmailText] = useState(""); // 이메일 텍스트 상태 추가
const [emailDomain, setEmailDomain] = useState("gmail.com"); // 이메일 도메인 상태 추가
const [nickname, setNickname] = useState(""); // 닉네임 상태 추가
const [verificationCode, setVerificationCode] = useState(""); // 인증번호 상태 추가
const [newPassword, setNewPassword] = useState(""); // 새 비밀번호 상태 추가
const [confirmPassword, setConfirmPassword] = useState(""); // 비밀번호 확인 상태 추가
const [isVerificationSent, setIsVerificationSent] = useState(false); // 인증번호 전송 여부 상태
const navigate = useNavigate(); // useHistory 훅 사용
useEffect(() => {
const interval = setInterval(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
}, 3000); // 3초마다 이미지 변경
return () => clearInterval(interval); // 컴포넌트 언마운트 시 인터벌 정리
}, []);
const handleImageClick = () => {
setShowSignupPopup(true); // 회원가입 팝업 표시
}
const closeSignupPopup = () => {
setShowSignupPopup(false); // 회원가입 팝업 닫기
}
const handleEmailChange = (e) => {
setEmailText(e.target.value); // 이메일 텍스트 업데이트
}
const handleDomainChange = (e) => {
setEmailDomain(e.target.value); // 이메일 도메인 업데이트
}
const handleNicknameChange = (e) => {
setNickname(e.target.value); // 닉네임 업데이트
}
const handleSignupSubmit = async (e) => {
e.preventDefault(); // 폼 제출 기본 동작 방지
// 이메일과 도메인 결합
const fullEmail = `${emailText}@${emailDomain}`;
try {
const response = await axios.post('http://localhost:5000/register', {
user_email: fullEmail, // 이메일
user_nickname: nickname, // 닉네임
user_password: e.target.password.value, // 비밀번호 입력값
});
if (response.status === 201) {
console.log('회원가입이 되었습니다.'); // 회원가입 성공 시 메시지 출력
setShowSignupPopup(false); // 회원가입 팝업 닫기
// 추가 작업 (예: 로그인 팝업 열기 등)
}
} catch (error) {
console.error('회원가입 실패:', error.response ? error.response.data.error : error.message);
}
};
const checkEmail = () => {
console.log(`Checking email: ${emailText}@${emailDomain}`);
// 실제 API 호출을 통해 이메일 중복을 확인하는 로직 추가
};
const checkNickname = () => {
console.log(`Checking nickname: ${nickname}`);
// 실제 API 호출을 통해 닉네임 중복을 확인하는 로직 추가
};
const closeLoginPopup = () => {
setShowLoginPopup(false); // 로그인 팝업 닫기
setShowResetPasswordPopup(false); // 비밀번호 재설정 팝업 닫기
}
const handleLoginSubmit = (e) => {
e.preventDefault(); // 기본 폼 제출 방지
// 로그인 처리 로직이 여기에 추가
navigate('/cafeList'); // CafeList로 이동
}
const handleResetPassword = () => {
setShowResetPasswordPopup(true); // 비밀번호 재설정 팝업 열기
}
const handleSendVerificationCode = () => {
setIsVerificationSent(true); // 인증번호 전송 상태 변경
console.log(`Sending verification code to: ${emailText}@${emailDomain}`);
// 실제 API 호출을 통해 인증번호 전송 로직 추가
}
const handleResetPasswordSubmit = (e) => {
e.preventDefault();
console.log(`New Password: ${newPassword}`);
// 비밀번호 재설정 처리 로직 추가
// 비밀번호 재설정이 완료되면 로그인 팝업 열기
setShowResetPasswordPopup(false); // 비밀번호 재설정 팝업 닫기
setShowLoginPopup(true); // 로그인 팝업 열기
}
return (
<div className={styles.bgImg}>
<div className={styles.header}>
<h1>
<Link to="/">CAFE 추천</Link>
</h1>
<div className={styles.contentWrap}>
{images.map((image, index) => (
<div
key={image.id}
className={`${styles.contentImg} ${currentIndex === index ? styles.visible : styles.hidden}`}
style={{ backgroundImage: `url(${image.url})` }}
onClick={handleImageClick} // 클릭 이벤트 핸들러
>
</div>
))}
</div>
</div>
{/* 회원가입 팝업 */}
{showSignupPopup && (
<div className={styles.registerPopup}>
<div className={styles.popupContent}>
<span className={styles.closeButton} onClick={closeSignupPopup}>×</span>
<h2>회원가입</h2>
<form onSubmit={handleSignupSubmit}>
<div>
<label htmlFor="email">이메일:</label>
<div className={styles.emailContainer}>
<input
type="text"
id="email"
name="email"
value={emailText}
onChange={handleEmailChange}
required
placeholder="이메일 입력"
/>
<select
value={emailDomain}
onChange={handleDomainChange}
className={styles.emailSelect}
>
<option value="gmail.com">@gmail.com</option>
<option value="naver.com">@naver.com</option>
<option value="kakao.com">@kakao.com</option>
</select>
<button type="button" onClick={checkEmail} className={styles.emailCheckButton}>중복 확인</button>
</div>
</div>
<div>
<label htmlFor="nickname">닉네임:</label>
<div className={styles.nicknameContainer}>
<input
type="text"
id="nickname"
name="nickname"
value={nickname}
onChange={handleNicknameChange}
required
/>
<button type="button" onClick={checkNickname} className={styles.nicknameCheckButton}>중복 확인</button>
</div>
</div>
<div>
<label htmlFor="password">비밀번호:</label>
<input type="password" id="password" name="password" required />
</div>
<div>
<label htmlFor="confirmPassword">비밀번호 확인:</label>
<input type="password" id="confirmPassword" name="confirmPassword" required />
</div>
<button type="submit">계정 만들기</button>
</form>
</div>
</div>
)}
{/* 로그인 팝업 */}
{showLoginPopup && (
<div className={styles.loginPopup}>
<div className={styles.popupContent}>
<span className={styles.closeButton} onClick={closeLoginPopup}>×</span>
<h2>로그인</h2>
<form onSubmit={handleLoginSubmit}>
<div>
<label htmlFor="loginEmail">이메일:</label>
<input type="email" id="loginEmail" name="loginEmail" required placeholder="이메일 입력" />
</div>
<div>
<label htmlFor="loginPassword">비밀번호:</label>
<input type="password" id="loginPassword" name="loginPassword" required placeholder="비밀번호 입력" />
</div>
<button type="submit">로그인</button>
<button type="button" onClick={handleResetPassword} className={styles.resetPassword} >비밀번호 재설정</button>
</form>
</div>
</div>
)}
{/* 비밀번호 재설정 팝업 */}
{showResetPasswordPopup && (
<div className={styles.resetPasswordPopup}>
<div className={styles.popupContent}>
<span className={styles.closeButton} onClick={closeLoginPopup}>×</span>
<h2>비밀번호 재설정</h2>
{!isVerificationSent ? (
<form onSubmit={handleSendVerificationCode}>
<div>
<label htmlFor="resetEmail">이메일:</label>
<input
type="email"
id="resetEmail"
name="resetEmail"
value={`${emailText}@${emailDomain}`}
readOnly
/>
</div>
<button type="button" onClick={handleSendVerificationCode}>인증번호 보내기</button>
</form>
) : (
<form onSubmit={handleResetPasswordSubmit}>
<div>
<label htmlFor="verificationCode">인증번호:</label>
<input
type="text"
id="verificationCode"
name="verificationCode"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="newPassword">새 비밀번호:</label>
<input
type="password"
id="newPassword"
name="newPassword"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="confirmNewPassword">비밀번호 확인:</label>
<input
type="password"
id="confirmNewPassword"
name="confirmNewPassword"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</div>
<button type="submit">비밀번호 재설정</button>
</form>
)}
</div>
</div>
)}
</div>
)
}
server.js코드
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const conn = require('./db'); // 데이터베이스 연결을 위한 모듈
const multer = require('multer');
const cors = require('cors');
const bcrypt = require('bcrypt'); // bcrypt 모듈 추가
const app = express();
const port = process.env.PORT || 5000; // 포트 번호
app.use(cors()); // CORS 미들웨어 추가
app.use(express.json()); // JSON 데이터 파싱을 위한 미들웨어 등록
app.use(bodyParser.json());
// React 빌드 파일을 정적 파일로 제공
app.use(express.static(path.join(__dirname, 'build')));
// 기본 라우트 설정
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// 모든 경로에 대해 index.html 제공
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// Multer 설정: 파일 업로드를 위한 미들웨어
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/'); // 업로드할 경로 설정
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname)); // 파일 이름 설정
}
});
const upload = multer({ storage });
// 카페 추가 API
app.post('/api/cafes', upload.single('image'), (req, res) => {
const { name, region, category, location } = req.body;
const imageUrl = req.file ? req.file.path : null; // 업로드한 이미지의 경로
const query = 'INSERT INTO cafes (name, region, category, location, image_url) VALUES (?, ?, ?, ?, ?)';
conn.query(query, [name, region, category, location, imageUrl], (err, result) => {
if (err) {
console.error('카페 추가 중 오류 발생:', err);
return res.status(500).send('서버 오류');
}
res.status(201).json({ id: result.insertId, name, region, category, location, image_url: imageUrl });
});
});
// 카페 목록 조회 API
app.get('/api/cafes', (req, res) => {
const query = 'SELECT * FROM cafes';
conn.query(query, (err, results) => {
if (err) {
console.error('카페 목록 조회 중 오류 발생:', err);
return res.status(500).send('서버 오류');
}
res.json(results); // 카페 목록을 JSON 형식으로 반환
});
});
// 회원가입 라우트
app.post("/register", async (req, res) => {
const email = req.body.user_email;
const nickname = req.body.user_nickname;
const password = req.body.user_password;
console.log("이메일:", email);
console.log("닉네임:", nickname);
console.log("비밀번호:", password);
if(!email || !nickname || !password) {
res.status(400).json({ error : '필수 데이터가 누락 되었습니다.'});
return;
}
// 비밀번호 해시화
const hashedPassword = await bcrypt.hash(password, 10);
const sql = "INSERT INTO users (user_email, user_nickname, user_password) VALUES (?, ?, ?)";
const values = [ email, nickname, hashedPassword ];
conn.query(sql, values, (err, result) => {
if (err) {
console.log("데이터 삽입 오류 : ", err);
res.status(500).json({ error: "회원 가입 오류" });
} else {
console.log("데이터 삽입 성공");
res.status(201).json({ success: true, message: "회원가입이 완료되었습니다." });
}
});
});
// 로그인 라우트
app.post("/login", async (req, res) => {
const { user_email, user_password } = req.body;
// 사용자 찾기
conn.query('SELECT * FROM users WHERE user_email = ?', [user_email], async (err, users) => {
if (err) {
return res.status(500).json({ message: '서버 오류' });
}
if (users.length === 0) {
return res.status(401).json({ message: '이메일 또는 비밀번호가 잘못 되었습니다.' });
}
// 비밀번호 확인
const isMatch = await bcrypt.compare(user_password, users[0].user_password);
if (!isMatch) {
return res.status(401).json({ message: '이메일 또는 비밀번호가 잘못 되었습니다.' });
}
// JWT 토큰 생성
const token = jwt.sign({ email: users[0].user_email }, 'your_jwt_secret', { expiresIn: '1h' });
res.json({ message: '로그인 성공', token });
});
});
// 서버 시작
app.listen(port, () => {
console.log(`서버가 http://localhost:${port}에서 실행 중입니다.`);
});
회원가입만 일단 봐주시면 될거같습니다...
'사이드 프로젝트' 카테고리의 다른 글
카페 추천 웹사이트 (0) | 2024.12.07 |
---|---|
카페 추천 웹사이트(메인페이지 로그인 팝업, 비밀번호 재설정, 상세페이지 수정요청 버튼) (1) | 2024.12.04 |
카페 추천 웹사이트(마이페이지 즐겨찾기한 목록 기능, 마이페이지 디자인) (4) | 2024.12.02 |
카페 추천 웹사이트(카카오맵 API 환경변수 설정, 마이페이지 컴포넌트 생성 및 css) (0) | 2024.12.01 |
카페 추천 웹사이트(즐겨찾기 기능) (3) | 2024.12.01 |