본문 바로가기
SeSAC

[코딩온] 프론트 혼자 로그인 구현 과정 recoil-persist (feat. 실패)

by 새파란레몬 2023. 12. 17.

주요 내용 출처  : ( https://www.daleseo.com/js-web-storage/ )

 

1인 프론트 프로젝트를 하다보니 새로운 문제가 계속 발생하는 것 같다.

웹 사이트의 가장 기본인 로그인 페이지를 구현하고 싶었는데 생각해보니 로그인을 하려면 그 정보를 어딘가에 저장해야 했다….!

우선 recoil을 최대한 활용해볼 예정이라 관련해서 구글링 중 웹 로컬 스토리지를 발견했다.

웹 스토리지는 로컬 스토리지 vs 세션 스토리지로 나뉜다.

세션 스토리지는 각 창이나 탭이 생기면 데이터가 생생되고 창이나 탭이 닫히면 데이터가 없어진다.

하지만 로컬 스토리지는 동일한 컴퓨터 동일한 브라우저를 쓴다는 요건만 충족되면, 데이터 영속성(persistence)에 의해 그 정보를 계속 활용할 수 있게 된다.

localStorage 기본 code

// 키에 데이터 쓰기
localStorage.setItem("key", value);

// 키로 부터 데이터 읽기
localStorage.getItem("key");

// 키의 데이터 삭제
localStorage.removeItem("key");

// 모든 키의 데이터 삭제
localStorage.clear();

// 저장된 키/값 쌍의 개수
localStorage.length;

사용 예시

> localStorage.getItem('name')
null
> localStorage.getItem('email')
null
> localStorage.setItem('email', 'test@user.com')
undefined
> localStorage.getItem('email')
"test@user.com"
> localStorage.setItem('email', 'test@admin.com')
undefined
> localStorage.getItem('email')
"test@admin.com"
> localStorage.removeItem('email')
undefined
> localStorage.getItem('email')
null

주의사항 → 문자열만 저장

일반 object 저장시 다음과 같이 나오게 된다.

> localStorage.setItem('obj', {a: 1, b: 2})
undefined
> localStorage.getItem('obj')
"[object Object]"

해결방법

> localStorage.setItem('json', JSON.stringify({a: 1, b: 2}))
undefined
> JSON.parse(localStorage.getItem('json'))
{a: 1, b: 2}

stringify는 JSON 형태로 직렬화(serialization)를 해주고

parse 라는 역직렬화(deserialization)로 데이터를 그대로 얻을 수 있다.

 

구현 code

 

import React, { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import { loginState } from '../../state/userNameState';

export default function LoginForm() {
  //   const setUserName = useSetRecoilState(userNameState);
  //useSetRecoilState : 상태를 업데이트 하는 setter 함수.
  // 현재 로그인한 userName을 전역으로 관리

  const [loginInfo, setLoginInfo] = useRecoilState(loginState);
  const [input, setInput] = useState('');
  // const [loginState, setLoginState] = useState({
  //   isLogin: false,
  //   userName: '',
  // });
  const loginText = loginState.isLogin ? 'LOGOUT' : 'LOGIN';

  const onChangeInputHandler = (e) => {
    const text = e.target.value;
    setInput(text);
  };

  // const inputText = <input type="text" onChange={onChangeInputHandler} />;

  // const getUserName = window.localStorage.getItem('userName');

  // 로컬 스토리지에 유저 data 저장
  // useEffect(() => {
  //   // const storedUserName = window.localStorage.getItem('userName');
  //   // if (storedUserName) {
  //   //   setLoginInfo({
  //   //     isLogin: true,
  //   //     userName: storedUserName,
  //   //   });
  //   // }
  //   // console.log()
  // }, [setLoginInfo]); // 의존성 배열 설정 [state.userName] 의존성 배열과의 차이?
  // console.log('loginstate >>', loginState);

  const onClickSubmitHandler = (e) => {
    e.preventDefault();
    if (!loginInfo.isLogin) {
      // false 시 실행
      setLoginInfo({
        isLogin: true,
        userName: input,
      });

    } else {
      setLoginInfo({
        isLogin: false,
        userName: '',
      });
    }

    setInput('');
  };

  return (
    <div>
      <span>
        {loginInfo.isLogin ? (
          <h2>안녕하세요 {loginInfo.userName}</h2>
        ) : (
          <h2>로그인 X </h2>
        )}
      </span>
      <form>
        <input type="text" value={input} onChange={onChangeInputHandler} />
        <button type="button" onClick={onClickSubmitHandler}>
          {loginText}
        </button>
      </form>
    </div>
  );
}



로그인 페이지 내에서 구현하고 로컬 스토리지에 넣고 활용하는 것까지는 이렇게 성공했는데, 새로 고침 시 데이터가 날라갔다. localStorage의 정보를 불러오려면 매번 useEffect를 써야 할 것 같았다…. 모든 페이지에서 그런 과정을 거치는 건 비효율적으로 보여 recoil-persist를이용했다.

 

recoil-persist를 사용하면 새로 고침해도 정보를 유지시켜준다. 

import { atom } from 'recoil';

import { recoilPersist } from 'recoil-persist';

const { persistAtom } = recoilPersist({
  key: 'localStorage',
  storage: localStorage,
});

export const loginState = atom({
  key: 'loginState',
  default: { isLogin: false, userName: '' },
  effects_UNSTABLE: [persistAtom],
});

 

하지만 이렇게 아이디를 구현한다 하면, email이나 비밀번호 같은 것은 localStorage에 저장되면 정보가 그대로 노출되어 보안에 문제가 생길 것 같았다. bcrypt를 이용하는 방법도 있긴 하지만,,, 불완전한 방법 같았다.

 

++ 리더님에게 물어보았더니 firebase라는 백엔드를 활용할 수 있는 사이트가 있었다. 이를 이용한 사용자 데이터 베이스를 구축해 볼 예정이다. 본래 계획했던 '보고 싶은 영화 찜하기', 평점 기능 등을 이를 통해 구현할 예정이다.