리액트 배우면서 멘붕이 왔다. 멘붕이 어서오고.
너무 많은 내용을 한꺼번에 흡수하려다 보니 체한듯 싶다. 천천히 다시 곱씹으면서 소화시키고 간다.
웹이 복잡해지면서 더 많은 상호작용을 더 효율적으로 처리하기 위해 SPA의 방식이 도입되었다. SPA방식을 사용하는 새로운 기술은 크게 3가지가 있는데, Angular, Vue, 그리고 React가 그것들이다. 그중에서 우리는 React를 공부할 것이다. 그 이유로는, 많은 회사에서 React를 이용하여 개발을 하고 있고, 그 만큼 커다란 커뮤니티가 존재하여, 개발중에 발생한 문제에 대해서 도움을 받기가 수월. 따라서, 초보 개발자의 입장에서 첫 걸음을 떼는 것으로 리액트 만한 것이 없다.
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
-리액트 Docs
리액트는 문서를 컴포넌트의 트리로 구성한다. 컴포넌트란 재사용 가능한 UI단위를 말한다.
각 컴포넌트는 UI 특정 부분을 담고 있고, 이러한 컴포넌트들이 계층구조를 이루어 문서를 구성한다. 따라서, 필연적으로 가장 높고 모든 하위 컴포넌트를 포함하고 있는 컴포넌트가 있고, 그 안에 포함된 하위 컴포넌트들이 존재한다. 이것에서 유추할 수 있듯, 정보의 전달 방향이 항상 위에서 아래를 향한다.
리액트는 상호작용이 일어났을때 모든 문서를 재렌더링하지 않는다. 대신 가상 DOM이라는 기술을 이용하여 변화가 생기는 부분을 감지하여, 그 부분만 실제 DOM에 반영시킨다.
가상 DOM이란, 간단하게 말하자면 실제 DOM의 간소화된 카피이다. 리액트에서는 상호작용이 일어나면, 가상 DOM에 먼저 전체 UI를 렌더링한다. 그 후 변화가 생긴 부분을 감지한 뒤, 실제 DOM에서 해당 부분만 재렌더링을 해준다. 이를 통해, 아주 작은 부분만 변화가 생겼을 경우에도 전체를 재렌더링 하던 기존의 방식에 비해 많은 컴퓨터 자원을 아낄 수 있다.
자바스크립트의 확장 문법이다. 단순하게 말하자면, JS + HTML 이다.
위에서 말했듯이, 리액트는 UI를 컴포넌트 단위로 나누어 렌더링한다. 즉, 컴포넌트는 UI를 담고 있다고 볼 수 있다. UI를 담는 다는 것은 곧, HTML요소들을 담는다는 것을 의미한다. HTML를 담기 위해선, 자바스크립트가 HTML 또한 이해할 수 있어야 한다. HTML을 이해하는 JS. 이것을 가능하게 한 확장 문법이 JSX이다.
예로,
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
위와 같이 자바스크립트와 HTML이 함께 쓰인다.
컴포넌트는 JSX를 반환한다. 컴포넌트는 함수형, 클래스형으로 나뉜다.
// 함수형 컴포넌트
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 클래스형 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
둘의 차이는, 크게 state와 Life Cycle Event의 사용 유무로 나눌수 있다. state란(나중에 더 자세히 다루겠지만) 문서에서 변하는 값들을 저장하는 일종의 저장소와 같은 것을 의미하고, Life Cycle Event란 리액트에서 컴포넌트가 렌더링 되는 과정에서 실행되는 일련의 메소드를 의미한다. 클래스형 컴포넌트의 경우 state와 Life Cycle Event를 사용할 수 있는 반면, 함수형 컴포넌트는 사용할 수 없다.
그 외에도, 클래스형 컴포넌트는 렌더링 과정에서 로직을 짜는데 사용하는 임의의 함수를 정의해 줄 수 있지만 함수형 컴포넌트는 그렇지 못하다. 하지만, 그렇기 때문에, 함수형 컴포넌트가 메모리를 덜 사용하기 때문에, 클래스형 컴포넌트에 비해 가볍다.
리액트에서는 컴포넌트가 렌더링되는 과정에서 단순하게 렌더링이 되는 것이 아니라, 렌더링 과정에서 일련의 함수들이 차례로 실행된다. 그러한 함수들을 통해 컴포넌트에 사용되는 데이터들을 업데이트하고 그에 맞춰 컴포넌트를 재렌더링할 수 있다. 주로 쓰이는 대표적 함수로, componentDidMount
, componentDidUpdate
, componentWillUnmount
가 있다.
위의 그림이 컴포넌트 생명주기 과정을 잘 요약한다. 그림을 설명을 조금 하자면, 처음 컴포넌트가 실행될 때, 먼저 constructor
가 실행되어 state
를 저장한다. 그 후, 초기 state
에 따라서 render
함수를 통해 렌더링이 되고, 렌더링 완료 후, componentDidMount
함수가 호출된다. 보통 componentDidMount
에서 데이터를 불러오는 등의 행동이 취해진다. 왜냐하면 componentDidMount
는 최초에 한번만 실행되기 때문. (한번만 실행되는 걸 모르고, componentDidMount
에 setState
가 실행되었는데 왜 무한 루프에 안빠지냐고 동기분한테 따진건 안비밀)
처음 렌더링이 완료된 후, setState
를 통해 state
가 변경되었을 때, 다시 한번 render
함수가 호출되어 새로운 화면이 렌더링이 된다. 그후, componentDidUpdate
가 실행된다. (여기서 setState
를 쓸 경우 무한 루프에 빠지는거지.)
컴포넌트가 사용되지 않아 제거될 때, componentDidUpdate
가 호출된다.
CRA는 리액트는 js 라이브러리로써 프레임워크와는 다르게 처음부터 모든 것을 직접 만들어 나가야 한다. 초기 셋팅은 WebPack, Babel, ESLint 와 같은 것들을 설정하는 것 또한 포함하는데, 나와 같이 개발에 이제 막 발을 딛인 사람들에게는 그렇게 프로젝트를 시작하는 것은 쉽지 않다. 그래서 누구나 리액트 프로젝트를 쉽게 시작할 수 있도록 어느 정도의 보일러 플레이트를 제공하는 것이 create-react-app
패키지이다.
npx install create-react-app <project_name>
을 통해 프로젝트를 시작한다. 그러면 처음 생성되는 파일과 폴더가 있는데, 아래와 같다.
여기서 CRA의 구조를 이해하는데 중요한 파일이 2가지가 있는데, index.html
과 index.js
가 그것.
결과적으로 렌더링 되어 화면에 나타나는 HTML파일이다. 다른 폴더를 다 뒤져봐도 HTML파일은 index.html
이거 하나 밖에 없다. 이 한개의 HTML 파일을 통해 모든 페이지가 나타나기 때문. (그래서 SPA이다.)
<body>
<div id="root"></div>
</body>
id
가 root
인 div
태그 하나로만 이루어져있고, 프로젝트 내내 전혀 건드릴 일이 없는 파일. 어떻게 이 HTML 파일 하나만으로 여러 웹페이지가 렌더링 되는지는 index.js
를 보면 알수 있다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
어떻게 HTML파일 하나에 모든 페이지들이 담길 수 있는 지 그 방법이 나타난다. react-dom
패키지에서 render
함수를 사용하는데, 이 함수는 index.html
파일에서 root
id를 가진 태그를 찾아, 그곳에 App
컴포넌트를 렌더링 한다. 보통 App
컴포넌트는 또 다시 여러가지의 하위 컴포넌트로 구성된다. 사용자의 상호작용에 맞게 여러 컴포넌트를 생성 또는 수정하여 웹페이지를 하고, 그 모든 것이 App
이라는 최상위 컴포넌트에 연결되어(이 예제에서는 App
이 최상위로 왔지만, 보통은 Router
가 최상위에 위치), 결과적으로 하나의 HTML파일에 모두 렌더링 된다.
쓰고 나니 생각 보다 엄청 많은 것을 배운 건 아닌듯 싶다. 멘붕이 올 정도는 아닌 것 같은데, 처음 공부하는 것이라 스트레스를 크게 받았나보다. 조급해 하지 말고 천천히 하나하나씩 그리고 꾸준히 해나가자.