리팩토링을 위해 공부하는 소프트웨어 디자인 패턴과 프로그래밍 패러다임

3
post-thumbnail

해당 포스트는 도서 [ 면접을 위한 CS 전공지식 노트 ] (주홍철 저) 를 읽고 정리한 글입니다.

디자인 패턴

디자인 패턴이란?

디자인 패턴이란 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 ‘규약’ 형태로 만들어놓은 것

💎 싱글톤 패턴(singleton pattern)

하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴

주로 데이터베이스 연결 모듈에서 많이 사용됨.

하나의 인스턴스를 만들어놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용하는 방식.

  • 장점 :

    • 인스턴스를 하나만 만들어도 되므로, 인스턴스 생성 비용 절감됨.
  • 단점 :

    • 의존성 높아짐.
    • TDD(Test Driven Development) 에 불리함. (TDD 는 주로 단위 테스트로 진행되는데, 단위 테스트를 수행하기 위해서는 테스트가 서로 독립적이어야 하고 테스트를 어떤 순서로든 실행할 수 있어야 하지만, 싱글톤 패턴을 사용할 경우 서로 독립적인 인스턴스를 만들기 어려움.)
  • 🚥 의존성 주입 :

    • 모듈 간의 결합을 강하게 만들어 의존성이 높아진다는 싱글톤 패턴의 단점을 보완하기 위한 방안
    • A가 B에 의존성(=종속성)이 있다는 것은 B의 변경 사항에 A 또한 변해야 된다는 것을 의미함.
    • 메인 모듈(=상위 모듈)이 직접 다른 하위 모듈에 대한 의존성을 주는 것이 아니라, 중간에 의존성 주입자를 통해 메인 모듈이 하위 모듈들에 간접적으로 의존성을 주입하는 방식. (이를 통해 메인 모듈은 하위 모듈에 의존성이 떨어지게 됨. 이를 디커플링이라고 함.)
    • 장점 :
      • 모듈을 쉽게 교체할 수 있어 테스팅하기 쉽고 마이그레이션에 수월함.
      • 애플리케이션의 의존성 방향이 일관되고, 애플리케이션을 쉽게 추론할 수 있으며 모듈 간 관계들이 명확해짐.
    • 단점 :
      • 클래스 수가 늘어나 복잡성이 증가함.
    • 의존성 주입 원칙 :
      • 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 한다.
      • 상, 하위 모듈은 모두 추상화에 의존해야 한다. 이때 추상화는 세부 사항에 의존하지 말아야 한다.

🏭 팩토리 패턴(factory pattern)

객체를 사용하는 코드에서 객체를 생성하는 부분을 떼어내 추상화한 패턴

상속 관계에 있는 두 클래스에서 상위 클래스가 중요 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴

  • 장점 :
    • 클래스가 상위(뼈대), 하위(상세) 클래스로 분리되기 때문에 느슨한 결합을 가지며 상위 클래스에서는 인스턴스 생성 방식에 대해 알 필요가 없으므로 더 많은 유연성을 가짐.
    • 객체 생성 로직이 분리되어 있으므로 리팩토링 시 한 곳만 고치면 되어 유지 보수성이 높아짐.

💊 전략 패턴(strategy pattern)

객체의 행위를 바꾸고 싶은 경우 ‘직접’ 수정하지 않고 전략(=캡슐화한 알고리즘)을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴

👁️‍🗨️ 옵저버 패턴(observer pattern)

주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴

주체란 객체의 상태 변화를 보고 있는 관찰자, 옵저버란 이 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 추가 변화 사항이 생기는 객체들을 의미함.

주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축할 수도 있음.

대표적인 예로, 트위터를 들 수 있음. B,C,D 라는 객체가 A 라는 객체를 팔로우 했다면, A가 게시글을 올렸을 때, 그 변경사항을 B,C,D가 모두 반영해야 함. 즉 A 는 주체 겸 변화가 발생하는 객체라고 볼 수 있고, B,C,D 는 추가 변화 사항이 생기는 객체이므로 옵저버라고 할 수 있음.

  • 자바스크립트의 프록시(proxy) 객체를 이용하여 옵저버 패턴 구현하기
    • 프록시 객체는 어떠한 대상의 기본적인 동작의 작업을 가로챌 수 있는 객체이며, 두 가지 매개변수를 가진다. 하나는 프록시할 대상을 의미하는 target, 다른 하나는 target 을 가로채서 수행할 동작을 정의한 함수를 의미하는 handler 이다.
    • 프록시 객체를 사용할 경우, 어떤 동작을 수행하기 앞서 검사하는 것이 가능하다. 따라서 객체의 변경사항을 감시할 수 있고, 이를 옵저버에 반영하는 것이 가능하다.

🦅 프록시 패턴(proxy pattern)

대상 객체에 접근하기 전 그 접근에 대한 흐름을 가로채 대상 객체 앞단의 인터페이스 역할을 하는 디자인 패턴

주로 보안, 데이터 검증, 캐싱, 로깅 등에 사용함.

  • 프록시 서버
    • 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 시스템
    • 대표적인 예
      • node.js 의 프록시 서버로 쓰이는 nginx 가 있음. nginx 를 node.js 서버 앞단에 두면, 익명 사용자의 직접적인 서버 접근을 차단할 수 있어 보안성이 강화됨.
      • 콘텐츠 분산을 통해 장거리에서도 데이터를 효과적으로 전달하는 CDN(Content Delivery Network) 서비스인 CloudFlare가 있음. CloudFlare는 CDN 서비스 말고도 DDOS 공격 방어, HTTPS 구축 등의 서비스를 제공하는데, 이는 프록시 패턴을 따르기에 가능함.
  • CORS 와 프론트엔드의 프록시 서버
    • CORS 는 웹 브라우저와 서버 간의 통신에서 서로 같은 오리진을 사용하게끔 제한하는 프로토콜이다. 만약 웹 브라우저와 서버가 서로 다른 오리진을 사용할 경우, CORS 에러가 발생한다.
      • 오리진(Origin) : 프로토콜과 호스트 이름, 포트 번호의 조합
    • 만약 서버와 프론트엔드 서버에서 사용하는 오리진이 다를 경우, 프록시 서버를 통해 프론트엔드에서 요청하는 오리진을 서버와 동일하게 바꿈으로써 CORS 에러를 해결할 수 있음.

🚢 이터레이터 패턴(iterator pattern)

이터레이터를 사용하여 컬렉션(collection)의 요소들에 접근하는 디자인 패턴

순회 대상의 자료 구조에는 상관없이, 이터레이터라는 하나의 인터페이스로 순회가 가능한 패턴

대표적인 예로, 자바스크립트의 set 과 map 을 들 수 있음. set 과 map 은 서로 다른 자료형이지만, for a of b 라는 하나의 이터레이터로 순회가 가능함.

🪄 노출모듈 패턴(revealing module pattern)

즉시 실행 함수를 통해 private, public 등의 접근 제어자를 만드는 패턴

자바스크립트에서 노출모듈 패턴을 이용해 접근 제어자를 구현할 수 있음.

이 원리를 기반으로 만들어진 자바스크립트 모듈 방식을 CJS(CommonJS) 라고 함.

🌉 MVC 패턴(ModelViewController Pattern)

모델(Model), 뷰(View), 컨트롤로(Controller)로 이루어진 디자인 패턴

애플리케이션의 구성 요소를 세 가지로 분리하여, 각각의 구성 요소에만 집중하여 개발 가능함.

따라서, 재사용성과 확장성이 용이하다는 장점이 있지만, 애플리케이션이 복잡해질수록 모델과 뷰의 관계가 복잡해진다는 단점이 있음.

MVC 패턴을 따르는 가장 대표적인 라이브러리로 React 가 있음.

  • 모델
    • 애플리케이션의 데이터
    • ex) 데이터베이스, 상수, 변수
    • 사용자 인터페이스 요소
    • 쉽게 말해, 사용자가 볼 수 있는 화면
  • 컨트롤러
    • 모델과 뷰를 잇는 다리 역할
    • 이벤트 등의 메인 로직을 담당함.
    • 모델과 뷰의 생명주기를 관리, 모델이나 뷰의 변경 통지를 받으면 이를 각각의 구성 요소에 해당 내용을 알려줌.

🎥 MVP 패턴(ModelViewPresenter Pattern)

MVC 패턴으로부터 파생된 패턴. 컨트롤러가 프레젠터(Presenter)로 교체됨.

뷰와 프레젠터는 일대일 관계이므로 MVC 패턴보다 더 강한 결합을 지닌 디자인 패턴이라 할 수 있음.

🖥️ MVVM 패턴((ModelViewViewModel Pattern))

MVC 패턴으로부터 파생된 패턴. 컨트롤러가 뷰모델(View Model)로 교체됨.

뷰모델은 뷰를 더 추상화한 계층으로, MVC 패턴과 달리 커맨드와 데이터 바인딩을 가지는 것이 특징.

뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI 를 재사용하기 쉽고 단위 테스팅에 유리하다는 장점을 가짐.

대표적인 프레임워크로 Vue 가 있음.

프로그래밍 패러다임

프로그래밍 패러다임이란?

프로그래머에게 프로그래밍의 관점을 갖게 해주는 역할을 하는 개발 방법론

예를 들어, 객체지향 프로그래밍은 프로그래머들이 프로그램을 상호 작용하는 객체들의 집합으로 볼 수 있게 하는 반면, 함수형 프로그래밍은 상태 값을 지니지 않는 함수 값들의 연속으로 생각할 수 있게 해줌.

🗂️ 프로그래밍 패러다임의 분류

  • 선언형
    • 함수형
  • 명령형
    • 객체지향형
    • 절차지향형

📢 선언형과 함수형 프로그래밍(declarative programming & functional programming)

선언형 프로그래밍(declarative programming)은 ‘어떻게’(how, 절차형 프로그래밍) 보다 ‘무엇을’(what) 풀어내는가에 집중하는 패러다임으로, ‘프로그램은 함수로 이루어진 것이다.’ 라는 명제가 담긴 패러다임이다.

함수형 프로그래밍은 선언형 프로그래밍의 일종으로 ‘순수 함수’들을 블록처럼 쌓아 로직을 구현하고 ‘고차 함수’ 를 통해 재사용성을 높인 패러다임이다.

자바스크립트는 유연하고 단순한 언어이며 함수가 일급 객체이기 때문에 객체지향 보다는 함수형 프로그래밍 방식이 선호된다.

  • 순수 함수
    • 출력이 입력에만 의존하는 함수
  • 고차 함수
    • 함수가 함수를 값처럼 매개변수로 받아 로직을 생성할 수 있는 것
    • 고차 함수를 쓰기 위해서는 해당 언어가 일급 객체라는 특징을 가져야 한다.
  • 일급 객체
    • 일급 객체란, 다음 세 가지 조건을 만족하는 객체를 의미한다.
      • 변수나 메서드에 함수를 할당할 수 있다.
      • 함수 안에 함수를 매개변수로 담을 수 있다.
      • 함수가 함수를 반환할 수 있다.

💊 객체지향 프로그래밍(Object-Oriented Programming)

객체들의 집합으로 프로그램의 상호 작용을 표현하며 데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용하는 방식

설계에 많은 시간이 소요되며 처리 속도가 느리다는 단점이 있지만, 추상화, 캡술화, 상속성, 다형성이라는 특징을 활용해 재사용성이 높은 프로그램을 제작할 수 있다는 장점이 있음.

객체지향 프로그래밍을 설계할 때는 아래 5가지 원칙을 지켜야 함.

  • 객체지향 프로그래밍의 SOLID 원칙
    • S 단일 책임 원칙(SRP, Single Responsibility Principle)
      • 모든 클래스는 각각 하나의 책임만 가져야 하는 원칙
      • ex) A라는 로직이 존재할 때, 어떠한 클래스는 A에 관한 클래스여야 하고, 이를 수정한다고 했을 때도 A와 관련된 수정이어야 함.
    • O 개방-폐쇄 원칙(OCP, Open Closed Principle)
      • 유지 보수 사항이 생긴다면 코드를 쉽게 확장할 수 있도록 하고 수정할 때는 닫혀 있어야 하는 원칙
      • 기존의 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야 한다.
    • L 리스코프 치환 원칙(Liskov Substitution Principle)
      • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 하는 원칙
      • 부모 객체에 자식 객체를 넣어도 시스템이 정상 동작하도록 만드는 것을 의미함.
    • I 인터페이스 분리 원칙(Interface Segregation Principle)
      • 하나의 일반적인 인터페이스보다 구체적인 여러 개의 인터페이스를 만들어야 하는 원칙
    • D 의존 역전 원칙(Dependency Inversion Principle)
      • 자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 원칙
      • 상위 계층은 하위 계층의 변화에 대한 구현으로부터 독립해야 한다.

⏰ 절차형 프로그래밍(Procedural Programming)

로직이 수행되어야 할 연속적인 계산 과정으로 이루어짐.

시간적 흐름에 따라 수행되므로 코드의 가독성이 좋고 실행 속도가 빠르다는 장점이 있지만, 모듈화가 어렵고 유지 보수성이 떨어져 주로 계산이 많은 작업에 쓰인다.

대표적인 언어로, C 가 있다.

🎨 패러다임은 섞어서 사용될 수 있을까?

섞일 수 있다. 어떠한 패러다임이 가장 좋은지는 비즈니스 로직이나 서비스의 특징에 따라 달라진다. 따라서 여러 패러다임을 조합하여 상황과 맥락에 따라 패러다임 간의 장점만을 취해 개발하는 것이 좋다.

profile
매일 1%씩 성장하는 개발자 보리몽입니다 :D

1개의 댓글

comment-user-thumbnail
2023년 8월 10일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

답글 달기