passport 모듈로 유저 인증을 구현하던 중 이상한 점을 발견했다
상황
passport.serializeUser((user, done) => {
done(null, user.id); //로그인 전략(Strategy)에서 전달받은 user객체의 id값을 세션에 저장
});
로그인 성공시 세션에 유저의 고유한 id를 저장하는 passport 함수인데 user객체에 id를 직접적으로 접근한다
그런데 나는 시퀄라이즈를 사용하고 있었고 User.fineOne() 메서드를 통해 반환 받은 유저 정보를 콘솔에 찍어보니 id를 직접적으로 접근할 수 없는 구조였다
아래는 시퀄라이즈를 통해 반환받은 유저 데이터이다
User {
dataValues: {
id: 2,
email: 'zxc@zxc.com',
nick: '이거 되냐',
password: '$2b$12$wlDfDR1VpdQJ4Zttt4aWSOxw54hfHThO3L9S3wHSEdZFD0Auy4/b.',
provider: 'local',
snsId: null,
createdAt: 2024-04-13T14:03:21.000Z,
updatedAt: 2024-04-13T14:03:21.000Z,
deletedAt: null
},
_previousDataValues: {
id: 2,
email: 'zxc@zxc.com',
nick: '이거 되냐',
password: '$2b$12$wlDfDR1VpdQJ4Zttt4aWSOxw54hfHThO3L9S3wHSEdZFD0Auy4/b.',
provider: 'local',
snsId: null,
createdAt: 2024-04-13T14:03:21.000Z,
updatedAt: 2024-04-13T14:03:21.000Z,
deletedAt: null
},
uniqno: 1,
_changed: Set(0) {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [
'id', 'email',
'nick', 'password',
'provider', 'snsId',
'createdAt', 'updatedAt',
'deletedAt'
]
},
isNewRecord: false
}
보다시피 id를 직접적으로 접근할 수 없고 dataValues 프로퍼티에 접근후 id값을 가져올 수 있는데(user.dataValues.id) 세션에는 정상적으로 유저의 id값이 저장되게된다

프록시(Proxy) 객체
이러한 방법이 가능했던것은 프록시 객체로 감싸졌기 때문이다
Proxy는 특정 객체를 감싸 프로퍼티 읽기, 쓰기와 같은 객체에 가해지는 작업을 중간에서 가로채는 객체로, 가로채진 작업은 Proxy 자체에서 처리되기도 하고, 원래 객체가 처리하도록 그대로 전달되기도 한다.
Proxy의 사전적 의미는 ''대리인", "대리" 라는 뜻으로
js에서도 말그대로 객체의 대리인 역할을 하고 다양한 라이브러리와 몇몇 브라우저 프레임워크에서 사용되고 있다
프록시 서버도 의미는 같다(클라이언트와 메인 서버 중간에 위치해 캐싱,분산 등 부가 기능을 대신 수행해주는 대리 서버)
프록시 객체 사용법
const proxy = new Proxy(target, handler);
- target - 프록시할 원본 객체
- handler - 가로채는 작업과 가로챈 작업을 재정의하는 객체
get
const target = {
message1: '1번 메세지',
message2: '2번 메세지',
};
const handler = {
get(target, prop, receiver) {
return '가로챈 메세지';
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.message1); //가로챈 메세지
console.log(proxy.message2); //가로챈 메세지
get 핸들러는 target, prop, receiver 3개의 인자를 받는다
- target - 프록시로 감싸진 객체
- prop - 프로퍼티 이름
- receiver - getter가 호출될 때 this 이다. 보통은 proxy 객체 자신이 this가 되고, 프록시 객체를 상속받은 객체가 있다면 해당 객체가 this가 되기도 한다(그냥 객체 자신을 가르킨다는 뜻)
핸들러 내부 함수(get, set, ...)는 대상 객체에 대한 호출을 잡아내기 때문에 트랩(traps) 이라고도 부른다
receiver 예시
const target1 = {
message: '나 부모 객체',
};
const handler1 = {
get(target, prop, receiver) {
console.log('target', target); // target { message: '나 부모 객체' }
return receiver;
},
};
const proxy1 = new Proxy(target1, handler1);
// 상속받은 객체
const inheritedObject = Object.create(proxy1);
//객체 프로퍼티 정의
inheritedObject.message = {
message: '나 상속받은 객체',
};
console.log(proxy1.message); // { message: '나 부모 객체' }
console.log(inheritedObject.message); // { message: '나 상속받은 객체' }
set
const target = {};
const handler = {
set(target, prop, value, receiver) {
if (typeof value === 'number') {
console.log('문자열만 입력하쇼');
return false // or throw new TypeError(`${value} is not an string`);
}
if (value.length < 5) {
console.log('5글자 이상으로 입력하쇼');
return false; // or throw new RangeError(`Please enter a value with at least 5 characters ${value}`);
}
console.log(`${prop} 이름으로 ${value} 저장 완료`);
return true;
},
};
const proxy = new Proxy(target, handler);
proxy.message = 1234; // 문자열만 입력하쇼
proxy.message = '저장해줘'; // 5글자 이상으로 입력하쇼
proxy.message = '나는 proxy'; // message 이름으로 나는 proxy 저장 완료
set은 target, prop, value, receiver 4개의 인자를 받는다
- target - 프록시로 감싸진 객체
- prop - 프로퍼티 이름
- value - 추가할 프로퍼티 값
- receiver - get 트랩과 유사하게 동작하는 객체로, setter 프로퍼티에만 관여
set 트랩을 사용할때는 성공했을시 반드시 true를 반환해야 성공적으로 저장된다
프록시 핸들러 메서드 표
| 내부 메서드 | 핸들러 메서드 | 작동 시점 |
| [[Get]] | get | 프로퍼티를 읽을 때 |
| [[Set]] | set | 프로퍼티에 쓸 때 |
| [[HasProperty]] | has | in 연산자가 동작할 때 |
| [[Delete]] | deleteProperty | delete 연산자가 동작할 때 |
| [[Call]] | apply | 함수를 호출할 때 |
| [[Construct]] | construct | new 연산자가 동작할 때 |
| [[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 |
시퀄라이즈 Proxy 구현해보기
const User = {
dataValues: {
id: 2,
email: 'zxc@zxc.com',
nick: '이거 되냐',
password: '$2b$12$wlDfDR1VpdQJ4Zttt4aWSOxw54hfHThO3L9S3wHSEdZFD0Auy4/b.',
provider: 'local',
snsId: null,
createdAt: '2024-04-13T14:03:21.000Z',
updatedAt: '2024-04-13T14:03:21.000Z',
deletedAt: null,
},
};
const handler = {
get(obj, prop, receiver) {
return obj.dataValues[prop];
},
};
const userProxy = new Proxy(User, handler);
console.log(userProxy.id); // 2
console.log(userProxy.nick); // 이거 되냐
참고한 글
https://ko.javascript.info/proxy#ref-1029
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Proxy - JavaScript | MDN
Proxy 객체를 사용하면 한 객체에 대한 기본 작업을 가로채고 재정의하는 프록시를 만들 수 있습니다.
developer.mozilla.org
'javaScript' 카테고리의 다른 글
| Symbol 자료형 (0) | 2024.10.23 |
|---|---|
| 프로토타입 (0) | 2024.10.23 |
| flat, every, some (0) | 2023.12.11 |
| 프로토타입 (0) | 2023.12.01 |
| Property Attribute (0) | 2023.12.01 |