React 공식 문서 읽기 (1) - Main Concepts

Chani·2022년 5월 11일
1
post-thumbnail

1. Element vs Component

리액트 문서의 주요개념의 엘리멘트 렌더링 부분부터 보기 시작했다.

첫번째 페이지에 바로 주의 가 나오는데 컴포넌트와 엘리멘트를 혼동할 수 있다고 나오길래 정확히 어떤 차이가 있는지 궁금해졌다.

element는 불변 객체이고, 요소에 메소드가 없지만 component는 hooks를 사용해 상태를 바꿀수 있고, 생명주기가 있다.

라고 나오는데 이걸 보니 더 헷갈려졌다.

구글링을 계속 하다가 우연히 TypeScript의 코드를 보고 차이점을 명확히 깨달았는데,

위 코드를 보면 App이라는 컴포넌트는 Element를 반환해주고 있다.

즉, 컴포넌트는 여러 상태(State)에 따라 element를 구성하고 반환해주는 함수인것이었다.

여기까지 이해하고나니 주의에 적혀있던 마지막 문장이 눈에 보였다.

엘리먼트는 컴포넌트의 구성요소이다

처음부터 이 문장부터 읽었더라면 시간을 좀 더 아낄수 있지 않았을까,,,

2. Component Props

함수 컴포넌트는 Props를 가지고 element를 구성하여 반환해줄 수 있다.

아래는 공식문서에서 가져온 Props를 사용하여 element를 구성한 함수 컴포넌트이다.

하지만 우리는 타입스크립트를 가지고 공부를 하고 있기때문에 공식문서처럼 props라고만 쓰면 안된다.

Practice01에서 사용했던 코드를 가져와서 Props를 받을수 있도록 고쳐보자

interface AppProps {
  propsString: string
};

const App = (props: AppProps): JSX.Element => {
  return (
    <div>
      {'Hello, World!'}
      {props.propsString}
    </div>
  )
}

props가 어떤 타입으로 정의되어있는지 interface를 사용하여 정의해주었다.

이렇게 고치게 되면 App 컴포넌트는 반드시 Props를 받아야하기 때문에 index.tsx도 고쳐주어야 한다.

이렇게 화가나있는 코드를 아래와 같은 코드로 바꿔주자.

root.render(<App propsString='practice02 props string' />);

실행시켜보면 아래와 같이 잘 바뀐것을 확인할 수 있다.

잘 실행은 되었지만 컴포넌트의 Props 객체는 하나인데 꼭 props.propsString과 같이 써야하나? 그냥 propsString만 사용하면 좋겠다 라는 생각이 들어서 구조 분해 할당을 해주기로 하였다.

const App = ({ propsString }: AppProps): JSX.Element => {
  return (
    <div>
      {'Hello, World!'}
      {propsString}
    </div>
  )
}

위 코드처럼 props 대신 props에 들어가는 key들을 객체 안에 써주면 바로 사용 할수 있다.

props.propsString 으로 사용하는것 보다 훨씬 보기 좋아진 것 같다.

3. 리스트와 Key

리스트에 담긴 데이터를 Props로 주는 컴포넌트를 반복적으로 만들고 싶을때는 어떻게 해야할까.

자바스크립트에는 map 함수가 있는데 원래 리스트의 데이터를 매개변수로 받는 콜백 함수의 리턴값에 따라 다시 계산해준 다음 새로운 리스트 객체로 만들어 리턴해주는 함수이다.

const arr1 = [1,2,3,4,5];
const arr2 = arr1.map((n) => n * 2);
// arr2 == [2,4,6,8,10];

위 코드에서 map함수의 콜백 함수는 리스트의 데이터를 2배씩 늘려주는 함수이다. 따라서 [1,2,3,4,5]를 각각 2배씩 한 리스트인 [2,4,6,8,10]arr2에 리턴해주게 된다.

이러한 기능을 하는 map 함수를 사용하면 리스트의 데이터를 사용하여 반복적으로 여러 컴포넌트를 만들어 줄 수가 있다.

import React from 'react';

interface ListComponentProps{
	array: number[],
}

const ListComponent = ({ array }: ListComponentProps): JSX.Element => {
	const listItem = array.map((n: number) => <li>{n}</li>);

	return (<ul>{listItem}</ul>);
}

export default ListComponent;

위 코드는 props로 들어온 배열을 <li> 태그로 감싸주고 감싸진 <li> 태그 엘리먼트들을 <ul>태그로 감싸서 리턴해주는 컴포넌트를 구현한 것이다.

위와 같은 컴포넌트를 하나 만들어주고 App 컴포넌트에서 아래 코드처럼 사용하게 되면 다음과 같은 결과를 얻을수 있다.

const App = ({ propsString }: AppProps): JSX.Element => {
  const numArray = [1,2,3,4,5];
  return (
    <div>
      <div>
        {'Hello, World!'}
        {propsString}
      </div>
      <ListComponent array={numArray}/>
    </div>
  )
}

결과화면은 잘 나오지만 크롬의 console 창을 보면 warning이 발생하고 있는걸 확인 할 수있다.

리스트 엘리먼트를 만들어줄때 key를 넣어달라는 경고인데 이러한 경고가 발생하는 이유가 뭘까

ListComponent 에서 리스트 데이터중 일부가 바뀐다면 <li>태그의 엘리먼트들은 어떻게 될까?

리액트에서는 최대한 바뀐 데이터의 엘리먼트만을 바꾸려고 노력한다.

그리고 그것을 구분지어주는게 key라는 친구이다.

App 컴포넌트를 다음과 같이 바꿔보자

const App = ({ propsString }: AppProps): JSX.Element => {
  const [ numArray, setNumArray ] = useState<number[]>([1,2,3,4,5]);
  return (
    <div>
      <div>
        {'Hello, World!'}
        {propsString}
        <button onClick={() => setNumArray([0,1,2,3,4,5])}>배열 바꾸는 버튼</button>
      </div>
      <ListComponent array={numArray}/>
    </div>
  )
}

버튼을 클릭하면 리스트가 [1,2,3,4,5]에서 [0,1,2,3,4,5]로 바뀌는 코드이다.

해당 상태에서 최대한 엘리먼트를 안바꾸려면 1,2,3,4,5 의 <li>는 그대로 두고 0을 데이터로 가지는 <li>태그만 추가해주면 될것이다.

하지만 실제로 실행시켜보면 다음과 같이 <li>태그의 모든 엘리먼트가 새로 변경되는것을 확인할 수 있다.

왜 이런결과가 생기는 것일까

현재의 ListComponent에는 key값이 주어지지 않았는데 이러한 경우 리액트는 기본 키값으로 리스트의 인덱스를 받는다.

즉, 맨 앞쪽에 0이 추가가 되었기 떄문에 리액트 입장에서는 각 키값에 대해 모든 요소들이 바뀐것으로 판단하여 모든 엘리먼트를 리렌더링 해주게 된것이다.

이 문제를 해결하기 위해서는 다음과 같이 코드를 변경하면 된다.

const ListComponent = ({ array }: ListComponentProps): JSX.Element => {
	const listItem = array.map((n: number) => <li key={n}>{n}</li>);

	return (<ul>{listItem}</ul>);
}

좋은 예제는 아니지만 위의 코드처럼 리스트의 데이터 자체를 키값으로 바꿔주면 같은 키값에 대해 1~5까지는 전혀 바뀌지 않았기때문에 원래 의도했던 것처럼 <li>0</li> 만 추가가 된다.

이렇게 key값을 주고 나니 console창의 경고도 사라진 모습을 확인 할 수 있다.

이런식으로 리스트의 데이터를 가지고 반복적으로 엘리먼트를 만드려고 할 때는 해당 데이터의 고유값을 가지고 key값을 설정해주어야 불필요한 요소들의 렌더링을 막을 수 있다.

profile
프론트엔드에 스며드는 중 🌊

0개의 댓글