자바스크립트/러닝 리엑트

데이터 포함시키기

막이86 2023. 11. 13. 13:02
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> ⛔ 코드에 오류가 있음 (잘못된 값을 넣어도 에러 메세지가 나오지 않음)
    • </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() { }
728x90

'자바스크립트 > 러닝 리엑트' 카테고리의 다른 글

리액트 테스트  (1) 2023.11.13
Suspense  (0) 2023.11.13
훅스 컴포넌트 개선하기  (1) 2023.11.13
리액트 상태 관리  (0) 2023.11.13
JSX를 사용하는 리액트  (1) 2023.11.13