React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌, 컴포넌트 단위로 시작하는 것입니다.
상향식(bottom-up)으로 앱을 만듭니다. 가장 큰 장점은 테스트가 쉽고 확장성이 좋습니다.(컴포넌트 계층 구조로 나누는 것이 가장 우선순위가 됩니다.)
컴포넌트는 트리 구조입니다.
컴포넌트 외부에서 props를 이용해 데이터를 인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있습니다.
데이터를 전달하는 주체는 부모 컴포넌트가 됩니다. 이때 데이터의 흐름은 하향식(top-down)을 의미합니다.
단방향 데이터 흐름(one-way data flow)는 React를 대표하는 설명 중 하나입니다.
변하는 값과, 변하지 않는 값이 있습니다. 사용자 입력에 따라 이벤트가 얼마든지 변할 수 있습니다. 이것을 상태(state)라고 합니다.
모든 데이터를 상태로 둘 필요는 없습니다. 상태는 최소화하는 것이 가장 좋습니다. 상태가 많아질수록 애플리케이션은 무겁고, 복잡해집니다.
하나의 상태를 두 컴포넌트가 영향을 받는다면 공통 소유 컴포넌트를 찾아 그 곳에 상태를 위치해야 합니다.
(두 개의 자식 컴포넌트가 하나의 상태에 접근하고자 할 때는 두 자식의 공통 부모 컴포넌트에 상태를 위치해야 합니다.)
하위 컴포넌트가 부모 컴포넌트에 영향을 주는 경우도 있습니다.
클릭 이벤트 발생 후 부모의 상태를 바꿔야하는 상황이라면 State 끌어올리기 (Lifting state up)을 통해
변경할 수 있습니다.
상태를 변경시키는 함수(handler)를 하위 컴포넌트에 props로 전달해 해결할 수 있습니다.
단방향 데이터 흐름이라는 원칙에 따라 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지 알 수 있습니다.
(상위 컴포넌트는 상태를 변경하는 함수를 하위 컴포넌트에 전달하고, 함수를 하위 컴포넌트가 실행합니다. 그럼 상태가 끌어올려집니다.)
함수 내에서의 구현이 함수 외부에 영향을 끼치는 경우 Side Effect가 있다고 이야기합니다.
ex)
let study = 'hello';
function hard() {
study = 'world';
}
hard(); // hard는 Side Effect를 발생시킵니다!
오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미합니다. 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우, 순수 함수라고 부를 수 없습니다. 또한 순수 함수는, 입력으로 전달된 값을 수정하지 않습니다.
ex)
function upper(str) {
return str.toUpperCase(); // toUpperCase
}
upper('hello') // 'HELLO'
React 애플리케이션을 작성할 때에는, AJAX 요청이 필요하거나, LocalStorage 또는 타이머와 같은 React와 상관없는 API를 사용하는 경우가 발생할 수 있습니다. 이는 React의 입장에서 전부 Side Effect 입니다. React Side Effect는 Effect Hook을 사용합니다.
useEffect는 컴포넌트 내에서 Side Effect를 실행할 수 있게 하는 Hook 입니다.
useEffect의 첫번째 인자는 함수입니다.
실행 조건
useEffect의 두 번 째 인자는 배열입니다. 배열은 조건을 담고 있고, 어떤 값의 변경이 일어날 때를 뜻합니다.
목록 내 필터링을 구현하기 위해서는 두가지 접근이 있습니다.
처음 단 한 번, 외부 API로 목록을 받아오고, filter 함수를 이용합니다.
검색어가 바뀔 때 마다, 외부 API를 호출합니다.
컴포넌트 내부에서 필터링은 HTTP 요청 빈도를 줄일 수 있지만, 브라우저 메모리 상 많은 데이터를 갖게 됨(클라이언트 부담)
컴포넌트 외부에서 필터링은 클라이언트가 필터링 구현을 생각하지 않아도 됩니다. 하지만 HTTP 요청 빈도가 늘어나 서버에 부담이 됩니다.
ex) CodeStates 참고 자료
useEffect(() => {
fetch(`http://서버주소/proverbs?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setProverbs(result);
});
}, [filter]);
fetch API를 사용해, 서버에 요청한다면? 목록을 제공하는 엔드포인트가 http://서버주소/proverbs 라고 가정했을 때 위와 같이 요청하면 된다.
모든 네트워크 요청이 즉각적인건 아니기 때문에 외부 API 접속이 느릴 경우 로딩 화면(loading indicator)의 구현은 필수적입니다.
로딩 화면 구현에도 상태 처리가 필요합니다.
ex)
const [isLoading, setIsLoading] = useState(true);
return {isLoading ? <LoadingIndicator/> : <div>로딩 완료 화면</div>
로딩 화면 구현은 fetch 전후에 설정해주면 훌륭한 UX를 구현할 수 있습니다.
useEffect(() => {
setIsLoading(true);
fetch(`http://서버주소/proverbs?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setProverbs(result);
setIsLoading(false);
});
}, [filter]);