[1차] 팩트폭행 MBTI 테스트 프로젝트 - 초기 세팅 및 회원 기능
REACT MBTI TEST PROJECT
베포 링크
프로젝트 설명
팩트로 폭행하는 MBTI TEST 입니다. F는 상처받을 수 있으니 테스트 진행 시 주의 요망
STACK
프로젝트 구조
- 1차에선 프로젝트 초기 세팅 및 로그인,회원가입,프로필 수정 기능을 적용했다.
- 깃에서
feat/user-auth
브랜치를 통해 확인 가능
전체 프로젝트 구조
📦src┣ 📂api
┃ ┣ 📜authAPI.js
┃ ┗ 📜mbtiAPI.js
┣ 📂assets
┃ ┣ 📂img
┃ ┃ ┣ 📂mbti
┃ ┗ 📜react.svg
┣ 📂components
┃ ┣ 📂footer
┃ ┣ 📂header
┃ ┣ 📜GlobalStyle.jsx
┃ ┣ 📜Layout.jsx
┃ ┣ 📜ProtectedRoute.jsx
┃ ┣ 📜TestForm.jsx
┃ ┣ 📜TestResultItem.jsx
┃ ┗ 📜TestResultList.jsx
┣ 📂constants
┃ ┗ 📜queryKeys.js
┣ 📂context
┃ ┗ 📜AuthContext.jsx
┣ 📂data
┃ ┗ 📜questions.js
┣ 📂hooks
┃ ┣ 📜mutations.jsx
┃ ┗ 📜queries.jsx
┣ 📂instance
┃ ┗ 📜baseInstance.js
┣ 📂pages
┃ ┣ 📜Join.jsx
┃ ┣ 📜Login.jsx
┃ ┣ 📜Main.jsx
┃ ┣ 📜Mypage.jsx
┃ ┣ 📜TestPage.jsx
┃ ┗ 📜TestResultPage.jsx
┣ 📂shared
┃ ┗ 📜Router.jsx
┣ 📂utils
┃ ┣ 📜dateResult.js
┃ ┣ 📜mbtiCalculator.js
┃ ┣ 📜mbtiImg.js
┃ ┗ 📜mbtiResult.js
┣ 📂zustand
┃ ┗ 📜userStore.js
┣ 📜App.css
┣ 📜App.jsx
┣ 📜index.css
┗ 📜main.jsx
프로젝트 세팅 및 기능 설명
4-1) 설치 패키지
- Json-server
- Tanstack query
- zustand
- Axios
- Tailwind
- styled-component
- react-router-dom
- the-new-css-reset
- lucide-react
4-2) 주요 기능 소개
-
메인 페이지
- 약간의 어그로성 문구와
테스트하기
버튼을 통해 로그인 페이지로 이동합니다.
- 약간의 어그로성 문구와
-
회원가입 페이지
- 닉네임, 아이디, 비밀번호 입력 후 회원가입 버튼을 클릭하면 “회원가입이 완료되었습니다.” 문구와 함께
로그인
페이지로 이동합니다.
- 닉네임, 아이디, 비밀번호 입력 후 회원가입 버튼을 클릭하면 “회원가입이 완료되었습니다.” 문구와 함께
-
로그인 페이지
- 아이디와, 비밀번호를 입력 후 로그인 버튼을 클릭하면 “로그인이 완료되었습니다.” 문구와 함께
메인
페이지로 이동합니다.
- 아이디와, 비밀번호를 입력 후 로그인 버튼을 클릭하면 “로그인이 완료되었습니다.” 문구와 함께
-
프로필 페이지
- 아이디, 이전 닉네임을 확인할 수 있으며, 변경 닉네임에 값을 입력 후 닉네임을 변경 버튼을 클릭하면 변경된 닉네임으로 확인이 가능합니다.
-
테스트 페이지
- 12개의 문항에 대해 체크 후 저장 버튼을 클릭하면, MBTI 테스트 결과가 화면에 출력됩니다.
-
결과보기 페이지
- MBTI 테스트 결과 리스트가 있는 페이지입니다.
- 게시물은 본인이 쓴 게시물만 공개/비공개, 삭제 처리가 가능합니다.
- 비공개 선택 시 해당 글은 결과보기 페이지에서 본인만 확인이 가능합니다.
작업 목록
5-1) 레이아웃 및 라우터 설정
-
레이아웃
- Layout.jsx 컴포넌트에서 Header,Footer 및 초기 레이아웃을 설정해줌
const Layout = ({ children }) => {
return (
<StContainer>
<Header />
<StContents>
{ children }
</StContents>
<Footer />
</StContainer>
)
}
const StContainer = styled.div``
const StContents = styled.div``
export default Layout
-
라우터 설정 (Protected Route)
- 이번 프로젝트에서 회원 여부에 따라 접근 가능한 페이지를 설정하기 위해
Protected Route
설정을 해주었다. - context API를 사용하여
isLogin
값으로 라우터 처리 및 메뉴도 로그인 여부에 따라 노출되게 설정했다.
- 이번 프로젝트에서 회원 여부에 따라 접근 가능한 페이지를 설정하기 위해
import { AuthContext } from '@/context/AuthContext';
import { useContext } from 'react';
import { Navigate } from 'react-router-dom';
const ProtectedRoute = ({ children, redirectIsLogin, redirectNotLogin}) => {
const { isLogin } = useContext(AuthContext);
// 로그인된 상태에서 접근할 수 없는 페이지 처리
if (isLogin && redirectIsLogin) {
alert("이미 로그인되어 있습니다.");
return <Navigate to={redirectIsLogin} />;
}
// 로그인된 상태에서 접근할 수 없는 페이지 처리
if (!isLogin && redirectNotLogin) {
alert("로그인이 필요한 페이지입니다.");
return <Navigate to={redirectNotLogin} />;
}
// 로그인되지 않은 상태에서 접근할 수 없는 페이지 처리
return children
};
export default ProtectedRoute;
5-2) 로그인
로그인 페이지에서 아이디, 비밀번호를 입력 후 버튼을 클릭했을 때 로그인 처리가 되어야 한다.
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const { login } = useContext(AuthContext);
const handleLogin = async (e) => {
e.preventDefault();
try{
const loginData = { id, password }
const response = await handleUserLogin(loginData);
if(response.success){
alert("로그인되었습니다. 메인 페이지로 이동합니다.")
login(response.accessToken);
navigate("/");
}
}catch(e){
console.log("Login Error =>", e);
alert("로그인이 실패했습니다.")
}
}
-
상태관리
-
useState
를 사용해서 is,password를 입력받아 저장한다.
-
-
AuthContext
- context 의 login 상태 관리 함수를 가져온다.
- login(token)
-
const login = (token) => { localStorage.setItem("accessToken", token); setLogin(true); }
-
useNavigate
- 라우터 훅을 사용하여 로그인 후 페이징 처리
-
handleLogin
- 로그인 처리 함수이며, 사용자가 폼을 제출하면 실행된다.
- loginData(사용자가 입력한 아이디,비밀번호를 객체로 생성)
- API 호출
-
handleUserLogin(loginData)
함수를 통해 서버에 로그인 요청을 보내고 성공 시 서버에서 받은accessToken
을 반환함 - 해당 토큰으로login(token)
실행 시 로컬 스토리지에 토큰 값이 저장되고 현재 로그인상태(setLogin(true)
)가 변경됨
5-3) 회원가입
회원가입 페이지에서 아이디, 비밀번호. 닉네임를 입력 후 버튼을 클릭했을 때 입력한 데이터가 저장(회원가입) 되어야 한다.
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const [nickname, setNickname] = useState("");
const navigate = useNavigate();
const handleJoin = async (e) => {
e.preventDefault();
try{
const joinData = {
id,
password,
nickname
}
const response = await handleUserRegister(joinData);
if(response.success) {
alert("회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.")
navigate("/login");
}else{
alert(response.message || "회원가입이 실패했습니다.");
}
}catch(e){
console.log("Join Error =>", e.response || e);
alert("회원가입을 실패했습니다.")
}
}
-
상태관리
-
useState
를 사용해서 is,password,nickname를 입력받아 저장한다.
-
-
useNavigate
- 라우터 훅을 사용하여 로그인 후 페이징 처리
-
handleJoin
- 회원가입 처리 함수이며, 사용자가 폼을 제출하면 실행된다.
- JoinData(사용자가 입력한 아이디,비밀번호,닉네임를 객체로 생성)
- API 호출
-
handleUserRegister(JoginData)
함수를 통해 서버에 로그인 요청을 보냄- 성공 시 알럿 실행 및 로그인 페이지로 이동
- 실패 시 에러 메세지 출력
5-4) 마이페이지
회원가입 페이지에서 아이디, 비밀번호. 닉네임를 입력 후 버튼을 클릭했을 때 입력한 데이터가 저장(회원가입) 되어야 한다.
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const [nickname, setNickname] = useState("");
const navigate = useNavigate();
const handleJoin = async (e) => {
e.preventDefault();
try{
const joinData = {
id,
password,
nickname
}
const response = await handleUserRegister(joinData);
if(response.success) {
alert("회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.")
navigate("/login");
}else{
alert(response.message || "회원가입이 실패했습니다.");
}
}catch(e){
console.log("Join Error =>", e.response || e);
alert("회원가입을 실패했습니다.")
}
}
-
상태관리
- useState를 사용하여 사용자가 입력한 아이디, 비밀번호, 닉네임을 각각 id, password, nickname 상태에 저장합니다. 이 상태들은 input 필드의 값과 연결되어 있으며, 사용자가 입력할 때마다 상태가 업데이트됩니다.
-
useNavigate
- 라우터의 훅인
useNavigate
로그인이 되어 있지 않은 사용자를 로그인 페이지로 리다이렉트한다.
- 라우터의 훅인
-
useEffect
- 컴포넌트가 처음 렌더링될 때, 로그인이 되어 있는지 확인하고, 로그인이 안 되어 있으면 로그인 페이지로 이동한다. 로그인이 되어 있으면 서버로부터 사용자 정보를 가져와 상태에 저장한다.
-
닉네임 변경 처리
- 사용자가 새 닉네임을 입력하고 제출하면
handleNicknameChange
함수가 실행된다. 닉네임 변경 요청을 서버에 보내고, 성공 시 사용자 정보 상태를 업데이트하며 알림을 띄운다. 실패 시 오류 메시지를 출력한다.
- 사용자가 새 닉네임을 입력하고 제출하면
회고
로그인,회원가입,마이페이지까지 개인과제 가이드에 작성된 내용이다. 처음에 복붙을 진행하며 기분이 조금.. 그랬지만 가이드 해준 코드가 아마 내가 안보고 만든 것 보다 더 효율적으로 작성했을 거라 생각해서 코드를 이해하는 방향으로 진행했다. 이후 Tanstack Query의 커스텀 훅으로 변경하고 사용할 예정이다.
Keep - 현재 만족하고 있는 부분
가이드로 제공한 코드를 보며 이해가 안되는 부분이 없었다는 것
Problem - 불편하게 느끼는 부분
복붙 후 이해하기 보다는 직접 만들어보는 게 더 좋았을 거란 걸 알지만 시간에 쫒기며 편함을 택한 나에게 용서를..
Try - problem에 대한 해결책, 당장 실행 가능한 것
프로젝트가 끝나면 항상 해설영상이 있기에 이번 개인과제 제출 후 안보고 작성하고 해설 영상을 통해 문제점을 찾아볼것이다.