티스토리 뷰
안녕하세요. 오늘은 JavaScript의 Proxy 객체를 활용하여 벨리데이션 체크를 하는 방법에 대해 소개해드리려고 합니다. 조금 지난 얘기이지만 지난 3월 보일러플레이트의 의 Analytics(GA, 카카오 픽셀 등 분석도구 모음) class로 리팩토링한 후, 테스트를 하지 않아서 문제가 발생했습니다. 그래서 이슈 픽스 할 겸, 리팩토링 과정을 거쳤는데 그 과정에 Proxy를 사용한 경험을 소개하고자 합니다.
Proxy란 무엇일까요??
Proxy는 JavaScript에서 객체에 대한 동작을 가로채거나 수정할 수 있는 강력한 도구입니다. 객체의 속성에 접근하거나 값을 설정할 때, 이를 중간에서 처리할 수 있습니다.
Proxy 기본 사용법
1. Proxy 객체 생성
먼저, Proxy 객체를 생성해보겠습니다.
const targetObject = {
name: 'John',
age: 30,
};
const handler = {
get: (target, propKey) => {
console.log(`Getting property "${propKey}"`);
return target[propKey];
},
set: (target, propKey, value) => {
console.log(`Setting property "${propKey}" to ${value}`);
target[propKey] = value;
},
};
const proxy = new Proxy(targetObject, handler);
- targetObject는 원본 객체입니다.
- handler는 Proxy가 객체의 속성에 접근하거나 수정할 때 호출되는 함수들을 정의합니다.
2. Proxy를 이용한 객체 접근
console.log(proxy.name); // Getting property "name" / "John"
proxy.age = 31; // Setting property "age" to 31
주요 메서드
- get(target, propKey, receiver): 속성에 접근할 때 호출됩니다.
- target: Proxy의 대상이 되는 객체
- propKey: 접근하려는 속성의 키
- receiver: getter가 호출될 때 this. 상속받은 객체일 경우에는 상속한 객체가 this가 됩니다.
- set(target, propKey, value, receiver): 속성에 값을 할당할 때 호출됩니다.
- target: Proxy의 대상이 되는 객체
- propKey: 할당하려는 속성의 키
- value: 할당하려는 값
- receiver: Proxy 객체 자체 또는 상속 체인상의 다음 객체
활용 예시
1. get 활용 예제
get 메서드는 객체의 속성 값을 읽을 때 호출됩니다. 이 예제에서는 속성에 접근할 때마다 메시지를 출력합니다.
const target = {};
const proxy = new Proxy(target, {
get(target, prop) {
return `${prop}을 조회했더니 ${target[prop]}가 나왔당~!`;
}
});
proxy.test = 5;
console.log(proxy.test); // test을 조회했더니 5가 나왔당~!
2. set 활용 예제
set 메서드는 속성 값이 설정될 때 호출됩니다. 여기서는 문자열 길이를 검사하여 유효성 검사를 수행합니다.
const target = {};
const proxy = new Proxy(target, {
set(target, prop, value) {
if (typeof value !== 'string') {
console.log('ERROR: 문자열을 입력해주세요');
return false;
}
if (value.length < 4) {
console.log('ERROR: 4자 이상의 문자열을 입력해주세요');
return false;
}
console.log(`${prop}에 '${value}' 저장`);
target[prop] = value;
return true;
}
});
proxy.test = 5; // ERROR: 문자열을 입력해주세요
proxy.test = 'abc'; // ERROR: 4자 이상의 문자열을 입력해주세요
proxy.test = '안녕하세요'; // test에 '안녕하세요' 저장
실제 적용 사례
이제 실제 적용했던 사례를 소개하겠습니다.
각 분석도구 클래스가 있었고, 분석 도구 메서드를 설정할 때 window 객체를 직접 참조하는 부분이 있었습니다. 하지만 클라이언트 사이드가 아닌 환경에서는 window 객체가 정의되지 않아서 문제가 발생했습니다. 이 문제를 해결하기 위해 매 메서드마다 window가 유효한지 확인해야 했는데요, 이를 Proxy를 사용해 간편하게 해결했습니다.
예시를 같이 볼까요? analyticsValidCheckHandler는 각 메서드가 호출되기 전에 window 객체와 해당 메서드의 유효성을 체크합니다. window 객체가 없거나 메서드가 올바르게 초기화되지 않은 경우 경고를 출력합니다.
type GaHandler = {
get: (
target: { [key: string]: (...args: any[]) => any },
propKey: string,
) => (...args: any[]) => any;
};
const ANALYTICS_NAME = {
gtag: 'Google Analytics',
fbq: 'Meta(Facebook)',
kakao: 'Kakao',
};
export const analyticsValidCheckHandler: GaHandler = {
get: (target, propKey) => {
const origMethod = target[propKey];
return (...args) => {
let isValid = false;
if (typeof window !== 'undefined') {
switch (propKey) {
case 'gtag':
isValid = typeof window.gtag === 'function';
break;
case 'fbq':
isValid = typeof window.fbq === 'function';
break;
case 'kakao':
isValid = typeof kakaoPixel === 'function';
break;
default:
break;
}
}
if (!isValid) {
return console.warn(
`${ANALYTICS_NAME[propKey as keyof typeof ANALYTICS_NAME]} Analytics is not initialized.`,
);
}
return origMethod.apply(target, args);
};
},
};
그 다음, GoogleAnalytics 클래스를 살펴보겠습니다. 이 클래스에서는 Proxy를 사용하여 gtag 메서드 호출 전 검증을 수행합니다. window 객체가 정의되지 않은 환경에서는 빈 함수로 대체하여 문제가 발생하지 않도록 했습니다.
import { Analytics } from './types';
import { analyticsValidCheckHandler } from './utils/valid-check-with-proxy';
export class GoogleAnalytics {
private ga: Gtag.Gtag = () => {};
constructor(private key?: string) {
if (!key) {
console.warn('GA 키 설정이 필요합니다.');
return;
}
this.key = key;
// Proxy를 사용하여 gtag메서드 호출 전 검증 수행
this.ga = new Proxy(
{ gtag: typeof window !== 'undefined' ? window.gtag : () => {} },
analyticsValidCheckHandler,
).gtag;
}
...
}
마무리
Proxy를 제대로 사용해 보기 전에는 그저 객체의 동작을 가로챈다는 의미로만 알고 있었는데요. 실제로 사용해보니, 예상보다 훨씬 유용하더라고요. 만약 Proxy를 사용하지 않았다면, 각 메서드에서 window의 정의 여부를 일일이 체크해야 했을 겁니다. 물론 이슈가 발생한 점은 반성해야겠지만 이번 일을 계기로 Proxy를 사용함으로서 오히려 코드가 훨씬 간결해졌고, 유지보수도 용이해졌습니다. 이런 경험을 통해 Proxy의 강력한 기능을 직접 체감할 수 있었던 좋은 기회였다고 생각합니다.
'Frontend > JavaScript' 카테고리의 다른 글
[JavaScript] (부록) 메모리 누수 디버깅 가이드 (feconf 2023 토스플레이스 세션을 듣고) - 클로저(7) (0) | 2023.10.27 |
---|---|
[JavaScript] 클로저 사용 시 주의할 점 - 클로저(6) (0) | 2023.10.26 |
[JavaScript] 리액트에서의 클로저 - 클로저(5) (0) | 2023.10.26 |
[JavaScript] 클로저와 메모이제이션 - 클로저(4) (0) | 2023.10.22 |
[JavaScript] 클로저의 이해 - 클로저(3) (0) | 2023.10.20 |
- Total
- Today
- Yesterday
- 모두를 위한 컴퓨터 과학
- 자바스크립트 비동기 처리
- React
- css
- 항해99
- 자바스크립트
- 알고리즘자바스크립트
- 프로그래머스 자바스크립트
- cs50
- 실전프로젝트
- React Query
- 프로그래머스 베스트앨범 자바스크립트
- 백준
- 리액트네이티브
- html
- network
- 네트워크
- 클로저
- javascript
- github
- GIT
- 타입스크립트
- python
- 자바스크립트 클로저
- 무한스크롤
- reactquery
- 프로그래머스
- 모두를위한컴퓨터과학
- 자바스크립트알고리즘
- 리액트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |