리액트 파일이 실행되면 브라우저는 index.html 파일을 연다.
그러고 나서 index.js 실행,
ReactDOM.render 메소드는 첫번째 아규먼트를 활용해서 html 요소를 만들고 두번째 아규먼트에 그 요소를 넣어준다.
ReactDOM.render(<h1>안녕 리액트!</h1>, document.getElementById('root'));
자바스크립트와 HTML을 섞어서 쓸 수 있는 자바스크립트의 확장된 문법
HTML의 문법을 그대로 사용할 수는 없다.
for나 class 처럼 자바스크립트 문법에 해당하는 예약어와 똑같은 이름의 속성명은 사용할 수 없다.
class는 className으로, for는 htmlFor라고 작성해야 한다.
html에서는 모두 소문자로 작성했던 이벤트 핸들러들을 jsx에서는 두번째 단어부터 대문자로 작성해줘야 한다.(카멜 케이스)
onblur, onfocus, onmousedown
-> onBlur, onFocus, onMouseDown
jsx에서는 여러 단어로 조합된 경우 카멜 케이스로 나타낸다.
JSX 문법으로 html 태그를 작성할 때는 반드시 하나의 태그로 묶어서 작성해야 한다.
여러 태그를 감싸줄 때, div 태그로 감싸줄수도 있지만 굳이 div 태그를 사용하고 싶지 않을 때 프래그먼트를 사용하면 된다.
import { Fragment } from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Fragment>
<h1>안녕 리액트!</h1>
<p>hi</p>
</Fragment>);
이름 없는 태그로 축약해서 사용할수도 있다. 이 경우에는 Fragment를 임포트하지 않아도 된다.
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<h1>안녕 리액트!</h1>
<p>hi</p>
</>);
JSX에서는 다음과 같이 중괄호{}를 사용하여 변수를 넣거나 자바스크립트 표현식을 넣을 수 있다.
import ReactDOM from 'react-dom/client';
const name="철수"
const imgUrl = "[이미지 URL]"
function handleClick(){
alert('안녕!!');
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<>
<h1>안녕 {name}!</h1>
<img src={imgUrl} alt="프로필 사진"/>
<button onClick={handleClick}>인사하기</button>
</>
);
중괄호 안에는 자바스크립트의 표현식만 사용 가능하기 때문에 if문이나 for문, 함수 선언과 같은 자바스크립트의 문장은 사용할 수 없다.
JSX 문법으로 작성한 요소는 결과적으로 자바스크립트 객체가 되고,
이러한 객체를 리액트 엘리먼트라고 부른다.
리액트로 화면을 구성하는데 가장 기본적이면서도 핵심적인 요소이다.
리액트 엘리먼트를 더 자유롭게 다루기 위한 문법으로 자바스크립트의 함수를 활용하여 만들 수 있다.
리액트 엘리먼트를 함수로 작성하면 JSX문법을 작성할 때 커스텀 태그처럼 활용할 수 있다.
import ReactDOM from 'react-dom/client';
function Hello() {
return <h1>안녕 리액트</h1>
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<>
<Hello/>
<Hello/>
</>
);
이 코드에서 Hello라는 함수를 리액트 컴포넌트라고 부른다.
리액트 컴포넌트는 단순히 함수를 만든다고 해서 컴포넌트가 되는 것이 아니고,
함수의 첫 글자를 꼭 대문자로 써야 하고 반드시 JSX 문법으로 만든 리액트 엘리먼트를 리턴해줘야 한다.
이런 특성을 모듈 문법으로 활용하면 독립적으로 컴포넌트 특성에 집중해서 코드를 작성할 수 있다.
리액트에서 컴포넌트에 지정된 속성
컴포넌트에 속성을 지정해주면 각 속성이 하나의 객체로 모여서 컴포넌트를 정의한 함수의 첫 번째 파라미터로 전달된다.
개발자 도구의 Components 창에서 확인할 수 있다.
prop에서 숫자를 사용하려면 중괄호로 감싸줘야 한다.
import Dice from './Dice';
function App() {
return (
<div>
<Dice color="red" num={2} />
</div>
);
}
export default App;
import diceBlue01 from './assets/dice-blue-1.svg';
import diceBlue02 from './assets/dice-blue-2.svg';
// ...
import diceRed01 from './assets/dice-red-1.svg';
import diceRed02 from './assets/dice-red-2.svg';
// ...
const DICE_IMAGES = {
blue: [diceBlue01, diceBlue02],
red: [diceRed01, diceRed02],
};
function Dice(props) {
const src = DICE_IMAGES[props.color][props.num - 1];
const alt = `${props.color} ${props.num}`;
return <img src={src} alt={alt} />;
}
export default Dice;
Destructuring 문법을 활용해서 더 간결하게 적을 수도 있다.
import diceBlue01 from './assets/dice-blue-1.svg';
import diceBlue02 from './assets/dice-blue-2.svg';
// ...
import diceRed01 from './assets/dice-red-1.svg';
import diceRed02 from './assets/dice-red-2.svg';
// ...
const DICE_IMAGES = {
blue: [diceBlue01, diceBlue02],
red: [diceRed01, diceRed02],
};
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에는 기본적으로 존재하는 prop이 있다, 바로 children이라는 prop!
JSX 문법으로 컴포넌트를 작성할 때 컴포넌트를 단일 태그가 아니라 여는 태그와 닫는 태그의 형태로 작성하면, 그 안에 작성된 코드가 바로 이 children 값에 담기게 된다.
function Button({children}) {
return <button>{children}</button>;
export default Button;
<Button>던지기</Button>
<Button>처음부터</Button>
더 직관적인 코드로 작성할 수 있다.
리액트에서 State는 상태가 바뀔 때마다 화면을 새롭게 보여주는 방식으로 동작하는 것이다.
리액트에서 state를 만들고 바꾸기 위해서는 useState라는 함수를 활용해야 한다.
import { useState } from 'react';
const [num, setNum] = useState(1);
파라미터로 초기값을 전달 받고 함수가 실행된 다음에는 배열의 형태로 요소 두개 리턴
첫번째 요소는 state 값, 현재 변수의 값
두번째 요소는 setter 함수 , 함수를 호출할 때 파라미터로 전달하는 값, state 값이 변경이 되는 것이다.
변수의 새로운 값을 할당해서 값을 변경하는 것이 아니라 반드시 세터 함수를 통해서 값을 변경해야 한다.
세터함수의 이름은 일반적으로 state 이름 앞에 set을 붙여서 쓰인다.
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);
};
배열이나 객체 같은 참조형 state를 변경할 때는 아예 전체를 새로 만든다고 생각하면 좋다.
push 메소드를 이용해서 배열의 값을 변경한다고 해도 결과적으로 참조하는 주솟값은 변경된 것이 아니기 때문에 코드가 제대로 작동하지 않는다.
그래서 참조형 state를 활용할 때는 반드시 새로운 참조형 값을 만들어 state를 변경해야 한다.
ex) spread 문법 활용
// ...
const [gameHistory, setGameHistory] = useState([]);
const handleRollClick = () => {
const nextNum = random(6);
setGameHistory([...gameHistory, nextNum]);
};
// ...
장점
반복적인 개발이 줄어든다.
오류를 고치기 쉽다.
일을 쉽게 나눌 수 있다.
스테이트 리푸팅 : 자식 컴포넌트의 스테이트를 부모 컴포넌트로 올리는 것
Virtual DOM이라는 자료구조를 활용
리액트가 일단 버추얼 돔에 적용 화면을 바꿀 준비만 하고 반영은 안 함
State 변경 전의 버추얼 돔과 변경 후의 버추얼 돔을 비교한다.
바뀐 부분을 찾아낸 다음에 각각에 해당하는 실제 돔 노드를 변경한다.
단순하고 깔끔한 코드 작성 가능
변경사항들을 효율적으로 관리할 수 있다.
이미지 파일은 import 구문을 통해 불러오고, 불러온 이미지 주소를 src 속성으로 사용하면 된다.
import diceImg from './assets/dice.png';
function Dice() {
return <img src={diceImg} alt="주사위 이미지" />;
}
export default App;
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)',
};
function Button({ color, children, onClick }) {
const style = color === 'red' ? redButtonStyle : blueButtonStyle;
return (
<button style={style} onClick={onClick}>
{children}
</button>
);
}
export default Button;
리액트에서 인라인 스타일은 문자열이 아닌 객체형으로 사용한다.
대시(-)로 연결된 속성은 카멜 케이스로 작성해야 한다.
css 파일을 불러올 때는 from 키워드 없이 바로 불러오면 된다.
import '경로'
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;
CSS 파일에 정의된 클래스명을 className prop에 문자열로 넣어주면 된다.
이때, 재사용성을 위해 className prop을 부모 컴포넌트에서 받으면 더 좋다.
웹 브라우저에서는 jsx 문법을 사용할 수 없다.
babaljs.io 사이트에서 jsx 파일을 js로 번역된 결과를 볼 수 있다.
트랜스파일링
babel은 대표적은 자바스크립트의 트랜스파일러이다.
jsx로 작성한 파일들은 트랜스파일링을 통해 순수 js로 번역되고, 번역된 코드들은 번들링을 통해 웹 브라우저가 다운 받기 좋도록 묶음으로 만들어진다.
실제 웹사이트에 접속한 사람들은 번들 파일이 실행된 모습을 접하게 되는 것이다.