React 프로젝트를 생성하려면, 먼저 Node.js가 설치되어 있어야 한다.
① 아래의 사이트에 접속 후 LTS 버전의 Node.js를 다운로드한다.
>> Node.js 설치
② Next 버튼을 클릭한다.
③ 동의 후 Next 버튼을 클릭한다.
④ 다운로드 경로를 지정한 후 Next 버튼을 클릭한다.
⑤ Next 버튼을 클릭한다.
⑥ Next 버튼을 클릭한다.
⑦ Install 버튼을 클릭한다.
⑧ 설치가 완료된다.
⑨ Node 및 npm의 버전은 아래의 명령으로 확인할 수 있다.
npm -v
node -v
CRA는 Create React App의 약어로, React로 웹 어플리케이션을 개발하는 데에 필요한 모든 설정이 되어 있는 상태의 프로젝트를 생성해주는 도구를 말한다. React App 을 생성할 때에는 npx 명령어를 사용하는데, 이는 npm 패키지를 설치 후 곧바로 실행(Execute)시키겠다는 의미이다.
① cmd 창을 열고, 아래의 명령을 입력한다.
npx create-react-app {프로젝트 명}
② 아래의 명령을 통해 리액트 앱을 실행시킬 수 있다.
cd {프로젝트 명} # 방금 생성한 앱 폴더로 이동
npm start
③ 실행이 완료되면, 아래와 같이 localhost:3000 주소의 페이지가 자동으로 열리게 된다.
React 개발은 VSCode에서 진행하기 때문에 VSCode가 설치되어 있지 않다면 설치해야 한다. VSCode는 이미 설치되어 있다고 가정하여, 설치 방법은 생략하기로 한다. VSCode에서 방금 생성한 폴더를 열어보자.
위와 같이 기본적인 내용이 이미 입력되어 있음을 확인할 수 있을 것이다. 이번엔 VSCode 터미널에 npm start
명령을 입력해보자. 전과 동일하게 리액트 앱이 실행되는 것을 확인할 수 있을 것이다.
JSX는 JavaScript Syntax Extension의 약어로, 자바스크립트를 확장한 문법이라는 의미를 갖는다. 한편으로는 JSX가 JavaScript와 XML/HTML을 혼합한 형태이기 때문에, JavaScript + XML의 의미도 내포하고 있다. 즉, 기존 자바스크립트에서 사용하던 문법을 그대로 사용하되, 중간에 XML/HTML 코드를 섞어서 사용할 수 있다는 것이다. 아래의 HTML 코드를 통해 JSX를 학습해보자.
<h1 id="greeting">Hello, world!</h1>
위 HTML 엘리먼트를 만들기 위해 리액트에서 사용할 수 있는 방법은 아래의 두가지이다.
# JSX 사용
const element = <h1 id="greeting">Hello, world!</h1>
# JSX 미사용
React.createElement("h1", { id: "greeting" }, "Hello, world!")
JSX 코드도 내부적으로는 React.createElement()를 사용하기 때문에 위 코드의 동작은 완전히 동일하지만, JSX를 사용함으로써 코드의 직관성과 간결성이 크게 향상되었음을 알 수 있다.
참고로, React.createElement()는 Element의 타입(h1, p 등), 속성(id, name 등), 자식 Element를 입력 파라미터로 받는다. 아래는 React.createElement 함수의 형식이다.
React.createElement(
type,
[props],
[..children]
)
HTML 코드 내에서 JavaScipt 코드를 사용하기 위해선, JavaScript 코드를 중괄호로 감싸주기만 하면 된다. 구체적인 사용법은 직접 실습해보며 알아보기로 하자.
① src 디렉토리 하위로, first_proj라는 이름의 디렉토리를 생성한다.
② first_proj 디렉토리 하위로, Book.jsx 파일을 추가한다.
③ Book.jsx 파일에 아래의 내용을 입력한다.
import React from "react";
function Book(props) {
return (
<div>
<h1>{`이 책의 이름은 ${props.name}입니다.`}</h1>
<h2>{`이 책은 총 ${props.numOfPage} 페이지로 구성되어 있습니다.`}</h2>
</div>
);
}
export default Book;
④ first_proj 디렉토리 하위로 Library.jsx 파일을 추가하고, 아래의 내용을 입력한다.
import React from "react";
import Book from "./Book";
function Library() {
return (
<div>
<Book name="Spring 잡학사전" numOfPage={10}/>
<Book name="알아두면 쓸데있는 AWS" numOfPage={9}/>
<Book name="도커란 무엇인가" numOfPage={8}/>
</div>
);
}
export default Library;
⑤ 방금 생성한 컴포넌트를 렌더링하기 위해서는 index.js 파일을 아래와 같이 수정해야 한다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Library from './first_proj/Library'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Library />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
⑥ 터미널에 npm start
명령을 입력하여 React 앱을 실행하면, 아래와 같은 웹 페이지가 로드된다.
본래 Element라는 용어는 DOM에서 사용하는 용어이기 때문에, 일반적으로 Element는 곧 DOM Element를 의미한다.
※ DOM
DOM(Document Object Model, 문서 객체 모델)은 HTML 요소를 JavaScript의 Object처럼 조작하기 위해 사용하는 모델이다. HTML 코드 내 깊은 부분까지 접근할 수 있는 계층 구조이므로, DOM을 통해 HTML로 구성된 웹페이지를 동적으로 조작할 수 있다.
하지만, 여기서 설명하고자 하는 Element는 DOM Element가 아닌 React Element이다. 여기서 React Element란, 리액트 앱을 이루는 가장 기본적인 단위를 의미하는 것으로, 쉽게 말해 JSX로 생성하는 엘리먼트를 가리킨다.
그렇다면 DOM Element와 React Element는 무엇이 다른걸까? 차이점을 이해하기 위해 먼저는, Virtual DOM의 개념을 이해해야 한다. 실제 브라우저에서 사용하는 DOM 구조는 매우 복잡하기 때문에 리액트에서 DOM을 제어할 때에는 실제 DOM이 아닌 Virtual DOM을 사용한다.
※ Virtual DOM
UI를 업데이트 하기 위해 복잡한 구조의 브라우저 DOM을 직접 조작하는 것은 매우 비효율적이기 때문에, 리액트에서는 가상 DOM을 사용한다. 개발자가 가상 DOM에서 특정 부분을 변경하면, 변경에 대응되는 부분만 실제 DOM에서 수정된다. 쉽게 말해 웹 페이지가 한번 렌더링된 이후부터는 UI 업데이트가 필요한 부분만 바꿔가면서 실제 DOM이 갱신된다는 것이다.
정리하자면, Virtual DOM은 리액트 엘리먼트로, 브라우저 DOM은 DOM 엘리먼트로 이루어져 있으며, React Element는 DOM Element에 비해 매우 가볍고 빠르다. 적어도 리액트에서 DOM Element를 직접 조작할 일은 없기 때문에, 이제부터는 편의상 React Element를 그냥 Element로 표기하기로 하겠다.
Element의 가장 중요한 특징은 Element가 생성된 후에는 children이나 attributes를 바꿀 수 없다는 것이다. 이는 마치 붕어빵틀(Component)에 이미 찍어낸 붕어빵(Element)을 뒤늦게 수정할 수 없는 이치와 비슷하다. 즉, 변경된 Element를 화면에 나타내기 위해선, 기존 Element를 수정하는 것이 아니라 새로운 Element를 만들어내야 한다는 것이다.
이 말은 곧 UI가 업데이트 될 때마다 Element가 계속해서 바뀌어야 한다는 의미이다. 언뜻 보기엔 비효율적인 프로세스 같지만, 위에서 설명한 Virtual DOM 덕분에 빠른 반응성이 유지될 수 있다.
Element 객체는 아래와 같은 형태를 갖는다.
// <h1 id="greeting">Hello, world!</h1>
{
$$typeof: Symbol(React.element),
"type": "h1",
"key": null,
"ref": null,
"props": {id: "greeting", children: "Hello, world!"},
"_owner": null,
"_store": {}
}
JSX 코드로 작성한 값이 type 속성과 props 속성으로 전달된 것을 볼 수 있다. 여기서 props는 children에 해당하는 엘리먼트를 렌더링하기 위해 사용된다.
HTML 코드에서 애플리케이션의 진입점 역할을 하는 Element를 root DOM node라 부르며, 그 형태는 아래와 같다.
<div id="root"></div>
위 <div> 태그 안에 React Element가 렌더링된다. 따라서 Element 렌더링을, Virtual DOM의 Element를 브라우저 DOM으로 옮기는 과정이라 생각할 수 있다. root <div>에 Element를 렌더링하려면 먼저 ReactDOM의 createRoot() 함수로 root를 생성하고, root.render() 함수의 파라미터로 렌더링할 Element를 전달하면 된다.
const element = <h1>안녕하세요</h1>;
const root = RootDOM.createRoot(document.getElementById('root'));
root.render(element)
이제 JSX 실습에서 index.js에 작성했던 코드를 기억해보자.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Library />
</React.StrictMode>
);
바로 위 코드가 Element를 렌더링하기 위해 사용한 코드이다. 먼저 React.StrictMode는 개발 단계에서 발생할 수 있는 잠재적인 문제를 감지하고 경고를 표시하는 컴포넌트이므로 일단은 무시하고 넘어가자(삭제해도 상관 없다).
root.render()
안에 JSX 코드가 사용된 것을 볼 수 있는데, JSX 코드는 내부적으로 React.createElement를 사용하므로 사실상 Element를 전달한 것이나 마찬가지이다. 결과적으로 Library에서 Book 컴포넌트로 생성한 3개의 Element가 화면에 렌더링되는 것이다.
React를 흔히 컴포넌트 기반 구조라고 부르는데, 그 이유는 작은 컴포넌트들이 마치 블록처럼 조립되어 완성된 페이지를 구성하기 때문이다. 컴포넌트는 재사용성이 높기 때문에 개발 비용 및 유지 보수 비용을 절감시키는 효과가 있다.
컴포넌트는 마치 JavaScript의 함수처럼 입력과 출력이 존재하는데, 컴포넌트에서의 입력은 Props이고, 출력은 React Element가 된다.
여기까지만 설명하면 컴포넌트가 낯선 개념으로만 느껴지겠지만, 쉽게 말하면 사용자 정의 태그(컨테이너) 정도로 생각할 수 있다. 즉, 기존 HTML 태그에 스타일을 적용하거나, 입력 인자를 전달하는 방식인 것이다.
이러한 점에서, 컴포넌트의 이름은 항상 대문자로 시작해야 한다. 그 이유는 소문자로 시작하는 이름은 리액트 내부에서 h1이나 span과 같은 DOM 태그로 인식하기 때문이다.
그러므로 리액트가 DOM 태그와 컴포넌트를 제대로 구분할 수 있도록, 컴포넌트 이름의 첫글자는 반드시 대문자로 명명해야 한다.
Props(Properties)는 컴포넌트의 속성을 의미하는 것으로, 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달해주는 객체이다. 쉽게 말해 컴포넌트(붕어빵틀)를 통해 엘리먼트(붕어빵)를 만들 때, 그 안에 들어가는 것이 Props(붕어빵 속)인 것이다.
Props는 아래의 두 가지 특징을 가진다.
Props를 사용하는 방법은 기존 HTML에서 사용하던 속성=속성값
형태와 동일하다.
function Introduction() {
return (
<Profile
name="크롬"
company="다우데이타"
salary={2000}
/>
);
}
JSX 코드에서 속성값에 해당하는 값은 중괄호로 감싸주어야 한다. 다만, 예외적으로 문자열만은 따옴표로 감싸주어야 한다.
State는 React Component에서 변경 가능한 데이터를 의미한다. State가 변경될 때마다 컴포넌트를 리렌더링해야 하기 때문에, 렌더링이나 데이터 흐름에 사용되는 값만 포함시켜야 최적화된 성능을 낼 수 있다. State를 바꿀 때에는 직접적으로 변경하는 방식보다는 setState()라는 setter 메서드를 사용하는 방식이 권장된다.
class LikeBtn extends React.Component {
constructor(props) {
super(props);
/* 잘못된 사용법 */
// this.state = {
// name: '크롬'
// };
/* 권장 사용법 */
this.setState({
name: '크롬'
});
...
컴포넌트는 함수 컴포넌트와 클래스 컴포넌트로 구분된다.
① 클래스 컴포넌트
class Welcome extends React.Component
render() {
return <h1>안녕, {this.props.name}</h1>;
}
}
② 함수 컴포넌트
function Welcome(props) {
return <h1>안녕, {props.name}</h1>;
}
사람이 출생 > 인생 > 사망의 순으로 생명주기를 가지듯, 컴포넌트에도 이와 상응하는 Mounting > Updating > Unmounting 순의 생명주기가 존재한다.
① Mounting
② Update
③ Unmount
React는 웹 애플리케이션을 개발하기 위한 라이브러리인 반면, 이와 유사한 이름의 React Native는 모바일 애플리케이션을 개발하기 위한 라이브러리이다. 두 라이브러리는 각각 HTML, CSS, JavaScript를 이용해 웹 어플리케이션과 모바일 어플리케이션을 개발하기 위한 목적으로 사용된다.
다만, React Native에서 개발할 수 있는 모바일 어플리케이션은 우리가 흔히 아는 모바일 어플리케이션(Native App)과는 조금 다르다. 아래에 모바일 어플리케이션의 분류에 대해 나타내었다.
① Native App
② Web App
③ Hybrid App