[F-Lab 모각코 챌린지 - 50일차] - 클로저(2)

Big One·2023년 6월 29일
0

F-Lab

목록 보기
23/69

저번시간에 이어 클로저에 대해 알아봤는데 어디서 이걸 쓰지? 왜 쓰지? 싶었다. 클로저의 활용 사례를 보고 자바스크립트에는 없는 private , public 를 구별해서 사용할 수 있게끔 역할을 해줄 수 있다는 것을 알게됐다.

클로저 활용 사례

콜백 함수 내부에서 외부 데이터를 사용하고자 할 때

const fruits = ['apple', 'banana', 'peach'];
const $ul = document.createElement('ul');

const alertFruit = function(fruit){
	alert('your choice is' + fruit);
}
fruits.forEach(function(fruit){
	const $li = document.createElement('li');
	$li.innerText = fruit;
	$li.addEventListener('click', alertFruit);
	$ul.appendChild($li);
});

document.body.appendChild($ul);
alertFruit(fruits[1]);  

위 코드를 실행하면 [object MouseEvent] 라는 값이 출력된다. addEventListener는 콜백 함수를 호출할 때 첫 번째 인자에 ‘이벤트 객체’ 를 주입하기 때문이다. 이 문제는 bind 사용하면 해결된다. 이 방법의 문제점은 이벤트 객체가 인자로 넘어오는 순서가 바뀌는 것과 함수 내부에서의 this가 원래의 그것돠 달라진다는 점이다.

이 이슈를 해결하려면 고차함수를 활용하면 된다.

(1) 번 부분을 아래와 같이 바꾸면 된다.

const fruits = ['apple', 'banana', 'peach'];
const $ul = document.createElement('ul');

const alertFruitBuilder = function(fruit){
	return function () {
		alert('your choice is' + fruit);
	}
}

fruits.forEach(function(fruit){
	const $li = document.createElement('li');
	$li.innerText = fruit;
	$li.addEventListener('click', alertFruitBuilder(fruit));
	$ul.appendChild($li);
});

접근 권한 제어 (정보 은닉)

정보 은닉은 내부 로직에 대해 외부로의 노출을 최소화 한느 것이다. 클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능하다.

const outer = () => {
	let a = 1;

	const inner = () => {
		return ++a;
	};

	return inner;
};

const outer2 = outer();
outer2();
outer2();

위와 같이 클로저를 활용하면 함수 내부의 변수들 중 선택적으로 일부의 변수에 대한 접근 권한을 부여할 수 있다. return을 통해서!!

내부에서만 사용할 정보들은 return 하지 않으므로 접근 권한 제어가 가능한 것이다.
return 한 변수들은 공개멤버(public member) , 그렇지 않은 변수는 비공개 멤버(private member)가 된다.

간단한 자동차 객체를 만들어서 클로저로 접근 권한 제어를 하는 과정을 살펴보겠다.

// 간단한 자동차 객체
const car = {
  fuel: Math.ceil(Math.random() * 10 + 10),
  power: Math.ceil(Math.random() * 3 + 2),
  moved: 0,
  run: function () {
    const km = Math.ceil(Math.random() * 6);
    const wasteFuel = km / this.power;
    if(this.fuel < wasteFuel){
      console.log('이동 불가');
      return;
    }
    this.fuel -= wasteFuel;
    this.moved += km;
    console.log(km + 'km  이동 (총: ' + this.moved + 'km');
  }
}

car.run();

위에 코드는 무엇이 문제일까? 문제는! 직접 접근이 가능하다는것이다. car.fuel = 1000; 이런식으로 해버리면 치트키 쓰는거임
위 객체를 함수로 만들게되면 다음과 같이 문제를 해결할 수 있다.(클로저를 활용함으로써 접근 권한 제어 가능)

const createCar = () => {
	let fuel = Math.ceil(Math.random() * 10 + 10);
  let power = Math.ceil(Math.random()* 3 + 2);
  let moved = 0;

	return {
    get moved() {
      return moved;
    },
    run: function () {
      let km = Math.ceil(Math.random() * 6);
      let wasteFuel = km / power;
      if(fuel < wasteFuel){
        console.log('이동 불가');
        return;
      }
      fuel -= wasteFuel;
      moved += km;
      console.log(km + 'km  이동 (총: ' + moved + 'km). 남은 연료: ' + fuel);
    }
  };
}
const car = createCar();
car.run();

이렇게 작성하면 클로저를 활용하여 정보은닉(접근 권한 제어) 이 가능하다. return 한 값을 빼고는 접근이 되지 않는다. car.fuel =1000; 해도 car 객체에 fuel 이라는 프로퍼티가 생길 뿐이지 실제로는 영향을 주지않는다. 하지만 여기서도 문제점은 하나 있다.. 바로 run 메서드를 다른 내용으로 덮어씌우는 어뷰징은 가능하단 것이다…

이것은 !! 객체 변수를 하나 만들어 return 값을 담아 객체 변수를 리턴하면 된다. +Object.freeze();

모두 적용한 전체 코드는 다음과 같다.

const createCar = () => {
	let fuel = Math.ceil(Math.random() * 10 + 10);
  let power = Math.ceil(Math.random()* 3 + 2);
  let moved = 0;
	
	const publicMembers = {
		get moved() {
      return moved;
    },
    run: function () {
      let km = Math.ceil(Math.random() * 6);
      let wasteFuel = km / power;
      if(fuel < wasteFuel){
        console.log('이동 불가');
        return;
      }
      fuel -= wasteFuel;
      moved += km;
      console.log(km + 'km  이동 (총: ' + moved + 'km). 남은 연료: ' + fuel);
    }
	}
	Object.freeze(publicMembers);
	return publicMembers;
}
const car = createCar();
car.run();

이렇게 하면 return값인 publicMembers 를 덮어씌우지도 못하게된다.

학습할 키워드

  • getter, setter (get, set 메서드)
  • Object.freeze();
  • 고차함수
profile
이번생은 개발자

0개의 댓글