아웃소싱 프로젝트 - Zustand 로 회원가입, 로그인 리팩토링

하영·2024년 9월 15일
1

팀프로젝트

목록 보기
8/27

팀프로젝트를 하면서 로그인, 회원가입을 추가하는게 좋을 것 같았다.
회원만 지도를 활용해서 생성할 수 있도록 하고 비회원은 간단한 검색기능만 사용하도록 하면 조금 더 좋지 않을까... 하는 생각..!

개인과제에서 짜둔 로그인, 회원가입 코드를 이번 기회에 Zustand로 꼭 만들어내고 싶었다.
그래서 바아로 스탠다드반 수업 강의도 다시 보고 강의 자료도 다시 들여다보면서 조금씩 바꿔보았다.

Zustand로 회원가입, 로그인 구현하기


01. 스탠다드반 로그인 유지 예시 코드 🧐

import { create } from "zustand";
import { persist } from "zustand/middleware";

const useAuthStore = create(
  persist(
    (set) => ({
      accessToken: "",
      avatar: "",
      nickname: "",
      success: false,
      userId: "",
      isLoggedIn: false,
      setAccessToken: (token) => set({ accessToken: token }),
      setAvatar: (avatar) => set({ avatar }),
      setNickname: (nickname) => set({ nickname }),
      setSuccess: (success) => set({ success }),
      setUserId: (userId) => set({ userId }),
      setIsLoggedIn: (isLoggedIn) => set({ isLoggedIn }),
    }),
    {
      name: "auth-storage", // 저장소 이름
      getStorage: () => localStorage, // 로컬 스토리지 사용
    }
  )
);

export default useAuthStore;

이 코드를 기반으로 개인과제로 만들었던 로그인, 회원가입 코드를 리팩토링해서 팀프로젝트 코드에 활용해볼 생각이다!


작업 과정 🚧

01. Zustand 설치 및 기본 사용법

//설치 드가자고 ✨
yarn add zustand
// src > zustand > bearsStore.js
import { create } from "zustand";

const useBearsStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

export default useBearsStore;
// src > App.jsx
import "./App.css";
import useBearsStore from "./zustand/bearsStore";

function App() {
  const bears = useBearsStore((state) => state.bears);
  const increasePopulation = useBearsStore((state) => state.increasePopulation);
  return (
    <div>
      <h1>{bears} around here ...</h1>
      <button onClick={increasePopulation}>one up</button>
    </div>
  );
}
export default App;
  1. zustand 를 활용한 Store 를 만들어서 전역상태관리를 할 코드 작성하기
  2. 만든 Storeimport 하고 코드 수정하기

02. 로그인, 회원가입 컴포넌트 정리 🔍

  • AuthContext.jsx : 전역상태관리 Context
  • AuthForm.jsx : 공통 Form 묶어둔 컴포넌트
  • SignUp.jsx : 회원가입 컴포넌트
  • Login.jsx : 로그인 컴포넌트
  • api / auth.js : 로그인, 회원가입 함수
  • axiosInstance / base.js : api 인스턴스 파일

03. 생성할 컴포넌트 및 삭제할 컴포넌트 정리 🔍

  • Zustand 스토어 생성하기 → authStore.jsx
  • Context 컴포넌트 제거
  • 로그인, 회원가입 코드 수정

04. Zustand 스토어 생성하기 👩🏻‍💻

// authStore.js
import { create } from "zustand";
import { persist } from "zustand/middleware"; // 새로고침 시 그대로 유지
import { register, login as loginAPI } from "../api/auth"; // 로그인, 회원가입 함수

const useAuthStore = create(
  persist(
    (set) => ({
      user: null,
      token: null,
      isAuthenticated: false,

      // 로그인 함수
      login: async (formData) => {
        try {
          const response = await loginAPI({
            id: formData.userId,
            password: formData.password
          });

          if (response.accessToken) {
            set({
              token: response.accessToken,
              user: response.user, // 서버 응답에서 유저 데이터 설정
              isAuthenticated: true
            });
          } else {
            alert("로그인에 실패했습니다.");
          }
        } catch (error) {
          console.error("로그인 실패:", error);
          alert("로그인에 실패했습니다. 다시 시도해주세요.");
        }
      },

      // 회원가입 함수
      signup: async (formData) => {
        try {
          const response = await register({
            id: formData.userId,
            password: formData.password,
            nickname: formData.nickname
          });

          if (response.success) {
            alert("회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.");
          } else {
            alert("회원가입에 실패했습니다.");
          }
        } catch (error) {
          console.error("회원가입 실패:", error);
          alert("회원가입에 실패했습니다. 다시 시도해주세요.");
        }
      },

      // 로그아웃 함수
      logout: () => {
        set({ user: null, token: null, isAuthenticated: false });
      }
    }),
    {
      name: "auth-storage"
    }
  )
);

export default useAuthStore;

코드가 조금 길게 보이는데 사실 try, catch문, 조건문을 빼고나면 정말 간단하게 구현했다.


04-1. Zustand 스토어 뜯어보기 🫨

// 🚧 내가 수정한 코드
const useAuthStore = create(
  persist(
    (set) => ({
      user: null,
      token: null,
      isAuthenticated: false,

// 🔥 스탠다드반 코드
const useAuthStore = create(
  persist(
    (set) => ({
      accessToken: "",
      avatar: "",
      nickname: "",
      success: false,
      userId: "",
      isLoggedIn: false,

zustanduseStore 훅을 사용해 상태를 관리하는 방식을 채택하고 있어 이를 활용했다.

create , set , 그리고 새로고침이 되어도 로그인이 풀리지 않도록 persist 미들웨어를 사용해서 자동으로 localStorage에 저장되도록 했다.

// 🚧 내가 수정한 코드 (로그인)
    login: async (formData) => {
      try {
        const response = await loginAPI({
          id: formData.userId,
          password: formData.password
        });

        if (response.accessToken) {
          set({
            token: response.accessToken,
            user: response.user, // 서버 응답에서 유저 데이터 설정
            isAuthenticated: true
          });
        } else {
          alert("로그인에 실패했습니다.");
        }
      } catch (error) {
        console.error("로그인 실패:", error);
        alert("로그인에 실패했습니다. 다시 시도해주세요.");
      }
    },
    
    
// 🔥 스탠다드반 코드
  setAccessToken: (token) => set({ accessToken: token }),
  setAvatar: (avatar) => set({ avatar }),
  setNickname: (nickname) => set({ nickname }),
  setSuccess: (success) => set({ success }),
  setUserId: (userId) => set({ userId }),
  setIsLoggedIn: (isLoggedIn) => set({ isLoggedIn }),
  }),
  
  
  // 🚧 스탠다드반이랑 비슷하게 코드 지워보고 비교
    login: async (formData) => {
        if (response.accessToken) {
          set({
            token: response.accessToken,
            user: response.user,
            isAuthenticated: true
          });
        } 
    },

처음에는 스탠다드반 참고한 코드랑 너무 다르게 보여서 막막했는데 일단 주먹구구식으로 기존에 작성했던 로그인 함수를 살짝씩 수정해서 넣어보니 구현이 되었다.

스탠다드반에서 만든 코드는 state값을 넣어준 것 같고 나는 api를 불러오고 함수로 처리하는 과정이라 다르게 느껴진 것 같다. 그래도 어디에서든 login함수를 뽑아서 쓸 수 있고, setAccessToken을 사용할 수 있다는 부분은 동일하다.

{
  name: "auth-storage"
}

namelocalStorage에 저장될 키 이름이다.


05. 로그인, 회원가입 코드 수정 👩🏻‍💻

//Login.jsx
import { Link } from "react-router-dom";
import AuthForm from "../components/AuthForm";
import useAuthStore from "../stores/authStore"; //zustand store 가져오기

const Login = () => {
  const login = useAuthStore((state) => state.login); //zustand 코드 적용 👏

  const handleLogin = async (formData) => {
    await login(formData);
  };
  

  return (
    <div>
      <h1>로그인</h1>
      <div>
        <AuthForm mode="login" onSubmit={handleLogin} />
      </div>
      <div>
        <p>
          계정이 없으신가요?{" "}
          <Link to="/signup" >
            회원가입
          </Link>
        </p>
      </div>
    </div>
  );
};

export default Login;
//SignUp.jsx
import AuthForm from "../components/AuthForm";
import { Link, useNavigate } from "react-router-dom";
import useAuthStore from "../stores/authStore"; //zustand store 가져오기

const Signup = () => {
  const signup = useAuthStore((state) => state.signup); //zustand 코드 적용 👏
  const navigate = useNavigate();

  const handleSignup = async (formData) => {
    await signup(formData);
    navigate("/login");
  };

  return (
    <div>
      <h1>회원가입</h1>
      <AuthForm mode="signup" onSubmit={handleSignup} />
      <div>
        <p>
          이미 계정이 있으신가요?{" "}
          <Link to="/login">
            로그인
          </Link>
        </p>
      </div>
    </div>
  );
};

export default Signup;

06. 실행화면 확인 ✅


이렇게 하면 회원가입도 데이터도 잘 들어가고, localStorage 코드를 작성하지 않았음에도 persist 미들웨어 때문에 저장되는 걸 확인할 수 있다!


zustand 문법이 낯설어서 전체적인 코드 길이에 비해 오래걸리긴 했는데 확실히 한번 적용해보고 나니까 왜 쌤들이나 같이 수업 듣는 분들이 코드가 훨씬 짧아졌다고 했는지 이해되었다!
앞으로도 여러번 써보면서 다양한 곳에서 휘뚤마뚤 쓸 수 있도록 연습해야지!🔥

profile
왕쪼랩 탈출 목표자의 코딩 공부기록

0개의 댓글

관련 채용 정보