Daily Front_Minhhk

[React] 클라이언트 AJAX,, useEffect( ) 본문

Code개발일지

[React] 클라이언트 AJAX,, useEffect( )

Minhhk 2022. 12. 7. 16:36

개념학습) React 데이터 흐름

  • 리액트의 데이터는 아래로 흐른다.
  • 일반적으로 이를 "하향식(top-down)" 또는 "단방향식" 데이터 흐름이라고 한다. 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 "아래"에 있는 컴포넌트에만 영향을 미친다.
  • 따라서 부모 컴포넌트나 자식 컴포넌트 모두 다른 컴포넌트에 대해서는 관심을 가질 필요가 없다.
  • state는 종종 로컬 또는 캡슐화로 불리며 state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다.
  • 리액트 앱에서 컴포넌트가 유상태 또는 무상태에 대한 것은 시간이 지남에 따라 변경될 수 있는 구현 세부 사항으로 간주한다. 따라서 유상태 컴포넌트 안에서 무상태 컴포넌트를 사용할 수 있으며, 그 반대 경우도 마찬가지로 사용할 수 있다.
  • 데이터를 전달하는 주체는 부모 컴포넌트가 되어야 한다. 단방향 데이터 흐름은 아래에서 위로는 전달할 수 없다.
  • 모든 데이터를 상태(state)로 둘 필요는 없으며 상태는 최소화하는 것이 좋다.
    • 부모로부터 props를 통해 데이터가 전달된다면 state가 아니다.
    • 시간이 지나도 변하지 않으면 state가 아니다.
    • 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능하다면 state가 아니다.

개념학습) State 끌어올리기 (Lifting State Up)

  • 하위 컴포넌트에서 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것을 state 끌어올리기로 볼 수 있다.
  • 리액트에서는 상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다라고 설명한다.

Side Effect (부수 효과)

  • 함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 이야기합니다.
  • React에서는 컴포넌트 내에서 fetch를 사용해 API 정보를 가져오거나 이벤트를 활용해 DOM 직접 조작할 때 Side Effect가 발생했다고 말합니다.
  • 순수 함수의 출력값에 영향을 미치는 작업들이 Side Effect

다음은, 전역 변수 foo를 bar라는 함수가 수정하는 예제

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar(); // bar는 Side Effect를 발생시킵니다!

Pure Function (순수 함수)

  • 순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미
  • 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우, 순수 함수라고 부를 수 없다.
  • 또한 순수 함수는, 입력으로 전달된 값을 수정하지 않습니다.
function upper(str) {
  return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않습니다 (Immutable)
}

upper('hello') // 'HELLO'

순수 함수에는 네트워크 요청과 같은 Side Effect가 없다.

순수 함수의 특징 중 하나는, 어떠한 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장합니다.

그래서 예측 가능한 함수이기도 합니다.

React의 함수 컴포넌트

우리가 앞서 배운 React의 함수 컴포넌트는, props가 입력으로, JSX Element가 출력으로 나갑니다.

여기에는 그 어떤 Side Effect도 없으며, 순수 함수로 작동

function SingleTweet({ writer, body, createdAt }) {
  return <div>
    <div>{writer}</div>
    <div>{createdAt}</div>
    <div>{body}</div>
  </div>}

하지만 보통 React 애플리케이션을 작성할 때에는,

AJAX 요청이 필요하거나, LocalStorage 또는 타이머와 같은 React와 상관없는 API를 사용하는 경우가 발생할 수 있습니다.

이는 React의 입장에서는 전부 Side Effect 입니다.

React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공합니다.

React 컴포넌트에서의 Side Effect

  • 타이머 사용 (setTimeout)
  • 데이터 가져오기 (fetch API, localStorage)

질문

  • Math.random()은 순수 함수가 아닙니다. 왜 일까요?
    • Math.random은 어떤 결과값이 나올지 예측이 불가능하기 때문에 순수 함수가 아니다.
  • 어떤 함수가 fetch API를 이용해 AJAX 요청을 한다고 가정해봅시다. 이 함수는 순수 함수가 아닙니다. 왜일까요?
    • 네트워크 상황, 서버상태에 따라 응답코드가 달라지기 때문에 예측이 불가능하다.
    ⇒순수 함수의 출력값에 영향을 미치는 작업들이 바로 Side Effect라고 할 수 있겠습니다.⇒ 순수 함수(Pure Function)는 항상 동일한 기능을 하며, 예측 가능한 함수이다.
  • 앞으로 여러분의 코딩에 있어서 Side Effect를 최소화하거나 따로 분리하여 함수로 묶어주는 작업은 해당 프로젝트나 소프트웨어의 유지보수를 좀 더 수월하게 해줍니다.
  • 함수 내부에서 외부의 값에 관여하는 경우 Side Effect가 있다고 합니다.

API

useEffect(함수)

  • useEffect의 첫 번째 인자는 함수이다. 해당 함수 내에서 side effect를 실행하면 된다.
  • 이 함수는 다음과 같은 조건에서 실행된다.
    • 컴포넌트 생성 후 처음 화면에 렌더링(표시)
    • 컴포넌트에 새로운 props가 전달되며 렌더링
    • 컴포넌트에 상태(state)가 바뀌며 렌더링

이와 같이 매번 새롭게 컴포넌트가 렌더링 될 때 Effect Hook이 실행된다.

Hook을 쓸 때 주의할 점

  • 최상위에서만 Hook을 호출한다.
  • 리액트 함수 내에서 Hook을 호출한다.
useEffect(() => {
	console.log(몇 번 호출될까요?)
})

//컴포넌트가 처음 생성되거나, props가 업데이트 되거나, state가 업데이트 될 때마다 실행됩니다.
useEffect(() => {
	console.log(몇 번 호출될까요?)
},[])

// 컴포넌트가 처음 생성될 때만 함수가 실행됩니다.
useEffect(() => {
	console.log(몇 번 호출될까요?)
},[dep])

// dep이 업데이트 될 때마다 실행됩니다.

빈 배열[ ]을 useEffect의 두 번째 인자로 사용하면,

이때에는 컴포넌트가 처음 생성될 때만 effect 함수가 실행됩니다.

이것이 언제 필요할까요?

대표적으로 처음 단 한 번, 외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 때에 사용할 수 있습니다.

 

 

Data Fetching

목록 내 필터링을 구현하기 위해서는 다음과 같은 두 가지 접근이 있을 수 있습니다.

  1. 컴포넌트 내에서 필터링: 전체 목록 데이터를 불러오고, 목록을 검색어로 filter 하는 방법
  2. 컴포넌트 외부에서 필터링: 컴포넌트 외부로 API 요청을 할 때, 필터링 한 결과를 받아오는 방법 (보통, 서버에 매번 검색어와 함께 요청하는 경우가 이에 해당합니다)

장단점

제목 장점 단점

컴포넌트 내부에서 처리 HTTP 요청의 빈도를 줄일 수 있다 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트의 부담이 늘어난다
컴포넌트 외부에서 처리 클라이언트가 필터링 구현을 생각하지 않아도 된다 빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담을 가져간다

AJAX 요청 보내는 법

예시

useEffect(() => {
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
    });
}, [filter]);

AJAX 요청이 매우 느릴 경우?

  • 웹 앱을 구성할 때, 서버로의 네트워크 요청을 보내야 되는 경우가 있다. 리액트에서는 이러한 Ajax 요청을 처리할 때, Side Effect를 최소화하기 위해서 Effect Hook을 사용한다. 만약 훅을 사용하지 않고 네트워크 요청을 하면 그 동안에 페이지가 멈추거나 깜빡일 수 있다.
  • 모든 네트워크 요청이 항상 즉각적인 응답을 가져다주는 것은 아니다. 외부 API 접속이 느릴 경우를 고려하여, 로딩 화면(loading indicator)의 구현은 필수적이다.
  • loading indicator의 구현도 상태 처리가 필요하다.
const [isLoading, setIsLoading] = useState(true);

// 생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정합니다
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}
  • fetch 요청의 전후로 setIsLoading을 설정해 주어 보다 나은 UX를 구현할 수 있다.
useEffect(() => {
  setIsLoading(true);
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
      setIsLoading(false);
    });
}, [filter]);

웹 앱을 구성할 때, 서버로의 네트워크 요청을 보내야 되는 경우가 있습니다.

React에서는 이러한 Ajax 요청을 처리할 때, Side Effect를 최소화하기 위해서

Effect Hook을 사용합니다.

만약 훅을 사용하지 않고 네트워크 요청을 하면 그 동안에 페이지가 멈추거나 깜빡일 수 있습니다.

'Code개발일지' 카테고리의 다른 글

Web Server (SOP, CORS)  (0) 2022.12.08
[REACT] StatesAirline Server  (0) 2022.12.08
postman (OpenWeather API)  (0) 2022.12.05
REST API  (0) 2022.12.02
HTTP / 네트워크  (0) 2022.11.30