카테고리 없음

[ JS ] 클로저에 대해 알아보자

modori@ 2024. 4. 27. 15:05

 

웹 분야를 공부하다보면 "클로저"라는 단어를 들어보셨을 겁니다. 

오늘은 이 한번쯤은 들어본 closure에 대해 정리 해보겠습니다.

 

클로저(Closure)

JavaScript에서 클로저(Closure)는 이렇게 정의할 수 있습니다.

함수와 그 함수가 선언될 당시의 렉시컬 환경(Lexical Environment)과의 조합

 

이렇게 정의만 보아선 이해하기 어려울 수 있습니다.
조금더 간단히 설명해 보도록 하겠습니다.

내부함수가 외부함수의 context에 접근할 수 있는 함수

 

조금 더 이해하기 쉬우신가요?
이 개념을 기억하면서 클로저에 대해 차근차근 알아보도록 합시다.

실행 컨텍스트

먼저 클로저를 이해하기 전 함수의 실행 컨텍스트에 대해 간단히 알아야합니다.

함수는 호출 될 때 함수의 실행 컨텍스트를 생성했다가, 실행이 끝나면 실행컨텍스트가 종료됩니다.
( 이 실행 컨텍스트의 lexical environment엔 함수의 지역변수의 정보, 이 함수의 상위 스코프에 대한 정보가 들어있습니다.)

 

간단한 예제를 하나 봐보도록 합시다.

const HelloWorld = () => {
    const hello = "Helloooo";
      return hello;
}

console.log(HelloWorld());
console.log(hello)

 

 

 

 

보이는대로 위의 함수 호출이 끝난후 실행 컨텍스트가 종료되며, 안의 hello라는 변수에 아무도 접근할 수 없게 되었습니다.
어떻게 보면 당연한 결과죠.

하지만 이 hello라는 변수에 접근할 수 없는 것은 아닙니다.

 

오늘 주제인 closure를 사용한다면 실행 컨텍스트가 끝난 이후에도 외부에서 이 hello라는 변수에 대해 접근할 수 있습니다.

 

const HelloWorld = () => {
    const hello = "Helloooo";
    console.log(hello)
    const sayHello = () => {
        return hello;
    }
    return sayHello;
}

const helloFunction = HelloWorld();
console.log(helloFunction()); 

 

HelloWorld가 마찬가지로 실행 후 종료되었습니다.
마찬가지로 실행 컨텍스트가 종료되었으니hello라는 변수에는 접근할 수가 없겠죠?


하지만 sayHello에서는 접근할 수가 있습니다.

 

 

이게 클로접니다.

 

클로져의 특성상sayHello함수가 선언될 때 그 주변의 lexical enviroment와 함께 번들로 묶였기 때문입니다.

하지만 왜 기존의 규칙을 바꾸면서까지 이 클로저라는 것을 만들었을까요?

유용한 클로저

전역변수

전역변수는 어디서든 접근이 가능하기 때문에 되도록이면 적은게 좋습니다.
하지만 단 1곳에 사용하는데도 전역변수가 필요한 경우가 있는데요, 바로 이런 순간에 사용하는 것이 클로저 입니다.

예시를 하나 살펴보도록 합시다.

const btn = document.querySelector('button')

btn.addEventListener('click',handleClick)

let count = 0
function handleCilck(){
  count++
  return count
}

 

위의 예제처럼 클릭할 때마다 count를 세주는 함수가 있습니다.
count를 전역변수로 작성해 줘야지만 값을 추가하는게 가능합니다.

하지만 클로저를 사용하면 전역변수가 아니더라도 counter를 구현할 수 있습니다.

 

const btn = document.querySelector('button')

btn.addEventListener('click',handleClick())

function handleCilck(){
  let count = 0
  return function (){
    count++
    return count
  }
}

 

외부함수(handleClick)의 lexical environment를 참조하는 함수를 btn의 콜백함수로 이용해 구현할 수 있습니다.

코드 재사용성

클로저를 사용하게 되면 비슷한 형태의 코드를 재사용률을 높일 수 있습니다.

const newTag = function(open, close) {
    return function(content) {
        return open + content + close
    }
}

const bold = newTag('<b>', '</b>')
const italic = newTag('<i>', '</i>')

console.log(bold(italic("This is my content!")))

 

위 코드를 보면 bold, italic같이 여러 태그를 새로운 태그를 만들 수 있는 newTag를 클로저를 이용해 간단하게 구현했습니다.

인자로 open, close, content를 한번에 다 받으면 코드의 가독성이 떨어질 수 있습니다.

하지만 클로저를 통해 구현하면 코드의 가독성, 재사용성까지 챙긴 편한 코드가 됩니다.

착각하기 쉬운 클로저

function outer() {
  let name = 'kyle'
  if (true) {
    let city = 'seoul'
    return function inner() {
      console.log(city)
    }
  }
}

 

위의 코드가 클로저를 사용한 코드일까요?

 

클로저를 정확하게 파악하지 않았을 때, 마치 함수를 리턴하는 것 자체가 클로저라고 오해하는 경우가 행길 수 있습니다.

클로저는 내부에 선언된 함수가 외부함수의 지역변수를 사용해 줬을 때만 클로저라고 선언됩니다.
inner함수에도 클로저를 사용하고 싶다면 name변수를 사용해주면 됩니다.

function outer() {
  let name = 'kyle'
  if (true) {
    let city = 'seoul'
    return function inner() {
      console.log(city)
     console.log(name)
    };
  }
}

 

마무리

마지막으로 클로저를 다시한번 정리해봅시다.

클로저란 내부함수에서 외부함수의 지역변수를 사용할 때 외부함수의 lexcial environment와 함께 짝을 이루는 것이라고 생각하면 될 것 같습니다.