콜백 함수를 다루다 보면 .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 유지 | 클래스 필드로 정의 시 메모리 증가 | ⭐⭐⭐⭐⭐ |
| 익명 함수 | 유연함 | 가독성 저하 | ⭐⭐ |
핵심 요약
- 콜백으로 메서드를 넘길 때 this가 사라지는 문제 발생
- .bind(this)는 함수에 this를 영구적으로 묶어줌
- 화살표 함수가 더 현대적이고 간편한 해결책
- 이벤트 리스너 제거 시 bind된 함수 참조를 저장해야 함
마치며
JavaScript/TypeScript에서 this는 까다로운 개념이지만, .bind()의 원리를 이해하면 콜백 함수를 다룰 때 발생하는 많은 문제를 해결할 수 있습니다.
최신 코드에서는 화살표 함수를 주로 사용하지만, 레거시 코드나 특정 상황에서는 여전히 .bind(this)를 사용하므로 두 가지 방법 모두 알아두는 것이 좋습니다!
반응형
'IT관련 > Angular' 카테고리의 다른 글
| Angular 테스트 - afterEach vs afterAll 완전 정복! (0) | 2025.11.26 |
|---|---|
| Angular tsconfig.json 왜 compilerOptions랑 angularCompilerOptions가 따로 있을까? (0) | 2025.11.21 |
| Angular Standalone: 모듈 없는 Angular 개발의 시작 (1) | 2025.09.26 |
| Angular에서 스프레드 연산자(Spread Operator) 활용 정리 (0) | 2025.09.23 |
| Angular 소스맵 설정 및 디버깅 방법 요약 (0) | 2025.09.16 |