본문 바로가기
개발/리액트

[리액트] VAC 패턴 적용 후기 및 장단점

by 핸디(Handy) 2022. 2. 3.

VAC 패턴을 접하고 리뷰? 글을 쓴 지 어느덧 반년이 되었습니다. 나름 빨리 글을 작성한 것인지 이젠 VAC 패턴을 검색하면 상위에 랭크되는 글이 되었네요.

글을 작성한 이후로 새로운 프로젝트에 VAC 패턴을 적용해서 프로젝트를 진행해보았습니다.
나름 장점도 있었고 단점도 있어서 이번 글에선 요런 내용들을 정리해보면서 후기를 작성해보려고 해요.

VAC 패턴이란 ?

View Asset Component의 준말로, JSX와 Style를 관리하여 UI와 비즈니스 로직을 분리하는데 목적을 둔 컴포넌트 설계 방법론입니다.

위의 사진이 VAC 패턴을 설명하는 가장 기초적인 이미지인데요. 사진으로 느낌이 안 오니 직접 코드를 보면서 살펴보겠습니다.

const SpinBox = () => {
  const [value, setValue] = useState(0);
  return (
    <div>
      <button onClick={() => setValue(value - 1)}>-</button>
      <span>{value}</span>
      <button onClick={() => setValue(value + 1)}>+</button>
    </div>
  );
};

아주 간단한 값을 증감시키는 컴포넌트입니다.
코드를 간단히 보면 useState를 이용한 부분과 return 부분에서 실제 랜더링 되는 html 태그 뭉텅이들이 있네요.
아직까진 코드가 10줄 정도밖에 되지 않아 한눈에 잘 들어오네요.
근데 만약 코드가 길어진다면? props가 생기고 state가 추가된다면 그리고 스타일을 입혀야 한다면 어떻게 될까요?

const StyledButton = styled(button){ borderRadius : 15px } 
const SpinBox = (testName) => {
  const [value, setValue] = useState(0);
  const [name, setName] = useState(testName);
  return (
    <div>
      <button onClick={() => setValue(value - 1)}>-</button>
      <span>{value}</span>
      <button onClick={() => setValue(value + 1)}>+</button> <span>{name}</span>
      <StyledButton onClick={() => setValue(name + "1")}>+</StyledButton>
    </div>
  );
};

점점 컴포넌트가 길어지고 복잡해지게 됩니다. 다행히 아직까지도 괜찮은 크기의 컴포넌트네요.
여기서 점점 더 복잡해지면 어떤 일이 일어날까요? 제 경험으로 잠깐 가보겠습니다.

문제 인식 | Conflict 자주 발생

프로젝트를 진행하면서 생기는 어려움 중에 하나는, 역할 분배입니다.
누군가는 컴포넌트 설계하고 구현하고
누군가는 서비스를 디자인하고
누군가는 프로세스를 정리하고 설계하죠.
서비스가 작고 소중할 때에는 혼자만 프로젝트를 진행해서 상관이 없습니다.

그래서 git 그래프가 일반적으로 쭉 뻗어나갑니다. 간혹 feature 단위의 개발을 위해 하는 브랜치가 생성이 되지만 그래도 1 depth 정도입니다.
근데 협업을 하면 어떻게 됩니까? 이렇게 됩니다..

특히나 업무가 제대로 분배되어있는 경우는 깔끔하지만 사이드 프로젝트의 경우 서비스 설계부터, 컴포넌트, 스타일까지 전부 다 건드리는 경우가 비일비재하기 때문에 여러 사람이 동시에 일을 하다 보면 꼭 Conflict가 발생합니다.
상황을 한번 봐볼까요?
A : 값을 증가시키는 로직을 +1이 아닌 +2로 바꿈 -> 푸시
B : 값을 증가시키는 버튼의 색상을 빨강으로 바꿈 -> 푸시

//수정 전 
<button onClick={() => setValue(value + 1)}>+</button> 
//수정 후 
<RedButton onClick={() => setValue(value + 2)}>+</RedButton>

코드로는 이렇게 될 겁니다. button이 RedButton 컴포넌트로 변경되었고 setValue안에 값이 +1에서 +2로 되는 간단한 상황입니다.
하지만 한 번에 같은 코드를 수정했으니 Conflict가 납니다. git은 라인 단위로 판단을 하니깐요.

문제 인식 | Refactor가 복잡해짐

서비스는 빠르고 또 생각지도 못하게 바뀝니다. 그래서 수정할게 많아요 ㅜㅜ
그럴 때 하나의 컴포넌트 내부에서 서비스부터, 스타일까지 변경하게 되면 정신이 없습니다.
저 같은 경우는 제가 서비스와 컴포넌트를 담당하고 다른 분은 스타일을 담당했는데, 같은 컴포넌트를 건드리지 않기 위해서 서로서로 데드락이 되는 경우가 종종 발생했습니다.
하는 일은 아주 다른 게 같은 파일 내에 있다는 것으로만 조심스러워지는 안타까운 현상인 거죠.

문제 해결 | VAC를 도입

그런데 이런 상황에서 VAC 패턴을 도입해보겠습니다.
일단 SpinBox를 VAC 패턴의 컴포넌트로 바꿔보자고요.

SpinBoxProps 부분과 SpinBoxView부분이 눈에 들어옵니다. 코드로 보니 Props를 위에서 모아서 생성하고 View 컴포넌트에 내려주는 모습으로 보이는 구조입니다.
이제 이 상황에서 아까와 같은 수정이 일어납니다.
A : 값을 증가시키는 로직을 +1이 아닌 +2로 바꿈
B : 값을 증가시키는 버튼의 색상을 빨강으로 바꿈

빨간색 줄을 친 코드 부분만 변경되었습니다. 이렇게 로직과 뷰를 분리하겠다는 디자인 패턴입니다.
위에서 말씀드렸듯이 onIncreate와 Style에 해당하는 JSX가 각각의 역할에 따라 분리되었습니다.

VAC 패턴 사용 후기

이젠 사이드 프로젝트에 적용한 찐 후기에 대해 말씀드려볼게요.

타입스크립트와 Good

첫 번 재로 타입스크립트와 아주 잘 맞습니다. 근데 이건 VAC 패턴이 잘 맞는다기 보다는 그냥 타입스크립트가 짱짱이라고 할 수 있는 포인트라고 보셔도 될 것 같네요.
일단 HomePage가 로직 컴포넌트, View가 붙은 게 ViewComponent입니다. 그래서 HomePage에서 커스텀 훅을 이용해 데이터와 메서드를 가져오고 이를 View에 넘기는 방식으로 VAC 패턴을 구현했습니다.

근데 여기서 타입스크립트와 시너지가 나오는데 Props가 가져야 할 값을 interface로 구현하고 이를 통해 View에 필요한 props들을 좀 더 직관적으로 확인할 수 있었습니다.
그리고 View 컴포넌트는 내려온 props interface만 보고 컴포넌트를 설계하고 스타일할수가 있게 됩니다. 어떻게 props가 내려오는지는 신경 쓸 필요가 없죠. 그냥 내려온다고 생각하면 됩니다.
이러한 장점은 스토리북에서도 두드러지게 나타나는데요.

독립적인 페이지 UI 테스트 가능

바로 UI 테스트가 간편해졌다는 얘기입니다.
또한 스토리북에서 interface를 통해 controls를 자동으로 생성해주고 실제 View에만 집중하면 되기 때문에 훨씬 좋아졌습니다.
일반적으로 UI를 테스트하기 어려운 점은 컴포넌트에 네트워크 요청이 있기 때문이라고 생각합니다.
근데 VAC 패턴으로 로직과 View를 분리하니 어려운 점이 간단히 해결되었습니다.

이전에는 각각의 컴포넌트 단위로만 스토리북을 생성하고 전체 UI의 경우 테스트 데이터를 전부 다 몰아넣고 테스트를 진행했었습니다. 불편하지만 방법이 없었거든요.
근데 이렇게 VAC패턴에서 View Component만 밖으로 빼고 실제 필요한 props의 경우 직접 넣거나 story에 더미로 넣어주는 방식으로 독립적인 UI 테스트가 가능해졌습니다.
그리고 스토리북을 static으로 배포를 하고 디자이너님께 url를 넘겨드렸죠.
그랬더니 실제 피그마 디자인과 개발된 결과물을 비교하시고 좀 더 구체적인 요청이나 피드백이 가능해졌다는 아주 큰 장점이 생겼습니다.

변경에 용이함

사이드프로젝트를 진행하다 보니 새로운 기술 스택에 대한 욕심이 생깁니다. 그래서 Next를 도입하고 MUI를 버전 4-> 5로 올려보기도 하고 또 tailwind를 도입해보기도 했습니다.
tailwind의 경우 독립된 스타일을 사전에 설정해놓고 조합한다는 방식이 아주 재밌었고 styled 컴포넌트나 mui의 makestyle 방식처럼 작은 디자인 변경을 위해 새로운 컴포넌트를 생성하는 방식에 점점 지쳐가고 있는 시점이기도 했고요.

 

[Next] Tailwind CSS 도입 및 세팅(feat.Next 12)

Tailwind CSS 란? 공식홈페이지의 설명을 살펴보겠습니다. "HTML에서 떠나는 일 없이 빠르게 모던한 웹사이트를 빌드해줄수 있게 하는 프레임워크" 라는 설명으로 시작합니다. 어떤걸 보여주는가 | B

all-dev-kang.tistory.com

위의 글을 읽어보시면 tailwind에 대한 고민과 next에 어떻게 적용했는지에 대해 설명하고 있습니다.
쨋든 본론으로 돌아와 기존의 View에 있는 컴포넌트들을 하나씩 바꾸고 있습니다.
이번엔 역할을 바꿔 제가 스타일을 건드리고 다른 분이 비즈니스 로직을 건드렸습니다.
그와 함께 서비스의 대대적인 개편이 이뤄졌는데요. 충돌이 거의 없었습니다.
View 컴포넌트만 전부다 tailwind로 바꿔주면 되는 문제였으니깐요.

Props가 비대 해질 수 있음

이제는 제가 느낀 단점입니다.
첫째로, 가끔씩 비즈니스 로직이냐 아니냐라는 기준으로 인해 Props가 비대해지게 됩니다.

코드를 보면 카카오 로그인을 할 수 있는 페이지이고, 각각의 라우팅 메서드들이 있습니다.
어떻게 보면 스타일이 아닌 로직이라 전부다 부모 컴포넌트로 들어가고 props로 내려주는 구조가 되었는데요.
이런 것들 많아지게 되면 props가 점점 비대해지게 됩니다. 어쩔 때는 10개 이상이 되는 경우도 심심찮게 나옵니다.
내부적으로 이런 router의 경우 하나의 훅으로 관리하자는 얘기가 나오고 있어 이건 어떻게든 변경이 될 테지만, 이렇게 애매모호한 기준 가진 props들이 점점 많아지면 불편해지게 됩니다. ( 이것도 보일러 플레이트니깐요)
기존에는 바로 버튼에 이벤트를 바인딩했다면 이젠 선언, props 내부에 넣기, props 받기, 이벤트 바인딩이라는 절차가 추가적으로 생겼기 때문입니다.

VAC 컴포넌트 기준의 모호함

이건 어쩔 수 없는 컴포넌트 기반 라이브러리의 한계라고 생각합니다. 컴포넌트를 설계할 때 어느 부분까지 공통으로 볼지에 대한 기준이 모호합니다.
리펙토링 기준으로 3번 이상 반복되면 망설임 없이 중복으로 생각하고 빼라고 하는데 이게 컴포넌트 기준으로 볼 땐 애매모호합니다.
이런 모호함은 VAC에도 똑같이 적용이 되는데요, 단순한 기능을 가진 컴포넌트 들도 VAC 패턴에 맞춰 로직과 스타일을 분리를 해야 하는가 고민을 하게 됩니다.
그래서 이런 모호함은 프로젝트를 시작하기 전에 내부적으로 결정하고 가면 좋다고 생각합니다.
VAC 패턴의 경우, 저는 스크린(페이지) 단위의 컴포넌트만 VAC 패턴으로 설계하기로 논의하고 그렇게 하고 있습니다.
결국엔 비즈니스 로직과 스타일이 철저하게 분리돼야 하는 컴포넌트는 페이지 크기일 것이고, 그것보다 작은 건 충분히 코드상으로 한눈에 들어올만한 크기였기 때문입니다.
물론 한눈에 안 들어온다면 필요한 경우 리펙토링을 하여 분리하기도 하니깐 결국 좋은 기준이라고 생각하고 있습니다.


마무리

6개월 동안 VAC 패턴을 적용하면서 얻었던 경험을 살짝 녹여보았습니다.
맨 처음엔 무슨 패턴이야 하고 어색해하던 팀원도 진행하다 보니 한눈에 들어온다고 하는 것을 보면 쓸모없는 패턴은 아니었나 봅니다. ( 저는 당연히 좋다고 생각해서 도입했으니 논외로 하고요 )
특히나 스타일쪽에선 tailwind와 함께 view 컴포넌트만 신경 쓰면서 하다 보니 UI 쪽 작업이 너무 수월해졌습니다.
아무튼 VAC 패턴에 대한 회고였습니다.

댓글