스무디 한 잔 마시며 끝내는 리액트 + TDD (1)

y_cat·2022년 8월 7일
0

React.js

2011년 페이스북 개발자였던 Jordan Walke가 PHP용 HTML 컴포넌트 프레임워크였던 XHP에 영감받아 개발.
2011년 페이스북에 적용 → 2012년 인스타그램에 적용 → 2013년 5월 JSConf US에서 오픈소스로 발표.

자바스크립트에 HTML을 포함하는 JSX(JavaScript XML) 기반의 간단한 문법과 단방향 데이터 바인딩, 가상 DOM 특징 등이 있다.

특징

1. 가상 DOM

브라우저가 네트워크를 통해 HTML을 전달받으면 브라우저 렌더 엔진에서 HTML을 파싱하여 DOM Node로 이루어진 트리와 CSS 파일과 각 element inline 스타일을 파싱하여 CSSOM 트리를 만든다. 이 두 개의 트리들은 서로 결합되어 렌더 트리(Render Tree)라는 화면에 표시되어야 할 노드의 내용과 스타일(CSS)을 포함하는 트리를 형성한다.

렌더 트리가 생성되면 브라우저는 Attachment라는 과정을 통해 스타일 정보를 계산한다. 렌더 트리에서 모든 노드에는 attach라는 메소드가 있으며 Attachment 과정에서 attach 메소드를 동기적으로(synchronous) 호출하여 계산 결과값을 객체로 반환한다.

Attachment 과정 후, 브라우저가 렌더 트리의 각 노드에 좌표를 부여하고 정확히 어디에 어떻게 표시하는지 결정하는 레이아웃(Layout) 과정을 거친다.

그리고 브라우저는 각 노드에 paint 메소드를 호출하여 렌더링된 요소들에 색상을 입히는 페인팅(Painting) 과정을 거친 후 최종적으로 브라우저 화면을 출력한다.

이후에 자바스크립트를 사용하여 DOM을 조작하게 되면 각 노드에 좌표를 계산하고 부여하는 레이아웃 과정을 다시 수행하며(리플로우, Reflow), 다음에 색상을 입히는 페인팅 과정을 다시 수행한다(리페인트, Repaint).

자세한 내용은 medium 포스트를 확인해보자.


리플로우와 리페인트는 DOM의 각 노드에 많은 연산을 수행하므로 실제 웹서비스를 운영할 때 성능 이슈가 발생하게 된다. 특히나 싱글 페이지 어플리케이션(SPA)처럼 DOM 변경이 동시다발적으로 빈번히 발생하는 경우에는 리플로우와 리페인트를 많이 수행하면서 사이트 성능 이슈가 발생하게 된다.

React는 리플로우와 리페인트의 문제를 해결하기 위해 화면에 표시되는 DOM과 동일한 동일한 DOM을 메모리 상에서 만들고(가상 DOM), DOM 조작이 발생하게 되면 가상 DOM에서 모든 연산을 한 후에 실제 DOM을 갱신하여 리플로우와 리페인트의 연산을 최소화한다.

2. 단방향 데이터 바인딩

먼저, Angular.js와 Vue.js는 양방향 데이터 바인딩을 사용하는데, 사용자 UI의 데이터 변경을 감시하는 Watcher와 자바스크립트 안에서 변경되는 데이터를 감시하는 Watcher를 통해 UI와 어플리케이션 안의 데이터를 자동으로 동기화해주는 시스템이다. 개발자 입장에서는 자바스크립트 내에 데이터 변경과 UI에서의 데이터 변경 및 동기화를 크게 신경 쓸 필요가 없어 코드 작성량이 적어지는 장점이 있다.
하지만 데이터 동기화를 위해 데이터 하나에 두 개의 Watcher가 사용하기에 DOM 객체에서 이를 전부 렌더링하므로 성능 저하가 발생할 수 있다.

React는 양방향 데이터 바인딩이 가지는 문제점과 복잡성을 피하고자 단방향 데이터 바인딩(One-way Data Binding)을 채택하고 있다.

단방향 데이터 바인딩은 단 하나의 Watcher가 자바스크립트의 데이터 갱신을 감지하여 사용자 UI 데이터를 갱신한다. 사용자가 UI를 통해 데이터를 갱신할 때는 Event를 통해 데이터를 갱신하게 된다.

또한 React는 Flux 개념을 도입하여 데이터 흐름이 한쪽으로만 진행되도록 하고 있다.

3. JSX

JSX는 자바스크립트와 HTML을 동시에 사용하며, HTML에 자바스크립트 변수들을 바로 사용할 수 있는 일종의 템플릿 언어이다.

// JSX
const App = () => {
	const hello = "Hello world!";
    return <div>{hello}</div>;
};

위의 코드를 보면 자바스크립트 변수인 hello를 div 태그 안에 {hello}로 사용하여 출력하고 있다. 유사한 사례로 아래 코드를 보면 JSP에서 HTML 태그 안에 자바 변수를 사용하는 경우가 있다.

// JSP
<div><%= hello %></div>

4. 선언형 프로그래밍

// 명령형 프로그래밍
const double = (arr) => {
	let results = [];
    for (let i = 0; i < arr.length; ++i) {
    	results.push(arr[i] * 2);
    }
    return results;
}

// 선언형 프로그래밍
const double = (arr) => {
	return arr.map((elem) => elem * 2);
}

명령형 프로그래밍을 이용한 첫번째 함수는 주어진 배열(arr)에 값을 두 배로 늘리기 위해 for문을 사용했으며, i 변수와 배열의 크기(length)를 사용하여 배열의 값을 하나씩 가져와 두 배로 만든 후, results라는 새로운 배열에 추가하여 반환한다. 즉, 명령형 프로그래밍은 과정을 중심으로 프로그래밍을 하게 된다.

반면, 선언형 프로그래밍을 이용한 두번째 함수는 map 함수를 사용하여 주어진 배열 값을 두 배로 만들어 반환한다. map이 어떻게 동작하는 지는 크게 신경쓰지 않고 결과인 배열 값을 두 배로 만드는 로직에 집중하여 프로그래밍을 하게 된다.

React에서는 JSX를 사용함으로써 더욱 명확하게 선언형 프로그래밍을 활용하고 있다.

// original JS code
<script>
var arr = [1, 2, 3, 4, 5]
var elem = document.querySelector("#list");

for(var i = 0; i < arr.length; ++i) {
	var child = document.createElement("li");
    child.innerHTML = arr[i];
    elem.appendChild(child);
}
</script>

// React JSX code
const arr = [1, 2, 3, 4, 5];
return (
	<ul>
    	{arr.map((elem) => (
        	<li>{elem}</li>
        ))}
    </ul>
);

위의 기존의 js코드와 비교해보면 React는 JSX을 활용하여 HTML을 조작할 때에도 선언형 프로그래밍을 할 수 있다. 이는 코드를 예측할 수 있게 하고 디버깅을 쉽게 할 수 있도록 도와주므로 전체적인 코드 퀄리티의 상승과 코드의 이해를 도와주는 효과를 얻을 수 있다.

React로 웹 UI를 개발할 때는 컴포넌트(Component)라는 불리는 작고 고립된 코드들을 이용하여 구현하게 된다.

// using Component
const Title = () => {
	return <h1>Hello world</h1>;
};

const Button = () => {
	return <button>This is a Button</button>;
};

const App = () => {
	return (
    	<div>
        	<Title />
            <Button />
        </div>
    };
};

위의 코드에서 React에서는 Title과 Button 컴포넌트를 만든 후에, App 컴포넌트에서 이미 만들어진 Title, Button 컴포넌트를 활용하여 페이지를 제작한다. 물론 이 둘은 다른 컴포넌트에서도 반복적으로 사용할 수 있다. 따라서 React는 JSX를 활용하여 UI 제작할 때 기본적으로 컴포넌트 기반 프로그래밍을 한다.

profile
토이 프로젝트와 기술들 정리하는 블로그

0개의 댓글