Youngjae

Web을 위한 16가지 디자인 패턴

Web을 위한 16가지 디자인 패턴

2022.09.08

dev
(Patterns.dev의 내용을 요약/정리한 것입니다)
 

Design Patterns (16가지)

Singleton Pattern

Share a single global instance throughout our application
  • Singleton : 단 하나의 인스턴스만 만들 수 있고, 전역적으로 접근 가능해야함
  • Singleton을 통해 어플리케이션의 global state를 효과적으로 관리할 수 있게끔 함
  • Object.freeze(object);
  • JS에서는 object를 단순하게 만들 수 있기 때문에 anti-pattern으로 여겨짐
 

Proxy Pattern

Intercept and control interactions to target objects
  • proxy는 대리인 이라는 뜻. 즉, object에 interact directly하는게 아니라 proxy object를 통해 interact
  • get, set 이 가장 유명한 메서드
  • Proxy Reflect
  • pros : validation, formatting, notification, debugging 할 때 사용
  • cons : affect performance if you use Proxy too much
 

Provider Pattern

Make data available to multiple child components
  • 자식 컴포넌트들에서 데이터를 쓸 때 props drilling이라는 anti-pattern이 있음
  • Context Object의 Provider이라는 HOC(High Order Component)를 이용하기
  • React.createContext() Context.Provider useContext
  • ex) styled-components의 Theme Provider
  • pros : props-drilling 막을 수 있음, 디버깅 쉬움
  • cons : rerender을 발생시켜 성능 저하 가능. 여러개의 provider을 사용하기
 

Prototype Pattern

share properties among many objects of the same type
  • prototype
  • prototype chain : JS가 __proto__ 가 가리키는 object를 재귀적으로 올라가는 것
  • 이게 왜 pattern인지 모르겠으나 class개념이 없는 JS에서 object가 어떻게 상속을 하는지(=prototype을 통해) 알 수 있도록
  • pros : prototpye chain을 통해 method나 properties의 duplication을 막아 메모리를 절약할 수 있다.
 

Container/Presentational Pattern

Enforce seperation of concerns by seperating the view from the application logic
  • 관심사 분리(seperation concerns)의 방법 중 하나
  • 아래 두 파트로 나눔
    • presentational components : how data is shown to user. application logic을 다룸 ex) rendering list of dog images
    • container components : what data is shown to user. ex) fetching the dog images
  • 간단함. presentational components들은 props로 보여줄 데이터를 container components로 부터 받아 다듬어서 보여주면 됨
  • 그러나 이 패턴은 대부분의 경우 React Hooks를 통해 대체됨. Hooks는 container components 없이도 statefulness를 추가할 수 있도록 도와줌
  • pros : 관심사 분리, presentational components의 재사용성 증진, p.c.의 경우 application logic을 몰라도 수정 가능하다(디자이너라던지), p.c.의 경우 순수함수라서 testing이 쉽다.
  • cons : Hooks라는 강력한 도구의 등장으로 이 패턴이 대체됨… 덕분에 statefulness를 위해 functional 대신 class components를 사용하는 것은 no more.
 

Observer Pattern

Use observables to notify subscribers when an event occurs
  • observers
  • subscribe() / unsubscribe()
  • notify()
  • asynchronous, event-based data에 유용하다
  • pros : 관심사 분리, 단일 책임 원칙을 지키도록 강제함(observable object → 이벤트 모니터링 / observer object → 받은 데이터 처리. 이 둘은 결합(coupling)되지 않아 언제든 (de)coupled 가능)
  • cons : observer가 너무 복잡해지면 모든 subscribers에 notifying하는 것에 퍼포먼스 이슈를 발생
 

Module Pattern

Split up your code into smaller, reusable pieces
  • module pattern은 코드를 작고, 재사용 가능한 조각으로 쪼개줌
  • 변수들이 file안에서 private함 → name collision 방지
  • export & import 문이 바로 이런 module pattern을 이용하는 것
  • React에서 사용하는 components들도 module을 만드는 것
  • props : encapsulate parts of the code, name collision, global scope pollution 방지
 

Mixin Pattern

Add functionality to objects or classes without inheritance
  • mixin : class나 object에 상속 없이도 reusable custom function을 추가하도록 하는 object
  • Object.assign 메서드를 통해 mixin을 prototype에 추가할 수 있음
  • mixin끼리 상속이 가능하다
  • ex) 브라우저의 Window interface : WindowOrWorkerGlobalScope & WindowEventHandlers mixins를 가져와서 setTimeout, setInterval, indexedDB, isSecureContext 등의 속성을 사용가능함
  • ex) React의 사례 : ES6 classes 개념의 등장 전까지는 mixin을 통해 컴포넌트에 functionality를 추가했지만, mixin 대신 high order components를 사용하도록 권장하고, 지금은 Hooks를 권장.
 

Mediator/Middleware Pattern

Use a central mediator object to handle communication between components
  • 중재자(mediator)을 통해 컴포넌트가 서로 interact할 수 있도록 함 (파일럿-관제탑 같은)
  • JS에서 mediator은 object literal 이나 function에 불과한 경우가 많음.
  • ex) Express.js → middleware
 

HOC Pattern

Pass reusable logic down as props to components throughout your application
  • 고차 컴포넌트(higher order component, HOC)를 통해 여러 컴포넌트에서 동일한 로직(스타일링, auth 요청, global state 추가 등)을 여러번 사용할 수 있다.
  • HOC : 다른 컴포넌트를 받는 컴포넌트
    • function withStyles(Component) { // withStyles가 HOC return props => { const style = { padding: '0.2rem', margin: '1rem' } return <Component style={style} {...props} /> } } const Button = () = <button>Click me!</button> const Text = () => <p>Hello World!</p> const StyledButton = withStyles(Button) const StyledText = withStyles(Text)
 
  • HOC를 compose 가능하다 (=HOC를 다른 HOC가 wrap 가능)
  • 대부분의 경우 HOC 패턴을 React Hooks로 대체 가능하지만, 일반적으로 React Hooks가 HOC 패턴을 대체하지는 않음(공식문서) → 상황에 맞게 HOC, Hooks 둘 다 사용해야
    • Hooks를 사용하면 HOC 때문에 발생하는 deeply nested tree를 막을 수 있고, wrap하지 않아도 된다.
    • HOC를 사용하면 같은 로직을 여러 컴포넌트에 한번에 제공할 수 있고, Hooks를 사용하면 컴포넌트 안에서 custom behavior을 추가할 수 있지만 여러 컴포넌트들이 이 behavior에 의존하게 된다면 HOC와 다르게 버그를 낼 가능성이 높아진다?
    • HOC 사용이 권장되는 경우 :
      • same, uncustomized behavior가 여러 컴포넌트에서 사용되어야할 때
      • 컴포넌트가 custom logic의 추가 없이 혼자서도 작동할 때
    • Hooks 사용이 권장되는 경우 :
      • 각각의 컴포넌트별로 behavior가 customized 되어야할 때
      • behavior가 전체적인 application에 퍼지지 않고 몇개의 컴포넌트에서만 사용될 때
      • behavior가 컴포넌트에 많은 properties를 추가할 때
  • ex) Apollo Client → graphql() HOC 또는 useMutation hook을 이용할 수 있음
  • pros : logic을 한 곳에서 관리 → 코드를 DRY하게, 관심사 분리
  • cons : naming collision 발생 가능

Render Props Pattern

Pass JSX elements to components through props
  • lifting states 대신 render props를 사용하는 방법
  • children도 render prop으로 넘겨주는 것이다.
  • ex) Apollo Client hooks
  • pros : 높은 컴포넌트 재사용성. 관심사 분리. HOC처럼 reusability, sharing data를 풀면서도 HOC에서 있던 naming collision, implicit props 이슈를 해결할 수 있음
  • cons : lifecycle methods를 render prop에 추가할 수 없음 → hooks로 대부분 대체됨
 

Hooks Pattern

Use functions to reuse stateful logic among multiple components throughout the app
  • React 16.8에서 등장
  • 많은 전통적인 디자인 패턴이 Hooks로 대체됨
  • class components
    • State, Lifecycle Methods(componentDidMount, componentWillUnmount) →
    • ES2015 class에 대한 이해를 동반해야했음 (bind, constructor, this 등)
    • HOC나 Render Props 방법으로 하면 wrapper hell이 발생 → data flow를 확인하기 어려움
    • 컴포넌트의 로직이 얽혀서 복잡도 증가
  • hooks
    • React Hooks → 컴포넌트의 상태관리라이프사이클 관리를 할 수 있는 function
    • useState : 상태관리
    • useEffect : lifecycle 관리
    • build-in hooks(useStateuseEffectuseReduceruseRefuseContextuseMemouseImperativeHandleuseLayoutEffectuseDebugValueuseCallback) 뿐 아니라 custom hooks도 만들 수 있음 → rules of Hooks를 따르면 됨
  • seperate the logic이 더 clear하다
  • 컴포넌트에서 stateful logic을 재사용함으로써 testability, flexibility, readability가 모두 증가!
  • pros : 적은 라인의 코드, 복잡한 컴포넌트 단순화, stateful logic 재사용, non-visual logic 공유
  • cons : rule of hooks을 지켜야함, 올바르게 사용하기 위해 러닝커브가 있음, 잘못된 사용을 조심하기(useCallback, useMemo)
 

Flyweight Pattern

Reuse existing instances when working with identical objects
  • Object를 만들 때 공유해서 사용함으로써 메모리를 절약하는 패턴
  • JS에서는 프로토타입 상속(prototypal inheritance)으로 통해 쉽게 구현 가능
 

Factory Pattern

Use a factory function in order to create objects
  • factory functions : new 키워드를 사용하지 않고 새로운 object를 반환하는 함수
  • pros : 같은 properties를 공유하는 여러개의 작은 object를 만들 때
  • cons : 패턴이라기 보다 함수에 가까운 간단함. ES6 arrow function도 일종의 factory functions. 새로운 instance를 만드는게 새로운 object를 만드는 것 보다 메모리 효율적이다.
 

Compound Pattern

Create multiple components that work together to perform a single task
  • 서로 dependent한 컴포넌트
  • Context API, React.Children.map
  • ex) Semantic UI
  • pros : child component를 따로 import하지 않아도 됨
  • cons : component nesting이 제한적(direct children만 parent component에 access 가능), React.cloneElement → shallow merge를 하기 때문에 naming collision 발생 가능
 

Command Pattern

Decouple methods that execute tasks by sending commands to a commander
  • 클래스 안에 methods를 만들지 말고, 외부에 command functions를 따로 두는 것
  • object와 command functions를 분리하자 ← command pattern
  • pros : 메서드를 operation을 실행하는 object와 decouple,
  • cons : 사용처가 아주 한정적임, 불필요한 보일러플레이트 추가
공유하기

Youngjae Jang

Copyright © 2022, All right reserved.