LocalStorage 사용하기

조뮁·2023년 1월 23일

React

목록 보기
30/34

https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

sessionStorage

  • 웹 브라우저가 켜져있는 동안만 데이터를 저장해둠
  • 브라우저가 닫히면 데이터가 날아감
  • 데이터를 서버로 전송하지 않음

localStorage

  • 브라우저를 닫았다 열어도 데이터가 남아있음
  • 데이터 저장 유효기간이 없고, js를 사용하거나 브라우저 캐시 or 로컬 저장 데이터를 삭제해야 데이터가 사라짐
  • 본인의 웹브라우저에 저장되기 때문에 다른 사람이 훔쳐볼 수 없음

저장공간 크기
localStorage > sessiongStorage > cookie


LocalStorage 이용 방법

1. 데이터 저장

// App.js

function App() {
  useEffect(() => {
    localStorage.setItem("key", 10);
  }, []);
  
  ...

  • localStorage.setItem값을 바꾸는 경우, 한 번 입력된 값은 localStorage에 계속 저장되어 있음

  • value로 객체 저장 시, JSON.stringify() 로 변환해줘야함

useEffect(() => {
    localStorage.setItem("item1", 10);
    localStorage.setItem("item2", "200");
    localStorage.setItem("item3", JSON.stringify({ value: 30 }));
  }, []);


2. 데이터 사용

useEffect(() => {
    const item1 = localStorage.getItem("item1");
    const item2 = localStorage.getItem("item2");
    const item3 = localStorage.getItem("item3");
    console.log(item1, item2, item3);
  }, []);

  • 근데, item1 은 숫자타입인데 문자타입으로 꺼내와짐.
    • localStorage에 저장될 때 모두 문자열로 변경됨.
    • 문자형에 맞게 parseInt, JSON.parse() 등과 같이 문자형 변환을 시켜줘야함
useEffect(() => {
    const item1 = parseInt(localStorage.getItem("item1"));
    const item2 = localStorage.getItem("item2");
    const item3 = JSON.parse(localStorage.getItem("item3"));
    console.log({ item1, item2, item3 });
  }, []);

LocalStorage에 일기데이터 저장

const reducer = (state, action) => {
  let newState = [];
  switch (action.type) {
    case "INIT": {
      return action.data;
    }

    case "CREATE": {
      newState = [action.data, ...state];
      break;
    }

    case "REMOVE": {
      newState = state.filter((it) => it.id !== action.targetId);
      break;
    }

    case "EDIT": {
      newState = state.map((it) =>
        it.id === action.data.id ? { ...action.data } : it
      );
      break;
    }

    default:
      return state;
  }
  /* 일기데이터의 변환은 모두 reducer에서 처리되기 때문에,
  newState가 변경될때마다 reducer에서 localStorage로 저장하면됨 */
  localStorage.setItem("diary", JSON.stringify(newState));
  return newState;
};


초기 일기 데이터를 localStorage데이터로 설정하기

// App.js
const reducer = (state, action) => {
  let newState = [];
  switch (action.type) {
    case "INIT": {
      return action.data;
    }

    case "CREATE": {
      newState = [action.data, ...state];
      break;
    }

    case "REMOVE": {
      newState = state.filter((it) => it.id !== action.targetId);
      break;
    }

    case "EDIT": {
      newState = state.map((it) =>
        it.id === action.data.id ? { ...action.data } : it
      );
      break;
    }

    default:
      return state;
  }
  /* 일기데이터의 변환은 모두 reducer에서 처리되기 때문에,
  newState가 변경될때마다 reducer에서 localStorage로 저장하면됨 */
  localStorage.setItem("diary", JSON.stringify(newState));
  return newState;
};

function App() {
  useEffect(() => {
    const localData = localStorage.getItem("diary");
    if (localData) {
      // 새로 생성할 dataId의 current값은 diary data에서 가져온 가장 높은 id의 +1을 해줘야 함 -> diaryList를 sort하여 가장 높은 값 가져오기
      // id기준으로 내림차순으로 정렬함수 생성
      const diaryList = JSON.parse(localData).sort(
        (a, b) => parseInt(b.id) - parseInt(a.id)
      );
      dataId.current = parseInt(diaryList[0].id) + 1;

      dispatch({ type: "INIT", data: diaryList });
    }
  }, []);
  ...
}

일기 삭제하기

  • 상세페이지에서 삭제하기 버튼 추가
// DiaryEditor.js
const DiaryEditor = ({ isEdit, originData }) => {
  const navigate = useNavigate();
  const [date, setDate] = useState(getStringDate(new Date()));
  const [slctEmotionId, setslctEmotionId] = useState(3);
  const [content, setContent] = useState("");
  const contentRef = useRef();
  // onRemove import
  const { onCreate, onEdit, onRemove } = useContext(DiaryDispatchContext);

  const handleSubmit = () => {
    if (content.length < 1) {
      contentRef.current.focus();
      return;
    }

    if (
      window.confirm(
        isEdit ? "일기를 수정할까요?" : "새로운 일기를 추가할까요?"
      )
    ) {
      if (!isEdit) {
        onCreate(date, content, slctEmotionId);
      } else {
        onEdit(originData.id, date, content, slctEmotionId);
      }
    }
    navigate("/", { replace: true });
  };

  // 일기 삭제 함수
  const handleRemove = () => {
    if (window.confirm("일기를 삭제하시겠습니까?")) {
      onRemove(originData.id);
      navigate("/", { replace: true });
    }
  };
  
  ...
  
  return (
    <div>
      <MyHeader
        headText={isEdit ? "일기 수정하기" : "새 일기쓰기"}
        leftChild={
          <MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)}></MyButton>
        }
        // 오른쪽 버튼으로 삭제하기 추가
        // 수정상태일때만 버튼 노출되도록 설정
        rightChild={
          isEdit && (
            <MyButton
              text={"삭제하기"}
              type={"negative"}
              onClick={handleRemove}
            />
          )
        }
)

  • 리스트 페이지에서 삭제하기 버튼 추가
import { useNavigate } from "react-router-dom";
import { useContext } from "react";
import { DiaryDispatchContext } from "./../App.js";
import MyButton from "./MyButton";

const DiaryItem = ({ id, emotion, content, date }) => {
  const navigate = useNavigate();
  const { onCreate, onEdit, onRemove } = useContext(DiaryDispatchContext);

  const env = process.env;
  env.PUBLIC_URL = env.PUBLIC_URL || "";

  // 날짜 객체 형식 변경
  const strDate = new Date(parseInt(date)).toLocaleDateString();

  // 일기 삭제 함수
  const handleRemove = () => {
    if (window.confirm("선택한 일기를 삭제하시겠습니까?")) {
      onRemove(id);
      navigate("/", { replace: true });
    }
  };

  return (
    <div className="DiaryItem">
      <div
        className={[
          "emotion_img_wrapper",
          `emotion_img_wrapper_${emotion}`,
        ].join(" ")}
        onClick={() => navigate(`/diary/${id}`)}
      >
        <img src={process.env.PUBLIC_URL + `assets/emotion${emotion}.png`} />
      </div>
      <div className="info_wrapper" onClick={() => navigate(`/diary/${id}`)}>
        <div className="diary_date">{strDate}</div>
        <div className="diary_content_preview">{content.slice(0, 25)}</div>
      </div>
      <div className="btn_wrapper">
        <MyButton text={"삭제하기"} type={"negative"} onClick={handleRemove} />
        <MyButton
          text={"수정하기"}
          onClick={() => navigate(`/edit/${id}`)}
        ></MyButton>
      </div>
    </div>
  );
};

export default DiaryItem;

0개의 댓글