Skip to main content

리액트 기초

React Native는 JavaScript를 사용해 사용자 인터페이스를 구축하기 위한 인기 있는 오픈소스 라이브러리인 React 위에서 동작한다. React Native를 최대한 활용하려면 React 자체를 이해하는 것이 도움이 된다. 이 섹션은 React를 처음 접하는 사람에게 시작점이 되거나, 이미 알고 있는 사람에게는 복습 자료가 될 수 있다.

우리는 React의 핵심 개념을 다룰 것이다:

  • 컴포넌트
  • JSX
  • props
  • state

더 깊이 알고 싶다면 React 공식 문서를 확인하는 것을 권장한다.

첫 번째 컴포넌트 만들기

이 React 입문 가이드에서는 예제로 고양이를 사용한다. 이름이 필요하고 카페에서 일하는 친근하고 다가가기 쉬운 생물이다. 다음은 여러분이 만들 첫 번째 Cat 컴포넌트다:

이렇게 만들 수 있다: Cat 컴포넌트를 정의하려면 먼저 JavaScript의 import를 사용해 React와 React Native의 Text 코어 컴포넌트를 불러온다:

tsx
import React from 'react';
import {Text} from 'react-native';

컴포넌트는 함수로 시작한다:

tsx
const Cat = () => {};

컴포넌트를 청사진이라고 생각할 수 있다. 함수 컴포넌트가 반환하는 것은 React 엘리먼트로 렌더링된다. React 엘리먼트는 화면에 표시하고 싶은 것을 설명할 수 있게 해준다.

여기서 Cat 컴포넌트는 <Text> 엘리먼트를 렌더링한다:

tsx
const Cat = () => {
return <Text>Hello, I am your cat!</Text>;
};

JavaScript의 export default를 사용해 함수 컴포넌트를 앱 전체에서 사용할 수 있도록 내보낼 수 있다:

tsx
const Cat = () => {
return <Text>Hello, I am your cat!</Text>;
};

export default Cat;

이는 컴포넌트를 내보내는 여러 방법 중 하나다. 이 방식은 Snack Player와 잘 동작한다. 하지만 앱의 파일 구조에 따라 다른 규칙을 사용해야 할 수도 있다. 이 JavaScript import와 export에 관한 유용한 치트시트가 도움이 될 것이다.

이제 return 문을 자세히 살펴보자. <Text>Hello, I am your cat!</Text>는 엘리먼트를 편리하게 작성할 수 있게 해주는 JavaScript 문법인 JSX를 사용한다.

JSX

React와 React Native는 JSX라는 문법을 사용한다. JSX는 JavaScript 내부에 엘리먼트를 작성할 수 있게 해준다. 예를 들어 <Text>Hello, I am your cat!</Text>와 같은 형태로 작성할 수 있다. React 문서에는 JSX에 대한 포괄적인 가이드가 있으므로 더 많은 내용을 참고할 수 있다. JSX는 JavaScript이기 때문에 변수를 사용할 수 있다. 아래 예제에서는 고양이의 이름을 name으로 선언하고, 중괄호를 사용해 <Text> 안에 삽입했다.

중괄호 안에는 {getFullName("Rum", "Tum", "Tugger")}와 같은 함수 호출을 포함해 모든 JavaScript 표현식을 사용할 수 있다.

중괄호는 JSX 안에 JavaScript 기능을 연결하는 포털 역할을 한다고 생각하면 된다!

JSX는 React 라이브러리에 포함되어 있으므로 파일 상단에 import React from 'react'를 작성하지 않으면 작동하지 않는다!

커스텀 컴포넌트

React Native의 코어 컴포넌트에 대해 이미 알아보았다. React는 이러한 컴포넌트를 서로 중첩시켜 새로운 컴포넌트를 만들 수 있게 한다. 이러한 중첩 가능하고 재사용 가능한 컴포넌트가 React 패러다임의 핵심이다.

예를 들어, 아래와 같이 TextTextInputView 안에 중첩하면, React Native는 이를 함께 렌더링한다:

개발자 노트

웹 개발에 익숙하다면, <View><Text>가 HTML의 <div><p> 태그를 떠올리게 할 것이다. 이들을 애플리케이션 개발에서의 <div><p> 태그로 생각해도 무방하다.

<Cat> 컴포넌트를 여러 번, 여러 곳에서 렌더링할 수 있으며, 이를 통해 코드를 반복하지 않고도 동일한 컴포넌트를 재사용할 수 있다:

다른 컴포넌트를 렌더링하는 컴포넌트는 부모 컴포넌트라 부른다. 여기서 Cafe는 부모 컴포넌트이고, 각 Cat자식 컴포넌트이다.

카페에 원하는 만큼 고양이를 추가할 수 있다. 각 <Cat>은 고유한 엘리먼트를 렌더링하며, 이를 props로 커스터마이징할 수 있다.

Props

Props는 "properties"의 줄임말이다. Props를 사용하면 React 컴포넌트를 커스터마이징할 수 있다. 예를 들어, 아래 예제에서는 각 <Cat> 컴포넌트에 다른 name을 전달해 Cat이 렌더링하도록 한다:

React Native의 코어 컴포넌트 대부분도 props를 통해 커스터마이징할 수 있다. 예를 들어, Image를 사용할 때 source라는 prop을 전달해 어떤 이미지를 표시할지 정의한다:

Imagestyle을 포함해 다양한 props를 지원한다. style은 디자인과 레이아웃 관련 속성-값 쌍을 담은 JS 객체를 인자로 받는다.

style의 width와 height를 둘러싼 이중 중괄호 {{ }}를 주목하자. JSX에서 JavaScript 값은 {}로 참조한다. 이는 문자열이 아닌 배열이나 숫자와 같은 값을 props로 전달할 때 유용하다: <Cat food={["fish", "kibble"]} age={2} />. 그러나 JS 객체도 중괄호로 표시한다: {width: 200, height: 200}. 따라서 JSX에서 JS 객체를 전달하려면 객체를 또 다른 중괄호 쌍으로 감싸야 한다: {{width: 200, height: 200}}

Props와 코어 컴포넌트인 Text, Image, View를 사용해 많은 것을 만들 수 있다! 하지만 인터랙티브한 무언가를 만들려면 상태(state)가 필요하다.

상태

props를 컴포넌트의 렌더링 방식을 구성하는 인자로 생각한다면, **상태(state)**는 컴포넌트의 개인 데이터 저장소와 같다. 상태는 시간에 따라 변하는 데이터나 사용자 상호작용으로부터 발생하는 데이터를 다루는 데 유용하다. 상태는 컴포넌트에 기억력을 부여한다!

일반적으로, 컴포넌트가 렌더링될 때 설정할 값은 props를 사용한다. 시간에 따라 변할 것으로 예상되는 데이터는 상태를 사용해 추적한다.

다음 예제는 배고픈 두 마리의 고양이가 있는 고양이 카페를 배경으로 한다. 고양이의 이름과 달리 시간에 따라 변할 것으로 예상되는 배고픔 상태를 저장한다. 고양이에게 밥을 주려면 버튼을 누르면 되는데, 이는 상태를 업데이트한다.

컴포넌트에 상태를 추가하려면 React의 useState을 호출한다. 훅은 React 기능에 "연결"할 수 있게 해주는 함수다. 예를 들어, useState는 함수 컴포넌트에 상태를 추가할 수 있게 해주는 훅이다. React 문서에서 다른 종류의 훅에 대해 더 알아볼 수 있다.

먼저, React에서 useState를 다음과 같이 가져온다:

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

그런 다음 컴포넌트 함수 내에서 useState를 호출해 상태를 선언한다. 이 예제에서 useStateisHungry라는 상태 변수를 생성한다:

tsx
const Cat = (props: CatProps) => {
const [isHungry, setIsHungry] = useState(true);
// ...
};

useState를 사용해 문자열, 숫자, 불리언, 배열, 객체 등 모든 종류의 데이터를 추적할 수 있다. 예를 들어, 고양이가 쓰다듬어진 횟수를 추적하려면 const [timesPetted, setTimesPetted] = useState(0)를 사용할 수 있다!

useState를 호출하면 두 가지 작업이 수행된다:

  • 초기값을 가진 "상태 변수"를 생성한다. 이 경우 상태 변수는 isHungry이고 초기값은 true다.
  • 해당 상태 변수의 값을 설정할 함수를 생성한다. 이 경우 setIsHungry다.

이름은 무엇을 사용해도 상관없다. 하지만 [<getter>, <setter>] = useState(<initialValue>) 패턴으로 생각하면 편리하다.

다음으로 Button 코어 컴포넌트를 추가하고 onPress prop을 설정한다:

tsx
<Button
onPress={() => {
setIsHungry(false);
}}
//..
/>

이제 누군가 버튼을 누르면 onPress가 실행되어 setIsHungry(false)를 호출한다. 이는 isHungry 상태 변수를 false로 설정한다. isHungryfalse가 되면 Buttondisabled prop이 true로 설정되고 title도 변경된다:

tsx
<Button
//..
disabled={!isHungry}
title={isHungry ? 'Give me some food, please!' : 'Thank you!'}
/>
>

> `isHungry`[const](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/const)임에도 불구하고 재할당되는 것처럼 보일 수 있다! 실제로는 `setIsHungry`와 같은 상태 설정 함수가 호출되면 해당 컴포넌트가 리렌더링된다. 경우 `Cat` 함수가 다시 실행되고, `useState``isHungry`의 다음 값을 반환한다.

마지막으로 고양이들을 `Cafe` 컴포넌트 안에 넣는다:

```tsx
const Cafe = () => {
return (
<>
<Cat name="Munkustrap" />
<Cat name="Spot" />
</>
);
};

위의 <></>를 보았는가? 이 JSX 조각들은 프래그먼트다. 인접한 JSX 엘리먼트는 감싸는 태그로 묶어야 한다. 프래그먼트를 사용하면 View와 같은 불필요한 감싸는 엘리먼트를 추가하지 않고도 이를 수행할 수 있다.


이제 React와 React Native의 코어 컴포넌트를 모두 다뤘으니, TextInput 처리를 살펴보며 이러한 코어 컴포넌트에 대해 더 깊이 알아보자.