IT관련/Angular

JavaScript/TypeScript - 콜백 함수에서 .bind(this) 완벽 이해하기

파란하늘999 2025. 10. 1. 10:47

콜백 함수를 다루다 보면 .bind(this)를 자주 마주치게 됩니다. 이게 정확히 무엇이고 왜 필요한지 알아보겠습니다.

문제 상황: this가 사라진다?

먼저 문제가 되는 상황을 보겠습니다.

class UserService {
  userName = 'John';

  saveUser() {
    console.log(`${this.userName} 저장 완료`);
  }

  registerUser() {
    // 3초 후 saveUser 실행
    setTimeout(this.saveUser, 3000);
  }
}

const service = new UserService();
service.registerUser();
// 💥 에러 발생! Cannot read property 'userName' of undefined

 

왜 에러가 날까요?

 

setTimeout에 this.saveUser를 넘기는 순간, 함수는 원래의 this 컨텍스트를 잃어버립니다. 콜백으로 실행될 때는 this가 undefined(strict mode) 또는 window(non-strict mode)가 되어버립니다.

 

해결 방법 1: .bind(this) 사용

.bind(this)는 함수에 this를 영구적으로 묶어주는 역할을 합니다.

class UserService {
  userName = 'John';

  saveUser() {
    console.log(`${this.userName} 저장 완료`);
  }

  registerUser() {
    // ✅ bind로 this를 고정
    setTimeout(this.saveUser.bind(this), 3000);
  }
}

const service = new UserService();
service.registerUser();
// ✅ "John 저장 완료" 정상 출력!

 

bind()의 동작 원리

const boundFunction = originalFunction.bind(thisValue);
  • 새로운 함수를 반환합니다 (원본 함수는 변경 안 됨)
  • 반환된 함수는 항상 지정된 this로 실행됩니다
  • 나중에 어떻게 호출되든 this가 바뀌지 않습니다

 

실전 예제: Angular에서의 활용

Angular에서 RxJS를 사용할 때 자주 마주치는 상황입니다.

@Component({
  selector: 'app-user-list',
  template: '...'
})
export class UserListComponent {
  users: User[] = [];

  loadUsers() {
    this.userService.getUsers()
      .subscribe(this.handleUsers.bind(this));
      //         ^^^^^^^^^^^^^^^^^^^^^^^ bind 필수!
  }

  handleUsers(users: User[]) {
    this.users = users; // this를 사용하므로 bind 필요
    this.changeDetector.detectChanges();
  }
}

 

왜 bind가 필요할까?

// ❌ bind 없이 사용하면
.subscribe(this.handleUsers)
// handleUsers가 실행될 때 this는 Component가 아닌
// subscribe 내부의 컨텍스트가 됨

// ✅ bind 사용하면
.subscribe(this.handleUsers.bind(this))
// handleUsers가 실행될 때 this는 항상 Component를 가리킴

 

다른 해결 방법들

 

방법 2: 화살표 함수 (권장 ⭐)

class UserService {
  userName = 'John';

  registerUser() {
    // ✅ 화살표 함수는 상위 스코프의 this를 자동으로 유지
    setTimeout(() => this.saveUser(), 3000);
  }

  // 또는 메서드를 화살표 함수로 정의
  saveUser = () => {
    console.log(`${this.userName} 저장 완료`);
  }
}

 

화살표 함수의 장점:

  • .bind(this) 불필요
  • 코드가 더 간결
  • 실수할 여지가 적음

 

방법 3: 익명 함수

setTimeout(function() {
  this.saveUser();
}.bind(this), 3000);

 

bind()의 추가 기능: 인자 고정

bind()는 this뿐만 아니라 인자도 미리 고정할 수 있습니다.

function greet(greeting, name) {
  console.log(`${greeting}, ${name}!`);
}

const sayHello = greet.bind(null, 'Hello');
sayHello('John');  // "Hello, John!"
sayHello('Alice'); // "Hello, Alice!"

const sayHelloToJohn = greet.bind(null, 'Hello', 'John');
sayHelloToJohn(); // "Hello, John!"

 

실무 팁

 

1. Angular에서는 화살표 함수 우선

// ✅ 좋은 방법
this.http.get('/api/users')
  .subscribe(users => this.handleUsers(users));

// ⚠️ 작동하지만 불필요하게 복잡
this.http.get('/api/users')
  .subscribe(this.handleUsers.bind(this));

 

2. 이벤트 리스너에서 주의

class MyComponent {
  ngOnInit() {
    // ❌ 제거할 때 문제 발생
    window.addEventListener('resize', this.onResize.bind(this));
    
    // ✅ 참조를 저장해야 제거 가능
    this.boundOnResize = this.onResize.bind(this);
    window.addEventListener('resize', this.boundOnResize);
  }

  ngOnDestroy() {
    window.removeEventListener('resize', this.boundOnResize);
  }

  onResize() {
    // ...
  }
}

 

3. 성능 고려사항

// ❌ 렌더링마다 새로운 함수 생성 (비효율)
<button (click)="handleClick.bind(this)">Click</button>

// ✅ 화살표 함수 사용
<button (click)="handleClick()">Click</button>

// 또는 컴포넌트에서 미리 bind
constructor() {
  this.boundHandleClick = this.handleClick.bind(this);
}

정리

방법 장점 단점 추천도
.bind(this) 명시적, 정확한 제어 코드가 길어짐 ⭐⭐⭐
화살표 함수 () => {} 간결, 자동으로 this 유지 클래스 필드로 정의 시 메모리 증가 ⭐⭐⭐⭐⭐
익명 함수 유연함 가독성 저하 ⭐⭐

 

핵심 요약

  1. 콜백으로 메서드를 넘길 때 this가 사라지는 문제 발생
  2. .bind(this)는 함수에 this를 영구적으로 묶어줌
  3. 화살표 함수가 더 현대적이고 간편한 해결책
  4. 이벤트 리스너 제거 시 bind된 함수 참조를 저장해야 함

 

마치며

JavaScript/TypeScript에서 this는 까다로운 개념이지만, .bind()의 원리를 이해하면 콜백 함수를 다룰 때 발생하는 많은 문제를 해결할 수 있습니다.

최신 코드에서는 화살표 함수를 주로 사용하지만, 레거시 코드나 특정 상황에서는 여전히 .bind(this)를 사용하므로 두 가지 방법 모두 알아두는 것이 좋습니다!

반응형