React를 사용하는 이유

Jin·2022년 10월 25일
0

react

목록 보기
1/1
post-thumbnail

1. 글을 쓰게 된 계기

최근 면접 공부를 하는 중에 React를 사용하는 이유에 대한 질문을 보게 되었다.
뭔가 두리뭉실하게는 알고 있지만, 막상 면접에서 물어보면 제대로 된 대답을 하지 못할거 같았고 스스로도 사람들이 많이 사용해서요, 많은 회사에서 사용해서요라는 답은 하고 싶지 않았다. 기술 스택 하나를 선택할 때도 그에 합당한 근거가 있어야 한다고 들었다. 그래서 이번에 내가 React를 사용하는 이유를 다시 한번 정리해보기로 했다.

2. 탄생 배경

흔히 프론트엔드 프레임워크/라이브러리의 3 대장이라고 하면 Angular, Vue.js, React가 있다. 이러한 프레임워크와 라이브러리들은 왜 필요한지에 대해 알아보자.

사실 정적인 웹 사이트를 만들기 위해서는 HTML과 CSS를 사용해서 만들 수가 있다. 예전에 JavaScript에 대해 몰랐을 때, 이 둘로 Instagram을 만들어 본 적이 있다.
하지만 여기에 JavaScript를 더해주면, 유저의 행동 흐름에 따라 동적으로 화면을 보여 줄 수가 있다. 하지만 요즘 웹은 예전에 비해 웹 애플리케이션이라 불릴 만큼 규모가 커지며, 매우 많은 것들을 할 수가 있다. 사용자와의 인터랙션이 별로 없는 프로젝트라면, 바닐라 자바스크립트로 구현할 수도 있겠지만, 프로젝트 규모가 커지고 다양한 유저 인터페이스와 인터랙션이 제공된다면, 그 많은 DOM 요소들을 직접 관리하는 것은 힘들 것이다. 결국 프론트엔드 라이브러리/프레임워크는 DOM 관리와 상태 변화 관리를 최소화하고 개발자가 기능 개발과 사용자 인터페이스에 집중할 수 있도록 탄생하게 되었다.

3. 그렇다면 왜 React일까?

1. Component 단위 개발

리액트는 화면의 한 부분을 컴포넌트라는 단위로 나눌 수 있으며 독립적으로 관리를 할 수 있다. 또한 이러한 컴포넌트들은 다른부분, 다른 웹에서 재사용이 가능해진다. 이는 코드의 재사용으로서 생산성과 유지보수를 용이하게 하는 장점이 있다.

2. Virtual DOM

리액트를 사용해본 분이라면 한번씩은 들어봤을 단어이다. 실제 DOM API를 계속 호출하면 리페인팅, 리플로우로 인해 렌더링이 계속 발생하고 이는 성능 저하로 이어질 것이다.
이를 해결하기 위해 리액트에서는 가상DOM을 사용하여 직접 브라우저의 DOM을 조작하는 것이 아니라, 가상의 DOM을 조작하게 되고, 리액트는 이 가상의 DOM 조작이 어느 정도 마무리 되면 변경 사항을 한번에 바뀐 부분만 실제 브라우저의 DOM에 반영하는 방식이다.
그렇다면 가상 DOM은 각 노드가 변경 되었는지 여부를 어떻게 확인을 할 수 있을까?

2-1) Diffing Algorithm

리액트 공식 홈페이지를 보면 비교 알고리즘(Diffing Algorithm)을 사용한다고 나와 있다.

두 개의 트리를 비교할 때, React는 두 엘리먼트의 루트(root) 엘리먼트부터 비교합니다. 이후의 동작은 루트 엘리먼트의 타입에 따라 달라집니다.

- 엘리 먼트 타입이 다른 경우

두 루트 엘리먼트의 타입이 다르면, React는 이전 트리를 버리고 완전히 새로운 트리를 구축한다. 트리를 버릴 때는 이전 DOM 노드들은 모두 파괴된다.

루트 엘리먼트 아래의 모든 컴포넌트도 언마운트되고 그 state도 사라진다. 아래 같은 경우 이전 Counter는 사라지고, 새로 다시 마운트가 될 것이다.

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>
- DOM 엘리먼트의 타입이 같은 경우

같은 타입의 두 React DOM 엘리먼트를 비교할 때, React는 두 엘리먼트 속성을 확인해, 동일한 내역은 유지하되 변경된 속성들만 갱신한다.

<div className="before" title="stuff" />

<div className="after" title="stuff" />

위와 같은 경우 React는 현재 DOM 노드에서 className만 수정한다. style이 갱신될때도 변경된 속성만을 갱신한다. DOM 노드의 처리가 끝나면, React는 이어서 해당 노드의 자식들을 재귀적으로 처리한다.

- 같은 타입의 컴포넌트 엘리먼트

컴포넌트가 갱신되면 인스턴스는 동일하게 유지되어 렌더링간 state가 유지된다. React는 새로운 엘리먼트 내용을 반영하기 위해 현재 컴포넌트 인스턴스의 props를 갱신한다.

- 자식에 대한 재귀적 처리

DOM 노드의 자식들을 재귀적으로 처리 할때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다.

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

React는 두 트리에서 <li>first</li가 일치하는 것을 확인하고, <li>second</li가 일치하는 것을 확인한다. 그리고 마지막으로 <li>third</li>를 트리에 추가한다.

하지만 위처럼 단순하게 구현하면, 아래와 같은 경우에는 트리 변환은 형편없이 작동할 것이다.

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React를 사용하면서 이런 생각을 누구나 해봤을 것이다. 도대체 Key는 왜 필요할까? 바로 아래와 같은 이유가 있기 때문이다. 자식들이 key를 가지고 있다면 React는 key를 통해 기존 트리와 이후 자식들이 일치하는지 확인할 수 있다.

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

위에서 key 값만 추가된 것이지만 React는 2014만 추가 되었고 나머지는 그저 이동만 하면 된다는 것을 알 수 있을 것이다.
인덱스를 key로 사용할 수는 있지만 배열이 재배열되면 key 또한 바뀔 것이기 때문에, 그 결과 컴포넌트의 state가 엉망이 될 수도 있다.
인덱스를 key로 사용하여 문제가 발생한 Copdepen 예시

그럼 어떻게 이러한 DOM 트리를 순회하면서 변화를 확인할 수 있을까? heuristics 알고리즘을 이용하여 순회를 한다고 한다. 하지만 16버전부터는 Fiber라는것을 도입했다고 하는데 이 둘에 대해서는 다음에 알아보자.

3. 넓은 생태계

사용자가 많은 만큼 커뮤니티도 방대하고 자료도 많아 왠만한 오류에 대한 해결책은 찾을 수 있다. 또한 페이스북이라는 대기업에서 만든 만큼 지속적으로 업데이트가 되고 있다.

출처 : https://velopert.com/3612
https://ko.reactjs.org/docs/reconciliation.html

profile
내가 다시 볼려고 작성하는 블로그. 아직 열심히 공부중입니다 잘못된 내용이 있으면 댓글로 지적 부탁드립니다.

0개의 댓글