public
디렉토리에 있어야 합니다.<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>주사위 게임</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
src
디렉토리에 위치합니다.import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<h1>안녕 리액트!</h1>
);
createRoot()
이 메소드는 React 18에서 추가된 새로운 API 중 하나이며, ReactDOM 패키지의 일부입니다. 이 메소드는 React 애플리케이션의 루트 엘리먼트를 선택하고, 이를 관리할 Root 객체를 리턴합니다.render()
Root 객체의 렌더 메소드를 호출하여 JSX를 사용하여 작성된 <h1>안녕 리액트!</h1>
엘리먼트를 렌더링합니다.createRoot()
메소드 대신에 ReactDOM.render()
메소드를 사용하여 렌더링을 처리했었습니다.Self-closing tag
camelCase attribute name
onClick
onBlur
onFocus
onMouseDown
onMouseOver
tabIndex
data-*
속성은 camelCase가 아니라 기존의 HTML 문법 그대로 작성해야 합니다.htmlFor
className
style attribute
<div style="color: red;"></div>
대신 <div style={{ color: "red" }}></div>
와 같이 작성해야 합니다.<>
</>
와 같이 작성합니다.react
에서 { Fragment }
를 import해서 <Fragment>
</Fragment>
와 같이 작성할 수도 있습니다.const product = 'MacBook';
const model = ' Air';
const imageUrl = "url"
function handleClick() {
alert('곧 도착합니다!');
}
<>
<h1>나만의 {product} 주문하기</h1>
<h1>나만의 {product.toUpperCase()} 주문하기</h1>
<h1>나만의 {product + model} 주문하기</h1>
<img src={imageUrl} alt="제품 사진" />
<button onClick={handleClick}>확인</button>
</>
const element = <h1>Hello, world!</h1>;
// src/Dice.js
import diceBlue01 from './assets/dice-blue-1.svg'
function Dice() {
return <img src={diceBlue01} alt="주사위" />;
}
export default Dice;
// src/App.js
import Dice from './Dice';
function App() {
return (
<div>
<Dice />
</div>
);
}
export default App;
React.Component
클래스를 상속하여 정의되는 리액트 컴포넌트입니다.render()
메소드를 반드시 구현해야 합니다.render()
메소드는 컴포넌트의 UI를 반환하는 함수입니다.constructor()
메소드에서 this.state 객체를 초기화하고, setState()
메소드를 사용하여 상태를 업데이트해야 합니다.setState()
메소드를 호출하면 React는 상태를 변경하고, render()
메소드를 호출하여 UI를 업데이트합니다.class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<div>Count: {this.state.count}</div>
<button onClick={() => this.handleClick()}>Click me</button>
</div>
);
}
}
destructuring
을 활용할 수 있습니다.destructuring
, default parameter
사용)// src/Dice.js
import ...
const DICE_IMAGES = {
blue: [diceBlue01, diceBlue02, diceBlue03, diceBlue04, diceBlue05, diceBlue06],
red: [diceRed01, diceRed02, diceRed03, diceRed04, diceRed05, diceRed06],
};
function Dice({ color = 'blue', num = 1 }) {
const src = DICE_IMAGES[color][num - 1];
const alt = `${color} ${num}`;
return <img src={src} alt={alt} />;
}
export default Dice;
props.children
으로 접근할 수 있습니다.// App.js
...
<Button>던지기</Button>
<Button>처음부터</Button>
...
// Button.js
function Button({ children }) {
return <button>{children}</button>;
}
export default Button;
useState()
는 React Hooks API에서 제공하는 함수 중 하나로, 함수형 컴포넌트에서도 state를 사용할 수 있게 해줍니다.useState()
함수는 배열을 반환하며, 첫 번째 요소는 현재 상태 값이고, 두 번째 요소는 해당 상태 값을 업데이트하고 컴포넌트를 다시 렌더링하는 setter 함수입니다.useState()
의 파라미터는 현재 상태 값의 초기값을 저장합니다.// App.js
import { useState } from 'react';
import Button from './Button';
import Dice from './Dice';
function random(n) {
return Math.ceil(Math.random() * n);
}
function App() {
const [num, setNum] = useState(1);
const handleRollClick = () => {
const nextNum = random(6);
setNum(nextNum);
};
const handleClearClick = () => {
setNum(1);
}
return (
<div>
<div>
<Button onClick={handleRollClick}>던지기</Button>
<Button onClick={handleClearClick}>처음부터</Button>
</div>
<Dice color="red" num={num} />
</div>
);
}
export default App;
// Button.js
function Button({ children, onClick }) {
return <button onClick={onClick}>{children}</button>;
}
export default Button;
...object
spread 문법으로 복사한 참조형 state를 setter 함수에 전달해서 해결할 수 있다.Lodash
나 Immer
같은 라이브러리를 사용하거나, JSON.stringify()
와 JSON.parse()
함수를 이용하여 객체를 문자열로 변환한 후 다시 객체로 변환하는 방법, 또는 재귀 함수를 이용한 깊은 복사를 하여 해결할 수 있다.function App() {
const [gameHistory, setGameHistory] = useState([]);
const handleRollClick = () => {
const nextNum = random(6);
setGameHistory([...gameHistory, nextNum]);
};
const handleClearClick = () => {
setGameHistory([]);
}
return {
...
<p>{gameHistory.join(', ')}</p>
...
};
}
state lifting
은 부모 컴포넌트로부터 자식 컴포넌트로 상태를 전달하고 업데이트하는 기술입니다.ReactDOMServer.renderToString()
메소드를 사용하여 리액트 컴포넌트를 렌더링할 수 있습니다. 이를 통해 서버에서 생성된 HTML 코드를 클라이언트에 전달하면 클라이언트는 이를 그대로 사용할 수 있습니다.<button style={style} />
형식으로 인라인 스타일을 적용할 수 있습니다.style
은 객체 형태로 스타일 정보를 전달해야 합니다.style
안의 property name은 camelCase로 작성해야하고, property value는 문자열로 작성해야 합니다.const baseButtonStyle = {
padding: '14px 27px',
outline: 'none',
cursor: 'pointer',
borderRadius: '9999px',
fontSize: '17px',
};
const blueButtonStyle = {
...baseButtonStyle,
border: 'solid 1px #7090ff',
color: '#7090ff',
backgroundColor: 'rgba(0, 89, 255, 0.2)',
};
const redButtonStyle = {
...baseButtonStyle,
border: 'solid 1px #ff4664',
color: '#ff4664',
backgroundColor: 'rgba(255, 78, 78, 0.2)',
};
이미지 불러오기
import
구문을 통해 불러오고, 불러온 이미지 주소를 src
속성으로 사용합니다.import diceImg from './assets/dice.png';
function Dice() {
return <img src={diceImg} alt="주사위 이미지" />;
}
export default App;
CSS 파일 불러오기
import
구문으로 파일을 불러올 수 있는데, 이때 from
키워드 없이 사용합니다.<head>
태그의 <style>
태그 안에 스타일시트가 자동으로 작성됩니다.import diceImg from './assets/dice.png';
import './Dice.css';
function Dice() {
return <img src={diceImg} alt="주사위 이미지" />;
}
export default App;
클래스네임 사용하기
import diceImg from './assets/dice.png';
import './Dice.css';
function Dice({ className = '' }) {
const classNames = `Dice ${className}`;
return <img className={classNames} src={diceImg} alt="주사위 이미지" />;
}
export default App;
function Button({ isPending, color, size, invert, children }) {
const classNames = `Button ${isPending ? 'pending' : ''} ${color} ${size} ${invert ? 'invert' : ''}`;
return <button className={classNames}>{children}</button>;
}
export default Button;
function Button({ isPending, color, size, invert, children }) {
const classNames = [
'Button',
isPending ? 'pending' : '',
color,
size,
invert ? 'invert' : '',
].join(' ');
return <button className={classNames}>{children}</button>;
}
export default Button;
import classNames from 'classnames';
function Button({ isPending, color, size, invert, children }) {
return (
<button
className={classNames(
'Button',
isPending && 'pending',
color,
size,
invert && 'invert',
)}>
{ children }
</button >
);
}
export default Button;
createRoot()
메소드와 ReactDOM.render()
메소드의 차이점을 찾아보자.classnames
라이브러리에 대해 찾아보고, 유용한 라이브러리를 더 찾아보자.Reference