Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2주차] 고주희 미션 제출합니다. #1

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b1ac074
feat: 기본 파일구조 정의, input hook 및 컴포넌트 생성
ZUITOPIA Sep 2, 2024
5773413
feat: 파일 구조 나누기 전으로 돌리고 전체적인 기능 먼저 한 파일에 구현하기
ZUITOPIA Sep 2, 2024
a4bb4f0
feat: todo에서 done으로, done에서 다시 todo로 갈 수 있게 toggle 버튼으로 변경
ZUITOPIA Sep 2, 2024
c83acb3
feat: 삭제 기능 추가
ZUITOPIA Sep 2, 2024
fb3aea3
feat: 로컬스토리지 값을 관리하는 hook 생성
ZUITOPIA Sep 2, 2024
4ef040f
feat: 반복되는 부분(todo list) 같은 컴포넌트 재사용하도록 수정
ZUITOPIA Sep 2, 2024
4aba320
fix: TodoList는 재사용하는 컴포넌트이므로 Content로 이름 변경 후 shared 폴더로 위치 변경
ZUITOPIA Sep 2, 2024
b2a22d4
style: reset.css 적용, UI 컴포넌트 생성, 스타일 입히기 시작
ZUITOPIA Sep 3, 2024
4229727
style: 스타일 수정 진행중
ZUITOPIA Sep 3, 2024
faaa434
style: 기본 스타일 구현 완료
ZUITOPIA Sep 3, 2024
7cd2a1e
fix: 입력된 할 일이 없을 때 alert 처리
ZUITOPIA Sep 3, 2024
5fc163a
fix: 컴포넌트 이름 import 시 소문자를 대문자로
ZUITOPIA Sep 3, 2024
8e2b311
Merge pull request #1 from ZUITOPIA/zuitopia
ZUITOPIA Sep 3, 2024
c654aaa
feat: styles 폴더 분리
ZUITOPIA Sep 4, 2024
edde4be
Merge branch 'main' of https://github.com/ZUITOPIA/react-todo into main
ZUITOPIA Sep 4, 2024
1c8a149
style: 진행중, 완료 문구 및 위치 수정
ZUITOPIA Sep 4, 2024
687e1bb
style: 사소한 margin 수정
ZUITOPIA Sep 4, 2024
81f27ff
fix: 반복되는 코드 더 간략하게 TaskItem과 Task로 분리
ZUITOPIA Sep 4, 2024
bc91bb8
style: 완료한 todo는 선 그어주는 효과 적용 추가
ZUITOPIA Sep 4, 2024
c7cc678
feat: todo, done 추가할 때 기존 배열의 앞에 추가하여 최신순 정렬처럼 보이게 수정
ZUITOPIA Sep 4, 2024
25f4859
style: 글꼴 수정
ZUITOPIA Sep 4, 2024
d073816
style: 파비콘 변경
ZUITOPIA Sep 4, 2024
52584a1
feat: Task 관련 뷰 묶기
ZUITOPIA Sep 4, 2024
a3cc61b
style: 디자인 수정 및 todo와 done 위치 수정
ZUITOPIA Sep 4, 2024
0208232
style: 아케이드 배경 고정
ZUITOPIA Sep 4, 2024
07a59b1
style: 배경 검정색으로 변경
ZUITOPIA Sep 4, 2024
9f9d6fc
style: 게임 이모지 위치 변경
ZUITOPIA Sep 4, 2024
b02d33f
refactor: 의미 없는 package-lock.json 파일 지우기
ZUITOPIA Sep 5, 2024
32e1632
refactor: 이벤트 핸들러 이름 네이밍 맞춰서 변경하기
ZUITOPIA Sep 5, 2024
7ab005d
refactor: 기존 setState 함수 사용 시 콜백함수 이용하도록 수정
ZUITOPIA Sep 5, 2024
d20fb37
refactor: Task라는 단어는 모두 Todo로 변경
ZUITOPIA Sep 5, 2024
49a62c1
refactor: Todo Header 컴포넌트 분리
ZUITOPIA Sep 5, 2024
d00702e
refactor: reset.css 파일 대신 Global 컴포넌트 이용하기
ZUITOPIA Sep 5, 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
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
"singleQuote": false
Copy link
Member

Choose a reason for hiding this comment

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

프리티어 설정 확인해 주세요!

}
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 Down Expand Up @@ -33,6 +33,7 @@ export default [
'warn',
{ allowConstantExport: true },
],
'react/prop-types': 'off', // prop-types 규칙 비활성화
},
},
]
];
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!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="/check.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Todo</title>
<title>Schedule</title>
</head>
<body>
<div id="root"></div>
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"prop-types": "^15.8.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"uuid": "^10.0.0"
},
Copy link
Member

Choose a reason for hiding this comment

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

저희는 yarn을 사용하기 때문에 package-lock.json 파일은 지우셔도 될 것 같습니다 🙂
참고 자료 - 패키지 잠금 파일 (package-lock.json, yarn.lock)

"devDependencies": {
"@eslint/js": "^9.9.0",
Expand Down
Binary file added public/arcade.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/check.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/emoji.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/game.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/game2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
217 changes: 213 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,214 @@
function App() {
return <div>React Todo</div>;
}
import TodoSection from "./components/shared/TodoSection";
import useLocalStorage from "./components/hooks/useLocalStorage";
import { Style } from "./components/styles/App.styles";
import Header from "./Header";
import { Global, css } from "@emotion/react";

export default function App() {
const [todos, setTodos] = useLocalStorage("todos", []);
Copy link
Member

Choose a reason for hiding this comment

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

오호 로컬스토리지 관련 로직을 커스텀훅으로 만드셨군요 👍🏻👍🏻

const [completed, setCompleted] = useLocalStorage("completed", []);

const handleFormSubmit = (todo) => {
setTodos((prev) => [todo, ...prev]);
};

const handleToggleTodo = (index, isCompleted) => {
if (isCompleted) {
const newCompleted = completed.filter((_, i) => i !== index);
Copy link
Member

Choose a reason for hiding this comment

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

이벤트 핸들러 네이밍 잘 지켜주셨군요 ! 저도 잘 지켜야 할 텐데 말입니다..

const todoToMove = completed[index];
setCompleted(newCompleted);
setTodos((prev) => [todoToMove, ...prev]);
} else {
const newTodos = todos.filter((_, i) => i !== index);
const completedTodo = todos[index];
setTodos(newTodos);
setCompleted((prev) => [completedTodo, ...prev]);
}
};

const deleteTodo = (index) => {
const newTodos = todos.filter((_, i) => i !== index);
setTodos(newTodos);
};

const deleteDone = (index) => {
const newCompleted = completed.filter((_, i) => i !== index);
setCompleted(newCompleted);
};

export default App;
return (
<div>
<Global
styles={css`
@font-face {
font-family: "DungGeunMo";
src: url("https://fastly.jsdelivr.net/gh/projectnoonnu/[email protected]/DungGeunMo.woff")
format("woff");
font-weight: normal;
font-style: normal;
}

html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
font-family: "DungGeunMo";
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #595ca1;
background-image: url("/public/arcade.png");
background-size: 1430px;
background-position: 49%;
background-repeat: no-repeat;
}
input {
font-family: "DungGeunMo";
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
`}
/>

<Style.AppWrapper>
<Header handleFormSubmit={handleFormSubmit} />

<Style.TodoSectionWrapper>
<TodoSection
title="IN PROGRESS"
items={todos}
onToggle={handleToggleTodo}
onDelete={deleteTodo}
isCompleted={false}
/>

<TodoSection
title="COMPLETED"
items={completed}
onToggle={handleToggleTodo}
onDelete={deleteDone}
isCompleted={true}
/>
</Style.TodoSectionWrapper>
</Style.AppWrapper>
</div>
);
}
15 changes: 15 additions & 0 deletions src/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Input from "./components/shared/Input";
import { Text } from "./components/shared/UI";
import { Style } from "./components/styles/App.styles";

export default function Header({ handleFormSubmit }) {
return (
<Style.Header>
<Style.TitleWrapper>
<Style.GameIcon src="game2.png" alt="game" />
<Text.Title>SCHEDULE</Text.Title>
</Style.TitleWrapper>
<Input addTodo={handleFormSubmit} />
</Style.Header>
);
}
11 changes: 11 additions & 0 deletions src/components/hooks/useInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useState, useCallback } from "react";

export default function useInput() {
const [value, setValue] = useState("");

const handleChange = useCallback((event) => {
setValue(event.target.value);
}, []);

return [value, handleChange, setValue];
}
16 changes: 16 additions & 0 deletions src/components/hooks/useLocalStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
});

useEffect(() => {
localStorage.setItem(key, JSON.stringify(storedValue));
}, [key, storedValue]);

return [storedValue, setStoredValue];
}
Comment on lines +3 to +14
Copy link

Choose a reason for hiding this comment

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

저도 이런식으로 localstorage를 따로 뺴서 적용해봐야겠네요! 덕부에 배워갑니다!

Copy link
Member

Choose a reason for hiding this comment

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

저는 아래 코드처럼 로컬스토리지를 구현했습니다. 근데 주희님은 커스텀훅으로 따로 만드셨더라구요! 따로 만들게 된 이유와 따로 만들게 되면 이점이 무엇인지 궁금합니다.

 // 처음 컴포넌트 렌더링될 때 로컬 스토리지에서 할 일 목록 불러오기
  const [todoList, setTodoList] = useState(() => {
    const savedTodoList = localStorage.getItem('todoList');
    return savedTodoList ? JSON.parse(savedTodoList) : [];
  });

  // todoList가 변경될 때마다 로컬 스토리지에 저장
  useEffect(() => {
    localStorage.setItem('todoList', JSON.stringify(todoList));
  }, [todoList]);


export default useLocalStorage;
30 changes: 30 additions & 0 deletions src/components/shared/Input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import useInput from "../hooks/useInput";
import { Style } from "../styles/Input.styles";

export default function Input({ addTodo }) {
const [inputValue, handleChange, setInputValue] = useInput("");
Comment on lines +1 to +5
Copy link

Choose a reason for hiding this comment

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

useInput 훅을 활용해 입력 값의 상태를 관리하는 부분이 매우 깔끔한 것 같습니다!!


const handleFormSubmit = (event) => {
event.preventDefault();
if (inputValue.trim() !== "") {
addTodo(inputValue);
setInputValue("");
} else {
alert("입력된 할 일이 없어요, 다시 확인해주세요!");
}
};

return (
<form onSubmit={handleFormSubmit}>
<Style.InputWrapper>
<Style.Input
type="text"
value={inputValue}
onChange={handleChange}
placeholder="할 일을 입력해주세요."
/>
<Style.Button type="submit">+</Style.Button>
</Style.InputWrapper>
</form>
);
}
18 changes: 18 additions & 0 deletions src/components/shared/Todo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import TodoItem from "./TodoItem";
import { Style } from "../styles/Todo.styles";

export default function Todo({ items, onToggle, onDelete, isCompleted }) {
return (
<Style.Wrapper>
{items.map((todo, index) => (
<TodoItem
key={index}
todo={todo}
isCompleted={isCompleted}
onToggle={() => onToggle(index, isCompleted)}
onDelete={() => onDelete(index)}
/>
))}
</Style.Wrapper>
);
}
Loading