[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - TodoApp (2)

2025. 6. 13. 19:39·신한투자증권/프로 디지털 아카데미

 

신한투자증권 프로 디지털 아카데미 (프디아) 6기 교육 중 배운 내용을 작성한 글입니다
Day 31 (20250611)
 

 

 

이전 게시글에 이어 Todo App 만들기와 심화 문제에 대해 작성해보겠다.

 

 


연습문제 9

심화 (3) - 로컬스토리지에 저장하기

새로고침해도 데이터가 사라지지 않도록 하기 위해 Storage API를 이용하였다. 

개발자도구에서 application 들어가면 거기에 존재하는 저장소를 볼 수 있는데

그 중에 가장 많이 사용되는 게 local storage, session storage 가 있다.

 

이전 게시글에 storage api에 대해 조금 더 자세하게 기록해두었다.

 

이전 게시글 참고

 

[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - TodoApp

신한투자증권 프로 디지털 아카데미 (프디아) 6기 교육 중 배운 내용을 작성한 글입니다Day 30 (20250610) 어제 배운 개념 정리1. useMemo정의: 특정 값을 memoization(캐싱)하여 불필요한 재계산을 방지하

tyvkwygk.tistory.com

 

 

간단하게 설명하자면 

둘다 영구적인 저장소이지만 로컬 스토리지와 세션 스토리지는 저장하고 있는 기간이 다르다.

session storage : 브라우저가 종료되면 사라지는 스토리지

local storage : 브라우저가 종료되어도 존재하는 스토리지

쿠키 : 자바스크립트가 아니라 서버 측에서 저장함

 


🚨 문제점

 

로컬 스토리지에 데이터를 저장하려고 할 때 아래와 같은 코드로 하면 오류가 난다.

const [curColor, setCurColor] = useState(COLORS[0]);
const [todoList, setTodoList] = useState([]); // {color: string, text: string}

const addTodo = ({ text, color }) => {
    setTodoList((prevTodoList) => [...prevTodoList, { text, color }]);
    // 로컬스토리지 사용하여 데이터 저장하는 부분
    localStorage.setItem("todo-list", JSON.stringify(todoList))
 };

 

왜 오류가 날까?

 

state를 바꾸는 함수는 항상 비동기적으로 동작하기 때문이다.

이렇게만 코드를 짜게 되면 로컬스토리지에는 비동기적으로 추가되어 업데이트된 TodoList가 아니라

이전 TodoList가 저장될 수도 있다.

 

const addTodo = ({ text, color }) => {
    // setState 함수는 비동기로 동작한다
    const newTodoList = [...todoList, { id: uuidv4(), text, color }];
    setTodoList(newTodoList);

    // 로컬스토리지 사용하여 데이터 저장
    localStorage.setItem("todo-list", JSON.stringify(newTodoList));
  };

 

addTodo 함수를 이와같이 변경해주면 제대로 로컬 스토리지에 데이터가 저장된다.

위처럼 새로운 배열(newTodoList)를 stringify 하면 최신 상태가 정확하게 반영되어 저장된다.

 

 

또한 처음에 새로고침 했을 때 로컬스토리지에 데이터가 저장되었는지, 아닌지를 판단한 뒤,

데이터가 있다면 그 데이터를 불러오는 로직을 추가하였다.

// todoApp이 처음 만들어질 때 데이터가 있는지 없는지 체크해서 있으면 그거 불러와주는 로직도 추가
  useEffect(() => {
    const loadedTodoList = localStorage.getItem("todo-list");
    if (loadedTodoList) {
      setTodoList(JSON.parse(loadedTodoList));
    }
  }, []);

 

 

결과 확인

(좌) 입력 버튼을 눌렀을 때 localstorage에 추가된 데이터가 제대로 반영됨을 볼 수 있다 (우) 새로고침 후에도 이전 데이터가 남아있는 모습을 확인할 수 있다

 

 

 

심화(4) - 삭제 시키기

const addTodo = (text, color) => {
    if (!text.trim()) return;
    const newItem = { id: nextId, text, color };
    setTodoList((prev) => [...prev, newItem]);
    setNextId((id) => id + 1);
  };

 

나는 uuid를 사용하지 않고 임의로 숫자 1부터 시작하여 다음 번에는 id 값이 + 1 되는 형태로 구현하였다

 

 const addTodo = ({ text, color }) => {
    const newTodoList = [...todoList, { id: uuidv4(), text, color }];
    setTodoList(newTodoList);
    localStorage.setItem("todo-list", JSON.stringify(newTodoList));
  };

이와같이 id를 uuidv4()를 이용하여 unique한 아이디를 부여할 수도 있다.

 

id를 uuid를 사용하여 unique 한 id를 부여하였다.

수정 상태인지 아닌지에 대해서도 체크할 필요가 있다

지금은 id를 text가 아니라 id로 둔 상태이다.

 

 

🚨 문제점 발견!!

기본 상태에서 1번 todo의 수정 상태를 누른 상태이다.

 

이 상태에서 1번 삭제를 누르면

 

1번 글과 하얀색 color가 같이 사라져야 하는데 2번 text가 사라진 모습을 볼 수 있다.

 

 

 

왜 이상하게 동작할까?

 

map에서 React는 key를 기준으로 컴포넌트 재사용 여부를 판단한다.

 

배열에서 [a, b, c, ,d ,e]에서 a를 지우면 [b, c, d, e] 가 되는데

배열은 항상 인덱스가 0부터 시작하니까 인덱스 (0,1,2,3,4)에서 (0,1,2,3,4)로 바뀐다.

 

React는 key를 따로 지정하지 않으면 배열의 인덱스(idx)를 key로 사용하게 된다.

key 값이 이전에 전달받았던 0, 1, 2, 3, 4, 5, 에서 key는 0, 1, 2, 3, 4 이런식으로 계속 존재하게 되는데

이때 key가 존재하면 react에서는 이전 컴포넌트를 재사용하자! 라고 판단하게 된다.

 

 

key 값은 unique 한 것으로 넣어두자

 

 

컴포넌트는 state를 가지고 있는 놈이라 안될걸?? 기존꺼 가지고 있을거야.

이 key props를 전달한 value를 가지고 이 컴포넌트를 재사용할지 말지를 판단한다. 근데 이걸 index로 전달하게 되면, 배열의 특성 상 0번째 인덱스를 삭제해도 하나씩 땡겨져서 0번째 인덱스가 계속 살아있게 된다.

여기서 내가 key를 지정안하면  배열의 인덱스(idx) 를 key 값으로 사용하기 때문에 문제가 생긴다.

 

여기서 색과 text의 차이는 state이냐 props 이냐의 차이였다. (글자는 state, 색은 props)

컴포넌트는 state와 UI로 이루어져 있는데 여기서 컴포넌트에 종속적인 state는 재사용되어서 다시 안그리고 그대로 남아있게 된다. state가 이상하게 재사용한거라 이상하게 남아있게 되었고,

props는 리랜더링할때마다 바뀌니까 여기서 리랜더링 할 때 제대로 삭제가 반영되어 색깔은 잘 지워졌던 것이었다.

 

이에 대해서는 리액트의 Virtual Dom 개념을 이해하면 더 이해하기가 쉬워질 것이다.

Virtual DOM 개념에 대해서는 다음 게시글에서 다뤄보도록 하겠다.

 

(좌) 기본화면 (우) 기본화면에서 첫번째 운동하기의 수정하기를 누른 모습이다.

 

(좌) 위의 오른쪽 상태에서 운동하기 -> 운동하기1로 수정하기도 잘 반영된 모습이고 (우) 다시 운동하기의 수정을 누른 상태에서 삭제를 누른 모습이다.

 

수정하기도 제대로 동작하고, 수정 누른 상태로 삭제를 눌러도 잘 삭제되는 것을 볼 수 있다.

 

 

map을 사용할 때 key를 unique하게 주자! 

import TodoItem from "./TodoItem";

export default function TodoList({ todoList, removeTodo, editTodo }) {
  return (
    <>
      <ul
        style={{
          padding: 0,
          listStyleType: "none",
          display: "flex",
          flexDirection: "column",
          gap: 10,
        }}
      >
        {todoList.map(({ id, text, color }, idx) => {
          return (
            <TodoItem
              key={id} // key = {id}로 주자 추가! 
              // (idx로 주면 배열의 특성상 문제가 생김! 
              // 안쓰면 default로는 key = {idx} 로 들어감)
              todoId={id}
              text={text}
              color={color}
              removeTodo={removeTodo}
              editTodo={editTodo}
            />
          );
        })}
      </ul>
    </>
  );
}

 

 

 

심화(6) - useTodo 라는 Hook 만들어서 Component 내부에서 사용

Todo 라는 기능의 집합체를 분리해서 사용하려고 한다

context API에 대해서는 리액트의 전역 상태 관리 부분에서 다룬 적이 있다.

 

이전 게시글 참고

 

[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - React Hook

신한투자증권 프로 디지털 아카데미 (프디아) 6기 교육 중 배운 내용을 작성한 글입니다Day 29 (20250609) 배운 개념 정리 1. 얕은 비교와 깊은 비교깊은 비교 : Collection Data(Array, Object 등)의 경우 개별

tyvkwygk.tistory.com

 

간단히 다시 말하면 context api를 사용하는 이유는 전역적으로 리액트의 state를 관리하고 싶기 때문이다.

Provider와 Consumer로 이루어져 있다.

 

처음에 createContext(null)로 초기값을 null로 주었다.

그 이후에 TodoProvider function을 만들고 value 안에 todo 와 관련된 것을 넣으면 된다.

 

Provider는 Context로 전달할 값을 설정하는 컴포넌트로

이 값이 Provider 컴포넌트의 props로 전달되고

이 설정된 값을 Consumer에서 사용할 수 있게 된다

 

 

TodoApp의 최종 완성본이다! (꾸미기를 제외한..)

반응형

'신한투자증권 > 프로 디지털 아카데미' 카테고리의 다른 글

[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React & TypeScript)  (0) 2025.06.16
[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - TodoApp  (0) 2025.06.10
[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - React Hook 및 상태 관리  (3) 2025.06.09
'신한투자증권/프로 디지털 아카데미' 카테고리의 다른 글
  • [신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React & TypeScript)
  • [신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - TodoApp
  • [신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - React Hook 및 상태 관리
  • [신한투자증권 프로디지털아카데미] React.js & JavaScript 헷갈렸던 개념 정리
쇼파죠하
쇼파죠하
소프트웨어학과 코린이 성장기
  • 쇼파죠하
    코린이의 성장기
    쇼파죠하
  • 전체
    오늘
    어제
    • 분류 전체보기
      • TIL
        • 한달 기록
      • 신한투자증권
        • 프로 디지털 아카데미
      • 공부기록
        • AWS
        • 트러블슈팅
        • 머신러닝
        • 알고리즘
        • 자료구조알고리즘
        • CS
        • 파이썬
      • 코린이의 성장기
        • 코린이의 백준 도전기
        • 코린이의 성장기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 프디아
    • AWS
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    프디아
    알파코캠퍼스
    전공지식노트
    백준
    9월기록
    부트캠프
    K디지털트레이닝
    네트워크
    AWS
    프로 디지털 아카데미
    c언어
    파이썬
    프로디지털아카데미6기
    AI 전문과과정
    신한투자증권
    클라우드
    파이썬알고리즘인터뷰
    kdt교육
    코린이
    Computer Networking: a top down approach
    파이썬문법
    프로디지털아카데미
    LG Aimers
    CS
    알파코
    알고리즘
    react
    JavaScript
    영리한 프로그래밍을 위한 알고리즘
    aws 구조와 서비스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
쇼파죠하
[신한투자증권 프로디지털아카데미] 클라우드 기반 프론트엔드 개발(React) 프로그래밍 - TodoApp (2)
상단으로

티스토리툴바