배경 이미지
728x90
반응형

오늘은 상황에 따라 달라지는 this에 대해서 정리해 보도록 할게요.

 

☑️ 전역 컨텍스트에서의 this

전역 공간에서 this를 호출하면, this가 가리키는 전역 객체 는 코드가 실행되는 런타임 환경에 따라 달라져요.

브라우저 환경에서는 window를, Node.js 환경에서는 global 객체를 가리킵니다.

console.log(this);  // 브라우저 환경에서는 window, Node.js 환경에서는 global 객체가 출력됩니다.

var name = 'Alice';
console.log(this.name);  // 'Alice' 출력

 

☑️ 메소드 내부에서의 this

메소드 내부에서 this는 해당 메소드를 호출한 객체를 가리켜요. 즉, 그 객체 자체가 this가 됩니다. 

호출한 객체 ===this  

var person = {
    name: 'Bob',
    sayName: function() {
        console.log(this.name);
    }
};

person.sayName();  // 'Bob' 출력

 

☑️함수 내부에서의 this

일반 함수에서의 this는 호출 주체를 명시할 수 없기 떄문에,  전역 객체를 가리켜요.

function showThis() {
    console.log(this);
}

showThis();  // 브라우저에서는 'window' 객체 출력

 

☑️생성자 함수 내부에서의 this

생성자 함수는 객체를 만들기 위한 함수인데요. 생성자 함수 내부에서 this를 호출하면, 새로 생성된 객체를 가리켜요.

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

var alice = new Person('Alice');
console.log(alice.name);  // 'Alice' 출력

 

☑️ 화살표 함수 내부에서의 this 

화살표 함수 (=this를 바인딩하지 않는 함수)는 일반 함수와 다르게 자신만의 this를 만들지 않고, 정의된 위치의 상위 스코프에서 this를 가져와요. 즉, 화살표 함수 내부에서는 상위 스코프의 this를 그대로 사용합니다.

var name = 'Ciryl Gane';

var person = {
    name: 'John Jones',
    getName: () => {
        console.log(this.fullname);
    }
};

person.getName();  // 'Ciryl Gane' 출력

여기서 주의해야할 포인트! 객체는 스코프를 제공하지 않아요. 즉, 객체 내부에서 정의된 화살표 함수는 그 객체의 this를 사용하지 않습니다. 객체는 데이터를 저장하는 구조이지만, 함수나 블록처럼 스코프를 형성하지는 않는다는거죠.

 

☑️ 콜백 함수 내부에서의 this

콜백 함수도 함수기 때문에 this는 전역 객체를 참조하지만(호출 주체가 없잖아요), 콜백함수를 넘겨받은 함수에서 콜백 함수에 별도로 this를 지정한 경우는 예외적으로 그 대상을 참조하게 되어있어요.

// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);

// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
	console.log(this, x);
});

 

예외) 이벤트 핸들러 내부에서의 this

// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
	console.log(this, e);
});

 

예외) 화살표 함수에서 이벤트 핸들러의 this

이벤트 핸들러를 화살표 함수로 정의하면, 상위 스코프의 this를 가져오기 때문에 이벤트가 발생한 요소를 가리키지 않아요

var button = document.getElementById('myButton');

button.addEventListener('click', () => {
    console.log(this);  // 상위 스코프의 'this' 출력 (전역 객체)
});

 

 

요약

상황 this가 가리키는 대상
전역 공간에서 전역 객체 (window 또는 global)
메소드 내부에서 해당 메소드를 호출한 객체
함수 내부에서 전역 객체 (window 또는 global)
생성자 함수 내부에서 새로 생성된 객체
화살표 함수 내부에서 상위 스코프의 this
728x90
반응형

'JavaScript' 카테고리의 다른 글

[JS] 클로저  (1) 2024.09.22
[JS] 반복문  (2) 2024.09.08
[JS] Map 사용 방법  (1) 2024.09.08
[JS] 1-3) 객체와 객체 메소드, 그리고 객체를 순회하는 방법  (1) 2024.09.04
[JS] 1-1) JS 언어의 특징과 역사  (1) 2024.09.02
728x90
반응형

리액트에서 JSX의 key 속성은 리스트 렌더링 시 중요한 역할을 하는데요. 이번 포스팅에서는 key 속성이 무엇인지, 왜 필요한지, 그리고 이를 어떻게 사용하는지에 대해 설명해드릴게요.

1. JSX Key 속성은 무엇인가?

리액트에서 리스트를 렌더링할 때 각 항목에 반드시 key라는 고유한 속성을 넣어줘야해요.

리스트가 업데이트되거나 순서가 바뀌면, 리액트는 key를 기준으로 어떤 항목이 변했는지, 추가되었는지, 혹은 삭제되었는지를 빠르게 알아낼 수 있어요.간단히 말해, 리스트의 각 항목에 고유한 key를 부여하지 않으면 리액트가 리스트의 변화를 인식하지 못할 수 있어요. 안정적인 업데이트를 위해, 배열 내 요소들에 key를 제공하는 것이 필수적이에요.

 

2. 리액트는 가상 돔을 이용해서 바뀐 부분만 실제 돔에 적용!!

리액트는 가상 돔을 이용해서 바뀐 부분만 실제 돔에 적용해주는데요

리액트에서는 리스트를 나열할 때 바뀐 부분만 찾을 때 어떻게 할까요?

리스트 렌더링 시, 리액트는 key를 사용해 어떤 항목이 바뀌었는지 인식합니다.

아래 이미지에서 가상 돔을 사용해 변경된 부분을 찾아내고, 그 부분만 실제 돔에 적용하는 과정을 설명하고 있어요:

 

3. key는 유니크한(고유한) 값을 넣기! (index는 비추천)

key 속성에는 항목을 고유하게 식별할 수 있는 값을 넣어야 해요.

key에 배열의 인덱스를 사용하는 것은 비추천이에요. 왜냐하면, 리스트 항목이 추가되거나 삭제되면 인덱스가 변하기 때문이죠. 이로 인해 리액트는 잘못된 항목을 업데이트할 수 있어요. 따라서 배열의 index를 key로 사용할 수는 있지만, 리스트 항목의 변경이 자주 발생하는 경우에는 key로 적합하지 않으니 비추드려요.  

 

4. key 속성을 제대로 사용하지 않으면?

좋지 않은 예시) 아래 예시처럼 

function App() {
  const items = [
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Orange' }
  ];

  const lists = items.map(item => <li>{item.name}</li>);

  return (
    <div>
      <h2>Fruits List</h2>
      <ul>
        {lists}
      </ul>
    </div>
  );
}

리스트 항목에 key를 지정하지 않으면 다음과 같은 경고(Warning) 메시지를 볼 수 있어요.

Warning: Each child in a list should have a unique "key" prop.

이는 리액트가 각 항목을 제대로 추적할 수 없다는 신호예요. key 속성이 없거나 고유하지 않으면 리액트는 항목을 구분하는 데 어려움을 겪고, 성능이 떨어질 수 있어요.

 

올바른 예시)

function App() {
  const items = [
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Orange' }
  ];

  const lists = items.map(item => <li key={item.id}>{item.name}</li>); // id를 key로 사용하여 고유성 보장

  return (
    <div>
      <h2>Fruits List</h2>
      <ul>
        {lists}
      </ul>
    </div>
  );
}

 

728x90
반응형

'React' 카테고리의 다른 글

[React] 리액트 동작과정과 최적화  (1) 2024.09.11
[React] Hooks란?  (3) 2024.09.09
[React] 리액트에서 불변성(Immutability) 관리의 중요성과 방법  (0) 2024.08.21
[React] State란?  (0) 2024.08.20
[React] Props란?  (0) 2024.08.16
728x90
반응형

우선 우리가 한가지 짚고 넘어가야 할 것은 re-evaluatingre-rendering은 완전히 동일한 개념이 아니다 라는 사실이다.
즉, 함수 컴포넌트가 재실행(re-execute)되고 재평가(re-evaluate)된다고 해서 무조건 리렌더링(re-render)이 일어나는 것이 아니다. 재실행 되는 것은 컴포넌트이고, 리렌더링은 실제 DOM에서 일어나는 변화이다

 

☑️ 리액트의 기본 동작 방식

  1. Re-evaluate (재평가):
    • 상태나 속성이 변경되면 컴포넌트를 다시 계산해서 새로운 가상 DOM을 만든다.
  2. Diffing (비교):
    • 새로 생성된 가상 DOM과 이전 가상 DOM을 비교해서 어떤 부분이 변했는지 찾는다.
  3. Re-render (재렌더링):
    • 변경된 부분만 실제 DOM에 반영하고, 화면을 다시 그린다.

 

React는 state, props, context 등이 변화하면 함수 컴포넌트를 재실행(re-execute) 하여, 위에서부터 차례로 재평가(re-evaluate)한다. 이렇게 재평가한 결과는 ReactDOM에게 전달되고, ReactDOM은 virtual DOM을 이용해서 전후 비교를 한 이후에, 바뀐 부분만 real DOM 에 반영하는 리렌더링을 일으킨다. 즉, 실행과 평가의 주체는 React이고 그 대상은 컴포넌트이며, 리렌더링의 주체는 ReactDOM이고 그 대상은 DOM이다. 서로 연관되어 있기는 하지만, 같은 의미도 아닐 뿐더러 꼭 함께 일어난다고 말할 수 없다. 

 

(하지만 많은 사람들이 re-rendering을 re-evaluate(재평가) 과정까지 포함하는 더 포괄적인 용어로 사용한다. 그리고 real DOM을 업데이트 하여 re-rendering 하는 것을 repaint 등으로 부를 수 있다. 그래서 이는 문맥과 상황에 맞게 받아들여야 한다)

 

☑️ React.momo

리액트의 공식 문서를 보면, React.memo에 대한 설명은 다음과 같다:

(memo를 사용하면 컴포넌트의 props가 변경되지 않은 경우 re-rendering을 건너뛸 수 있다)

 

여기서 말하는 Re-rendering을 건너뛴다는 말은 사실 정확히 말하면 Re-evaluate를 건너뛴다는 뜻이야. React.memo안 바뀐 것이 확실한 부분에 대해서는 리액트가 다시 계산하거나 비교하지 않도록 해주는 역할을 한다.

 

처음에 React.momo 공부할 때 헷갈렸던 부분이 리액트는 애초에 비교 후 변화된 부분이 없으면 랜더링을 안해 최적화를 해주는데 왜 굳이 React.memo를 추가적으로 사용해야 할까? 라고 생각했다 

 

근데 알고보니 React.memo불필요한 Re-evaluate 자체를 막아주는 역할을 한다.

부모 컴포넌트가 다시 렌더링될 때, 자식 컴포넌트의 속성(props)이 변하지 않았다면, 자식 컴포넌트는 당연히 바뀐게 없기 떄문에 자식 컴포넌트를 다시 평가(Re-evaluate)할 필요가 없다.

 

따라서 React.memo는 props가 변화하였을 때만 해당 컴포넌트를 재실행 및 재평가 하도록 컴포넌트를 메모이제이션(memoization) 한다. 즉, React.memo는 컴포넌트를 메모이제이션(memoization)하여 이전 props와 새 props가 동일하면 컴포넌트를 다시 평가하지 않도록 최적화해준다.

 

[주의]

React.memo를 사용할 때 주의해야 할 한 가지는 함수를 props로 전달하는 경우다. 함수는 참조 타입(reference value)이기 때문에, 매번 새로운 함수 인스턴스가 생성되고 메모리 주소가 달라지기 때문에 React.memo가 이를 다른 값으로 인식하게 된다.따라서 props가 그대로인 것처럼 보여도 함수가 포함되어 있다면 매번 다른 함수로 인식하게 되어, React.memo가 적용되지 않을 수 있다.

이 문제를 해결하기 위해서는 useCallback을 함께 사용해 함수를 메모이제이션하는 것이 필요하다.

 

☑️ useCallback

useCallback함수를 메모이제이션하여, 같은 함수가 불필요하게 새로 생성되지 않도록 최적화해준다. 이렇게 함으로써 함수가 props로 전달될 때 매번 새로운 함수가 생성되는 것을 방지하고, 불필요한 Re-evaluate를 줄일 수 있다.

 

예시 1: React.memo만 사용한 경우

import React, { useState } from 'react';

// 자식 컴포넌트, React.memo로 감싸져 있음
const Child = React.memo(({ onClick }) => {
  console.log("Child component rendered");
  return <button onClick={onClick}>Click me!</button>;
});

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    console.log("Button clicked!");
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
      <Child onClick={handleClick} />  {/* Child에 함수 전달 */}
    </div>
  );
}

export default Parent;

부모 컴포넌트가 렌더링될 때마다, handleClick 함수가 새로운 함수로 인식되기 때문에, Child 컴포넌트는 다시 렌더링된다

 

예시 2: React.memo와 useCallback을 함께 사용한 경우

import React, { useState, useCallback } from 'react';

// 자식 컴포넌트, React.memo로 감싸져 있음
const Child = React.memo(({ onClick }) => {
  console.log("Child component rendered");
  return <button onClick={onClick}>Click me!</button>;
});

function Parent() {
  const [count, setCount] = useState(0);

  // useCallback으로 handleClick 함수 메모이제이션
  const handleClick = useCallback(() => {
    console.log("Button clicked!");
  }, []);  // 빈 배열이므로 처음에 한 번만 생성됨

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
      <Child onClick={handleClick} />  {/* Child에 메모이제이션된 함수 전달 */}
    </div>
  );
}

export default Parent;

 

☑️ 최적화를 남발하면 안 되는 이유

최적화는 분명히 유용하지만, 모든 컴포넌트에 적용할 필요는 없다. 최적화에도 비용이 따르기 때문이다.

React.memo로 최적화 한 컴포넌트의 경우, 기존 props 와 새로운 props 의 값을 비교해야한다. 그러기 위해서 기존의 props 값을 저장할 공간 또한 필요하다. 즉, 최적화란, 컴포넌트를 재평가하는 데 필요한 성능 비용과 props 를 비교하는 성능의 비용을 서로 맞바꾸는 것이다.
자식 컴포넌트가 매우 많고, 많이 겹쳐있는 상황이라면 최적화를 통해서 비용을 아낄 수 있겠지만, 매우 작은 앱이나 매우 작은 앱이나 간단한 컴포넌트 트리에서는 React.memo의 효과가 미미할 수 있다. 따라서 꼭 필요한 컴포넌트에만 선택적으로 적용하는 것이 좋다.
 

728x90
반응형

'React' 카테고리의 다른 글

[React] JSX Key 속성 이해하기  (1) 2024.09.12
[React] Hooks란?  (3) 2024.09.09
[React] 리액트에서 불변성(Immutability) 관리의 중요성과 방법  (0) 2024.08.21
[React] State란?  (0) 2024.08.20
[React] Props란?  (0) 2024.08.16
728x90
반응형

개발할 때 웹사이트가 제대로 동작하는지 확인하고, 문제가 생기면 찾아내는 일이 중요해요. 이런 일을 도와주는 도구들이 바로 React Developer ToolsVue.js Devtools입니다. 둘 다 크롬이나 파이어폭스 같은 웹 브라우저에서 쓸 수 있는 확장 프로그램이에요. 그럼, React와 Vue에서 각각 어떤 도구를 사용하는지 쉽게 설명해볼게요!

 

1. React에서 사용하는 디버깅 도구: React Developer Tools

React로 만든 웹사이트를 디버깅하고, 상태(State)나 컴포넌트 구조를 쉽게 확인할 수 있도록 도와주는 도구가 React Developer Tools입니다. React를 쓰는 개발자들이 자주 사용하는 필수 도구죠.

React Developer Tools는 이런 걸 할 수 있어요:

  • 컴포넌트 트리 확인: 애플리케이션 내에 렌더링된 모든 React 컴포넌트를 트리 구조로 볼 수 있어tj, 부모-자식 관계를 쉽게 파악할 수 있어요.
  • 상태(State)와 props 확인: 각 컴포넌트를 클릭하면 해당 컴포넌트의 현재 state와 전달받은 props를 바로 확인할 수 있어서, 어떤 데이터가 어디에서 어떻게 사용되는지 알 수 있어요.
  • React Hooks 확인: React에서 Hooks라는 기능을 많이 쓰는데, Hook을 사용한 컴포넌트의 경우, 각각의 Hook 상태를 추적하여 컴포넌트가 어떻게 동작하는지 알 수 있어요. 
  • 성능 문제 확인: 애플리케이션이 어떻게 렌더링되고, 성능 이슈가 있는지 분석할 수 있는 성능 분석 도구를 제공해요.

설치 방법:

React Developer Tools는 Chrome 웹스토어에서 간단하게 설치할 수 있고, 설치하면 개발자 도구(DevTools)에 React 탭이 생겨서 바로 사용할 수 있어요!

 

React Developer Tools - Chrome 웹 스토어

Adds React debugging tools to the Chrome Developer Tools. Created from revision ccb20cb88b on 7/3/2024.

chromewebstore.google.com

 

☑️ 설치 완료 하시면 components 탭에서 React 컴포넌트 트리를 확인할 수 있어요.

 

 

또한 components 탭 안에 톱니바뀌 클릭하셔서 Highlight updates 부분을 체크하면 쉽게 컴포넌트가 렌더링 되는 것을 보실 수 있어요.

 

-

 

☑️ 프로파일러 탭에서는 성능 정보를 기록할 수 있어요. 

Profiler는 컴포넌트가 재 렌더링이 될 때마다 성능을 기록합니다.

 

2. Vue.js에서 사용하는 디버깅 도구: Vue.js Devtools

Vue.js로 만든 웹사이트를 분석하고 디버깅할 때 사용하는 도구가 Vue.js Devtools입니다. Vue.js를 쓰는 개발자들이 자주 사용하는 필수 도구죠.

Vue.js Devtools는 이런 걸 할 수 있어요:

  • 컴포넌트 트리 확인: Vue로 만든 웹페이지 안에 있는 컴포넌트들이 어떻게 연결되어 있는지 트리 구조로 한눈에 볼 수 있어, 컴포넌트 간의 관계를 쉽게 파악할 수 있어요. 
  • 상태와 props 확인: Vue 컴포넌트가 가지고 있는 데이터(data), 다른 컴포넌트에서 받은 props, 그리고 계산된 값(computed) 등을 직접 확인할 수 있어요. 
  • Vuex 상태 관리 확인: Vuex라는 Vue의 상태 관리 도구를 쓸 때, 상태가 어떻게 바뀌는지, 어떤 이벤트가 발생했는지 실시간으로 확인할 수 있어, Vuex의 흐름을 분석하기 매우 유용해
  • 이벤트 추적: 컴포넌트끼리 주고받는 이벤트를 쉽게 추적해서, 이벤트 핸들링 문제를 쉽게 파악할 수 있어요.

설치 방법:

Vue.js Devtools도 Chrome 웹스토어에서 설치할 수 있고, 설치 후 개발자 도구(DevTools)에 Vue 탭이 생기면 그걸 통해 Vue 애플리케이션을 분석할 수 있어요.

 

Vue.js devtools - Chrome 웹 스토어

Browser DevTools extension for debugging Vue.js applications.

chromewebstore.google.com

 

☑️ 설치 완료 하시면 Vue 탭에서 Vue의 계층 구조, Router, params, emit, path 등 다양한 값들을 한 눈에 볼 수 있어요.

 

 

정리

기능 React Developer Tools Vue.js Devtools
컴포넌트 트리 탐색 컴포넌트 구조를 트리로 보여줌 컴포넌트 구조를 트리로 보여줌
상태 및 props 확인 지원 지원
Hooks/Computed/Watch 추적 React Hooks 지원 Vue의 computed 및 watch 지원
상태 관리 라이브러리 지원 React의 Context API, Redux Vuex 지원
성능 분석 지원 지원
이벤트 흐름 분석 제한적 이벤트 흐름 분석 지원

 

 

728x90
반응형

+ Recent posts