본문으로 건너뛰기

안동민 개발노트

본문 시작

화살표 함수와 this

일반 함수의 호출 방식에 따라 달라지는 this와 상위 스코프를 캡처하는 화살표 함수의 바인딩을 타입과 함께 구분합니다.

자바스크립트에서 this 키워드는 개발자가 가장 자주 헷갈리는 지점 중 하나입니다.

this 값이 함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다.

ES2015(ES6)의 화살표 함수(Arrow Functions)는 이 this 바인딩 규칙을 크게 바꿨고, 타입스크립트에서도 이 차이를 정확히 이해하는 것이 중요합니다.

이번 절에서는 화살표 함수의 기본적인 사용법을 다시 한번 살펴보고, 특히 this 바인딩 규칙이 어떻게 달라지는지, 그리고 이것이 타입스크립트에서 어떤 의미를 가지는지 자세히 알아보겠습니다.

화살표 함수와 this

this는 일반 함수에서는 호출 방식에 따라 달라지고, 화살표 함수에서는 바깥 스코프의 값을 이어받습니다.

  1. 화살표 함수 기본 문법 복습

    화살표 함수는 function 키워드 없이 매개변수와 반환식을 직접 연결하며, 자체 this 바인딩을 만들지 않습니다. this 키워드

  2. this 바인딩의 차이점

    this 키워드는 함수가 실행될 때 함수의 실행 컨텍스트(Execution Context)에 따라 결정됩니다. 호출 방식

  3. 타입스크립트에서 this 타입 명시하기

    타입스크립트는 함수의 this가 어떤 타입을 가리킬지 명시적으로 지정해 메서드 분리나 콜백 전달 시 잘못된 호출을 컴파일 단계에서 잡게 합니다. 화살표 함수(Arrow Functions)

  4. 화살표 함수와 this 기준

    정확성 자바스크립트에서 this 키워드는 개발자가 가장 자주 헷갈리는 지점 중 하나입니다. 비용 this 값이 함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다. 확장성 화살표 함수는 자신만의 this를 만들지 않아 바깥 스코프의 this를 그대로 사용합니다. 예외 이벤트 핸들러나 배열 콜백처럼 호출 주체가 바뀌는 위치에서는 화살표 함수가 바깥 this를 보존합니다.

  5. 정확성 자바스크립트

    this 키워드는 개발자가 가장 자주 헷갈리는 지점 중 하나입니다.

  6. 비용 this 값

    함수 선언이 아니라 호출 방식에 따라 동적으로 결정되기 때문입니다.

  7. 확장성 화살표 함수

    자신만의 this를 만들지 않아 바깥 스코프의 this를 그대로 사용합니다.

  8. 예외 이벤트 핸들러나 배열 콜백처럼 호출 주체

    바뀌는 위치에서는 화살표 함수가 바깥 this를 보존합니다.


화살표 함수 기본 문법 복습

화살표 함수는 짧은 문법과 바깥 this 캡처를 함께 가진다

매개변수와 반환 타입 계약은 유지하되, this는 새로 만들지 않고 외부 lexical this를 따른다.

  1. params
    params (x

    number) 입력 타입을 명시하거나 추론

  2. return
    return => string 필요하면 결

    타입을 표시

  3. body
    body expr 또

    block 표현식 본문은 즉시 반환

  4. this
    this 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.greetingundefined가 되는 것을 볼 수 있습니다.

    이는 콜백 함수가 독립적으로 호출되면서 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를 주면 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); // 10

this: 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 매개변수는 런타임 값을 만들지 않고 호출 컨텍스트만 타입으로 제한한다.

  1. receiver가 필요한가

    객체 상태를 읽지 않으면 this 자체를 제거한다.

  2. 콜백으로 넘기는가

    나중에 실행되면 화살표 함수나 bind를 검토한다.

  3. 호출자가 this를 주는가

    라이브러리 콜백이면 일반 함수와 this 타입을 맞춘다.

  4. 타입으로 잠글 것인가

    실수 여지가 있으면 this 매개변수를 첫 위치에 둔다.

fn(this: Logger, message: string)

아래 다이어그램은 콜백을 넘길 때 this가 필요한지에 따라 일반 함수, 화살표 함수, 명시적 바인딩을 고르는 흐름입니다.

콜백을 넘기기 전 this 의존도 우선 확인

화살표 함수는 바깥 this를 유지하고, 일반 함수는 호출 방식의 this를 받습니다.

  1. 필요 없음

    콜백 내부에서 this가 필요한가? 짧은 변환이면 화살표 함수로 간결하게 표현합니다. items.map(x => x.id) 클래스 필드나 메서드 내부 콜백은 화살표 함수가 안전합니다. setTimeout(() => this.run()) 라이브러리 콜백은 `this` 매개변수로 계약을 드러냅니다. function (this: Button)

  2. 필요 없음

    짧은 변환이면 화살표 함수로 간결하게 표현합니다. items.map(x => x.id)

  3. 바깥 this 필요

    클래스 필드나 메서드 내부 콜백은 화살표 함수가 안전합니다. setTimeout(() => this.run())

  4. 호출자 this 필요

    라이브러리 콜백은 `this` 매개변수로 계약을 드러냅니다. function (this: Button)

화살표 함수 덕분에 this 바인딩에 대한 고민을 줄이고 더 간결하고 직관적인 코드를 작성할 수 있게 됩니다.


화살표 함수와 this의 관계는 자바스크립트 및 타입스크립트에서 매우 중요한 개념입니다.

화살표 함수가 렉시컬 this를 가진다는 점을 명확히 이해하면 콜백 함수나 비동기 코드에서 this 컨텍스트를 유지하는 데 큰 도움이 됩니다.

필요한 경우에는 타입스크립트의 this 타입 명시 기능을 활용하여 코드의 안정성을 더욱 높일 수 있습니다.

이로써 3장 함수와 타입에 대한 모든 내용을 마쳤습니다.


다음 다이어그램은 화살표 함수와 this 타입 명시를 콜백 설계 기준으로 정리한 표입니다.

화살표 함수는 this를 새로 만들지 않습니다

일반 함수와 화살표 함수의 this 바인딩 차이를 알면 메서드, 콜백, 클래스 필드에서 타입 오류를 줄일 수 있습니다.

  1. 호출자가 결정

    일반 함수 function 선언의 this는 호출 방식에 따라 달라질 수 있어 명시가 필요합니다. this: User

  2. 바깥값 캡처

    화살표 함수 화살표 함수는 주변 스코프의 this를 사용해 콜백에서 문맥을 유지합니다. () => value

  3. 동적 바인딩

    메서드 설계 객체 메서드로 this를 쓰는 경우 일반 메서드 문법이 의도를 더 잘 드러냅니다. method()

  4. 문맥 보존

    콜백 안정성 이벤트 핸들러나 비동기 콜백에서는 화살표 함수가 this 손실을 줄입니다. handler = () =>

이 다이어그램은 화살표 함수와 this 학습을 선언 위치, 타입 좁히기, 재사용 경계 순서로 마무리합니다.

화살표 함수 this 경계

일반 함수의 this는 호출 방식에 따라 바뀌지만 화살표 함수는 바깥 this를 사용하므로 콜백과 메서드 설계에서 차이가 큽니다.

  1. 호출자가 this 결정

    일반 함수 메서드로 부르느냐 단독 호출하느냐에 따라 this 값이 달라질 수 있습니다. function

  2. 외부 this 유지

    화살표 콜백에서 주변 객체의 this를 유지해야 할 때 화살표 함수가 안전합니다. () =>

  3. this 매개변수

    명시 타입 일반 함수는 this 타입을 첫 매개변수처럼 선언해 잘못된 호출을 막을 수 있습니다. this: User

  4. 메서드와 콜백 분리

    조합 기준 객체 동작은 메서드로, 전달용 콜백은 화살표로 두면 의도가 더 선명합니다. callback

아래 다이어그램은 화살표 함수와 this 바인딩 차이를 TypeScript 타입 명시와 함께 정리합니다.

화살표 함수와 this 판단

화살표 함수는 자신만의 this를 만들지 않고 바깥 this를 사용하므로 메서드, 콜백, 클래스 필드에서 의미가 달라집니다.

  1. 일반 함수
    호출 방식에 따라 변경

    일반 함수 객체 메서드, 단독 호출, bind 호출에 따라 this 값이 달라질 수 있습니다. function run()

  2. 화살표 함수
    외부 this 캡처

    화살표 함수 콜백으로 전달해도 바깥 스코프의 this를 유지하는 특징이 있습니다. () => this.name

  3. this 타입
    명시적 문맥 선언

    this 타입 일반 함수의 첫 매개변수 자리에 this 타입을 적어 잘못된 호출을 막습니다. function f(this: User)

  4. 클래스 필드
    콜백 전달에 유리

    클래스 필드 이벤트 핸들러처럼 메서드를 넘길 때 this 손실을 줄일 수 있습니다. handle = () => {}

아래 다이어그램은 화살표 함수와 this을 실제 코드에 적용하기 전에 값의 모양, 타입 선언, 오류 확인 순서를 점검합니다.