소프트웨어 디자인 패턴(software design pattern)은 소프트웨어 공학의 소프트웨어 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. 소스나 기계 코드로 바로 전환될수 있는 완성된 디자인은 아니며, 다른 상황에 맞게 사용될 수 있는 문제들을 해결하는데에 쓰이는 서술이나 템플릿이다. 디자인 패턴은 프로그래머가 어플리케이션이나 시스템을 디자인할 때 공통된 문제들을 해결하는데에 쓰이는 형식화 된 가장 좋은 관행이다. (위키백과)
흔히 디자인패턴이라 하면, 가장 유명하고 보편적으로 떠오른 개념은 위의 개념이고, 위의 개념을 정립시킨 아래의 책을 떠올리는 사람이 많을 것이다.
GoF — Design Patterns: Elements of Reusable Object-Oriented Software (1994)
위의 책에서는 크게 다음과 같은 패턴으로 분류하여, 다양한 디자인 패턴을 소개한다. 디자인 패턴 자체는 방법론의 일종이기도 하고, 워낙 개념이 모호하고 방대하다보니 아래 개념은 언급만 하는 정도로 넘어가고 조금 더 자세한 주제로 넘어가려 한다.
프론트엔드에서의 디자인 패턴에 대한 다양한 글을 읽어 보면, 주로 지속적으로 관리가 잘 되는 코드를 위한 좋은 아키텍처에 대한 논의가 이어져 온 것이라는 생각이 든다. 개인적으로는 ‘FE 개발에는 어떤 디자인 패턴이 좋은가’에 대한 명확한 답을 얻는다기보다는, ‘FE 관점에서 어떤 아키텍처가 나오고 어떻게 진화해 왔는가’에 대한 흐름을 파악하기에 좋은, 일종의 역사서를 읽는 듯한 느낌이기도 했다.
(아래의 각 이름과 아키텍처가 현재 라이브러리들과 딱 맞게 떨어지는 것은 아니고, 그냥 비슷한 개념을 이해하기 위한 일종의 분류 방식. 어떤 아키텍처는 라이브러리 출시 이후 설명을 위해 만들어지기도 했고, 어떤 라이브러리는 전략적으로 차별화를 강조하기 위해 새로 이름 짓기도 함)
특히 프론트엔드라는 구분이 명확하지 않던 시절에는, 웹사이트가 그 자체로 View의 역할을 수행하는 것으로 바라봤다.
백엔드와 프론트엔드가 명확하게 분리되며, 기존 서버 라우터에서 하던 역할을 클라이언트의 자바스크립트가 처리하게 되고, Database 등은 분리된 백엔드에서 처리하여 REST API를 통해 ajax로 받아오게 되었다.
이 과정에서 jQuery가 Controller의 주요 역할을 하였다.
MVC를 최대한 분리해야 한다는 원칙에 맞게, HTML과 jQuery를 따로 관리하는 것이 주요 포인트였다고 한다.
{{ }}
, <?= ?>
, <%= %>
등의 치환자를 통해 선언적인 개발이 가능했으나, jQuery의 경우 전체 html 갱신을 지양해야 하므로 수정해야 할 부분을 일일이 찾아서 수정해야 한다.MVC 구조에서 View와 Model간의 의존성을 해결하기 위해 제시된 모델이다. Controller 대신 Presenter가 생긴 구조로, 한때 Android 개발에서 가장 인기 있었던 구조라고 한다.
2013년 구글의 앵귤러 발표로 웹 개발 방식의 패러다임이 전환되기 시작한다. jQuery를 통해 DOM을 직접 조작하는 방식에서, 템플릿과 바인딩을 통한 선언적인 방법으로 변화하게 된다. MVC의 역할은 그대로이나, 이를 구현하는 방식이 바뀐 것에 가깝다.
FE 코드에서 DOM을 직접 조작하던 코드는 사라지고, 이 기능은 프레임워크가 담당하게 되며, 개발자는 화면에 그려져야 할 데이터만 만들면 프레임워크가 알아서 그려 주었다. ⇒ 이를 View를 그리는 Model만 신경쓰게 되었다는 의미로 ViewModel이라고 부른다.
이후 등장하는 React, Vue, Angular2, Svelte 등은 모두 어떤 방식의 템플릿과 바인딩 문법을 쓰느냐에 차이가 있을 뿐, MVVM이라는 아키텍처는 대부분 그대로 유지된다.
웹서비스가 발전하며, 하나의 Page 안에 여러 모듈이 있고, 하나의 화면 안에 여러 개의 독립적인 화면이 구성될 수 있도록 발전하게 된다. 때문에 조금 더 작게 재사용할 수 있는 단위를 만들어서 조립하는 패턴이 등장하였다. ⇒ Component Pattern
비지니스 로직이 들어가게 되면 컴포넌트의 재사용성이 떨어지기 때문에, 가급적 비지니스 로직과 ui 컴포넌트를 분리하여 개발을 하는 것을 지향한다. ⇒ Container-Presenter Architecture
기존의 MVC 아키텍처는 컴포넌트의 재사용과 독립성만 강조한 형태이다. 같은 데이터를 공유할 때에 props를 통해서만 데이터를 전달할 수 있으므로 Model의 관리가 파편화되는 문제가 있었다.
⇒ 단방향 아키텍처(uni-directional architecture)에 대한 제안이 등장한다.
⇒ 비지니스 로직과 View를 아예 분리하며, 이 개념을 상태 관리(State Management)라고 부른다.
MVC 컴포넌트 관점에서 벗어나, View를 그냥 하나의 큰 단위로 바라보는 관점이다.
View에서 Dispatch를 통해 Action을 전달 → 전달된 Action에서는 Reducer를 통해 Store에 데이터를 보관 → Store에 있는 데이터는 다시 View로 연결 (단방향 cycle)
거대한 View와 상태 관리 모델을 나누는 관점은 Flux와 동일하나, 복잡한 Dispatch와 Action은 배제한 채 값이 바뀌면 그 바뀐 값을 모두에게 전달하는 개념이다. 초창기 MobX, RxJS 등이 이러한 패턴을 따른다.
Flux 패턴의 Dispatch, Action, Update의 인터페이스를 전부 Observable을 이용한 하나의 스트림으로 만든 형태이다.
Cycle.js 등의 웹 프론트엔드에서 먼저 제시되었으나, 웹보다는 ios, android에서 새로운 아키텍처로 부상되고 있다고 한다.
Props drilling problem을 해결하기 위해 새로운 개념을 만들기보다는, props만 곧바로 전달할 수 있는 방법을 제공하면 되지 않는가에 대한 시각이다.
컴포넌트 트리에서 Context라는 큰 공통 조상을 만들어 데이터를 제공받는 방식으로, 개념적으로는 별도의 Store를 두는 Flux 패턴과 유사하나, 상대적으로 문법이 복잡하지 않다는 장점이 있다.
거대한 View 영역과 Store 영역을 나누는 것은 동일하나, Action-Dispatch-Reducer 구조는 너무 복잡하기 때문에 손쉽게 Model에 접근하는 방법만 제공하는 것을 목표로 하는 패턴이다.
간단한 문법으로 컴포넌트 외부에서 공통의 데이터를 set, get하고 동시에 동기화를 하는 방향성이다. 더불어 computed, derived, select 등의 반응형 기능을 제공하여 관련된 데이터의 동시 업데이트를 제공한다.
Recoil, Svelte Store, Vue Composition, iotai 등이 해당한다.
상태 관리에 편향되어 있던 시각에서 벗어나 다시 MVC의 개념을 확대하는 방향으로, 고전적인 ajax의 데이터를 Model로 간주한다.
대부분의 프론트엔드 개발은 서버 데이터를 CRUD하고 UI를 그리는 것이 중점인데, Flux, Atomic은 비동기 데이터를 처리하기에는 너무 복잡한 방법이라는 관점이다.
미리 스키마를 정의하고 이를 기반으로 데이터를 교환한다. (Schema based 아키텍처)
이 역시 서버 데이터를 Model로 간주하고, 서버와의 통신을 통해 View에게 데이터를 전달하는 방식이다.