자바스크립트를 배우다 보면, 종종 this라는 키워드를 마주치게 되는데요. 언뜻 보기에는 단순히 영단어 ‘this’의 뜻과 비슷하게 현재의 문맥이나 맥락을 나타내는 것처럼 보입니다. 하지만 막상 사용하려고 하면 생각보다 복잡하다는 것을 깨닫게 되죠. 그렇다고 너무 걱정할 필요는 없습니다. this 키워드는 그 원리를 한 번 제대로 이해한다면, 다양한 상황에서도 쉽게 그 동작을 예측하고 활용할 수 있는 강력한 도구가 됩니다. <출처: DALL-E 생성> this는 마치 한 마리의 카멜레온과도 같습니다. 카멜레온이 주변 환경에 따라 색을 바꾸듯, this도 ‘함수가 어디에서 호출되었는지’, ‘어떤 문맥에서 실행되는지’에 따라 다른 값을 가지게 됩니다. 이 특성 때문에 초보 개발자뿐만 아니라, 많은 개발자에게도 종종 혼란을 안겨주곤 합니다. 예를 들어, 같은 this가 함수 안에서는 전역 객체를 가리키다가도, 메서드 안에서는 해당 객체를 가리키고, 콜백 함수 안에서는 또 다른 값을 가리킵니다. 그렇다면, 이러한 this의 특성은 무엇을 위해 만들어진 것일까요? 자바스크립트에서 this는 다양한 상황에서 유연하게 동작하도록 만들어졌습니다. this의 유연성을 이해하고 잘 활용한다면, 코드를 더욱 직관적이고 간결하게 작성할 수 있게 됩니다. 이번 글에서는 함수의 호출 방식에 따라 this가 어떻게 변화하는지 체계적으로 살펴보겠습니다. 전역 공간에서의 호출부터, 일반 함수와 메서드, 생성자 함수, 콜백 함수, 그리고 call, apply, bind 메서드를 사용한 예제까지 모두 다뤄보겠습니다. 전역 공간에서의 호출자바스크립트에서 this는 코드가 실행되는 문맥에 따라 달라지는데요, 그중 가장 기본적인 경우는 전역 공간에서의 호출입니다. 전역 공간에서는 this가 기본적으로 전역 객체(Global Object)를 참조합니다. 브라우저 환경에서는 this가 전역 객체인 window 객체를 가리키며, Node.js 환경에서는 global 객체를 참조합니다. console.log(this); // 브라우저 환경 : window 객체 출력 // Node.js 환경 : global 객체 출력 일반 함수에서의 호출일반 함수에서의 this는 함수가 호출된 방식에 따라 값이 달라집니다. 일반 함수에서의 호출은 가장 기본적인 사례 중 하나로, 기본적으로는 전역 공간에서의 호출과 동일하게 전역 객체를 참조합니다. function regularFunction() { console.log(this); } regularFunction(); // 브라우저 환경: window 객체 출력 // Node.js 환경: global 객체 출력 위의 코드에서 일반 함수인 regularFunction은 결론적으로 전역 공간에서 호출되었기 때문에, this는 브라우저에서는 window 객체를, Node.js에서는 global 객체를 참조합니다. 메서드에서의 호출자바스크립트에서 this는, 함수가 호출되는 방식에 따라 값이 달라지며, 메서드 내부에서는 호출한 객체에 따라 달라지는 특징을 보입니다. 이번에는 메서드 내부에서의 this가 어떤 값을 가리키는지 자세하게 살펴보겠습니다. 1) 메서드란?메서드는 객체의 속성으로 정의된 함수입니다. 다시 말해, 객체 프로퍼티에 저장된 함수라고 할 수 있습니다. 메서드는 일반적으로 객체의 데이터를 처리하거나 특정 동작을 수행하기 위해 사용되며, 다음과 같이 선언할 수 있습니다. const user = { name: ‘홍길동’, greet: function () { console.log(`안녕하세요, 홍길동입니다!`); } }; user.greet(); // 안녕하세요, 홍길동입니다! 위의 코드에서 greet는 user 객체의 메서드로, user.greet() 로 메서들을 호출하면, ‘안녕하세요, 홍길동입니다!’가 출력됩니다. 2) 메서드 내부에서의 this그렇다면, 메서드 내부에 this를 사용하면 어떨까요? 아래 예제를 통해 확인해 봅시다. const user = { name: ‘홍길동’, greet: function () { console.log(this); } }; user.greet(); // 출력: { name: '홍길동', greet: [Function: greet] } 위의 코드에서 this는 greet 메서드를 호출한 객체인 user를 가리킵니다. 자바스크립트는 메서드가 호출될 때, 해당 호출한 객체를 this에 자동으로 바인딩합니다. 즉, user.greet()는 greet 메서드가 user 객체에 의해 호출되었기 때문에, this는 user 객체를 가리키는 것입니다. 이처럼 메서드 내부에서 this는 호출된 객체에 따라 달라지므로, 메서드 호출 방식이 this의 동작을 결정짓는 핵심 요소라고 할 수 있습니다. 그럼 이번에는 아래와 같은 코드를 통해 지금까지 배운 내용을 테스트해 보겠습니다. const user = { name: ‘홍길동’, greet: function () { console.log(this); } }; const greetFunction = user.greet; greetFunction(); 위의 코드를 실행했을 때, 이번에도 this는 user 객체를 가리킬까요? <출처: 작가> 아닙니다. 실제로 위의 코드를 실행하면 this는 더 이상 user 객체를 가리키지 않습니다. 대신 전역 객체(window 또는 global)를 가리키게 되죠. 그 이유는 this가 포함된 greet 메서드가 user 객체에 의해 호출된 것이 아니라, 이번에는 전역 공간에서 greetFunction에 의해 호출되었기 때문입니다. 이처럼 메서드가 자신을 포함하고 있는 객체에 의해 직접적으로 호출되지 않고, 독립적으로 실행되는 경우, this는 다른 값을 참조하게 됩니다. 이러한 상황에서 this를 원하는 값에 고정시키기 위해서는 call, apply, bind와 같은 메서드들을 사용하는 방법을 사용할 수 있는데요. 이 메서드들에 대해서는 조금 나중에 더 자세하게 배워보겠습니다. 생성자 함수에서의 호출이제 this가 생성자 함수에서 어떻게 동작하는지 살펴보겠습니다. 생성자 함수는 객체를 생성하기 위해 사용되는 자바스크립트의 특별한 함수 유형으로, new라는 키워드와 함께 사용되며, 호출 시 다음과 같은 일들을 처리합니다. 새로운 빈 객체를 생성합니다. new 키워드를 사용하면 자바스크립트가 자동으로 새로운 빈 객체를 생성합니다. 이 객체는 이후 우리가 필요한 데이터를 담게 될 그릇과도 같습니다.this가 새로 생성된 객체를 가리키도록 설정합니다. 생성자 함수 내부에서 this는 우리가 방금 만든 빈 객체를 가리키게 됩니다. 이렇게 하면 생성자 함수 내부에서 this를 통해 객체의 속성을 추가하거나 수정할 수 있습니다.함수의 실행이 끝난 이후, 자동으로 새 객체를 반환합니다. 생성자 함수는 우리가 명시적으로 다른 객체를 반환하지 않는 한, this가 가리키고 있는 새 객체를 자동으로 반환합니다. 따라서 추가적인 반환 작업을 하지 않아도 새로운 객체를 얻을 수 있습니다. 1) 생성자 함수란?생성자 함수는 객체를 생성하는 데 사용되는 특별한 함수로, 일반적으로 생성자 함수의 이름은 첫 글자를 대문자로 작성하며, new 키워드와 함께 호출됩니다. 생성자 함수는 유사한 구조의 객체를 반복적으로 생성해야 할 때 사용하면 유용합니다. 예를 들어, 여러 사용자의 정보를 관리해야 하는 애플리케이션에서는 사용자의 이름, 나이 등의 정보를 담고 있는 객체를 만들어야 하는 경우, 생성자 함수를 사용하면 중복되는 작업을 줄이고 코드의 가독성을 높일 수 있습니다. function User(name, age) { this.name = name; // 새로운 객체의 name 프로퍼티 설정 this.age = age; // 새로운 객체의 age 프로퍼티 설정 } const user1 = new User('홍길동', 25); const user2 = new User('김철수', 30); console.log(user1); // 출력: User { name: '홍길동', age: 25 } console.log(user2); // 출력: User { name: ‘김철수’, age: 30 } 위의 예제처럼, 생성자 함수를 사용하면 비슷한 구조의 객체를 빠르고 일관되게 생성할 수 있습니다. 또한 생성자 함수 내부에 공통 로직을 작성하면 모든 객체에서 일관된 초기 설정을 적용할 수 있어 유지 보수에도 유리합니다. 2) 생성자 함수 내부에서의 this생성자 함수 내부에서 this는 특별한 역할은 합니다. new 키워드와 함께 생성자 함수를 호출하면, this는 자동으로 새로 생성된 객체를 참조하게 되어, 생성자 함수가 객체의 초기 상태를 설정하는 데 매우 유용합니다. 예제를 통해 this가 어떻게 동작하는지 살펴보겠습니다. 다시 코드를 가져와 보겠습니다. function User(name, age) { this.name = name; // 새로운 객체의 name 프로퍼티 설정 this.age = age; // 새로운 객체의 age 프로퍼티 설정 } const user1 = new User('홍길동', 25); const user2 = new User('김철수', 30); console.log(user1.name); // 출력: 홍길동 console.log(user2.age); // 출력: 30 위의 코드에서 new User(‘홍길동’, 25)가 호출되면, 다음과 같은 과정이 진행됩니다. 우선 새로운 빈 객체가 생성되고, this는 새로 생성된 객체를 가리킵니다. 그 다음 this.name과 this.age를 통해 새로운 객체의 속성이 설정되며, 생성자 함수가 완료된 이후 this가 자동으로 반환됩니다. 결론적으로 user1과 user2는 각각 홍길동과 김철수의 정보를 가진 객체로 생성되어, user1.name의 출력값은 ‘홍길동’, user2.age의 출력값은 30이 됩니다. 콜백 함수에서의 호출이번에는 콜백 함수 내부에서 this가 어떤 값을 가지는지 살펴보겠습니다. 콜백 함수는 자바스크립트에서 매우 자주 사용되며, this의 동작을 이해하는 데 중요한 개념입니다. 1) 콜백 함수란?콜백 함수는 다른 함수에 인수로 전달되어 실행되는 함수를 말합니다. 즉, 특정 작업이 완료된 후 실행되거나, 이벤트가 발생했을 때 호출되는 함수입니다. 다음은 콜백 함수의 간단한 예제입니다. function greet(callback) { console.log("안녕하세요!"); callback(); } function sayGoodbye() { console.log("안녕히 가세요!"); } greet(sayGoodbye); // 출력: // 안녕하세요! // 안녕히 가세요! 위의 코드에서 sayGoodbye가 바로 greet 함수에 전달된 콜백 함수입니다. greet 함수 내부에서 callback()이 호출되면서, 전달된 콜백 함수가 실행됩니다. 2) 콜백 함수 내부에서의 this콜백 함수 내부의 this 또한, 자신을 포함하고 있는 함수가 어떻게 호출되었는지에 따라 값이 달라집니다. 특히 콜백 함수는 전역 객체를 가리키거나, 호출한 객체를 가리키는 등 상황에 따라 동작이 달라지기 때문에 주의해서 사용해야 하는데요. 아래의 예제 코드를 통해 조금 더 구체적으로 살펴보겠습니다. const user = { name: '홍길동', greet: function () { console.log(`안녕하세요, ${this.name}입니다.`); } }; function executeCallback(callback) { callback(); } executeCallback(user.greet); // 출력: 안녕하세요, undefined입니다. 위의 코드에서 user.greet는 원래 user 객체의 메서드입니다. 하지만 executeCallback 함수에 전달된 이후, user.greet는 독립적인 함수로 전역 공간에서 호출되고 있기 때문에, 더 이상 user 객체를 참조하지 않습니다. 대신, 일반 함수처럼 동작하며 this는 기본적으로 전역 객체(window 또는 global)를 가리키게 됩니다. <출처: 작가> 이처럼 콜백 함수가 독립적으로 실행될 경우에 this는 전역 객체를 참조하게 됩니다. 콜백 함수의 this를 고정하려면 bind, call, apply와 같은 메서드를 사용해야 하는데, 이어서 살펴보겠습니다. this를 고정시키는 방법콜백 함수에서 this가 예상치 못한 값을 참조하게 되는 문제를 해결하기 위해, 자바스크립트는 this를 고정시키는 여러 가지 방법을 제공합니다. 이를 활용하면 콜백 함수가 원래 의도했던 객체를 게속 참조하도록 만들 수 있습니다. 1) bind 메서드bind 메서드는 함수의 this 값을 영구적으로 고정할 수 있는 방법입니다. bind는 새로운 함수를 반환하며, 반환된 함수는 항상 특정 객체를 this로 사용합니다. 예제 코드를 통해 살펴보겠습니다. const user = { name: '홍길동', greet: function () { console.log(`안녕하세요, ${this.name}입니다.`); } }; function executeCallback(callback) { callback(); } // this를 고정 const boundGreet = user.greet.bind(user); executeCallback(boundGreet); // 출력: 안녕하세요, 홍길동입니다. 위의 코드에서 user.greet.bind(user)라는 코드를 통해 this가 항상 user 객체를 참조하도록 고정해 보았습니다. 이후 this가 executeCallback 함수에서 호출되더라도, this는 의도한 대로 user 객체를 참조해 ‘안녕하세요, 홍길동입니다.’라는 문장이 알맞게 출력됩니다. 2) call, apply 메서드bind 메서드 외에도 this를 원하는 값으로 설정할 수 있는 메서드로 call과 apply가 있습니다. 이 두 메서드는 함수를 즉시 실행하면서, this를 특정 객체로 설정할 수 있도록 도와줍니다. call과 apply의 차이는 인수를 전달하는 방식에 있습니다. call은 인수를 개별적으로 전달하고, apply는 배열 형태로 전달한다는 차이점이 있는데요. 예제를 통해 살펴봅시다. const user = { name: '홍길동', greet: function (greeting) { console.log(`${greeting}, ${this.name}입니다.`); } }; // call을 사용한 예제 user.greet.call({ name: '김철수' }, '안녕하세요'); // 출력: 안녕하세요, 김철수입니다. // apply를 사용한 예제 user.greet.apply({ name: '이영희' }, ['반갑습니다']); // 출력: 반갑습니다, 이영희입니다. 위의 코드에서는 call과 apply를 사용해 this를 각각 { name: ‘김철수’ }와 { name: ‘이영희’ }로 설정을 해주었는데요, 이처럼 call 메서드는 첫 번째 인수로 this가 가리키는 값을 설정하고, 나머지 인수는 함수에 전달하는 반면, apply는 동일한 방식으로 동작하지만, 추가 인수를 배열 형태로 전달한다는 차이점이 있습니다. call, apply, bind 메서드 비교 이처럼 call과 apply를 사용하면 함수의 this를 유연하게 조작할 수 있어, 특정 상황에서 유용하게 활용할 수 있습니다. 마치며자바스크립트에서 this는 마치 한 마리의 카멜레온처럼 상황에 따라 참조 대상을 바꾸는 유연한 도구입니다. 이번 글에서 살펴본 것처럼, this는 호출 방식에 따라 다른 대상을 가리키며, 이를 이해하고 활용하는 것이 자바스크립트를 잘 다루는 첫걸음입니다. this를 능숙하게 다루는 것은 단순히 코드를 작성하는 것을 넘어, 더욱 직관적이고 유지 보수하기 쉬운 코드를 만드는 데 핵심적인 역할을 합니다. 이번 글을 통해 다양한 호출 방식에서 this를 능숙하게 다룰 수 있는 개발자가 될 수 있길 바랍니다.<참고>https://ko.javascript.info/object-methodshttps://inf.run/WfRmYhttps://poiemaweb.com/js-this ©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.