코딩쿠의 코드 연대기

JavaScript의 this: 개념부터 실전 활용 본문

코딩스터디/JavaScript

JavaScript의 this: 개념부터 실전 활용

코딩쿠 2024. 11. 26. 21:50

JavaScript의 this 사용법과 실전 예제

this는 JavaScript에서 함수가 실행되는 **문맥(context)**을 나타내는 키워드로, 호출 방식에 따라 동적으로 값이 결정됩니다. this의 동작은 코드를 작성할 때 헷갈릴 수 있지만, 이해하면 보다 유연하고 강력한 코드를 작성할 수 있습니다. 아래에서는 다양한 상황에서의 this 사용법을 예제와 함께 살펴보겠습니다.


1. 전역 콘텍스트에서의 this

console.log(this); // 브라우저에서는 window 객체, Node.js에서는 global 객체

전역 컨텍스트에서 this는 기본적으로 글로벌 객체를 참조합니다.


2. 객체 메서드에서의 this

const obj = {
  name: 'John',
  greet: function() {
    console.log(`Hello, ${this.name}!`);
  }
};

obj.greet(); // 출력: Hello, John!
  • 설명: 객체의 메서드에서 this는 해당 메서드를 호출한 객체를 참조합니다. 위 예제에서 greet 메서드 내부의 this는 obj를 가리킵니다.

3. 생성자 함수에서의 this

function Person(name) {
  this.name = name;
}

const john = new Person('John');
console.log(john.name); // 출력: John
  • 설명: 생성자 함수에서 this는 새로 생성된 객체를 참조합니다. Person 함수는 new 키워드를 사용해 호출되었고, 이로 인해 this는 john 객체를 가리키게 됩니다.

4. 이벤트 핸들러에서의 this

const button = document.querySelector('button');

button.addEventListener('click', function() {
  console.log(this); // 출력: 클릭된 버튼 요소
});
  • 설명: 이벤트 핸들러 내부에서 this는 이벤트가 발생한 DOM 요소를 참조합니다. 위 예제에서는 클릭된 button 요소가 this가 됩니다.

5. 화살표 함수에서의 this

const obj = {
  name: 'John',
  greet: () => {
    console.log(`Hello, ${this.name}!`);
  }
};

obj.greet(); // 출력: Hello, undefined!
  • 설명: 화살표 함수는 자신만의 this를 생성하지 않고 상위 스코프의 this를 상속받습니다. 따라서 위 코드에서 화살표 함수 내부의 this는 글로벌 객체를 참조합니다.

6. this 바인딩 변경

call, apply, bind 메서드를 사용하여 this를 명시적으로 변경할 수 있습니다.

const obj = { name: 'John' };

function greet() {
  console.log(`Hello, ${this.name}!`);
}

greet.call(obj); // 출력: Hello, John!
greet.apply(obj); // 출력: Hello, John!

const boundGreet = greet.bind(obj);
boundGreet(); // 출력: Hello, John!
  • 설명:
    • call: 함수 호출과 함께 this를 설정.
    • apply: call과 유사하지만, 인자를 배열로 전달.
    • bind: 새로운 함수로 this를 고정.

7. 비동기 컨텍스트에서의 this

비동기 작업에서도 this는 호출 방식에 따라 달라질 수 있습니다.

const obj = {
  name: 'John',
  greet: function() {
    setTimeout(function() {
      console.log(this.name); // 출력: undefined
    }, 1000);
  }
};

obj.greet();

위 예제에서 setTimeout 내의 this는 글로벌 객체를 참조합니다. 이를 해결하려면 다음 중 하나를 사용할 수 있습니다.

  • 해결책 1: bind 사용
setTimeout(function() {
  console.log(this.name);
}.bind(this), 1000);
  • 해결책 2: 화살표 함수 사용
setTimeout(() => {
  console.log(this.name);
}, 1000);

예제와의 연관성: 실전 코드에 적용하기

1. 단순 대화 함수

function displayMessage(message, sender) {
  const messageElement = document.createElement('div');
  messageElement.classList.add(sender === 'ai' ? 'ai-message' : 'user-message');
  messageElement.textContent = message;
  chatContainer.appendChild(messageElement);

  console.log(this); // `displayMessage`가 호출된 컨텍스트
}

위 코드에서 displayMessage 내부의 this는 호출하는 방식에 따라 다르게 평가됩니다.

2. Chatbot UI Integration

sendButton.addEventListener('click', function() {
  console.log(this); // 버튼 요소
});

 

3. Using Promises and Async/Await에서의 this

비동기 작업을 수행하는 함수에서도 this의 값은 호출 컨텍스트에 따라 달라집니다. 이를 관리하지 않으면 다음과 같은 문제가 발생할 수 있습니다.

문제 상황:

const obj = {
  name: 'Chatbot',
  async fetchData() {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
      const data = await response.json();
      console.log(`${this.name} received:`, data.title);
    } catch (error) {
      console.error(this.name, 'Error:', error);
    }
  }
};

obj.fetchData(); // 정상적으로 실행: this는 obj를 참조

위 코드는 정상적으로 작동합니다. 그러나 this가 엉뚱한 값을 참조하게 되는 경우도 있습니다.


this 관리 문제

const obj = {
  name: 'Chatbot',
  async fetchData() {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
      const data = await response.json();
      console.log(`${this.name} received:`, data.title);
    } catch (error) {
      console.error(this.name, 'Error:', error);
    }
  }
};

const fetchDataFunc = obj.fetchData;
fetchDataFunc(); 
// TypeError: Cannot read properties of undefined (reading 'name')
  • 원인: fetchData 메서드를 객체에서 분리해서 호출하면 this가 글로벌 객체 또는 undefined(strict mode)로 평가됩니다.

해결책: bind, call, 또는 apply를 활용

const boundFetchData = obj.fetchData.bind(obj);
boundFetchData(); // 정상 동작

해결책: 화살표 함수 사용 (상위 스코프의 this 유지)

const obj = {
  name: 'Chatbot',
  fetchData: async () => {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
      const data = await response.json();
      console.log(`${this.name} received:`, data.title);
    } catch (error) {
      console.error(this.name, 'Error:', error);
    }
  }
};

obj.fetchData(); // 출력: undefined received: (제대로 동작하지 않음!)

주의: 화살표 함수는 상위 스코프의 this를 상속하기 때문에, 객체의 메서드로 사용할 경우 의도한 this를 참조하지 않을 수 있습니다. 이 방법은 적합하지 않습니다.


가장 권장되는 방법: 메서드 형태 유지

객체 메서드로 호출되도록 설계하면 불필요한 this 관련 문제를 피할 수 있습니다.

obj.fetchData(); // 안전하고 명확하게 동작

요약:

  • 비동기 함수에서도 this를 정확히 이해해야 의도한 결과를 얻을 수 있습니다.
  • this가 문제를 일으킬 가능성이 있다면 bind를 사용해 명시적으로 바인딩하거나 객체 메서드로 호출하는 방식을 고수하세요.

비동기 함수에서도 this의 값을 정확히 이해하고 관리해야 코드를 예측 가능하게 작성할 수 있습니다.