리액트는 자바스크립트 라이브러리로 사용자 인터페이스를 만드는데 사용한다.
구조가 MVC, MVW 등인 프레임워크와 달리, 오직 V(View)만을 신경 쓰는 라이브러리이다.
Node.js가 설치 되었다는 전제로 시작한다.
저자는npm을 통하여 설치를 진행한다.
npx create-react-app <프로젝트 이름>npm create-react-app <프로젝트 이름>두개의 명령어가 있는데 무엇이 다를까?
| npm | npx | |
|---|---|---|
| 의미 | Node.js 패키지 매니저로 사용 | 패키지를 실행하지만, 설치하지 않음 |
| 사용 목적 | 패키지 설치 및 전역 명령 실행 | 일회성 패키지 실행 및 설치 (전역 설치 X) |
| 실행 방법 | npm install -g 패키지명 | npx 패키지명 |
| 패키지 버전 | 패키지의 전역 설치 버전 사용 | 항상 최신 버전을 사용 |
| 특징 | 패키지 버전 관리가 가능, 여러 프로젝트에서 재사용 가능 | 글로벌 설치 없이 필요한 패키지를 즉시 실행 |
설치가 완료 되었으면, 해당 폴더로 이동 cd 프로젝트 이름 -> npm start

오케이 시작이 반이라고 했으니 반정도 왔다.
가장 먼저 package.json 파일에 대해 알아보자
Node.js 프로젝트의 구성 및 의존성 정보를 저장하는 .json 파일
name, version, private, ....start, build, test, ....스크립트 부분에서 start 부분이 react-scripts start인 것을 보아
왜 npm start를 했을 때 리액트 프로젝트가 실행이 되는 것을 알 수 있다.
import React from 'react';
CSS에서 어떠한 파일을 불러와 사용하는 문법이랑 똑같이 생겼다.
위 코드는 내가 생각한 것 처럼 리앤트를 불러와 사용할 수 있게 해주는데
프로젝트 생성 과정에서 node_modules 폴더에 react 모듈이 설치 되고, 이를 통해
import문을 통하여 리액트를 불러와 사용할 수 있는 것이다.
import logo from './logo.svg';
import './App.css';
import문을 사용하여 다른 파일에서 정의한 내용을 현재 파일로 불러와 사용할 수 있으며, 이를 통해 코드를 모듈화하고 다른 파일에서 정의한 내용을 현재 파일에서 재사용할 수 있다.
function App() {
return (
<div className="App">
...
</div>
)
};
App이라는 컴포넌트를 function 키워드를 사용하여 컴포넌트를 만들게 되는데
이를 함수형 컴포넌트라 부른다. 이러한 코드를 JSX라고 부르는데 JSX가 무엇인지 알아보자
JSX(JavaScript XML) 자바스크립트의 확장 문법으로, JS코드 내에서 HTML과 유사한 구문을 사용하여 UI 컴포넌트를 만들고 렌더링하는 데 도움을 준다.
function App() {
return(
<div>
Hello! <b>React</b>
</div>
)
};
이러한 코드는 다음과 같이 변환이 된다.
function App() {
return React.createElement("div", null, "Hello!", React.createElement("b", null, "React"));
}
JSX 문법을 사용하는 것이 아니라 항상 createElement를 쓰게 되면 불편할 것이다
그렇기에 JSX를 사용하면 편하게 렌더링을 할 수 있다.
JSX를 올바르게 사용하기 위해 규칙에 대하여 알아보자
import React from 'react';
function App () {
return (
<h1>리액트의 JSX 문법 공부</h1>
<p>1. 감싸인 요소를 꼭 포함한다.</p>
)
}
export default App;
이렇게 되게 되면 제대로 작동하지 않을 것이다.
"JSX 식에는 부모 요소가 하나 있어야 합니다" 라고 에러가 발생하는 것을 볼 수 있다.
위의 식을 에러 없이 제대로 작동하게끔 코드를 수정해보자
div 태그 사용하기import React from 'react';
function App () {
return (
<div>
<h1>리액트의 JSX 문법 공부</h1>
<p>1. div 태그로 감싸기</p>
</div>
)
}
export default App;
Fragment 태그 사용하기import React from 'react';
function App () {
return (
<React.Fragment>
<h1>리액트의 JSX 문법 공부</h1>
<p>2. 최상위 요소를 두기 싫을 때 Fragment 사용하기</p>
</React.Fragment>
)
}
export default App;
import React from 'react';
function App () {
return (
<>
<h1>리액트의 JSX 문법 공부</h1>
<p>3. 빈 태그 사용하기</p>
</>
)
}
export default App;
jsx내부에서 js 표현식 사용하기
import React from 'react';
function App () {
const name = "리액트";
return (
<div>
<h1>{name}의 JSX 문법 공부</h1>
</div>
)
}
export default App;
{}를 사용하여 jsx 내부에서 렌더링 하기
JSX 내부의 자바스크립트 표현식에서 if문을 사용 할 수 없다.
import React from 'react';
function App() {
const name = '리액트';
return <div>{name === '리액트' ? <h1>리액트 입니다.</h1> : <h1>리액트가 아닙니다</h1>}</div>;
}
export default App;
JSX내부에선 삼항 연산자를 사용하자
아무것도 렌더링을 하고 싶지 않을 때 AND연산자를 사용하자
위의 3번에서 배운 조건부 연산자를 사용하여 null값을 사용하여 아무것도 렌더링 하지 않을 수 있지만
AND 연산자(&&)를 사용하면 더 간결하게 사용할 수 있다.
import React from 'react';
function App() {
const name = '리액트';
return <div>{name === '리액트' && <h1>리액트 입니다.</h1>}</div>;
}
export default App;
name이 "리액트"일 경우 화면에 표시가 되지만 아닐 경우 아무것도 표시가 되지 않는걸 볼 수 있다.
카멜표기법으로 작성하자
import React from 'react';
function App() {
const name = '리액트';
const style = {
backgroundColor: 'black',
fontSize: '50px',
textAlign: 'center',
color: 'aqua',
};
return <div style={style}>{name}</div>;
}
export default App;
import React from 'react';
function App() {
return (
<div>
<h2>리액트를 배워보자</h2>
<input>
</div>
);
}
export default App;
해당 코드는 Parsing error : Unterminated JSX contents
즉 모든 태그는 닫는 태그를 사용해야한다.
<input />, <br /> ....
JS나 HTML에서 사용하는 주석을 사용하게 되면 화면에 표시되게 된다
그렇기에 {/* ... */}의 형태로 사용하자!
import React from 'react';
function App() {
return (
<>
<h1>리액트의 JSX 문법 공부</h1>
{/* 주석은 이렇게 사용합니다 */}
// 이렇게 사용하면 화면에 보여지게 됩니다.
/* 이렇게 사용해도 화면에 보여지게 됩니다.*/
</>
);
}
export default App;
컴포넌트 내부에서 바뀔 수 있는 값

React에서 사용하는 State를 알아보기 위해 기본적으로 버튼을 눌렀을 때
1씩 증가, 1씩 감소 그 값을 화면에 렌더링 하는것을 구현해보자
State를 사용하기 위해서는 React를 import 하고useState를 추가적으로 적어야 한다.import React, { useState } from 'react';
Returns a stateful value, and a function to update it
-> "현재 상태 값을 나타내는 변수 + 상태 값을 업데이트하는 함수를 반환한다" 라고 알려준다.
useState를 사용하여 상태 값을 초기화, 업데이트 함수를 선언const [count, setCount] = useState(0);
배열의 비구조화 할당을 이용하여 다음과 같은 값을 가지게 한다.
count- 상태 값
setCount- 상태 값을 업데이트하는 함수
const onIncrease = () => {
setCount(count + 1);
};
const onDecrease = () => {
setCount(count - 1);
};
증가,감소 함수를 사용하여 현재 상태 값인
count를 +1, -1씩 하는 함수를 만들어 봤다.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const onIncrease = () => {
setCount(count + 1);
};
const onDecrease = () => {
setCount(count - 1);
};
return (
<div>
<h2>{count}</h2>
<button onClick={onIncrease}>증가 버튼</button>
<button onClick={onDecrease}>감소 버튼</button>
</div>
);
};
처음 보는 문법이라 아직 익숙하진 않은데 확실한건 js로만 했을 때보다는 더 간결하다는 점
useState(0) - 초기 값 0 그렇기에 count가 0으로 출력 되는 걸 볼 수 있다.컴포넌트에 데이터 전달하기
부모 컴포넌트인 App.js 파일이 있고 자식 컴포넌트인 Counter.js가 있다
위에서 버튼을 눌렀을 때 증감이 되는 함수가 포함된 파일 즉 자식 컴포넌트가 Counter.js
import React from 'react';
import Counter from './Counter';
function App() {
return(
<>
<Counter initialNumber={5}/>
</>
)
}
import React, { useState } from 'react';
const Counter = (props) => {
console.log(props);
const [count, setCount] = useState(0);
const onIncrease = () => {
setCount(count + 1);
};
const onDecrease = () => {
setCount(count - 1);
};
return (
<div>
<h2>{count}</h2>
<button onClick={onIncrease}>증가 버튼</button>
<button onClick={onDecrease}>감소 버튼</button>
</div>
);
};
export default Counter;
부모 컴포넌트에서 데이터를 적기만 한다고 자식 컴포넌트에 전달이 되는것이 아닌
데이터를 받고자 하는 자식 컴포넌트에서 props를 매개변수로 받는다.
이를 console.log에 찍어보면 object 즉 객체로 받는것을 볼 수 있다.
편의를 위해 코드를 생략하고 작성하겠다
// App.js
function App() {
return (
<>
<Counter initialNumber={5} />
</>
);
}
// Counter.js
const Counter = (props) => {
const [count, setCount] = useState(props.initialNumber); // 화면에 초기 값이 5로 경 된 것을 볼 수 있다.
};
props가 console 창에서 객체로 나오기에 그 값에 접근 하기 위해서 props.키값
으로 접근을 하였고 화면에 받은 데이터
// App.js
function App() {
const counterProps = {
a: 1,
b: 2,
c: 3,
d: 4,
f: 5,
};
return (
<>
<Counter {...counterProps} />
</>
);
}
넘겨주고자 하는 데이터가 많을 경우 태그 안에 나열 보다는 새로운 객체를 생성해서
스프레드 연산자로 받는것을 지향하자
// App.js
import React from 'react';
import Counter from './Counter';
function App() {
const counterProps = {
a: 1,
b: 2,
c: 3,
d: 4,
f: 5,
};
return (
<>
<Counter {...counterProps} />
</>
);
}
export default App;
// Counter.js
import React, { useState } from 'react';
const Counter = (props) => {
const [count, setCount] = useState(props.initialNumber);
const onIncrease = () => {
setCount(count + 1);
};
const onDecrease = () => {
setCount(count - 1);
};
return (
<div>
<h2>{count}</h2>
<button onClick={onIncrease}>증가 버튼</button>
<button onClick={onDecrease}>감소 버튼</button>
</div>
);
};
export default Counter;
이렇게 되면 initialNumber이 undefined가 되기에 여기에 연산을 하게 되면 NaN이 렌더링 된다.
이는 오류이기에 해당 코드를 초기 설정값을 통해 이러한 문제를 해결해보자
Counter.defaultProps = {
initialNumber: 0,
};
App.js에 해당 코드를 넣음으로써 NaN이 렌더링 되는것을 방지할 수 있다.
또한 신기한 사실은 부모 컴포넌트의 State의 변경에 따른 자식 요소 컴포넌트도 리렌더링 된다.