본문 바로가기
제로베이스 BE 21기/면접을 위한 CS 전공지식 노트

[CS] Ch.1 디자인 패턴과 프로그래밍 패러다임 : 옵저버 패턴, 프록시 패턴, 이터레이터 패턴, 노출모듈, MVC 패턴, MVP 패턴, MVVM 패턴

by ♡˖GYURI˖♡ 2024. 5. 16.

1.1.4 옵저버 패턴

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

 

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

주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축하기도 한다.

옵저버패턴은 주로 이벤트 기반 시스템에 사용하며 MVC 패턴에도 사용된다.

 

 

자바에서의 옵저버 패턴

import java.util.ArrayList;
import java.util.List;

interface Subject {
    public void register(Observer obj);
    public void unregister(Observer obj);
    public void notifyObservers();
    public Object getUpdate(Observer obj);
}

interface Observer {
    public void update(); 
}

class Topic implements Subject {
    private List<Observer> observers;
    private String message; 

    public Topic() {
        this.observers = new ArrayList<>();
        this.message = "";
    }

    @Override
    public void register(Observer obj) {
        if (!observers.contains(obj)) observers.add(obj); 
    }

    @Override
    public void unregister(Observer obj) {
        observers.remove(obj); 
    }

    @Override
    public void notifyObservers() {   
        this.observers.forEach(Observer::update); 
    }

    @Override
    public Object getUpdate(Observer obj) {
        return this.message;
    } 
    
    public void postMessage(String msg) {
        System.out.println("Message sended to Topic: " + msg);
        this.message = msg; 
        notifyObservers();
    }
}

class TopicSubscriber implements Observer {
    private String name;
    private Subject topic;

    public TopicSubscriber(String name, Subject topic) {
        this.name = name;
        this.topic = topic;
    }

    @Override
    public void update() {
        String msg = (String) topic.getUpdate(this); 
        System.out.println(name + ":: got message >> " + msg); 
    } 
}

public class HelloWorld { 
    public static void main(String[] args) {
        Topic topic = new Topic(); 
        Observer a = new TopicSubscriber("a", topic);
        Observer b = new TopicSubscriber("b", topic);
        Observer c = new TopicSubscriber("c", topic);
        topic.register(a);
        topic.register(b);
        topic.register(c); 
   
        topic.postMessage("amumu is op champion!!"); 
    }
}
/*
Message sended to Topic: amumu is op champion!!
a:: got message >> amumu is op champion!!
b:: got message >> amumu is op champion!!
c:: got message >> amumu is op champion!!
*/

 

 

 

자바 : 상속과 구현

상속(extends)

  • 자식 클래스가 부모 클래스의 메서드등 을 상속받아 사용하며 자식 클래스에서 추가 및 확장할 수 있는 것
  • 장점 : 재사용성, 중복성의 최소화 

구현(implements)

  • 부모 인터페이스를 자식 클래스에서 재정의하여구현하는 것
  • 상속과 달리 반드시 부모 클래스의 메서드를 재정의하여 구현해야 함  

상속과 구현의차이

상속은 일반 클래스, abstract 클래스를 기반으로 구현하며, 구현은 인터페이스를 기반으로 구현한다.

 

 

자바스크립트에서의 옵저버 패턴 

자바스크립트에서의 옵저버 패턴은 프록시 객체를 통해 구현할 수 있다.

 

프록시 객체

어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체

 

자바스크립트에서 프록시 객체는 두 개의 매개변수를 가진다

  • target : 프록시할 대상
  •  handler : 프록시 객체의 target 동작을 가로채서 정의할 동작들이 정해져 있는 함수

 

프록시 객체 구현 코드

const handler = {
	get: function(target, name) {
    	return name === 'name' ? '${target.a} ${target.b}' : targetn[name]
    }
}

const p = new Proxy({a: 'KUNDOL', b: 'IS AUMUMU ZANGIN'}, handler)
console.log(p.name)	// KUNDOL IS AUMUMU ZANGIN

 

new Proxy로 선언한 객체의 a와 b라는 속성에 특정 문자열을 담아서 handler에 "name이라는 속성에 접근할 때는 a와 b라는 것을 합쳐서 문자열을 만들어라."를 구현했다. 이렇게 p라는 변수에 name이라는 속성을 선언하지 않았는데도 p.name으로 name 속성에 접근하려고할 때, 그 부분을 가로채 문자열을 만들어 반환하는 것을 볼 수 있다.

 

 

프록시 객체를 이용한 옵저버 패턴

function createReactiveObject(target, callback) { 
    const proxy = new Proxy(target, {
        set(obj, prop, value){
            if(value !== obj[prop]){
                const prev = obj[prop]
                obj[prop] = value 
                callback(`${prop}가 [${prev}] >> [${value}] 로 변경되었습니다`)
            }
            return true
        }
    })
    return proxy 
} 
const a = {
    "형규" : "솔로"
} 
const b = createReactiveObject(a, console.log)
b.형규 = "솔로"
b.형규 = "커플"
// 형규가 [솔로] >> [커플] 로 변경되었습니다

 

프록시객체의 get() 함수는 속성과 함수에 대한 접근을 가로채며, has() 함수는 in 연산자의 사용을 가로챈다. set() 함수는 속성에 대한 접근을 가로챈다. set() 함수를 통해속성에 대한 접근을 "가로채"서 형규라는 속성이 솔로에서 커플로 되는 것을 감시할 수 있다.

 

프론트엔드에서 많이 쓰는 프레임워크 Vue.js 3.0에서 ref나 reactive로 정의하면 해당 값이 변경되었을 때 자동으로 DOM에 있는 값이 변경되는데, 이는 앞서 설명한 프록시 객체를 이용한 옵저버 패턴을 이용하여 구현한 것이다.

 

DOM (Document Object Model)
문서 객체 모델을 말하며, 웹 브라우저상의 화면을 이루고 있는 요소들을 지칭한다.

 

 

1.1.5 프록시 패턴과 프록시 서버

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

 

이를 통해 객체의 속성, 변환 등을 보완하며 보안, 데이터 검증, 캐싱, 로깅에 사용한다. 이는 프록시 객체로 쓰이기도 하지만 프록시 서버로도 활용된다.

 

프록시 서버에서의 캐싱
캐시 안에 정보를 담아두고, 캐시 안에 있는 정보를 요구하는 요청에 대해 다시 저 멀리 있는 원격 서버에 요청하지 않고, 캐시 안에 있는 데이터를 활용하는 것을 말한다. 이를 통해 불필요하게 외부와 연결하지 않기 때문에 트래픽을 줄일 수 있다는 장점이 있다.

 

 

프록시 서버

서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램

nginx를 이용한 프록시 서버

위 그림처럼 nginx를 프록시 서버로 둬서 실제 포트를 숨길 수 있고 정적 자원을 gzip 압축하거나, 메인 서버 앞단에서의 로깅을 할 수도 있다. (+ 보안성 강화)

 

버퍼 오버플로우
버퍼는 보통 데이터가 저장되는 메모리 공간으로, 메모리 공간을 벗어나는 경우를 말한다. 이 때 사용되지 않아야 할 영역에 데이터가 덮어씌워져 주소, 값을 바꾸는 공격이 발생하기도 한다.

gzip 압축
LZ77과 Huffman 코딩의 조합인 DEFLATE 알고리즘을 기반으로 한 압축 기술이다. gzip 압축을 하면 데이터 전송량을 줄일 수 있지만, 압축을 해제했을 때 서버에서의 CPU 오버헤드도 생각해서 gzip 압축 사용 유무를 결정해야 한다.

 

 

 

CloudFlar는 전 세계적으로 분산된 서버가 있고, 이를 통해 어떠한 시스템의 콘텐츠 전달을 빠르게 할 수 있는 CDN 서비스이다. CDN 외에도 DDOS 공격 방어, HTTPS 구축 등의 이점이 있다. 이 모든 것은 웹 서버 앞단에 두어 '프록시 서버'로 쓰기 때문이다.

 

CDN (Content Delivery Netword)
각 사용자가 인터넷에 접속하는 곳과 가까운 곳에서 콘텐츠를 캐싱 또는 배포하는 서버 네트워크를 말한다. 이를 통해 사용자가 웹 서버로부터 콘텐츠를 다운로드하는 시간을 줄일 수 있다.

 

DDOS 공격 방어

DDOS는 짧은 기간 동안 네트워크에 많은 요청을 보내 네트워크를 마비시켜 웹 사이트의 가용성을 방해하는 공격 유형이다. CloudFlare는 의심스러운 트래픽, 특히 사용자가 접속하는 것이 아닌 시스템을 통해 오는 트래픽을 자동으로 차단해서 DDOS 공격으로부터 보호한다. CloudFlare의 거대한 네트워크 용량과 캐싱 전략으로 소규모 DDOS 공격은 쉽게 막아낼 수 있으며, 이러한 공격에 대한 방화벽 대시보드도 제공한다.

 

HTTPS 구축

서버에서 HTTPS를 구축할 때 인증서를 기반으로 구축할 수도 있다. 하지만 CloudFlare를 사용하면 별도의 인증서 설치 없이 좀 더 손쉽게 HTTPS를 구축할 수 있다.

 

 

CORS와 프론트엔드의 프록시 서버

CORS (Cross-Origin Resource Sharing)

서버가 웹 브라우저에서 리소스를 로드할 때 다른 오리진을 통해 로드하지 못하게 하는 HTTP 헤더 기반 메커니즘

 

프론트엔드 개발 시 프론트엔드에 서버를 만들어서 백엔드 서버와 통신할 때 주로 CORS 에러를 마주치는데, 이를 해결하기 위해 프론트엔드에서 프록시 서버를 만들기도 한다.

 

오리진
프로토콜 + 호스트 이름 + 포트

 

e.g. 프론트엔드에서는 127.0.0.1:3000으로 테스팅을 하는데, 백엔드 서버는 127.0.0.1:12010이라면?

→ 포트 번호가 서로 달라서 CORS 에러가 나타난다!

 

이 때 프록시 서버를 둬서 프론트엔드 서버에 요청되는 오리진을 127.0.0.1:12010으로 바꾸는 것이다.

 

위 그림처럼 프론트엔드 서버 앞단에 프록시 서버를 놓아 /api 요청은 users API, /api2 요청은 users API2에 요청할 수 있다. 자연스레 CORS 에러 해결은 물론이며 다양한 API 서버와의 통신도 매끄럽게 할 수 있는 것이다.

 

 

1.1.6 이터레이터 패턴

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

 

이를 통해 순회할 수 있는 여러 가지 자료형의 구조와는 상관없이 이터레이터라는 하나의 인터페이스로 순회가 가능하다.

 

 

자바스크립트에서의 이터레이터 패턴

const mp = new Map() 
mp.set('a', 1)
mp.set('b', 2)
mp.set('cccc', 3) 
const st = new Set() 
st.add(1)
st.add(2)
st.add(3) 
const a = []
for(let i = 0; i < 10; i++)a.push(i)

for(let aa of a) console.log(aa)
for(let a of mp) console.log(a)
for(let a of st) console.log(a) 
/* 
a, b, c 
[ 'a', 1 ]
[ 'b', 2 ]
[ 'c', 3 ]
1
2
3
*/

 

서로 다른 자료구조인 set과 map임에도 똑같은 for a of b 라는 이터레이터 프로토콜을 통해 순회할 수 있게 된다.

 

이터레이터 프로토콜
이터러블한 객체들을 순회할 때 쓰이는 규칙

이터러블한 객체
반복 가능한 객체로 배열을 일반화한 객체

 

 

1.1.7 노출모듈 패턴

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

 

자바스크립트는 private나 public 같은 접근 제어자가 존재하지 않고 전역 범위에서 스크립트가 실행된다. 그렇기 때문에 노출모듈 패턴을 통해 private와 public 접근 제어자를 구현하기도 한다.

 

const pukuba = (() => {
    const a = 1
    const b = () => 2
    const public = {
        c : 2, 
        d : () => 3
    }
    return public 
})() 
console.log(pukuba)
console.log(pukuba.a)
// { c: 2, d: [Function: d] }
// undefined

 

a와 b는 다른 모듈에서 사용할 수 없는 변수나 함수이며 private 범위를 가진다. c와 d는 다른 모듈에서 사용할 수 있는 변수나 함수이며 public 범위를 가진다. 위와 같은 노출모듈패턴을 기반으로 만든 자바스크립트 모듈 방식으로는 CJS(CommonJS) 모듈 방식이 있다.

 

즉시 실행 함수
함수를 정의하자마자 바로 호출하는 함수
초기화 코드, 라이브러리 내 전역 변수의 충돌 방지 등에 사용한다.

 

 

1.1.8 MVC 패턴

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

 

애플리케이션의 구성 요소를 세 가지 역할로 구분하여 개발 프로세스에서의 각각의 구성 요소에만 집중해서 개발할 수 있다. 재사용성과 확장성이 용이하다는 장점이 있고, 애플리케이션이 복잡해질수록 모델과 뷰의 관계가 복잡해지는 단점이 있다.

 

 

모델

  • 애플리케이션의 데이터인 데이터베이스, 상수, 변수 등
  • 뷰에서 데이터를 생성하거나 수정하면 컨트롤러를 통해 모델을 생성하거나 갱신

 

 

  • 사용자 인터페이스 요소
  • 모델을 기반으로 사용자가 볼 수 있는 화면
  • 모델이 가지고 있는 정보를 따로 저장하지 않아야 하며 단순히 화면에 표시하는 정보만 가지고 있어야 함
  • 변경이 일어나면 컨트롤러에 이를 전달해야 함

 

컨트롤러

  • 하나 이상의 모델과 하나 이상의 뷰를 잇는 다리 역할
  • 이벤트 등 메인 로직 담당
  • 모델과 뷰의 생명주기 관리
  • 모델이나 뷰의 변경 통지를 받으면 이를 해석하여 각각의 구성 요소에 해당 내용에 대해 알려줌

 

1.1.9 MVP 패턴

MVC로부터 파생되었으며, MVC에서 C에 해당하는 컨트롤러가 프레젠터(presenter)로 교체된 패턴

 

뷰와 프레젠터는 일대일 관계이기 때문에 MVC보다 더 강한 결합을 지닌 디자인 패턴이다.

 

 

1.1.10 MVVM 패턴

MVC의 C에 해당하는 컨트롤러가 뷰모델(view model)로 바뀐 패턴

 

  • 뷰모델 : 뷰를 더 추상화한 계층
  • MVC 패턴과는 다르게 커맨드와 데이터 바인딩을 가짐
  • 뷰와 뷰모델 사이의 양방향 데이터 바인딩 지원
  • UI를 별도의 코드 수정 없이 재사용 가능
  • 단위 테스팅하기 쉬움
커맨드
여러 가지 요소에 대한 처리를 하나의 액션으로 처리할 수 있게 하는 기법


데이터 바인딩
화면에 보이는 데이터와 웹 브라우저의 메모리 데이터를 일치시키는 기법으로, 뷰모델을 변경하면 뷰가 변경된다.

 

 

 

 


참고

 

[디자인패턴] 옵저버 패턴 (Observer Pattern) 아주 간단하게 정리해보기

옵저버 패턴이란? 옵저버란 스타크래프트 프로토스의 유닛으로 적들을 관찰하기 위해 탄생한 유닛이다. 테란전에서 필수 유닛이며 옵저버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관

pjh3749.tistory.com

 

[디자인패턴] 프록시패턴(Proxy Pattern)

프록시(Proxy)를 번역하면 대리자, 대변인의 의미를 갖고 있다. 대리자, 대변인은 누군가를 대신해서 그 역할을 수행하는 존재이다. 이는 프로그램에도 똑같이 적용된다. 즉, 프록시에게 어떤 일

velog.io

 

 

💠 프록시(Proxy) 패턴 - 완벽 마스터하기

Proxy Pattern 프록시 패턴(Proxy Pattern)은 대상 원본 객체를 대리하여 대신 처리하게 함으로써 로직의 흐름을 제어하는 행동 패턴이다. 프록시(Proxy)의 사전적인 의미는 '대리인'이라는 뜻이다. 즉, 누

inpa.tistory.com

 

 

💠 반복자(Iterator) 패턴 - 완벽 마스터하기

Iterator Pattern 반복자(Iterator) 패턴은 일련의 데이터 집합에 대하여 순차적인 접근(순회)을 지원하는 패턴이다. 데이터 집합이란 객체들을 그룹으로 묶어 자료의 구조를 취하는 컬렉션을 말한다.

inpa.tistory.com

 

 

[Design Pattern] 노출모듈 패턴(revealing module pattern)

노출모듈 패턴

velog.io

 

 

 

[디자인패턴] MVC, MVP, MVVM 비교

웹 개발자로 일을 하면서 가장 먼저 접한 디자인패턴이 바로 MVC 패턴이었습니다. 그만큼 유명하고 많이 쓰이는 디자인패턴인 MVC 패턴과 MVC 패턴에서 파생되어져 나온 MVP 패턴과 MVVM 패턴을 이야

beomy.tistory.com

 

 

여기도 MVC, 저기도 MVC! MVC 패턴이 뭐야?

어딜가든 MVC에 대해서 많이 듣고 접하게 되는데 과연 MVC 패턴은 무엇이고 왜 등장했는지, 더 나아가 MVC의 필요성과 한계점은 무엇인지 학습하고 고민한 내용을 기록하였습니다.

velog.io