React/To-do 앱

React_to-do_6

oyatplum 2023. 1. 31. 14:48

6. To-do_1

: 리액트 불변성 지키기

 

 

불변성이란 사전적 의미에서 값이나 상태를 변경할 수 없는 것을 의미한다.

 

자바스크립트는 두 가지의 타입이 있다.

1. 원시 타입 : Boolean, String, Number, null, undefined, Symbol

2. 참조 타입 : Object, Array

 

여기서 원시 타입은 불변성을 가지고 있고 참조 타입은 그렇지 않다.

 

자바스크립트에서는 원시 타입에 대한 참조 및 값을 저장하기 위해 call stack 메모리 공간을 사용하고

참조 타입인 경우 heap이라는 별도의 메모리 공간을 사용한다. 이 경우 call stack은 개체 및 배열 값이 아닌 메모리에만 heap 메모리 참조 ID를 저장한다.

 

즉, 원시 타입은 고정된 크기로 call stack 메모리에 저장하고 실제 데이터가 변수에 할당된다.

참조 타입은 데이터 크기가 정해지지 않고 call stack 메모리에 저장, 데이터 값이 heap에 저장되며 변수에 heap 메모리의 주소값이 할당된다.

 

 

 

자 예를 들어 원시 타입에서

let username = "walter"

username = "john"

으로 하면 walter를 john으로 대체한 것이 아니라 메모리 영역 a에 walter를 두고, 메모리 영역 b에 john을 새로 할당한 것이다.

 

반면 참조 타입에서

let array = ["1", "2"]

array = ["3", "4"]

로 하면 call stack의 참조 ID는 동일하게 유지되지만 heap 메모리에서만 변경이 된다.

따라서 이런 경우 불변성이 유지되지 않기 때문에 리액트에서 따로 신경써줘야 하는 것이다.

 

 

 

그렇다면 불변성을 지켜야 하는 이유는?

1. 참조 타입에서 객체나 배열의 값이 변할 때 원본 데이터가 변경되기에 원본 데이터를 참조하고 있는 다른 객체에서 오류가 발생할 수 있기 때문이다.

2. 리액트에서 화면을 업데이트할 때 불변성을 지켜서 값을 이전 값과 비교해 변경된 사항을 확인한 후 업데이트를 하기 때문이다.

 

 

이런 불변성을 지키는 방법은?

위의 예시에서 참조 타입인 경우 call stack 주소 값은 같은데 heap 메모리 값만 바꿔주기 때문에 불변성을 유지할 수 없으므로 아예 새로운 배열을 반환하는 메소드를 사용하면 된다.

바로 spread operator, map, filter, slice, reduce 메소드이다.

 

원본 데이터를 변경하는 메소드는 slice, push가 있다.

 

 

 


 


6. To-do_2

: List 컴포넌트 생성하기

 

 

지금까지 만든 List.js를 Lists.js로 바꾸고 자녀 컴포넌트로 새롭게 List.js 파일을 만들어 주었다.

Lists.js는 큰 목록에 해당하고 List.js는 각각의 목록에 해당한다. 이는 rfce를 써서 함수형 컴포넌트로 생성했다.

 

 

그리고 Lists.js로 부터 해당하는 UI부분과 이에 필요한 함수도 가져온다.

 

이렇게 목록에 해당하는 UI 부분을 빼왔고!!

여기서 필요한 함수들인

 

 

handleCompleteChange와 handleClick도 가져왔다ㅎㅎ

 

 

 

이제 Lists컴포넌트에서 List컴포넌트를 import하고 props를 내려주자.

 

 

요롷게 import 하고

List.js를 보면서 오류나는 부분들을 보고 필요한 props들을 Lists.js에서 이렇게 내려준다.

 

 

 

이제 List.js에서 props들을 가져오면 된다.

하하...

자 이제 마지막으로 return 내에 UI로 있었던 코드에서 data를 모두 지워주면

 

끝!!!

 

 

 


 

 


6. To-do_3

: React.memo를 이용한 렌더링 최적화

 

 

현재 Todo 앱의 문제점!!

 

지금 만든 todo 앱은 App, Lists, List, Form 컴포넌트로 나눠져 있다. 이렇게 나눈 이유는 재사용성을 위해서 이기도 하지만 각 컴포넌트의 렌더링을 최적화하기 위해서 이기도 하다.

예를 들어 Form 컴포넌트에(입력 창에) 글을 작성하고 있으면 Form 컴포넌트는 렌더링 되지만 나머지 컴포넌트들은 그대로 있는 것이다.

 

 

 

이렇게 각 컴포넌트들이 잘 동작되고 있는지 확인하기 위해서 모든 컴포넌트에 console.log를 출력해서 확인해보자.

 

그러면

 

 

입력 창에 글씨를 썼을 때 이렇게 렌더링 되지 않아도 되는 Lists, List 컴포넌트가 모두 실행되는 것을 확인할 수 있다.

 

 

 

 

 

따라서 이런한 문제를 React.memo를 사용해서 해결할 것이다.

사용하는 방법은 간단하다.

 

 

이렇게 React.memo로 감싸주면 끝이다.

 

 

Lists.js도 화살표 함수로 형태를 바꿔주고 React.memo로 감싸주었다.

 

 

 

좌좌좌 그러면 이제

이렇게 App과 From 컴포넌트만 나오는 것을 확인할 수 있다!!!

 

헤헤....

 

 

 


 

 

6. To-do_4

: useCallback을 이용한 함수 최적화

 

 

 

원래 컴포넌트가 렌더링 될 때 내부에 있는 함수도 다시 생성된다.

하지만 똑같은 함수를 컴포넌트가 렌더링 될 때마다 계속 다시 만드는 것은 좋지 않다.

 

또한 만약 부모 컴포넌트에서 함수가 다시 만들어지면 이 함수가 자식 컴포넌트에 props를 내려줄 때, 자식 컴포넌트도 계속 함수가 새롭게 만들어져 리 렌더링되기 때문에 문제점이 있는 것이다.

 

 

이러한 문제를 막기 위해 useCallback을 사용하는 것이다.

 

 

handleClick 함수로 살펴보자.

현재 todo 앱에서 x 버튼을 클릭할 때 List.js에서 handleClick 함수를 호출하게 되어있다.

그런데 만약 이 함수가 상위 컴포넌트인 App.js에 있다면 이를 Lists.js에 props로 내려주고, 또 Lists.js에서 List.js로 props를 내려준다면 x버튼을 누를 때, 세 컴포넌트가 모두 리렌더링된다.

 

 

 

이렇게 App.js로 handleCilck 함수를 가져오고

props를 내려준다.

 

 

 

 

그러면 Lists.js에서

React.memo에 handleClick을 추가해주고

 

역시나 props로 내려준다.

 

 

그러면 List.js에서

 

이렇게 React.memo로 handleClick을 추가해줬을 때

 

이렇게 모든 컴포넌트가 리렌더링 되는 것을 확인할 수  있다.

 


 

따라서 useCallback을 사용해보자.

 

App.js에서

 

리액트 라이브러리에서 useCallback을 가져오고

 

 

handleClick 함수를 useCallback으로 감싸준다.

함수 내에서 참조하는 state, props가 있다면 의존성 배열에 추가하면 된다.

여기서는 todoData를 참조하기 때문에 의존성 배열인 두 번째 인자에 [todoData]를 추가했다.

즉, todoData가 바뀔 때만 함수가 다시 생성되도록 해주는 것이다.

 

 

 

이렇게 하고 확인해보면

 

필요한 컴포넌트들만 나오는 것을 확인할 수 있다.

즉, 불필요한 리렌더링이 사라졌다는 거쥐~~~!!!!

 

 


 

 

6. To-do_5

: useMemo를 이용한 결과 값 최적화

 

 

useMemo의 Memo는 Memoization의 약자로 메모이제이션은 비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때, 캐시된 결과를 반환하여 컴퓨터 프로그램의 속도를 높이는데 사용 되는 최적화 기술이다.

 

 

즉, 이를 쉽게 풀어쓰면...

만약 컴포넌트 내의 compute라는 함수가 인자로 a와 b를 받아 복잡한 연산을 수행한다고 해보자.

 

이때 컴포넌트가 계속 리렌더링 된다면 복잡한 연산을 계속 수행하기 때문에 오랜 시간이 걸려 성능에 안 좋은 영향을 미칠 것이다.

 

따라서 이러한 현상을 해결해주는 것이 useMemo이다.

compute 함수에 넘겨주는 인자가 a,b로 이전과 동일하다면, 컴포넌트가 리 렌더링 되어도 연산을 다시 수행하지 않고 이전 렌더링 때 저장해두었던(캐시된) 값을 재활용하는 것이다.

 

 

 

useMemo를 사용하는 방법은 간단하게 useMemo로 감싸주고 첫 번째 인수에다가 의존성 배열에 사용한 값을 compute함수의 인자로 넣어주면 된다.

이렇게ㅎㅎ

 

 

 

 

우선 이렇게 useCallback 과 useMemo를 이용해서 함수와 결과 값을 최적화 할 수 있다는 것까지 알아보았다!!!

 

 

 

 

'React > To-do 앱' 카테고리의 다른 글

React_to-do_8  (0) 2023.02.01
React_to-do_7  (1) 2023.02.01
React_to-do_5  (0) 2023.01.30
React_to-do_4  (1) 2023.01.29
React_to-do_3  (1) 2023.01.28