리액트 기초
React Native는 JavaScript로 사용자 인터페이스를 구축하기 위한 인기 있는 오픈소스 라이브러리인 React 위에서 동작한다. React Native를 최대한 활용하려면 React 자체를 이해하는 것이 도움이 된다. 이 섹션은 React를 처음 시작하거나 복습할 수 있는 기회를 제공한다.
React의 핵심 개념을 다룰 것이다:
- 컴포넌트
- JSX
- props
- state
더 깊이 알고 싶다면 React 공식 문서를 확인하는 것을 추천한다.
첫 번째 컴포넌트 만들기
이 React 입문서의 나머지 부분에서는 예제로 고양이를 사용한다. 이름이 필요하고 카페에서 일하는 친근하고 다가가기 쉬운 생물이다. 다음은 여러분이 만들 첫 번째 Cat 컴포넌트다:
이렇게 만든다: Cat
컴포넌트를 정의하려면 먼저 JavaScript의 import
를 사용해 React와 React Native의 Text
코어 컴포넌트를 가져온다:
import React from 'react';
import {Text} from 'react-native';
컴포넌트는 함수로 시작한다:
const Cat = () => {};
컴포넌트를 청사진으로 생각할 수 있다. 함수 컴포넌트가 반환하는 것은 React 엘리먼트로 렌더링된다. React 엘리먼트는 화면에 표시하고 싶은 내용을 설명한다.
여기서 Cat
컴포넌트는 <Text>
엘리먼트를 렌더링한다:
const Cat = () => {
return <Text>Hello, I am your cat!</Text>;
};
JavaScript의 export default
를 사용해 함수 컴포넌트를 앱 전체에서 사용할 수 있도록 내보낼 수 있다:
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는 자바스크립트 안에 엘리먼트를 작성할 수 있게 해주며, 예를 들어 <Text>Hello, I am your cat!</Text>
와 같이 표현할 수 있다. React 문서에는 JSX에 대한 포괄적인 가이드가 있으므로 더 많은 정보를 얻을 수 있다. JSX는 자바스크립트이기 때문에 변수를 내부에 사용할 수 있다. 여기서는 고양이의 이름을 name
으로 선언하고, 중괄호를 사용해 <Text>
안에 삽입한다.
중괄호 사이에는 어떤 자바스크립트 표현식도 사용할 수 있다. 예를 들어 {getFullName("Rum", "Tum", "Tugger")}
와 같은 함수 호출도 가능하다.
- TypeScript
- JavaScript
중괄호는 JSX 안에서 자바스크립트 기능을 사용할 수 있는 포털을 만든다고 생각하면 된다!
JSX는 React 라이브러리에 포함되어 있으므로, 파일 상단에
import React from 'react'
를 추가하지 않으면 작동하지 않는다!
커스텀 컴포넌트
이전에 React Native의 코어 컴포넌트를 살펴보았다. React는 이러한 컴포넌트를 서로 중첩하여 새로운 컴포넌트를 만들 수 있게 한다. 이렇게 중첩 가능하고 재사용할 수 있는 컴포넌트가 React 패러다임의 핵심이다.
예를 들어, 아래와 같이 Text
와 TextInput
을 View
안에 중첩하면, React Native는 이를 함께 렌더링한다:
개발자 노트
- Android
- Web
웹 개발에 익숙하다면,
<View>
와<Text>
가 HTML의<div>
와<p>
태그를 떠올리게 할 것이다. 이들은 애플리케이션 개발에서<div>
와<p>
태그와 유사한 역할을 한다고 생각하면 된다.
안드로이드에서는 보통
LinearLayout
,FrameLayout
,RelativeLayout
등에 뷰를 배치하여 화면에 자식 뷰를 어떻게 배열할지 정의한다. React Native에서는View
가 자식 뷰의 레이아웃을 위해 Flexbox를 사용한다. 자세한 내용은 Flexbox를 사용한 레이아웃 가이드에서 확인할 수 있다.
<Cat>
컴포넌트를 여러 번, 여러 곳에서 렌더링할 수 있으며, 이를 통해 코드를 반복하지 않고도 동일한 컴포넌트를 재사용할 수 있다:
다른 컴포넌트를 렌더링하는 컴포넌트를 부모 컴포넌트라고 한다. 여기서 Cafe
는 부모 컴포넌트이고, 각 Cat
은 자식 컴포넌트이다.
카페에 원하는 만큼 고양이를 넣을 수 있다. 각 <Cat>
은 고유한 엘리먼트를 렌더링하며, props를 통해 이를 커스터마이징할 수 있다.
Props
Props는 'properties'의 줄임말이다. Props를 사용하면 React 컴포넌트를 커스터마이징할 수 있다. 예를 들어, 아래 코드에서 각 <Cat>
컴포넌트에 다른 name
을 전달해 Cat이 렌더링하도록 한다:
- TypeScript
- JavaScript
React Native의 코어 컴포넌트 대부분도 props를 통해 커스터마이징할 수 있다. 예를 들어, Image
를 사용할 때 source
라는 prop을 전달해 어떤 이미지를 표시할지 정의한다:
Image
는 style
을 포함해 다양한 props를 지원한다. style
은 디자인과 레이아웃 관련 속성-값 쌍으로 구성된 JS 객체를 받는다.
style
의 너비와 높이를 감싸는 이중 중괄호{{ }}
를 주목하라. JSX에서 JavaScript 값은{}
로 참조한다. 문자열이 아닌 배열이나 숫자 같은 값을 props로 전달할 때 유용하다:<Cat food={["fish", "kibble"]} age={2} />
. 하지만 JS 객체도 중괄호로 표시한다:{width: 200, height: 200}
. 따라서 JSX에서 JS 객체를 전달하려면 객체를 또 다른 중괄호로 감싸야 한다:{{width: 200, height: 200}}
Text
, Image
, View
같은 코어 컴포넌트와 props를 활용해 다양한 것을 만들 수 있다! 하지만 인터랙티브한 기능을 구현하려면 상태(state)가 필요하다.
상태
props를 컴포넌트 렌더링을 설정하는 인자로 생각한다면, 상태는 컴포넌트의 개인 데이터 저장소와 같다. 상태는 시간이 지남에 따라 변하거나 사용자 상호작용에서 오는 데이터를 다루는 데 유용하다. 상태는 컴포넌트에 기억력을 부여한다!
일반적으로, 컴포넌트가 렌더링될 때 설정하는 데는 props를 사용하고, 시간이 지남에 따라 변할 것으로 예상되는 데이터를 추적하는 데는 상태를 사용한다.
다음 예제는 두 배고픈 고양이가 먹이를 기다리는 고양이 카페를 배경으로 한다. 고양이의 이름과 달리 시간이 지남에 따라 변할 것으로 예상되는 배고픔은 상태로 저장된다. 고양이에게 먹이를 주려면 버튼을 누르면 되는데, 이 버튼은 상태를 업데이트한다.
React의 useState
Hook을 호출해 컴포넌트에 상태를 추가할 수 있다. Hook은 React 기능에 "연결"할 수 있게 해주는 함수다. 예를 들어, useState
는 함수 컴포넌트에 상태를 추가할 수 있게 해주는 Hook이다. React 문서에서 다른 종류의 Hook에 대해 더 알아볼 수 있다.
- TypeScript
- JavaScript
먼저 React에서 useState
를 다음과 같이 가져온다:
import React, {useState} from 'react';
그런 다음 컴포넌트 함수 내에서 useState
를 호출해 상태를 선언한다. 이 예제에서 useState
는 isHungry
상태 변수를 생성한다:
const Cat = (props: CatProps) => {
const [isHungry, setIsHungry] = useState(true);
// ...
};
useState
를 사용해 문자열, 숫자, 불리언, 배열, 객체 등 모든 종류의 데이터를 추적할 수 있다. 예를 들어, 고양이가 쓰다듬어진 횟수를 추적하려면const [timesPetted, setTimesPetted] = useState(0)
을 사용할 수 있다!
useState
를 호출하면 두 가지 일이 일어난다:
- 초기값을 가진 "상태 변수"를 생성한다. 이 경우 상태 변수는
isHungry
이고 초기값은true
다. - 그 상태 변수의 값을 설정하는 함수를 생성한다. 이 경우
setIsHungry
다.
이름은 중요하지 않다. 하지만 [<getter>, <setter>] = useState(<initialValue>)
패턴으로 생각하면 편리하다.
다음으로 Button Core Component를 추가하고 onPress
prop을 설정한다:
<Button
onPress={() => {
setIsHungry(false);
}}
//..
/>
이제 누군가 버튼을 누르면 onPress
가 실행되어 setIsHungry(false)
를 호출한다. 이는 isHungry
상태 변수를 false
로 설정한다. isHungry
가 false
가 되면 Button
의 disabled
prop이 true
로 설정되고 title
도 변경된다:
<Button
//..
disabled={!isHungry}
title={isHungry ? 'Give me some food, please!' : 'Thank you!'}
/>
isHungry
가 const임에도 불구하고 재할당되는 것처럼 보일 수 있다! 사실은setIsHungry
같은 상태 설정 함수가 호출되면 해당 컴포넌트가 리렌더링된다. 이 경우Cat
함수가 다시 실행되고, 이번에는useState
가isHungry
의 다음 값을 제공한다.
마지막으로 고양이들을 Cafe
컴포넌트 안에 넣는다:
const Cafe = () => {
return (
<>
<Cat name="Munkustrap" />
<Cat name="Spot" />
</>
);
};
위의
<>
와</>
를 보았는가? 이 JSX 조각은 프래그먼트다. 인접한 JSX 엘리먼트는 반드시 감싸는 태그 안에 있어야 한다. 프래그먼트를 사용하면View
같은 불필요한 감싸는 엘리먼트를 중첩하지 않고도 이를 수행할 수 있다.
이제 React와 React Native의 Core Components를 모두 다뤘으니, TextInput 처리를 살펴보며 이 Core Components에 대해 더 깊이 알아보자.