1. 옵저버 패턴(observer pattern)
옵저버 패턴은 주체가 어떤 객체(subject)의 상태 변화를 관찰하다가 상태 변화가 있을 때 마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴이다.
1-1. 객체와 주체가 분리되어 있는 옵저버 패턴
여기서 주체란 객체의 상태 변화를 보고 있는 관찰자이며, 옵저버들이란 이 객체의 상태 변화에 따라 전달되는 메서드 등을 추가 변화 사항이 생기는 객체들을 의미한다.
1-2. 객체와 주체가 합쳐진 옵저버 패턴
또한, 앞의 그림처럼 주체와 객체를 따로 두지 않고 상태가 변경되는 객체를 기반으로 구축하기도 한다.
1-3. 옵저버 패턴 구조
또한, 옵저버 패턴은 주로 이벤트 기반 시스템에 사용하여 MVC(Model-View-Controller) 패턴에도 사용된다.
예를 들어 주체라고 볼 수 있는 모델(model)에서 변경 사항이 생겨 update() 메서드로 옵저버 인 뷰에 알려주고 이를 기반으로 컨트롤러(controller) 등이 작용하는 것이다.
2. 자바스크립트에서의 옵저버 패턴
자바스크립트에서 옵저버 패턴은 프록시 객체를 통해 구현할 수 있다.
2-1. 프록시 객체
프록시(proxy) 객체는 어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체를 뜻하며, 자바스크립트에서 프록시 객체는 두 개의 매개 변수를 가진다.
- target : 프록시할 대상
- handler : target 동작을 가로채고 어떠한 동작을 할 것인지가 설정되어 있는 함수
다음은 프록시 객체를 구현한 코드이다.
const handler = {
get: function(target, name) {
return name === 'name' ? `${target.a} ${target.b}` : target[name]
}
}
const p = new Proxy({a: 'faker', b: 'is god'}, handler)
console.log(p.name) // faker is god
new proxy()로 a와 b 속성을 가지고 있는 객체와 handler 함수를 매개변수로 넣고 p라는 변수를 선언했다. 이후 p의 name 속성을 참조하니 a와 b라는 속성밖에 없는 객체가 handler의 "name이라는 속성에 접근할 때 a와 b를 합쳐서 문자열을 만들라'는 로직에 따라 어떤 문자열을 만든다. 이렇게 name 속성 등 특정 속성에 접근할 때 그 부분을 가로채서 어떠한 로직을 강제로 할 수 있는 것이 프록시 객체이다.
2-2. 프록시 객체를 이용한 옵저버 패턴
function createReactiveObject(target, callback) {
const proxy = new Proxy(target, {
set(obj, prop, value) {
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() 함수를 통해 속성에 대한 접근을 가로채서 계절 이라는 속성이 봄에서 커플로 바뀌는 것을 감시할 수 있습니다.
3. 프록시 패턴과 프록시 서버
위에 설명한 프록시 객체는 사실 디자인 패턴 중 하나인 프록시 패턴이 녹아들어 있는 객체이다.
3-1. 프록시 패턴(proxy pattern)
프록시 패턴은 대상 객체(subject)에 접근하기 전 그 접근에 대한 흐름을 가로채 해당 접근을 필터링하거나 수정하는 등의 역할을 하는 계층이 있는 디자인 패턴이다.
이를 통해 객체의 속성, 변환 등을 보완하며 보안, 데이터 검증, 캐싱, 로깅에 사용한다. 이는 앞서 설명한 프록시 객체로 쓰이기도 하지만 프록시 서버로도 활용된다.
프록시 서버에서의 캐싱
캐시 안에 정보를 담아두고, 캐시 안에 있는 정보를 요구하는 요청에 대해 다시 저 멀리 있는 원격 서버에 요청하지 않고 캐시 안에 있는 데이터를 활용하는 것을 말한다. 이를 통해 불필요하게 외부와 연결하지 않기 때문에 트래픽을 줄일 수 있다는 장점이 있다.
3-2. 프록시 서버(proxy server)
프록시 서버는 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다.
3-3. CORS 에러와 프론트엔드의 프록시서버
CORS(Cross-Origin Resource Sharing)는 서버가 웹 브라우저에서 리소스를 로드할 때 다른 오리진을 통해 로드하지 못하게 하는 HTTP 헤더 기반 메커니즘이다.
프론트엔드 개발 시 프론트엔드 서버를 만들어서 백엔드 서버와 통신할 때 주로 CORS 에러를 마주치는데, 이를 해결하기 위해 프론트엔드에서 프록시 서버를 만들기도 한다.
오리진
프로토콜과 호스트 이름, 포트의 조합을 말한다. 예를 들어 https://imleeeokim:12010/test 라는 주소에서 오리진은https://imleeeokim:12010를 뜻한다.
예를 들어 프론트엔드에서 127.0.0.1:3000 으로 테스팅을 하는데 백엔드 서버는 127.0.0.1:12010이라면 포트 번호가 다르기 때문에 CORS 에러가 나타난다. 이때 프록시 서버를 둬서 프론트엔드 서버에서 요청되는 오리진을 127.0.0.1:12010으로 바꾼다.
참고로 127.0.0.1 이란 루프백(loopback) IP로, 본인 PC서버의 IP를 뜻한다. localhost나 127.0.0.1을 주소창에 입력하면 DNS를 거치지 않고 바로 본인 PC로 연결된다.
위 그림처럼 프론트엔드 서버 앞단에 프록시 서버를 놓아 /api 요청은 users API, /api2 요청은 users API2에 요청할 수 있다. 자연스레 CORS 에러 해결은 물론이며 다양한 API 서버와의 통신도 매끄럽게 할 수 있다.
4. 이터레이터 패턴(iterator pattern)
이터레이터 패턴은 이터레이터(iterator)를 사용하여 컬렉션(collection)의 요소들에 접근하는 디자인 패턴이다. 이를 통해 순회할 수 있는 여러가지 자료형의 구조와는 상관없이 이터레이터라는 하나의 인터페이스로 순회가 가능하다.
위 그림은 이터레이터라는 똑같은 배로, 동그라미로 이루어진 컬렉션이든 마름모로 이루어진 컬렉션이든 순회할 수 있는 것을 보여준다.
4-1. 자바스크립트에서의 이터레이터 패턴
const mp = new Map()
mp.set('a', 1)
mp.set('b', 1)
mp.set('c', 1)
const st = new Set()
st.add(1)
st.add(2)
st.add(3)
for (let a of mp) console.log(a)
for (let a of st) console.log(a)
/*
[ 'a', 1 ]
[ 'b', 1 ]
[ 'c', 1 ]
1
2
3
*/
분명 다른 자료 구조인 set과 map임에도 똑같은 for a of b 라는 이터레이터 프로토콜을 통해 순회하는 것을 볼 수 있다.
이터레이터 프로토콜
이터러블한 객체들을 순회할 때 쓰이는 규칙
이터러블한 객체
반복 가능한 객체로 배열을 일반화한 객체
'CS' 카테고리의 다른 글
프로그래밍 패러다임 / 선언형과 함수형 프로그래밍 (0) | 2024.07.21 |
---|---|
노출모듈 패턴 / MVC 패턴 / MVP 패턴 / MVVM 패턴 (0) | 2024.07.14 |
싱글톤 패턴 / 팩토리 패턴 / 전략 패턴 (0) | 2024.06.29 |
디자인 패턴 - 프록시 패턴 / Nginx / CloudFlare / CORS (0) | 2024.04.29 |
디자인 패턴 - 팩토리 패턴 / 전략 패턴 / 옵저버 패턴 (0) | 2024.03.22 |