객체 메서드의 this 바인딩

JYROH·2023년 11월 21일
0
post-thumbnail

최근에 회사에서 작업을 하다가 이해가 안되는 상황을 맞이했었다.

우리 회사는 상태관리 도구로 Mobx를 사용하기에, 자연스럽게 Mobx Store, Model을 위한 Class를 자주 사용하고, class내의 프로퍼티들을 가리킬수있는 this도 자주 사용하게 되었다.

문제의 코드

// DialogStore.ts
class DialogStore {

  dialogNum = 1
  
  constructor(){
    makeAutoObservable(this)
  }
  
  goDialogNext(){
    this.dialogNum += 1;
  }
  
}

// Dialog.tsx
import dialogStore from "./DialogStore"


<DialogTypeSelection
	onClose={onClose}
	open={isOpen}
	handleDialogNext={???}
/>

위의 코드에서 Dialog.tsx는 여러 페이지의 dialog를 모두 가지고 있는 부모 컴포넌트고, 각 자식 dialog에 dialogNext(다음페이지로 이동)기능을 핸들러로 넘겨야 하는 상황이었다.

일반적으로 핸들러를 넘기듯이, 단순히 함수를 넘기면 될 것 같다고 생각했었고

<DialogTypeSelection
	onClose={onClose}
	open={isOpen}
	handleDialogNext={dialogStore.goDialogNext}
/>

그리하여 위와 같이 코드를 작성하게 되었다.

항상 핸들러를 넘길때 인자가 있으면 화살표 익명함수로 감싸서 내려줬을뿐, 인자가 없으면 그냥 쌩으로 핸들러를 내려줬었기 때문이다.

그러나...
goDialogNext메서드 내부의 thisundefined라는 에러를 코드가 뿜어냈다.

왜?

이해가 가지 않았다. this에 대해 공부할때, 객체의 메서드에 있는 this는 해당 객체를 가르키는 것이라고 들었기 때문이다. 그러니, 무조건 thisdialogStore그 자체를 가르키는게 당연하다고 생각했었다.

그러나 this바인딩은, 호출될때 정해지는 것이고 위의 콜백 방법(메서드를 직접 다른곳에 전달)으로는 this가 인스턴스에 바인딩 되지않고 끊어진다는 것이었다.

Class를 자주 사용해보지 않아, this바인딩의 위험성에 대해 모르고 그대로 메서드를 전달했던것이 화근이었던 것이다.

해결책

  1. bind를 활용한 명시적 bind
<DialogTypeSelection
	onClose={onClose}
	open={isOpen}
	handleDialogNext={dialogStore.goDialogNext.bind(dialogStore)}
/>

위와 같이 bind를 활용하여 명시적으로 인스턴스를 참조하도록 바인딩을 걸어주면 this는 dialogStore를 가르키게 된다

  1. 함수로 감싸 전달하기
<DialogTypeSelection
	onClose={onClose}
	open={isOpen}
	handleDialogNext={()=>{dialogStore.goDialogNext()}}
/>

함수로 메서드를 한번 감싸서 자식에 전달하면 this바인딩에 깨지지 않고 잘 전달된다.

Class와 this공부에 소홀했었는데, 실무에서 오류를 겪으니 제대로 공부해야겠다고 다시한번 생각하게 되었다

profile
안녕하세요 노준영입니다.

2개의 댓글

comment-user-thumbnail
2024년 2월 14일

this 바인딩 정말 무섭네요 ㄷㄷ 조심해야겠어요

답글 달기
comment-user-thumbnail
2024년 12월 31일

this 바인딩 정말 무섭네요 ㄷㄷ 조심해야겠어요

답글 달기