jsx에서 왜 map 메서드를 사용해야 할까? (Feat. Babel)

허재원·2022년 11월 15일
0

바벨 사용하지 않고 리액트 컴포넌트 만들어보기
JSX에서 map 메서드를 사용해 배열을 반환해야 하는지 궁금해서 글을 작성하게 됐다.

React Component 만들기

JSX에서 map 메서드를 왜 사용하는지 알아보기 전에 babel의 도움 없이 React 컴포넌트를 만들어보면서 babel이 해주는 역할에 대해 알아갈 필요가 있다.

바벨을 사용하여 컴포넌트를 만드는 경우

<body>
  <div id="root"></div>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

  <script type="text/babel">
    const List = (
      <ul className="list" style={{ color: "blue" }}>
        <li>React</li>
      </ul>
    );

    const root = ReactDOM.createRoot(document.getElementById("root"));
    // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
    root.render(List);
  </script>
</body>

babel을 사용하면 다음처럼 태그를 사용할 수 있는 자바스크립트 확장 문법이 사용 가능하고, 이를 사용하지 않는 경우는 아래처럼 작성해야 한다.

바벨을 사용하지 않고 컴포넌트를 만드는 경우

<body>
	<div id="root"></div>
  	<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  
	<script>
        const List = React.createElement("ul", { className: "list", style: { color: "blue" } },
     		   React.createElement("li", null, "React"),
      	);
      	
      	const root = ReactDOM.createRoot(document.getElementById("root"));
	    // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
		root.render(List);
    </script>
</body>
  • React.createElement를 사용해 실제 DOM 엘리먼트가 어떻게 생겨야 하는지 작성하는데
    - 첫 번째 인자: 엘리먼트 타입 정의
    - 두 번째 인자: 엘리먼트 프로퍼티 표현
    - 세 번째 인자 이후: 엘리먼트를 여는 태그와 닫는 태그 사이에 들어가야 할 자식 노드들을 표현할 수 있다.
    여기서 우리가 사용하는 jsx 문법에서 babel이 해주는 역할을 알 수 있다.

바벨 사용한 경우와 사용하지 않은 경우 비교

// 바벨을 사용한 경우
const List = (
  <ul className="list" style={{ color: "blue" }}>
	  <li>React</li>
  </ul>
);
// 바벨을 사용하지 않은 경우
const List = React.createElement("ul", { className: "list", style: { color: "blue" } },
	React.createElement("li", null, "React"),
);
  • 이렇게만 봐도 JSX를 사용하는 것이 더 가독성이 뛰어나고, 유지보수 면에서도 훨씬 좋은 것을 확인할 수 있다. 그리고 이러한 컴파일링을 도와주는 녀석이 바로 babel이다!

  • babel은 이렇게 JSX와 함께 최신 자바스크립트 기능을 활용하고 싶을 때 우리가 작성한 코드를 브라우저가 해석할 수 있는 코드로 변환해줄 수 있도록 도와준다. 그리고 예전에는 페이스북이 만든 JSX 변환기가 JSX를 처리하는 표준적인 방식이었지만 이제는 바벨이 JSX 처리 표준이다.

map을 사용하는 이유

<body>
	<div id="root"></div>
  	<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  
	<script>
        const List = React.createElement("ul", { className: "list", style: { color: "blue" } },
			React.createElement("li", null, "React"),
			React.createElement("li", null, "Vue"),
            React.createElement("li", null, "Angular"),
      	);
      	
      	const root = ReactDOM.createRoot(document.getElementById("root"));
	    // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
		root.render(List);
    </script>
</body>
  • 세 번째 인자부터는 자식 노드들을 표현한다고 말했는데 네 번째 인자부터는 children 프롭의 값으로 배열로 들어가는 것을 콘솔로 찍어보면 확인할 수 있다.

세 번째 인자로 배열을 넣어보자

<body>
	<div id="root"></div>
  	<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  
	<script>
   		const spa = ["React", "Vue", "Angular"];
      
        const List = React.createElement("ul", { className: "list", style: { color: "blue" } },
      		// [
      		//   React.createElement("li", { key: 0, onClick: () => alert("React") }, "React"),
      		//   React.createElement("li", { key: 1, onClick: () => alert("Vue") }, "Vue"),
            //   React.createElement("li", { key: 2, onClick: () => alert("Angular") }, "Angular"),
      		// ]
			spa.map((v, i) => React.createElement("li", { key: i, onClick: () => alert(v) }, v))
      	);
      	
      	const root = ReactDOM.createRoot(document.getElementById("root"));
	    // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
		root.render(List);
    </script>
</body>
  • 자식 노드들이 두 개 이상부터는 children 프롭의 값으로 배열이 들어가기 때문에 세 번째 인자로 배열을 넣어줘서 작성할 수 있다.
    * 하지만 이때부터는 key 값을 넣어줘야 하는데 key값 넣는 이유는 코딩앙마 - React 에서 key 가 중요한 이유는? 그냥 index 를 쓰면 왜 안될까? 코딩앙마님 영상 추천한다.
  • 세 번째 인자로 배열을 넣어줘서 처리할 수 있기 때문에 배열을 반환하는 map을 사용하는 이유가 이것이다!
    * 자식 JSX에서 사용해서 자식 노드들을 표현할 때 map을 사용하면 세 번째 인자로 반환된 배열이 들어가게 되는 것이다!

다시 babel 사용하기

  • 지금부터는 함수형 컴포넌트로 만들어서 작성하면 다음과 같다.
  <body>
    <div id="root"></div>
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
      const spa = ["React", "Vue", "Angular"];

      function List() {
        return (
          <ul className="list" style={{ color: "blue" }}>
            {spa.map((v, i) => (
              <li key={i} onClick={() => alert(v)}>{v}</li>
            ))}
          </ul>
        );
      }

      const root = ReactDOM.createRoot(document.getElementById("root"));
      // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
      root.render(<List />);
    </script>
</body>

그럼 꼭 map 메서드만 사용해줘야 하나?

그렇지 않다! children 프롭으로 아까 말했던 것처럼 세 번째 인자로 배열만 반환해준다면 reduce를 사용하든 forEach로 return하든 상관이 없다.

// spa.map((v, i) => React.createElement("li", { key: i, onChange: () => alert(v) }, v))
spa.reduce((arr, v, i) => [...arr, React.createElement("li", { key: i, onClick: () => alert(v) }, v)], [])
function List() {
        return (
          <ul className="list" style={{ color: "blue" }}>
            {spa.reduce((arr, v, i) => (
              [...arr, <li key={i} onClick={() => alert(v)}>{v}</li>]
            ), [])}
          </ul>
        );
      }
  • map 메서드 대신 reduce를 바꿔 사용해도 똑같은 결과가 나오는 것을 확인할 수 있다.
  • 근데 굳이 이렇게 쓸 이유는 없는 것 같다...ㅎㅎ

children 프롭을 활용한 다른 표현

<body>
	<div id="root"></div>
  	<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  
	<script>
   		const spa = ["React", "Vue", "Angular"];
    
     	function List({ children }) {
        	return React.createElement("ul", { className: "list" },
          		children
        	);
        }
        
      	const root = ReactDOM.createRoot(document.getElementById("root"));
	    // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
     	root.render(
        	React.createElement("ul", { className: "list" },
           		spa.map((v, i) => React.createElement("li", { key: i, onChange: () => alert(v) }, v)),
	        )
        );
    </script>
</body>

babel 사용하기

<body>
    <div id="root"></div>
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"
    ></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
      const spa = ["React", "Vue", "Angular"];

      function List({ children }) {
        return (
          <ul className="list" style={{ color: "blue" }}>
            {children}
          </ul>
        );
      }

      const root = ReactDOM.createRoot(document.getElementById("root"));
      // 렌더링 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
      root.render(
        <List>
          {spa.map((v, i) => (
            <li key={i} onClick={() => alert(v)}>
              {v}
            </li>
          ))}
        </List>
      );
    </script>
</body>
  • 위에서 말한 것처러 여는 태그와 닫는 태그 사이가 세 번째 인자로 들어가고 props의 프로퍼티가 되기 때문에 List 함수형 컴포넌트에서 children으로 받아 이를 children으로 사용해 같은 표현을 할 수 있다.

0개의 댓글