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> ); }
컴포넌트 Import 및 Export 하기
하나의 파일에 많은 컴포넌트를 선언할 수 있지만, 파일이 커지면 탐색하기 어려워집니다. 이를 해결하기 위해 컴포넌트를 별도의 파일로 만들어 export하고 다른 파일에서 해당 컴포넌트를 import할 수 있습니다.
import Profile from './Profile.js'; export default function Gallery() { return ( <section> <h1>Amazing scientists</h1> <Profile /> <Profile /> <Profile /> </section> ); }
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에서 중괄호를 이용하여 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> ); }
조건부 렌더링
컴포넌트는 조건에 따라 다른 항목을 표시해야 하는 경우가 많습니다. 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> ); }
컴포넌트 순수하게 유지하기
어떤 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 렌더 트리는 컴포넌트 간의 부모-자식 관계를 나타냅니다.
트리의 상단에 위치한 컴포넌트와 루트 컴포넌트 근처의 컴포넌트를 최상위 컴포넌트라고 합니다. 자식 컴포넌트가 없는 컴포넌트를 리프 컴포넌트라고 합니다. 이 컴포넌트 분류는 앱의 데이터 흐름과 성능을 이해하는 데 유용합니다.
자바스크립트 모듈 간의 관계를 모델링하는 것은 앱을 이해하는데 유용한 또 다른 방법입니다. 이를 모듈 의존성 트리라고 정의합니다.
의존성 트리는 종종 빌드 도구에 의해 클라이언트가 다운로드하고 렌더링하는 데 필요한 모든 관련 자바스크립트 코드를 bundle 하는 데에 사용됩니다. 큰 bundle 크기는 리액트 앱의 사용자 경험을 저하합니다. 모듈 의존성 트리를 이해하는 것은 이러한 문제를 디버깅하는 데 도움이 됩니다.
What’s next?
첫 컴포넌트 페이지로 이동하여 이 장을 페이지별로 읽어보세요!
이미 이러한 주제에 대해 알고 있다면 상호작용 추가하기를 읽어보는 것은 어떨까요?