리액트 공식 문서를 천천히 읽어보기로 했다.
컴포넌트 생성 및 중첩
리액트 앱은 컴포넌트로 구성되어 있다. 컴포넌트는 고유의 논리와 모양을 가진 UI의 일부이다. 컴포넌트는 버튼만큼 작을 수도 있고 페이지만큼 클 수도 있다.
리액트 컴포넌트는 마크업을 반환하는 자바스크립트 함수이다.
function MyButton() {
return (
<button>I'm a button</button>
);
}
MyButton을 선언했으니 다른 컴포넌트에 중첩시킬 수 있다.
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
<MyButton />
은 대문자로 시작한다. 이것이 리액트 컴포넌트임을 나타낸다. html 태그가 반드시 소문자여야 함과 반대로 리액트 컴포넌트 이름은 항상 대문자로 시작해야 한다.
function MyButton() {
return (
<button>
I'm a button
</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
위와 같은 마크업 문법을 JSX 라고 하고, 편의상 대부분의 리액트 프로젝트는 JSX 를 사용한다. 로컬 개발을 위해 추천하는 모든 툴은 JSX를 지원한다.
JSX는 HTML 보다 엄격하다. 태그를 반드시 닫아야하며 <br />
, 컴포넌트는 여러개의 JSX 태그를 반환할 수 없다. 따라서 <div>..</div>
혹은 <>..</>
로 꼭 감싸야한다.
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
리액트에서는, CSS 클래스를 classname
으로 부여한다. HTML에서의 class
와 같다. 리액트는 CSS 파일을 추가하는 방법을 규정하지 않는다. 가장 간단한 경우는 HTML 에 <link>
태그를 추가하는 것이다.
JSX는 Javascript 안에 마크업을 할 수 있도록 한다. 중괄호는 다시 자바스크립트로 돌아가서 코드로부터의 변수를 가져오고 유저에게 보여준다.
return (
<h1>
{user.name}
</h1>
);
JSX속성에서 Javascript 로 이스케이프 할 수도 있지만 따옴표 대신 중괄호를 사용해야한다. 예를 들어 className="avatar"
는 CSS 클래스로 "avatar"
문자열을 전달하지만 src={user.imageUrl}
은 user.imageUrl
변수값을 읽은 다음 이 값을 src 특성으로 전달한다.
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
리액트에서는 조건을 작성하기 위한 특별한 구문이 없다. 대신 일반 자바스크립트 코드를 작성할 때 사용하는 것과 동일한 기술을 사용하게 된다. 예를 들어 if문을 사용하여 조건부로 JSX를 포함할 수 있다.
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
만약 더 간결한 코드를 선호한다면, 옵셔널체이닝을 사용할 수 있다.
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>
else
가 필요 없다면 &&
구문을 사용할 수 있다.
<div>
{isLoggedIn && <AdminPanel />}
</div>
Javascript 의 기능인 for
반복문과 map()
함수를 이용해서 컴포넌트의 리스트를 렌더링 할 수 있다.
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
컴포넌트 안에 map()
함수를 사용하여 배열을 <li>
항목 배열로 변환한다.
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
<li>
가 키 속성을 갖는 방식에 주목해야 한다. 리스트의 각각의 아이템에 고유하게 식별하는 문자나 숫자를 전달해야 한다. 대개 키는 데이터베이스 ID 같은 데이터에서 가져오는 값이다. 리액트는 키를 사용하여 나중에 항목을 삽입, 삭제 또는 다시 정렬할 경우 발생한 상황을 알 수 있다.
구성 요소 내에서 이벤트 핸들러 기능을 선언하여 이벤트에 응답할 수 있다.
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
onClick={handleClick}
의 마지막에 괄호()
가 없는 것을 주목해야 한다. 이벤트 핸들러 함수를 호출하지 마라: 그냥 전달해주기만 하면 된다. 유저가 버튼을 클릭하면 리액트가 이벤트 핸들러를 호출할 것이다.
컴포넌트가 어떤 정보를 기억하고 나타내기를 원할 수 있다. 예를 들어 버튼을 클릭한 횟수를 계산할 수 있다.
import { useState } from 'react';
우선 useState를 불러온 후
function MyButton() {
const [count, setCount] = useState(0);
컴포넌트 내부에 상태 변수를 선언한다.
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
useState에서는 현재 상태(count)와 이를 업데이트할 수 있는 함수(setCount)의 두 가지를 얻을 수 있다. 이름은 무엇이든 지정할 수 있지만, 관례는 [something, setSomething]으로 쓴다.
버튼이 처음 표시되면 State()
를 사용하기 위해 0을 넘겼기 때문에 카운트가 0이 된다. 상태를 변경하려면 setCount()를 호출하고 새 값을 전달한다. 이 버튼을 클릭하면 카운터가 증가한다.
import { useState } from 'react';
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
리액트는 컴포넌트 안의 함수를 다시 부를 것이고, count가 1, 2 와 같이 점점 늘어날 것이다. 동일한 컴포넌트를 여러 번 렌더링하면 각 컴포넌트의 상태를 얻을 것이다.
use로 시작하는 함수를 Hooks 라고 부른다. useState
는 React에서 제공하는 기본 제공 Hook다. API reference에서 다른 내장 Hooks도 찾을 수 있다. 기존 Hooks를 결합하여 자신만의 Hooks를 만들 수도 있다.
Hooks는 다른 기능보다 제한적이다. 컴포넌트의 맨 위에서만 Hooks를 호출할 수 있다. 조건이나 반복문에서 useState
를 사용하려면 새로 컴포넌트를 만들어 불러야 한다.