사용자 인터페이스를 구축하는 자바스크립트 라이브러리
좀더 간단히 사용자 인터페이스를 구축할 수 있다.
컴포넌트다!
HTML과 자바스크립트 그리고 CSS로 구성된 재사용 가능하고 반응하는 컴포넌트를 만들 수 있게 해준다.
선언적 접근 방식 사용
실제 웹페이지에서 어떤 요소가 추가되거나, 삭제되고, 업데이트되어야 하는지 해결하는 것
공식 github: create-react-app
node.js 설치
npx create-react-app my-app
cd my-app
npm start
리액트 프로젝트 파일을 받았을 때 구동방법
npm install
npm start
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
프로젝트가 실행되었을 때 가장 먼저 실행되는 파일
react-dom이라는 서드 파티 라이브러리에서 ReactDOM을 임포트해서 index.js 파일의 내부 변수로 만든다.
ReactDOM은 render라는 메소드를 호출한다.
<div>
가 <App/>
으로 대체된다.<App/>
은 JSX구문function App() {
return (
<div>
<h2>Let's get started!</h2>
</div>
);
}
export default App;
App이라는 함수를 갖고 있고, 이 함수를 내보낸다.
App 함수는 자바스크립트 안에 있는 Html 코드를 반환한다. (JSX 구문)
components라는 폴더를 만들어서 컴포넌트 생성
파일 이름은 첫번째 알파벳 대문자, 두단어 이상이 합해졌다면 단어들의 첫번째 알파벳 대문자 예) ExpensiveItem.js
리액트에 있는 컴포넌트는 단지 자바스크립트 함수
만든 함수를 내보내는 코드를 작성
function ExpenseItem() {
return <h2>Expense item!</h2>
}
export default ExpenseItem;
App.js 파일에서 컴포넌트 임포트
import ExpenseItem from "./components/ExpenseItem";
function App() {
return (
<div>
<h2>Let's get started!</h2>
<ExpenseItem></ExpenseItem>
</div>
);
}
export default App;
리액트 요소 이름 규칙
JSX 규칙
function ExpenseItem() {
return (
<div>
<div>March 28th 2021</div>
<div>
<h2>Car Insurance</h2>
<div>$294.67</div>
</div>
</div>
);
}
export default ExpenseItem;
className=""
이러한 형태로 입력해야한다.내용, 가격을 데이터를 받아서 입력된 값으로 동적으로 출력하고 싶다.
자바스크립트 코드를 추가
const expenseDate = new Date(2021, 2, 28)
const expenseTitle = 'Car Insurance'
const expenseAmount = 294.67
리액트 JSX에서 중괄호 {} 안에 일반적인 자바스크립트 코드를 작성할 수 있다.
date 객체는 string으로 변환하는 toISOString() 메소드 사용
function ExpenseItem() {
const expenseDate = new Date(2021, 2, 28)
const expenseTitle = 'Car Insurance'
const expenseAmount = 294.67
return (
<div className='expense-item'>
<div>{expenseDate.toISOString()}</div>
<div className="expense-item__description">
<h2>{expenseTitle}</h2>
<div className="expense-item__price">${expenseAmount}</div>
</div>
</div>
);
}
일반 자바스크립트에서 함수를 재사용할 때 매개변수를 받아 재사용 할 수 있다.
리액트에서 또한 매개변수를 사용하거나 props를 통해 재사용이 가능하다.
현재 컴포넌트에 저장되어있는 값들이 컴포넌트가 아니라 App.js에 저장되어야한다.
비용을 저장할 배열 생성
const expenses = [
{
id: 'e1',
title: 'Toilet Paper',
amount: 94.12,
date: new Date(2020, 7, 14),
},
{ id: 'e2', title: 'New TV', amount: 799.49, date: new Date(2021, 2, 12) },
{
id: 'e3',
title: 'Car Insurance',
amount: 294.67,
date: new Date(2021, 2, 28),
},
{
id: 'e4',
title: 'New Desk (Wooden)',
amount: 450,
date: new Date(2021, 5, 12),
},
];
ExpenseItem.js의 객체들은 데이터를 밖(App.js)에서 받아와야 한다.
컴포넌트에 속성을 추가 (중괄호 구문 사용)
<ExpenseItem title={expenses[0].title} amount={expenses[0].amount} date={expenses[0].date}></ExpenseItem>
<ExpenseItem title={expenses[1].title} amount={expenses[1].amount} date={expenses[1].date}></ExpenseItem>
<ExpenseItem title={expenses[2].title} amount={expenses[2].amount} date={expenses[2].date}></ExpenseItem>
<ExpenseItem title={expenses[3].title} amount={expenses[3].amount} date={expenses[3].date}></ExpenseItem>
리액트는 모든 컴포넌트에서 한 개의 매개변수만을 사용하고, 그 매개변수는 프로퍼티로서 모든 속성을 받는 객체가 된다.
function ExpenseItem(props) {
return (
<div className='expense-item'>
<div>{props.date.toISOString()}</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
date를 보기좋게 변경하기
date.props로 부터 month, year, day를 추출
toLocaleString() 메소드 활용 참고
function ExpenseItem(props) {
const month = props.date.toLocaleString('en-US', { month: 'long' })
const day = props.date.toLocaleString('en-US', { day: '2-digit' })
const year = props.date.getFullYear()
return (
<div className='expense-item'>
<div>
<div>{month}</div>
<div>{year}</div>
<div>{day}</div>
</div>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
현재의 ExpenseItem을 두가지 컴포넌트로 분리할 수 있다.
이미 갖고있는 ExpenseItem 컴포넌트
import ExpenseDate from './ExpenseDate';
import './ExpenseItem.css'
function ExpenseItem(props) {
return (
<div className='expense-item'>
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</div>
);
}
export default ExpenseItem;
date를 달력 형태로 화면에 렌더링하는 컴포넌트
function ExpenseDate(props) {
const month = props.date.toLocaleString('en-US', { month: 'long' })
const day = props.date.toLocaleString('en-US', { day: '2-digit' })
const year = props.date.getFullYear()
return (
<div>
<div>{month}</div>
<div>{year}</div>
<div>{day}</div>
</div>
)
}
export default ExpenseDate
컴포넌트를 분리하면
정리
다른 컨텐츠 주변에서 셸 역할을 하는 컴포넌트를 생성하고 싶다면?
props를 통해 모든 것을 설정하는 컴포넌트가 아니라 컴포넌트의 열고 닫는 태그 사이에 있는 컨텐츠를 전달하고 싶을 수도 있다.
현재 만든 페이지를 보면 ExpenseItem.js와 Expenses.js 파일의 <div>
컨테이너가 공동으로 갖고 있는 스타일을 추출하여 별도의 컴포넌트를 만들 수 있다.
box-shadow, border-radius를 새로 만든 Card 컴포넌트로 옮긴다.
Card 컴포넌트가 ExpenseItem이나 Expenses 컨텐츠를 감싸는 셸같은 역할을 한다.
ExpenseItem의 <div>
를 사용자 지정 Card 컴포넌트로 대체할 수 있다.
그러나 사용자 지정 컴포넌트는 일종의 컨텐츠를 감싸는 래퍼로 사용할 수 없다.
해결방법
Card에 props를 취하도록 하고, 모든 컴포넌트가 갖는 리액트에 내장된 특별한 props를 사용한다.
function Card(props) {
return <div className="card">{props.childeren}</div>
}
props.children은 예약어이고, props.children의 값은 사용자 지정 컴포넌트의 열고 닫는 태그 사이에 있는 컨텐츠를 가리킨다.
이때 css가 깨지는 것을 알 수 있는데 Card가 사용자 지정 컴포넌트 이므로 className이라는 속성을 줄 수 없는 것이다.
변수 Card의 클래스에 props.className을 추가
function Card(props) {
const classes = 'card ' + props.className
return <div className={classes}>{props.children}</div>
}
Expenses에도 div대신 Card 컴포넌트 사용
정리
JSX 코드를 사용하지 않고 직접 react 메소드를 사용한다면 복잡한 코드를 작성해야 한다.
return React.createElement(
'div',
{},
React.createElement('h2', {}, "Let's get started!"),
React.createElement(Expenses, { item: expenses })
)
이전에는 import React from 'react'
라는 구문을 꼭 넣어주어야 JSX구문을 html로 변환이 가능했지만, 최근에는 React를 임포트하지 않아도 내부에서 변환이 일어나기 때문에 임포트 하지 않아도 된다.
하지만 JSX 코드를 사용할 때 리액트가 사실은 내부에서 사용되고 있다는 것을 강조하기 위해 넣어주도록 하자.
components 폴더 안에 모든 컴포넌트 파일들을 넣는 것 보다 기능에 따라 분류
컴포넌트 폴더를 구분하면 파일을 잘 정리하고 컴포넌트를 체계적으로 유지할 수 있다.