본문 바로가기
React

React에서 복잡한 상태를 관리하기 위해서 MVC패턴을 적용해보자 - Why편

by ttum 2024. 1. 25.

들어가며

회사에서 업무를 하며 프로젝트에 MVC패턴을 적용했다. 프론트엔드에서는 흔하게 사용되는 구조가 아니기도 하며, 또 나와 같은 어려운 상황에 겪을 누군가를 위해 조금이라도 도움이 되고자 글을 작성한다. 글의 순서는 다음과 같다.

  1. 나는 어쩌다가 프론트에서 MVC 패턴을 적용하게 되었을까?
  2. MVC 패턴이란 무엇인가?
  3. 프론트에서 MVC 패턴을 사용해본 후기

나는 어쩌다가 프론트에서 MVC 패턴을 적용하게 되었을까?

현재 회사에서 AI 휴먼 영상생성 에디터를 개발하고 있다. 에디터 자체도 개발하기에 복잡한 편이지만, 자동으로 AI 음성/ 영상생성해서 캔버스나 타임라인에 적용해줘야하기 때문에 복잡함이 배가 된다. (이 글에서 타임라인은 대부분의 영상편기 하단부에 있는 그 타임라인이다. 아래 우측 사진 참고)

좌) PERSO 버전 2의 사진 / 우) PERSO 버전3의 초기 디자인 시안인데, 레이아웃 구조의 변경은 있었지만 얼추 비슷한 느낌이다.

 

PERSO 서비스의 제로투원을 만들어내는 과정에서 각기 다양한 미션들이 있었다.

Version 1. 빠른 시간 내에 서비스를 완성시키기

Version 2. 각각의 역할을 구조화 시키고 기능 추가해가기 (왼쪽 사진 참고)

Version 3. 타임라인 기능이 추가되면서 복잡도가 급격히 상승 (오른쪽 사진 참고)

 

버전 2까지는 각각의 역할을 하는 커스텀 훅으로 필요한 기능들을 만들 수 있었다. 그러나 계속해서 늘어나는 피처들을 추가할때마다 개발 생산성이 떨어져갔다. 그러다보니 유연성 및 확장성에 대한 갈증이 계속 커졌고 더 나은 구조를 만들고 싶다는 생각 뿐이었다. 그런 상황에서 버전 3에 대한 기능 명세를 받게 되었다. 스펙을 보니 이전과는 비교되지 않게 복잡해졌다. 시간이 걸리더라도 이번 기회에 제대로 설계하고 가는 것이 장기적으로 봤을때 반드시 도움이 될것이라는 확신이 생겼다.

 

당시 문제라고 생각했던 것은 다음과 같다.

  • 기능이 많은 만큼 코드의 양이 너무 많아지자 역할이 애매모호하거나 중복되는 커스텀훅들이 생겨났다. 하나의 기능을 수정할때 어디에 가서 수정해야할지 고민하는 시간이 늘어갔다. 각자 역할이 명확해야 했다.
  • 연속적으로 이어져야 하는 플로우가 너무 많기 때문에 이를 effect에 의존하는 순간 디버깅이 헬이 된다. 명시적으로 할 일들을 큐잉할 수 있어야 한다.
  • 당시에 VAC패턴을 사용중이어서 하나의 컴포넌트 당 View와 비즈니스로직을 작성하는 두 개의 파일이 있었는데, 워낙 작은 버튼 하나에 거대한 기능이 들어가다보니 비즈니스 로직의 코드량이 너무 거대해졌다.
  • 에러처리가 직관적이고 일관되지 못하게 되고 있었다. 다양한 뎁스의 커스텀훅에서 사용되는 에러처리는 더욱 디버깅을 어렵게 만들었다. 나머지 모든 커스텀 훅에서는 에러를 뱉기만 하고, 가장 최상단(뷰와 밀접한) 로직에서만 에러 핸들링을 해야겠다는 생각이 들었다.

 

한 마디로 정리하자면 코드간의 커플링이 심한상태였고, 각각의 명확한 역할만 할 수 있도록 수정이 필요했다.

다양한 디자인 패턴이나 방법론에 대해서 찾아본 후에 하나의 결론은 한 가지로 수렴했다. 어떤 방법을 적용하여 문제를 해결하던, 원하는 본질은 하나였다. 서로를 침범하지 않으면서 본인의 일에만 충실하도록 만들도록 해야했다. 

 

  1. 타임라인의 각 에셋들은 영향을 미치는 범위가 너무 넓으니, 코어단에서 자신이 맡은 역할만을 수행 하는 아이가 필요했다. 이 코어단에서는 전역 상태값을 관리하고, 상태를 업데이트하고 비즈니스 로직을 수행하는 로직이 포함되어 있다. 또한 컴포넌트에 구애받지 않고 독립적이어야 한다. (*뷰에 영향을 받지 않은채로 동작하는 로직들을 코어단이라고 칭하겠다.)
  2. 이 코어단의 아이들에게 일련의 일들을 시켜주는 이벤트 핸들러 함수들을 모아놓은 파일이 필요했다.
  3. 마지막으로 어떠한 로직도 들어가지 않고 이벤트 핸들러 함수나 값을 받아 뿌려주기만 하는 뷰의 역할을 하는 아이도 있는것이 좋을 것 같았다.

이렇게 세가지가 필요했다. 코어에서 관리하는 상태는 Model, 일을 시키는 애는 Controller, 보여주는 애는 View. 그렇게 무엇보다 자기 역할에 충실할 수 있도록 MVC패턴을 적용하게 되었다.

 

MVC 패턴이란 무엇인가?

MVC(Model View Controller)는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 사용되는 소프트웨어 디자인 패턴이다. 비즈니스로직과 화면을 구분하는데 중점을 두고 있다. 이렇게 관심사를 분리함으로서 MVC 패턴은 각 구성 요소의 역할이 명확하게 분리되어 있어, 유지 관리와 확장이 용이하며, 여러 개발자가 동시에 작업하는 대규모 프로젝트에 효과적이다.

 

기존의 MVC의 역할

  1. Model: 데이터와 비즈니스 로직을 관리
  2. View: 레이아웃과 화면을 처리
  3. Controller: 모델과 뷰로 명령을 전달

 

리액트에서 정의해본 MVC의 역할

  1. Model: 상태들이 저장되는 곳. 우리 프로젝트의 경우 Recoil을 사용했기 때문에 여기에 저장되는 전역 상태들.
  2. View: tsx 문법에 해당한다. 리액트에서는 뷰의 업데이트가 선언적으로 이루어지기 때문에 모델이나 컨트롤러에서 렌더를 요청하는 일은 없었으며, 데이터를 보여주는 역할
  3. Controller: 뷰에 추가할 이벤트 핸들러 함수나 렌더링할 데이터를 내보내는 역할

 

프론트엔드에서 MVC 패턴을 사용해본 후기

좋았던 점

  • 일단 프로젝트에 적용해보고 실제로 유지 및 보수하는 데 있어 굉장히 성공적이었다고 말하고 싶다. 예측 가능한 코드가 되었고, 코드를 추가할때에도 거침이 없어졌다. 미리 설계를 해둔 덕분에 머뭇거리는 시간이 많이 줄었다.
  • 많은 사람들에게 이런 복잡한 서비스를 만들때 Recoil로 하는게 어렵지 않냐는 질문들 들었었는데, 생각보다 구조가 잘 갖춰진 후로 Reocil을 사용해서 불편한 점을 거의 느끼지 못했다. 오히려 커스텀이 자유로운 것이 장점으로 다가왔다.

 

아쉬웠던 점

  • PERSO를 개발하며, 에디터를 만드는 다양한 분들을 만날 기회가 있었다. 이미 충분히 만족하지만서도 다른 좋은 예시들을 충분히 찾아보지 못했다는 아쉬움이 남았다. 이건 PERSO 프로젝트를 하며 얻은 큰 교훈이기도 하다. 어려운 일을 마주했을때 좋은 선례들은 늘 인사이트를 가져온다. (혹시나 에디터를 만드시게될 분들을 위하여 정말 흥미롭게 봤던 심흥운님의 컨퍼런스 링크를 추가한다.)
  • 아무래도 MVC 패턴을 적용하다보니 보일러플레이트와 코드량이 증가했던 부분도 있다. 이를 해결하기 위해 plop.js의 파일 제너레이터를 이용하여 보일러플레이트를 만들어 사용했다.

 

느낀 점

이 리액트의 MVC 패턴은 상태가 복잡한 프로젝트에 더 어울린다. 하지만 진짜로 상태가 복잡한 프로젝트를 경험하고 있다면, 어떻게 설계해야 할지 막막하다면 한번쯤 추천해보고 싶다. (물론 이 패턴을 사용한다고 만사오케이는 아니다. 어떤 서비스가 필요하고 어떤 역할을 맡게할지에 대해 깊이있게 고민하는 것은 필수다.)

 

 

레퍼런스

 

 

 

'React' 카테고리의 다른 글

ReactNode와 ReactElement의 차이  (0) 2024.02.20
Recoil under the hood🤿 리코일을 직접 만들어보기  (3) 2023.11.24
리코일(Recoil) 기초  (0) 2021.05.19
React this.state의 this의 의미  (1) 2020.04.06
Redux 세 가지 원칙  (0) 2020.02.18