Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7주차/현] 워크북 제출합니다. #69

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions keyword/chapter07/keyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
- Tanstack-Query 🍠
- Tanstack-Query 초기 세팅 방법

```jsx
$ npm i @tanstack/react-query
```

```jsx
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'

```

QueryClientProvider 컴포넌트를 최상단에서 감싸주고 QueryClient 인스턴스를 client 속성에 넣어줌.

- Query-DevTools?

```jsx
npm i @tanstack/react-query-devtools
```

```jsx
// App.tsx

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
// ...

const queryClient = new QueryClient();

function App() {
return (
// provide React Query client to App
<QueryClientProvider client={queryClient}>
<div className="App">
<h1>Blog Posts</h1>
<Posts />
</div>
<ReactQueryDevtools />
</QueryClientProvider>
);
}
```

- 리액트 쿼리에서 기본 제공하며, 다양한 기능을 제공
- key에 기반하여 쿼리를 보여줌
- 쿼리 상태
- 마지막 업데이트 타임스탬프
- 반환된 데이터 보여줌
- 쿼리 탐색기 제공
- useQuery

```jsx
import { useQuery } from "react-query";
// 주로 사용되는 3가지 return 값 외에도 더 많은 return 값들이 있다.
const { data, isLoading, error } = useQuery(queryKey, queryFn, options)
```

### QueryKey

- `QueryKey` 를 기반으로 데이터 캐싱을 관리
- 문자열 또는 배열로 지정

```tsx
// 문자열
useQuery('todos', ...)
// 배열
useQuery(['todos'], ...)
```

- 쿼리가 변수에 의존하는 경우에는 QueryKey 에도 해당 변수를 추가해야 함

```tsx
const { data, isLoading, error } = useQuery(['todos', id], () => axios.get(`http://.../${id}`));
```


### Query Functions

- useQuery 의 두번째 인자에는 promise 를 반환하는 함수를 넣음
- 거의 첫번째나 두번째 방식으로 코드를 작성

```tsx
useQuery('todos', fetchTodos);
useQuery(['todos', todoId], () => fetchTodoById(todoId));
useQuery(['todos', todoId], async () => {
const data = await fetchTodoById(todoId);
return data
});
useQuery(['todos', todoId], ({ queryKey }) => fetchTodoById(queryKey[1]));
```

- useInfiniteQuery

파라미터 값만 변경하여 동일한 useQuery를 무한정 호출할 때 사용

```jsx
const res = useInfiniteQuery(queryKey, queryFn);
```

- pageParam
- useInfiniteQuery가 현재 어떤 페이지에 있는지를 확인할 수 있는 파라미터 값
- getNextPageParam
- 다음 api를 요청할 때 사용될 pageParam값
- fetchNextPage
- 다음 페이지의 데이터를 호출할 때 사용
- getPreviousPageParam
- 이전 api를 요청할 때 사용될 pageParam값
- fetchPreviousPage
- 이전 페이지의 데이터를 호출할 때 사용
- queryKey

useQuery와 useMutation을 사용하면 해당 hooks의 `queryKey`를 지정

- React Query에서는 query keys를 기반으로 해서 쿼리 캐싱을 관리
- query key는 `문자열`, `문자열의 배열` 혹은 `중첩된 객체(nested object)`로 지정 가능
- query data에 고유하고 직렬화하여 사용

규칙

- querykeys는 반드시 [배열]이어야 함
- 추가적인 정보가 필요하다면, [배열]안에 작성
- 배열로 작성되는 queryKey의 값은 string key를 먼저 작성
- string key는 여러 개를 지정할 수 있지만 key가 연관되는 data는 고유함
- query에 필요한 추가 정보는 string key 뒤에 작성
- Pagination 🍠
- Pagination은 무엇인가요?
- 많은 데이터를 부분적으로 불러오는 기술
- 페이지 기반 페이지네이션(Page Based Pagination)
- 구현이 쉽고 어디를 페이지 네이션하는지 명확한 네비게이션 존재
- 데이터가 추가되거나 삭제될 경우 데이터가 누락되거나 중복될 가능성
- 커서 기반 페이지네이션(Cursor Based Pagination)
- 가장 최근에 가져온 데이터를 기준으로 다음 데이터를 가져오는 방식
- 요청을 보낼 때, 가장 마지막 데이터의 id와 함께 몇 개의 데이터를 가져올 지 개수를 명시
- 스크롤 형태의 리스트에서 자주 사용
- Pagination을 어떠한 방식으로 구현할 수 있을까요?
1. 클라이언트에서 **전체 데이터 한번에 받아서** 페이지 누를때마다 다른 데이터 보여주기
1. useEffect로 한번에 모든 데이터 다 받아오기
2. 페이지를 누를때마다, 저장된 데이터를 10개씩(`postPerPage`) 보여줌
2. 클라이언트에서 page를 query로 보내면 서버에서 **그에 맞는 데이터 보내주기**
1. DB에서 가져오는 총 데이터의 개수를 알아야 페이지네이션에 마지막 페이지 숫자를 설정
2. 한 페이지에 몇개의 데이터를 보여줄지 알아야 함
3. 현재 몇 페이지인지 알아야 함
- Pagination의 장점과 단점에 대해 정리해주세요.
- 특정 항목 검색 쉬움
- 페이지 북마크
- 사용자의 추가적인 액션이 필요함
- Infinite Scroll 🍠
- Intersection Observer는 무엇인가요?
- 브라우저 뷰포트(Viewport)와 원하는 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 아닌지 구별하는 기능을 제공
- 특정 요소가 사용자 화면에 보이는지 안보이는지 판단
- 비동기적으로 실행되기 때문에, 메인 스레드에 영향을 주지 않으면서 요소들의 변경사항들을 관찰할 수 있음

- Infinite Scroll은 무엇일까요?

사용자가 페이지 가장 아래에 도달할 때 데이터나 콘텐츠가 계속 로드가 되게 하는 방식

```jsx
npm i react-infinite-scroller
```

```jsx
import InfiniteScroll from 'react-infinite-scroller';
```

- Inifinite Scroll은 어떻게 구현할까요?

```jsx
const observer = new IntersectionObserver(callback, options);
```

- callback
- 관찰할 대상(target)이 등록되거나 threshold 만큼 observer가 뷰포트(root)에 교차할 때 콜백이 실행

```jsx
const io = new IntersectionObserver((entries, observer) => {}, options)

io.observe(element) // 관찰할 target 등록
```

- `IntersectionObserverEntry`인터페이스는 특정 전환 순간에 대상 요소와 루트 컨테이너 간의 교차를 설명
- `boundingClientRect`: target의 경계를 사각형으로 반환
- `intersectionRatio`: `intersectionRect` 와 `boundingClientRect`가 교차한 비율 (target이 root에 교차한 비율)
- `intersectionRect`: target과 교차한 부분의 영역 정보
- `isIntersecting`: target의 교차 여부(Boolean)
- `rootBounds`: root(options에 지정한 뷰포트) 정보
- `target`: 관찰 대상 요소
- `time`: 언제 교차가 발생했는지

```jsx
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log(entry) // IntersectionObserverEntry
})
}, options)

io.observe(element)
```

- options

```jsx
let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
```

- root
- 대상 객체의 가시성을 확인할 때 사용되는 뷰포트 요소(대상 객체의 조상 요소)
- 기본값은 `null` (null일 경우 브라우저의 뷰포트가 기본 사용)
- rootMargin
- 바깥 여백(Margin)을 이용해 Root 범위를 확장하거나 축소할 수 있음
- 사용 방법은 CSS와 동일 (단위 입력 필수 `px` `%`)
- threshold
- observer의 콜백이 실행될 대상 요소의 가시성 퍼센티지를 나타내는 단일 숫자 혹은 숫자 배열
- `0 ~ 1.0` 사이의 값을 가진다
- `0` : 요소가 1픽셀이라도 보이자 마자 콜백이 실행
- `0.5` : 50% 만큼 요소가 보여졌을 때를 콜백이 실행
- `[0, 0.25, 0.5, 0.75, 1]` : 배열의 요소만큼 보여졌을 때마다 콜백이 실행
- Infinite Scroll의 장점과 단점에 대해 정리해주세요.
- 사용자 참여 증가
- 사용자 추가 액션 불필요
- 모바일 기기 적합
- but, 페이지 성능 느려짐
- 특정 위치로 이동하기 어려움
- 컨텐츠 수량이 얼마나 되는지 알 수 없음
26 changes: 26 additions & 0 deletions mission/chapter07/mission/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
15 changes: 15 additions & 0 deletions mission/chapter07/mission/QueryProvider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { QueryClient, QueryClientProvider } from 'react-query';
import App from './src/App';

const queryClient = new QueryClient();

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
8 changes: 8 additions & 0 deletions mission/chapter07/mission/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# React + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
38 changes: 38 additions & 0 deletions mission/chapter07/mission/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'

export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]
12 changes: 12 additions & 0 deletions mission/chapter07/mission/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./src/main.jsx"></script>
</body>
</html>
Loading