UI 표현하기

React는 사용자 인터페이스(UI)를 렌더링하기 위한 JavaScript 라이브러리입니다. UI는 버튼, 텍스트, 이미지와 같은 작은 요소로 구성됩니다. React를 통해 작은 요소들을 재사용 가능하고 중첩할 수 있는 컴포넌트로 조합할 수 있습니다. 웹 사이트에서 휴대전화 앱에 이르기까지 화면에 있는 모든 것을 컴포넌트로 나눌 수 있습니다. 이 장에서는 React 컴포넌트를 만들고, 사용자화하며, 조건부로 표시하는 방법에 대해서 알아봅시다.

첫 컴포넌트

React 애플리케이션은 컴포넌트라고 불리는 독립된 UI 조각들로 이루어집니다. React 컴포넌트는 마크업을 얹을 수 있는 JavaScript 함수입니다. 컴포넌트는 버튼과 같이 작을 수도 있고 전체 페이지와 같이 큰 경우도 있습니다. 다음의 Gallery 컴포넌트는 세 개의 Profile 컴포넌트를 렌더링하고 있습니다.

function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3As.jpg"
      alt="Katherine Johnson"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

이 주제를 배울 준비가 되셨나요?

React 컴포넌트를 선언하고 사용하는 방법을 배우려면 첫 컴포넌트 를 읽어보세요.

더 보기

컴포넌트 Import 및 Export 하기

하나의 파일에 많은 컴포넌트를 선언할 수 있지만, 파일이 커지면 탐색하기 어려워집니다. 이를 해결하기 위해 컴포넌트를 별도의 파일로 만들어 export하고 다른 파일에서 해당 컴포넌트를 import할 수 있습니다.

import Profile from './Profile.js';

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

이 주제를 배울 준비가 되셨나요?

컴포넌트를 개별 파일로 분리하는 방법을 배우려면 컴포넌트 Import 및 Export 하기 를 읽어보세요.

더 보기

JSX로 마크업 작성하기

React 컴포넌트는 React가 브라우저에 렌더링하는 마크업을 포함할 수 있는 JavaScript 함수입니다. React 컴포넌트는 그 마크업을 표현하기 위해 JSX라는 확장된 문법을 사용합니다. JSX는 HTML과 매우 유사하지만 조금 더 엄격하며 동적인 정보를 표시할 수 있습니다.

기존의 HTML 마크업을 React 컴포넌트에 그대로 붙여넣으면 동작하지 않을 수도 있습니다.

export default function TodoList() {
  return (
    // This doesn't quite work!
    <h1>Hedy Lamarr's Todos</h1>
    <img
      src="https://i.imgur.com/yXOvdOSs.jpg"
      alt="Hedy Lamarr"
      class="photo"
    >
    <ul>
      <li>Invent new traffic lights
      <li>Rehearse a movie scene
      <li>Improve spectrum technology
    </ul>

만약 이미 만들어진 HTML 마크업이 있다면 converter를 사용하여 변환할 수 있습니다.

export default function TodoList() {
  return (
    <>
      <h1>Hedy Lamarr's Todos</h1>
      <img
        src="https://i.imgur.com/yXOvdOSs.jpg"
        alt="Hedy Lamarr"
        className="photo"
      />
      <ul>
        <li>Invent new traffic lights</li>
        <li>Rehearse a movie scene</li>
        <li>Improve spectrum technology</li>
      </ul>
    </>
  );
}

이 주제를 배울 준비가 되셨나요?

올바르게 JSX를 작성하는 방법을 배우려면 JSX로 마크업 작성하기 를 읽어보세요.

더 보기

JSX에서 중괄호를 이용하여 JavaScript 사용하기

JSX를 사용하면 JavaScript 파일에 HTML과 비슷한 마크업을 작성할 수 있어 렌더링 로직과 콘텐츠를 같은 곳에 둘 수 있습니다. 때로는 그 마크업 내부에 JavaScript 로직을 추가하거나 동적인 프로퍼티를 참조해야 하는 경우가 있습니다. 그럴 때 JSX에서 중괄호를 사용하여 JavaScript와 연결된 “창문을 열 수” 있습니다.

const person = {
  name: 'Gregorio Y. Zara',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img
        className="avatar"
        src="https://i.imgur.com/7vQD0fPs.jpg"
        alt="Gregorio Y. Zara"
      />
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

이 주제를 배울 준비가 되셨나요?

JSX에서 중괄호를 사용하여 JavaScript 데이터에 접근하는 방법을 배우려면 JSX에서 중괄호를 이용하여 JavaScript 사용하기 를 읽어보세요.

더 보기

컴포넌트에 Props 전달하기

React 컴포넌트는 서로 통신하기 위해 props를 사용합니다. 모든 부모 컴포넌트는 자식 컴포넌트에 props를 제공하여 정보를 전달할 수 있습니다. Props는 HTML 어트리뷰트와 유사해 보이지만 객체, 배열, 함수를 포함한 모든 JavaScript 값이 전달될 수 있습니다. 심지어 JSX도 가능합니다!

import { getImageUrl } from './utils.js'

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

이 주제를 배울 준비가 되셨나요?

Props를 전달하고 활용하는 방법을 배우려면 컴포넌트에 Props 전달하기 를 읽어보세요.

더 보기

조건부 렌더링

컴포넌트는 조건에 따라 다른 항목을 표시해야 하는 경우가 많습니다. React는 if 문, &&? : 연산자와 같은 자바스크립트 문법을 사용하여 JSX를 조건부로 렌더링할 수 있습니다.

이 예시에서는 JavaScript && 연산자를 사용하여 체크 표시를 조건부로 렌더링합니다.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item
          isPacked={true}
          name="Space suit"
        />
        <Item
          isPacked={true}
          name="Helmet with a golden leaf"
        />
        <Item
          isPacked={false}
          name="Photo of Tam"
        />
      </ul>
    </section>
  );
}

이 주제를 배울 준비가 되셨나요?

조건부 렌더링 을 통해 콘텐츠를 조건부로 렌더링하는 다양한 방법을 배울 수 있습니다.

더 보기

리스트 렌더링

데이터 모음으로부터 유사한 컴포넌트를 여러 개 표시하고 싶을 때가 종종 있습니다. React와 JavaScript의 filter()map()을 함께 사용하면 데이터 배열을 필터링하고 컴포넌트 배열로 변환할 수 있습니다.

각 배열 항목마다 key를 지정해야 합니다. 일반적으로 데이터베이스에서 가져온 ID를 key로 사용하게 될 것입니다. Key를 사용하면 리스트가 변경되더라도 React가 각 항목의 위치를 추적할 수 있습니다.

import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}

이 주제를 배울 준비가 되셨나요?

컴포넌트 목록을 렌더링하는 방법과 어떻게 key를 선택하는지에 대해 배우려면 리스트 렌더링 을 읽어보세요.

더 보기

컴포넌트 순수하게 유지하기

어떤 JavaScript 함수는 순수합니다. 순수 함수는 다음과 같은 특징이 있습니다.

  • 자신의 일만 처리합니다. 호출되기 전에 존재했던 어떤 객체나 변수도 변경하지 않습니다.
  • 입력이 같으면 출력도 같습니다. 순수 함수는 같은 입력을 받으면 언제나 같은 결과를 반환해야 합니다.

컴포넌트를 엄격하게 순수 함수로만 작성하면 코드 베이스가 커져도 이해하기 어려운 버그와 예측할 수 없는 동작을 피할 수 있습니다. 다음은 순수하지 않은 컴포넌트의 예시입니다.

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

기존 변수를 수정하는 대신 prop을 전달하여 컴포넌트를 순수하게 만들 수 있습니다.

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

이 주제를 배울 준비가 되셨나요?

컴포넌트를 순수하고 예측 가능한 함수로 작성하는 방법을 배우려면 컴포넌트 순수하게 유지하기 를 읽어보세요.

더 보기

트리로서의 UI

React는 컴포넌트와 모듈 간의 관계를 모델링하기 위해 트리를 사용합니다.

React 렌더 트리는 컴포넌트 간의 부모-자식 관계를 나타냅니다.

A tree graph with five nodes, with each node representing a component. The root node is located at the top the tree graph and is labelled 'Root Component'. It has two arrows extending down to two nodes labelled 'Component A' and 'Component C'. Each of the arrows is labelled with 'renders'. 'Component A' has a single 'renders' arrow to a node labelled 'Component B'. 'Component C' has a single 'renders' arrow to a node labelled 'Component D'.
A tree graph with five nodes, with each node representing a component. The root node is located at the top the tree graph and is labelled 'Root Component'. It has two arrows extending down to two nodes labelled 'Component A' and 'Component C'. Each of the arrows is labelled with 'renders'. 'Component A' has a single 'renders' arrow to a node labelled 'Component B'. 'Component C' has a single 'renders' arrow to a node labelled 'Component D'.

React 렌더 트리 예시

트리의 상단에 위치한 컴포넌트와 루트 컴포넌트 근처의 컴포넌트를 최상위 컴포넌트라고 합니다. 자식 컴포넌트가 없는 컴포넌트를 리프 컴포넌트라고 합니다. 이 컴포넌트 분류는 앱의 데이터 흐름과 성능을 이해하는 데 유용합니다.

자바스크립트 모듈 간의 관계를 모델링하는 것은 앱을 이해하는데 유용한 또 다른 방법입니다. 이를 모듈 의존성 트리라고 정의합니다.

A tree graph with five nodes. Each node represents a JavaScript module. The top-most node is labelled 'RootModule.js'. It has three arrows extending to the nodes: 'ModuleA.js', 'ModuleB.js', and 'ModuleC.js'. Each arrow is labelled as 'imports'. 'ModuleC.js' node has a single 'imports' arrow that points to a node labelled 'ModuleD.js'.
A tree graph with five nodes. Each node represents a JavaScript module. The top-most node is labelled 'RootModule.js'. It has three arrows extending to the nodes: 'ModuleA.js', 'ModuleB.js', and 'ModuleC.js'. Each arrow is labelled as 'imports'. 'ModuleC.js' node has a single 'imports' arrow that points to a node labelled 'ModuleD.js'.

모듈 의존성 트리 예시

의존성 트리는 종종 빌드 도구에 의해 클라이언트가 다운로드하고 렌더링하는 데 필요한 모든 관련 자바스크립트 코드를 bundle 하는 데에 사용됩니다. 큰 bundle 크기는 React 앱의 사용자 경험을 저하합니다. 모듈 의존성 트리를 이해하는 것은 이러한 문제를 디버깅하는 데 도움이 됩니다.

이 주제를 배울 준비가 되셨나요?

React 앱을 위한 렌더, 모듈 의존성 트리를 생성하는 법과 사용자 경험과 성능을 향상하기 위한 유용한 사고방식을 알고 싶은 경우 **트리로서의 UI**을 읽어보세요.

더 보기

What’s next?

첫 컴포넌트 페이지로 이동하여 이 장을 페이지별로 읽어보세요!

이미 이러한 주제에 대해 알고 있다면 상호작용 추가하기를 읽어보는 것은 어떨까요?