리액트, 제대로 알고 개발하기 (feat.라면 받침)

김민기·2020년 8월 5일
1

어떠한 물건이 새로이 만들어 질 때는 대개 그것을 만들고자 한 의도가 있다. 가령, 책은 독자들로 하여금 읽는 행위를 통해 지식을 전달하기 위해 탄생한 것 처럼 말이다. 그런 책이 때로는 라면 받침으로 쓰이는 것 처럼, 간혹 그 쓰임새가 의도와 다른 경우가 있긴 하지만, 대개는 그 둘이 일치할 때 가장 좋은 성능을 나타낸다.

본인은 무언가를 학습할 때 탑-다운 방식을 선호하는 편이다. 개발에 있어서도 마찬가지였다. 일단은 원하는 대로 작동되게 코드를 작성하고, 구체적인 작동 원리나 관련된 개념은 코드가 익숙해질 때쯤 공부했다. 더불어, 시작이 빠른 친구들에 비해 조금 느리게 개발을 시작했다는 생각에 조급한 마음이 들었는지, 리액트니 뭐니 하며 최신 기술 스택을 빨리 익히고 싶은 마음이 늘 자리잡고 있었다. 덕분인지, 제법 빠르게 웬만한 것은 작동하게끔 만들어내는 것에 익숙해질 수 있었다.

그 즈음, 인턴을 시작하며 코드를 리뷰 받을 수 있는 환경에서 개발을 하게 되었다. 내가 작성한 코드는 나뿐만이 아니라, 협업하는 사람들에게도 납득 가능한 것 이어야 했다. 안타깝게도, 나는 분명 '책'이라 생각하고 작성했던 코드들이, '라면 받침' 정도의 쓰임새로 활용되고 있는 경우도 더러 있었다. 이렇듯, 단순히 작동만 하는 수준에서 나아가 여럿에게 설득력 있는 코드를 작성하기 위해 고민하다 보니, 자연스레 내가 왜 리액트를 사용하는지, 리액트를 제대로 사용하고 있는 것 인지에 대한 궁금증이 생기기 시작했다.이제는 '제대로 알고' 코드를 작성해야 할 시점이 된 것 같다는 생각을 했다.

# 리액트란? 리액트의 탄생배경 및 철학 이해하기

리액트를 정확히 알고 사용하기 위해서, 리액트가 탄생한 배경과 그 철학을 이해할 필요성을 느꼈다. 다행히도, 이와 관련된 훌륭한 참고자료들이 많이 있었고, 그렇기에 이 글에서는 본인이 핵심적이라고 생각이 든 부분만 정리하고 넘어가려 한다. 참고 자료는 아래 정리해두었으니, 같은 고민을 하고 있는 사람들은 한 씩 읽어보기를 추천한다.

2013년 당시 React 팀 소속 Pete Hunt가 발표했던 키노트 슬라이드에서 강조했던 세 가지 내용이 있다.

Components, not template

리액트의 근본적인 목표는 훌륭한 성능보다는 유지 가능한 앱을 만드는 것에 있다. 따라서 전통적으로는 Template 단위로 구성되던 앱이, 리액트에서는 Component 라는 블록 같은 단위들을 조립해서 하나의 앱을 만들게 된다. 이 때, Component 들은 재사용이 가능하다는 점이 특징이다.

Re-render, don't mutate

리액트 등장 이전의 프레임워크들은 MVC 패턴으로 이뤄져 있었다. 특정 이벤트가 발생했을 때, 모델의 값이 변화하면 뷰에서도 이를 변화시켜주는 과정이 수반되어야 했다. 리액트는 데이터가 바뀌면 뷰를 날리고, Re-rendering을 한다. 하지만, 브라우저에게 DOM을 해석하고 렌더링 하는 과정은 굉장히 비싼 작업이다. 따라서 변화가 생길 때마다 다시 렌더링을 해주면 필히 성능 상의 문제를 야기할 가능성이 크다. 아이러니하게도 페이스북은 React를 “지속해서 데이터가 변화하는 대규모 애플리케이션을 구축하기”위해 만들었다고 밝힌 바 있는데, 이것이 어떻게 가능한 것일까?

Virtual DOM is simple and fast

이 것을 가능하게 해주는 것이 바로 Virtual DOM이다. Virtual DOM의 기능을 알아보기에 앞서, 브라우저가 DOM을 렌더링하는 과정이 왜 비싼 작업이 되는지 부터 이해할 필요가 있다.

위 그림은 웹 페이지가 Display 되는 과정을 도식화한 그림이다. DOM에 조작이 일어나게 되면 각 조작을 반영하기 위해 렌더 트리를 재생성하고 레이아웃 계산, 리렌더링을 하게 된다. SPA 같은 경우에는 많은 DOM 조작이 발생하게 되는데, 원래대로라면 N개의 DOM 노드가 수정되면 N번의 레이아웃 재계산, N번의 리렌더링이 발생하게 되며 브라우저에서의 연산의 양이 많이 늘어나게 된다.

결론부터 말하자면, Virtual DOM은 N번 연산할 것을 단 한번의 연산으로 해결해준다. 리액트에서 render 함수는 실제 html을 반환하는 것이 아니라, virtual DOM을 만들기 위한 재료를 반환한다. 리액트는 이를 가지고 새로운 Virtual DOM을 구성한다. 이 DOM은 매 변화마다 렌더링이 되지는 않기 때문에 연산 비용이 적다. Virtual DOM이 구성되고 나면 현재 브라우저에 보여지는 진짜 DOM과 비교하여 달라진 점을 찾아내어 바뀐 부분만 진짜 DOM에 적용한다. 브라우저는 이 DOM을 해석해서 새로운 화면을 보여주게 되는 것이다. 즉, 레이아웃과 리렌더링의 횟수를 단 한번으로 줄여 준 것이다.

# 리액트를 올바르게 사용하기 위한 방안

위 주제에 대해 느낀 바를 토대로, 코드를 2가지 방안으로 개편해야 할 필요성을 느꼈다.

1. 코드를 처음 보는 사람이 이해 가능할 정도로 컴포넌트화 하기.

왜 컴포넌트를 만들어야 하는지에 대한 이해가 없을 때는, Template을 만드는 것 처럼 하나의 페이지를 단일 파일에서 개발한 적도 있었다. 컴포넌트화를 하며 생기는 오버헤드가 불필요한 것이라 여겼으며, 중복되는 코드는 복붙을 하면 된다는 단순한 생각 때문이었다. 이 생각이 어리석었다는 것은 앱의 규모가 조금씩 더 커지며 금방 깨달을 수 있었다. 더불어, 유지 가능한 앱을 만들기 위해서는 컴포넌트화를 통한 추상화 작업이 필요하다 느꼈다.

내가 컴포넌트화를 진행할 때 세웠던 기준은 다음과 같다.

  • 레이아웃 상에서 구분되는 영역인가

  • (추후에라도) 재사용 가능성이 있는 코드인가

  • 비슷한 기능을 하는 컴포넌트가 이미 존재하지는 않는가 (이 경우 중복되는 컴포넌트들을 하나로 추상화)

    컴포넌트화를 통해서 프로젝트의 성능이 개선되는 것은 아니었지만, 가독성이 좋아져 코드를 설명하기에도 쉬워졌으며, 불필요한 코드를 찾아내기도 용이해졌다.

2. 불필요한 렌더링 횟수 줄이기.

위에서 설명한 Virtual DOM에 대한 개념을 잘 이해했다면, 불필요한 렌더링이 바람직하지 않다는 것은 금방 알아챌 수 있다. 이 문제를 해결하기 위해서는 리액트 컴포넌트의 라이프 사이클 API와, 렌더링을 수행하는 조건에 대한 이해가 필요했다.

React Component가 렌더링을 수행하는 시점은 다음과 같다.

  1. Props가 변경되었을 때
  2. State가 변경되었을 때
  3. 부모 컴포넌트가 렌더링 되었을 때
  4. forceUpdate()가 실행되었을 때

본인은 이에 대한 정확한 인지가 없었던 터라, 다음과 같은 코드들을 무분별하게 작성했었다.

this.setState ({ rawData: data }, () ⇒ {
	this.setState({ data: this.processData(condition) })
})

state 에 저장되어 있는 rawData 를 가공해서 data 에 저장하기 위해서 rawData를 먼저 저장한 다음, 다시 setState를 해주는 과정인데, 이 경우 불필요한 렌더링을 실행한 것이다.

이를 해결하기 위해, processData 함수를 rawData를 state로 부터 받아오지 않고, parameter로 같이 입력받는 방식으로 바꾸어주었다. 이 과정을 통해 아래와 같이 코드를 고칠 수 있었다.

this.setState ({ 
	rawData: data,
	data: this.processData(condition, data)
})

이후 렌더 함수에 console.log()를 통해 렌더링 횟수를 측정해봤을 때, 렌더링 횟수가 한 번 줄어든 것을 확인할 수 있었다.


마치며

리액트를 사용하며, 늘 찜찜했던 부분을 정리해보았다. 느낀 점을 코드에도 적용시켜 본 다음 긍정적인 피드백도 받고, 실제로 렌더링 횟수가 줄어든 것을 관찰하고 나니, 이제 조금은 리액트에 대한 이해가 높아진 기분이 들었다.

이 글은 추가적인 예시가 생기면 지속적으로 업데이트 할 예정이다. 오개념에 대한 피드백이나, 앞으로 나아가면 좋을 것 같은 방향에 대해서 얘기해주시는 분들에 대해서는 미리 무한한 감사를 표하며 글을 마친다.

참고 문헌

1) 리액트 탄생 배경: https://medium.com/@RianCommunity/react의-탄생배경과-특징-4190d47a28f

2) 리액트 탄생 배경: https://spoqa.github.io/2015/09/09/react-guide-01.html

3) 리액트 개괄: https://velopert.com/3612

4) Virtual DOM에 대한 자세한 설명: https://velopert.com/3236 & https://velog.io/@sbinha/React%EC%97%90%EC%84%9C-Virtual-DOM

5) Pete Hunt의 키노트 슬라이드: slideshare.net/floydophone/react-preso-v2

6) 렌더링 관련 개념: https://medium.com/vingle-tech-blog/react-렌더링-이해하기-f255d6569849

앞으로 공부해볼 것

- 리액트 Virtual DOM의 diff algorithm :https://calendar.perfplanet.com/2013/diff/

profile
봄 날을 기다리는 눈사람

0개의 댓글