Thinking in React

Park June Chul·2021년 4월 21일
3

React

목록 보기
1/7
post-thumbnail

https://ko.reactjs.org/docs/thinking-in-react.html
이 글은 위 페이지의 몇가지 주제에 대해서 자세하게 다루어 보는 글입니다.

State에 대해 알기

이 줄은 React를 처음 접하는 분들이 가장 많이 실수하는 항목중에 하나입니다.

const [password, setPassword] = useState('');
const [valid, setValid] = useState(false);

const onChangePassword = (x: string) => {
  setValid(x.length >= 8);
  setPassword(x);
};

return (
  <>
    <input
      value={password}
      onChange={x => onChangePassword(x.target.value)}
    />
    {valid === false && '비밀번호는 8자 이상으로 입력해주세요'}
  </>
);

위와 같은 코드, 혹은 비슷한 코드를 작성하는 경우를 흔하게 볼 수 있습니다.

하지만 위 코드는 3번 항목에 어긋납니다.
validpassword 값을 가지고 계산할 수 있기 때문에 state로 지정되어야 할 이유가 없습니다.

const [password, setPassword] = useState('');
const valid = password.length >= 8;

어떤 개발자들은 매번 계산하는것보다 state를 사용해 변경될 때만 계산시키고, 결과를 캐싱하는것이 더 좋다고 생각할 수 있습니다.

첫번째로는, 그렇지 않습니다. 대부분의 코드는 매우 빠르게 실행되며 매번 다시 계산하는게 성능에 전혀 영향을 주지 않습니다.

두번쨰로는, 정말 캐싱을 해야 하는 코드가 있을 수도 있습니다. React는 이를 위해 state 가 아닌 useMemo 라는 훅을 제공합니다. 이걸 사용하세요.

const [password, setPassword] = useState('');
const valid = useMemo(() => password.length >= 8, [password]);

세번째로는, state에 대한 얘기는 아닙니다만, 컴포넌트 함수 실행속도를 줄이는 것 보다, 컴포넌트 호출 횟수(re-render)를 줄이는것이 성능에 크게 영향을 미칩니다.

단방향 흐름에 대해 알기

React와 다른 프레임워크(대표적으론 Angular)의 다른점은 양방향-단방향의 차이입니다.
React는 단방향 흐름을 가지고 있으며, 이는 위에서 아래로 내려가는 구조를 뜻합니다.

단방향 흐름이란 단순히 input 의 값이 프레임워크에 의해 자동으로 업데이트 되지 않는 단순한 개념이 아닙니다. 단방향 흐름은 모든 변경을 명시적으로 만들어줍니다.

예를들어 Angular의 가장 단순한 양방향 바인딩 예제를 보세요:

<input [(ngModel)]="name">

name 의 변경은 명시적이지 않습니다. Angular가 알아서 name을 변경해주고 개발자는 이 과정을 알 수 없습니다. (정확히는 몰라도 된다고 표현해야 할 것 같습니다.)

반면 React 는 아래와 같이 처리합니다.

const [name, setName] = useState('');

<input value={name} onChange={e => setName(e.target.value)} />

모든 변경은 명시적입니다. 그러니까 모든 변경은 코드를 작성하는 개발자 에 의해 일어납니다.
React는 단지 이벤트를 날려주거나, 상태를 변경할 수 있는 API만을 제공합니다.


조금 더 구체적인 단방향 흐름에 대한 예시를 들어보도록 하겠습니다.
이것은 단순히 단방향 흐름이 value, onChange 에만 한정되는 사항이 아니라는것을 설명합니다.


일반적인 앱 개발에서 다음과 같은 코드는 꽤 흔합니다.

parentComponent.clearInput();

꼭 parent가 아니라고 하더라도, 다른 컴포넌트의 레퍼런스를 가지고 있고, 해당 컴포넌트의 함수를 직접 호출하는 구조는 굉장히 많이 쓰인다고 할 수 있습니다.


React는 이런식으로 동작하지 않습니다. React는 해당 컴포넌트를 사용하는(parent)쪽에서 어떤 함수를 넘겨줄것인지를 직접 지정합니다.
const Child = ({
  onClearInput,
}) => {
  
  /* ... */
  onClearInput();
};

그게 그거처럼 보일 수 있지만 여기에는 많은 차이가 있습니다.

  • Child가 어떤 동작을 수행하는지 굉장히 명확합니다. ChildComponent에서 전달을 받는다는것은, 미래에 이 동작을 수행하겠다는뜻이고, 반드시 혹은 선택적으로 제공할 수 있습니다. (TypeScript를 선택하면 이 동작이 required인지 optional인지 더 명확하게 할 수 있겠죠)
  • 레퍼런스를 전달하는 방식은 불안정합니다. Child가 Parent의 모든 함수를 다 호출할 수 있습니다. public/private의 개념처럼 호출하면 안되는 함수까지도 호출할 수 있고, 코드를 다 읽기 전에는 이 컴포넌트가 clearInput이라는 동작을 필요로 하는지도 알 수 없습니다.
  • 심지어 몇몇 프로그램 혹은 프레임워크에서는 레퍼런스조차 전달하지 않습니다. Child가 알아서 Parent를 검색하고 호출합니다. 이 구조는 버그와 재사용성 모두에 취약합니다. (Child가 정확한 하이라키 구조로 정확한 타입의 Parent 아래에 있을 때만 돌아갈 것입니다.)

아직도 잘 이해가 안가신다면 아래의 코드를 한번 보세요.

parentComponent.inputText = '';

이 코드는 parentComponent.clearInput(); 보다 더 안좋습니다.
Parent는 Child가 자신의 input 내용이 바꿔놓는걸 인식할 수 조차 없습니다.
안타까운 사실은 일반적인 프로그램에서 위 코드는 얼마든지 가능한 구조이며, 심지어 자주 쓰입니다.

React의 단방향 구조는 이러한 가능성을 원천적으로 차단합니다.

단방향 구조는 어떻게 보면 생산성에서 불리하게 느껴질 수 있습니다만, 더 명확한 코드를 작성하기 위해 React의 개발자들이 채택한 구조입니다.

역방향 흐름에 대해 알기

먼저 역방향 흐름에 대해 확실하게 해야 할 두가지가 있습니다.

  • 역방향 흐름은 위에서 아래로 내려오는 단방향 흐름의 반대 개념이 아닙니다.
  • 단방향 흐름 + 역방향 흐름 = 2way binding 이 아닙니다.

React를 처음 접하는 개발자들이 실수하는 잘못된 코드 중 유명한것으론 아래의 케이스가 있습니다.

<EditProfilePopup
  name={name}
  age={age}
  setName={setName}
  setAge={setAge}
/>

위 코드는 아래처럼 되어야 합니다.

<EditProfilePopup
  name={name}
  age={age}
  onChangeName={setName}
  onChangeAge={setAge}
/>

두 코드는 정말 이름만 다르고, 모든 구현은 똑같습니다.

근데 왜 setName 이 아니라 onChangeName 이 되어야 할까요?

단순히 onChange 로 이름을 짓는것이 아니라, 이 부분에 대해 대답할 수 있어야 React의 단방향 바인딩, 그리고 역방향 흐름에 대해 안다고 할 수 있습니다.

쓰다 말았음
profile
다른 곳에서 볼 수 없는 이상한 주제를 다룹니다. https://pjc0247.github.io/new-home

1개의 댓글

comment-user-thumbnail
2021년 9월 6일

쓰신 글을 쭉 읽어보는데 하나하나가 다 너무 좋은 내용들이네요. 감사합니다!

답글 달기