화살표 함수와 this
우리는 6장 "자바스크립트 심화 I"의 this
장에서 자바스크립트의 this
키워드가 함수가 어떻게 호출되었는지에 따라 동적으로 바인딩되는 복잡한 특성을 가지고 있음을 학습했습니다. 이러한 this
의 예측 불가능한 동작은 많은 개발자들에게 혼란을 주었고, 특히 콜백 함수나 중첩된 함수에서 this
값을 유지하기 위해 bind
, call
, apply
또는 self = this
와 같은 번거로운 패턴을 사용해야 했습니다.
ES2015(ES6)에서 도입된 화살표 함수(Arrow Function) 는 이러한 this
바인딩 문제를 근본적으로 해결하는 동시에, 함수를 선언하는 문법을 훨씬 간결하게 만들어 주었습니다. 화살표 함수는 모던 자바스크립트 코드에서 거의 필수적으로 사용되는 기능이 되었으므로, 정확한 이해가 중요합니다.
이번 장에서는 화살표 함수의 기본 문법부터 시작하여, 가장 중요한 특징인 this
바인딩 방식의 차이점, 그리고 기존 함수와 화살표 함수의 사용 시나리오를 비교하며 깊이 있게 알아보겠습니다.
화살표 함수의 기본 문법
화살표 함수는 function
키워드 대신 화살표 =>
를 사용하여 함수를 정의하는 간결한 문법입니다.
기본 형태
매개변수 => 표현식 또는 { 함수 본문 }
// 기존 함수 표현식
const add = function(a, b) {
return a + b;
};
// 화살표 함수 (기본 형태)
const addArrow = (a, b) => a + b; // 중괄호와 return이 생략됨
console.log(addArrow(2, 3)); // 결과: 5
다양한 형태
-
매개변수가 하나일 때: 괄호를 생략할 수 있습니다.
const greet = name => `Hello, ${name}!`; console.log(greet("Alice")); // 결과: Hello, Alice!
-
매개변수가 없을 때: 빈 괄호를 사용해야 합니다.
const sayHi = () => "Hi there!"; console.log(sayHi()); // 결과: Hi there!
-
함수 본문이 여러 줄일 때: 중괄호를 사용하고 명시적으로
return
키워드를 사용해야 합니다.const calculateSum = (numbers) => { let sum = 0; for (let num of numbers) { sum += num; } return sum; }; console.log(calculateSum([1, 2, 3, 4, 5])); // 결과: 15
-
객체 리터럴을 반환할 때: 소괄호로 감싸야 합니다. (중괄호가 함수 본문으로 해석되는 것을 방지)
const createUser = (name, age) => ({ name: name, age: age }); console.log(createUser("Bob", 28)); // 결과: { name: 'Bob', age: 28 }
화살표 함수와 this
바인딩의 차이점
이것이 화살표 함수를 사용하는 가장 큰 이유이자 가장 중요한 특징입니다. 기존의 function
키워드로 선언된 함수는 호출 방식에 따라 this
가 동적으로 결정됩니다. 하지만 화살표 함수는 this
를 자신이 선언될 때의 상위 스코프(Lexical Scope)에서 가져옵니다. 즉, this
가 정적으로 바인딩됩니다.
기존 함수의 this
const person = {
name: "Minjun",
// 1. 메서드로서 호출: `.` 앞의 객체(person)가 `this`가 됨
sayHello: function() {
console.log(`Hello, my name is ${this.name}.`);
},
// 2. 중첩 함수에서 `this` 문제 발생
greetDelayed: function() {
// 기존 함수의 `this` (person)를 유지하기 위한 꼼수: `self = this`
const self = this;
setTimeout(function() { // setTimeout의 콜백 함수는 일반 함수이므로 `this`가 전역 객체(window)를 가리킴
console.log(`Delayed Hello, my name is ${self.name}. (using self)`);
// console.log(`Delayed Hello, my name is ${this.name}. (using this - WRONG!)`);
}, 500);
}
};
person.sayHello(); // 결과: Hello, my name is Minjun.
person.greetDelayed(); // 결과: Delayed Hello, my name is Minjun. (using self)
// WRONG! 줄은 undefined가 출력되거나 에러 발생
위 예시에서 setTimeout
내부의 function() { ... }
은 일반 함수이므로 this
가 window
(브라우저) 또는 undefined
(엄격 모드)를 가리킵니다. 그래서 this.name
은 제대로 된 값을 가져오지 못합니다.
화살표 함수의 this
화살표 함수는 자신이 정의될 때의 상위 스코프의 this
를 그대로 가져옵니다. 절대 자기 자신만의 this
바인딩을 생성하지 않습니다.
const personArrow = {
name: "Seulgi",
sayHello: () => { // 화살표 함수가 최상위 스코프에 정의되면 `this`는 `window`를 가리킴
console.log(`Hello, my name is ${this.name}. (WRONG! - window or undefined)`);
},
greetDelayed: function() { // 이 함수는 일반 함수이므로 `this`는 `personArrow`를 가리킴
// 화살표 함수는 자신의 상위 스코프(greetDelayed)의 `this`를 가져옴
setTimeout(() => {
console.log(`Delayed Hello, my name is ${this.name}. (using arrow function)`);
}, 500);
}
};
personArrow.sayHello(); // 결과: Hello, my name is . (window.name이 없거나 undefined)
// 주의: 객체의 메서드로 최상위 화살표 함수 사용은 권장되지 않음
personArrow.greetDelayed(); // 결과: Delayed Hello, my name is Seulgi. (using arrow function)
personArrow.greetDelayed
내부의 setTimeout
콜백은 화살표 함수로 정의되었습니다. 이 화살표 함수는 자신이 선언된 greetDelayed
함수 스코프의 this
값을 그대로 가져옵니다. greetDelayed
는 personArrow
객체의 메서드로 호출되었으므로, greetDelayed
내부의 this
는 personArrow
를 가리킵니다. 따라서 화살표 함수 내부의 this
도 personArrow
를 가리키게 되어 원하는 대로 동작합니다.
핵심: 화살표 함수는 this
바인딩에 대한 문제를 해결해주는 강력한 도구입니다. 특히 콜백 함수에서 this
컨텍스트를 유지해야 할 때 유용합니다.
화살표 함수를 사용하지 말아야 할 경우
화살표 함수는 매우 편리하지만, 모든 상황에서 기존 함수를 대체할 수 있는 것은 아닙니다. this
바인딩 방식의 차이 때문에 특정 상황에서는 기존 함수를 사용해야 합니다.
-
객체의 메서드를 정의할 때 (최상위 메서드): 위
personArrow.sayHello()
예시에서 보았듯이, 객체 리터럴의 최상위 메서드를 화살표 함수로 정의하면this
가 객체 자신이 아닌 전역 객체를 가리키게 되어 의도치 않은 결과를 초래합니다.const myObject = { value: 10, // BAD: 화살표 함수는 this가 상위 스코프 (window/global)를 가리킴 getValueArrow: () => { console.log(this.value); // undefined (또는 window.value) }, // GOOD: 일반 함수는 `.` 호출 시 myObject를 this로 바인딩 getValueNormal: function() { console.log(this.value); // 10 } }; myObject.getValueArrow(); myObject.getValueNormal();
객체의 메서드는 항상 일반 함수(또는 ES6의 메서드 축약형
method() { ... }
)로 정의해야 합니다. -
생성자 함수 (Constructor): 화살표 함수는
prototype
프로퍼티를 가지지 않으며,new
키워드와 함께 호출될 수 없습니다.new
와 함께 호출하면TypeError
가 발생합니다.// BAD: 화살표 함수는 생성자로 사용할 수 없음 // const MyClass = () => { this.value = 1; }; // const instance = new MyClass(); // TypeError: MyClass is not a constructor // GOOD: 생성자는 일반 함수 또는 class 키워드로 정의 function MyClass(value) { this.value = value; } const instance = new MyClass(10);
-
arguments
객체에 접근해야 할 때: 화살표 함수는arguments
객체를 바인딩하지 않습니다. 대신 상위 스코프의arguments
를 참조합니다. 기존 함수는 자신에게 전달된 인자들을arguments
객체로 접근할 수 있습니다. (ES6에서는...rest
매개변수 사용이 더 권장되긴 합니다.)function sumAllNormal() { console.log(arguments); // [1, 2, 3] return Array.from(arguments).reduce((a, b) => a + b); } console.log(sumAllNormal(1, 2, 3)); // 결과: 6 const sumAllArrow = () => { // console.log(arguments); // ReferenceError: arguments is not defined (상위 스코프에 arguments 없으면) // 화살표 함수에서는 ...rest 매개변수를 사용해야 함 // return [...arguments].reduce((a, b) => a + b); // Error! }; // console.log(sumAllArrow(1, 2, 3));
-
addEventListener
등 이벤트 핸들러: 특정 DOM 요소에 이벤트 리스너를 붙일 때,this
는 기본적으로 이벤트를 발생시킨 요소를 가리킵니다. 화살표 함수를 사용하면 이this
바인딩이 깨져 의도치 않은 결과를 초래할 수 있습니다.<button id="myButton">클릭하세요</button> <script> const button = document.getElementById('myButton'); // GOOD: 일반 함수는 this가 버튼 요소 자체를 가리킴 button.addEventListener('click', function() { console.log("일반 함수 this:", this.id); // 결과: myButton }); // BAD: 화살표 함수는 this가 상위 스코프 (window)를 가리킴 button.addEventListener('click', () => { console.log("화살표 함수 this:", this.id); // 결과: undefined (window.id는 없음) }); </script>
마무리하며
이번 장에서는 ES2015(ES6)의 핵심 기능인 화살표 함수(Arrow Function) 의 간결한 문법과, 특히 개발자들을 혼란스럽게 했던 this
바인딩 문제를 어떻게 해결하는지에 대해 심도 있게 학습했습니다.
여러분은 화살표 함수가 자신이 선언될 때의 상위 스코프 this
를 그대로 가져오는 렉시컬 this
바인딩 특성을 이해했습니다. 이는 콜백 함수와 중첩 함수에서 this
컨텍스트를 유지해야 할 때 bind
나 self = this
와 같은 번거로운 작업 없이 코드를 더욱 간결하고 예측 가능하게 작성할 수 있도록 해줍니다.
하지만 화살표 함수가 모든 상황에서 기존 함수의 대안이 될 수는 없다는 점도 중요하게 다루었습니다. 객체의 메서드, 생성자 함수, arguments
객체에 접근해야 할 때, 그리고 이벤트 리스너의 this
바인딩이 중요한 경우에는 여전히 기존 함수(또는 class
문법의 메서드)를 사용해야 합니다.
화살표 함수는 모던 자바스크립트 코드에서 거의 모든 곳에서 사용될 정도로 보편화되었습니다. 이 장의 내용을 통해 여러분은 화살표 함수의 강력함과 한계를 명확히 이해하고, 코드의 가독성과 안정성을 높이는 데 올바르게 활용할 수 있을 것입니다.