🖊️

Component 에서 presentation 과 로직의 분리의 중요성

Tags
clean code
front
React
Component
Date
2021/11/30
속성

개요

최근 rebitly 에서 demo 기능을 개발해야할 일이 있었다. 가입을 하지 않고 처음 서비스에 방문한 유저들에게 기존에 있던 서비스의 user interface 를 모두 제공하되 기능만 모두 잠궈서 제공하는 것이다. 실제 UI 는 기존에 있던 것들이라 대략 하루 개발하면 완성할거라고 estimate 을 했다. 그런데 실제 개발을 하니까 1주일이 걸리는 테스크가 되었다?!

무슨일이 있었나 1

실제로 이미 만들어진 UI 만 사용하면 되는 일이었다. 그러나 문제는 이미 만들어진 Component 들과 결합한 데이터 로직들이었다.
우선 아래는 Demo 가 아니라 기존 유저가 사용하던 LinkListSection 컴포넌트다.
LinkListSection
썩 괜찮다고는 할 수 없지만 여기까지는 괜찮은 것 같다. 이제 로직(useLinkList)과 UI 를 분리하고 해당 데이터들을 props 로 받으면 될 것만 같다. 그런데 문제는 내부에 있는 컴포넌트들에 들어가면서부터 생긴다.
Header and inner button
당연히 presentation component 라고 예상했던 component 가 까보니 내부에 있는 CSVDOwnloadButton 이란 로직과 결합된 컴포넌트가 있었다! csvDownloadButton 컴포넌트는 onClick 을 props 로 받지 않고 이미 컴포넌트 내부에 기능이 정의되어있다.

그 때는 맞고 지금은 틀리다?

일단 해당 CSV 다운로드 기능은 내가 구현한 것이 아니라 내가 코드리뷰를 했다. (사실 코드리뷰를 했다면 내가 만든 것과 크게 다르지 않다고 생각한다. 이 코드에 문제가 있다면 내가 책임을 벗어날 수 없다) 리뷰를 할 때는 변경사항만 보다보니 크게 틀린 것이 없어보이지만 전체적으로 보니 아쉬운 부분은 Header component 는 외부의 props 를 통해서 컨트롤 되는 것 처럼 선언적으로 프로그래밍 되어있는데, 그 내부에서 사용되는 csvButton component 는 이미 기능이 붙은 체로 구현이 되어있다는 점이 아쉽다.
그런데 잘 생각해보면 내가 만일 그 때 당시로 다시 돌아가서 코드를 전체적으로 본다면 똑같은 생각을 할까? 생각해보면 CSVDownload button 은 말 그대로 link 들을 csv 로 저장하는 컴포넌트기 때문에 사용하는 곳이 한군데 밖에 없었고(링크 리스트 페이지), 때문에 button 내부에 UI 와 로직을 그대로 두는게 크게 이상하게 느껴지지는 않을 수 있을 것 같은데? (그렇게 생각할지도 모르지만 나는 아무튼 컴포넌트를 선언적으로 만드는 편이 더 좋다고 느껴지긴 한다. 그리고 presentaion 과 container 를 분리하는게 단일 책임 원칙(이하 SRP)에 맞다고 생각하기도 한다.)
아무튼 지금으로선 demo 기능이 들어가기 때문에 (demo 에서는 이 버튼을 누르면 demo 에서는 해당 기능을 지원하지 않는다는 알림을 보여줘야한다. copy 해봤자 의미 없는 url 을 보여줄 것이기 때문에!) 그래서 지금으로선 UI 와 click 을 분리해야한다. 그 때는 맞을지도 모르지만 지금은 명백히 틀리다!
지금은 LinkListHeader 만 보이지만 더 큰 문제는 사실 LinkListItem 컴포넌트다. 내부에 많은 컴포넌트들로 이루어져 있으면 위의 케이스와 마찬가지로 로직과 UI 가 뒤섞여있다. 자, 그렇다면 어떻게 해야할까?

Atomic design system 에서 방법을 찾다?

Atomic 디자인 시스템은 Atom, Molecule, Organism, Template, Page 단위의 컴포넌트 단위를 구성할 것을 제안한다. (Atomic design system 참고) 이 중에서 주로 Presentation Component (UI 만 관여하는 컴포넌트) 단위로 적절한 것은 Atom, Molecule, Template 단위의 컴포넌트다. 이 단위의 컴포넌트들이 UI 레벨 단계에서의 재사용성이 매우 높게 나타난다. 따라서 이 단위들을 제외한 영역(Organism, Page)에서는 로직이 관여하도록 만들 수 있을 것이다.

해결책?

우선 위의 예에서 LinkListSection 의 경우는 organism 단위이다. 그리고 하위의 컴포넌트들은 molecule 이하의 단위의 컴포넌트로 취급할 수 있겠다.(하나 이상의 기능을 해서 organism 으로 편성된다고 해도 상관 없겠다.) 따라서 Demo 용, Live 서비스용 로직 관리를 하는 LinkListSection 컴포넌트를 두개를 생성한다. 전체적인 UI 의 구성은 같으니 이를 구성할 수 있는 템플릿 단위의 컴포넌트를 만든다면 재사용성이 더 높아질 것이다. (organism 안에 template 컴포넌트가 들어가게 되는 것이다. 이게 옳은가 그른가에 대해서는 잘 모르겠지만 당면한 문제를 해결하면서 재사용성도 높일 수 있는 방법이라고 생각이 된다)
정리하자면 Atomic 디자인 시스템에서 상태관리 로직이 들어가는 컴포넌트는 Organism, Page 단위의 컴포넌트들만 허용이 되고 그 외의 컴포넌트들은 Presentational Component 로만 사용하는 규칙을 정해서 사용한다.
위의 대원칙을 정하고 컴포넌트들을 구성하니 재사용성과 관심사를 분리(SRP)하면서 프로젝트를 만들어나갈 수 있어서 좋았다.