
프로그래밍 과정을 나만의 언어로 표현할 수 있도록 노력하자!
왜 React를 사용하게 되었을까? 웹이 발전하면서 어째서 React가 생겨나게 되었고 인기를 얻게 되었는지 생각해보자.
초기 웹
정적인 페이지의 향연. 인터넷은 정적인 정보 공유를 위한 곳.
웹 2.0
웹이 보급이 되면서 사용자들이 참여할 수 있는 기능이 생겨남. 블로그, 싸이월드, 게시판 등등의 전성시대.
노드의 등장 : 구글의 V8엔진에 팔다리를 붙여 마개조를 한 node.js가 탄생했다! 브라우저 바깥에서도 JS를 실행하고 디버깅이 가능해져 개발의 난이도가 쉬워지고 프론트뿐만 아니라 백엔드에서도 사용 가능해지는 등 활용도가 급격히 늘어나게 되었다.
모던 웹(2010년대 이후)
모바일 인터넷 사용 급증하면서 반응형 웹 디자인의 중요성이 증가했고, 웹에서 앱처럼 동작하는 SPA가 등장하게 된다.
리액트의 탄생 (2013년)
프론트엔드 프레임워크는 이전부터 존재해왔지만, 프로젝트 규모가 커지면서 브라우저 안에 여러 요소들을 표시하는 것이 너무 느려지게 되었다. 페이스북에서는 JS만을 위한 가상 돔을 만들어서 이를 이용해 필요한 부분만 렌더링을 하면 어떨까? 라는 생각에 도달하게 되었고, 이렇게 리액트가 탄생했다. 예전의 렌더링 복잡도와 이슈를 해결하고 사용자는 상태 변화에만 집중하도록 만들어주었다.
🤪트럭 지수 Truck Factor
버스 팩터, 복권 팩터 등 여러 이름으로 불리는 이 용어는 팀원 중 몇명이 제대로 된 인수인계 등의 절차 없이 갑작스럽게 빠지게 되었을 때 프로젝트에 심각한 문제가 생기는지를 나타내는 지표다. 몇명의 직원이 갑자기 트럭에 치여서 회사에 못 나오게 되면 프로젝트가 망할까(?) 에서 나온 말이다.트럭 지수가 높을수록 프로젝트의 안전성이 높아진다.
트럭 지수가 1이면 한 명만 빠져도 프로젝트에 문제가 생기지만
트럭 지수가 100이면 100명이 빠져야 프로젝트에 문제가 생긴다.
React로 화면을 구성하고자 할 때, 먼저 이를 컴포넌트로 나눈다. 그리고 각 컴포넌트의 다양한 시각적 상태들을 정의한다. 마지막으로 컴포넌트들을 연결하여 데이터가 그 사이를 흘러가게 한다. 이를 단계별로 나누어 살펴보자!
UI를 기능에 따라 여러 컴포넌트로 나누고, 이들의 부모-자식 관계를 생각해봐야 한다. 여기엔 정답이 없다! 다만 기능을 살펴보고 어떻게 나누는 것이 효율적일지 고민해봐야 한다.

위 그림의 경우, UI를 다섯 컴포넌트로 나누었다. 저기에서 더 쪼갤지 덜 쪼갤지는 본인의 판단이다. 이 컴포넌트들의 상하 관계를 나누어보면 다음과 같다.
상호작용 기능이 없는 UI만 우선 만들어본다. props를 적절히 활용해야한다. 다만 이 과정에서는 state를 쓰지 말자! state는 상호작용을 위해 아껴두자.
일반적으로는 하향식(top-down)으로 컴포넌트를 만들지만, 큰 프로젝트의 경우 상향식(bottom-up)으로 만들면서 테스트를 작성하면서 개발하기도 한다. 상향식의 경우 만든 컴포넌트가 상위 컴포넌트가 없더라도 에러 없이 동작하기 때문이다. (물론 props를 받지 못하지만, 컴포넌트는 기본적으로 함수이기에 props는 파라미터일 뿐이므로 에러를 일으키지 않는다.)
state 결정하기state는 앱이 기억해야 하면서도 변경할 수 있는 데이터의 최소 집합이다. 이외의 데이터는 필요에 따라 실시간으로 계산해야한다.
state를 결정하는 3가지 질문 :
state가 아니군.props를 통해 전달된다? 음.. 확실히 state가 아니군.state 또는 props를 가지고 계산이 가능한가? 오, 이건 절대로 state가 아니군이외의 것들은 아마 state일 것이다.
state를 최소화하는 이유왜 상태를 최소화하는 것이 좋을까? 여러 이유가 있겠지만, 가장 중요한 이유는
상태의 복잡성 관리와 일관성 유지를 통해 상태를 효율적으로 관리하고 유지보수를 용이하게 만들기 위해서다.
여러 상태가 서로 의존하거나 중복될 경우, 이를 동기화하는 로직이 복잡해진다. 동일한 데이터를 두 곳의 상태로 관리하면 한 곳에서 상태를 업데이트했을 때 다른 곳도 함께 업데이트해야 하기 때문이다.
불필요한 상태가 많을수록 상태 간의 관계를 이해하기 어려워지고, 예상치 못한 버그가 발생할 가능성이 높아지게 된다. 그래서 계산이 가능한 데이터의 경우 상태로 관리하지 않는다!
state위치 결정하기최소한의 state를 결정했다면 이제 어떤 컴포넌트가 이 state를 책임질지 정해야한다. React는 위에서 아래로 내려가는 단방향 데이터 바인딩을 사용하기 때문에 일반적으로 state는 공통 부모 컴포넌트, 또는 공통 부모의 상위 컴포넌트에 둔다. 적절한 컴포넌트를 찾지 못하겠다면 아예 state 전용의 컴포넌트를 만들어서 상위 계층에 추가하면 된다.
지금까지의 단계는 데이터가 아래로만 흐르도록 만들었다면, 마지막 단계는 반대 방향의 데이터 흐름을 만드는 것이다. 하위 컴포넌트에서 공통 부모 컴포넌트에 있는 state를 변경해볼 것이다!
React는 양방향 데이터 바인딩을 지원하지 않는다. 그래서 React에서 state를 변경하기 위해서는 setState를 사용해야 한다.
state를 변경한다고 해서 state를 조작하는 것이 아니다. state는 직접 변경할 수 없으며, 불변성을 유지하는 것이 React의 권장 사항이기에 React의 철학에 따라 읽기 전용으로 취급해야 한다.
state를 변경한다는 것은 setState를 통해 기존 상태를 교체하거나 병합하여 새로운 상태를 저장하는 것이다.
setState로 바뀐 값은 다음 렌더링 때 바뀐다. 그 전엔 변하지 않는다.
...
console.log(state)
setState(변경된state)
console.log(state)
...
두 출력 값은 같다! 아직 다음 렌더링이 발생하지 않았기 때문이다.
리액트를 사용한 프로젝트를 어떻게 만들어야 하는지에 대한 전체적인 흐름을 파악하면 앞으로의 개발에 많은 힘이 될 것이다. 프로젝트 시작하기 전에 전체적인 컴포넌트 구조를 어떻게 만들지와 데이터를 어떻게 써야할지 구상을 먼저 해보도록 하자!
현재 리액트의 트렌드인 함수형 컴포넌트를 사용할 때 함수적으로 생각하는 것이 중요하다. 결국 컴포넌트와 훅은 함수다.
// App.jsx
import { Child } from './Child'
function App() {
return (
<div>
<Child/>
</div>
);
}
// Child.jsx
function Child() {
return (
<p>Hello!</p>
);
}
App.jsx의 <Child/>에는 Child컴포넌트의 결과값(jsx)이 들어간다. 어떻게 보면 Child()의 반환값이 곧 <Child/>인 것이다.
함수 이름
파라미터
반환값
간단한 3개의 요소지만, 원함수와 콜백함수가 섞여있을 경우 반환값이 어떤 함수의 반환값인지 햇갈릴 때가 있다. 어떤 함수가 실행되고, 어떤 함수가 실행되지 않는지 잘 판단해야한다.