반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 자바
  • 팩터리 메서드 패턴
  • item9
  • Java
  • next js app
  • 싱글톤 패턴
  • NEXT JS
  • 메이븐 골
  • 도커공식문서
  • 자바스크립트 면접
  • item8
  • 작업기억공간
  • MSSQL
  • 느린 쿼리
  • 참조 해제
  • 자바 디자인패턴
  • 토비의 스프링
  • 알고리즘
  • pnpm
  • 객체복사
  • Pre-rendering
  • 프로그래머의 뇌
  • 이펙티브 자바 item9
  • 추상 팩터리 패턴
  • 이펙티브자바
  • 자바 검증
  • 싱글톤
  • serverless computing
  • 외래키 제약조건
  • 빈 검증
  • 자료구조
  • 스프링 검증
  • 슬로우 쿼리
  • 싱글턴
  • try-with-resources
  • 러스트
  • prerendering
  • rust
  • 서버리스 컴퓨팅
  • item7
  • 메이븐 라이프사이클
  • Javadoc 자바독 자바주석 주석 Comment
  • 플라이웨이트패턴
  • 자바스크립트 인터뷰
  • 자바스크립트
  • 이펙티브 자바
  • 디자인패턴
  • 메이븐 페이즈
  • Next.js
  • bean Validation

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트의 Proxy 란?
자바스크립트/개념

자바스크립트의 Proxy 란?

2022. 6. 30. 11:27

프록시(Proxy)란?

Proxy 오브젝트는 다른 오브젝트를 감싸 연산을 가로챈다. 이를테면 프로퍼티를 읽고 쓰거나 다른 자체적인 오브젝트 조작 연산등을 가로챈다. 혹은 아무런 핸들러도 등록하지 않아 오브젝트가 프로퍼티를 다루는 것을 투명하게 허용할 수도 있다.

일반적으로는 중간에 get 과 같은 연산을 가로채 프로퍼티 접근 로그를 남기거나 set 으로 값이 입력 되기 전 값 검증하기, 값 포맷팅하기, 입력된 값을 깔끔하게 만드는 등의 작업을 추가하여 해당 연산이 일어날 때마다 개발자가 입력한 추가 작업이 수행되도록 하는 방식으로 많이 사용한다.

많은 라이브러리나 브라우저 프레임워크에서 사용된다.

문법

let proxy = new Proxy(target, handler);
  • target: 감쌀 오브젝트이다. 함수를 포함해 어떤 것이든 될 수 있다.
  • handler: 소위 "트랩" 이라 불리는 메서드가 담긴 오브젝트이다. "트랩" 이란 연산을 가로채는 메서드이다.
    • ex) get 트랩은 target 의 프로퍼티를 읽는 연산을 가로챈다.
    • ex) set 트랩은 target 에 프로퍼티가 쓰여지는 것을 가로챈다.

위에서 설명한 "트랩" 이란 단어에 대해 MDN 에서 사용하는 공식적인 명칭은 Hanlder function 이다. Handler functions are sometimes called traps, presumably because they trap calls to the target object.

어떤 메서드를 가로챌 수 있는가?

MDN 문서 의 왼쪽에 Proxy/handler 하위 Methods 라고 적혀있는 부분에 있는 모든 메서드를 활용해 가로챌 수 있다.

 

 

Internal Method Handler Method Triggers when...
[[Get]] get reading a property
[[Set]] set writing to a property
[[HasProperty]] has in operator
[[Delete]] deleteProperty delete operator
[[Call]] apply function call
[[Construct]] construct new operator
[[GetPrototypeOf]] getPrototypeOf Object.getPrototypeOf
[[SetPrototypeOf]] setPrototypeOf Object.setPrototypeOf
[[IsExtensible]] isExtensible Object.isExtensible
[[PreventExtensions]] preventExtensions Object.preventExtensions
[[DefineOwnProperty]] defineProperty Object.defineProperty, Object.defineProperties
[[GetOwnProperty]] getOwnPropertyDescriptor Object.getOwnPropertyDescriptor, for..in, Object.keys/values/entries
[[OwnPropertyKeys]] ownKeys Object.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in, Object.keys/values/entries

주의점

가로채고 난 이후의 주의점도 있는데,

  • [[SET]] 메서드는 값이 올바르게 쓰여졌다면, true 를 반환해야 한다.
  • [[Delete]] 메서드는 올바르게 값이 지워졌다면 true 를 반환해야 한다.
  • [[GetPropertyOf]] 는 항상 타겟 오브젝트의 프로토타입을 반환해야 한다는 것이다.

트랩으로 연산을 가로채어 개발자가 원하는 연산을 추가적으로 넣는 것은 문제가 되지 않지만, 반드시 MDN 문서 를 보고 주의사항을 지켜주어야 한다.

 

예제

프록시에서 일어나는 연산 살펴보기

let target = {};
let proxy = new Proxy(target, {}); // empty handler

proxy.test = 5; // writing to proxy (1)

alert(target.test); // 5, the property appeared in target!
alert(proxy.test); // 5, we can read it from proxy too (2)

for (let key in proxy) alert(key); // test, iteration works (3)
  • 위는 아무런 역할도 하지 않는 빈 핸들러가 추가된 프록시이다.
    • 빈 핸들러가 추가되어, 그냥 평범한 오브젝트와 똑같은 상태이다.

그러나 위에서 일어나는 연산들에 대해서는 한 번 볼만하다.

  • proxy.test = 5 라고 할 때 쓰기(set) 연산이 일어난다.
  • proxy.test 를 가져올 때 읽기(get) 연산이 일어난다.
  • for 문 안에서 반복(iteration) 연산이 일어난다.

get 으로 기본 값 적용해보기

let target = {};
let proxy = new Proxy(target, {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    }

    return 5555;
  },
});

proxy.a = 100;

console.log(proxy.a); // 100
console.log(proxy.b); // 5555

위와 같이 get 연산이 일어났을 때 특정한 기본 값(5555)을 설정해 적용할 수 있다.

set 으로 값 검증하기

let numbers = [];

numbers = new Proxy(numbers, {
  set(target, prop, val) {
    if (typeof val === "number") {
      target[prop] = val;
      return true;
    }

    throw "값은 숫자만 입력 가능합니다.";
    return false;
  },
});

numbers.push(100);
numbers.push("안녕"); // Uncaught 값은 숫자만 입력 가능합니다.

console.log(numbers); // Proxy {0: 100}

numbers 라는 배열에 숫자 이외의 타입이 들어올 수 없도록 하게 하는 예제이다.

프록시 set 메서드의 함정

위의 코드가 잘 작동했으니, 아래의 코드의 출력 결과를 예측해보자.

let numbers = [];

numbers = new Proxy(numbers, {
  set(target, prop, val) {
    if (typeof val === "number") {
      target[prop] = val;
      console.log("값이 입력되었습니다.");
      return true;
    }

    throw "값은 숫자만 입력 가능합니다.";
    return false;
  },
});

numbers.push(1);
  • "값이 입력되었습니다." 가 두번 출력된다.
    • 배열의 protype 메서드인 push() 에는 length 를 수정하는 내용도 포함되어 있기 때문이다.

오브젝트에 값을 직접 할당하는 것이 아닌 내부 프로토타입 메서드를 이용할 때는 어떤 연산들을 거쳐서 해당 prototype 메서드가 수행되는 것인지 제대로 알아야 내가 원하지 않은 동작이 일어나는 것을 예방할 수 있다.

set 연산을 이용할 때는 성공 시 ture 를 반환하는 것도 잊지 말자.

ownKeys 로 접근 제어하기

let user = {
  name: "John",
  age: 30,
  _password: "***",
};

user = new Proxy(user, {
  ownKeys(target) {
    return Object.keys(target).filter((key) => !key.startsWith("_"));
  },
});

// "ownKeys" filters out _password
for (let key in user) alert(key); // name, then: age

// same effect on these methods:
alert(Object.keys(user)); // name,age
alert(Object.values(user)); // John,30
  • Object.keys() 혹은 Object.values() 로 자바스크립트 오브젝트의 키나 밸류를 추출할 때, 자동으로 ownKeys 연산이 수행된다.
  • 위는 _ 로 시작하는 key 인 경우에 ownKeys 에서 false 를 반환하여 iteration 이 일어나지 않게 하는 예제이다.
    • _password 라는 필드명을 가진 패스워드는 iteration 에 걸리지 않는다.

Object.keys(), Object.values(), Object.getOwnPropertyNames(), for ... in 루프는 [[OwnPropertyKeys]] 내부 메서드를 사용한다.

range 오브젝트 만들기

let range = {
  start: 1,
  end: 10,
};

range = new Proxy(range, {
  has(target, prop) {
    return prop >= target.start && prop <= target.end;
  },
});

alert(5 in range); // true
alert(50 in range); // false

실제로 오브젝트에 해당 숫자가 없더라도, 범위 내의 숫자라면 true 가 반환된다.

apply 를 통해 사용자가 정의한 딜레이를 가진 프록시 만들기

function delay(f, ms) {
  return new Proxy(f, {
    apply(target, thisArg, args) {
      setTimeout(() => target.apply(thisArg, args), ms);
    },
  });
}

function sayHi(user) {
  alert(`Hello, ${user}!`);
}

sayHi = delay(sayHi, 3000);

alert(sayHi.length); // 1 (*) proxy forwards "get length" operation to the target
sayHi("John"); // Hello, John! (after 3 seconds)

사용자가 정의한 함수인 sayHi 에 대해 3초의 딜레이를 가지는 새로운 함수를 만들었다.

레퍼런스

  • https://javascript.info/proxy
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
반응형
저작자표시 비영리

'자바스크립트 > 개념' 카테고리의 다른 글

자바스크립트 클로저 (Closure) 란 무엇인가?  (0) 2022.07.13
자바스크립트 var , let , const 의 스코프 차이에 대해 알아보자.  (0) 2022.07.13
자바스크립트 fetch API 알아보기 (feat. ajax)  (0) 2022.06.26
자바스크립트 Promise 알아보기  (0) 2022.06.25
자바스크립트 XMLHttpRequest 알아보기  (0) 2022.06.25
    '자바스크립트/개념' 카테고리의 다른 글
    • 자바스크립트 클로저 (Closure) 란 무엇인가?
    • 자바스크립트 var , let , const 의 스코프 차이에 대해 알아보자.
    • 자바스크립트 fetch API 알아보기 (feat. ajax)
    • 자바스크립트 Promise 알아보기
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바