728x90
8.1 데이터 요청하기
- 자바스크립트에서 HTTP 요청을 수행하는 가장 유명한 방법은 fetch이다.
- GitHub에 clghks에 대한 정보를 요청하고 싶다면 fetch 요청을 보낼 수 있다.
- fetch 함수는 프라미스를 반환한다.
- API에 비동기 요청을 보낸 이후 응답이 오기 전까지 기다린다.
- 응답 값이 도착하면 .then(callback)을 통해 callback에 정보가 전달 된다.
- GitHub API에서는 JSON 데이터로 응답하기 때문에 response.json()을 호출해서 JSON으로 파싱한다.
- 데이터를 정상적으로 받게 되면 .then(console.log)가 호출되고 오류가 발생하면 .catch(console.error)가 호출된다.
fetch(`https://api.github.com/users/clghks`) .then(response => response.json()) .then(console.log) .catch(console.error)
- 프라미스를 다루는 다른 방법으로는 async/await를 사용하는 방법이 있다.
- fetch는 프라미스를 반환하기 때문에 async 함수 안에서 fetch 요청을 await 할 수 있다.
async function requestGithubUser(githubLogin) { try { const response = await fetch(`https://api.github.com/users/clghks`) const userData = await response.json() console.log(userData) } catch (error) { console.error(error) } }
8.1.1 요청으로 데이터 보내기
- fetch 함수의 두번째 인자는 HTTP 요청을 만들 때 사용할 옵션이 담겨 있는 객체 이다.
- fetch(`/create/user`, { method: 'POST', body: JSON.stringify({ username, password, bio}) })
8.1.2 fetch로 파일 업로드하기
- 파일을 업로드하려면 multipart-formdata라는 다른 HTTP 요청을 보내야한다.
- 자바스크립트에서 만들려면 FormData 객체를 요청 본문에 넘겨야 한다.
- HTML에서 form을 사용하면 쉽게 formData를 얻을 수 있다.
const formData = new FormData() formData.append('username', 'moontahoe') formData.append('fullname', 'Alex Banks') formData.append('avatar', imageFile) fetch(`/create/user`, { method: 'POST', body: formData })
8.1.3 권한 요청
- 요청을 하기 위해 권한을 얻어야 하는 경우가 있다.
- 보통 사용자는 자신을 식별하도록 서비스가 부여한 유일한 토큰을 요청마다 덧붙여서 자기 신원을 나타낸다.
- 토큰은 일반적으로 Authorization 헤더에 추가된다.
- fetch(`https://api.github.com/users/clghks`, { method: 'GET', headers: { Authorization: `Bearer ${token}` } })
- 리액트 컴포넌트에서 외부 데이터를 가져오려면 useState와 useEffect 훅을 함께 사용해야 한다.
- useState 훅을 사용해 fetch의 응답을 상태에 저장하고 useEffect 훅을 사용해 fetch 요청을 만든다.
- GitHub에서 사용자 정보를 받아와서 출력하는 예제 코드
- GithubUser 컴포넌트를 렌더링하고 clghks 에 대한 JSON 정보를 표시한다.
- 최초로 렌더링될 때 GithubUser는 useState 훅으로 data라는 상태 변수를 준비한다.
- 처음에는 data가 null이기 때문에 컴포넌트가 null을 반환한다.
- 리엑트에서 컴포넌트라 null을 반환하면 아무것도 렌더링하지 말라는 뜻이다.
- null이 발생해도 오류가 생기지 않고 그냥 검은 화면을 보게 된다.
- 컴포넌트가 렌더링된 다음에 useEffect 훅이 호출된다.
- useEffect 훅에서 fetch 요청을 수행한다.
- 응답을 받으면 JSON으로 만들고 setDate 함수에 값을 넘긴다.
- 컴포넌트는 다시 렌더링되고 data에 값이 있기 때문에 data의 내용이 랜더링 된다.
import React, {useState, useEffect } from 'react' function GithubUser({ login }) { const [data, setData] = useState() useEffect(() => { if (!login) return fetch(`https://api.github.com/users/${login}` .then(response => response.json()) .then(setData) .catch(console.error) }, [login]) if (data) { return
{ JSON.stringify(data, null, 2} }
} return null } export default function App() { return }
8.1.4 데이터를 로컬 스토리지에 저장하기
- 웹 스토리지 API를 사용하면 브라우저에 데이터를 저장할 수 있다.
- window.localStorage, window.sessionStorage 객체를 사용해 데이터를 저장
- sessionStorage API는 사용자 세션에만 저장한다. 탭을 닫거나 브라우저를 재시작하면 sessionStorage에 있는 데이터는 사라진다.
- localStorage는 사용자가 제거하기 전까지는 데이터를 무기한 보관한다.
- 브라우저에 데이터를 저장하기 위해서는 문자열 형태로 저장을 해야한다.
- loadJSON 함수는 localStorage에서 key를 사용해 값을 가져온다. 값이 없다면 null을 반환한다.
- saveJSON 함수는 데이터를 key를 식별자로 사용해 localStorage에 저장한다.
- 저장할때는 JSON을 문자열로 변환한다.
const loadJSON = key => key && JSON.parse(localStorage.getItem(key)) const saveJSON = (key, data) => localStorage.setItem(key, JSON.stringify(data))
- 웹스토리지로 부터 데이터를 저장하거나 불러올때는 동기적인 작업니다.
- 자주 사용하거나 너무큰 데이터를 사용하면 성능상 문제가 될 수 있다.
- GithubUser 컴포넌트에서 localStorage를 사용하는 예제 코드
- loadJSON 함수는 동기적으로 useState를 호출해서 데이터 초기값을 설정 한다.
- 데이터가 없으면 null로 초기화 됨
- 데이터를 가지고와서 data가 변경이 되었다면 saveJSON을 호출해 정보를 저장할 수 있다.
const [data, setData] = useState(loadJSON(`user:${login}`)) useEffect(() => { if (!data) return if (data.login === login) return const { name, avatar_url, location } = data saveJSON(`user:%{login}`, { name, login, avatar_url, location } }, [data])
- GithubUser 전체 코드
- import React, {useState, useEffect } from 'react' const loadJSON = key => key && JSON.parse(localStorage.getItem(key)) const saveJSON = (key, data) => localStorage.setItem(key, JSON.stringify(data)) function GithubUser({ login }) { const [data, setData] = useState(loadJSON(`user:${login}`)) useEffect(() => { if (!data) return if (data.login === login) return const { name, avatar_url, location } = data saveJSON(`user:%{login}`, { name, login, avatar_url, location } }, [data]) useEffect(() => { if (!login) return fetch(`https://api.github.com/users/${login}` .then(response => response.json()) .then(setData) .catch(console.error) }, [login]) if (data) { return{ JSON.stringify(data, null, 2} }} return null } export default function App() { return }
- localStorage에 있는 데이터를 지울때
- localStorage.clear()
- sessionStorage, localStorage를 사용해 오프라인일 때도 사용하거나 네트워크 요청을 줄임으로써 성능향상시킬 수 있다.
- 적절하지 않은 곳에서 사용하게 되면 복잡도가 증가하여 개발이 어려울 수 있다.
- 성능 향상만을 바라는 경우에는 HTTP가 캐시를 처리하게 하면 좋다
- 헤더에 Cache-Control: max-age=<EXP_DATE> 추가
8.1.5 프라미스 상태 처리하기
- HTTP 요청과 프라미스에는 3가지 상태가 있다.
- 진행 중, 성공, 실패
- HTTP 요청을 보낼 때는 3가지 경우를 모두 처리해야 한다.
- GithubUser 컴포넌트를 개성해서 error 의 세부 정보를 표시하는 코드
- 오류가 발생하면 error 객체를 JSON 문자열로 출력 한다.
- 요청이 진행 중이면 loading... 이라는 메세지를 h1으로 표시 한다.
- API에 따라 오류가 발생하더라도 성공으로 응답을 한다.
- 이때는 예외처리가 필요하다.
- </aside>
function GithubUser({ login }) { const [data, setData] = useState() const [error, setError] = useState() const [loading setLoading] = useState(false) useEffect(() => { if (!login) return setLoading(true) fetch(`https://api.github.com/users/${login}` .then(response => response.json()) .then(setData) .catch(setError) }, [login]) if (loading) return
hoading...
if (error) { return { JSON.stringify(error, null, 2)} } if (!data) return null return (
HTTP 요청을 재사용가능한 훅이나 컴포넌트로 처리하거나 이랙트 기능인 서스펜스로 처리할 수도 있다.
8.2 렌더 프롭
- 렌더 프롭(Render Props)는 말 그대로 렌더링되는 프로퍼티를 뜻한다.
- 렌더 프롭은 비동기 컴포넌트의 재상용성을 극대화하고 싶을때 유용하다.
- 컴포넌트가 렌더링될 때 데이터를 함수에 인자로 넘겨서 반환되는 컴포넌트를 렌더링에 사용할 수 있다.
- ?????
- 리스트를 표시하는 예제 코드
- import React from 'react' const tahoe_peaks = [ { name: "Freel Peak", elevation: 10891 }, { name: "Monument Peak", elevation: 10067 }, { name: "Pyramid Peak", elevation: 9983 }, { name: "Mt. Tallac", elevation: 9735 } ] export default function App() { return ( <ul> { tahoe_peaks.map((peak, i) => ( <li key={i}> {peak.name} - {peak.elevation.toLocaleString()}ft </li> )) } </ul> ) }
- 리스트가 비어 있을 경우 메세지를 표시하는 예제
- 배열은 값을 가지고 있거나 없을 수 있다.
- 리스트가 비어 있을때 렌더링해야 하는 컴포넌트를 전달할 수 있다.
- renderEmpty는 렌더 프롭이다.
function List({ data=[], renderEmpty }) { if (!data.length) return renderEmpty return <p>{data.length} items<p> } // This list is empty를 출력 하는 예제 export default function App() { return <List renderEmpty={<p>This list is empty</p>} /> } // 4개의 값이 잘 표시 되는 예제 export default function App() { return <List data={tahoe_peaks} renderEmpty={<p>This list is empty</p>} /> }
- 컴포넌트에게 배열의 원소를 렌더링할 수 있게 전달
- 배열이 비어있지 않으면 List 컴포넌트는 순서가 없는 <ul> 리스트를 렌더링 한다.
- <li> 엘리먼트에서 renderItem 프로퍼티가 호출
- 장점으로는 <ul>을 렌더링하고 싶을 때 재사용할 수 있는 컴포넌트를 만들 수 있다.
- 단점으로는 이 컴포는트는 너무 뼈대만 있다는 점
export default function App() { return ( <List data={tahoe_peaks} renderEmpty={<p>This list is empty</p>} renderItem={item => (<> {item.name} - {item.elevation.toLocaleString() } ft </>) ) } function List({ data = [], renderItem, renderEmpty }) { return !data.length ? ( renderEmpty ) : ( <ul> {data.map((item, i) => ( <li key={i}>{renderItem(item)}</li>)} </ul> ) }
8.3 가상화된 리스트
- 리스트를 렌더링하기 위해 재사용 가능한 컴포넌트를 만드려면 고려해야할 부분이 많다
- 대표적으로 리스트가 아주 큰 경우
- 브라우저가 렌더링할 수 있는 양도 제한이 있다.
- 시간, 프로세싱 파워, 메모리 등
- 1000가지 결과가 있을 경우 모두 렌더링하면 브라우저에 큰 부담이 갈 수 있다.
- 1000가지 대신 11가지만 렌더링 하는 방법을 고민해보자
- [그림 8-2 오프스크린 콘텐츠를 사용한 렌더링]
- 모바일에서는 이런 부분이 고려가 되어 있음 Android: RecyclerView, iOS: ListView
- 사용자가 스크롤 하면 이미 보여준 결과를 마운트 해제하고 오프스크린에 있는 결과를 몇개 더 렌더링해서 스크롤할 때 표시할수 있게 해준다.
- 이런 기법을 윈도잉이나 가상화라고 부른다.
- 가상화된 리스트 컴포넌트를 만드려면 고려해야할 내용이 많이 있다.
- 대부분은 직접 구현하기 보다는 라이브러리를 사용한다.
- react-window, react-virtualized가 있다.</aside>
- <aside> ❓ 정말 많이 쓰이나???
- 가상화된 리스트를 사용하는 방법을 알아보는 예제
- 많은 데이터가 필요하기 때문에 faker를 설치
npm i faker
- faker를 이용하면 가짜 데이터로 이뤄진 큰 배열을 만들 수 있다.
- 5천명의 가짜 사용자를 만들자
- import faker from 'faker' const bigList = [...Array(5000)].map(() => ({ name: faker.name.findName(), email: faker.internet.email(), avatar: faker.internet.avatar() }));
- 5천명의 사용자 정보를 렌더링
- export default function App() { }
- 많은 데이터가 필요하기 때문에 faker를 설치
728x90
'자바스크립트 > 러닝 리엑트' 카테고리의 다른 글
리액트 테스트 (1) | 2023.11.13 |
---|---|
Suspense (0) | 2023.11.13 |
훅스 컴포넌트 개선하기 (1) | 2023.11.13 |
리액트 상태 관리 (0) | 2023.11.13 |
JSX를 사용하는 리액트 (1) | 2023.11.13 |