<리액트를 다루는 기술> 2장 JSX

|

velopert님의 <리액트를 다루는="" 기술=""> 개정판을 보고 공부한 것을 정리합니다.

코드 이해하기

// scr/App.js

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        </header>
      </div>
  );
}

export default App;
  • import React from 'react'
    • 리액트를 불러와서 사용할 수 있게 해 준다.
    • 리액트 프로젝트를 만들 때 node_modules라는 디렉터리도 함께 생성되는데, 프로젝트 생성 과정에서 node_modules 디렉터리에 react 모듈이 설치된다. 그리고 import 구문을 통해 리액트를 불러와서 사용할 수 있게 된다.

모듈을 불러와 사용하기

모듈을 불러와서 사용하는 것은 원래 브라우저에는 없던 기능이다. 브라우저가 아닌 환경에서 자바스크립트를 실행할 수 있게 해 주는 환경인 Node.js에서 지원하는 기능!

Node.js에서는 import가 아닌 require라는 구문으로 패키지를 불러올 수 있다.

이러한 기능을 브라우저에서도 사용하기 위해 번들러(bundler) 를 사용한다. 이름대로, 파일을 묶듯이 연결해준다.

대표적인 번들러로 웹팩, Parcel, browserify라는 도구들이 있으며, 각 도구마다 특성이 다르다. 리택트 프로젝트에서는 주로 웹팩을 사용하는 추세이다. 편의성과 확장성이 다른 도구보다 뛰어나기 때문. 번들러 도구를 사용하면 import로 모듈을 불러왔을 때 불러온 모듈을 모두 합쳐서 하나의 파일을 생성해준다. 또 최적화 과정에서 여러 개의 파일로 분리될 수도 있다.

이 책의 프로젝트에서는 src/index.js를 시작으로 필요한 파일을 다 불러와서 번들링하게 된다.

2017년부터 브라우저에서도 import 구문을 사용할 수 있게 되었지만, 이는 단순히 다른 경로에 있는 자바스크립트를 불러오는 용도로만 사용되기 때문에 프로젝트 번들링과는 다르다.

import logo from './logo.svg';
import './App.css';

리액트를 불러오는 코드 하단에 SVG 파일과 CSS 파일을 import하는 코드가 있다.

웹팩을 사용하면 SVG 파일과 CSS 파일도 불러와서 사용할 수 있다. 이렇게 파일들을 불러오는 것은 웹팩의 로더(loader) 라는 기능이 담당한다. 로더는 여러 가지 종류가 있다.

  • css-loader: CSS 파일을 불러올 수 있게 해 준다.
  • file-loader: 웹 폰트나 미디어 파일 등을 불러올 수 있게 해 준다.
  • babel-loader: 자바스크립트 파일들을 불러오면서 최신 자바스크립트 문법으로 작성된 코드를 바벨이라는 도구를 사용하여 ES5 문법으로 변환해 준다.

ES5는 이전 버전의 자바스크립트를 의미하는데, 구버전 웹 브라우저와의 호환을 위해 ES5 문법으로의 변환이 필요하다. 또, 리액트 컴포넌트에서 사용하는 JSX 문법도 정식 자바스크립트 문법이 아니기 때문에 ES5 형태의 코드로 변환해야 한다.

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

이 코드는 App이라는 컴포넌트를 만들어 준다. function 키워드를 사용하여 컴포넌트를 만들었는데, 이러한 컴포넌트를 함수형 컴포넌트라고 부른다. 프로젝트에서 컴포넌트를 렌더링(렌더링: ‘보여 준다’는 의미) 함수에서 반환하고 있는 내용을 나타낸다. 이 코드가 바로 JSX 이다.

JSX란?

JSX는 자바스크립트의 확장 문법이며 XML과 매우 비슷하게 생겼다. 이런 형식으로 작성한 코드는 브라우저에서 실행되기 전에 코드가 번들링되는 과정에서 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환된다.

function App() {
  return (
    <div>
      Hello <b> react </b>
    </div>
  );
}

이렇게 작성된 코드는 다음과 같이 변환된다.

function App() {
  return React.createElement("div", null, "Hello", React.createElement("b", null, "react"));
}

컴포넌트를 렌더링할 때마다 JSX 코드를 작성하는 것이 아니라 위 코드처럼 매번 React.createElement 함수를 사용해야 한다면 매우 불편할 것이다.

JSX는 리액트로 프로젝트를 개발할 때 사용되므로 공식적인 자바스크립트 문법이 아니다. 바벨에서는 여러 문법을 지원할 수 있도록 preset 및 plugin을 설정한다. 바벨을 통해 개발자들이 임의로 만든 문법, 혹은 차기 자바스크립트의 문법들을 사용할 수 있다.

JSX의 장점

보기 쉽고 익숙하다

JSX를 사용하는 편이 더 가독성이 높고 작성하기도 쉽다. HTML 코드를 작성하는 것과 비슷하기 때문이다.

더욱 높은 활용도

JSX에서는 div나 span같은 HTML 태그를 사용할 수 있을 뿐만 아니라, 앞으로 만들 컴포넌트도 JSX 안에서 작성할 수 있다. App.js에서 만든 App 컴포넌트를 다른 js 파일 안에서 마치 하나의 HTML 태그처럼 사용할 수 있다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

ReactDOM.render?

이 코드는 컴포넌트를 페이지에 렌더링하는 역할을 하며, react-dom 모듈을 불러와 사용할 수 있다. 이 함수의 첫번째 파라미터에는 페이지에 렌더링할 내용을 JSX 형태로 작성하고, 두 번째 파라미터에는 해당 JSX를 렌더링할 document 내부 요소를 설정한다. 여기서는 id가 root인 요소 안에 렌더링을 하도록 설정했다.

JSX 문법

감싸인 요소

컴포넌트에 여러 요소가 있다면 반드시 부모 요소 하나로 감싸야 한다. Virtual DOM 내에서 컴포넌트 변화를 감지해 낼 때 효율적으로 비교할 수 있도록 컴포넌트 내부는 하나의 DOM 트리 구조로 이루어져야 한다는 규칙이 있기 때문이다.

이때 <div> 요소를 사용하고 싶지 않다면, 리액트 v16 이상부터 도입된 Fragment 라는 기능을 사용하면 된다.

import React, { Fragment } from 'react';

function App() {
  return (
    <Fragment>
      <h1>리액트 안녕!</h1>
      <h2> 작동하니?</h2>
    </Fragment>
  );
}

export default App;

다음과 같은 형태로도 표현될 수 있다.

import React from 'react';

function App() {
  return (
    <>
      <h1>리액트 안녕!</h1>
      <h2> 작동하니?</h2>
    </>
  );
}

export default App;

자바스크립트 표현

JSX 안에서 자바스크립트 표현식을 쓸 수 있다. 자바스크립트 표현식을 작성하려면 JSX 내부에서 코드를 {}로 감싸면 된다.

import React from 'react';

function App() {
  const name = '리액트';
  return (
    <>
      <h1>{name}안녕!</h1>
      <h2> 작동하니?</h2>
    </>
  );
}

export default App;
const

const는 ES6 문법에서 새로 도입되었으며 한번 지정하고 나면 변경이 불가능한 상수를 선언할 때 사용하는 키워드이다. let은 동적인 값을 담을 수 있는 변수를 선언할 때 사용하는 키워드이다.

var는 scope가 함수 단위이다.

letconst는 scope가 함수 단위가 아닌 블록 단위이므로, if문 내부에서 선언한 a값은 if문 밖의 a값을 변경하지 않는다.

또한 letconst를 사용할 때 같은 블록 내부에서 중복 선언이 불가능하며, const는 한번 선언하면 재설정할 수 없다.

기본적으로 const를 사용하고, 해당 값을 바꾸어야 할 때는 let를 사용하면 된다.

If문 대신 조건부 연산자

JSX 내부의 자바스크립트 표현식에서 if문을 사용할 수는 없다. 하지만 조건에 따라 다른 내용을 렌더링해야 할 때는 JSX 밖에서 if문을 사용하여 사전에 값을 설정하거나, {} 안에 조건부 연산자(삼항 연산자)를 사용 하면 된다.

import React from 'react';

function App() {
  const name = '리액트';
  return (
    <div>
        {name === '리액트'?(
            <h1>리액트입니다.</h1>
        ):(
            <h2>리액트가 아닙니다.</h2>
            )}
    </div>
  );
}

export default App;
  • condition ? true_value : false_value

AND 연산자(&&)를 사용한 조건부 렌더링

조건부 연산자를 통해 null을 렌더링하면 아무것도 보여 주지 않도록 할 수 있다.

import React from 'react';

function App() {
  const name = '뤼왝트';
  return (
    <div>
        {name === '리액트'?(
            <h1>리액트입니다.</h1>
        ):null}
    </div>
  );
}

export default App;

&& 연산자를 사용해서 더 짧은 코드로 똑같은 작업을 할 수 있다.

import React from 'react';

function App() {
  const name = '뤼왝트';
  return (
    <div>
        {name === '리액트'&&
            <h1>리액트입니다.</h1>
        }
    </div>
  );
}

export default App;

&& 연산자로 조건부 렌더링을 할 수 있는 이유는 리액트에서 false를 렌더링할 때는 null과 마찬가지로 아무것도 나타나지 않기 때문이다. 다만 falsy한 값인 0은 예외적으로 화면에 나타난다.

const number = 0;
return number && <div>내용</div>

위의 코드는 화면에 숫자 0을 보여 준다.

주로 JSX를 여러 줄로 작성할 때 괄호로 감싸고, 한 줄로 표현할 수 있는 코드는 감싸지 않는다. JSX를 괄호로 감싸는 것은 필수 사항이 아니다. 감싸도 되고 감싸지 않아도 된다.

undefined를 렌더링하지 않기

리액트 컴포넌트에서는 함수에서 undefined만 반환하여 렌더링하는 상황을 만들면 안 된다.

import React from 'react';
import './App.css';

function App(){
  const name = undefined;
  return name;
}

export default App;

위 코드는 다음과 같은 오류를 출력한다.

App(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

어떤 값이 undefined일 수도 있다면, OR(||) 연산자를 사용하면 해당 값이 undefined일 때 사용할 값을 지정할 수 있으므로 간단하게 오류를 방지할 수 있다.

import React from 'react';
import './App.css';

function App(){
  const name = undefined;
  return name||'값이 undefined입니다.';
}

export default App;

반면 JSX 내부에서 undefined를 렌더링하는 것은 괜찮다.

import React from 'react';
import './App.css';

function App() {
  const name = undefined;
  return <div>{name}</div>;
}

export default App;

name 값이 undefined일 때 보여주고 싶은 문구가 있다면 다음과 같이 작성

import React from 'react';
import './App.css';

function App() {
  const name = undefined;
  return <div>{name||'리액트'}</div>;
}

export default App;

인라인 스타일링

리액트에서 DOM 요소에 스타일을 적용할 때는 문자열 형태로 넣는 것이 아니라 객체 형태로 넣어 주어야 한다.

스타일 이름 중 background-color처럼 - 문자가 포함되는 이름이 있다. 이러한 이름은 - 문자를 없애고 카멜 표기법 (camelCase)으로 작성해야 한다. 따라서 background-colorbackgroundColor로 작성한다.

import React from 'react';

function App() {
  const name = '리액트';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: '48px',
    fontWeight: 'bold',
    padding: 16 // 단위를 생략하면 px로 지정된다.
  };
  return <div style={style}>{name}</div>;
}

export default App;

style 객체를 미리 선언하지 않고 바로 style 값을 지정하고 싶다면 다음과 같이 작성.

import React from 'react';

function App() {
  const name = '리액트';
  return (
    <div
      style = 
    >
      {name}
    </div>
  );
}

export default App;

class 대신 className

일반 HTML에서 CSS 클래스를 사용할 때는 <div class="myclass"></div>와 같이 class라는 속성을 설정한다. 하지만 JSX에서는 class가 아닌 className으로 설정해 주어야 한다.

우선 새 App.css를 작성한다.

.react {
  background: aqua;
  color: black;
  font-size: 48px;
  font-weight: bold;
  padding: 16px;
}
import React from 'react';
import './App.css';

function App() {
    const name = '리액트';
    return <div className="react">{name}</div>;
}

export default App;

JSX를 작성할 때 CSS 클래스를 설정하는 과정에서 className이 아닌 class 값을 설정해도 스타일이 적용되기는 한다. 하지만 그렇게 사용하면 브라우저 개발자 도구의 Console 탭에 다음과 같은 경고가 나타난다.

Warning: Invalid DOM property 'class'. Did you mean 'className'?
  in div (at App.js:6)
  in App (at src/index.js:7)

이전에는 class로 CSS 클래스를 설정할 때 오류가 발생하고 CSS 클래스가 적용되지 않았는데, 리액트 v16 이상부터는 class를 className으로 변환시켜 주고 경고를 띄웁니다.

꼭 닫아야 하는 태그

HTML 코드를 작성할 때는 태그를 닫지 않은 상태로 코드를 작성해도 문제없이 작동하지만, JSX에서는 태그를 닫지 않으면 오류가 발생한다.

태그 사이에 별도의 내용이 들어가지 않는 경우에는 <input /> self-closing 태그로 작성할 수 있다. 태그를 선언하면서 동시에 닫을 수 있는 태그이다.

주석

JSX 안에서 주석을 작성하는 방법은 일반 자바스크립트에서 주석을 작성할 때와 조금 다르다.

{/* 주석 */}
<div
  className="react" // 시작 태그를 여러 줄로 작성하게 된다면 여기에 주석을 작성
>

ESLint와 Prettier 적용하기

ESLint

ESLint는 문법 검사 도구이고, Prettier는 코드 스타일 자동 정리 도구이다. ESLint는 코드를 작성할 때 실수를 하면 에러 혹은 경고 메시지를 VS Code 에디터에서 바로 확인할 수 있게 해 준다.

Prettier

VS Code에서 F1을 누르고 format이라고 입력한 다음 Enter를 누르면 Prettier를 통해 자동 코드 정리가 진행된다. 이 과정에서 코드가 제대로 정렬되고, 세미콜론(;)이 빠진 곳에는 세미콜론이 자동으로 추가되고, 기존에 사용하던 작은따옴표는 모두 큰따옴표로 바뀐다.

Prettier의 장점은 코딩 스타일을 쉽게 커스터마이징할 수 있다는 것이다. 현재 열려 있는 프로젝트의 루트 디렉터리(src, public 디렉터리들이 위치한 곳)에서 .prettierrc라는 파일을 생성한 후 다음과 같이 입력하면,

{
  "singleQuote": true,
  "semi": true,
  "useTabs": false,
  "tabWidth": 2
}

들여쓰기를 할 때 탭 대신 공백을 두 칸 사용하도록 되어 있고, 큰따옴표 대신 작은따옴표를, 세미콜론은 언제나 붙이도록 설정되어 있다. (참고: prettier options 페이지)

저장할 때 자동으로 코드 정리하기

  1. VS Code 환경 설정 열기
    • Code > 기본 설정 > 설정
  2. 상단 텍스트 박스에서 format onn save를 검색
  3. 나타나는 체크 박스에 체크
  4. 저장할 때마다 코드가 자동으로 정리됨!

정리

JSX는 코드로 보면 XML 형식이지만 실제로는 자바스크립트 객체이며, 용도도 다르고 문법도 조금씩 차이가 난다.

Comments