회원관리기능 - Part 2 로그인

Project Diary/React + Firebase (Snack ShoppingMall)

회원관리 기능의 두 번째 파트인 로그인 기능을 구현 방법 기록


로그인 기능은 사용자가 등록된 계정으로 로그인을 하여 개인화된 서비스를 이용할 수 있게 해주는 중요한 기능입니다.

 

 

1. 로그인 컴포넌트 구현

로그인 컴포넌트는 사용자가 입력한 이메일과 비밀번호를 받아 이를 검증하고, 성공 시 사용자 정보를 저장하는 역할을 합니다.

import React, { useState, useRef, useEffect } from "react";
import { fetchMembers, userLogin } from "@/store/member";
import { fetchCarts } from "@/store/product";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import LoginSectionBlock from "./LoginSectionBlock";

const LoginSection = () => {
  const navigate = useNavigate(); // 페이지 이동을 위해 useNavigate 사용
  const dispatch = useDispatch(); // Redux 액션 디스패치를 위해 사용
  const members = useSelector((state) => state.members.members); // Redux에서 members 상태를 가져옴
  const [userId, setUserId] = useState(""); // 사용자 ID 상태 관리
  const [userPw, setUserPw] = useState(""); // 사용자 비밀번호 상태 관리

  const userIdRef = useRef(""); // 사용자 ID 입력 필드 참조
  const userPwRef = useRef(""); // 사용자 비밀번호 입력 필드 참조

  const previousUrl = sessionStorage.getItem("previousUrl"); // 이전 URL 저장
  const choiceProduct = sessionStorage.getItem("choiceProduct"); // 선택한 제품 정보 저장

  useEffect(() => {
    dispatch(fetchMembers()); // 컴포넌트 마운트 시 members 데이터를 가져옴
  }, [dispatch]);

  const handleLogin = (e) => {
    e.preventDefault(); // 폼 제출 시 페이지 리로드 방지

    // 사용자 ID와 비밀번호 입력 검증
    if (!userId) {
      alert("이메일을 입력하세요.");
      userIdRef.current.focus();
      return;
    }
    if (!userPw) {
      alert("비밀번호를 입력하세요.");
      userPwRef.current.focus();
      return;
    }

    // 입력한 사용자 ID로 members에서 사용자 찾기
    let findUser = members.find((item) => item.userId === userId); 

    if (findUser) {
      // 사용자가 존재할 경우 비밀번호 검증
      if (findUser.userPw !== userPw) {
        alert("비밀번호가 틀렸습니다.");
        userPwRef.current.focus();
        return false;
      } else {
        // 비밀번호가 일치할 경우 로그인 처리
        dispatch(userLogin({ findUser }));
        dispatch(fetchCarts()); // 카트 정보 가져오기

        // 이전 URL에 따라 페이지 이동
        if (previousUrl === "/payment") {
          navigate(previousUrl, { state: JSON.parse(choiceProduct) });
          sessionStorage.removeItem("previousUrl");
        } else if (previousUrl === "/product" || previousUrl === "/cart") {
          navigate(previousUrl);
          sessionStorage.removeItem("previousUrl");
        } else {
          navigate("/");
        }
      }
    } else {
      alert("회원이 아닙니다.");
      userIdRef.current.focus();
      return false;
    }
  };

  return (
    <LoginSectionBlock>
      <h2>Login</h2>
      <form onSubmit={handleLogin}>
        <div className="loginWrap">
          <table>
            <colgroup>
              <col />
              <col />
            </colgroup>
            <tbody>
              <tr>
                <td>
                  <label htmlFor="userId">E-mail: </label>
                </td>
                <td>
                  <input
                    ref={userIdRef}
                    type="text"
                    id="userId"
                    name="userId"
                    placeholder="이메일을 입력해 주세요."
                    onChange={(e) => setUserId(e.target.value)}
                  />
                </td>
              </tr>
              <tr>
                <td>
                  <label htmlFor="userPw">Password: </label>
                </td>
                <td>
                  <input
                    ref={userPwRef}
                    type="password"
                    id="userPw"
                    name="userPw"
                    placeholder="비밀번호를 입력해 주세요."
                    onChange={(e) => setUserPw(e.target.value)}
                  />
                </td>
              </tr>
            </tbody>
          </table>
          <div className="btn">
            <button type="submit">Sign In</button>
          </div>
        </div>
      </form>
    </LoginSectionBlock>
  );
};

export default LoginSection;

 

  • 상태 및 참조 : useState와 useRef를 사용해 사용자 ID와 비밀번호를 상태로 관리하고, 해당 입력 필드를 참조합니다.
  • useEffect : 컴포넌트가 마운트될 때 fetchMembers 액션을 디스패치하여 members 데이터를 가져옵니다.
  • handleLogin 함수 : 로그인 버튼을 클릭하면 호출되는 함수로, 사용자가 입력한 이메일과 비밀번호를 검증하고, 로그인에 성공하면 Redux 상태를 업데이트하고 적절한 페이지로 이동합니다.

 

2.  Redux 설정 및 상태 관리

로그인 상태를 관리하기 위해 Redux 설정을 살펴보겠습니다. userLogin 액션을 사용해 로그인된 사용자 정보를 저장합니다.

import { createSlice } from "@reduxjs/toolkit";
import { kuwazawa_memberDB } from "@/assets/firebase";

const memberSlice = createSlice({
  name: "member",
  initialState: {
    members: [],
    user: null,
  },
  reducers: {
    // 멤버 초기화
    initMembers(state, action) {
      state.members = action.payload;
    },
    // 사용자 로그인
    userLogin(state, action) {
      const {
        key,
        userId,
        userIrum,
        userPw,
        handphone,
        addr1,
        addr2,
        zipCode,
      } = action.payload.findUser;

      state.user = {
        key,
        userId,
        userIrum,
        userPw,
        handphone,
        addr1,
        addr2,
        zipCode,
      };
      localStorage.loging = JSON.stringify({ key: key, userId: userId });
    },
    // 로컬 사용자 설정
    localUser(state, action) {
      const findUser = state.members.find(
        (item) => item.key === action.payload.key
      );
      state.user = findUser;
    },
    // 사용자 로그아웃
    userLogout(state, action) {
      state.user = null;
      localStorage.clear();
    },
  },
});

export const { initMembers, userLogin, userLogout, localUser } =
  memberSlice.actions;

// 멤버 가져오기 액션
export const fetchMembers = () => async (dispatch) => {
  try {
    kuwazawa_memberDB.on("value", (snapshot) => {
      const membersObj = snapshot.val();
      const membersArr = Object.entries(membersObj).map(([key, value]) => {
        return { key: key, ...value };
      });
      dispatch(initMembers(membersArr));
    });
  } catch (error) {
    console.error("오류:", error);
  }
};

export default memberSlice.reducer;

 

  • memberSlice: 로그인, 로그아웃, 멤버 초기화와 같은 액션을 정의합니다.
  • fetchMembers 함수: Firebase에서 멤버 데이터를 가져와 Redux 상태에 저장합니다.