Introduction



안녕하세요, 오픈소스 컨설팅의 Playce-Dev 팀 Front-end 개발자 노은지입니다. :blush:

여러 가지 Front-end development framework들 중, 현재 전 세계적으로 널리 쓰이고 있는 대표적인 library 또는 framework로는 React, Vue, Angular가 있습니다. 그중 React는 2013년 출시 이후 현재까지 개발자들에게 가장 많은 사랑을 받고 있는데요. 저희 Playce-Dev 팀 또한 현재 React를 사용하여 Front-end 개발을 하고 있습니다.

저는 이번에 React가 무엇이기에 이렇게 인기가 많은지, 깊이 있게 공부해 보고 Playce-Dev 팀이 React를 선택하게 된 배경에 대해서 이해해 보는 시간을 가지려고 합니다.


잘못된 정보에 대한 수정은 저에게 큰 자산이 됩니다!

일단 React가 무엇인지 설명하기에 앞서 지금까지 웹이 어떻게 변화해 왔는지에 대해서 간단히 알아보겠습니다.

정적 웹 페이지에서 SPA로

정적 웹 페이지의 화면 렌더링

static website before React
이미지 출처: Dynamic vs SPA vs Static Websites

예전에는 정적인 웹 페이지가 주를 이루었습니다.
회사 소개 페이지나 인터넷 뉴스 기사, 블로그 글 같은 것이 정적 웹 페이지의 대표적인 예입니다.

이런 정적 웹 사이트들의 서버에서는 이미 만들어진 html 문서들이 있고, 사용자가 브라우저를 통해서 도메인에 접속하면 서버에 이미 배포 되어있는 html 문서들을 가져와서 보여주는 형식을 사용해 왔습니다. 이것을 Server Side Rendering(SSR)이라고 합니다.
브라우저는 서버가 응답한 html을 응답받아 렌더링 하는데 이때 전체 페이지를 다시 렌더링 하게 되어서 새로고침이 일어납니다. 사용성이 좋지 않은 단점이 있죠.

시간이 지나 기술이 발전하면서 웹 페이지 내에서 부분적인 업데이트가 가능해졌고, html이 아니라 필요한 데이터를 자바스크립트를 이용해 동적으로 HTML 요소를 생성해 페이지를 업데이트하는 방식으로 변화하였습니다.

이러한 방식이 AJAX 라고 불리게 되며 구글에서 이것을 이용해 Gmail, Google map 등의 웹 애플리케이션을 만들기 시작했습니다. 이것이 바로 SPA(Single Page Application)입니다.


Single Page Application (SPA)

SPA의 화면 렌더링

Render DOM in spa
이미지 출처: Dynamic vs SPA vs Static Websites

전통적인 웹페이지에서 전체 페이지를 다시 렌더링 하는 방식(SSR)은 새로고침이 되면서 화면이 깜빡이는 현상이 나타납니다. 그리고 변경이 필요 없는 부분까지 포함해서 전체 페이지를 갱신하므로 비 효율적이라고 할 수 있습니다.

하지만 SPA는 말 그대로 하나의 페이지로 구성되어 있습니다. 이것은 웹 애플리케이션에 필요한 모든 리소스를 최초 단 한 번만 다운로드하고, 이후에 새로운 페이지를 요청할 때 변경이 일어난 데이터 만을 전달받습니다.
이는 새로고침 없이 페이지 갱신이 되므로 기존의 SSR 방식에 비해 효율적이며 사용자 경험이 좋습니다.

SPA 방식의 웹 애플리케이션은 많이 생겨나게 되었고 점차 기능이 많아지면서 자연스럽게 프로젝트들은 규모가 커지게 되었습니다. React는 이렇게 커진 앱들을 유지, 보수하기 편리한 자바스크립트 라이브러리입니다. 어떻게 편한지, React가 가지고 있는 특징들은 무엇인지 한 번 알아보겠습니다.


:earth_asia: React

Facebook Team이 React를 만든 이유?

Facebook team은 Model과 View, Controller (MVC) 모델을 적용해 SPA 개발을 하고 있었습니다. 애플리케이션 규모가 작을 땐 괜찮았지만, 새로운 기능이 추가될수록 구조는 너무 복잡해져갔고 디버깅이 쉽지 않았습니다.
이를 해결하기 위해 더 많은 엔지니어들이 투입되었습니다. 하지만 View의 아주 작은 변화만으로도 일어나는 리 렌더링과 계단식 업데이트로 인해 코드는 점점 예측 불가능해져 갔습니다.

Facebook 팀의 일원인 엔지니어 Jordan Walker는 이를 해결하기 위해 뭔가를 해야겠다고 생각했고, 개발에 착수했습니다. 그리고 2013년, React는 오픈소스로 세상에 공개되었습니다.

React의 특징

1. 리액트는 재사용 가능한 컴포넌트 구현을 장려합니다.

리액트 공식 블로그에 따르면, 리액트는 재사용 가능한 사용자 인터페이스(User Interface, UI)를 만들기 위한 라이브러리입니다. 리액트는 가변적인 데이터를 반영할 수 있는 재사용 가능한 UI 컴포넌트를 만드는 것을 장려합니다.
이는 반복되는 코드를 줄임으로써 크고 복잡한 UI를 효과적으로 구성할 수 있습니다.

과연 컴포넌트를 어떻게 재사용 가능한지, 예제로 함께 보실까요?

저는 Test라는 컴포넌트에서 제 이름과 사는 지역을 입력해 화면에 출력하고 싶습니다.

// Test.jsx

export default function Test() {
  const name = "Eunji";
  const city = "Seoul";

  return (
    <>
      <h1>
        Hi my name is {name}, I live in {city}.
      </h1>
    </>
  );

}
// App.js

import "./styles.css";
import Test from "./Test";

export default function App() {

  return (
    <div className="App">
      <Test />
    </div>
  );

}

위 코드는 다음과 같이 화면에 출력 됩니다.

reusable component in react

지금 namecity라는 변수에 담긴 문자열은 제가 직접 입력한 값이지만, 서버에서 받아오는 유저의 정보라고 가정한다면 우리는 매번 다른 사용자의 정보를 받아 올 때마다, 그 클라이언트의 정보에 해당하는 다른 값을 보여주고 싶을 겁니다.

그럴 경우를 대비해서, 이렇게 하면 Test라는 컴포넌트를 다시 사용할 수 있을 것 같습니다.

// App.js

import "./styles.css";
import Test from "./Test";

export default function App() {
  const name = "Eunji";
  const city = "Seoul";

  return (
    <div className="App">
      <Test name={name} city={city} />
    </div>
  );

}
// Test.jsx

export default function Test(props) {
  const { name, city } = props;
  return (
    <>
      <h1>
        Hi my name is {name}, I live in {city}.
      </h1>
    </>
  );
}

부모 컴포넌트인 App 컴포넌트에서 name, city라는 변수에 데이터를 저장해 Test 컴포넌트에 props로 보내주는 방식으로 바꿨습니다. Test 컴포넌트에서는 구조 분해 할당을 통해 namecity를 받아 화면에 출력해 줍니다.

reusable component in react

코드는 조금 바뀌었지만 똑같은 화면이 출력 됩니다. App.js에서 데이터가 바뀌더라도 Test.jsx의 코드는 재사용이 가능합니다. 이번에는 데이터가 많은 상황에서도 컴포넌트가 재활용이 잘 되는지 확인해 보고 싶습니다.

// App.js

import Test from "./Test";

export default function App() {
  const users = [
    { name: "Eunji", city: "Seoul" },
    { name: "Suji", city: "Daejeon" },
    { name: "Wonchul", city: "Wonju" },
    { name: "Dahyun", city: "ChunCheon" },
    { name: "Yongtae", city: "Mokpo" }
  ];

  return (
    <div className="App">
      {users.map(({ name, city }) => (
        <Test key={name} name={name} city={city} />
      ))}
    </div>
  );
}

컴포넌트 재활용 예 – CodeSandbox

App.js의 코드만 수정을 해 보았습니다. users라는 변수 안에 (이 데이터를 서버에서 받아오는 데이터라고 가정하겠습니다.) 객체로 저장된 정보들이 배열 형태로 저장되어 있습니다.

map 메서드를 사용해 반복되는 컴포넌트를 효율적으로 보여주었고 비 구조화 할당으로 namecity를 이전과 마찬가지로 props로 보내줍니다. 여전히 Test 컴포넌트의 코드는 변한 게 아무것도 없습니다. 예전 컴포넌트 그대로 가져다 쓰면 됩니다.

그럼 화면 출력이 어떻게 되는지 확인해 볼까요?

reusable component in react

컴포넌트 재활용이 잘 되는 것을 확인할 수 있습니다.

React는 이렇게 반복되는 코드들을 컴포넌트 화해서 재사용 한다면, 훨씬 간편하고 효율적으로 코드를 작성할 수 있다고 설명하고 있습니다.

2. JSX의 사용

JSX는 JavaScript 언어 구문에 대한 React 확장으로 HTML과 모양이 비슷합니다. 정말 그런지 비교 해 볼까요?

<!-- HTML 문법 -->

<!DOCTYPE html>
<html>
<body>

<h1>Hello World!</h1>

<p>good to see you</p>

</body>
</html>
// JSX 문법

export default function App(){
  return(
    <div>
      <h1>Hello World!</h1>
      <p>good to see you</p>
    </div>
  );
}

어느 정도 흡사한 모습을 갖추고 있는 것 같네요.

JSX를 쓰면 React.createElement() 호출을 반복해야 하는 불편을 해소하고 React 엘리먼트를 생성하면서 JavaScript의 모든 기능을 쓸 수 있도록 도와줍니다.

아래의 두 코드 스니펫은 동일한 코드입니다. 위는 JSX를 사용하였지만, 아래는 JSX를 쓰지 않은 순수 JavaScript 입니다. JSX를 사용한 코드가 더 간결해 보이는군요. JSX는 이렇게 개발자가 편리하게 UI를 렌더링 할 수 있도록 돕습니다.

// JSX 문법

export default function App(){
  return(
    <div>
      <h1>Hello World!</h1>
      <p>good to see you</p>
    </div>
  );
}
// JSX를 사용하지 않은 코드

export default function App() {
  return React.createElement(
    "div",
        null,
    React.createElement(
      "h1",
      null,
      "Hello World!"
    ),
    React.createElement(
      "p",
      null,
      "good to see you"
    )
  );

}

물론 JSX를 사용하는 것이 필수는 아닙니다.

빌드 환경에서 컴파일 설정을 하고 싶지 않을 때, JSX 없이 React를 사용하는 것은 특히 편리하다고 합니다.

💡 React 컴포넌트 파일에서 JSX를 작성하면 Babel은 이것을 JavaScript로 변환해 주는데, Babel은 아직 지원되지 않은 최신 문법 등을 정식 자바스크립트 형태로 변환하여 구형 브라우저 환경에서도 제대로 실행 되도록 해주는 역할을 합니다.

JSX를 사용할 때, 지켜야 할 몇 가지 규칙이 있습니다.

  • 컴포넌트 안에 두 개 이상의 요소가 있으면 반드시 부모 요소 하나로 감싸주어야 합니다.
    다음 예제는 <div><p> 2개의 요소가 컴포넌트 안에 존재합니다.
error in react jsx

빨간 줄로 에러가 있음을 알려 주고 있네요.

해당 라인에 마우스 오버를 해 보면 React 컴포넌트에서 JSX는 꼭 하나의 부모 요소로 감싸져야 한다고 에러 메시지를 띄워 줍니다.

example

마찬가지로 감싸주는 태그로 JSX를 감싸주어야 한다고 TypeError 메시지가 화면에 나타납니다. 친절하게 제가 혹시 Fragment 태그를 쓰고 싶었던 건지도 물어봐 줍니다.

💡 Fragment는 React v16에 추가된 기능으로 DOM에 별도로 노드를 추가하지 않고도 여러 자식을 그룹화 함으로써 메모리 사용을 아낄 수 있습니다.

그럼 오류를 해결해 볼까요? Fragment 태그로 요소들을 감싸 보겠습니다.

react jsx

‘Hello World!’와 함께 인자한 미소가 화면에 잘 출력 되고 있습니다.

  • JSX 내부의 자바스크립트 변수는 중괄호 로 감싸줍니다.

    아래 예제들처럼 자바스크립트 값을 JSX 내부에서 사용하려면 앞서 보셨던 코드 스니펫처럼, 값을 중괄호로 감싸주면 됩니다. JSX를 묶어주는 괄호는 자동 세미콜론 삽입을 피할 수 있으므로 사용이 권장됩니다.
export default function Test() {
  const name = "Eunji";
  const city = "Seoul";

  return (
    <>
      <h1>
        Hi my name is {name}, I live in {city}.
      </h1>
    </>
  );

}
export default function App() {
  const users = [
    { name: "Eunji", city: "Seoul" },
    { name: "Suji", city: "Daejeon" },
    { name: "Wonchul", city: "Wonju" },
    { name: "Dahyun", city: "ChunCheon" },
    { name: "Yongtae", city: "Mokpo" }
  ];

  return (
    <div className="App">
      {users.map(({ name, city }) => (
        <Test key={name} name={name} city={city} />
      ))}
    </div>
  );

}

  • CSS class가 아닌 className으로 설정해 주어야 합니다.
import React from "react";

export default function App() {
  return (
    <>
      <div className="title">Hello World!</div>
      <p>:)</p>
    </>
  );
}

  • Inline style을 적용할 때는 객체 형태로 작성합니다.
import React from "react";

export default function App() {
  const style = {
    width: "300px",
    height: "200px",
    border: "1px dashed black"
  };
  return (
    <div style={style}>
      <div className="title">Hello World!</div>
      <p>:)</p>
    </div>
  );
}

  • JSX 내부 주석은 / **/ 를 사용해야 합니다. 단 중괄호로 그 밖을 감싸주어야 합니다.
import React from "react";

export default function App() {

  return (
    <>
      <div>{/*Hello World!*/}</div>
      <h1>:)</h1>
    </>
  );
}

3. 단방향 데이터 바인딩

데이터 바인딩?

export default function App() {
  const date= "2023-01-01";

  return (
    <div className="App">
      <h1>{date}</h1>
    </div>
  );

}

위와 같이 date라는 변수에 '2023-01-01'이라는 날짜를 담아 중괄호 사이에 값을 넣어주면 그 값이 화면에 출력 됩니다. 값을 변수에 연결하여 할당하는 것, 이를 데이터 바인딩이라 합니다.

React는 전통적 양방향 데이터 바인딩과 비교하면 더 많은 코드를 필요로 합니다.

하지만 데이터 흐름을 명시적으로 만들어서 프로그램이 어떻게 동작하는지 파악할 수 있도록 도와줍니다. 그래서 디버깅이 쉽습니다.

하지만 데이터의 변화를 감지하여 동작하게 하는 코드를 매번 작성해 주어야 한다는 단점이 있습니다.

그래서 코드의 양이 양방향 데이터 바인딩 보다 늘어나게 됩니다.

이해를 위해 양방향 데이터 바인딩을 지원하는 Vue의 코드와 React의 코드를 잠시 비교해 보겠습니다.

Vue에서의 input 입력: 양방향 바인딩

💡 양방향 데이터 바인딩이란?

사용자의 입력값이 곧바로 코드 상의 변수에 바인딩 되는 것입니다. React의 경우 변수는 적절한 Event를 통해서
코드 상의 변수에 데이터 값이 담깁니다. 하지만 양방향 데이터 바인딩의 경우엔 사용자의 입력값이 이벤트 없이도 즉시 화면에 반영됩니다.
// App.vue

<script>
export default {
  data() {
    return {
      message: "",
      age: "",
      name: "",
    };
  },
};
</script>

<template>
  <p>메세지 내용: {{ message }}</p>
  <p>age: {{ age }}</p>
  <p>name: {{ name }}</p>
  <input v-model="message" placeholder="메세지 입력하기" />
  <input v-model="age" placeholder="나이 입력하기" />
  <input v-model="name" placeholder="이름 입력하기" />

</template>

Vue 양방향 바인딩 – CodeSandbox

v-model 은 폼 입력 엘리먼트 상태를 JavaScript 상태와 동기화해야 하는 경우, 값 바인딩을 수동으로 연결하고 이벤트 리스너를 변경하는 번거로운 작업을 단순화해 줍니다.

위의 링크를 통해 예제 코드와 화면 상에 어떻게 출력 되는지 확인해 볼수 있습니다.

Vue에 대한 더 자세한 포스트를 읽고 싶으시다면, 이정현 개발자님의 포스팅 “작고 소중한 Vue를 알아보자“를 참고해 주세요 🙂


React에서의 input 입력: 단방향 바인딩

// App.js

import { useState } from "react";

export default function App() {
  const [message, setMessage] = useState("");
  const [age, setAge] = useState("");
  const [name, setName] = useState("");
  return (
    <div className="App">
      <p>메시지 내용: {message}</p>
      <p>나이 : {age}</p>
      <p>이름: {name}</p>
      <input
        type="text"
        onChange={(e) => setMessage(e.target.value)}
        placeholder="메시지 입력하기"
        value={message}
      />
      <input
        type="number"
        onChange={(e) => setAge(e.target.value)}
        placeholder="나이 입력하기"
        value={age}
      />
      <input
        type="text"
        onChange={(e) => setName(e.target.value)}
        placeholder="이름 입력하기"
        value={name}
      />
    </div>
  );

}

React 단방향 바인딩 – CodeSandbox

이렇게 코드를 비교해 보니 확실히 양방향 바인딩을 사용하는 Vue가 코드의 양이 적은 것을 볼 수 있습니다. 지금은 input이 3개뿐이지만 더 복잡한 애플리케이션에서는 그 코드가 더 길어질 것입니다.

4. 가상 DOM (Virtual DOM)

💡 DOM이란?

Document Object Model의 약자로, 웹 페이지에 대한 인터페이스입니다. 문서의 구조화된 표현을 제공해 프로그래밍 언어가 DOM에 접근해 문서 구조나 스타일, 내용 등을 변경할 수 있게 합니다. 예를 들어 자바스크립트와 같은 스크립팅 언어를 이용해 DOM을 수정할 수 있죠.

기존의 JavaScript 애플리케이션은 데이터가 변경되었을 때 직접 DOM을 변경해 최신 상태로 유지해 주어야 했습니다. 요즘 우리가 접하는 큰 규모의 웹 애플리케이션인 트위터나 페이스북은 우리가 스크롤 바를 내릴수록 수많은 데이터가 로딩 됩니다.

이런 큰 규모의 애플리케이션들은 수천수만 개의 DOM 요소들이 있겠죠. 웹 브라우저 단에서 DOM에 변화가 일어났을 경우에는 브라우저가 CSS를 다시 연산하고, 레이아웃을 구성해 리 렌더링이 일어나는 과정이 또 일어나게 됩니다. 이것은 성능 이슈를 야기합니다.

하지만 React는 Virtual DOM을 사용하여 데이터 변경 전의 Virtual DOM과 변경 후 Virtual DOM을 비교하고, 바뀐 부분만 실제 DOM에 반영하면서 리 렌더링 합니다. 이로써 React는 비효율적인 DOM 조작을 줄여 브라우저 내에서 발생하는 연산의 양을 줄여 성능을 개선합니다.

아래 이미지는 전체 UI를 다시 그려도 React DOM은 변경된 텍스트 노드만 업데이트되는 모습을 보여 줍니다.

React DOM
이미지 출처 : React Element Rendering


React에서 Virtual DOM 이란 무엇인가요?

Virtual DOM은 UI의 이상적 또는 가상적 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 실제 DOM과 동기화하는 것입니다. 이것을 재조정(Reconciliation)이라고 합니다.

재조정 (Reconciliation)

재조정은 리액트의 View에 변화가 있을 때, 변경 전 Virtual DOM tree와 변경 후 Virtual DOM tree를 비교해 변경된 내용만이 실제 DOM에 적용 되도록 비교 연산하는 과정을 일컫습니다. 이 둘 트리를 비교할 때, 리액트는 비교 알고리즘을 이용해 두 엘리먼트의 루트 엘리먼트부터 비교합니다. 이후 동작은 루트 엘리먼트 타입에 따라 달라집니다.

  • DOM 엘리먼트의 타입이 다를 경우
<div>
  <Counter />
</div>

<span>
  <Counter />

</span>

이 경우 루트 엘리먼트 아래의 모든 컴포넌트들은 언마운트 되고 state도 사라집니다. 이전의 Counter는 사라지고, 새로 다시 마운트가 됩니다.

  • DOM 엘리먼트의 타입이 같을 경우
<div className="before" title="stuff" />


<div className="after" title="stuff" />

이 두 엘리먼트를 비교할 경우, 속성을 비교해 변경된 속성들만 갱신해 줍니다. 즉, className만 수정됩니다.
이것은 style이 갱신될 때도 마찬가지입니다. 변경된 style 속성만을 업데이트해 줍니다.

  • DOM 노드의 자식에 대한 재귀적 처리
/* Before */

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

/* After */

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>

</ul>

React 공식 홈페이지에서는 위 코드로 리스트의 맨 앞에 엘리먼트가 추가된 경우 성능이 좋지 않다는 것을 보여주고 있습니다. <li>Duke</li> 와 <li>Villanova</li> 종속 트리를 그대로 유지하는 대신 모든 자식을 변경하기 때문에 비 효율적이라는 것입니다.

⚠️ Warning: Each child in a list should have a unique "key" prop.

개발을 하는 도중, 우리는 이런 경고 메시지를 종종 마주칩니다. 각각의 자식들은 고유한 “key” 값을 가져야 한다고 말입니다.
key는 이 비효율적인 방식을 해결하는 “열쇠”가 됩니다. 자식들이 key를 가지고 있다면, 이것을 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인할 수 있습니다.
아래 코드처럼 key를 추가하면 트리의 변환 작업이 효율적으로 수행되도록 수정할 수 있습니다.

/* Before */

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

/* After */

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>

</ul>

React는 ‘2014’라는 key를 가진 엘리먼트가 새로 추가되었다는 것을 알게 되었고, ‘2015’, ‘2016’ key를 가진 엘리먼트들은 이동만 하면 된다는 것 또한 알게 될 것입니다.

  • key는 어떻게 부여하면 좋을까요?
💡 우리의 데이터 구조에 ID라는 고유한 속성이 있거나, 특정한 고윳값이 존재한다면 그것을 key로 사용하면 됩니다. 또는 데이터 일부에 Hash를 적용하면 key를 생성할 수 있습니다.
Hash는 임의의 크기를 가진 데이터(key)를 고정된 크기의 데이터(value)로 변화시켜 저장하는 것을 뜻합니다.

key는 오로지 자식들 사이에서만 유일하면 되고, 전역에서 유일할 필요는 없습니다. 최후의 수단으로 배열의 indexkey로 사용할 수 있지만, 항목의 순서가 바뀔 수 있는 경우엔 index를 key로 사용하는 것은 비 효율적으로 동작합니다.

key는 React가 DOM 엘리먼트들을 식별하기 위한 유일한 수단입니다.

만약 배열에 아이템 하나를 추가했거나 중간에 있던 것을 삭제 한 경우는 어떻게 될까요? 앞서 key가 같으면 React가 같은 DOM element로 인식한다고 말씀드렸는데요, indexkey라면 이는 더 이상 사실이 아니게 됩니다.

아래의 코드는 <div>key 값으로 index를 부여했습니다.

// index = key 일 때

import React, { useState } from "react";

export default function App() {
  const [list, setList] = useState([
    { name: "John" },
    { name: "Heim" },
    { name: "Billy" },
    { name: "Suji" },
    { name: "Molly" }
  ]);

  const addItem = () => {
    setList([{ name: "Somebody" }, ...list]);
  };

  return (
    <>
      <h2> Input에 글씨를 입력해 보아요</h2>
      {list.map(({name}, index) => (
        <div key={index}>
          {name}, idx: {index} <input type="text" />
        </div>
      ))}
      <input type="button" value="추가" onClick={addItem} />
    </>
  );
}

추가 버튼을 누르면 어떤 일이 일어나는지 아래 영상을 통해 확인해 볼까요?

when you do not use key

Somebody라는 element들이 추가되면서 헬로, 나이스, , , 는 상단에 그대로 있지만, 다른 이름들은 아래로 밀려납니다. 코드 상에서 setList 함수를 보시면 새로 추가되는 객체 형태의 Somebody가 전개 구문으로 펼쳐진 list 배열 전에 추가되고 있습니다. 따라서, 리스트에 가장 최근에 추가되는 요소인 Somebody들이 위에서부터 차례로 위치 하게 되지만 여전히 최상단 element의 index는 0입니다.

React는 변경 전과 후의 트리에서 key로 자식들을 구분한다고 언급했던 것, 기억나시나요? 이런 점 때문에 indexkey 값으로 부여했을 때 문제가 생기게 됩니다.

이번엔 고유한 값을 key로 지정했을 때, 어떻게 되는지 살펴볼까요?

// id = key 일 때

import React, { useState } from "react";

export default function App() {
  const [list, setList] = useState([
    { name: "John", id: 1 },
    { name: "Heim", id: 2 },
    { name: "Billy", id: 3 },
    { name: "Suji", id: 4 },
    { name: "Molly", id: 5 }
  ]);

  const addItem = () => {
    const id = new Date().getTime();
    setList([{ name: "Somebody", id }, ...list]);
  };

  return (
    <>
      <h2> Input에 글씨를 입력해 보아요</h2>
      {list.map(({ name, id }) => (
        <div key={id}>
          {name}: <input type="text" />
        </div>
      ))}
      <input type="button" value="추가" onClick={addItem} />
    </>
  );
}

각 이름에 다른 id를 부여하고, 새로 생겨날 Somebody에게도 중복되지 않는 id를 부여하기 위해 new Date()로 현재 시간을 구해 milliseconds로 변환해 주었습니다. 여전히 새로 생성되는 Somebody가 리스트의 최상단에 추가 되도록 할 겁니다.

when you use key

이번엔 input의 글씨들이 제 주인을 따라 잘 움직입니다. 앞으로는 React가 착각하지 않도록 key를 중복되지 않는, 고유한 값으로 부여해 줘야겠습니다.



So, why React?

그래서, React는 2013년 공개된 이래로 급변하는 Front-end 트렌드 속에서 어떻게 그 꾸준한 인기를 유지할 수 있었을까요?

리액트는 배우기 쉽고 사용이 간편하다.

Angular와 같은 프레임워크와 비교하면, ReactJS는 러닝 커브가 훨씬 낮습니다. 더 배우기 쉽기 때문에 자연스럽게 사용 인구는 늘어나게 됩니다.

강력한 React 커뮤니티.

Facebook 팀이 개발한 오픈 소스이며 대규모 커뮤니티에 의해서 유지되고 있기 때문입니다. 특히 저와 같은 주니어 개발자들이 배우기 좋은 환경을 제공해 줍니다. 정기적인 버전 업데이트 또한 끊임없이 발전하고 변화하는 Front-end 트렌드에서 살아남기 위한 필수 요소입니다. 이 때문에 코드의 안정성 또한 높습니다.

React는 컴포넌트 커스터마이징을 권장합니다.

재사용 가능한 컴포넌트 개발은 개발자들이 자신만의 컴포넌트 개발과 재사용을 가능하게 하며 규모가 큰 애플리케이션을 구축하기에 용이합니다. 이는 높은 수준의 UI를 구현하는 데 큰 도움을 주고 프로젝트를 구축하는데 드는 시간도 단축시킵니다.

높은 유연성과 효과적인 비용 절감.

높은 유연성은 곧 개발자에게 더 높은 자율성을 부여하게 됩니다. 상대적으로 자유도가 떨어지는 다른 프레임워크들과는 달리 React로 구축한 프로젝트는 필요로 하는 기능을 구현하기 위해 원하는 라이브러리만을 선택할 수 있습니다. 이 때문에 기업들은 많은 비용을 절감할 수 있습니다.

많은 React 관련 extension들을 제공합니다.

리액트는 많은 extension들이 존재합니다. 이들은 개발자들이 편하게 코딩할 수 있도록 도와주며 코드를 더욱 읽기 쉽게 만들어 주죠. VS code는 React를 위해 많은 extension 들을 제공하고 크롬 브라우저 또한 redux store와 React 컴포넌트들을 추적할 수 있는 extension을 지원하고 있습니다.

이 외에도 제가 언급하지 않았지만 다른 많은 장점들이 있기 때문에 Facebook(Meta) 뿐만 아니라 Airbnb, Netflix, Uber 등의 글로벌 기업들이 React를 이용해 개발을 하고 있을 것이라고 생각됩니다.

현재 프론트엔드 자바스크립트 프레임워크(or 라이브러리)들은 저마다의 장점이 있는 모두 훌륭한 개발 도구라고 생각합니다. 그리고 우리는 각자의 실정에 맞추어 최적의 환경에서 최고의 가치를 창출해 낼 수 있는 Tool을 찾아 내는 과정을 거칩니다. 그렇게 선택된 Tool들은 그게 무엇이든 각자에게는 최고의 건축 재료라고 할 수 있을 것 같습니다.



글을 마무리하며,

이제 갓 Front-end 개발자로서의 커리어를 시작한 저로서는 아직까지 React를 제외한 다른 JavaScript 라이브러리나 프레임워크를 이용해 개발을 해 본 경험이 없습니다.

때문에 ‘다른 프레임 워크를 경험한 후 React를 사용 해보니 이런 점에서 React가 뛰어나더라‘라는 저의 개인적인 의견을 낼 수는 없었던 점이 조금 아쉬웠습니다.

하지만 이번 기회에 React에 대한 조사를 하면서, 제가 사용하고 있는 React가 어떤 고민으로부터 출발해 개발되기 시작했는지 알게 되었습니다.

또한 왜 저에게 Key prop 필요하다는 에러 메시지를 보여 주었던 것인지 등 제가 몰랐던, 또는 그냥 지나쳤던 리액트에 대해 더 깊이 이해해 볼 수 있었던 시간이었습니다.

이번에 양방향 데이터 바인딩에 대해 알아보며 Vue를 아주 살짝 맛볼 기회도 있었는데요.

React의 장점을 갖추고 있으면서도 더 가볍고, 배우기 쉽다는 큰 장점으로 개발자들에게 많은 사랑을 받고 있다고 합니다.

확실히 정해진 문법이 있다는 점이 저에게는 조금 더 배우기 쉽게 다가왔던 것 같습니다.

아직까지는 React에 비하면 작은 생태계를 보유하고 있지만, Vue의 수요가 계속해서 증가하는 만큼 앞으로의 발전도 기대되는 프레임워크라고 생각됩니다.

지금까지 저의 부족한 글을 읽어주셔서 감사드리고, 더 발전된 모습으로 또 찾아뵙겠습니다! :slight_smile:



참고

다양한 경험을 하고 그 가치를 함께 나누는 것을 좋아합니다. 오픈소스컨설팅 프론트엔드 개발자 노은지입니다.

Leave a Reply

Your email address will not be published. Required fields are marked *