본문 바로가기
JavaScript

[JavaScript] 클로저(Closure)를 활용한 상태 유지

by teamnova 2024. 11. 28.
728x90

안녕하세요.

오늘은 JavaScript의 중요한 개념 중 하나인 클로저(Closure)를 활용하여 상태를 안전하게 관리하는 방법과 이를 활용해 간단한 카운터를 만드는 예제를 함께 진행해 보겠습니다.


클로저란?

클로저의 정의

클로저는 함수와 그 함수가 선언될 당시의 렉시컬 스코프(Lexical Scope)의 조합입니다. 즉, 함수가 선언된 환경(스코프)을 기억하고, 이 환경에 접근할 수 있는 능력을 뜻합니다.

클로저를 사용하는 이유

클로저는 다음과 같은 상황에서 유용합니다:

  1. 상태 유지: 함수가 실행된 후에도 변수 상태를 유지할 수 있습니다.
  2. 데이터 보호: 외부에서 접근할 수 없는 변수를 보호합니다.
  3. 재사용성: 함수를 독립적인 단위로 만들어 재사용할 수 있습니다.

 


1. 클로저 없이 상태 관리하기

먼저, 클로저를 사용하지 않고 전역 변수로 상태를 관리하는 방식의 문제를 살펴봅시다.

 

HTML 구조

다음은 간단한 HTML 구조입니다. 버튼을 클릭하여 카운트를 증가/감소/리셋할 수 있습니다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>카운터 예제</title>
</head>
<body>
  <div>
    <button id="increment">증가</button>
    <button id="decrement">감소</button>
    <button id="reset">리셋</button>
    <p>현재 카운트: <span id="count">0</span></p>
  </div>
  <script src="script.js"></script>
</body>
</html>

 

JavaScript 코드 (전역 변수 사용)

let count = 0; // 전역 변수로 상태 관리

// 증가 버튼 클릭 이벤트
document.getElementById("increment").addEventListener("click", () => {
  count++; // 카운트를 증가
  updateDisplay();
});

// 감소 버튼 클릭 이벤트
document.getElementById("decrement").addEventListener("click", () => {
  count--; // 카운트를 감소
  updateDisplay();
});

// 리셋 버튼 클릭 이벤트
document.getElementById("reset").addEventListener("click", () => {
  count = 0; // 카운트를 초기화
  updateDisplay();
});

// 화면 업데이트 함수
function updateDisplay() {
  document.getElementById("count").textContent = count; // 현재 카운트를 표시
}

 

클로저 없이 상태 관리의 문제점

1. 전역 변수 오염

  • count가 전역에 선언되어 있으므로, 다른 코드에서 의도치 않게 값을 변경할 위험이 있습니다.
count = 100; // 외부 코드에서 카운트를 임의로 변경
updateDisplay(); // 잘못된 카운트가 표시됨

 

2. 독립성 부족

  • 여러 개의 카운터를 만들고 싶다면, 각 카운터마다 별도의 전역 변수를 만들어야 합니다. 이는 관리하기 어렵고, 코드가 지저분해질 가능성이 큽니다.

2. 클로저로 상태 유지하기

위의 문제를 해결하기 위해, 클로저를 사용해 상태를 안전하게 관리하는 방법을 살펴봅시다.

 

JavaScript 코드 (클로저 사용)

// 카운터를 생성하는 함수
function createCounter() {
  let count = 0; // 상태를 함수 내부에 캡슐화

  return {
    increment() {
      count++; // 카운트를 증가
    },
    decrement() {
      count--; // 카운트를 감소
    },
    reset() {
      count = 0; // 카운트를 초기화
    },
    getCount() {
      return count; // 현재 카운트를 반환
    },
  };
}

const counter = createCounter(); // 독립적인 상태를 가진 카운터 생성

// 증가 버튼 클릭 이벤트
document.getElementById("increment").addEventListener("click", () => {
  counter.increment(); // 카운트를 증가
  updateDisplay();
});

// 감소 버튼 클릭 이벤트
document.getElementById("decrement").addEventListener("click", () => {
  counter.decrement(); // 카운트를 감소
  updateDisplay();
});

// 리셋 버튼 클릭 이벤트
document.getElementById("reset").addEventListener("click", () => {
  counter.reset(); // 카운트를 초기화
  updateDisplay();
});

// 화면 업데이트 함수
function updateDisplay() {
  document.getElementById("count").textContent = counter.getCount(); // 현재 카운트를 표시
}

 

클로저 사용의 장점

1. 상태 캡슐화

  • count는 createCounter 함수 내부에 선언된 변수로, 외부에서 직접 접근할 수 없습니다. 상태가 안전하게 보호됩니다.
counter.count = 100; // 외부에서 count에 접근하려고 시도해도 실패
console.log(counter.getCount()); // 여전히 올바른 상태를 유지

 

2. 독립적인 상태 관리

  • 클로저를 통해 여러 개의 카운터를 독립적으로 생성할 수 있습니다.
const counter1 = createCounter();
const counter2 = createCounter();

counter1.increment();
console.log(counter1.getCount()); // 1
console.log(counter2.getCount()); // 0 (별개의 상태)

 


3. 두 접근 방식 비교

특징 클로저 없이 전역 변수 사용 클로저 사용
상태 저장 위치 전역 변수 (let count) 함수 내부 변수 (let count)
상태 변경 가능성 외부에서 자유롭게 변경 가능 클로저를 통해서만 변경 가능
독립성 한 개의 상태만 유지 가능 여러 독립적인 상태 생성 가능
보안성 데이터가 노출되어 취약 데이터가 캡슐화되어 안전