코드스테이츠 프론트엔드 부트캠프 - Day 34~35

정우시·2022년 8월 9일
1

2. 코드스테이츠

목록 보기
31/52

[SEB FE] Section 2

Unit9 - [React] 클라이언트 Ajax 요청

  • React 데이터 흐름
  • Effect Hook

React 데이터 흐름

학습목표

  • 리액트에서 데이터 흐름, 특히 단방향 데이터 흐름을 이해할 수 있다.
  • 어떤 컴포넌트에 state가 위치해야 하는지 알 수 있다.
  • State 끌어올리기의 개념을 이해할 수 있다.
    • 상태 변경 함수가 정의된 컴포넌트와, 상태 변경 함수를 호출하는 컴포넌트가 다름을 알 수 있다.

개념학습) React 데이터 흐름

  • 리액트의 데이터는 아래로 흐른다.

  • 일반적으로 이를 "하향식(top-down)" 또는 "단방향식" 데이터 흐름이라고 한다. 모든 state는 항상 특정한 컴포넌트가 소유하고 있으며 그 state로부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 "아래"에 있는 컴포넌트에만 영향을 미친다.

  • 따라서 부모 컴포넌트나 자식 컴포넌트 모두 다른 컴포넌트에 대해서는 관심을 가질 필요가 없다.

  • state는 종종 로컬 또는 캡슐화로 불리며 state가 소유하고 설정한 컴포넌트 이외에는 어떠한 컴포넌트에도 접근할 수 없다.

  • 리액트 앱에서 컴포넌트가 유상태 또는 무상태에 대한 것은 시간이 지남에 따라 변경될 수 있는 구현 세부 사항으로 간주한다. 따라서 유상태 컴포넌트 안에서 무상태 컴포넌트를 사용할 수 있으며, 그 반대 경우도 마찬가지로 사용할 수 있다.

  • 데이터를 전달하는 주체는 부모 컴포넌트가 되어야 한다. 단방향 데이터 흐름은 아래에서 위로는 전달할 수 없다.

  • 모든 데이터를 상태(state)로 둘 필요는 없으며 상태는 최소화하는 것이 좋다.

    • 부모로부터 props를 통해 데이터가 전달된다면 state가 아니다.
    • 시간이 지나도 변하지 않으면 state가 아니다.
    • 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능하다면 state가 아니다.

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

  • 하위 컴포넌트에서 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것을 state 끌어올리기로 볼 수 있다.

  • 리액트에서는 상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다라고 설명한다.


    Effect Hook

    학습목표

  • Side effect가 어떤 의미인지 알 수 있다.

  • React 컴포넌트를 만들 때 side effect로부터 분리해서 생각할 수 있다. (비즈니스 로직과 표현 영역 구분)

    • Side effect의 예를 들 수 있다.
  • Effect Hook을 이용해 비동기 호출 및 AJAX 요청과 같은 side effect를 React 컴포넌트 내에서 처리할 수 있다.

  • Effect Hook에서의 dependency array 사용법을 이해할 수 있다.

  • 컴포넌트 내에서 네트워크 요청 시, 로딩 화면과 같이 보다 나은 UI를 만드는 법을 이해할 수 있다.

개념학습) Side Effect

Side Effect

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

  • 순수 함수의 출력값에 영향을 미치는 작업들이 Side Effect이다.

  • 코딩에 있어서 Side Effect를 최소화하거나 따로 분리하여 함수로 묶어주는 작업은 해당 프로젝트나 소프트웨어의 유지보수를 좀 더 수월하게 해준다.

전역 변수 foo를 bar라는 함수가 수정하는 예제
let foo = 'hello';

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

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

Pure Function (순수 함수)

  • 순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미한다. 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우, 순수 함수라고 부를 수 없다. 또한 순수 함수는, 입력으로 전달된 값을 수정하지 않는다.

  • 순수 함수에는 네트워크 요청과 같은 Side Effect가 없다. 순수 함수의 특징 중하나는, 어떤 전달인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장한다. 그래서 예측 가능한 함수이기도 하다.

  • Math.random() 같은 메서드는 동일한 입력에도 다른 출력이 나올 수 있으므로 순수 함수가 아니다.

  • fetch API를 이용해 AJAX 요청을 하는 경우도 네트워크 상황, 서버 상태에 따라 응답코드가 달라지기 때문에 예측이 불가능하다.

function upper(str) {
  return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않습니다 (Immutable)
}

upper('hello') // 'HELLO'

React의 함수 컴포넌트

  • 리액트의 함수 컴포넌트는, props가 입력으로, JSX Element가 출력으로 나간다. 여기에는 그 어떤 Side Effect도 없으며, 순수 함수로 작동한다.
function SingleTweet({ writer, body, createdAt }) {
  return <div>
    <div>{writer}</div>
    <div>{createdAt}</div>
    <div>{body}</div>
  </div>
}
  • 하지만 보통 리액트 애플리케이션을 작성할 때에는, AJAX 요청이 필요하거나, LocalStorage 또는 타이머와 같은 리액트와 상관없는 API를 사용하는 경우가 발생할 수 있다. 이는 리액트 입장에서는 전부 Side Effect이다. 리액트는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.

React 컴포넌트에서의 Side Effect

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

개념학습) Effect Hook 기본

API

useEffect(함수)

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

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

Hook을 쓸 때 주의할 점

  • 최상위에서만 Hook을 호출한다.
  • 리액트 함수 내에서 Hook을 호출한다.

예제

  • 컴포넌트가 처음 생성되거나, props가 업데이트 되거나, state가 업데이트 될 때마다 실행되는 경우
useEffect(() => {
	console.log(몇 번 호출될까요?)
})
  • 컴포넌트가 처음 생성될 때만 함수가 실행되는 경우
useEffect(() => {
	console.log(몇 번 호출될까요?)
},[])
  • dep이 업데이트 될 때마다 실행되는 경우
useEffect(() => {
	console.log(몇 번 호출될까요?)
},[dep])

개념학습) 조건부 effect 발생 (dependency array)

조건부 effect 발생 (dependency array)

  • useEffect의 두 번째 인자는 배열이다. 이 배열은 조건을 담고 있다. 여기서 조건은 불리언 형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때를 의미한다. 따라서, 해당 배열에는 어떤 값의 목록이 들어간다. 이 배열을 특별히 종속성 배열이라고 부른다.

API

useEffect(함수, [종속성1, 종속성2, ...])

  • useEffect의 두 번째 인자는 종속성 배열이다. 배열 내의 종속성1, 또는 종속성2의 값이 변할 때, 첫 번째 인자의 함수가 실행된다.

  • 배열 내의 어떤 값이 변할 때에만, (effect가 발생하는) 함수가 실행된다.

단 한 번만 실행되는 Effect 함수

  • 빈 배열 넣기 : 빈 배열을 useEffect의 두 번째 인자로 사용하면, 이때에는 컴포넌트가 처음 생성될 때만 effect 함수가 실행된다. 대표적으로 처음 단 한 번, 외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 때에 사용할 수 있다.
    • useEffect(함수, [])
  • 아무것도 넣지 않기 : 기본 형태의 useEffect는 컴포넌트가 처음 생성되거나, props가 업데이트되거나, 상태(state)가 업데이트될 때 effect 함수가 실행된다.
    • useEffect(함수)

Data Fetching

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

  • 컴포넌트 내에서 필터링
    • 전체 목록 데이터를 불러오고, 목록을 검색어로 filter 하는 방법
    • 처음 단 한 번, 외부 API로부터 명언 목록을 받아오고, filter 함수를 이용한다.
  • 컴포넌트 외부에서 필터링
    • 컴포넌트 외부로 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]);
profile
프론트엔드 공부하고 있는 정우시입니다.

0개의 댓글