Remix: 리액트에 없는 반 쪽

Danny DongWook Kim·2022년 5월 1일
0
post-thumbnail

원문은 Remix: The Yang to React's Yin에 있습니다.
Kent.C.Dodds님의 글입니다

저는 2015년부터 리액트 애플리케이션을 만들기 시작했습니다.
리액트는 그때부터 오랫동안 저의 개발생산성을 크게 높여줬습니다.

리액트가 제공하는 웹개발을 위한 상태기반 ui 랜더링 선언적 모델은 UI를 만드는 데에 대한 생각을 극도로 단순화시켜 주었습니다.

상태에 대해서, 이전에 앵귤러나 백본을 사용할 때에 생각했던 것보다 더 앞선 생각을 할 수 있게 해줬죠.

React의 슬로건은

사용자 인터페이스를 만들기 위한 JavaScript 라이브러리

React는 이 역할을 훌륭하게 해냅니다. 리액트가 개척해낸 선언적 컴포넌트 모델이 바로 그것이죠.

우리는 어떤 상태를 관리하지 않고는 ui를 만들 수 없습니다.
이를테면, 어떤 combobox 메뉴가 열려 있을지(open) 닫혀(close) 있을지 상태 처럼 말이죠. 이것이 리액트가 컴포넌트 상태 관리 기능이 있는 이유입니다.

문제는 웹 애플리케이션은 지역 상태 관리 말고도 많은 것들이 있다는 것입니다.
사실 일반적인 React 애플리케이션에 로드 되는 대부분의 "상태"는 전혀 상태가 아닙니다.
서버에서 가지고 온 상태의 캐시 데이터죠. (예를 들면 데이터베이스와 같은 퍼시스턴트 계층으로부터 가져온 것이죠.) React가 상태를 관리하는 데 있어서 멋진 방법을 제공해 주었지만, 우리가 관리하는 대부분의 상태가 사실은 캐시이며 그 캐싱 문제가 굉장히 고통스러운 것은 숨길 수 없는 사실이죠.

필 카이트에 유명한 말이 있죠.

컴퓨터 사이언스에는 두 가지의 어려움만 존재한다. 캐시 무효화와 네이밍

많은 면에서 이것은 농담이지만, 캐시 무효화는 분명 어려운 문제입니다. 여기까지도 리액트는 이 문제에 대해 이 문제를 해결하는 데 있어서 어떤 것도 별도로 제공해 주지 않았어요.
이를 문제를 쉽게 풀기 위해 존재하는 리액트 생태계에 수많은 툴과 라이브러리들이 그 증거죠.

리엑트 툴킷, MobX, Apollo, React Query, SWR 무엇을 사용하던지, 우린 그저 웹개발에서 공통적으로 일어나는 이 문제를 해결하기 위한 시도를 하는 것이죠. 리액트는 이에 대해 빌트인으로 솔루션을 제공해주지 않습니다

네트워크 캐즘의 관리

네트워크 캐즘이 뭘까요.
제가 말하는 네트워크 키즘이란 이런 것입니다.

Network Chasm

웹 개발자로서 우리는 서버와 클라이언트에서 동작하는 코드를 작성합니다. 우리는 네트워크에 대해서 어떠한 제어권도 갖고 있지 않죠.
애초에 이것이 바로 우리가 캐싱을 하는 이유입니다.
사용자가 타코메뉴를 하나 골라서 리액트 컴포넌트가 리 랜더링 될 때, 우리는 그 선택한 타코에 가능한 옵션들을 동기적으로 접근해서 가져와야 하죠. 그래서 우리는 네트워크에 HTTP 요청을 하고, 응답받은 데이터를 리액트 상태 또는 다른 라이브러리가 제공하는 방식으로 인메모리 캐시에 저장하게 됩니다. 그리고 우리는 그 데이터로 리랜더링을 할 수 있죠

규모와 관계없이 버그의 원인이 되는 첫 번째가 무엇일까요?

코드입니다.

네트워크 캐즘으로 인해 방대한 양의 코드를 작성하게 됩니다.
이 일을 제대로 하는 건 굉장히 어렵지만, 우리는 앱을 만들고 있으니, 시도해야겠죠. 자 우리는, 자바스크립트의 힘과 모던 fetch api, 그리고 손쉽고 깔끔한 라이브러리를 사용해서, 백핸드로부터 http 요청을 해서 데이터를 가지고 오는 훅(원어: grappling hook: 끌거나 잡기 용도의 투척용 갈고리)을 쏘게 됩니다.

SPA grappling hook

이 훅이 동작하기 위한 코드는 모두 프론트엔드에 존재합니다.
데이터 패칭을 위해서 우리는 가지고 올 데이터를 알고 있어야 하는데, 꽤나 어려운 문제가 되기도 하죠. 왜냐하면 우리는 데이터가 소비되는 데이터를 소비할 코드와 함께 데이터를 패칭하는 코드를 함께 두고(co-locate) 시키고 싶어 하거든요. (그렇게 함으로써 버그와 실수 데이터 오버 패칭을 크게 줄일 수 있으니까요.) 하지만 이로인해 그 컴포넌트가 렌더링 되기까지는 데이터 패칭을 할 수 없다는 부작용이 생깁니다.

앱 로드 속도를 높이기 위해 code-splitting까지 구현하고자 한다면, 컴포넌트가 렌더링 되는 것을 기다려야 할 뿐 아니라, 그것이 렌더링될때 fetch동작을 하는 코드를 fetch해야하죠. 이렇게 네트워크 워터폴이 만들어집니다.

https://developer.chrome.com/docs/devtools/network/reference/ 역자 주) https://developer.chrome.com/docs/devtools/network/reference/

우리 모두 워터폴이 얼마나 위험한지는 알고 있잖아요.

불행하게도 fetching 그 자체는 문제를 해결하지 못합니다.
사실 데이터 fetching을 위한 React Suspense 조차도 이 문제를 해결하지는 않을 겁니다. React Suspense는 컴포넌트 안에서 데이터를 사용할 수 있(고 캐시되어있지 않으면 새로운 데이터를 가져온)다는 점 때문에 많은 데이터 패칭 라이브러리를 대체할 테지만, waterfall 효과를 피하고싶다면, 그 컴포넌트들이 렌더링되기 전에 fetching을 시작해야합니다.

더 빨리 fetch 하기

그래서 저는 React Router가 이 문제를 해결한다는 것이 정말 흥분됩니다! Remix의 멋진 점들을 React Router로 가져옴으로써 말이죠.

Ryan이 그의 포스트 Remixing React Router 글을 통해 설명합니다.

layout nested routesloaders, actions의 힘으로 우린 컴포넌트로부터 데이터 패팅을 떼어놓을(decoupling) 수 있으면서도,여전히 함께 두면서(co-loation) 많은 이점을 누릴 수 있게 되는 것이죠.

이 경우, fetch 하는 코드는 더이상 컴포넌트 안에 있지 않게되지만, nested layout routes 덕분에 완전 가깝게 위치하게 되죠.

이런 기능을 사용하면,
"어떤 리소스가 필요한지 알기 위해서는 렌더링을 해야 해" 에서 "URL만 봐도 난 필요한게 뭔지 알 수 있어"로 바뀌게 되는 것이죠.

게다가, 이제 리액트 라우터가 어느 정도 네트워크 캐즘을 해결해 주게 되었으며 관리해주니까 코드에서는 이제 로딩이나 에러 스테이트에 대해서 훨씬 덜 신경써도 된다는 겁니다. 또 React Router가 대신 캐시 리밸리데이션을 해준다는 의미기도 해요!
오, 폼 재전송과 레이스 컨디션도요 (이건 UI 개발에 있어서 더 어려운 문제들이죠)
이렇게 훌륭한 (Optimistic UI같은)UX 만드는 것이 전에 없이 쉬워지죠. 이렇게 되면, React Router는 효과적으로 약간의 네트워크 캐즘을 좁혀주게 됩니다.

Remixed router apps narrow network chasm

더 좋은 방법은 없을까요?

React Router에 이러한 기능이 생기면 앱 속도를 높이고 코드를 단순화하고자 하는 모든 사람들에게 좋을거에요. React Router는 fetching을 위해 React Suspense를 모든 사람들에게 찰떡이겠죠.(아마 Meta(전 facebook)가 가진 인프라, 컴파일러, 라우터를 사용하고 있는게 아니라면 말이죠)

하지만 우린 더 잘 할 수도 있습니다. 이전처럼 브라우저로부터 데이터를 가져오기 시작했다면, 유저는 여전히 첫 자바스크립트 번들 불러와서 화면을 띄우고 실행되어야 무엇인가를 화면에서 볼 수 있을겁니다. React Router가 데이터 로딩과 뮤테이션을 관리해주는 기능을 해주고 우리가 상태 (캐시) 관리 코드를 지우더라도, 여전히 모두 브라우저에 있게 됩니다. 게다가 fetch된 데이터가 필요한 코드를 가져오기 전에 fetch를 수행할 코드가 필요하기 때문에 더 이상 code-splitting이 아니게 되죠.

우리가 모든 코드를 브라우저로부터 서버로 옮겨 갈 수 있다면, 멋지지 않을까요? 프라이비키 넣어가지고 API 요청을 하거나, 데이터베이스와 통신하기 위해 매번 서버리스 함수를 쓰는 건 너무 짜증 나지 않나요? (정말 그렇죠)
React Server Component가 그런 것들을 제공한다 하니, 분명, 데이터 로딩 에 대해서는 기대할 수 있겠죠. 하지만 뮤테이션에 대해서는 어떤 것도 기대할 수 없고, 브라우저에서 서버를 코드를 옮길 수 있다는 것은 멋진 일이겠죠.(그리고 정식 릴리즈를 기다릴 필요도 없다면요)

그리고 출시될 때까지 기다릴 필요도 없습니다.

바로 만나보세요 : Remix

당신의 웹앱을 next level로 올려놓기 위해, 서버가 화면을 그려주는 걸 원하실 것입니다. 그렇다면 Remix가 최고의 방법이죠

Remix는 Network boundary를 가로지르는 다리를 만들어줍니다. 당신이 신경쓰지 않아도 될정도로 완성되었죠.

우린 데이터 패칭과 뮤테이션 코드를 모두 가져다가 정해진 "Remix route modules"에서 export하는 함수들로 옮겨 놓기만 하면, 모든 코드는 서버에서 동작하게되며, Remix가 모든 Network Chasm을 처리해주죠.

이제 웹앱은 정말 빠르게 날아다니기 시작합니다. 유저는 자바스크립트가 로드된 걸 기다릴 필요도 없게 된 것이죠. 앱은 이미 띄워져 있고 준비가 되어 있습니다.(progressive enhancement 덕분에 모든 링크와 폼들은 자바스크립트가 백그라운드에서 다운로드되는 동안에도 동작할 것입니다.)

그리고 알아두세요. 이제 서버에서 돌아가는 코드를 쓸 수 있기 때문에 프라이비키를 가지고 API를 요청하거나 직접적으로 db에 콜을 날리는 것에 대해서 걱정할 필요가 없습니다.
당신의 로더와 액션들이 서버에서만 동작하기 때문에 필요한 만큼 당신이 필요한 대로 다 할 수 있어요. 멋진 개발자 경험이 바로 이런거죠!

전체 앱 들여다보기

Remix로 서버사이드 코드를 쓸 수 있게 된다는 건, 정말로 앱 전체를 필요한대로 다룰 수 있게 해준다는 걸 의미합니다. 모두가 이런 방식으로 일하고 싶은건 아니지만, 서드파티 서비스와 DB에 직접 콜을 할 수 있는 백엔드를 다룰 수 있다면, 앱 구조는 이런 형태일겁니다.

app structure with Remix

멋진 점은, Remix를 100% 활용하던 안던, Remix를 사용해서 network chasm을 완전히 관리할 수 있는 이점을 누릴 수 있다는 겁니다. 그래서 이미 있는 백엔드가 좋다면 그래도 사용해도 문제없죠.

결론

React의 슬로건은

사용자 인터페이스를 만들기 위한 JavaScript 라이브러리

React는 이 목표를 멋지게 해내고 있습니다. React는 network chasm management를 약속한 적이 없죠. 하지만, 많은 웹 앱이 필요로 하죠. network chasm을 관리하는 Remix를 활용해서 우리는 React의 음(ying, 陰)에 대응하는 양(yang, 陽)을 갖게 되었습니다. 훌륭한 렌더링 라이브러리와 network chasm 슈퍼관리자를 통해서, 우리는 더 빠르고 멋진 웹앱을 만들 수 있게 되었습니다. 버그는 더 적고 단순한 코드로, 더 재밌게 말이죠.

개인적으로는, Remix로 앱을 만드는 것에 사랑하게 해준 이유입니다. 지금 읽고 있는 사이트도 Remix로 다시 쓰여진 결과입니다. 시작했을 떄는 얼마나 멋진 게 될지 몰랐습니다. Remix를 통해 이 모든 것을 실현할 수 있었습니다. 왜냐하면 기본적인 기능을 모두 마쳤을 때 더 많은 것을 할 수 있는 시간과 능력이 있다는 것을 깨달았기 때문입니다(사이트를 개편한 이야기). 리믹스는 제가 가지고 있는 재미있는 아이디어에 "네"라고 말할 수 있게 해주었고, 그것은 정말 신선했습니다.

이것이 당신이 더 나은 웹사이트를 만드는 데 도움이 되기를 바랍니다. 앞으로도, 멋지게 😎

0개의 댓글