Closure 와 HOF, HOC

조미라·2024년 10월 29일

개발지식

목록 보기
7/7
post-thumbnail

클로저에 대해서 알아보기 위해 우선 테스트로 아래의 코드로 html 파일을 생성한다.

🖥️ 확인용 예시 코드

<!DOCTYPE html>
<html lang="ko">
  <head>
    <title>클로저 실습!!</title>
    <script>
      function aaa() {
        const apple = 10;

        function bbb() {
          const banana = 20;
          console.log(banana);
          console.log(apple);
          console.log(orange);
        }

        const orange = 30;
        bbb();
      }

      aaa();
    </script>
  </head>
  <body></body>
</html>

생성 후 실행하여 크롬의 개발자 모드에서 sources탭으로 들어가서 aaa() 부분에 브레이크 포인트를 잡아주고 새로고침하면 한단계씩 실행하는 버튼을 클릭하여 단계별로 실행을 확인해볼 수 있다.

한단계씩 실행을 하다보면 콜스택에 aaa, bbb가 생성되었음을 볼수 있다.

bbb()는 스택의 가장 위에 있기 때문에 가장 먼저 콜스택을 빠져나가게 된다. 또한 bbb() 안에는 apple이라는 변수가 없기 때문에
자바스크립트는 apple라는 변수를 찾기위해 스코프를 올라가게 된다.
이 과정에서 closure가 생성되게 된다.

클로저(closure)란?

bbb() 함수안에 apple이라는 변수가 없어서 aaa()라는 상위 함수의 스코프로 찾아 올라가 apple라는 변수를 찾게된다.

aaa() 함수는 bbb() 함수의 closure가 되며 bbb() 함수가 apple을 찾아 올라갈 수 있는 이유는 실행 컨텍스트가 외부 환경 요소를 수집하기 때문이다.

즉, 클로저라고 하는것은 상위 함수(예시:aaa())와 해당 함수(예시:bbb())가 선언된 스코프 즉 상위 함수를 둘러싼 환경이 된다.

또한 bbb() 함수에서 aaa() 함수 스코프로 올라가는 과정에서 스코프 체인이 일어나는 것이다.

스코프 체인과 클로저 간단 정리

  • 클로저(closure) : 상위 함수 + 상위함수의 lexical enviroment(상위함수를 둘러싼 환경)
  • 스코프 체인(scope chain) : 바로위 함수 스코프 뿐만아니라 global 스코프 까지 찾아 올라가는 과정을 scope chain이라고 합니다.

🖥️ 함수를 리턴하는 고차함수 형태의 예시

// 함수 선언식
function aaa(apple) {

	return function bbb(banana) {
		console.log(banana)
		console.log(apple)
	}
}

aaa(100)(500)

// 실행 결과
// 100 => aaa에 넣은 인자값
// 500 => bbb에 넣은 인자값

위와 같은 형태로 함수를 리턴하는 함수가 있을때 함수 선언시 각 변수를 함수의 파라미터 값으로 넘기게 되면 함수가 실행될때
넘겨준 인자의 값으로 콘솔이 나오게 된다.

bbb() 함수에는 apple이 없는 상태이지만 값을 받아올수 있는 이유는 앞전에 설명했듯이 클로저 때문이다.

클로저는 함수가 생성될 당시의 외부 스코프를 기억해서, 그 스코프 안에 있던 변수를 나중에 함수가 호출될 때도 참조할 수 있는 기능을 말한다.

위 예시 코드를 화살표 함수 형태로 변형도 가능하다.

// 화살표 함수로 변경, 중괄호 생략
const bbb = (apple) => (banana) => {
	console.log(banana);
	console.log(apple);
};

bbb(50)(60);

이러한 함수의 핵심은 클로저로 인하여 외부에서 받은 인자를 계속 기억하고 있다는 것에 있다.

고차함수(HOF)란?

위의 예시처럼 함수안에 다른 함수를 반환하는 함수 또는 함수를 인자로 받는 함수를 고차함수라고 한다.

const onClickPage = (page: number) => () => {
  refetch({ page });
};

위 예시에서 onClickPage는 페이지 번호를 인자로 받아 새로운 함수를 반환하는 고차 함수로 사용되어있다.

여기서 onClickPagepage라는 인자를 받아 또 다른 화살표 함수를 반환하는데, 반환된 함수는 특정 페이지 번호를 기준으로 Apollo Client의 refetch 메서드를 호출한다. 이 패턴은 고차 함수(HOF)를 활용한 형태로, 동적 이벤트 핸들러를 쉽게 정의할 수 있는 방법이다.

이 방식을 사용하면, span 태그에서 페이지 번호를 클릭할 때마다 해당 페이지에 맞는 데이터를 페칭할 수 있다.

이처럼 고차 함수로 정의하면 코드가 더 간결하고 유연해지며, 클릭 이벤트 발생 시 바로 사용할 수 있는 형태가 된다.

id 바인딩과 버블링 이슈 해결

만약 위와 같은 형태의 이벤트 핸들러에서 page를 직접 함수 안에 넣지 않고, 이벤트 객체(event)를 받아 처리한다면 이벤트 버블링 이슈가 발생할 수 있다. 버블링은 자식에서 발생한 이벤트가 부모까지 전파되어 여러 이벤트가 예상치 못한 방식으로 동작하는 문제를 말한다.

이러한 문제를 화살표 함수로 이벤트를 바인딩하여 아래와 같은 형태로 해결할 수 있다.

<span key={index + 1} id={String(index + 1)} onClick={onClickPage(index + 1)}>
  {index + 1}
</span>

여기서 onClickPage(index + 1)는 즉시 실행되는 함수가 아니고, span 클릭 시에만 실행되도록 되어 있다. 만약 함수 실행 시 page 값을 미리 고정하지 않았다면, 이벤트가 발생할 때마다 event 객체를 처리해야 하고, 그 과정에서 의도하지 않은 값이 전달될 가능성이 커지게 된다.

💡 요약 정리

  • 고차 함수(HOF)를 사용하면 동적 이벤트 핸들러를 정의할 수 있으며, 이벤트 실행 시점에 원하는 값을 쉽게 전달할 수 있다.
  • 화살표 함수를 사용해 코드를 더 간결하게 만들고, 버블링 문제를 방지하는 동시에 간결한 이벤트 핸들링을 구현할 수 있다.

HOC(Higher Order Component)란?

HOC는 상위에 있는 컴포넌트로 다른 컴포넌트보다 먼저 실행되는 컴포넌트이다. 이를 위해 위에서 배운 고차함수를 활용하게 된다.

위와 같은 형태로 HOC 고차함수 컴포넌트를 만들게되면
해당 컴포넌트가 실행되기 전에 내가 실행하고자 하는 함수를 어떠한 함수를 먼저 실행하고, 그 이후에 컴포넌트가 렌더링 되도록 설정할 수 있다.

위 예시 같은 경우 로그인을 확인하는 부분을 먼저 실행하게 하고
그 다음 컴포넌트가 렌더링되게끔 설정한 예시로 로그인 전이라면 바로 페이지를 이동하게 되고 로그인 후라면 정상적으로 로그인 정보를 표시하는 LoginSuccessPage가 보이게 된다.

유지보수를 위해서는 보통은 hook을 만들어서 구현하는 경우도 많지만
경우에 따라서 어떠한 방법이 효율적일지 모르기 때문에 hook을 hoc로 hoc를 hook으로 리팩토링하는 작업을 연습해보는 것이 좋다고 한다.

profile
안녕하세요 후훗ㅋ 멋진 프론트엔드 개발자가 되고 싶은 조미라입니다!

0개의 댓글