요즘 유데미에서 한 입 리액트 저자인 이정환님의 강의를 듣고 있다. (React + NextJS)
이전에 리액트를 슬쩍 공부한 적이 있었는데, 일단 학교에선 쓸 일도 없고 알고리즘에 몰두하다보니 다 잊어버렸고...
차근차근 다시 강의를 들으며 이해하고 있는데, 처음엔 너무 어려웠던 내용의 개념이 어느 정도 잡히기 시작했다.
강의를 듣고 이해한 내용으로, 정확한 내용이 아닐 수 있음!
React는 컴포넌트 구조로 이루어진다.
쇼핑몰 등의 웹페이지를 보다보면 상품 이미지와 이름, 가격만 다른, 틀이 같은 구조를 종종 볼 수 있다.
기본 틀은 같은데, 틀 안에 담기는 내용(데이터)만 달라지니 데이터 처리만 따로 하고 기본 뼈대를 재사용 하는 것.
이게 React를 비롯한 SPA(Single Page Application)의 핵심이다.
기존 HTML 웹페이지는 데이터가 변경되거나, 어떤 작업을 수행할 때 페이지 전체가 다시 불러와진다.(like F5..)
그러나, SPA 웹페이지는 큰 구조 안에 작은 부분으로 부모-자식의 구조를 갖기에 이 비효율적인 문제를 효과적으로 해결할 수 있는 것이다. 어떻게? 데이터가 변경되는 부분만 다시 렌더링(Re Rendering) 하면 된다.
부모-자식 간의 데이터 전달은 Props
React는 컴포넌트 구조로 이루어 진다고 했다. 근데 그 컴포넌트 간의 데이터는 어떻게 전달될까?
기본적으로 부모-자식 간의 데이터 전달은 props를 통해 가능하다.
부모 컴포넌트에서 자식 컴포넌트를 호출하면서 어떤 키와 값을 함께 전달한다고 이해했다.
자식은 그 키를 받아서 컴포넌트 내에서 키를 통해 값을 다룬다.
자식-부모로 가는 데이터 전달은 부모 컴포넌트에서 선언한 콜백 함수를 통해 가능한 것 같다.(아직 잘 모름..)
부모 컴- 에서 자식 컴-으로 전달하는 props에 콜백함수를 전달하면, 자식이 받은 그 props(콜백함수)를 호출해 setState를 건드리고, 이렇게 데이터를 변경/전달 하는 것이다.
자식-자식 간의 데이터 전달은 기본적으론 불가능하다고 이해했는데, 불가능이라기 보단 무조건 부모를 거쳐야만 하는 것 같다. 어쨌든 자식끼리는 할 수 없다는 것.
자식-> 부모, 부모-> 자식 의 흐름을 이용하면 복잡하긴 해도구현할 수 있을 것 같다.
React의 데이터 = State(상태)
React에선 데이터를 State(상태) 라고 부른다. 데이터가 변경되면 상태가 변경되는 것.
그리고 이 상태를 관리하기 위한 기능이 useState이다.
use어쩌고 하는 기능은 일단 컴포넌트 상단에 react로부터 import 받아와야 사용 가능하다!!
useState는 state, useState로 이뤄진다. 쉽게 말하면 데이터 자체와 데이터 변경 함수이다.
실제론 뭐 count, setCount 등으로 이름을 바꿔서 사용한다.
동작 자체는 setCount 를 통해 count + 1을 하면 count 값이 증가하는.. 단순한 동작인 것 같다.
그리고 이 state와 props를 같이 사용하면 부모 컴포넌트에서 변경한 데이터가 자식한테까지 쫙 퍼지는...
효율적인(?) 데이터 흐름을 갖는 것이다.
useEffect로 리렌더링을 막자!
useState를 보면 매우 효과적이고 좋을 것만 같지만, 한 가지 문제가 있는데 state가 변경될 때마다 컴포넌트가 리렌더링 된다.
님 어짜피 자식 컴포넌트만 리렌더링 되면 상관 없는거 아님?!
이라고 할 수 있겠지만.. 최상위 부모에서 변경된 데이터가 자식에 자식에 자식을 거쳐 후손까지 쫙 퍼진다면?
굳이 변경하지 않아도 되는 내용까지 리렌더링 되는 것이다. 대충 들어도 매우매우 비효율적이라는 느낌이 온다.
이를 방지할 수 있는 방법이 바로 useEffect 이다. 이 친구의 역할은 컴포넌트의 렌더링에 따른 어떤 작업을 수행하는 것인데, 리렌더링을 막자는 제목을 달았으니 이걸로 예를 들어보자.
useEffect는 콜백함수와 의존성 배열이라고 하는 배열을 매개변수로 한다. 의존성 배열에 들어가는 친구에 따라 useEffect의 콜백함수가 통제되는 것!
내가 리렌더링을 막고 싶다? -> 의존성 배열 내용을 비워두면 된다.
State가 변경될 때 렌더링을 하고 싶다? -> 의존성 배열에 State를 넣으면 된다.
(근데 기본적으로 State가 변경되면 리렌더링 된다. 위의 예시는 특정 상태나 특정 props에 대해서 적용이 가능하다는 것)
하나의 예시를 더 들어볼까 한다.
컴포넌트 내에 text와 number라는 State가 있다고 할 때,
useEffect의 의존성 배열(Deps)에 두 State를 모두 넣으면? -> 어떤 State가 변경되던 간에 렌더링/콜백함수가 실행됨.
number가 변경될 때만 실행하고 싶어! -> 뎁스에 number만 넣으면 됨.
text가 변경될 때만 실행하고 싶어! -> 뎁스에 text만 넣으면 됨.
useEffect를 두 개 두고 각각 구현하면 변경된 state에 따라 설정한 콜백함수 각각 실행되므로, 효율적인 설계가 가능하다!
근데 그거 그냥 각각 setState로 변경하면 원래 되는거 아님?
이라는 의문을 가지긴 했지만, 결론은 아님!
상태변화 함수의 작동은 비동기적이므로, state의 변경에 따라 바로 실행된다고 보장할 수 없다.
비동기적이라는 것은 어떤 공간에서 대기 상태로 있다가 차후에 실행된다고 생각하면 되는데,
React는 가상 DOM(Virtual DOM)을 통해, 변경 내용을 렌더링/리렌더링 할 때 변경된 상태를 반영하게 된다.
그런데, 상태변화 함수를 통한 상태의 변경이 리렌더링을 트리거하고, 가상 DOM을 통해 비교 후 최적화된 변경이 이뤄지기 때문에 즉각적인 변경을 보장할 수 없다. 변경된 부분만 감지 후 하나의 렌더링 주기로 처리해 최소한의 DOM 조작 수행을 목적으로 하기 때문에 즉시 업데이트 되지 않는 것!!
우리는 이걸 React Hook 이라 부르기로 했어요.
지금 까지 설명한 use어쩌고 하는 친구들을 Hook이라고 부른다. 더 다양한 것들이 있긴 하지만, 일단 지금까지 이해한 내용은 이렇다. 일종의 API? 같은 느낌이다. 공식적인 설명으론 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 한다고 하는데... 클래스형 컴포넌트는 사실 제대로 다뤄본 적이 없어서 아직 잘 모르겠다...
수업을 열심히 듣진 않지만.. 어쨌든 학교를 아직 다니고 있고, 알고리즘이며 프로젝트며 이것 저것 하다보니 꾸준하게 하기가 참 힘든 것 같다. 짧게라도 두번 세번씩 보면서 최대한 이해하고 기록해야겠다 ㅠ