완전한 싱글톤 패턴으로 리팩토링을 진행해보자
2024.05.23 - [TIL] - 그저 작성했는데 싱글톤 패턴이었던 것에 대하여
그저 작성했는데 싱글톤 패턴이었던 것에 대하여
오늘도 평화로움을 바라며 코딩하던 날이었습니다.서버로부터 전달받은 토큰을 변수에 캐싱하는 로직을 포함해 PR했는데요.하나의 인스턴스로 관리한다는 키워드로 구글링을 할 때,여러 블로
betterpalywon.tistory.com
이전 포스팅인 싱글톤 패턴 구현에 이어 제가 구현한 싱글톤 패턴의 문제점과
완전한 싱글톤 패턴으로 변경해야 하는 이유, 그리고 리팩토링했던 방법을 설명해보려 합니다.
이전 싱글톤 패턴의 문제점
인스턴스의 내부 상태에 접근해 변경이 가능하다!
이전 코드를 예제로 들어보겠습니다.
let userInstance = null;
const createUser = () => {
let userInfo = {};
const setUserInfo = (name, age, email) => {
userInfo = { name, age, email };
};
const getUserInfo = () => {
return userInfo;
};
return {
setUserInfo,
getUserInfo,
};
};
const getUserInstance = () => {
if (!userInstance) {
userInstance = createUser();
}
return userInstance;
};
const user = getUserInstance();
user.setUserInfo('won',33,'won@naver.com');
console.log(user.getUserInfo()); //. { age: 33, ... }
const otherUser = user.getUserInfo(); // 인스턴스에 직접 접근 시도
otherUser.age = 23;
console.log(user.getUserInfo()); //. { age: 23, ...}
otherUser라는 변수에서 내부 인스턴스에 직접 접근해서 값 변경이 가능했는데요.
해당 이슈로 나중에 저나 동료가 직접 접근을 통한 코딩으로 사이드 이펙트가 예상됐습니다.
또한, 코드를 작성하다 보니 userInstance 변수를 외부 환경에서 접근할 수 있도록 두는 것도
계속 눈에 밟혔어요.
메서드를 통한 접근만을 팀 단위로 강조하는 것은 애초에 말이 안되겠죠!
그래서 리팩토링을 해야겠다 마음먹던 도중, 갓티쳐 제로초님의 블로그를 보고
힌트를 얻어 리팩토링을 진행했습니다.
즉시실행함수(IIFE)를 통한 리팩토링
const getUserInstance = (() => {
let userInstance = null;
const createUser = () => {
let userInfo = {};
const setUserInfo = (name, age, email) => {
userInfo = { name, age, email };
};
const getUserInfo = () => {
return {...userInfo};
};
return {
setUserInfo,
getUserInfo,
};
};
return () => {
if (!userInstance) {
userInstance = createUser();
}
return userInstance;
};
})();
const user = getUserInstance();
user.setUserInfo('won',33,'won@naver.com');
console.log(user.getUserInfo()); //. { age: 33, ... }
const otherUser = user.getUserInfo(); // 인스턴스에 직접 접근 시도
otherUser.age = 23;
console.log(user.getUserInfo()); //. { age: 33, ... }
IIFE 내부에서 userInstance 변수를 정의하며 함수 스코프에 의해 외부 접근이 불가해졌네요!
userInfo 변수는 IIFE가 return하는 객체의 메서드를 통해서만 접근할 수 있게 되었구요.
userInstance, userInfo 변수들처럼 외부 접근이 불가하거나
객체 메서드를 통한 접근이 가능한 변수를 비공개 변수(= private variable)라고 말하더라고요😎
또한 클로저 덕분에 userInfo는 IIFE가 종료된 후에도 그 값을 유지하고,
외부에서는 직접 접근할 수 없도록 처리했습니다.
+ 추가 사항
getUserInfo 메서드는 userInfo 객체의 복사본을 return하면서
외부에서 return된 객체를 수정해도 원본 객체에는 영향을 미치지 않아
항상 동일한 결과만을 return하도록 만들어줬습니다.