자바스크립트/러닝 리엑트

JSX를 사용하는 리액트

막이86 2023. 11. 13. 11:30
728x90

러닝 리엑트을 요약한 내용입니다.

  • JSX는 자바스크립의 JS와 XML의 X를 합친 말이다.
  • JSX는 자바스크립트 코드 안에서 바로 태그 기반의 구문을 써서 리액트 엘리먼트를 정의할 수 있게 해주는 자바스크립트 확장 이다.
  • JSX가 HTML과 닮았기 때문에 때로 이 둘을 혼동하기도 한다.
  • JSX는 단지 복잡한 createElement 호출에서 빠트린 콤마(,)를 찾는것을 줄여준다.

5.1 JSX로 리액트 엘리먼트 정의하기

  • 페이스북의 리액트 팀은 리액트를 내놓으면서 JSX도 함께 내놨다
  • JSX는 속성이 붙은 복잡한 DOM 트리를 작성할 수 있는 간편한 문법을 제공
  • JSX 엘리먼트에 다른 JSX 엘리먼트를 작식으로 추가할 수 있다.
  • JSX는 컴포넌트와도 잘 작동한다.

5.1.1 JSX 팁

  • JSX는 대부분의 문법은 HTML과 비슷하다
  • JSX를 다룰때 알아둬야 할 내용 몇가지가 있다.
    • JSX에서는 다른 컴포넌트의 자식으로 컴포넌트를 추가할 수 있다.
    • <IngredientsList> <Ingredient /> <Ingredient /> <Ingredient /> </IngredientsList>
    className
    • 자바스크립트에서 class가 예약어이므로 class 속성 대신 className을 사용한다.
    • <h1 className="fnacy">구운 연어</h1>
    자바스크립트 식
    • 중괄호로 자바스크립트 식을 감싸면 중괄호 안의 식을 평가해서 결괏값을 사용해야 한다는 뜻 이다.
    • <h1>{title}</h1>
    • 문자열이 아닌 다른 값도 자바스크립트 식 안에 넣어야 한다.
    • <input type="checkbox" defaultChecked={false} />
    평가
    • 중괄호 안에 들어갈 자바스크립트 코드는 그 값을 평가 받는다.
    • <h1>{"Hello" + title}</h1> <h1>{title.toLowerCase().replace()}</h1>
  • 내포된 컴포넌트

5.1.2 배열을 JSX로 매핑하기

  • JSX는 자바스크립트이므로 자바스크립트 함수 안에서 JSX를 직접 사용할 수 있다.
  • <ul> {pros.ingredients.map((ingredient, i) => ( <li key={i}>{ingredient}</li> ))} </ul>

JSX는 깔끔하고 일기 쉽지만 브라우저는 JSX를 해석할 수 없다. createElement나 팩토리를 사용해 모든 JSX를 변환해야만 한다. 이런 작업에 쓸수 있는 바벨이라는 유용한 도구가 있다.

5.2 바벨

  • 자바스크립트는 인터프린터 언어라서 브라우저가 코드 택스트를 해석하기 때문에 컴파일 할 필요가 없다.
  • 모든 브라우저가 최신 자바스크립트 문법을 지원하지 않는다.
  • 브라우저는 JSX를 지원하지 않는다.
  • JSX 코드를 브라우저가 해석할 수 있는 코드로 변환해줄 수단으로 바벨을 사용한다.
  • 변환 과정을 컴파일링이라고 부른다.
  • JSX를 순수 리액트로 변환하는 과정을 지원하도록 발전하였다.
  • 페이스북, 넷플릭스, 페이팔, 에어비앤비 등 기업에서 바벨을 사용중 이다.
  • 바벨이 JSX 처리 표준이다.
    
    
 

<aside> 💡 브라우저내 변환기를 사용하면 프로덕션 환경에서는 스크립트를 미리 컴파일해 사용하라는 경고를 볼수 있다. 여기 있는 코드나 그와 비슷한 목적의 데모의 경우 이경고를 무시해도 좋다.

</aside>

5.3 JSX로 작성한 조리법

  • JSX로 작성한 코드는 이해하기 쉽고 다른 엔지니어들이 볼 때도 이해하기가 쉽다
  • JSX의 단점은 브라우저가 JSX를 해석하지 못한다는 점이다.
  • 배열에 2가지 조리법이 들어 있다.
    • 데이터는 자바스크립트 객체가 2개 포함된 배열, 각 객체에 조리법의 이름, 재료 리스트, 조리 절차가 들어 있다.
    const data = [
    	{
    	    "name": "Baken Salmon",
    	    "ingredients": [
    	        { "name": "연어", "amount": 500, "measurement": "그램"},
    	        { "name": "잣", "amount": 1, "measurement": "컵"},
    	        { "name": "버터 상추", "amount": 2, "measurement": "컵"},
    	        { "name": "옐로 스쿼시(Yello Squash, 호박의 한 종류)", "amount": 1, "measurement": "개"},
    	        { "name": "올리브 오일", "amount": 0.5, "measurement": "컵"},
    	        { "name": "마늘", "amount": 3, "measurement": "쪽"}
    	        
    	    ],
    	    "steps": [
    	        "오븐을 180도로 예열한다.",
    	        "유리 베이킹 그릇에 올리브 오일을 두른다.",
    	        "연어, 마늘, 잣을 그릇에 담는다.",
    	        "오븐에서 15분간 익힌다.",
    	        "옐로 스쿼시를 추가하고 다시 30분간 오븐에서 익힌다.",
    	        "오븐에서 그릇을 꺼내서 15분간 식힌 다음에 상추를 곁들여서 내놓는다."
    	    ]
    	},
    	{
    	    "name": "생성 타코",
    	    "ingredients": [
    	        { "name": "흰살생성", "amount": 500, "measurement": "그램"},
    	        { "name": "치즈", "amount": 1, "measurement": "컵"},
    	        { "name": "아이스버그 상추", "amount": 2, "measurement": "컵"},
    	        { "name": "토마토", "amount": 2, "measurement": "개(큰것)"},
    	        { "name": "또띠야", "amount": 3, "measurement": "개"}
    	    ],
    	    "steps": [
    	        "생선을 그릴에 익힌다.",
    	        "또띠야 3장 위에 생선을 얹는다.",
    	        "또띠야에 얹은 생성 위에 상추, 토마토, 치즈를 얹는다."
    	    ]
    	}
    ];
    
    • 조리법을 가지고 2가지 컴포넌트가 들어 있는 UI를 만들 수 있다.
    // 데이터는 조리법 객체의 배열이다. 
    const data = [...];
    
    // 조리법 하나를 표현하는 상태가 없는 함수 컴포넌트
    const Recipe = (props) => (
        ...
    );
    
    // 조리법으로 이뤄진 메뉴를 표현하는 상태가 없는 함수 컴포넌트
    const Menu = (props) => (
        ...
    );
    
    // ReactDOM.render를 호출해서 Menu를 현재의 DOM안에 렌더링한다. 
    ReactDOM.render(
        <Menu recipes={data} title="맛있는 조리법" />,
        document.getElementById("root")
    );
    
    • Menu 컴포넌트 안에 쓰인 리액트 엘리먼트는 JSX로 되어 있다.
    const Menu = (props) => {
        return (
            <article>
                <header>
                    <h1>{props.title}</h1>
                </header>
                <div className="recipes">
                </div>
            </article>
        )
    };
    
    • div.recipes 엘리먼트 안에는 각 조리법 컴포넌트가 들어간다.
      • 중괄호를 사용해서 자식 노드 배열을 반환하는 자바스크립트 식을 추가한다.
      • map을 적용해서 배열 안의 각 객체에 대한 컴포넌트를 만든다.
      • 각 조리법 엘리먼트를 유일하게 식별하기 위해 key 프로퍼티를 추가해야한다는 점을 잊지 말아야 한다.
    const Menu = (props) => {
        return (
            <article>
                <header>
                    <h1>{props.title}</h1>
                </header>
                **<div className="recipes">
                    {props.recipes.map((recipe, i) => (
                        <Recipe 
                            key={i}
                            name={recipe.name}
                            ingredients={recipe.ingredients}
                            steps={recipe.steps}
                        />
                    ))}
                </div>**
            </article>
        )
    };
    
    • Menu 컴포넌트 구무을 개선할수 있는 부분으로 props 인자를 받는 부분이 있다.
      • 객체 구조 분해를 사용하면 title과 recipes를 사용할 수 있다.
      • props를 덧부여야 하는 번거로움을 줄일 수 있다.
    const Menu = ({title, recipes}) => {
        return (
            <article>
                <header>
                    <h1>{title}</h1>
                </header>
                <div className="recipes">
                    {recipes.map((recipe, i) => (
                        <Recipe 
                            key={i}
                            name={recipe.name}
                            ingredients={recipe.ingredients}
                            steps={recipe.steps}
                        />
                    ))}
                </div>
            </article>
        )
    };
    
    • 조립법을 처리하는 코드
      • 상태가 없는 함수 컴포넌트 이다.
      • 이름, 재료, 절차를 구조분해를 사용해 변수로 뽑아 낼수 있다.
      • 공백( )을 대시(-)로 바꿔서 UI의 id속성으로 사용한다.
    const Recipe = ({name, ingredients, steps}) => {
        return (
            <section id={name.toLowerCase().replace(/ /g, "-")}>
                <h1>{name}</h1>
                <ul className="ingredients">
                    {ingredients.map((ingredient, i) => (
                        <li key={i}>{ingredient.name}</li>
                    ))}
                </ul>
                <selction className="instructions">
                    <h2>조리 절차</h2>
                    {steps.map((step, i) => (
                        <p key={i}>{step}</p>
                    ))}
                </selction>
            </section>
        )
    };
    
    • 전체 코드
        
        
     
    
            // 데이터는 조리법 객체의 배열이다. 
            const data = [
                {
                    "name": "Baken Salmon",
                    "ingredients": [
                        { "name": "연어", "amount": 500, "measurement": "그램"},
                        { "name": "잣", "amount": 1, "measurement": "컵"},
                        { "name": "버터 상추", "amount": 2, "measurement": "컵"},
                        { "name": "옐로 스쿼시(Yello Squash, 호박의 한 종류)", "amount": 1, "measurement": "개"},
                        { "name": "올리브 오일", "amount": 0.5, "measurement": "컵"},
                        { "name": "마늘", "amount": 3, "measurement": "쪽"}
                        
                    ],
                    "steps": [
                        "오븐을 180도로 예열한다.",
                        "유리 베이킹 그릇에 올리브 오일을 두른다.",
                        "연어, 마늘, 잣을 그릇에 담는다.",
                        "오븐에서 15분간 익힌다.",
                        "옐로 스쿼시를 추가하고 다시 30분간 오븐에서 익힌다.",
                        "오븐에서 그릇을 꺼내서 15분간 식힌 다음에 상추를 곁들여서 내놓는다."
                    ]
                },
                {
                    "name": "생성 타코",
                    "ingredients": [
                        { "name": "흰살생성", "amount": 500, "measurement": "그램"},
                        { "name": "치즈", "amount": 1, "measurement": "컵"},
                        { "name": "아이스버그 상추", "amount": 2, "measurement": "컵"},
                        { "name": "토마토", "amount": 2, "measurement": "개(큰것)"},
                        { "name": "또띠야", "amount": 3, "measurement": "개"}
                    ],
                    "steps": [
                        "생선을 그릴에 익힌다.",
                        "또띠야 3장 위에 생선을 얹는다.",
                        "또띠야에 얹은 생성 위에 상추, 토마토, 치즈를 얹는다."
                    ]
                }
            ]
    
            // 조리법 하나를 표현하는 상태가 없는 함수 컴포넌트
            const Recipe = ({name, ingredients, steps}) => {
                return (
                    <section id={name.toLowerCase().replace(/ /g, "-")}>
                        <h1>{name}</h1>
                        <ul className="ingredients">
                            {ingredients.map((ingredient, i) => (
                                <li key={i}>{ingredient.name}</li>
                            ))}
                        </ul>
                        <section className="instructions">
                            <h2>조리 절차</h2>
                            {steps.map((step, i) => (
                                <p key={i}>{step}</p>
                            ))}
                        </section>
                    </section>
                )
            };
    
            // 조리법으로 이뤄진 메뉴를 표현하는 상태가 없는 함수 컴포넌트
            const Menu = ({title, recipes}) => {
                return (
                    <article>
                        <header>
                            <h1>{title}</h1>
                        </header>
                        <div className="recipes">
                            {recipes.map((recipe, i) => (
                                <Recipe 
                                    key={i}
                                    name={recipe.name}
                                    ingredients={recipe.ingredients}
                                    steps={recipe.steps}
                                />
                            ))}
                        </div>
                    </article>
                )
            };
    
            // ReactDOM.render를 호출해서 Menu를 현재의 DOM안에 렌더링한다. 
            ReactDOM.render(
                <Menu recipes={data} title="맛있는 조리법" />,
                document.getElementById("root")
            );
        

5.4 리액트 프래그먼트

  • 리액트 프래그먼트를 사용해 두 형제 컴포넌트를 렌더링해주는 작은 예제를 잠시 살펴보자
function Cat({name}) {
	return <h1>고양이 이름은 {name} 입니다.</h1>;
}
ReactDOM.render(<Cat name="나비">, document.getElementById("root"));
  • Cat 컴포넌트 내부에 h1과 같은 수준에 p 태그를 추가하면 어떤일이 벌어질까?
    • 콘솔에서 "Adjacent JSX ele..."라는 메세지와 프레그먼트를 사용하라고 권장하는 메시지가 포함된 오류가 발생
    • 리액트는 둘 이상의 인접한 현제 엘리먼트를 컴포넌트로 렌더링하지 않기 때문에 div와 같은 태그를 감싸는 태그가 생겨버린다.
function Cat({name}) {
	return (
		<h1>고양이 이름은 {name} 입니다.</h1>
		<p>이 고양이는 멋져요.</p>
	);
}
ReactDOM.render(<Cat name="나비">, document.getElementById("root"));
  • 리액트 프래그먼트를 사용하면 새로운 태그를 실제로 만들지 않아도 이런 래퍼의 동작을 흉내낼 수 있다.
    • 프래그먼트 태그를 추가하면 경고가 사라진다.
function Cat({name}) {
	return (
		<React.Fragment>
			<h1>고양이 이름은 {name} 입니다.</h1>
			<p>이 고양이는 멋져요.</p>
		</React.Fragment>
	);
}
ReactDOM.render(<Cat name="나비">, document.getElementById("root"));
  • 프래그먼트를 단축한 태그를 써서 코드를 더 깔끔하게 만들 수 있다.
function Cat({name}) {
	return (
		<>
			<h1>고양이 이름은 {name} 입니다.</h1>
			<p>이 고양이는 멋져요.</p>
		</>
	);
}
ReactDOM.render(<Cat name="나비">, document.getElementById("root"));
  • 결과 DOM을 살펴보면 트리에서 프래그먼트가 빠져 있음을 알 수 있다.
<div id="root">
    <h1>고양이 이름은 나비 입니다.</h1>
    <p>이 고양이는 멋져요.</p>
</div>

5.5 웹팩 소개

  • 리액트를 프로덕션에서 사용하려고 한다면 고려애야할 것이 많다.
    • JSX와 ESNext의 변환을 어떻게 처리해야할까?
    • 프로젝트의 의존 관계를 어떻게 관리 해야할까?
    • 이미지와 CSS를 어떻게 최적화 할까?
  • 웹팩, 브라우저리파이, 걸프, 그런트, 프리팩 등이 있다.
    • 웹팩이 많이 사용되는 도구중 하나이다.
  • 웹팩은 모듈 번들러로 알려져 있다.
    • 모듈 번들러는 여러 다른 파일들(자바스크립트, LESS, CSS, JSX, ESNext 등)을 받아서 한 파일로 묶어준다.
    • 모듈을 하나로 묶어서 얻는 2가지는 모듈성, 네트워크 성능이 있다.
      • 모듈성은 소스 코드를 여러 부분 또는 모듈로 나눠서 다룰수 있게 해준다.
      • 의존 관계가 있는 여러 파일들을 묶은 번들은 브라우저가 한번만 읽기 때문에 네트워크 성능이 좋아진다.
  • 컴파일 외에 웹팩이 처리할 수 있는 일은 다음과 같다
    • 코드 분리: 코드를 여러 덩어리로 나눠서 필요할 때 각각 로딩할 수 있다.
    • 코드 축소: 공백, 줄바꿈, 긴변수 이름, 불필요한 코드 등을 없애서 파일 크기를 줄여준다.
    • 특징 켜고 끄기: 코드의 기능을 테스트해야 하는 경우 코드를 각각의 환경에 맞춰 보내준다.
    • HMR(Hot module replacement): 소스코드가 바뀌는지 감지해서 변경된 모듈만 즉시 갱신해준다.
  • 웹팩 모듈 번들러를 사용하면 다음과 같은 장점이 있다.
    • 모듈성: 모듈 패턴을 사용해 도뮬을 외부에 익스포트하고 나중에 그 모듈을 필요한 곳에 임포트해서 쓸수 이어 소스 코드를 관리하기 쉬운 규모로 나눌수 있다.
    • 조합: 모듈을 사용하면 애플리케이션을 효율적으로 구축할 수 있는 작고 단순하며 재사용하기 쉬운 리액트 컴포넌트를 구축할 수 있다.
    • 속도: 모든 애플리케이션 모듈과 의존 관계를 하나의 클라이언트 번들로 묶으면 여러 파일을 HTTP로 요청함에 따라 발생할 수 있는 지연시간이 없어져 애플리케이션 로딩 속도가 빨라진다.
    • 일관성: 웹팩이 JSX나 자바스크립트를 컴파일해주기 때문에 프로젝트에서 아직 표준화되지 않은 미래의 문법을 사용할 수 있다.

5.5.1 프로젝트 설정하기

  • recipes-app이라는 새로운 폴더를 만들고 진행하자
mkdir recipes-app
cd recipes-app
  • 다음과 같은 단계를 거친다.
    1. 프로젝트 생성
    2. 조리법 앱을 컴포넌트로 나눠서 서로 다른 파일에 넣는다.
    3. 바벨을 사용하는 웹팩 빌드를 설정한다.
    <aside> 💡 create-react-app 도구를 사용하기전에 각 단계를 좀더 살펴보자
  • </aside>

1. 프로젝트 생성하기

  • npm을 사용해 프로젝트를 생성한다.
npm init -y
npm install react react-dom serve
  • 아래와 같은 폴더 구조를 만든다.

<aside> 💡 리액트 프로젝트에서 파일과 폴더를 구성하는 방법이 딱 하나 정해져 있지는 않다. 여러가지 방법중 하나일 뿐이다.

</aside>

2. 컴포넌트를 모듈로 나누기

  • 현재 Recipe 컴포넌트는 상당히 많은 일을 한다. 제목을 표시하고, 재료들의 ul을 만들고, 조리 절차의 각 단계를 p 엘리먼트로 만들어 표시한다.
  • Recipe.js에 넣어보자
    • Recipe를 더 작고 담당하는 기능이 작은 컴포넌트로 분리해보자
      • 조리 절차를 컴포넌트로 따로 분리
      import React from 'react';
      
      export default function Instructions({title, steps}) {
          return (
              <section className="instructions">
                  <h2>{title}</h2>
                  {steps.map((step, i) => (
                      <p key={i}>{step}</p>
                  ))}
              </section>
          );
      }
      
    • 재료 이름 및 분량과 단위 정보도 표시하는 Ingredient 컴포넌트를 만들자
    • import React from 'react' export default function Ingredient({ amount, measurement, name }) { return ( <li> { amount } { measurement } { name } </li> ) }
    • 재료 목록을 표시할 때 사용할 수 있는 IngredientsList 컴포넌트를 만들자
      • 재료를 보여줄 Ingredient 컴포넌트를 임포트해야 한다.
      • JSX 스프레드 연산자를 사용하면 배열의 원소에 들어 있는 모든 데이터를 props로 Ingredient에게 넘길 수 있다.
      import React from "react";
      import Ingredient from './Ingredient'
      
      export default function IngredientsList({ list }) {
          return (
              <ul className="ingredients">
                  {
                      list.map((ingredient, i) => (
                          <Ingredient key={i} {...ingredient} />
                      ))
                  }
              </ul>
          )
      }
      
      • 스프레드 연산자를 사용하는 것은 다음과 같다
      <Ingredient {...ingredient} />
      
      • 스프레드 연산자를 사용하지 않았을 경우
      <Ingredient amount={ingredient.amount} measurement={ingredient.measurement} name={ingredient.name} />
      
    • Recipe를 아래와 같이 변경할 수 있다.
    • import React from "react" import IngredientsList from "./IngredientList" import Instructions from "./Instructions" export default function Recipe({ name, ingredients, steps }) { return ( <section id={name.toLowerCase().replace(/ /g, "-")}> <h1>{name}</h1> <IngredientsList list={ingredients} /> <Instructions title="조리 절치" steps={steps} /> </section> ) }
    • Menu 컴포넌트를 생성
    • import React from 'react' import Recipe from './Recipe' function Menu({ recipes }) { return ( <article> <header> <h1> 맛있는 조리법 </h1> </header> <div className="recipes"> {recipes.map((recipe, i) => ( <Recipe key={i} {...recipe} /> ))} </div> </article> ) } export default Menu
    • recipes.json 데이터 생성
    • [ { "name": "Baken Salmon", "ingredients": [ { "name": "연어", "amount": 500, "measurement": "그램"}, { "name": "잣", "amount": 1, "measurement": "컵"}, { "name": "버터 상추", "amount": 2, "measurement": "컵"}, { "name": "옐로 스쿼시(Yello Squash, 호박의 한 종류)", "amount": 1, "measurement": "개"}, { "name": "올리브 오일", "amount": 0.5, "measurement": "컵"}, { "name": "마늘", "amount": 3, "measurement": "쪽"} ], "steps": [ "오븐을 180도로 예열한다.", "유리 베이킹 그릇에 올리브 오일을 두른다.", "연어, 마늘, 잣을 그릇에 담는다.", "오븐에서 15분간 익힌다.", "옐로 스쿼시를 추가하고 다시 30분간 오븐에서 익힌다.", "오븐에서 그릇을 꺼내서 15분간 식힌 다음에 상추를 곁들여서 내놓는다." ] }, { "name": "생성 타코", "ingredients": [ { "name": "흰살생성", "amount": 500, "measurement": "그램"}, { "name": "치즈", "amount": 1, "measurement": "컵"}, { "name": "아이스버그 상추", "amount": 2, "measurement": "컵"}, { "name": "토마토", "amount": 2, "measurement": "개(큰것)"}, { "name": "또띠야", "amount": 3, "measurement": "개"} ], "steps": [ "생선을 그릴에 익힌다.", "또띠야 3장 위에 생선을 얹는다.", "또띠야에 얹은 생성 위에 상추, 토마토, 치즈를 얹는다." ] } ]
    • index.js 생성
    • import React from 'react' import { render } from 'react-dom' import Menu from './components/Menu' import data from './data/recipes.json' render(<Menu recipes={data} />, document.getElementById("root"));
    3. 웹팩 빌드 만들기
    • 웹팩으로 정적인 빌드 프로세스를 만드려면 몇몇 모듈을 설치해야한다.
     npm install --save-dev webpack webpack-cli
    
    • 모듈화한 조리법 앱이 작동하게 만드려면 소스 코드를 어떻게 한 번들파일로 만들수 있는지 웹팩에게 알려줘야 한다.
      • 웹팩 4.0.0 부터는 프로젝트를 번들하기 위해 설정 파일을 만들 필요가 없어졌다.
        • 안만들면 디폴트 설정
      • 설정 파일을 만들면 설정을 원하는대로 커스텀화 할 수 있다.
      • 웹팩 설정 파일은 항상 webpack.config.js
    <aside> 💡 import 문을 사용하지만 노드나 현재 사용중인 대부분의 브라우저는 이를 지원하지 않는다. 바벨이 import를 require('모듈/경로')로 변환하기 때문이다.
    • webpack.config.js 파일을 생성
      • 웹팩에게 클라이언트의 시작 파일이 ./src/index.js 로 지정한다.
      • ./dist/bundle.js 라는 자바스크핍트 파일에 출력하라고 지정한다.
    var path = require("path")
    
    module.export = {
        entry: "./src/index.js",
        output: {
            path: path.join(__dirname, "dist", "assets"),
            filename: "bundle.js"
        }
    }
    
    • 바벨 의존 관례를 설치
    npm install babel-loader @babel/core --save-dev
    
    • 웹팩에게 특정 모듈을 실행할 때 사용할 로더 목록을 추가 한다.
      • test는 각 모듈에서 로더가 적용해야 하는 파일 경로를 찾기 위한 정규식이다.
      • node_modules 폴더에서 찾은 자바스크립트 파일이 아닌 모든 자바스크립트에 대해서 babel-loader를 실행 한다.
    var path = require("path")
    
    module.export = {
        entry: "./src/index.js",
        output: {
            path: path.join(__dirname, "dist", "assets"),
            filename: "bundle.js"
        },
        module: {
            rules: [{ test: /\\.js$/, exclude: /node_modules/, loader: "babel-loader"}]
        }
    }
    
    • 바벨을 실행할 때 사용할 프리셋을 지정해야한다.
      • 프리셋을 지정하면 바벨에게 어떤식으로 파일을 변환할지 알려주게 된다.
      npm install @babel/preset-env @babel/preset-react --save-dev
      
      • 추가로 프로젝트 루트에 .babelrc라는 파일을 만들자
      {
          "presets": ["@babel/preset-env", "@babel/preset-react"]
      }
      
    • 웹팩을 실행해서 설정이 정상동작하는지 확인해보자
    • npx webpack --mode development
    • package.json 파일에 npm 스크립트를 추가해서 npm을 통해 웹팩을 간단하게 실행수 있다.
      • npm run build
      "scripts": {
        "test": "echo \\"Error: no test specified\\" && exit 1",
        "build": "webpack --mode production"
      }
      
  • </aside>
  • import React from "react" export default function Recipe({ name, ingredients, steps }) { return ( <section id={name.toLowerCase().replace(/ /g, "-")}> <h1>{name}</h1> <ul className="ingredients"> {ingredients.map((ingredient, i) => ( <li key={i}>{ingredient.name}</li> ))} </ul> <section className="instructions"> <h2>조리 절차</h2> {steps.map((step, i) => ( <p key={i}>{step}</p> ))} </section> </section> ) }

5.5.2 번들 로딩하기

  • index.html 파일에 방금 만든 번들을 로딩하기 위한 srcipt 태그를 추가
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <title>리액트 조리법 앱</title>
</head>
<body>
    <div id="root"></div>
    <script src="assets/bundle.js"></script>
</body>
</html>

5.5.3 소스 맵

  • 코드를 한 번들로 만들면 브라우저에서 앱을 디버깅 하기 어려워 진다.
  • 소스 맵을 통해 해결 할 수 있다.
    • webpack.config.js에 devtool: '#srouce-map'을 추가 한다.
var path = require("path")

module.export = {
    entry: "./src/index.js",
    output: {
        path: path.join(__dirname, "dist", "assets"),
        filename: "bundle.js"
    },
    module: {
        rules: [{ test: /\\.js$/, exclude: /node_modules/, loader: "babel-loader"}]
    },
    devtool: 'source-map'
}
  • 웹팩을 다시 실행하면 bundle.js와 bundle.map이라는 두 파일이 생긴다.
  • 브라우저의 개발자 도구에 있는 소스 탭에서 webpack://이라고 쓰인 폴더를 찾을 수 있다.
    • 번들에 있는 모든 소스파일이 보여야 한다.
  • 브라우저의 디버거를 통해서 소스파일을 디버깅 할 수 있다.
  • 영역 패널에서 각 영역에 속한 변수를 살펴보거나 감시 패널에 변수 이름을 입력해 값을 추척할 수 있다.

5.5.4 create-react-app

  • create-react-app을 사용해서 리액트 프로젝트를 생성할 수 있다.
  • 개발자들이 직접 웹팩, 바벨, ESLint 등 여러 도구의 설정을 손보지 않아도 빠르게 리액트 프로젝트를 시작할 수 있게 해준다.
  • create-react-app을 사용하려면 패키지를 글로벌 설치 해야한다.
npm install -g create-react-app
  • 설치후 create-react-app 명령과 생성할 폴더의 이름을 지정하면 앱을 만들 수 있다.
    • my-project 폴더에 React, ReactDOM, react-srcipts에 대한 의존 관계를 설정해 준다.
    • react-scripts는 페이스북이 만들 도구로 바벨, ESLint, 웹팩 등을 설치해서 개발자가 직접 그런 도구를 설정할 필요 없게 해준다.
    • App.js 파일에서 루트 컴포넌트를 수정하거나 다른 컴포넌트 파일을 임포트 할 수 있다.
    • npm start로 실행해볼 수 있다.
    create-react-app my-project
    
  • create-react-app은 초보자나 경험자나 모두에게 훌륭한 도구이다.
  • 직접 커스텀 웹팩 빌드를 설정하지 않고 리액트를 시작 할수 있는 방법으로는 코드 샌드박스가 있다.
728x90