‘단방향 데이터 흐름’.
조던 워크가 제시한 이 원칙은 며칠 동안 맷의 머릿속을 떠나지 않았다. 그는 개념 자체는 이해했다. 거미줄처럼 얽힌 양방향 통신 대신, 위에서 아래로 흐르는 단 하나의 폭포수. 혼란을 줄이고 예측 가능성을 높인다는 철학은 매력적이었다.
하지만 아무리 생각해도 현실적인 그림이 그려지지 않았다. 그는 결국 자리에서 일어나 조던에게 다가갔다.
“조던, 질문이 있습니다.”
코드를 작성하던 조던은 고개를 들어 맷을 바라보았다.
“단방향 흐름은 알겠습니다. 그런데 페이스북 페이지는 수십, 수백 개의 UI 조각들로 이루어져 있잖아요. 예를 들어, 뉴스피드 안에 게시물이 있고, 게시물 안에 댓글 창이 있고, 댓글 창 안에 사용자 아바타가 있죠. 이 모든 게 어떻게 위에서 아래로만 움직인다는 겁니까?”
조던은 말없이 자리에서 일어나 화이트보드로 걸어갔다. 그는 익숙하게 마커를 들고 나무와 같은 구조를 그리기 시작했다.
<App>
/ \
<Header> <Newsfeed>
|
<Post>
/ \
<PostHeader> <PostBody>
|
<Avatar>
“이것이 우리 UI의 구조, 즉 컴포넌트 트리라고 가정해 봅시다.” 조던이 말했다. “가장 위에 전체 애플리케이션을 감싸는 <App>
이 있고, 그 아래에 자식으로 <Newsfeed>
가, 또 그 아래에 <Post>
가 있죠. 데이터 흐름은 이 트리를 따라 위에서 아래로만 전달됩니다.”
그는 App
컴포넌트 옆에 커다란 데이터 덩어리를 그렸다.
var appData = { currentUser: { ... }, posts: [ ... ] }
“<App>
컴포넌트는 전체 데이터인 appData
를 가집니다. 그리고 자식인 <Newsfeed>
를 렌더링할 때, 필요한 데이터 조각, 즉 posts
배열만 넘겨줍니다.”
조던은 <App>
에서 <Newsfeed>
로 향하는 화살표 위에 posts
라고 적었다.
“<Newsfeed>
는 전달받은 posts
배열을 가지고, 각각의 게시물 데이터를 <Post>
컴포넌트에 하나씩 넘겨주며 렌더링하겠죠. <Post>
는 다시 받은 게시물 데이터에서 작성자 정보만 추려내 <PostHeader>
에 넘겨줄 거고요. 이런 식으로 데이터는 부모에서 자식으로, 자식에서 손주로, 마치 유전자를 물려주듯이 한 단계씩 아래로만 전달됩니다.”
맷이 고개를 갸웃거렸다. “그럼 <Avatar>
컴포넌트는, 자신의 부모인 <PostHeader>
가 어떤 컴포넌트인지, 혹은 할아버지인 <Post>
가 무슨 일을 하는지 전혀 알 필요가 없다는 뜻인가요?”
“바로 그겁니다!” 조던의 목소리에 힘이 들어갔다. “<Avatar>
는 오직 부모가 자신에게 넘겨준 ‘이미지 주소’ 데이터 하나에만 관심이 있습니다. 그 데이터가 어디서 왔는지, 왜 필요한지는 전혀 신경 쓰지 않죠. 각 컴포넌트는 완전히 독립적이고, 오직 위에서 내려오는 데이터에만 반응하는 수동적인 존재가 되는 겁니다.”
이것이 바로 캡슐화의 정점이었다. XHP가 HTML 구조를 컴포넌트 안으로 숨겼다면, 이 새로운 방식은 컴포넌트 간의 관계마저 끊어내고 있었다. 자식은 부모를 알지 못하고, 형제는 서로의 존재조차 모른다. 그들은 오직 위에서 내려오는 명령, 즉 ‘데이터’에만 복종할 뿐이다.
맷은 순간 소름이 돋는 것을 느꼈다. 만약 이 방식이 정말로 가능하다면, 그는 더 이상 하나의 버그를 잡기 위해 애플리케이션 전체의 구조를 머릿속에 그릴 필요가 없어진다. 문제가 생긴 컴포넌트와, 그 컴포넌트에 데이터를 내려주는 바로 위 부모 컴포넌트. 딱 그 둘의 관계만 살펴보면 되는 것이다.
복잡성은 사라지고, 명료함만이 남는다. 그는 비로소 조던이 그리려는 새로운 지도의 위대함을 어렴풋이 엿보기 시작했다.