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주차] 박지수 미션 제출합니다. #10

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

jsomnium
Copy link

@jsomnium jsomnium commented Sep 21, 2024

- ̗̀ 결과물 ̖́-

https://react-todo-20th-final.vercel.app/

느낀점

바닐라에서 리액트로 넘어가면서 확실히 리액트로 상태 관리를 하기가 수월해서 더 편리함을 느낀 것 같습니다.
styledcomponent는 처음 사용해봤는데 코드가 훨신 깔끔하게 보여서 좋은 것 같습니다. 이전에 css를 사용할 때는 className으로 구분했는데, 확실히 div 태그속의 className이 아니라 클래스 자체 이름을 설정해서 보니 코드 가독성도 훨씬 좋아진 것 같네요. 앞으로도 코드 짤 때 styledComponent 사용할 것 같습니다.
이번주 KeyQuestion이 최적화에 대한 것이었는데, 사실 최적화에 대한 것은 생각해보지 못해서 아쉽습니다... 스터디 하면서 배우고 나중에는 개발 단계부터 최적화를 생각하는 여유를 가지고 싶습니당..
아직 코드 고쳐야 될 점이 많은데, 주말 안 수정하도록 하겠습니다🥹

Key Question

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

  • 메모리 내에 가상으로 존재하는 DOM

실제 DOM을 조작하는 방식이 아닌, DOM을 모방한 가상의 DOM을 구성해 원래 DOM과 비교하여 달라진 부분을 리렌더링 하는 방식으로 작동하는 것을 말한다. 즉, DOM을 직접 조작하지 않고 변경사항을 하나의 가상 돔에 모았다가 DOM에 한 번에 보내는 기술이다. 리액트는 가상 돔 방식을 사용하고 있다. 왜 리액트는 실제 DOM을 조작하지 않고 가상 돔을 사용하는 것일까?

[재조정 과정]

리액트는 상태 변경 시마다 리랜더링이 발생한다. 이 시점마다 새로운 내용이 담긴 가상 돔을 생성하게 된다. 아래의 사진처럼, 랜더링 이전에 화면의 내용을 담고 있는 첫 번째 가상 돔과 두 번째 가상돔을 비교해 정확히 어떤 요소가 변했는지를 비교한다. 이를 Diffing이라고 표현한다. 리액트는 이를 통해 차이가 발생한 부분만을 실제 DOM에 적용하게 되는 것이고, 이를 재조정이라고 한다.

[DOM을 사용한 이점]

DOM조작에 비용이 가장 많이 발생하는 지점은 브라우저에 화면을 그려주는 작업이다. 재조정 과정의 Batch Update가 변경된 Element를 별개로 그려주는 것이 아닌, 변경된 내용을 한 번에 받아와 이를 실제 DOM에 한번에 적용시켜 주기 때문에 효율적이다.

image

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

React.memo()

  • 렌더링 결과를 메모이징(Memoizing)함으로써, 불필요한 리렌더링을 건너뛰는 기술이다.
  • 컴퍼넌트가 React.memo()로 래핑 될 때, React는 컴퍼넌트를 렌더링하고 결과를 메모이징한다. 그리고 다음 렌더링이 일어날 때 props가 같다면, React는 메모이징된 내용을 재사용한다.

언제 React.memo()를 쓰는게 좋을까?

컴퍼넌트가 같은 props로 자주 렌더링되거나, 무겁고 비용이 큰 연산이 있는 경우, React.memo()로 컴퍼넌트를 래핑할 필요가 있다.

useMemo()

  • 렌더링 결과를 메모이징(Memoizing)함으로써, 불필요한 리렌더링을 건너뛴다.
  • 컴퍼넌트가 React.memo()로 래핑 될 때, React는 컴퍼넌트를 렌더링하고 결과를 메모이징한다. 그리고 다음 렌더링이 일어날 때 props가 같다면, React는 메모이징된 내용을 재사용한다.

언제 useMemo()를 쓰는게 좋을까?

많은 계산이 필요한 경우에 useMemo를 사용하여 최적화를 이룰 수 있다. 복잡한 배열 필터링, 정렬 또는 큰 데이터 셋을 다룰 때를 예시로 들 수 있다. 컴포넌트가 반복해서 랜더링 될 때, 의존성 배열에 따라 메모이제이션이 필요할 때도 마찬가지이다.

useCallback()

  • 함수형 컴포넌트에서 함수의 재생성을 방지하는 데 사용되는 기술이다.
  • 특정 조건에서만 함수가 재생성되도록 메모이제이션된 콜백 함수를 반환한다.

언제 useCallback()를 쓰는게 좋을까?

부모 컴포넌트에서 자식 컴포넌트로 함수를 props로 전달할 경우, 부모 컴포넌트가 렌더링될 때마다 함수가 새로 생성되면 자식 컴포넌트도 불필요하게 리렌더링될 수 있다. 이를 방지하기 위해 useCallback()을 사용해 함수의 재생성을 막아 최적화를 이룰 수 있다. 또한, 리스트 항목과 같은 반복되는 컴포넌트에서 함수가 자주 재생성되면 성능이 저하될 수 있다. 이때 useCallback()으로 함수의 재생성을 제어하면 성능을 개선할 수 있다.

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

  • 생명주기?

생명주기(Lifecycle)는 컴포넌트가 생성되고 업데이트되고, 소멸되는 과정을 의미한다. 이전에는 클래스형 컴포넌트 생명주기가 쓰였으나, 최근에는 함수형 컴포넌트와 훅 기반 생명주기가 많이 사용된다.

생명 주기 단계는 크게 세 가지로 구분할 수 있다.

  1. 마운트
    컴포넌트가 처음 DOM에 렌더링되는 단계이다.
    주로 초기 상태 설정, API 호출, DOM 조작, 이벤트 등록 등을 처리한다.
  2. 업데이트
    컴포넌트의 상태나 props가 변경되어 재렌더링 되는 단계이다. 이 시점에서 API 호출이나 DOM 조작을 할 수 있다. useEffect 와 같은 의존성 배열에 전달한 값이 변경될 때마다 이 함수가 실행된다.
  3. 언마운트
    컴포넌트가 DOM에서 제거되는 단계이다. useEffect에서 반환하는 함수는 컴포넌트가 언마운트되거나 업데이트되기 직전에 실행된다. 여기서 정리 작업을 수행할 수 있다.

생명주기와 관련된 훅은 useState, useEffect, useMemo, useCallback 등이 있다.

Copy link

@jiwonnchoi jiwonnchoi left a comment

Choose a reason for hiding this comment

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

전반적으로 깔끔한 코드에 배열을 다루는 메서드도 잘 이해하고 쓰신 것 같아요😊👍🔥
2주차 과제도 고생많으셨습니다!!

Comment on lines +2 to +6
import {
AppContainer, Main, TodoContainer, MainText, Line, TodosBody,
TodoHeader, TodoList, TodoItem, TodoInput,
Input, Button, RemoveButton
} from './styles';
Copy link

@jiwonnchoi jiwonnchoi Sep 23, 2024

Choose a reason for hiding this comment

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

style.js에서

export const S = {
  AppContainer, Main, ...
};

과 같이 씀으로써 export도 일일이 쓰지 않고

Suggested change
import {
AppContainer, Main, TodoContainer, MainText, Line, TodosBody,
TodoHeader, TodoList, TodoItem, TodoInput,
Input, Button, RemoveButton
} from './styles';
import { S } from './styles';

import문은 파일 자체를 지정하여 아래쪽 return 부에서 컴포넌트를 쓸 때 <S.MainText></S.MainText> 와 같이 쓰면 더 간단할 것 같아요!

Choose a reason for hiding this comment

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

오.. 지원님이 말씀해주신 방법 정말 간결하고 좋네요

TodoHeader, TodoList, TodoItem, TodoInput,
Input, Button, RemoveButton
} from './styles';
import { GlobalStyle } from './styles';

Choose a reason for hiding this comment

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

글로벌 스타일은 파일을 따로 빼도 좋을 것 같아요! 물론 컴포넌트들이 여기서는 따로 없지만 쪼개진다면 한눈에 전역 스타일이 있는지, 혹은 어느 스타일이 특수한지 쉽게 파악할 수 있으니까요 :)

Comment on lines +32 to +36
// 할 일 삭제
const removeTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
};

Choose a reason for hiding this comment

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

useCallback 함수로 감싸서 해당 App.js가 렌더링 될 때마다 불필요하게 리렌더링 되는걸 막으면 최적화에 더 좋을 것 같습니당

Suggested change
// 할 일 삭제
const removeTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
};
// 할 일 삭제
const removeTodo = useCallback((index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
}, []);

Comment on lines +46 to +51
// 엔터키로 할 일 추가
const handleKeyDown = (e) => {
if (e.key === "Enter") {
addTodo();
}
};

Choose a reason for hiding this comment

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

저는 인풋을 form 태그로 감싸서 button에 submit 타입을 지정해주고 onClick만 더 지정해주었는데 keyDown이라는 해당 방식도 알아갑니다!

};

// 완료된 할 일 개수
const completedTodosCount = todos.filter(todo => todo.complete).length;

Choose a reason for hiding this comment

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

filter이란 함수를 자유자재로 잘 쓰시는 것 같아요 👍👍

@@ -0,0 +1,134 @@
import styled from 'styled-components';

Choose a reason for hiding this comment

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

css-in-js 코드를 따로 만든 스타일 파일로 빼서 App.js 코드를 줄이셨군요!
저도 따로 만든 스타일 파일이 있어서 이 방식을 잠깐 고민했는데, styled-component는 지난주 바닐라와 달리 컴포넌트
별로 스타일을 한 파일 안에서 쉽게 다룰 수 있어서 편리하다고 느꼈어요! 그래서 따로 만든 스타일 파일에는 공용 박스, 글자 등의 스타일만 넣어두었는데, 이 방식도 고려해보시면 좋을 것 같아요!

Copy link

Choose a reason for hiding this comment

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

저도 지원님 의견에 동감해요!

개인적으로 styled-component를 사용했을 때 가장 큰 장점은 css-in-js로 한 파일 내에서 스타일까지 한 번에 정의해서 개발 속도가 css-in-css보다 훨씬 빨라지는 것이라고 생각하고 있어요. 🤩

css-in-js vs css-in-css
해당 문서 읽어보시면 css-in-js와 css-in-css의 차이에 대해 더 자세히 알 수 있어서 도움이 되실 것 같아요!

Comment on lines +61 to +63
Better<p></p>
than<p></p>
Yesterday!

Choose a reason for hiding this comment

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

줄 바꿈을 p태그로 쓰신 점 새로워요😯 의미를 좀 더 명확하게 하려면 <br /> 로 대신해도 좋을 것 같습니다!

Comment on lines +80 to +89
<TodoInput>
<Input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="할 일 추가"
/>
<Button onClick={addTodo}>추가</Button>
</TodoInput>

Choose a reason for hiding this comment

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

인풋에 onChange 속성을 주면서 value가 바뀔 때마다 해당 컴포넌트(여기서는 App.js)가 불필요하게 계속 리렌더링된다고 해요! 해당링크 참고해보시면 도움 많이 되실 것 같습니다 😊😊
(+저도 처음에 이 방식으로 최적화해야하나 하고 고쳐보았었는데 다시 보니 저는 인풋이 들어있는 컴포넌트가 분리되어 있어서 상위 컴포넌트에서 addTodo 함수를 정의하고 useCallback으로 감싸주었더니 리렌더링이 발생하지 않더라고요! 여러 방식이 가능할 것 같아요)

src/styles.js Outdated
Comment on lines 63 to 69
background-color: aliceblue;
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
`;

Choose a reason for hiding this comment

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

제 pr 참고자료에도 올려둔 링크인데, css 속성 순서와 관련해서 참고해보시면 좋을 것 같아 남깁니다🔥

https://uxkm.io/publishing/css/03-cssMiddleclass/10-css_attr_rule#gsc.tab=0

`;

export const Input = styled.input`
flex-grow: 1;

Choose a reason for hiding this comment

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

저는 이번에 flex-grow와 flex-shrink 속성을 제대로 알고 써보았는데, 필요한 최소한의 속성만 알맞게 잘 쓰시는 것 같아요👍


// 할 일 삭제
const removeTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
Copy link

@hiwon-lee hiwon-lee Sep 23, 2024

Choose a reason for hiding this comment

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

_를 사용하는 게 특이해서 찾아보니 해당 기호로 표현하는걸 언더스코어라고 하는군요.~
저같은 경우에 filtermap함수와 같이 인자를 넘겨줄 수 있지만 안쓰는 경우에도 그냥 변수로 할당해줬는데 담부턴 언더스코어를 활용해볼 수 있을것같아요

Comment on lines +81 to +87
<Input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="할 일 추가"
/>
Copy link

@hiwon-lee hiwon-lee Sep 23, 2024

Choose a reason for hiding this comment

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

해당 부분 민재님 코드리뷰하다가 useRef라는 훅을 사용하면서 렌더링 최적화 하는 방식을 배웠습니다!
지수님도 관련 부분 찾아보면 도움이 될 것 같아요~

Comment on lines +97 to +99
style={{
textDecoration: todo.complete ? "line-through" : "none"
}}

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.

스크린샷 2024-09-23 오후 5 40 07

마자요! 그리고 padding 디테일 챙기는 것도 잊지 말기

Copy link

@Shunamo Shunamo left a comment

Choose a reason for hiding this comment

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

지수님! 이번 과제도 수고 많으셨어요 😀
상태 관리와 이벤트 처리 부분에서 깔끔한 코드가 돋보였던 것 같아요!
로컬 스토리지 활용해서 리팩토링하시면 UX적으로 크게 개선될 것 같아요!
다음 과제도 기대하겠습니다 🙌

@@ -0,0 +1,134 @@
import styled from 'styled-components';
Copy link

Choose a reason for hiding this comment

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

저도 지원님 의견에 동감해요!

개인적으로 styled-component를 사용했을 때 가장 큰 장점은 css-in-js로 한 파일 내에서 스타일까지 한 번에 정의해서 개발 속도가 css-in-css보다 훨씬 빨라지는 것이라고 생각하고 있어요. 🤩

css-in-js vs css-in-css
해당 문서 읽어보시면 css-in-js와 css-in-css의 차이에 대해 더 자세히 알 수 있어서 도움이 되실 것 같아요!

Copy link

Choose a reason for hiding this comment

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

모든 컴포넌트를 App.js 안에 정의하고 계시네요!

VanillaJS와 비교했을 때, React의 이점은 컴포넌트 기반 아키텍처와 재사용성인 것 같아요!
Reactstyled-components를 사용하는 핵심 목적은 UI를 작은, 재사용 가능한 단위로 나누어 관리하고, 상태와 로직을 각각의 책임에 맞게 분리하는 것이라고 생각해요 :)

모든 UI와 로직을 한 파일에 몰아넣는 방식은 이러한 장점을 훼손할 수 있어요..!
작은 프로젝트에서부터 컴포넌트 단위로 쪼개고 재사용하는 습관을 잡아놓으면 나중에 볼륨이 큰 프로젝트에서 빛을 발할 것 같아요 🥹

Comment on lines +72 to +79
export const TodoInput = styled.div`
display: flex;
justify-content: space-between;
border-radius: 10px;
border-style: solid;
border-width: 1px;
margin-bottom: 10px;
`;
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-09-23 오후 5 21 11

focus 상태일 때 인풋박스 outline은 없애주기로 해요 🌞

Suggested change
export const TodoInput = styled.div`
display: flex;
justify-content: space-between;
border-radius: 10px;
border-style: solid;
border-width: 1px;
margin-bottom: 10px;
`;
export const TodoInput = styled.div`
display: flex;
justify-content: space-between;
border-radius: 10px;
border-style: solid;
border-width: 1px;
margin-bottom: 10px;
&:focus {
outline: none;
}
`;

padding: 8px 8px;
background-color: skyblue;
border: none;
border-radius: 5px;
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-09-23 오후 5 37 36 스크린샷 2024-09-23 오후 5 37 43

여기도 겹쳐지는 부분에 border-radius 값를 통일시켜주기로 해요!

Suggested change
border-radius: 5px;
border-radius: 10px;

Comment on lines +97 to +99
style={{
textDecoration: todo.complete ? "line-through" : "none"
}}
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-09-23 오후 5 40 07

마자요! 그리고 padding 디테일 챙기는 것도 잊지 말기

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.

4 participants