Skip to content

Conversation

sungahChooo
Copy link

@sungahChooo sungahChooo commented Sep 20, 2025

구현 기능

1. 투두 입력, 완료, 삭제 기능

  • 사용자는 하단에 입력칸에 할일을 입력하고 Add버튼을 눌러 투두를 추가할 수 있습니다.
  • 투두 앞 체크 박스를 클릭하여 완료한 할일을 체크 표시할 수 있습니다.
  • 투두를 삭제하려면 투두의 오른편에 있는 ❌버튼을 클릭하면 삭제 됩니다.
  • 제목을 누르면 오늘 날짜를 기준으로 조회됩니다.
  • local storage에 저장되어 새로고침하거나 창을 껐다 켜도 그대로 조회가 가능합니다.

2. 요일 별 조회 기능

  • 사용자는 원하는 날짜를 클릭하여 그날에 입력한 투두를 조회할 수 있습니다.
  • 요일 양 끝에 있는 화살표를 눌러 전주와 다음주를 조회할 수 있습니다.
  • 조회한 주에서 날짜 및 요일을 클릭하면 그 날짜의 투두를 조회할 수 있으며 그 날의 총 할일과 완료한 할일의 개수를 제목 하단에서 조회할 수 있습니다.

Review Questions

✨1. Virtual-DOM은 무엇이고, 이를 사용함으로서 얻는 이점은 무엇인가요?

먼저, DOM은 요소들을 트리 형태로 표현한다. 흔히 우리가 아는 p태그 ,div태그 등이 여기에 속한다.

개발자들은 DOM이 제공하는 API를 이용해 이러한 DOM 구조에 접근하고, 내용이나 스타일을 변경할 수 있다. 이런 과정을 DOM 조작이라고 한다.

그렇다면, virtual-dom은 어떤 방식으로 작동할까?
virtual-dom은 실제 DOM의 가벼운 복사본으로 메모리 상에 존재한다. 렌더링 이전의 화면 구조와 렌더링 이후의 화면 구조를 가진 두 개의 가상 돔 객체를 유지하고 있고, 이 두 가상 돔을 비교하여 변경된 부분만 실제 DOM에 반영한다. virtual-dom은 이러한 방식으로 효율적인 방식으로 작동하여 이것을 사용하면 성능이 좋아진다. 이번 과제 때 사용한 React가 이 방식을 사용한다.

✨2. React.memo(), useMemo(), useCallback() 함수로 진행할 수 있는 리액트 렌더링 최적화에 대해 설명해주세요. 다른 방식이 있다면 이에 대한 소개도 좋습니다.

리액트는 부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 기본적으로 리렌더링된다. 그런데, 값이나 함수가 실제로 바뀌지 않았는데도 불필요하게 리렌더링되는 경우가 생긴다. 바로, 이를 줄여주는 도구들이 바로 React.memo, useMemo, useCallback이다.

✅ React.memo()
컴포넌트를 기억해서, props가 바뀌지 않으면 리렌더링을 막아줌.
클래스 컴포넌트의 PureComponent 같은 역할.

const Child = React.memo(({ value }) => {
  console.log("Child 렌더링");
  return <div>{value}</div>;
});

// 부모
function Parent() {
  const [count, setCount] = useState(0);
  return (
    <>
      <Child value="고정된 값" />
      <button onClick={() => setCount(count + 1)}>+</button>
    </>
  );
}

👉 버튼을 눌러도 Child는 props가 안 바뀌었으니 렌더링 안 됨.

✅ useMemo()
계산 결과를 기억 해서, 의존 값이 변하지 않으면 다시 계산하지 않음.
복잡한 계산 로직이 있을 때 유용.

const heavyCalculation = (num) => {
  console.log("무거운 계산 실행");
  return num * 2;
};

function App({ number }) {
  const result = useMemo(() => heavyCalculation(number), [number]);
  return <div>{result}</div>;
}

👉 number가 바뀔 때만 다시 계산하고, 그렇지 않으면 저장된 값 재사용.

✅ useCallback()
함수를 기억해서, 의존 값이 변하지 않으면 같은 함수 객체를 재사용.
자식 컴포넌트에 콜백을 props로 넘길 때 불필요한 리렌더링 방지.

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("clicked");
  }, []); // 빈 배열 → 항상 같은 함수 재사용

  return <Child onClick={handleClick} />;
}

✨3. React 컴포넌트 생명주기에 대해서 설명해주세요.

생명주기 = 컴포넌트가 화면에 나타나고(마운트) → 상태가 바뀌며 다시 렌더링되고(업데이트) → 사라지는(언마운트) 전체 흐름

배포 링크

https://react-todo-22nd-810a6ofgl-chosungahs-projects.vercel.app/

느낀점 및 배운점은 velog에 작성하였습니다.

https://velog.io/@sungahchooo/CEOS-%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C

@KWONDU
Copy link
Member

KWONDU commented Sep 22, 2025

성아님 배포 링크가 지금 권한 문제가 있는 거 같아요 ㅜ-ㅜ 확인 한 번만 부탁드려요 !

@@ -0,0 +1,66 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
Copy link

Choose a reason for hiding this comment

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

이게 지금 사이트가 안보여서 잘은 모르겠는데... 안쓰는 폰트들이 들어가 있는 거 같아요

<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/pretendard/dist/web/static/pretendard.css"
/>
Copy link

Choose a reason for hiding this comment

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

링크로 하는 것도 괜찮은데, 다른 파일에 같이 넣어두면 보기 더 편할 거 같아요

@@ -0,0 +1,223 @@
import { useEffect, useState } from "react";
Copy link

Choose a reason for hiding this comment

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

코드들이 한 파일에 다 모여있어서, 다른 파일들로 분리해 놓으면 더 보기 편할 거 같아요

Copy link

@hammoooo hammoooo left a comment

Choose a reason for hiding this comment

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

주석이 상세하게 잘 작성되어 있어 코드 보기에 좋았습니다! 과제 고생하셨습니다

다만 app.jsx의 코드를 컴포넌트 별로 분리하면 더 좋은 코드가 될 것 같습니다!

Comment on lines +175 to +184
.map((todo, index) => ({ ...todo, index }))
.filter((todo) => todo.date === selectedDate)
.map((todo) => (
<TodoItem
key={todo.index}
style={{
textDecoration: todo.isDone ? "line-through" : "none",
color: todo.isDone ? "#aaa" : "inherit",
}}
>

Choose a reason for hiding this comment

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

index를 key로 사용하기보다 고유의 id를 부여해서 key로 사용해야 불필요한 리렌더링을 하지 않아서 더 좋은 코드가 될 것 같습니다!

Copy link
Member

@chaeyoungwon chaeyoungwon left a comment

Choose a reason for hiding this comment

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

과제하시느라 수고 많으셨습니다!!
배포 링크가 열리지 않아 직접 동작은 확인하지는 못했지만, 코드 잘 봤습니당

다음번에는 타입스크립트 적용과 컴포넌트 분리에 조금 더 집중해보셔도 좋을 것 같습니다! 🙂
그리고 블로그도 잘 보았습니다 👍

<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.png" />
Copy link
Member

Choose a reason for hiding this comment

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

type="image/svg+xml"은 SVG 전용 MIME 타입이라, png 형식의 이미지를 사용하실 경우에는 type="image/png"로 변경해주시면 좋을 거 같아요!

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
Copy link
Member

Choose a reason for hiding this comment

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

무헌님께도 리뷰 남겨드린 부분인데, lang="ko"로 지정하면 스크린 리더가 영어를 자동으로 올바르게 해석해줍니다!
따라서 한국어 콘텐츠가 많은 웹페이지라면 기본 언어를 lang="ko"로 지정하실 것을 추천드립니다. :D

[참고 자료]
https://codingeverybody.kr/html-lang-%EC%86%8D%EC%84%B1-%EC%98%AC%EB%B0%94%EB%A5%B8-%EC%9D%B4%ED%95%B4%EC%99%80-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95/

https://developer.mozilla.org/ko/docs/Web/HTML/Reference/Global_attributes/lang

Comment on lines +2 to +11
import {
AppContainer,
TodoList,
Title,
WeekCalendar,
Day,
AddArea,
ListArea,
TodoItem,
} from "./App.styles";
Copy link
Member

Choose a reason for hiding this comment

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

스타일 파일에서 스타일을 불러올 때는
import * as S from "./App.styles";처럼 네임스페이스를 지정하고,
S.AppContainer와 같이 접두어를 붙여 작성해주시면 좋습니다!
가독성도 높이고 스타일의 출처를 명확하게 해주는 장점이 있습니다:D

Comment on lines +102 to +108
<div
style={{
textAlign: "center",
marginBottom: "1rem",
fontWeight: 500,
color: "black",
}}
Copy link
Member

Choose a reason for hiding this comment

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

추후 유지보수를 위해, 중간에 인라인으로 작성된 스타일 부분도 별도의 컴포넌트로 분리해주시면 더 좋을 것 같습니다 👍

Comment on lines +86 to +92
const TitleClick = () => {
const today = new Date();
today.setHours(0, 0, 0, 0);
const startOfThisWeek = getStartOfWeek(today);
setCurrentWeekStart(startOfThisWeek);
setSelectedDate(today.getTime());
};
Copy link
Member

Choose a reason for hiding this comment

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

TitleClick 함수는 이벤트 핸들러 성격을 가지므로,
컴포넌트 네이밍과 혼동되지 않도록 camelCase (ex: handleTitleClick)로 작성해보시는 것도 좋을 것 같습니다 🙂

>
</button>
{"일월화수목금토".split("").map((day, i) => (
Copy link
Member

Choose a reason for hiding this comment

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

요일은
const DAYS = ["일", "월", "화", "수", "목", "금", "토"];

처럼 상수로 분리해두고, map을 활용해 렌더링해주셔도 좋을 것 같습니다!

setCurrentWeekStart(prev);
setSelectedDate(prev.getTime());
}}
aria-label="이전 주"
Copy link
Member

Choose a reason for hiding this comment

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

aria-label을 사용해주신 점 좋습니다! 👍

Comment on lines +114 to +115
&:hover {
}
Copy link
Member

Choose a reason for hiding this comment

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

&:hover {}가 현재는 비어 있어서 동작하지 않는데요, 필요 없다면 삭제하셔도 좋을 거 같네용

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants