2021-08-18 this

2021-08-18
  • JavaScript

What is ‘this’?

this는 일반적으로 객체지향 언어에서 클래스로 생성한 인스턴스 객체를 의미하고, 클래스 내에서만 사용 가능하다.

자바스크립트에서는 동일한 this 키워드의 의미가 조금 다르다. 클래스가 아닌 어느 곳에서든 사용할 수 있고, 실행 위치에 따라 this가 가리키는 대상이 달라진다. 호출되는 방식에 따라 동적으로 결정된다.

사정이 그렇다 보니 이를 두고 많은 개발자들이 혼란스러워 한다, 고 한다. 실제로 JSON의 아부지 더글라스 크록포드 역시 this의 동작에 대해 설계 오류가 있다는 의견을 내비친 바 있다, 고 한다.

그런데 솔직히 나는 자바스크립트 부터 배워서 그런지 전자의 경우, 그러니까 JavaC++ 같은 언어에서 이야기 하는 의미들은… 그냥 그러려니 싶다. 솔직히 그들의 일은 그들의 일이 아닌지…? 다들 너무 헷갈리는 개념이라 한다고 해서 지레 겁먹지 말자는 뜻이다.

하지만 여전히 Java 같은 객체지향 언어처럼, this 키워드가 클래스 내부 뿐만 아니라 전역이나 함수 내부에서 쓰이고 있다는 점은 JavaScript 문법의 큰 특징 중 하나라는 사실은 기억해둘 필요가 있다. 모든 문제는 사실 여기서 출발하니까.

실은 지난 해 5월에 같은 주제에 대해서 정리했던 적이 있는데, 올해의 나는 지난 해의 나보다 강해졌으니 몇 가지 깨달음을 더 추가해보겠다.

보통 this에 대해 공부를 한다고 하면 각각의 경우에 어떤 값을 갖게 되는지에 대해서 정리하게 된다. 하지만, 그 전에 this는 왜 필요할까? 예전에는 이 사실에 대해 궁금해본 적이 없었다.

동작을 나타내는 메서드는 자신이 속한 객체의 상태, 즉 프로퍼티를 참조하고 변경할 수 있어야 한다. 이때 메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.

객체 리터럴 방식으로 생성한 객체의 경우 메서드 내부에서 메서드 자신이 속한 객체를 가리키는 식별자를 재귀적으로 참조할 수 있다.

이웅모, <모던 자바스크립트 Deep Dive>

this자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수라고 정리할 수 있다. 객체생성자 함수로 생성된 인스턴스 내에서 자기 자신에 접근하기 위한 키워드라는 뜻이다. 아마 이러한 의미로서의 this가 가장 본래 의도에 부합하는 정의가 아닐까 싶다.

<자바스크립트 완벽 가이드>에서는 this의 의미에 대해 조금 가슴이 웅장해지는 설명을 해두었는데, 인용으로 곁들여본다.

메서드와 this 키워드는 자바스크립트 객체 지향 프로그래밍 패러다임의 중심이다. 메서드로 사용되는 함수는 메서드의 호출 대상 객체를 암시적 인자로 전달 받는다. 보통 메서드는 해당 객체에 어떤 작업을 수행하기 때문에, 메서드 호출 문법은 그 함수가 해당 객체에 무언가를 한다는 사실을 나타내는 세련된 방법이다. 다음 두 줄을 비교해보라.

rect.setSize(width, height);

setRectSize(rect, width, height);

어떤 메서드를 작성하고 있는데 특별한 반환 값이 없다면, this를 반환하면 어떨지 고려해보라. 이러한 방식을 당신의 API 전체에 일관성 있게 적용한다면, 메서드 체이닝이라는 프로그래밍 스타일을 사용할 수 있게 된다. 메서드 체이닝은 객체 이름은 한 번만 사용하고 메서드는 여러 번 호출할 수 있는 방식이다.

이런 관점에서 바라보자면 결국 ‘함수를 호출한 주체’가 중요해진다. 앞서 this는 자기 자신을 참조하는 변수라고 했다. 함수 혹은 메서드는 자기 스스로 실행하는 개념이 아니라서 이를 ‘호출’ 해주는 주체가 중요하다. 누군가 불러줘야 곁에 가서 꽃이 되는, 김춘수 시인의 과도 같다.

함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치이다. 이 둘을 구분하는 유일한 차이는 독립성이다. 함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다.

정재남, <코어 자바스크립트>


함수의 호출 주체

함수의 호출 주체는 함수 호출 방식에 따라 달라질 수 있다. this는 함수를 호출할 때(실행 컨텍스트가 생성될 때) 결정된다고 바꿔 말할 수도 있을 것이다. 자바스크립트에서 함수를 호출할 수 있는 방법은 다음과 같이 총 4가지다.

함수 호출 방식 this 바인딩
일반 함수 호출 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 (미래에) 생성할 인스턴스
Function.prototype.call/apply/bind 메서드에 의한 간접 호출 해당 메서드 첫 번째 인수로 전달한 객체

먼저 일반 함수 호출의 경우다. 일반 함수의 경우 다음과 같은 이유(개발자가 코드를 호출함, this가 지정되지 않은 경우엔 전역 객체를 참조)로 전역 객체가 this에 바인딩 된다.

this에는 호출한 주체에 대한 정보가 담긴다고 했습니다. 그런데 함수로서 호출하는 것은 호출 주체(객체지향 언어에서의 객체)를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 호출 주체의 정보를 알 수 없는 것입니다. 2장에서 실행 컨텍스트를 활성화할 당시에 this가 지정되지 않은 경우 this는 전역 객체를 바라본다고 했습니다. 따라서 함수에서의 this는 전역 객체를 가리킵니다. 더글라스 크락포드는 이를 명백한 설계상의 오류라고 지적합니다.

정재남, <코어 자바스크립트>

변수와 달리, this 키워드에는 유효범위(scope)가 없고 중첩 함수는 호출자의 this 값을 상속하지 않는다. 만약 중첩 함수가 메서드 형태로 호출되면, 그 함수의 this 값은 그 함수의 호출 대상 객체다. 만약 중첩 함수가 함수 형태로 호출되면, 중첩 함수의 this 값은 global 객체(일반 모드) 또는 undefined(엄격 모드) 중 하나다.

데이비드 플래너건, <자바스크립트 완벽 가이드>

하나 주의할 점은 일반 함수 혹은 객체의 메서드 내에서 중첩 함수를 호출했을 경우다. 전역에서 정의된 함수든 중첩 함수든 어떤 함수를 일반 함수로 호출하면 (ex. foo()) 함수 내부의 this는 전역 객체(strict mode에서는 undefined)를 참조하게 된다.

일반적으로 함수 안에 정의된 함수는 상위 스코프의 헬퍼 함수로서 보조적인 역할을 하게 되기 때문에 독립적인 this를 갖기 보다 부모 함수의 this를 상속받기를 바라는 경우가 더욱 많다. 하지만 역시나 어떤 함수든 this는 전역 객체를 참조하기 때문에 곤란한 상황이 연출되기 일쑤다.

이 경우 클로저를 활용해 새로운 변수에 할당한 this를 활용하거나(ex. self), 화살표 함수를 활용하여 상위 스코프의 this를 상속 받거나, 후술할 Function.prototype.call/apply/bind를 사용하여 해결할 수 있다. ES6 환경에서는 화살표 함수를 쓰면 되지만, 그렇지 않다면 첫 번째 혹은, 세 번째 방법을 사용하면 된다.

메서드 호출생성자 함수 호출은 일반 함수 호출과는 반대되는 이유로 메서드 혹은 인스턴스가 this의 참조 대상이 된다. 해당 함수들의 호출 주체를 바라보는 상황을 떠올리면 좋다.

마지막으로 앞서 언급했던 Function.prototype.call/apply/bind 메서드를 활용한 간접 호출이다. 다른 사용법은 그러려니 했는데 .bind()를 사용한 함수의 부분 실행을 구현하는 방법이 퍽 인상적이었다.

const func = function (a, b, c, d) {
  console.log(this, a, b, c, d);
};

const bindFunc = func.bind({ x: 1 }, 4, 5);

bindFunc(6, 7); // { x: 1 }, 4, 5, 6, 7

예상 면접 질문

  • this에 대하여 간단히 설명하고, 개발 중 사용해본 적이 있다면 언제 왜 사용했는지 말씀해주세요.

  • 중첩 함수 내에서 this의 참조 대상으로 인한 문제가 발생할 수 있습니다. 그 이유를 this가 바인딩 되는 규칙과 연결지어 설명하고, 이 문제를 해결할 수 있는 방법을 아는 대로 말씀해주세요.


참고 자료

  • 정재남, <코어 자바스크립트>

  • 이웅모, <모던 자바스크립트 Deep Dive>

  • 데이비드 플래너건, <자바스크립트 완벽 가이드>

Profile picture

saengmotmi

'내가 원하는 건 문학이 아닌 기쁨이다.'