Frontend Design Pattern

aborile·2023년 4월 7일
6

Basic CS & Web

목록 보기
2/2
post-thumbnail

디자인 패턴

소프트웨어 디자인 패턴(software design pattern)은 소프트웨어 공학의 소프트웨어 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. 소스나 기계 코드로 바로 전환될수 있는 완성된 디자인은 아니며, 다른 상황에 맞게 사용될 수 있는 문제들을 해결하는데에 쓰이는 서술이나 템플릿이다. 디자인 패턴은 프로그래머가 어플리케이션이나 시스템을 디자인할 때 공통된 문제들을 해결하는데에 쓰이는 형식화 된 가장 좋은 관행이다. (위키백과)

흔히 디자인패턴이라 하면, 가장 유명하고 보편적으로 떠오른 개념은 위의 개념이고, 위의 개념을 정립시킨 아래의 책을 떠올리는 사람이 많을 것이다.
GoF — Design Patterns: Elements of Reusable Object-Oriented Software (1994)

위의 책에서는 크게 다음과 같은 패턴으로 분류하여, 다양한 디자인 패턴을 소개한다. 디자인 패턴 자체는 방법론의 일종이기도 하고, 워낙 개념이 모호하고 방대하다보니 아래 개념은 언급만 하는 정도로 넘어가고 조금 더 자세한 주제로 넘어가려 한다.

  • Creational Pattern
    • 객체 생성에 관련된 패턴
    • 객체의 생성과 조합을 캡슐화하여 특정 객체가 생성, 변경되어도 프로그램 구조에 큰 영향이 없도록
    • Abstract Factory, Builder, Factory Methods, Prototype, Singleton, …
  • Structural Pattern
    • 클래스나 객체를 조합해 더 큰 구조를 만드는 패턴
    • 예를 들어, 서로 다른 인터페이스를 지닌 객체를 묶어 단일 인터페이스를 제공하거나, 객체를 서로 묶어 새로운 기능을 제공하는 패턴
    • Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, …
  • Behavioral Pattern
    • 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴
    • 한 객체가 혼자 수행할 수 없는 작업을 어떻게 여러 개의 객체로 분배하는지, 그 과정에서 객체 간의 결합도를 어떻게 최소화하는지에 중점
    • Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor, …

프론트엔드 디자인 패턴

프론트엔드에서의 디자인 패턴에 대한 다양한 글을 읽어 보면, 주로 지속적으로 관리가 잘 되는 코드를 위한 좋은 아키텍처에 대한 논의가 이어져 온 것이라는 생각이 든다. 개인적으로는 ‘FE 개발에는 어떤 디자인 패턴이 좋은가’에 대한 명확한 답을 얻는다기보다는, ‘FE 관점에서 어떤 아키텍처가 나오고 어떻게 진화해 왔는가’에 대한 흐름을 파악하기에 좋은, 일종의 역사서를 읽는 듯한 느낌이기도 했다.

(아래의 각 이름과 아키텍처가 현재 라이브러리들과 딱 맞게 떨어지는 것은 아니고, 그냥 비슷한 개념을 이해하기 위한 일종의 분류 방식. 어떤 아키텍처는 라이브러리 출시 이후 설명을 위해 만들어지기도 했고, 어떤 라이브러리는 전략적으로 차별화를 강조하기 위해 새로 이름 짓기도 함)

MVC Architecture

Model, View, Component

  • View
    • 화면에 보여지는 UI
    • 최종적으로 HTML과 CSS로 만들어지는 결과물
  • Model
    • 데이터를 주관하는 영역
    • javascript object, server api를 통해 받는 데이터, DB 등 소프트웨어가 다룰 데이터
  • Controller
    • Model과 View 사이 중간 역할
    • Model의 데이터를 받아서 화면에 그림, 화면으로부터 사용자의 동작을 받아 Model을 변경
    • 사용자와의 상호작용은 Controller에서 담당

초창기 웹 서비스의 MVC

특히 프론트엔드라는 구분이 명확하지 않던 시절에는, 웹사이트가 그 자체로 View의 역할을 수행하는 것으로 바라봤다.

  • Model: Database
  • View: HTML+CSS+JS
  • Controller: 라우터를 통해 데이터를 처리하고 새로운 HTML을 만들어서 보여주는 영역

jQuery 등장 후 MVC

  • Model: ajax를 통해 받아오는 서버 데이터
  • View: HTML+CSS
  • Controller: 서버 데이터를 받아 화면을 바꾸고, 이벤트를 처리해서 서버에 데이터를 전달하는 JavaScript

백엔드와 프론트엔드가 명확하게 분리되며, 기존 서버 라우터에서 하던 역할을 클라이언트의 자바스크립트가 처리하게 되고, Database 등은 분리된 백엔드에서 처리하여 REST API를 통해 ajax로 받아오게 되었다.

이 과정에서 jQuery가 Controller의 주요 역할을 하였다.

jQuery - DOM Traversal and Manipulation, Event Handling, Ajax

MVC를 최대한 분리해야 한다는 원칙에 맞게, HTML과 jQuery를 따로 관리하는 것이 주요 포인트였다고 한다.

MVC의 도입 배경

  • Model과 View 간의 의존 관계를 최소화하면, 각자의 수정이 서로에 영향을 미치지 않는다.

단점

  • View를 업데이트하기 위해서는 결국 Model과 View 사이에 의존성이 존재할 수 밖에 없다. (규모가 커질수록 복잡해지게 됨.)
  • 데이터를 받아오고 연동하는 부분에서 반복적인 패턴이 발생할 수 밖에 없게 된다.
  • 서버에서 html을 만들어서 내려줄 때에는 어차피 전체적으로 렌더링해야 하므로 {{ }}, <?= ?>, <%= %> 등의 치환자를 통해 선언적인 개발이 가능했으나, jQuery의 경우 전체 html 갱신을 지양해야 하므로 수정해야 할 부분을 일일이 찾아서 수정해야 한다.
  • 현대의 Controller는 책임이 많기 때문에 Controller의 크기가 커질 수 밖에 없다.

MVP Architecture

MVC 구조에서 View와 Model간의 의존성을 해결하기 위해 제시된 모델이다. Controller 대신 Presenter가 생긴 구조로, 한때 Android 개발에서 가장 인기 있었던 구조라고 한다.

  • Model
    • 데이터를 관리
  • View
    • 데이터를 보여주는 방식을 정의
    • 사용자의 action이 들어오면 presenter에게 알리는 역할
  • Presenter
    • Model과 View 사이의 중계자 역할
    • 서로의 요청과 응답을 전달해 줌

장점

  • View - Model 사이의 의존성 문제를 해결한다.

단점

  • View - Presenter가 1:1로 연결되면서 View와 Presenter 사이의 의존성이 발생하게 된다.

MVVM Architecture

2013년 구글의 앵귤러 발표로 웹 개발 방식의 패러다임이 전환되기 시작한다. jQuery를 통해 DOM을 직접 조작하는 방식에서, 템플릿과 바인딩을 통한 선언적인 방법으로 변화하게 된다. MVC의 역할은 그대로이나, 이를 구현하는 방식이 바뀐 것에 가깝다.

FE 코드에서 DOM을 직접 조작하던 코드는 사라지고, 이 기능은 프레임워크가 담당하게 되며, 개발자는 화면에 그려져야 할 데이터만 만들면 프레임워크가 알아서 그려 주었다. ⇒ 이를 View를 그리는 Model만 신경쓰게 되었다는 의미로 ViewModel이라고 부른다.

  • Model: 데이터
  • View: 시각적인 요소
  • ViewModel: Model을 가지고 View를 그리는 역할이자, View가 Model과 상호작용할 수 있게 하는 역할. UI의 재사용 가능한 부분을 추상적으로 표현할 수 있음.

이후 등장하는 React, Vue, Angular2, Svelte 등은 모두 어떤 방식의 템플릿과 바인딩 문법을 쓰느냐에 차이가 있을 뿐, MVVM이라는 아키텍처는 대부분 그대로 유지된다.

장점

  • 컨트롤러의 반복적인 기능을 선언적인 방식으로 개선하였다.
  • Model과 View의 관점을 분리하지 않고, 하나의 템플릿을 통해 관리하려는 방식으로 발전하게 된다.
    • 기존: class, id를 통해 간접적으로 HTML에 접근
    • 개선: 직접 HTML에 접근할 수 있게 됨
  • 웹의 DOM api를 잘 몰라도 비지니스 로직만으로 서비스를 만들 수 있다.

Component Pattern

웹서비스가 발전하며, 하나의 Page 안에 여러 모듈이 있고, 하나의 화면 안에 여러 개의 독립적인 화면이 구성될 수 있도록 발전하게 된다. 때문에 조금 더 작게 재사용할 수 있는 단위를 만들어서 조립하는 패턴이 등장하였다. ⇒ Component Pattern

비지니스 로직이 들어가게 되면 컴포넌트의 재사용성이 떨어지기 때문에, 가급적 비지니스 로직과 ui 컴포넌트를 분리하여 개발을 하는 것을 지향한다. ⇒ Container-Presenter Architecture

장점

  • 향상된 재사용성

단점

  • Props Drilling Problem: 컴포넌트 구조가 복잡해지면, 최하위에 특정 값을 전달하기 위해 중간 레벨의 컴포넌트가 모두 그 props를 들고 있어야 한다.

Flux Pattern

기존의 MVC 아키텍처는 컴포넌트의 재사용과 독립성만 강조한 형태이다. 같은 데이터를 공유할 때에 props를 통해서만 데이터를 전달할 수 있으므로 Model의 관리가 파편화되는 문제가 있었다.

  • 컴포넌트 구조가 복잡할 때에 발생하는 Props Drilling Problem
  • 특정 sibling에서 같은 데이터를 공유하고 사용하려 할 때 구조가 번거로워질 수밖에 없다.

⇒ 단방향 아키텍처(uni-directional architecture)에 대한 제안이 등장한다.
⇒ 비지니스 로직과 View를 아예 분리하며, 이 개념을 상태 관리(State Management)라고 부른다.

MVC 컴포넌트 관점에서 벗어나, View를 그냥 하나의 큰 단위로 바라보는 관점이다.

Action -> Dispatcher -> Store -> View, and Action

View에서 Dispatch를 통해 Action을 전달 → 전달된 Action에서는 Reducer를 통해 Store에 데이터를 보관 → Store에 있는 데이터는 다시 View로 연결 (단방향 cycle)

  • Action
    • 사용자의 상호작용, 서버 상호작용 등
    • type을 통해 역할을 지정
  • Dispatcher
    • Action을 받는 부분
    • Store가 callback을 등록할 수 있음
  • Store
    • 데이터를 보관하는 부분
    • Reducer를 통해 비즈니스 로직을 처리하여 데이터를 보관
    • 이후 View를 갱신

장점

  • Props drilling problem의 해소
  • 다양한 상태 관리 라이브러리가 등장하는 등 많은 영감을 줌 (Redux, Vuex 등)

단점

  • 장황한 문법으로 인한 높은 학습 비용
  • Action, Dispatch, Reducer를 만들고 관리해야 하므로 관리가 어려워짐

Observer-Observable Pattern

거대한 View와 상태 관리 모델을 나누는 관점은 Flux와 동일하나, 복잡한 Dispatch와 Action은 배제한 채 값이 바뀌면 그 바뀐 값을 모두에게 전달하는 개념이다. 초창기 MobX, RxJS 등이 이러한 패턴을 따른다.

MVI Architecture

Flux 패턴의 Dispatch, Action, Update의 인터페이스를 전부 Observable을 이용한 하나의 스트림으로 만든 형태이다.

Cycle.js 등의 웹 프론트엔드에서 먼저 제시되었으나, 웹보다는 ios, android에서 새로운 아키텍처로 부상되고 있다고 한다.

  • Intent
    • 유저의 버튼 클릭, api 호출 결과 등 어플리케이션의 상태를 바꾸려는 의도

이 외의 방향성

Context

Props drilling problem을 해결하기 위해 새로운 개념을 만들기보다는, props만 곧바로 전달할 수 있는 방법을 제공하면 되지 않는가에 대한 시각이다.

컴포넌트 트리에서 Context라는 큰 공통 조상을 만들어 데이터를 제공받는 방식으로, 개념적으로는 별도의 Store를 두는 Flux 패턴과 유사하나, 상대적으로 문법이 복잡하지 않다는 장점이 있다.

Atomic Pattern

거대한 View 영역과 Store 영역을 나누는 것은 동일하나, Action-Dispatch-Reducer 구조는 너무 복잡하기 때문에 손쉽게 Model에 접근하는 방법만 제공하는 것을 목표로 하는 패턴이다.

간단한 문법으로 컴포넌트 외부에서 공통의 데이터를 set, get하고 동시에 동기화를 하는 방향성이다. 더불어 computed, derived, select 등의 반응형 기능을 제공하여 관련된 데이터의 동시 업데이트를 제공한다.

Recoil, Svelte Store, Vue Composition, iotai 등이 해당한다.

React-Query

상태 관리에 편향되어 있던 시각에서 벗어나 다시 MVC의 개념을 확대하는 방향으로, 고전적인 ajax의 데이터를 Model로 간주한다.

대부분의 프론트엔드 개발은 서버 데이터를 CRUD하고 UI를 그리는 것이 중점인데, Flux, Atomic은 비동기 데이터를 처리하기에는 너무 복잡한 방법이라는 관점이다.

  • Model: 서버에서 데이터를 fetch 해오는 영역
  • View: React
  • Controller: query & mutation. 서버 데이터의 상태를 관리하고 캐싱, 동기화, refetch 등을 관리

GraphQL

미리 스키마를 정의하고 이를 기반으로 데이터를 교환한다. (Schema based 아키텍처)

이 역시 서버 데이터를 Model로 간주하고, 서버와의 통신을 통해 View에게 데이터를 전달하는 방식이다.

References

profile
기록하고 싶은 것을 기록하는 저장소

0개의 댓글