화살표 함수와 this
일반 함수의 호출 방식에 따라 달라지는 this와 상위 스코프를 캡처하는 화살표 함수의 바인딩을 타입과 함께 구분합니다.
자바스크립트에서 this 키워드는
개발자가 가장 자주 헷갈리는 지점 중 하나입니다.
this 값이 함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다.
ES2015(ES6)의 화살표 함수(Arrow Functions)는
이 this 바인딩 규칙을 크게 바꿨고,
타입스크립트에서도 이 차이를 정확히 이해하는 것이 중요합니다.
이번 절에서는 화살표 함수의 기본적인 사용법을 다시 한번 살펴보고, 특히 this 바인딩 규칙이 어떻게 달라지는지, 그리고 이것이 타입스크립트에서 어떤 의미를 가지는지 자세히 알아보겠습니다.
this는 일반 함수에서는 호출 방식에 따라 달라지고, 화살표 함수에서는 바깥 스코프의 값을 이어받습니다.
- 화살표 함수 기본 문법 복습
화살표 함수는 function 키워드 없이 매개변수와 반환식을 직접 연결하며, 자체 this 바인딩을 만들지 않습니다. this 키워드
- this 바인딩의 차이점
this 키워드는 함수가 실행될 때 함수의 실행 컨텍스트(Execution Context)에 따라 결정됩니다. 호출 방식
- 타입스크립트에서 this 타입 명시하기
타입스크립트는 함수의 this가 어떤 타입을 가리킬지 명시적으로 지정해 메서드 분리나 콜백 전달 시 잘못된 호출을 컴파일 단계에서 잡게 합니다. 화살표 함수(Arrow Functions)
- 화살표 함수와 this 기준
정확성 자바스크립트에서 this 키워드는 개발자가 가장 자주 헷갈리는 지점 중 하나입니다. 비용 this 값이 함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다. 확장성 화살표 함수는 자신만의 this를 만들지 않아 바깥 스코프의 this를 그대로 사용합니다. 예외 이벤트 핸들러나 배열 콜백처럼 호출 주체가 바뀌는 위치에서는 화살표 함수가 바깥 this를 보존합니다.
- 정확성 자바스크립트
this 키워드는 개발자가 가장 자주 헷갈리는 지점 중 하나입니다.
- 비용 this 값
함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다.
- 확장성 화살표 함수
자신만의 this를 만들지 않아 바깥 스코프의 this를 그대로 사용합니다.
- 예외 이벤트 핸들러나 배열 콜백처럼 호출 주체
바뀌는 위치에서는 화살표 함수가 바깥 this를 보존합니다.
화살표 함수 기본 문법 복습
매개변수와 반환 타입 계약은 유지하되, this는 새로 만들지 않고 외부 lexical this를 따른다.
- paramsparams (x
number) 입력 타입을 명시하거나 추론
- returnreturn => string 필요하면 결
타입을 표시
- bodybody expr 또
block 표현식 본문은 즉시 반환
- thisthis lexical 바깥 this
캡처
| 상황 | 판단 |
|---|---|
| 짧은 계산 | 표현식 본문으로 가독성 향상 |
| 상태 접근 callback | 화살표 함수로 instance this 보존 |
| 주의 | 호출자가 this를 주는 API에는 일반 함수와 this 매개변수 검토 |
화살표 함수는 기존의 function 키워드 함수보다 더 간결한 문법으로 함수를 정의할 수 있게 해줍니다.
// 1. 기본 화살표 함수 (매개변수 2개, 반환 값 존재)
const add = (a: number, b: number): number => {
return a + b;
};
console.log(add(5, 3)); // 8
// 2. 단일 매개변수 (괄호 생략 가능)
const square = (num: number): number => num * num;
console.log(square(4)); // 16
// 3. 매개변수 없음 (빈 괄호 필요)
const sayHello = (): string => "안녕하세요!";
console.log(sayHello()); // 안녕하세요!
// 4. 객체 리터럴 반환 (괄호로 감싸야 함)
const createPoint = (x: number, y: number) => ({ x: x, y: y });
console.log(createPoint(1, 2)); // { x: 1, y: 2 }화살표 함수는 특히 콜백 함수나 짧은 함수를 정의할 때 유용하며, 코드를 훨씬 간결하고 읽기 쉽게 만듭니다.
this 바인딩의 차이점
this 키워드는 함수가 실행될 때 함수의 실행 컨텍스트(Execution Context)에 따라 결정됩니다.
이 this 바인딩 방식에서 화살표 함수와 일반 함수는 결정적인 차이를 보입니다.
-
일반 함수 (
function키워드 함수):this는 함수가 어떻게 호출되었는지(호출 방식)에 따라 동적으로 결정됩니다.- 객체의 메서드로 호출될 때:
this는 해당 객체를 가리킵니다. - 단독으로 호출될 때 (엄격 모드가 아닐 경우):
this는 전역 객체(브라우저에서는window, Node.js에서는global)를 가리킵니다. 엄격 모드('use strict')에서는undefined를 가리킵니다. new키워드와 함께 생성자 함수로 호출될 때:this는 새로 생성된 인스턴스를 가리킵니다.call(),apply(),bind()메서드를 사용할 때:this는 명시적으로 바인딩된 객체를 가리킵니다.
class TraditionalGreeter { greeting: string = "Hello"; sayGreeting(name: string) { console.log(`${this.greeting}, ${name}!`); } // 메서드를 콜백으로 전달할 때 문제가 발생할 수 있습니다. sayGreetingLater() { setTimeout(function() { // 여기서 this는 setTimeout에 의해 window (또는 undefined in strict mode)를 가리킵니다. // 따라서 this.greeting은 undefined가 됩니다. console.log(`${this.greeting}, world!`); }, 100); } } const tg = new TraditionalGreeter(); tg.sayGreeting("TypeScript"); // Hello, TypeScript! // tg.sayGreetingLater(); // Uncaught TypeError: Cannot read properties of undefined (reading 'greeting')sayGreetingLater메서드 내부의setTimeout콜백 함수에서this.greeting이undefined가 되는 것을 볼 수 있습니다.이는 콜백 함수가 독립적으로 호출되면서
this의 컨텍스트가 변경되었기 때문입니다. - 객체의 메서드로 호출될 때:
-
화살표 함수: 화살표 함수는 자신만의
this바인딩을 가지지 않습니다.대신, 함수가 선언된 스코프(Lexical Scope)의
this값을 상속(Lexical this) 받습니다.즉, 화살표 함수 내부의
this는 항상 자신을 감싸는(바로 바깥) 일반 함수 또는 전역 스코프의this와 동일합니다.class ArrowGreeter { greeting: string = "Hello"; sayGreetingLater() { setTimeout(() => { // 화살표 함수이므로, sayGreetingLater 메서드의 this를 상속받습니다. // 따라서 여기서 this는 ArrowGreeter 인스턴스를 가리킵니다. console.log(`${this.greeting}, world!`); }, 100); } } const ag = new ArrowGreeter(); ag.sayGreetingLater(); // 약 100ms 후: Hello, world!ArrowGreeter클래스의sayGreetingLater메서드 안에서 사용된 화살표 함수는sayGreetingLater메서드의this를 그대로 상속받습니다. 덕분에this.greeting이 올바르게Hello값을 참조할 수 있습니다. 이런this의 자동 바인딩 특성 때문에 화살표 함수는 이벤트 핸들러나 콜백 함수에서 특히 편리합니다.
아래 다이어그램은 콜백과 메서드에서 this를 어떻게 다룰지 선택하는 흐름을 보여줍니다.
인스턴스 상태가 필요하면 화살표 함수로 캡처하고, 호출자가 this를 주면 this 매개변수로 계약을 드러낸다.
| 질문 | 판정 |
|---|---|
| 콜백이 인스턴스 상태를 읽는가? | arrow property 또는 arrow callback으로 바깥 this 캡처 |
| 외부 API가 this를 제공하는가? | function (this: Context) 형태로 계약 명시 |
| 메서드를 값으로 넘기는가? | bind 또는 arrow property로 this 끊김 방지 |
| this가 필요 없는가? | 일반 callback으로 단순하게 유지 |
| 기준 | 해석 |
|---|---|
| 문제 신호 | const fn = obj.method; fn() 호출에서 this 소실 |
| 해결 | bind, arrow property, this parameter 중 API 성격에 맞게 선택 |
| 판단 | this는 타입보다 호출 방식의 문제까지 함께 본다 |
타입스크립트에서 this 타입 명시하기
타입스크립트는 함수의 this가 어떤 타입을 가리킬지 명시적으로 지정할 수 있는 기능을 제공합니다.
이는 특히 라이브러리를 만들거나 복잡한 콜백 패턴에서 this의 타입을 명확히 하고 싶을 때 유용합니다.
this 매개변수는 함수 매개변수 목록의 가장 첫 번째에 위치하며, 실제 자바스크립트 코드로는 컴파일되지 않습니다.
interface MyButton {
label: string;
onClick: (this: MyButton, event: Event) => void; // this가 MyButton 타입임을 명시
}
class ButtonHandler {
buttonLabel: string = "클릭 미";
// 이벤트 핸들러 함수
// 첫 번째 매개변수로 this: ButtonHandler를 명시하여
// 이 함수의 this가 ButtonHandler 인스턴스임을 알려줍니다.
handleClick(this: ButtonHandler, event: Event) {
console.log(`버튼 '${this.buttonLabel}'이 클릭되었습니다.`);
console.log(`이벤트 타입: ${event.type}`);
}
}
const handler = new ButtonHandler();
// 실제로 HTML 버튼에 연결되는 상황을 가정
// document.getElementById('myButton').addEventListener('click', handler.handleClick);
// 가상 호출:
// 만약 handler.handleClick이 버튼의 컨텍스트에서 호출되었다면 (DOM 이벤트)
// 이 예시에서는 this가 MyButton에 바인딩되지 않으므로 오류가 발생할 수 있습니다.
// 올바르게 this를 바인딩하여 호출하는 방법
const boundHandleClick = handler.handleClick.bind(handler);
// boundHandleClick(new Event('click')); // 이 경우 this는 handler 인스턴스
// ---
// 다른 예시: 객체 리터럴 내에서 this 타입 명시
interface Calculator {
value: number;
add(this: Calculator, num: number): void;
}
const calc: Calculator = {
value: 0,
add(num) { // 여기서 this는 Calculator 타입으로 추론됩니다.
this.value += num;
},
};
calc.add(10);
console.log(calc.value); // 10this: MyButton 또는 this: ButtonHandler와 같이 this 매개변수를 선언하면, 타입스크립트 컴파일러는 해당 함수가 호출될 때 this의 타입이 일치하는지 검사해줍니다.
이는 특히 this의 컨텍스트가 중요하거나 복잡한 상황에서 코드의 안정성을 크게 향상시킵니다.
화살표 함수와 this 타입 명시의 조합
화살표 함수는 기본적으로 렉시컬 this를 가지므로, 명시적인 this 타입 매개변수를 사용하는 경우는 일반 함수에 비해 드뭅니다.
하지만 인터페이스나 타입 별칭으로 함수의 시그니처를 정의할 때 화살표 함수 문법을 사용하면, 자연스럽게 this가 상위 스코프를 따르도록 유도할 수 있습니다.
interface Logger {
logLevel: string;
// 화살표 함수 문법으로 콜백 시그니처 정의
logMessage: (message: string) => void;
}
class MyLogger implements Logger {
logLevel: string = "INFO";
constructor() {
// 화살표 함수를 사용하여 this를 MyLogger 인스턴스에 바인딩
this.logMessage = (message: string) => {
console.log(`[${this.logLevel}] ${message}`);
};
}
}
const logger = new MyLogger();
logger.logMessage("데이터 로드 완료."); // [INFO] 데이터 로드 완료.
// 만약 MyLogger의 logMessage가 일반 함수였다면,
// constructor에서 this를 바인딩하는 과정이 필요했을 것입니다.
/*
class MyLoggerWithTraditionalFunction implements Logger {
logLevel: string = "INFO";
logMessage: (message: string) => void;
constructor() {
this.logMessage = function(message: string) {
console.log(`[${this.logLevel}] ${message}`); // 여기서 this는 MyLogger 인스턴스가 아닐 수 있음
}.bind(this); // 명시적으로 bind를 해줘야 합니다.
}
}
*/아래 다이어그램은 일반 함수의 동적 this, 화살표 함수의 렉시컬 this, 타입스크립트의 this 매개변수를 선택 기준별로 비교합니다.
일반 함수는 호출 방식이 this를 정하고, 화살표 함수는 선언된 스코프의 this를 붙잡는다. TypeScript의 this 매개변수는 런타임 값을 만들지 않고 호출 컨텍스트만 타입으로 제한한다.
- receiver가 필요한가
객체 상태를 읽지 않으면 this 자체를 제거한다.
- 콜백으로 넘기는가
나중에 실행되면 화살표 함수나 bind를 검토한다.
- 호출자가 this를 주는가
라이브러리 콜백이면 일반 함수와 this 타입을 맞춘다.
- 타입으로 잠글 것인가
실수 여지가 있으면 this 매개변수를 첫 위치에 둔다.
fn(this: Logger, message: string)
아래 다이어그램은 콜백을 넘길 때 this가 필요한지에 따라 일반 함수, 화살표 함수, 명시적 바인딩을 고르는 흐름입니다.
화살표 함수는 바깥 this를 유지하고, 일반 함수는 호출 방식의 this를 받습니다.
- 필요 없음
콜백 내부에서 this가 필요한가? 짧은 변환이면 화살표 함수로 간결하게 표현합니다. items.map(x => x.id) 클래스 필드나 메서드 내부 콜백은 화살표 함수가 안전합니다. setTimeout(() => this.run()) 라이브러리 콜백은 `this` 매개변수로 계약을 드러냅니다. function (this: Button)
- 필요 없음
짧은 변환이면 화살표 함수로 간결하게 표현합니다. items.map(x => x.id)
- 바깥 this 필요
클래스 필드나 메서드 내부 콜백은 화살표 함수가 안전합니다. setTimeout(() => this.run())
- 호출자 this 필요
라이브러리 콜백은 `this` 매개변수로 계약을 드러냅니다. function (this: Button)
화살표 함수 덕분에 this 바인딩에 대한 고민을 줄이고 더 간결하고 직관적인 코드를 작성할 수 있게 됩니다.
화살표 함수와 this의 관계는 자바스크립트 및 타입스크립트에서 매우 중요한 개념입니다.
화살표 함수가 렉시컬 this를 가진다는 점을 명확히 이해하면 콜백 함수나 비동기 코드에서 this 컨텍스트를 유지하는 데 큰 도움이 됩니다.
필요한 경우에는 타입스크립트의 this 타입 명시 기능을 활용하여 코드의 안정성을 더욱 높일 수 있습니다.
이로써 3장 함수와 타입에 대한 모든 내용을 마쳤습니다.
다음 다이어그램은 화살표 함수와 this 타입 명시를 콜백 설계 기준으로 정리한 표입니다.
이 다이어그램은 화살표 함수와 this 학습을 선언 위치, 타입 좁히기, 재사용 경계 순서로 마무리합니다.
아래 다이어그램은 화살표 함수와 this 바인딩 차이를 TypeScript 타입 명시와 함께 정리합니다.
화살표 함수는 자신만의 this를 만들지 않고 바깥 this를 사용하므로 메서드, 콜백, 클래스 필드에서 의미가 달라집니다.
- 일반 함수호출 방식에 따라 변경
일반 함수 객체 메서드, 단독 호출, bind 호출에 따라 this 값이 달라질 수 있습니다. function run()
- 화살표 함수외부 this 캡처
화살표 함수 콜백으로 전달해도 바깥 스코프의 this를 유지하는 특징이 있습니다. () => this.name
- this 타입명시적 문맥 선언
this 타입 일반 함수의 첫 매개변수 자리에 this 타입을 적어 잘못된 호출을 막습니다. function f(this: User)
- 클래스 필드콜백 전달에 유리
클래스 필드 이벤트 핸들러처럼 메서드를 넘길 때 this 손실을 줄일 수 있습니다. handle = () => {}
아래 다이어그램은 화살표 함수와 this을 실제 코드에 적용하기 전에 값의 모양, 타입 선언, 오류 확인 순서를 점검합니다.