본문 바로가기

FRONTEND

[React] Learning React Chapter 5 - 웹팩과 바벨 part 1

해당 글은 'Learning React 러닝 리액트 2판(한빛미디어 출판)에 대한 내용을 바탕으로 개인 학습 목적을 위해 작성되었습니다. 잘못된 내용이 있을 수 있으니 참고 부탁드리며 해당 내용에 대해 피드백주시면 반영할 수 있도록 하겠습니다.

 

 

JSX 문법

이전 챕터에서 React.createElement 메서드를 활용하여 리액트 엘리먼트를 직접 생성하여 페이지를 구성해보았다. 또 재사용할 수 있는 컴포넌트도 만들어 보았다. 하지만 리액트를 활용하여 페이지를 만들 때에는 JSX(JavaScript XML) 문법을 사용하여 코드를 작성한다.

 

함수형 컴포넌트를 사용하여 리액트 엘리먼트를 만들때 JSX 문법을 사용하면 다음과 같이 나타낼 수 있다. 이 모습이 우리가 평소 익숙하게 사용하던 문법의 모습이라 할 수 있다.

function IngredientList() {
return React.createElement(
"ul",
{ className: "ingredients" },
secretIngredients.map((ingredient, index) =>
React.createElement("li", { key: index }, ingredient)
)
);
}
//React.createElement
React.createElement(IngredientList, { list : [...] });
//JSX 문법
<IngredientList list={[...]} />

 

JSX 문법의 몇 가지 특징을 살펴보면 다음과 같다.

첫번째로, 프로퍼티에 값을 넘길 때에는 중괄호({ })를 사용해야 한다는 점과

두번째로, 프로퍼티의 값은 꼭 자바스크립트 식을 꼭 사용해야한다는 점이다.

중괄호 안에 사용된 자바스크립트 식을 평가한 결과값이 들어가게 된다.

 

또한, JSX 문법을 사용하면 다른 컴포넌트의 자식으로 해당 컴포넌트를 추가할 수 도 있고 여러번 추가할 수 도 있다.

<Ingredients>
<Ingredient />
<Ingredient />
<Ingredient />
</Ingredients>

 

JSX를 사용하여 매핑(mapping)도 가능하다.

<ul>
{props.ingredients.map((ingredient) => {
<li key={ingredient.id}>{ingredient}</li>;
})}
</ul>

 

 

바벨(Babel)

이렇게 보면 리액트에서는 JSX 문법을 사용하는게 당연한 것 같다. 실제로 리액트를 활용한 거의 모든 프로젝트는 JSX 문법을 사용한다. 하지만 브라우저에서는 JSX 문법을 해석할 수 없다. JSX 문법은 사람이 사용하기에는 간편하고 편리하지만 브라우저 입장에서는 이해할 수 없는 문법이다. 따라서 브라우저가 해당 문법을 이해할 수 있도록 변환해주어야 하는데 이때 사용하는 것이 바벨(Babel)이라는 트랜스파일러(transpiler)이다.

 

간단하게 바벨에 대해 살펴보자. 

 

바벨은 최신의 자바스크립트 코드를 현재 및 과거의 브라우저와 같은 환경에서 호환되는 버전으로 변환하는데 주로 사용되는 도구 또는 트랜스 파일러이다. 앞에서도 설명했지만, 바벨은 최신 자바스크립트 코드가 다양한 브라우저 환경에서 작동하도록 ES6 문법을 ES5 문법으로 변환하는 것을 통해 크로스 브라우징 관련 이슈를 해결하기 위해 사용한다. 여기서 말하는 크로스 브라우징이란 브라우저나 플랫폼마다 보여지는 모습이 다른 경우에 이러한 차이를 최소화 하여 브라우저 환경에 영향을 최소한으로 받고 해당 웹 서비스를 사용할 수 있게 최적화를 하는 작업을 말한다.

 

간단하게 레시피 페이지를 만들어보자.

recipes.js 파일을 만들고 다음과 같이 코드를 작성하자. JSX 문법으로 작성된 코드를 화면에 렌더링 하기 위해서는 html  파일이 필요하다. index.html 파일도 같이 만들어주자.

//recipes.js
const data = [
{
name: "Baked Salmon",
ingredients: [
{ name: "Salmon", amount: 1, measurement: "l lb" },
{ name: "Pine Nuts", amount: 1, measurement: "cup" },
{ name: "Butter Lettuce", amount: 2, measurement: "cups" },
{ name: "Yellow Squash", amount: 1, measurement: "med" },
{ name: "Olive Oil", amount: 0.5, measurement: "cup" },
{ name: "Garlic", amount: 3, measurement: "cloves" },
],
steps: [
"Preheat the oven to 350 degrees.",
"Spread the olive oil around a glass baking dish.",
"Add the salmon, garlic, and pine nuts to the dish.",
"Bake for 15 minutes.",
"Add the yellow squash and put back in the oven for 30 mins.",
"Remove from oven and let cool for 15 minutes. Add the lettuce and serve.",
],
},
{
name: "Fish Tacos",
ingredients: [
{ name: "Whitefish", amount: 1, measurement: "l lb" },
{ name: "Cheese", amount: 1, measurement: "cup" },
{ name: "Iceberg Lettuce", amount: 2, measurement: "cups" },
{ name: "Tomatoes", amount: 2, measurement: "large" },
{ name: "Tortillas", amount: 3, measurement: "med" },
],
steps: [
"Cook the fish on the grill until hot.",
"Place the fish on the 3 tortillas.",
"Top them with lettuce, tomatoes, and cheese",
],
},
];
const Ingredients = ({ data = [] }) => (
<ul className="ingredients">
{data.map((ingredient, index) => (
<li key={index}>{ingredient.name}</li>
))}
</ul>
);
const Instructions = ({ data = [] }) => (
<section className="instructions">
<h2>Cooking Instructions</h2>
{data.map((step, index) => (
<p key={index}>{step}</p>
))}
</section>
);
const Recipe = ({ name, ingredients, steps }) => (
<section id={name.toLowerCase().replace(/ /g, "-")}>
<h1>{name}</h1>
<Ingredients data={ingredients} />
<Instructions data={steps} />
</section>
);
const Menu = ({ title, recipes }) => (
<article>
<header>
<h1>{title}</h1>
</header>
<div className="recipes">
{recipes.map((recipe, index) => (
<Recipe key={index} {...recipe} />
))}
</div>
</article>
);
ReactDOM.render(
<Menu recipes={data} title="Delicious Recipes" />,
document.getElementById("react-container")
);

 

// index.html
<!DOCTYPE html>
<html>
<head>
<meta
name="viewport"
content="minimum-scale=1.0, width=device-width, maximum-scale=1.0, user-scalable=no"
/>
<meta charset="utf-8" />
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.production.min.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"
></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<title>React Recipes</title>
</head>
<body>
<div id="react-container"></div>
<script src="recipes.js" type="text/babel"></script>
</body>
</html>

html 파일을 보면 title 태그 위에 바벨을 실행시키기 위한 코드를 작성한 것을 볼 수 있다. recipes.js 파일의 JSX 문법을 이해하기 위해서는 바벨이 필요하며 바벨을 실행시키는 해당 코드를 삭제하면 화면에 아무것도 렌더링되지 않는다.

 

브라우저에 렌더링된 모습

 

웹팩(Webpack)

위에서 작성한 코드를 실제로 프로덕션 레벨에서 사용하기는 힘들다. 데이터, 컴포넌트등 모든 코드들이 recipes.js 파일 하나에 작성되어 있어 유지보수 측면에서도 좋지 못한 파일 구조이다. 하나의 파일이 하나의 역할만 할 수 있도록 모듈화를 통해 코드를 분리하고 팀으로 협업하기 용이하게 해당 프로젝트를 변경해 볼 수 있을 것 같다.

 

모듈화 하기에 앞서, 웹팩에 대해 알아보자. 만약 어떤 프로젝트의 규모가 정말 커져서 작성해야할 코드가 많아지면 앞서 말한것처럼 파일을 분리해야할 필요를 느낀다. 분리하지 않고 한 파일에 모든 코드를 작성한다면 사실상 분업하기도 힘들 뿐더러 변수명 등의 중복이 일어나 서로 다른 코드에 영향을 끼쳐 예상하지 못한 버그가 발생할 수 있고 발생한 버그에 대한 원인을 발견하기도 힘들다. 이에 따라 파일을 작은 단위로 쪼개서 작업해야할 필요성이 점차 커지게 된다. 하지만 파일을 여러개로 분리하여 너무 많은 경우에는 네트워크 성능상의 문제를 초래한다.

 

분업을 위해 파일을 분리를 해야하는 상황속에서 또 네트워크 상의 문제를 해결하기 위해 하나로 합쳐야 하는 난감한 상황이 발생하는 것이다. 이러한 문제를 해결할 수 있는 것이 바로 웹팩이다. 

 

웹팩은 모듈 번들러로 여러 종류의 파일(자바스크립트, LESS, CSS, JSX, ESNext)을 하나로 묶어 준다. 이를 통해 모듈화, 네트워크 성능 두 가지 측면에서 이점을 얻을 수 있다. 좀 더 세부적으로 말하자면, 웹팩을 통해 소스 코드를 좀 더 작은 단위로 작성하는 것을 통해 관리의 용이성을 갖으며, 그로 인해 재사용성이 뛰어난 컴포넌트를 만들 수 있고 모듈화된 수 많은 파일들을 하나로 합치는 것을 통해 파일을 로딩하는 시간을 감소시킬 수 있다는 점에서 장점을 갖는다고 할 수 있다. 또한, 웹팩이 최신 자바스크립트 문법이나 JSX와 같은 문법을 브라우저가 이해할 수 있는 문법으로 변환해주기 때문에 최신 문법을 일관성있게 사용할 수 있다는 장점도 있다.

 

이외에도 웹팩은 JSX와 같은 문법의 변환을 처리하고 프로젝트간의 의존성 문제를 해결하며 이미지, CSS 등의 최적화에도 관여한다.