본문 바로가기
IT

this가 undefined — 바인딩·화살표 함수 원인·해결

by 샤나엘 2026. 7. 2.
반응형

this가 undefined — 바인딩·화살표 함수 원인·해결

자바스크립트에서 this만큼 헷갈리는 것도 드물다. 클래스 메서드가 분명 있는데 "this.something is not a function"이 뜨거나, 객체 메서드 안에서 this가 undefined가 되어 값을 못 읽는 일이 흔하다. 원인은 하나로 요약된다. this는 함수가 "어디에 정의됐는가"가 아니라 "어떻게 호출됐는가"로 정해지기 때문이다. 그래서 같은 메서드라도 obj.method()로 부르면 this가 obj이지만, 메서드를 떼어 콜백으로 넘기면 this를 잃는다. 특히 클래스 안은 엄격 모드라, 이렇게 this를 잃으면 undefined가 되어 그 안의 속성 접근에서 터진다. 이 글은 this가 결정되는 방식, 바인딩이 깨지는 원인, 그리고 화살표 함수와 bind로 고정하는 해결법을 정리한다.

this가 undefined

 

이 글의 구성

 

🔎this는 호출 방식으로 정해진다
⚖️this가 정해지는 4가지 방식
🧩this가 깨지는 원인 4가지
💻재현과 진단
🛠해결 — 화살표·bind로 고정
💬자주 묻는 질문 5가지

🔎 this는 호출 방식으로 정해진다

this를 이해하는 열쇠는 하나다. this는 함수가 정의된 위치가 아니라, 함수가 호출되는 방식에 따라 그때그때 정해진다는 것이다. 같은 함수라도 어떻게 부르느냐에 따라 this가 달라진다. 그래서 코드를 눈으로 볼 때가 아니라 "실제로 어떻게 호출됐는가"를 따져야 this를 알 수 있다.

가장 흔한 형태가 메서드 호출이다. obj.method()처럼 점 앞에 객체를 두고 부르면, 그 메서드 안의 this는 점 앞의 obj가 된다. 문제는 이 연결이 호출 순간에만 성립한다는 점이다. 메서드를 obj에서 떼어내 변수나 콜백에 담아 나중에 부르면, 그때는 점 앞에 객체가 없어 this가 원래 객체를 가리키지 못한다. 이게 this가 깨지는 대부분의 사연이다.

핵심 — 정의가 아니라 호출이 this를 정한다

this는 "어디에 쓰였는가"가 아니라 "어떻게 불렸는가"로 결정된다. obj.method()면 this는 obj, 그냥 method()면 this는 (엄격 모드에서) undefined다. 클래스 안은 엄격 모드라 this를 잃으면 undefined가 되고, 그 안의 this.something을 읽다 터진다. 그래서 "메서드를 떼어 넘겼는가"를 먼저 의심한다.


⚖️ this가 정해지는 4가지 방식

this가 무엇이 되는지는 호출 방식으로 갈린다. 네 가지만 알면 대부분 설명된다.

호출 방식 this는
메서드 호출 obj.method() 점 앞의 객체(obj)
일반 함수 호출 func() 엄격 모드면 undefined(아니면 전역)
화살표 함수 자신을 감싼 바깥 스코프의 this
명시적 호출 call·apply·bind 지정한 값

핵심은 화살표 함수가 다르다는 점이다. 일반 함수는 호출 방식에 따라 this가 매번 정해지지만, 화살표 함수는 자기만의 this를 갖지 않고 정의된 위치를 감싼 바깥 스코프의 this를 그대로 쓴다. 그래서 화살표 함수의 this는 "어떻게 호출됐는가"와 무관하게 고정된다. 이 성질이 this 문제를 푸는 열쇠가 된다. 반대로 일반 함수를 콜백으로 넘기면 그 안의 this는 호출 시점에 따라 바뀌어, 원래 객체를 잃기 쉽다.


🧩 this가 깨지는 원인 4가지

원인 1 — 메서드를 떼어 콜백으로 전달

가장 흔하다. setTimeout(obj.method, 100)처럼 메서드를 떼어 콜백으로 넘기면, 나중에 this가 obj를 잃는다. 결과는 호출 방식에 따라 갈린다. 순수한 일반 호출(const f = obj.method; f())이면 엄격 모드에서 this가 undefined가 되어 this.something 접근이 터지고, 브라우저의 setTimeout처럼 호스트가 부르면 this가 window로 넘어가 에러 없이 조용히 잘못 동작한다. 어느 쪽이든 원래 인스턴스를 못 가리킨다는 게 문제다.

원인 2 — 클래스 메서드를 이벤트 핸들러로

addEventListener("click", this.handleClick)처럼 클래스 메서드를 핸들러로 넘긴 경우다. 클릭 시 호출될 때 this가 클래스 인스턴스가 아니게 되어(엄격 모드라 undefined 등), 메서드 안의 this 접근이 깨진다.

원인 3 — 메서드 안의 일반 함수·콜백

메서드 안에서 배열의 forEach·map 등에 일반 함수를 콜백으로 넘긴 경우다. 그 콜백은 일반 함수 호출이라 this가 바깥 메서드의 객체가 아니게 된다. 콜백 안에서 this를 쓰면 원하는 값이 안 나온다.

원인 4 — 화살표 함수를 객체 메서드로 잘못 씀

반대 실수다. 객체 메서드를 화살표 함수로 정의하면, this가 그 객체가 아니라 바깥 스코프(대개 전역·undefined)를 가리킨다. 화살표는 this를 고정하려는 곳에 쓰는 것이지, 객체 메서드 자리엔 부적합하다.


💻 재현과 진단

전형적인 재현이다. 메서드를 떼어 콜백으로 넘기면 this가 손실된다.

// 메서드를 떼어 일반 호출 → this 손실
class Counter {
  count = 0;
  increment() { this.count++; }
}
const c = new Counter();
const inc = c.increment;  // 떼어냄
inc();                    // 일반 호출 → this = undefined
// ✗ Cannot read properties of undefined (reading 'count')

진단은 this가 무엇인지 찍어 보는 것이다. 문제의 함수 안에서 console.log(this)로 확인하면, 기대한 객체가 아니라 undefined나 엉뚱한 값이 나오는 걸 볼 수 있다. 그 함수가 "어떻게 호출됐는지"를 되짚으면 원인이 드러난다.

// this 정체 확인
increment() {
  console.log(this); // 인스턴스? undefined? 확인
  this.count++;
}

🛠 해결 — 화살표·bind로 고정

해결은 this를 원래 객체에 고정하는 것이다. 대표적으로 화살표 함수, bind, 클래스 필드 화살표 세 가지가 있다.

// ✓ 화살표로 감싸 호출 — this 유지
setTimeout(() => c.increment(), 100);

// ✓ bind 로 this 고정
setTimeout(c.increment.bind(c), 100);

// ✓ 클래스 필드 화살표 — 자동 바인딩
class Counter {
  count = 0;
  increment = () => { this.count++; }; // this 고정
}

상황별로 정리하면 이렇다. 원리는 하나다. this가 바뀔 만한 자리엔 화살표로 감싸거나 미리 bind해 원래 객체에 묶는다.

상황별 해결 방법

01콜백 전달 → () => obj.method()로 감싸거나 obj.method.bind(obj).
02클래스 핸들러 → 클래스 필드 화살표(handleClick = () => ...)로 자동 바인딩.
03메서드 안 콜백 → forEach·map 콜백을 화살표로 써서 바깥 this 유지.
04객체 메서드는 화살표 금지 → 일반 메서드 문법(method() {})으로.
05이벤트 핸들러에서 요소가 필요하면 this 대신 e.currentTarget 사용.

한 가지 덧붙이면, 이벤트 핸들러에서는 함수 종류에 따라 this가 다르다. addEventListener에 일반 함수를 넘기면 그 안의 this는 이벤트가 걸린 요소가 되지만, 화살표 함수를 넘기면 this가 바깥 스코프를 가리켜 요소가 아니다. 그래서 요소를 다뤄야 하면 화살표 대신 일반 함수를 쓰거나, 안전하게 e.currentTarget으로 요소에 접근하는 편이 낫다.


💬 자주 묻는 질문 5가지

Q1메서드는 맞는데 this.xxx is not a function이 떠요.

this가 원래 객체를 잃은 경우입니다. 메서드를 떼어 콜백·이벤트 핸들러로 넘기면 호출 시점에 this가 인스턴스가 아니게 되어, 그 안에서 부르는 다른 메서드를 못 찾죠. 화살표로 감싸거나 bind로 this를 고정하면 풀립니다.

Q2화살표 함수를 쓰면 다 해결되나요?

대개 편하지만 만능은 아닙니다. 화살표는 this를 바깥 스코프에 고정하므로 콜백·핸들러엔 좋지만, 객체 메서드나 프로토타입 메서드로 쓰면 this가 그 객체가 아니라 바깥을 가리켜 오히려 문제가 됩니다. "this를 고정하고 싶은 곳"에만 쓰세요.

Q3bind와 화살표 중 뭘 쓰나요?

둘 다 this를 고정하는 목적은 같습니다. 클래스 메서드를 핸들러로 자주 넘긴다면 클래스 필드 화살표(handleClick = () => ...)가 깔끔하고, 특정 호출 한 번에 this를 지정하려면 bind나 () => obj.method() 감싸기가 편합니다. 프로젝트 관습에 맞춰 일관되게 쓰면 됩니다.

Q4이벤트 핸들러에서 this가 요소가 아니에요.

화살표 함수를 핸들러로 넘겼을 가능성이 큽니다. addEventListener에 일반 함수를 넘기면 this가 이벤트가 걸린 요소지만, 화살표는 바깥 스코프의 this라 요소가 아닙니다. 요소가 필요하면 일반 함수를 쓰거나 e.currentTarget으로 접근하세요.

Q5왜 클래스에서만 this가 undefined가 되나요?

클래스 본문은 항상 엄격 모드이기 때문입니다. 엄격 모드에서 일반 함수로 호출된 함수의 this는 전역 객체가 아니라 undefined가 됩니다. 그래서 클래스 메서드를 떼어 일반 호출하면 this가 undefined가 되고, this.something 접근에서 바로 터지는 것입니다.


📌 결론

this가 undefined가 되는 문제의 뿌리는 하나다. this는 함수가 정의된 곳이 아니라 호출된 방식으로 정해진다는 것이다. obj.method()면 this는 obj지만, 메서드를 떼어 콜백·핸들러로 넘기면 그 연결이 끊겨 this를 잃는다. 클래스는 엄격 모드라 이때 this가 undefined가 된다.

 

실무 원칙은 다음과 같다.

🎯 호출 방식을 본다. this가 이상하면 "이 함수가 어떻게 호출됐는가"를 먼저 따진다. 메서드를 떼어 넘긴 곳이 대개 범인이다.

🏹 화살표로 this를 고정한다. 콜백·핸들러는 () => obj.method()나 클래스 필드 화살표로 감싸 바깥 this를 유지한다. 단 객체 메서드엔 화살표를 쓰지 않는다.

🔗 bind와 e.currentTarget을 활용한다. 명시적으로 this를 묶어야 하면 bind, 이벤트에서 요소가 필요하면 this 대신 e.currentTarget을 쓴다.

this 바인딩 체크리스트

 

01문제 함수 안에서 console.log(this)로 정체 확인.
02"어떻게 호출됐는가"(메서드 vs 콜백) 되짚기.
03콜백·핸들러는 () => obj.method()·bind로 this 고정.
04클래스 핸들러는 클래스 필드 화살표로 자동 바인딩.
05객체 메서드엔 화살표 쓰지 않기(this가 객체 아님).
06이벤트에서 요소는 this 대신 e.currentTarget으로.

본 글은 자바스크립트 this 바인딩 문제의 일반적 원인과 해결 방법을 정리한 자료다. 코드 수정은 영향 범위를 확인한 뒤 신중히 적용한다.

 

#JavaScript #this #this바인딩 #화살표함수 #arrowfunction #bind #자바스크립트에러 #콜백 #이벤트핸들러 #클래스 #엄격모드 #currentTarget #프론트엔드 #JS디버깅 #웹개발

반응형

댓글