3월 2주차. React.js 입문

변현섭·2024년 3월 25일
0

다우데이타 인턴십

목록 보기
9/17
post-thumbnail

1. React 개발 환경 구축

1) Node.js 설치

React 프로젝트를 생성하려면, 먼저 Node.js가 설치되어 있어야 한다.

① 아래의 사이트에 접속 후 LTS 버전의 Node.js를 다운로드한다.
>> Node.js 설치

② Next 버튼을 클릭한다.

③ 동의 후 Next 버튼을 클릭한다.

④ 다운로드 경로를 지정한 후 Next 버튼을 클릭한다.

⑤ Next 버튼을 클릭한다.

⑥ Next 버튼을 클릭한다.

⑦ Install 버튼을 클릭한다.

⑧ 설치가 완료된다.

⑨ Node 및 npm의 버전은 아래의 명령으로 확인할 수 있다.

  • 참고로, npm은 node와 함께 자동으로 설치된다.
npm -v
node -v

2) CRA

CRA는 Create React App의 약어로, React로 웹 어플리케이션을 개발하는 데에 필요한 모든 설정이 되어 있는 상태의 프로젝트를 생성해주는 도구를 말한다. React App 을 생성할 때에는 npx 명령어를 사용하는데, 이는 npm 패키지를 설치 후 곧바로 실행(Execute)시키겠다는 의미이다.

① cmd 창을 열고, 아래의 명령을 입력한다.

npx create-react-app {프로젝트 명}

② 아래의 명령을 통해 리액트 앱을 실행시킬 수 있다.

cd {프로젝트 명} # 방금 생성한 앱 폴더로 이동
npm start

③ 실행이 완료되면, 아래와 같이 localhost:3000 주소의 페이지가 자동으로 열리게 된다.

4) VSCode 개발

React 개발은 VSCode에서 진행하기 때문에 VSCode가 설치되어 있지 않다면 설치해야 한다. VSCode는 이미 설치되어 있다고 가정하여, 설치 방법은 생략하기로 한다. VSCode에서 방금 생성한 폴더를 열어보자.

위와 같이 기본적인 내용이 이미 입력되어 있음을 확인할 수 있을 것이다. 이번엔 VSCode 터미널에 npm start 명령을 입력해보자. 전과 동일하게 리액트 앱이 실행되는 것을 확인할 수 있을 것이다.

2. JSX

1) JSX 개념

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]
)

2) JSX 실습

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;
  • import React from "react"
    • HTML, CSS, JavaScript만 읽을 수 있는 브라우저가 React로 작성한 코드를 읽을 수 있도록 만들어주는 설정
    • JSX 파일에 필수적으로 필요한 구문. 그 이유는 위에서 설명하였듯 JSX 코드가 내부적으로 React.createElement()를 사용하기 때문임.
  • function Book(props): Book이라는 함수형 컴포넌트 생성
  • export default Book: 다른 파일에서 이 컴포넌트를 import하여 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;
  • <Book name="Spring 잡학사전" numOfPage={10}/>: Book 컴포넌트에 두 가지 props(책 이름, 페이지 수)를 전달
  • 이와 같이 여러 개의 컴포넌트를 포함하는 하나의 컴포넌트를 생성하는 것을 Component 합성(Component Composing)이라 함.

⑤ 방금 생성한 컴포넌트를 렌더링하기 위해서는 index.js 파일을 아래와 같이 수정해야 한다.

  • Library 컴포넌트를 import 한다.
  • ReactDOM을 사용해 root DOM node에 렌더링한다.
  • 코드에 대한 자세한 설명은 아래의 Element 렌더링 부분에서 자세히 다루기로 한다.
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 앱을 실행하면, 아래와 같은 웹 페이지가 로드된다.

3. Elements

1) DOM Element VS React Element

본래 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로 표기하기로 하겠다.

2) 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에 해당하는 엘리먼트를 렌더링하기 위해 사용된다.

3) Element 렌더링

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가 화면에 렌더링되는 것이다.

4. Components & Props

1) Components

React를 흔히 컴포넌트 기반 구조라고 부르는데, 그 이유는 작은 컴포넌트들이 마치 블록처럼 조립되어 완성된 페이지를 구성하기 때문이다. 컴포넌트는 재사용성이 높기 때문에 개발 비용 및 유지 보수 비용을 절감시키는 효과가 있다.

컴포넌트는 마치 JavaScript의 함수처럼 입력과 출력이 존재하는데, 컴포넌트에서의 입력은 Props이고, 출력은 React Element가 된다.

여기까지만 설명하면 컴포넌트가 낯선 개념으로만 느껴지겠지만, 쉽게 말하면 사용자 정의 태그(컨테이너) 정도로 생각할 수 있다. 즉, 기존 HTML 태그에 스타일을 적용하거나, 입력 인자를 전달하는 방식인 것이다.

이러한 점에서, 컴포넌트의 이름은 항상 대문자로 시작해야 한다. 그 이유는 소문자로 시작하는 이름은 리액트 내부에서 h1이나 span과 같은 DOM 태그로 인식하기 때문이다.

그러므로 리액트가 DOM 태그와 컴포넌트를 제대로 구분할 수 있도록, 컴포넌트 이름의 첫글자는 반드시 대문자로 명명해야 한다.

2) Props

Props(Properties)는 컴포넌트의 속성을 의미하는 것으로, 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달해주는 객체이다. 쉽게 말해 컴포넌트(붕어빵틀)를 통해 엘리먼트(붕어빵)를 만들 때, 그 안에 들어가는 것이 Props(붕어빵 속)인 것이다.

Props는 아래의 두 가지 특징을 가진다.

  • Props의 값은 컴포넌트 내에서 변경되지 않는다(Pure).
  • 같은 Props에 대해서 항상 결과가 동일해야 한다.

Props를 사용하는 방법은 기존 HTML에서 사용하던 속성=속성값 형태와 동일하다.

function Introduction() {
    return (
        <Profile
            name="크롬"
            company="다우데이타"
            salary={2000} 
        />
    );
}

JSX 코드에서 속성값에 해당하는 값은 중괄호로 감싸주어야 한다. 다만, 예외적으로 문자열만은 따옴표로 감싸주어야 한다.

3) State

State는 React Component에서 변경 가능한 데이터를 의미한다. State가 변경될 때마다 컴포넌트를 리렌더링해야 하기 때문에, 렌더링이나 데이터 흐름에 사용되는 값만 포함시켜야 최적화된 성능을 낼 수 있다. State를 바꿀 때에는 직접적으로 변경하는 방식보다는 setState()라는 setter 메서드를 사용하는 방식이 권장된다.

class LikeBtn extends React.Component {
	constructor(props) {
  		super(props);
  
		/* 잘못된 사용법 */
		//  this.state = {
		//	  name: '크롬'
		//  };
  
		/* 권장 사용법 */
		this.setState({
			name: '크롬'
		});
        ...

4) 컴포넌트의 종류

컴포넌트는 함수 컴포넌트와 클래스 컴포넌트로 구분된다.

① 클래스 컴포넌트

  • 리액트 초기 버전에서 자주 사용하던 형태
  • React.Coponent 상속
  • 함수 컴포넌트에 비해 제공되는 기능이 다양함. state 관리 및 생명주기 메서드 사용 가능
class Welcome extends React.Component
	render() {
  		return <h1>안녕, {this.props.name}</h1>;
    }
}

② 함수 컴포넌트

  • 형식이 간결하여 작고 간단한 컴포넌트를 작성하기에 적합
  • 대부분의 상황에서 클래스 컴포넌트보다 선호됨.
function Welcome(props) {
    return <h1>안녕, {props.name}</h1>;
}

5) 컴포넌트의 생명주기

사람이 출생 > 인생 > 사망의 순으로 생명주기를 가지듯, 컴포넌트에도 이와 상응하는 Mounting > Updating > Unmounting 순의 생명주기가 존재한다.

① Mounting

  • constructor: 컴포넌트의 생성자
  • render: UI를 렌더링하는 메서드
  • componentDidMount(): 컴포넌트가 웹 브라우저상에 나타난 직후 호출되는 메서드

② Update

  • Props가 변경될 때, State가 변경될 때, 강제 업데이트를 수행할 때가 여기에 해당
  • getDerivedStateFromProps: Mount 과정 또는 props 변화에 따라 state 값을 변화시키기 위해 사용
  • shouldComponentUpdate
    • 컴포넌트를 리렌더링 해야 할지 여부를 결정
    • true 반환 시 다음 라이프사이클 메서드를 계속 실행하고, false 반환 시 작업이 중지된다.
  • render: 컴포넌트 리렌더링
  • getSnapshotBeforeUpdate: 컴포넌트 변화를 DOM에 반영하기 직전에 호출되는 메서드
  • componentDidUpdate: 컴포넌트의 업데이트 작업이 다 끝난 후 호출되는 메서드

③ Unmount

  • 컴포넌트를 DOM에서 제거하는 과정
  • componentWillUnmount: 컴포넌트가 웹 브라우저상에서 사라지기 직전에 호출되는 메서드

5. Native App VS Hybrid App VS Web App

React는 웹 애플리케이션을 개발하기 위한 라이브러리인 반면, 이와 유사한 이름의 React Native는 모바일 애플리케이션을 개발하기 위한 라이브러리이다. 두 라이브러리는 각각 HTML, CSS, JavaScript를 이용해 웹 어플리케이션과 모바일 어플리케이션을 개발하기 위한 목적으로 사용된다.

다만, React Native에서 개발할 수 있는 모바일 어플리케이션은 우리가 흔히 아는 모바일 어플리케이션(Native App)과는 조금 다르다. 아래에 모바일 어플리케이션의 분류에 대해 나타내었다.

① Native App

  • 모바일 기기에 최적화된 언어(Java, Kotlin, Swift 등)를 이용해 개발된 앱
  • 스마트폰에서 제공하는 기본 기능(카메라, GPS, 마이크 등)에 대한 접근성이 좋고 성능 및 안정성 상의 이점이 존재
  • 운영체제에 따라 다른 개발 환경이 요구되기 때문에 개발 비용이 높은 편임.

② Web App

  • HTML, CSS, JavaScript를 이용해 개발된 앱
  • 별도의 App 파일 설치 없이도 인터넷 브라우저를 기반으로 동작
  • 제작 비용이 저렴하고, 개발 속도도 매우 빠른 편임.
  • 스마트폰에 대한 접근 권한이 제한적이고, 구동 속도 및 안정성이 낮은 편임.

③ Hybrid App

  • 네이티브 앱과 웹 앱의 개발 방식이 혼합된 형태
  • 컨텐츠는 웹 기반으로 제작되어 있지만, 외형적인 모습은 네이티브 앱의 형태를 띄고 있음.
  • HTML, CSS, JavaScript 기반으로 개발하면서도, 스마트폰 제공 기능을 활용할 수 있다는 것이 장점
  • 네이티브 앱 개발 지식이 어느 정도 요구되며, 낮은 구동 속도 및 안정성은 여전히 문제임.
profile
LG전자 Connected Service 1 Unit 연구원 변현섭입니다.

0개의 댓글