Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f77ec81
Feat: Initial Setting
hyorish03 Sep 3, 2024
283fb0d
Feat: Vite 기본 마진 제거
hyorish03 Sep 3, 2024
2084c35
Revert "Feat: Vite 기본 마진 제거"
hyorish03 Sep 3, 2024
88b2c19
Feat: Vite 기본 마진 제거 및 StrictMode 제거
hyorish03 Sep 3, 2024
b8dda23
Feat: Header UI 구현
hyorish03 Sep 3, 2024
84775e7
Feat: TodoList UI 구현
hyorish03 Sep 3, 2024
b078703
Feat: DoneList UI 구현
hyorish03 Sep 3, 2024
c93b636
Feat: Header, TodoList, DoneList 컴포넌트 App.jsx에 import
hyorish03 Sep 3, 2024
c50916b
Feat: LocalStorage에 대한 get, set 함수 구현
hyorish03 Sep 3, 2024
af732c8
Fix: setLocalStorage 함수 내의 id 지정 방식 수정
hyorish03 Sep 3, 2024
fea26ee
Feat: addTodo 기능 구현
hyorish03 Sep 3, 2024
85abe44
Feat:TodoList 렌더링 기능 구현
hyorish03 Sep 3, 2024
031827f
Fix: localStorage에서 데이터를 불러와 todoList 상태 초기화
hyorish03 Sep 3, 2024
a7a470c
Refactor: useState 초기값으로 localStorage 데이터 사용하여 todoList 상태 초기화
hyorish03 Sep 3, 2024
f85502f
Refactor: 함수명 변경 setLocalStorage -> addToLocalStorage
hyorish03 Sep 3, 2024
f65969a
Fix: onKeyDown 한글 입력 시 두 번 실행되는 오류 수정
hyorish03 Sep 3, 2024
d957d87
Feat: todoList 완료여부 상태 변경 기능 구현
hyorish03 Sep 3, 2024
0de0688
Feat: Todo 삭제 기능 구현
hyorish03 Sep 3, 2024
eaf3c16
Feat: App에서 미완료/완료 리스트 구분 후 list 컴포넌트의 props로 전달하도록 수정
hyorish03 Sep 3, 2024
779bed8
Feat: 스타일링 수정 및 삭제버튼, todoItem 모듈화
hyorish03 Sep 3, 2024
5fd92bc
Refactor: Todo 항목 렌더링 모듈화
hyorish03 Sep 3, 2024
29b4b6e
Fix: 배경 색 스타일링 수정
hyorish03 Sep 3, 2024
b835b2d
Fix: 웹 사이트 아이콘 변경
hyorish03 Sep 3, 2024
32fe0fd
Feat: 배포확인용 커밋
hyorish03 Sep 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import js from '@eslint/js';
import globals from 'globals';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';

export default [
{ ignores: ['dist'] },
Expand All @@ -20,6 +20,7 @@ export default [
settings: { react: { version: '18.3' } },
plugins: {
react,
emotion,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
Expand All @@ -35,4 +36,4 @@ export default [
],
},
},
]
];
9 changes: 9 additions & 0 deletions index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
body {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emotion의 <Global> 컴포넌트를 이용하여 Global style을 적용해 봐도 좋을 것 같아요!
Emotion - Global Styles

margin: 0;
padding: 0;
box-sizing: border-box;
}

* {
box-sizing: border-box;
}
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/public/redHeart.svg" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디테일이 진짜 멋져요. 지나치기 쉬운데 이런 꼼꼼함을 배워야겠습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아이고 과찬이십니다

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

진짜 꼼꼼함 인정!!!저도 효린이 코드보고 많이 배웁니다!!!!

<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Todo</title>
</head>
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.13.3",
"eslint-plugin-emotion": "^11.0.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

devDependencies에 추가하면 좋을 것 같습니다.

"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
1 change: 1 addition & 0 deletions public/redHeart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 50 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
/** @jsxImportSource @emotion/react */

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다음과 같이 vite.config 수정하는 것으로 babel pragma를 따로 쓰지 않아도 됩니다.

export default defineConfig({
  plugins: [
    react({
      jsxImportSource: '@emotion/react',
    }),
  ],
});

그리고 이게 왜 필요한건지 찾아보시는 것도 좋을 것 같습니다.
https://emotion.sh/docs/css-prop

import { css } from '@emotion/react';
import Header from './Components/Header';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 경우 디렉토리 이름이 소문자인 것이 좋겠네요.

Suggested change
import Header from './Components/Header';
import Header from './components/Header';

import { useEffect, useState } from 'react';
import { getLocalStorage } from './utils/getLocalStorage';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utils로 따로 분리한 부분! 진짜 깔끔하고 제가 다음번에 꼭해야겠다고 생각했던 부분인데 또 한번 배우네요!

import List from './Components/List';

function App() {
return <div>React Todo</div>;
const [todoList, setTodoList] = useState(getLocalStorage() || []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로컬스토리지 관련 코드를 util 함수로 분리하니 훨씬 깔끔하네요 👍🏻

useEffect(() => {
if (getLocalStorage() === null) {
localStorage.setItem('todo', JSON.stringify([]));
}
}, []);
Comment on lines +9 to +14

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

초기 로컬스토리지 값은 undefined라 의미가 없는 코드일 것 같네요.
만약 값이 없을 경우 예외 처리 해주고 싶다면 이미 useState 쓸때 되어 있는 것 같아요.

const notDoneTodoList = todoList.filter((todo) => todo.done === false);
const doneTodoList = todoList.filter((todo) => todo.done === true);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그냥 Done기능 todoList 기능 다 작성했던 제가 하고 싶었던 코드예요..ㅠㅠㅠ 엄청 깔끔하고... 다음엔 꼭 이렇게 작성해보고 싶어요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

과분한 칭찬 너무 감사합니다 .. 🥹 js의 Array 메서드는 공부해두면 항상 도움이 되는 것 같아요 !

Comment on lines +10 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useEffect를 활용하여 로컬 스터리지 초기화 처리 저도 적용해보겠습니다! 직관적인 변수,함수명 제가 잘 못하는 부분인데 참고하겠습니다!

return (
<div
css={css({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

css() 함수 부분을 별도로 분리하여 작성하면 가독성이 더욱 좋아질 것 같아요!

width: '100vw',
height: '100vh',
color: 'black',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
background: 'linear-gradient(pink,skyblue)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

색상이 아주 예쁘네요 👍🏻

})}
>
<div
css={css({
width: '360px',
height: '600px',
backgroundColor: 'white',
borderRadius: '20px',
boxShadow: '0px 0px 25px rgba(0, 0, 0, 0.25)',
})}
>
<Header setTodoList={setTodoList} />
<List
listName="📋 TO DO"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 매직 스트링은 지양하는 것이 좋습니다. 상수로 분리해주세요.

Suggested change
listName="📋 TO DO"
const LISTNAME = {
TODO: "TO DO",
DONE: "DONE",
}
<List
listName={`📋 ${LISTNAME.TODO}`}

list={notDoneTodoList}
setTodoList={setTodoList}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 setState 함수를 그대로 전달하면 비즈니스 로직을 관리하기가 더 어려워집니다. 하위 컴포넌트들에서 마음대로 수정할 수 있는 점, 책임이 전가되는 점, 유지보수 측면 등.
따라서 미리 필요한 함수를 만들어서 전달하는 것이 좋겠네요.

ex) deleteTodo, addTodo ...

또한 이런 데이터를 조작하는 로직은 따로 분리하는 것이 좋겠습니다. -> useTodo()

/>
<List
listName="💿 DONE"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 매직 스트림

list={doneTodoList}
setTodoList={setTodoList}
/>
</div>
</div>
);
}

export default App;
18 changes: 18 additions & 0 deletions src/Components/DeleteButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { deleteFromLocalStorage } from '../utils/deleteFromLocalStorage';
import { getLocalStorage } from '../utils/getLocalStorage';

export const DeleteButton = ({ id, setTodoList }) => {
return (
<div
css={css({ cursor: 'pointer' })}
onClick={() => {
deleteFromLocalStorage(id);
setTodoList(getLocalStorage());
Comment on lines +11 to +12

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocalStorage를 왜 사용하는지 생각해보셨으면 좋겠습니다. LocalStorage를 데이터 원본 저장소처럼 활용하고 있는데요. 성능 면이나 관리 면에서 그냥 메모리에 있는 상태를 원본으로 두고 LocalStorage는 백업 용도로 사용하는 것이 좋겠습니다.

저라면 Todo State를 변경하고 그에 대한 Effect로 LocalStorage에 상태를 저장할 것 같네요.
그렇게 되면 여기서는 위에서 말한 deleteTodo 함수만 호출하면 됩니다.

}}
>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uills부분 한번 더 배우고갑니다! onClick이벤트 핸들러를 통해로컬 스토리지 적용한 부분 정말 간결하고 저도 이렇게 작성하고싶어요!

🗑️
</div>
);
};
68 changes: 68 additions & 0 deletions src/Components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { useEffect, useRef } from 'react';
import { addToLocalStorage } from '../utils/addToLocalStorage';
import { getLocalStorage } from '../utils/getLocalStorage';

function Header({ setTodoList }) {
const inputRef = useRef(null);

const addTodo = () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 말한 addTodo 함수를 미리 만들어서 받아오고 여기는 handleKeyDown을 처리하는 것이 좋겠습니다.
keydown을 처리하려는 관심사와 Todo를 추가하려는 관심사가 섞여있습니다.

const input = inputRef.current;
if (input.value === '') {
return;
}
addToLocalStorage(input.value);
input.value = '';
inputRef.current.focus();
setTodoList(getLocalStorage());
Comment on lines +15 to +18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 말했듯이 addTodo 함수를 미리 받아와서 쓰는 것이 좋겠네요.

};

useEffect(() => {
inputRef.current.focus();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻👍🏻

}, []);
Comment on lines +21 to +23

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

속성에 autoFocus를 활용하면 필요 없는 로직입니다.


return (
<div css={css({ height: '118px' })}>
<div css={css({ fontSize: '24px', margin: '18px 18px 0 18px' })}>
📚 투두리스트
</div>
<div
css={css({
width: '100%',
height: '81px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '10px',
borderBottom: 'solid 1px lightgray',
})}
>
<input
type="text"
ref={inputRef}
onKeyDown={(e) => {
if (e.nativeEvent.isComposing) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오잉? e.nativeEvent.isComposing 처음 보는데 이런 오류도 생길 수 있군요. 이번트 핸들링 관련 같은데 설명도 듣고싶어요. 이해가 안 가기 때문이죠..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Sieonn
저도 이 문제로 한참 애먹어서 블로그 글도 작성했었다죠... 그냥 자바스크립트 자체의 버그입니다 🥹
keydown/keyup에서 한글 입력 시 함수가 두 번 실행되는 경우

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그래서 keyDown 핸들러를 구현하는 것보다는 <form> 태그로 감싸서 사용하는 게 훨씬 편한 것 같아요!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헐 저도 오류는 처음보네요! 자바스크립트 자체 버그도 존재했다니ㅠㅠ저도 설명 듣고싶어요!

if (e.key === 'Enter') {
addTodo();
}
Comment on lines +44 to +48

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로직이 퍼져있는데요 handleKeyDown을 만들고 그 안에서 관리를 하는 것이 좋을 것 같습니다.

}}
css={css({
width: '288px',
height: '48px',
borderRadius: '15px',
border: 'solid 1px lightgray',
paddingLeft: '15px',
'&:focus': {
outline: 'none',
},
})}
placeholder="할 일을 입력하세요"
/>
<div onClick={addTodo}></div>
</div>
</div>
);
}

export default Header;
35 changes: 35 additions & 0 deletions src/Components/List.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { TodoItem } from './TodoItem';

function List({ list, setTodoList, listName }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

할일/완료한일 컴포넌트를 따로 분리하지 않고 List 컴포넌트 하나로 각각을 렌더링할 수 있도록 추상화한 점 정말 좋습니다!! 👍🏻👍🏻

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 추상화하는 부분을 어떻게해야할지가 너무 고민이였는데 효린님 코드보고 배우는거밖에 없는 것 같습니다!!

return (
<div
css={css({
height: '220px',
width: '100%',
padding: '18px 18px 0 18px',
borderBottom: listName == 'TO DO' ? 'solid 1px lightgray' : 'none',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listName이 실제로는 "📋 TO DO" 이런 식이라서 실제로는 적용이 안되고 있습니다. 역시 매직 스트링을 써서 문제가 되는 부분입니다.

})}
>
<div css={css({ fontSize: '18px', marginBottom: '15px' })}>
{listName} ({list.length})
</div>
<div
css={css({
width: '100%',
height: '156px',
overflowY: 'scroll',
display: 'flex',
flexDirection: 'column',
gap: '10px',
paddingRight: '20px',
})}
>
<TodoItem todoList={list} setTodoList={setTodoList} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 정말 구현하고 싶었던 내용이예요. 너무 깔끔해서 속이 시원합니다. 모양이 같은데 각각의 item과 섹션에만 영향을 줘야하는데 그런걸 처음에 다 설계를 하고 구현하시나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 아닙니다 !! 제 커밋 내역을 보면 알겠지만 처음엔 별 생각 없이 구현하다가 동일한 구조가 보이면 그때 컴포넌트화 시키는 편인 것 같습니다 ! 사실 처음부터 설계하고 개발하는게 최고겠지만.. 아직 많이 부족하네요 😅

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이밍이 어색한 부분이 있네요. TodoItem인데 list를 전달해주고 있어서 그런 것 같습니다. 현재 TodoItem의 역할이 단순 map만 돌고 있는데. 차라리 다음과 같이 하는 게 좋겠습니다.

Suggested change
<TodoItem todoList={list} setTodoList={setTodoList} />
todoList.map((todo, index) => (
<TodoItem key={index} todo={todo} setTodoList={setTodoList} />
)

</div>
</div>
);
}

export default List;
46 changes: 46 additions & 0 deletions src/Components/TodoItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { DeleteButton } from './DeleteButton';
import { updateFromLocalStorage } from '../utils/updateFromLocalStorage';
import { getLocalStorage } from '../utils/getLocalStorage';

export const TodoItem = ({ todoList, setTodoList }) =>
todoList.map((todo, index) => (
Comment on lines +7 to +8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 이어서, todo를 받고 todo에 대한 UI를 렌더링 하는 역할을 하는 것이 적절해보입니다.

<div
key={index}
css={css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '20px',
width: '100%',
})}
>
<div
css={css({
display: 'flex',
flexDirection: 'row',
textAlign: 'center',
alignItems: 'center',
width: '100%',
justifyContent: 'space-between',
})}
>
<div
css={css({
cursor: 'pointer',
textDecoration: todo.done ? 'line-through' : 'none',
Comment on lines +29 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

할 일 항목 완료여부에 따라 동적으로 설정하신 부분도 명확하게 보여서 너무 좋아요!!!!!

color: todo.done ? '#D3D3D3' : '#000',
textAlign: 'center',
})}
onClick={() => {
updateFromLocalStorage(todo.id);
setTodoList(getLocalStorage());
Comment on lines +37 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 순서가 로컬 스토리지에 반영 -> 로컬 스토리지에서 불러오기 -> 불러온 값으로 state 변경이 되고 있는데,
state 변경 -> 변경된 state를 로컬 스토리지에 적용 하는 방식이 더욱 간단할 것 같아요!

}}
>
{todo.todo}
</div>
<DeleteButton id={todo.id} setTodoList={setTodoList} />
</div>
</div>
));
13 changes: 4 additions & 9 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
<StrictMode>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 보고 StricMode가 뭔지 왜 자동으로 생성되어 감싸고 있는지 찾아보는 계기가 됐어요! 감사합니다. 휘발성 지식 획득~🦖

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 StrictMode를 사용하면 개발 과정에서 발생하는 버그를 빠르게 잡을 수 있다는 장점이 있지만,, 전 디버깅하는 과정에서 console.log가 2회씩 찍히는게 불편해서 삭제했습니다 😞

StrictMode 관련 문서 추천드려용 ! (공식문서임)
https://ko.react.dev/reference/react/StrictMode

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헐 저도 문서 같이 봐볼께요! 좋은 정보 감사합니다!

<App />
</StrictMode>,
)
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import '../index.css';
createRoot(document.getElementById('root')).render(<App />);
11 changes: 11 additions & 0 deletions src/utils/addToLocalStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { getLocalStorage } from './getLocalStorage';

export const addToLocalStorage = (value) => {
const prevTodo = getLocalStorage();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 따로 js를 분리해서 보니까 코드 보기가 더 편한 것 같네요!
이번에 진행하면서 컴포넌트, js, css 나누는 기준이나 방법이 너무 어려웠는데 이런 방법이 있다는 것을 배우고 갑니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 저도 이게 옳은 코드 스타일이다라고 말하긴 어렵지만 (아직은 이런 부분에 대한 확신이 없네요 ..🥲) 함수를 따로 js 파일로 빼서쓰니 깔끔하게 느껴져서 해봤씁니다 !

const newTodo = prevTodo.concat({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concat 메서드 사용 아주 좋습니다!!
개인적인 느낌이긴 하지만 실제 프로젝트를 진행하면서는 concat보다는 spread 연산자를 더 많이 사용하는 것 같아요!

id: prevTodo.length + 1,
todo: value,
done: false,
});
localStorage.setItem('todo', JSON.stringify(newTodo));
};
8 changes: 8 additions & 0 deletions src/utils/deleteFromLocalStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { getLocalStorage } from './getLocalStorage';

export const deleteFromLocalStorage = (id) => {
const prevTodo = getLocalStorage();
const newTodo = prevTodo.filter((todo) => todo.id !== id);
localStorage.setItem('todo', JSON.stringify(newTodo));
alert('삭제되었습니다.');
};
Comment on lines +1 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수 이름인 addToLocalStorage, deleteFromLocalStorage, getLocalStorage, updateFromLocalStorage가 각 함수의 역할을 명확하게 설명하고 있어서 코드를 이해하기 정말 쉬웠습니다! 함수 이름을 짓는 기준이 혹시 있으신가요? 저도 다음번에는 더 직관적이게 개선하고싶어요!

3 changes: 3 additions & 0 deletions src/utils/getLocalStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const getLocalStorage = () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 util 함수로 getLocalStorage라는 이름으로 함수를 만들고 싶었다면 key를 매개변수로 받아서 쓸수 있게 하는 것이 좋을 것 같습니다.

현재는 todo라는 관심사에 강하게 결합되어 있습니다.

return JSON.parse(localStorage.getItem('todo'));
};
14 changes: 14 additions & 0 deletions src/utils/updateFromLocalStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getLocalStorage } from './getLocalStorage';

export const updateFromLocalStorage = (id) => {
const prevTodo = getLocalStorage();
const newTodo = prevTodo.map((todo) => {
return todo.id === id
? {
...todo,
done: !todo.done,
}
: todo;
});
localStorage.setItem('todo', JSON.stringify(newTodo));
};
Loading