옵저버 패턴에 대해 알아보자
다 되면 알려주세요 옵저버 패턴 by ZeroCho, 근데 이제 Tanstack Query를 살짝 곁들인
2024-10-23 · 5분 · 조회 1@ZeroChoTV님의 다 되면 알려주세요 옵저버 패턴 를 보고 정리한 내용입니다.
옵저버 패턴이란?
옵저버 패턴은 객체의 상태가 변화할 때 그 상태를 구독하고 있는 다른 객체들에게 알림을 보내는 패턴이다.
class A {
check(b) {
if (b.finished()) {
return b.getItem();
}
return false;
}
}
class B {
item = null;
finished() {
return !!this.item;
}
getItem() {
return this.item;
}
}
const b = new B();
const a = new A();
while (!a.check(b)) {
// A는 계속해서 B가 만들어졌는지 확인해야함
console.log('NOT YET');
}위와 같이 객체 A가 객체 B의 상태를 계속해서 확인해야 하는 경우가 있다.
옵저버 패턴을 사용하면 '다 되면 알려주세요' 라는 요청을 할 수 있게 한다.
옵저버 패턴 적용하기
class A {
constructor(id) {
this.id = id;
}
itemDone(item) {
console.log(item);
}
}
class B {
item = null;
// 고객 리스트
customers = [];
// 고객 등록 받기 -> subscribe
getOrderFrom(a) {
this.customers.push(a);
}
// 고객 취소 받기 -> unsubscribe
cancelOrderFrom(a) {
this.customers = this.customers.filter((v) => v !== a);
}
finish(item) {
this.customers.forEach((c) => c.itemDone(item));
}
getItem() {
return this.item;
}
}
const b = new B();
const a = new A();
b.getOrderFrom(a);
// ...
// B가 일을 끝냈을 때
b.finish();- while문으로 계속해서 확인하지 않고, B가 일을 끝냈을 때 A에게 알려주는 방식으로 변경할 수 있다.
결론
옵저버 패턴은
- 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용하며 발행/구독 모델로 알려져있다.
- 기본적으로 다음과 같은 주요 요소를 가진다.
- 발행자(Subject): 상태를 유지하고 변화할 때 옵저버(구독자)들에게 알림을 보내는 객체.
- 구독자(Observer): 주제의 상태를 구독하고, 상태가 변화할 때 알림을 받는 객체.
- 알림(Notification): 주제의 상태가 변화하면 옵저버들에게 알림을 보내는 메커니즘.
추가: Tanstack Query
FrontEnd 개발에서 자주 사용하는 Tanstack Query도 옵저버 패턴을 사용하고 있다.
Tanstack Query에서는 QueryObserver가 옵저버 패턴의 발행자(Subject) 로서 동작한다.
상태 변화 시 QueryObserver는 등록된 옵저버들에게 알림을 보내는 역할을 한다.
useBaseQuery
Tanstack Query의 Data Fetching Hook이다.
// src/react/query/core/useBaseQuery.ts
export function useBaseQuery<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
// ...
const query = useQueryInstance({
queryKey,
queryFn,
config,
notifyManager,
queryHash,
defaultOptions,
state,
defaultQueryObserverOptions,
})
// ...
return query
}useBaseQuery에서useQueryInstance함수를 호출하는데, 이 함수는 데이터 상태(로딩, 성공, 실패 등)를 관리하는QueryObserver인스턴스를 생성한다.
QueryObserver
subscribe, unsubscribe, notify 메서드를 가지고 있는 Observer 역할이다.
// src/react/query/core/QueryObserver.ts
export class QueryObserver<TResult, TError> {
// ...
constructor({
queryKey,
queryHash,
observerProps,
defaultOptions,
notifyManager,
state,
defaultQueryObserverOptions,
}: QueryObserverOptions<TResult, TError>) {
// ...
this.listeners = new Set()
this.listeners.add(this)
}
// ...
subscribe(listener: QueryObserverListener<TResult, TError>) {
this.listeners.add(listener)
}
unsubscribe(listener: QueryObserverListener<TResult, TError>) {
this.listeners.delete(listener)
}
// ...
notify() {
this.listeners.forEach(listener => listener.onQueryUpdate(this.state))
}
}Notify 흐름
- QueryObserver는 서버에서 데이터를 가져오는 동안 상태를 loading으로 설정하고, 구독자들에게 이 상태를 알림(notify).
- 서버에서 응답이 도착하면 상태가 success 또는 error로 변경되고, 다시 구독자들에게 알림(notify).
- 구독자(컴포넌트 또는 훅)는 이 상태 변화에 따라 UI를 업데이트하거나 다른 로직을 실행.
function MyComponent() {
const { data, error, isLoading } = useQuery('myKey', fetchMyData);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error occurred!</p>;
return <div>{data}</div>;
}