React란?
- 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리
- Declarative
- 상호작용이 많은 UI를 만들 때 생기는 어려움을 줄여줍니다.
- 데이터가 변경됨에 따라 적절한 컴포넌트만 효율적으로 갱신하고 렌더링합니다.
- Component-Based
- 스스로 상태를 관리하는 캡슐화된 컴포넌트를 만들어서 이를 조합해 복잡한 UI를 만들 수 있습니다.
- 컴포넌트 로직은 템플릿이 아닌 JavaScript로 작성됩니다. 따라서 다양한 형식의 데이터를 앱 안에서 손쉽게 전달할 수 있고, DOM*과는 별개로 상태를 관리할 수 있습니다.
- Learn Once, Write Anywhere
- Node에서 렌더링을 할 수도 있고, React Native를 이용하면 모바일 앱도 만들 수 있습니다.
왜 쓸까?
- SPA(Single Page Application) 가능하다. (Vue, Angular도 가능)
- React Native 포팅에 오랜시간을 투자하지 않을 수 도 있음(css를 잘 고려해서 적용한 경우에 상당한 시간 단축 가능)
- META(구 Facebook)의 지원과 커뮤니티 이용자가 굉장히 많기 때문에 자료가 방대함
- 컴포넌트 재사용 가능 (중복성 최소화, 리소스 최소화, 시간절약 가능)
💡
*DOM(The Document Object Model)
- 문서 객체 모델
- HTML, XML 문서프로그래밍 인터페이스 입니다.
- 언어의 구조적 표현을 제공하여 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕습니다.
- DOM은 구조화된 nodes와 property와 method를 갖고 있는 객체로 문서를 표현합니다.
일반적인 브라우저의 렌더링 과정
- 리소스 요청
- 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트파일 등 렌더링에 필요한 리소스를 서버에 요청하고 응답을 받습니다.
- HTML 문서 파싱 & DOM tree 생성
- 브라우저의 렌더링 엔진은 서버로부터 응답받은 HTML 문서를 한줄 한줄 순차적으로 파싱*하여 DOM을 생성해 나갑니다.
- CSS 파싱 & CSSOM 생성
- 2. 작업중에 CSS를 로드하는 link 태그 혹은 style 태그를 만나면 DOM 생성을 중지한 후 CSS 파싱의 결과물인 CSSOM*을 생성하는 과정을 진행합니다.
- JS 엔진 실행 & JS 파싱 &
- 2. 작업중에 script태그를 만나면 자바스크립트 엔진은 제어권을 넘기며 시작됩니다. 자바스크립트 엔진은 CPU가 이해할 수 있는 저수준 언어로 자바스크립트 코드를 파싱하고 > DOM, CSSOM과 같은 AST*를 생성하며 > AST를 기반으로 바이트코드를 생성하여 > 인터프리터가 실행될 수 있도록 만듭니다.
- 렌더트리 생성
- 이 둘을 결합하여 문서의 시각적인 구조를 나타내는 렌더트리를 생성합니다.
- Layout (reflow) 🔁
- 각 노드들의 스크린에서의 좌표에 따라 위치 결정
- Paint(repaint) 🔁
- 결합된 렌더트리를 기반으로 HTML요소의 레이아웃을 계산하고 브라우저에 HTML요소를 페인팅 합니다.
문제점
어떠한 인터랙션에 의하여, 자바스크립트 코드가 실행되는 과정에서 DOM과 CSSOM을 변경하는 DOM API가 사용되면 DOM과 CSSOM이 다시 렌더트리로 결합되고 레이아웃과 페인트 과정을 거쳐 다시 렌더링 됩니다. 이 과정을 reflow, repaint라 합니다.
이러한 전체 노드들이 다시 그려지는 것으로 불필요한 과정으로 인하여, DOM을 조작하는 소모적인 비용이 생깁니다.
* 리플로우는 레이아웃을 다시 계산하는것이며, 리 페인트는 재결합된 렌더트리를 기반으로 다시 페인트 하는 것입니다.
리플로우는 노드의 추가/삭제, 요소의 크기/위치 변경, 윈도우의 리사이징 등 레이아웃에 영향을 주는 경우에만 실행되기 때문에 리플로우와 리페인트가 반드시 순차적으로 실행되는것은 아닙니다. 레이아웃의 변화가 없다면 리페인트만 실행됩니다.
* script 코드(<script ></script>) 의 경우 헤드(<head></head>)안에 있는 경우 DOM이 완성되지 않은 상태에서 자바스크립트의 코드가 DOM Api를 통해 DOM을 조작하면 에러가 발생할 수 있고, 자바스크립트의 로딩/파싱/실행으로 인하여 페이지의 로딩시간이 길어질 수 있기 때문에 script 코드는 DOM이 모두 생성된 후 파싱/실행될 수 있도록 아래와 같이 바디(<body></body>)의 가장 하단에 적어주면 DOM 조작시 오류가 없고, HTML 파싱이 블로킹 없이 실행되어 페이지 로딩시간이 단축됩니다.
💡
*CSSOM(CSS Object MOdel)
- JavaScript에서 CSS를 조작할 수 있는 API 입니다. HTML 대신 CSS가 대상인 DOM이라고 생각할 수 있으며, 사용자가 CSS 스타일을 동적으로 읽고 수정할 수 있는 방법입니다.
*파싱(parsing)
- 구문 분석하는것을 의미
- 브라우저 렌더링에서의 파싱은 문자열을 의미 있는 토큰(token)으로 분해하고, 문법적 의미와 구조를 반영하여 트리형태의 자료구조인 파스 트리(parse tree)를 만드는 과정을 말합니다.
*AST(Abstract Syntax Tree), ST(Syntax Tree)
- 추상 구문 트리
- 이 트리의 각 노드는 소스 코드에서 발생되는 구조를 나타냅니다.
- 구문이 추상적이라는 의미는 실제 구문에서 나타나는 모든 세세한 정보를 나타내지는 않는다는것을 의미합니다.
Virtual DOM
Virtual DOM(VDOM)은 UI의 이상적인 또는 "가상"적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 "실제" DOM과 동기화 하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.
React에게 원하는 UI의 상태를 알려주면 DOM이 그 상태와 일치하도록 합니다. 이러한 방식은 앱 구축에 사용해야 하는 애트리뷰트 조작, 이벤트 처리, 수동 DOM 업데이트를 추상화합니다.
"Virtual DOM"은 특정 기술이라기보다는 패턴에 가깝기 때문에 사람들마다 의미하는 바가 다릅니다. React의 세계에서 "Virtual DOM"이라는 용어는 보통 사용자 인터페이스를 나타내는 객채에기 때문에 React elements와 연관됩니다.
왜 쓸까?
- DOM object와 같은 속성들을 가지고 있지만 실제 DOM이 가지고 있는 API는 가지고 있지 않습니다.
- Virtual DOM은 html객채에 기반한 자바스크립트 객체로 표현할 수 있습니다.
- 이러한 처리는 실제 DOM이 아닌 메모리 상에서 동작하기 때문에 훨씬 더 빠르게 동작합니다.
- Virtual DOM tree는 실제 렌더링이 되지 않기 때문에 연산 비용이 적습니다.
- 요소가 30개가 바뀌었다고 레이아웃을 30개가 바뀌는것이 아니고 모든 변화를 하나로 묶어서 딱 한번만 실행시킵니다.
- 이렇게 연산횟수를 줄일 수 있기 때문에 실제 DOM 리렌더링에 비해 효율적입니다.
- 변경 사항을 DOM에 직접 수정하는 것이 아니라 중간 단계로 Virtual DOM을 수정하고 Virtual DOM을 통해서 DOM을 수정하게 했습니다.
- 실질적인 방법은 Virtual DOM에 변경 내역을 한 번에 모으고(버퍼링) 실제 DOM과 변경된 Virtual DOM의 차이를 판단한 후, 구성 요소의 변경 부분만 찾아 DOM fragment에 묶어서 그에 따른 렌더링을 한 번만 하는 것으로 해결하였습니다.
- Virtual DOM은 메모리에 존재하기 때문에 DOM에 준하는 무거운 객체(Virtual DOM)가 메모리에 상주하고 있기 때문에 메모리의 사용이 많이 늘어날 수 밖에 없다.
- Virtual DOM을 조작하는 것도 엄청나게 많은 컴포넌트를 조작하게 된다면 오버헤드가 생기기 마련입니다. Virtual DOM 제어가 DOM 직접 제어에 비해 상대적으로 비용이 적게 들 뿐입니다.
간단 실습 Web(browser) with React + SPA
모바일 브라우저로 웹을 표시하는 경우에 웹앱이라 하고, 안드로이드, IOS로 패키징 하는 경우 하이브리드 앱이라 할 수 있으나
하이브리드 앱이라 하지만 어떠한 인터랙션도 하지 않고, 네이티브 동작이 거의 없기 때문에 웹앱이라 하였습니다.
우선 VSCode를 엽니다.
터미널을 열고 react-training 디렉터리를 생성하고 이동합니다.
node 설치합니다.
$ brew install node
node 설치 확인합니다.
$ node -v
설치가 되었으면 리액트 앱을 생성합니다.
$ npx create-react-app react-web
만일 npx가 없다면 설치 해 진행합니다.
$ npm install npx -g
Happy hacking! 설치가 완료되었습니다.
VSCode에서 생성한 리액트앱(프로젝트) 볼 수 있도록 합니다. 저의 경우 파인더 열어서 폴더 끌어다 VSCode에 올려다 놓습니다.
이렇게 되면 준비 완료입니다.
시작점이 되는 index.js로 이동합니다.
ReactDOM.render()는 React Element인 element와 DOM element인 container(id가 'root'인, index.html에 있음)가 인자로 받아서 Container의 SubTree로 React Element를 Render합니다.
실행하여 정상적으로 설치되었고, 구동 확인합니다.
$ npm start
SPA(Single Page Application)
- 서버로부터 완전한 새로운 페이지를 불러오지 않고 현재의 페이지를 동적으로 다시 작성함으로써 사용자와 소통하는 웹 애플리케이션 또는 웹사이트
- 동적으로 다시 작성함으로써 연속되는 페이지들 간의 사용자 경험의 간섭을 막아주고, 애플리케이션이 더 데스크톱 애플리케이션처럼 동작하도록 만들어줌
react-router-dom
- 웹 애플리케이션에서 React Router를 사용하기 위한 바인딩이 포함되어있음
리액트 라우터를 사용하기 위하여 패키지 설치합니다.
$ npm install react-router-dom
각 디렉터리, 파일 생성합니다.
Login.js
import React from "react";
import { Link } from "react-router-dom";
const Login = () => {
return (
<div>
<div>Login</div>
<Link to="/home">로그인 하기</Link>
</div>
);
};
export default Login;
Home.js
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
return (
<div>
<div>Home</div>
<Link to="/login">로그아웃</Link>
</div>
);
};
export default Home;
Router.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/home/Home";
import Login from "../pages/login/Login";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/home" element={<Home />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
App.js
import React from "react";
import Router from "../src/router/Router";
const App = () => {
return <Router />;
};
export default App;
기본 화면이 나오고 링크 클릭시 이동합니다. 이미 브라우저에 페이지들이 내려왔기 때문에 새로고침은 발생하지 않습니다.(요청은 갈 수 있으나 비교하여 판단) Virtual DOM의 장점입니다.