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주차] 최서연 미션 제출합니다. #6

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

seoyeon5117
Copy link

@seoyeon5117 seoyeon5117 commented Mar 21, 2025

🔗 배포 링크

느낀점 & 배운점

처음 써보는 라이브러리들을 이것저것 추가하고 디자인도 리뉴얼하다보니까 바닐라 자바스크립트보다 오래 걸렸습니다. 그래도 확실히 리액트 사용하는게 훨씬 편한 것 같습니다! 오랜만에 스타일드 컴포넌트도 써봤는데 이미 테일윈드에 적응해버려서 테일윈드가 그리웠습니다,, 스타일드 컴포넌트로 background-image 추가할 때 url로 svg 파일을 추가가 안돼서 png로 추가했습니다. 파일 형식 문제인줄 몰라서 원인 찾는게 힘들었습니다... 그리고 다크모드 처음 적용해봐서 재미있었습니다ㅎㅎ

Key Questions

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

DOM에서 웹 페이지의 특정 요소의 색상을 변경하려면, 해당 요소를 찾고 색상을 변경한 후, 그 변경 사항을 적용하기 위해 해당 요소부터 하위 요소까지 브라우저가 화면을 다시 그리는데 가장 많은 비용이 발생합니다. 즉, 리플로우(Reflow) 및 리페인트(Repaint)에 대한 비용이 많이 발생하게 됩니다.

  • 리플로우(Reflow): DOM의 구조나 레이아웃이 변경되면 브라우저는 새로운 레이아웃을 계산하고 화면을 다시 그림.
  • 리페인트(Repaint): 요소의 색상이나 테두리 등 외양이 변경되면, 브라우저는 해당 요소를 다시 그림.

DOM은 새로운 요청이나 변경사항이 있을 때마다 전체를 다시 렌더링 합니다. 매번 전체를 렌더링 하기 때문에, 레이아웃의 양이 많을 경우 렌더링 속도가 눈에 띄게 느려집니다. 이를 해결하기 위해 React는 Virtual-DOM(가상돔)을 도입했습니다.

가상돔은 주로 React, Vue, Elm 등에서 사용되고, 실제 브라우저의 DOM이 아닌 가상의 DOM을 만들어 브라우저에 렌더링 합니다. 추상화된 복사본을 메모리에 유지하고 이를 통해서 변경사항을 적용시킵니다.

image
만약 Virtual-DOM을 사용하지 않았다면 위 그림에서 전체 DOM이 렌더링 되었을 것입니다.

가상돔은 비교과정에서 Diffing 알고리즘을 사용해 기존의 DOM과 달라진 부분만 변경합니다. Diffing 알고리즘은 다음과 같이 동작합니다.

  1. 이전 가상돔 트리와 새로운 가상돔 트리를 비교하고 루트 노드에서 시작해서 이전과 새로운 노드를 비교
  2. 두 노드가 다른 유형이면 새 노드를 생성하여 기존 노드를 대체
  3. 두 노드가 같은 유형이면 속성을 비교해서 변경된 것이 있는지 확인하고 변경된 속성이 없으면 그대로 사용하고 있으면 속성을 업데이트

이 과정을 재조정(reconciliation) 이라고 부릅니다. 재조정을 통해 변경 사항이 파악되면, 해당 변경 사항만 실제 DOM에 반영하여 업데이트합니다. 이런 방식으로 전체 DOM 트리를 재구축하지 않고 불필요한 업데이트를 줄여 렌더링 속도를 올려줍니다.

Virtual-DOM은 대부분의 경우 실제 DOM보다 빠릅니다. 하지만 조그만 변화에도 반응하기 때문에 최적화가 되어있지 않으면 오히려 실제 DOM보다 느려질 수도 있습니다. 이런 현상을 방지하기 위해 React에서는 useCallback과 같은 함수를 써서 최적화를 해야합니다.

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

React.memo()는 특정 컴포넌트가 동일한 props를 받으면 이전 렌더링 결과를 재사용하여 다시 렌더링되지 않도록 합니다. 따라서 React.memo(MyComponent)에서 부모 컴포넌트가 리렌더링되더라도 props가 변경되지 않으면 MyComponent는 렌더링되지 않습니다. 단, props가 객체, 배열, 함수 등 참조 타입일 경우 매번 새로운 값으로 인식될 수 있기 때문에 useMemo() 또는 useCallback()과 함께 사용하면 더욱 효과적입니다.

useMemo()는 연산 비용이 높은 계산 결과를 메모이제이션하여 불필요한 연산을 방지하는 hook입니다. useMemo는 첫 번째 인자로 콜백 함수를, 두 번째 인자로 의존성 배열을 받습니다. 의존성 배열 내의 값이 변경되면 콜백 함수가 다시 실행되어 계산이 이루어지고 그 결과가 메모리에 저장됩니다. 변경이 없다면 이전에 메모리에 저장된 값을 재사용합니다.

useCallback()은 함수를 메모이제이션하여 동일한 함수 인스턴스를 유지하는 hook입니다. 즉, 컴포넌트가 리렌더링될 때마다 함수가 새롭게 생성되는 것을 방지합니다. useCallback() hook은 함수를 메모리에 저장하고 의존성 배열에 명시된 변수들의 값이 변경될 때만 함수를 새로 생성합니다. 이를 통해 컴포넌트가 재렌더링되더라도 동일한 함수 참조를 유지할 수 있어서 성능을 개선할 수 있습니다.

const memoizedMethod = useCallback(() => {
    setCount(a + b);
}, [a, b]);

위 함수에서 a와 b 값이 변경되지 않으면 함수가 새롭게 생성되지 않기 때문에 memoizedMethod 함수가 동일한 참조를 유지합니다.

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

생명주기란 컴포넌트가 생성되고 사용되고 소멸될 때 까지 일련의 과정을 말합니다. 생명주기 안에서는 특정 시점에 자동으로 호출되는 메서드가 있는데, 이를 라이프 사이클 이벤트라고 합니다.
React 컴포넌트 생명주기는 Mount, Update, Unmount의 세 단계로 나눌 수 있습니다.

Mount(마운트)
마운트는 React 컴포넌트의 생명주기에서 컴포넌트가 생성되는 단계입니다.

클래스형 컴포넌트에서는 다음 생명주기 메서드가 실행됩니다.
constructor()static getDerivedStateFromProps()render()componentDidMount()

Update(업데이트)
업데이트는 상태 또는 props가 변경될 때 발생합니다.

클래스형 컴포넌트에서는 다음 생명주기 메서드가 실행됩니다.
static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()

Unmount(언마운트)
언마운트는 컴포넌트가 제거될 때 발생합니다. 언마운트는 리소스 효율성을 보장하고 메모리 누수를 방지하는 데 중요한 역할을 합니다. 이 단계는 컴포넌트의 생애 동안 설정된 작업들, 예를 들어 타이머, 네트워크 요청, 이벤트 리스너 등이 제대로 해제되도록 보장합니다.

클래스형 컴포넌트에서는 다음 생명주기 메서드가 실행됩니다.
componentWillUnmount()

함수형 컴포넌트에서는 클래스형 컴포넌트의 생명주기 메서드를 useEffect로 대체할 수 있습니다.
componentDidMount()useEffect(() => {...}, [])로, componentDidUpdate()useEffect(() => {...}, [dependency])로, componentWillUnmount()useEffect(() => { return () => {...}; }, [])로 대체합니다.

Copy link

@only1Ksy only1Ksy left a comment

Choose a reason for hiding this comment

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

코드 보면서 애니메이션 활용이나 유틸함수 분리 등 여러 가지 배워갑니다!! 2주차 과제 하시느라 수고 많으셨습니다!

gap: 0.75rem;
`;

const TodoListItem = styled(motion.li)`

Choose a reason for hiding this comment

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

전체적으로 애니메이션 사용을 곳곳에 잘하신 것 같아요 저는 아직 애니메이션 사용이 미숙한데 코드 보면서 배워갑니당

Copy link
Author

Choose a reason for hiding this comment

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

최근에 motion이라는 라이브러리를 추천받아서 처음 활용해봤습니다! 저도 애니메이션 사용이 미숙한 편입니다...😂

/>
<Text
htmlFor={item.id}
checked={item.completed === true ? true : false}

Choose a reason for hiding this comment

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

item.completed를 그대로 넣어도 되지 않을까요?? 이렇게 설정하신 이유가 따로 있는지 궁금합니당

Copy link
Author

Choose a reason for hiding this comment

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

아 이전 코드에서 리팩토링하다보니 이렇게 작성한 것 같네요,, 좋은 지적입니다... 수정하겠습니다!

}, [todos, date.selectedDate]);

const addTodoItem = (todoText) => {
if (todoInput.trim() === "") {
Copy link

@only1Ksy only1Ksy Mar 24, 2025

Choose a reason for hiding this comment

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

handleOnChange에서 disabled를 관리하고 있는 것 같은데 여기서 다시 한 번 검사해 주시는 이유가 있을까요??

Copy link
Author

Choose a reason for hiding this comment

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

그냥 혹시 모를 에러 방지용입니다...ㅎㅎ

@@ -0,0 +1,7 @@
const FORMAT_OPTIONS = { year: "numeric", month: "long", day: "numeric", weekday: "long" };

export const formatDate = (dayOffset = 0) => {

Choose a reason for hiding this comment

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

계산 로직 같은 건 util로 빼서 관리하는 게 확실히 편하겠네요! 생각 못해봤던 부분인데 하나 배워갑니당

color: { ...basic },
}

export const DarkTheme = {

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/x-icon" href="/favicon.ico" />

Choose a reason for hiding this comment

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

따로 설정해두신 거 섬세하시네요! ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

헉 근데 지금 보니까 언어가 영어네요 수정해야겠습니다..

Copy link

@DefineXX DefineXX left a comment

Choose a reason for hiding this comment

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

motion 라이브러리를 활용한 애니메이션 및 렌더링 로직이 깔끔한 것 같아요!
고생하셨습니다 :)

cursor: pointer;
`;

const Date = styled.time`

Choose a reason for hiding this comment

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

time 태그는 저도 처음 보는데, 보통 YYYY-MM-DD 형식의 문자열을 datetime 속성으로 할당해줘서 사용을 하는 것 같은데, span 태그가 아닌 time 태그를 사용하신 이유가 있을까요?!


const TodoListSection = styled.section`
width: 100%;
max-height: 50dvh;

Choose a reason for hiding this comment

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

dvh 라는 다이나믹 뷰포트 단위를 처음 봤는데, 스크롤에 따라 동적으로 바뀐다는게 인상적이네요,,

/>
<Text
htmlFor={item.id}
checked={item.completed === true ? true : false}

Choose a reason for hiding this comment

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

동적 스타일링 목적으로 사용되는 props는 $을 붙인 $checked 와 같은 형식으로 transient props를 넘겨주는게 styled-components에서 권장되는 방식이라고 알고 있습니다!

Copy link
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.

추후 타입스크립트로 프로젝트를 진행할 때, 사용 시에 theme.d.ts 파일 내에 아래와 같이 전역 스타일 가이드 객체에 대한 타입을 정의하면 IDE에서 편리하게 사용할 수 있어요! (개인적으로 편리해서 코멘트 남겨요 :)

import 'styled-components';
import { ColorPalette } from '@styles/theme/colors';
import { Typography } from '@styles/theme/typography';

declare module 'styled-components' {
  export interface DefaultTheme {
    colors: ColorPalette;
    fontStyles: Typography;
  }
}

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.

None yet

3 participants