React :
Framework :
Animation :
- 23-08-17 : #1.0 ~ #2.2 / Styled-Components(1)
- ํจํค์ง : 'styled-components'
- React ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ์คํ์ผ๋ง์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- JavaScript ์ฝ๋ ์์์ ์คํ์ผ์ ์ ์ํ๊ณ , ์ปดํฌ๋ํธ์ ํจ๊ป ์ฌ์ฉ ๊ฐ๋ฅ
- ์ฌ์ฉ๋ฒ
- import๊ตฌ๋ฌธ : import styled from "styled-components";
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = styled.HTMLํ๊ทธ๋ช `CSS์ฝ๋`;
- props ์ฌ์ฉ
- CSS : ${(์ธ์๋ช
) => ์ธ์๋ช
.prop๋ช
}
- ์ธ์๋ช ์ ์๋ฌด๊ฑฐ๋ ์๊ด์์ผ๋, ์ฃผ๋ก 'props'๋ก ์ฌ์ฉ
- ์ปดํฌ๋ํธ : <์ปดํฌ๋ํธ๋ช prop๋ช =prop๊ฐ />
- CSS : ${(์ธ์๋ช
) => ์ธ์๋ช
.prop๋ช
}
- ์ด๋ฏธ ๋ง๋ค์ด์ง ์ปดํฌ๋ํธ๋ก๋ถํฐ ํ์ฅ ๊ฐ๋ฅ
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = styled(๊ธฐ์กด์ปดํฌ๋ํธ๋ช )`...`;
- ์๋ ์์ฑ ๊ธฐ๋ฅ 'vscode-styled-components' ํ์ฅํ๋ก๊ทธ๋จ ์ฌ์ฉ
- ํจํค์ง : 'styled-components'
- 23-08-18 : #2.3 ~ #3.1 / Styled-Components(2) & Set up TypeScript
- styled-components
- ์ปดํฌ๋ํธ์ ์คํ์ผ์ ๊ทธ๋๋ก ์ฌ์ฉํ๋, HTMLํ๊ทธ๋ง์ ๋ฐ๊พธ๊ณ ์ถ์ ๋
- ์ปดํฌ๋ํธ์์ 'as="HTMLํ๊ทธ๋ช "' ์์ฑ์ ์ฌ์ฉ
- ์ปดํฌ๋ํธ ์์ฑ ์ ์์ฑ๊ฐ์ ์ค์ ํ ์ ์์
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = styled.HTMLํ๊ทธ๋ช .attrs({์์ฑ๊ฐ๋ค})`...`;
- 'attrs'๋ props๋ฅผ ์ฌ์ฉํด ์ปค์คํ ๋ง์ด์ง ๊ฐ๋ฅ
- ์ ๋๋ฉ์ด์
- import๋ฌธ : import { keyframes } from "styled-components";
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช = keyframes`...`;
- ์ ๋๋ฉ์ด์ ์ ์ฉ์ ๊ธฐ์กด CSS ๋ฐฉ์๊ณผ ๋์ผ
- ์ ํ์(pseudo selector)
- ํ์ ์ ํ์๊ฐ ์ผ๋ฐ HTMLํ๊ทธ ์ด๋๋ผ๋ ์ ํ ๊ฐ๋ฅ (SCSS์ ๊ตฌ๋ฌธ์ด ๋น์ทํจ)
- ํ์ ์ ํ์๊ฐ ์ปดํฌ๋ํธ('as'๋ฅผ ์ฌ์ฉํ๋๋ผ๋) ์ฌ์ฉ ๊ฐ๋ฅ
- '${์ปดํฌ๋ํธ๋ช }'์ผ๋ก ์ฌ์ฉ
- '&' ๊ธฐํธ๋ ์๊ธฐ ์์ ์ ๊ฐ๋ฆฌํด
- theme : ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ์์๋ค์ ๊ฐ์ง๊ณ ์๋ object
- ๋ชจ๋ ์์๋ค์ ํ๋์ object ์์ ๋ฃ์ด๋จ๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ์ ์ฉํจ
- ์ถํ ์์๋ค์ ๋ฐ๊ฟ ๋ ํด๋น object๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋๊ธฐ ๋๋ฌธ
- ์ค์ ๋ฒ
- 'index'ํ์ผ์์ 'import { ThemeProvider } from "styled-components";'
- ์ฌ์ฉํ ์ปดํฌ๋ํธ๋ฅผ '<ThemeProvider>'๋ก ๊ฐ์ธ์ฃผ๊ธฐ
- '<ThemeProvider>'์ 'theme' ์์ฑ ๋ถ์ฌํ๊ธฐ
- ์ฌ์ฉ๋ฒ
- 'styled-components'์์ 'props.theme' ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ์ฌ์ฉ
- 'theme'์์ ์ฌ์ฉํ ํ๋กํผํฐ๋ช ์ ์ฌ์ฉํด์ผ ํจ
- 'theme' ๊ต์ฒด ์ '<ThemeProvider>'์ 'theme' ์์ฑ๊ฐ๋ง ๊ต์ฒดํ๋ฉด ๋จ
- ์ปดํฌ๋ํธ์ ์คํ์ผ์ ๊ทธ๋๋ก ์ฌ์ฉํ๋, HTMLํ๊ทธ๋ง์ ๋ฐ๊พธ๊ณ ์ถ์ ๋
- TypeScript : JavaScript๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด, compileํ์ฌ ํ๋ฒํ JavaScript๊ฐ ๋จ
- ์ค์น๋ฒ
- ์๋ก์ด ํ๋ก์ ํธ ์์ฑ : 'npx create-react-app ํด๋๋ช --template typescript'
- ๊ธฐ์กด ํ๋ก์ ํธ์ TS ์ถ๊ฐ : 'npm i --save typescript @types/node @types/react @types/react-dom @types/jest'
- TS๊ฐ ํจํค์ง๋ฅผ ์์ง ๋ชปํ ๋
- ๋ช๋ช ํจํค์ง๋ JS๋ก ๋ง๋ค์ด์ก๊ธฐ ๋๋ฌธ
- 'npm i -D @types/ํจํค์ง๋ช '์ ์คํํด ์ค์น
- 'index.tsx'์์ root ์ฝ๋๊ฐ error ๋ ์ ์๋์ ์ฝ๋๋ก ์์ (+ as HTMLElement)
- const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
- ์ค์น๋ฒ
- styled-components
- 23-08-21 : #3.2 ~ #4.2 / TypeScript + React-Router-V6(1)
- TypeScript
- React Component
- Component๊ฐ ํ์๋ก ํ๋ prop์ TS์๊ฒ ์ค๋ช ํด์ผ ํจ
- interface : ๊ฐ์ฒด ๋ชจ์(object shape)์ TS์๊ฒ ์ค๋ช
ํด์ฃผ๋ TS์ ๊ฐ๋
- ์ ์ธ ๊ธฐ๋ณธํ : interface ์ธํฐํ์ด์ค๋ช { ... }
- ์ฌ์ฉ ๊ธฐ๋ณธํ : ':ํ์ ๋ช '
- 'styled-components'์์ prop์ ๋ฐ์ผ๋ ค๋ฉด, ํ์
์ ์ ์ธํด ์ฃผ์ด์ผ ํจ
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = styled.HTMLํ๊ทธ๋ช <ํ์ ๋ช >`...`;
- ์ ํ์ (optional) props
- '?'๊ธฐํธ๋ฅผ ์ฌ์ฉํด ์ ํ์ prop ์ค์ ๊ฐ๋ฅ
- ๋ณํฉ์ฐ์ฐ์ '??' : 'null' ๋๋ 'undefined'์ผ ๋ ๊ธฐ๋ณธ๊ฐ ์ง์ ๊ฐ๋ฅ
- React State
- TS๊ฐ 'useState'์ ์ด๊น๊ฐ์ ๋ณด๊ณ , ์๋์ ์ผ๋ก ํ์ ์ ์ถ๋ก ํจ
- ๊ฐ๋ 2๊ฐ ์ด์์ ํ์
์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ง์ ํ์
์ ์ ์ธํด ์ฃผ์ด์ผ ํจ
- ๊ธฐ๋ณธํ : const [๋ณ์๋ช , ํจ์๋ช ] = useState<ํ์ ๋ช >(์ด๊น๊ฐ);
- React Event
- 'addEventListener'์ ์ธ์๋ก ์ฌ์ฉํ๋ 'event'์ ํ์ ์ ์ค๋ช ํด์ผ ํจ
- React Component
- TypeScript
- 23-08-22 : #5.0 ~ #5.3 / React-Router + createGlobalStyle + fetchAPI
- React-Router v6
- ์ค์ ๋ฒ
- '/src/routes' ํด๋์ ํ๋ฉด์ ๋ณด์ฌ์ค ์ปดํฌ๋ํธ ํ์ผ๋ค์ ์์ฑํ๊ธฐ
- '/src/Router.tsx' ๋ผ์ฐํฐ ํ์ผ ์์ฑ ๋ฐ ์ค์ ํ๊ธฐ
- { BrowserRouter, Routes, Route } ๋ฑ
- ๋ผ์ฐํฐ ํ์ผ์ ๋ ๋๋งํ๊ธฐ
- 'App.tsx'์์ <Router />๋ฅผ returnํ์ฌ ๋ ๋๋ง
- 'useParams()' : URL ํ๋ผ๋ฏธํฐ ๊ฐ(object)์ ๋ฐ์ ์ ์์
- <Link> : React-Router ๋ด์์ ํ์ด์ง๋ฅผ ์ด๋ํ๋ ํ๊ทธ
- ๊ธฐ๋ณธํ : <Link to="์ด๋ํ URL">๋ด์ฉ</Link>
- CSS์์๋ 'Link' ๋์ 'a'ํ๊ทธ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
- ์ค์ ๋ฒ
- 'styled-components'์์์ ์ ์ญ ์คํ์ผ๋ง
- 'createGlobalStyle' ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํด ๋ ๋๋ง ์ ์ ์ญ ์ค์ฝํ์ ์คํ์ผ ์ฌ์ฉ
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = createGlobalStyle` ... `;
- '<์ปดํฌ๋ํธ๋ช
/>'์ ์ฌ์ฉํด return
- ํฐํธ ์ฌ์ฉ ์ '@import'๋ฌธ์ด ์ ๋๋ก ์ ์ฉ๋์ง ์์ผ๋ฏ๋ก, 'index.html'์ <head>์ ๋ฃ์ ํ ์ฌ์ฉ
- 'createGlobalStyle' ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํด ๋ ๋๋ง ์ ์ ์ญ ์ค์ฝํ์ ์คํ์ผ ์ฌ์ฉ
- <Fragment> : ์ผ์ข
์ ์ ๋ น ์ปดํฌ๋ํธ
- ๋ถ๋ชจ element ์์ด ์๋ก ๋ถ์ด์๋ ๋ง์ ์ปดํฌ๋ํธ๋ค์ returnํ ์ ์๊ฒ ํด์ค
- <div> element ์ฌ์ฉ ์์ด ๋ ๋๋ง
- ๋ฌด์ํ <div>๊ฐ ์ฌ์ฉ๋๋ ๊ฒ์ ๋ฐฉ์งํจ
- ์ฃผ๋ก ๋น ํ๊ทธ(<></>)๋ก ์ฌ์ฉํจ
- ๋ถ๋ชจ element ์์ด ์๋ก ๋ถ์ด์๋ ๋ง์ ์ปดํฌ๋ํธ๋ค์ returnํ ์ ์๊ฒ ํด์ค
- fetch API
- API๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ TS์๊ฒ ๋ฐ์ดํฐ์ ํ์ ์ ์๋ ค์ฃผ์ด์ผ ํจ (interface)
- 'axios'ํจํค์ง ์ฌ์ฉ ์ json ํ์์ผ๋ก ๋ฐ๋ก ๊ฐ์ ธ์ด
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช = await axios("URL์ฃผ์");
- ์ฆ์ ์คํ ํจ์ ํํ (IIFE; Immediately Invoked Function Expression)
- ์ ์๋์๋ง์ ์ฆ์ ์คํ๋๋ ํจ์ (์ ์ธ ํ ์คํํ ํ์ ์์)
- ๊ธฐ๋ณธํ : (ํจ์์ ์ธ)();
- React-Router v6
- 23-08-23 : #5.4 ~ #5.8 / Link state + Nested Routes + useMatch
- <Link> state : ํ๋ฉด์ ๋ณด์ด์ง ์๋ ๋ฐฉ์์ผ๋ก ๋ค๋ฅธ ํ์ด์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๋ฐฉ๋ฒ
- 'state' ์์ฑ์ ์ฌ์ฉํด object ํ์์ผ๋ก ๋ฐ์ดํฐ ์ ์ก ๊ฐ๋ฅ
- 'useLocation()'์ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฅผ ์์
- ๋ฐ์ ๋ฐ์ดํฐ์ ํ์ ์ TS์๊ฒ ์ค๋ช ํด์ฃผ์ด์ผ ํจ
- react-router-dom v6๋ถํฐ 'useLocation()'์ ์ ๋ค๋ฆญ์ ์ง์ํ์ง ์์
- 'as ์ธํฐํ์ด์ค๋ช ' ๋ฐฉ์์ผ๋ก ์ฌ์ฉ
- '?.' ์ต์
๋์ฒด์ด๋
- ๊ฐ์ฒด์ ํ๋กํผํฐ์ ์ ๊ทผ ์ ํด๋น ํ๋กํผํฐ๊ฐ ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ, Error๋ฅผ ๋ฐ์์ํค์ง ์๊ณ 'undefined'๋ฅผ ๋ฐํํจ
- Nested Routes : route ์์ ์กด์ฌํ๋ ๋ ๋ค๋ฅธ route
- ์น์ฌ์ดํธ์์ ํญ์ ์ฌ์ฉํ ๋ ์ ์ฉํจ (์คํฌ๋ฆฐ ์์ ์น์ ์ด ๋ง์ ๋ ๋ฑ)
- ์์ฑ๋ฒ (์์ route๋ฅผ ๋ผ์ฐํฐ ํ์ผ์ ์์ฑํ๋ ๋ฐฉ๋ฒ)
- ๋ผ์ฐํฐ ํ์ผ์์ ๋ถ๋ชจ <Route>์ ๊ฐ์ธ์ ์ฌ์ฉํ๊ธฐ
- ์๋๊ฒฝ๋ก URL์ ์ง์ํ๋ฏ๋ก, ์์ route์ path๋ ์๋๊ฒฝ๋ก๋ฅผ ์ฌ์ฉ
- ์์ route๊ฐ ์ด๋์ render๊ฐ ๋ ์ง ๋ถ๋ชจ route ์ปดํฌ๋ํธ์์ '<Outlet />'์ผ๋ก ํ์ํ๊ธฐ
- ๋ผ์ฐํฐ ํ์ผ์์ ๋ถ๋ชจ <Route>์ ๊ฐ์ธ์ ์ฌ์ฉํ๊ธฐ
- useMatch() : ํ์ฌ URL ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ์ง์ ๋ ๊ฒฝ๋ก์ ๋ํ ์ผ์น ๋ฐ์ดํฐ๋ฅผ ๋ฐํ
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช
= useMatch("URL์ฃผ์");
- import { useMatch } from "react-router-dom";
- ์๋๊ฒฝ๋ก URL ์ฌ์ฉ ๋ถ๊ฐ๋ฅ
- ์ฌ์ฉ์๊ฐ ์ ํํ URL์ ์์นํ๋ฉด object๋ฅผ ๋ฐํ, ๊ทธ๋ ์ง์์ผ๋ฉด null์ ๋ฐํ
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช
= useMatch("URL์ฃผ์");
- <Link> state : ํ๋ฉด์ ๋ณด์ด์ง ์๋ ๋ฐฉ์์ผ๋ก ๋ค๋ฅธ ํ์ด์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๋ฐฉ๋ฒ
- 23-08-26 : #5.9 ~ #5.11 / React-Query
- React-Query
- ํธ๋ฆฌํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ fetch ํ ์ ์๋ ํจํค์ง
- ๊ธฐ์กด ๋ฐฉ์์ธ useEffect์ useState๋ฅผ ํ์ฉํ fetch API๋ฅผ ํ์ง ์์๋ ๋จ
- ์ค์น๋ฒ : 'npm i @tanstack/react-query'
- ์ค์ ๋ฒ
- 'index.tsx'์์ 'queryClient' ๊ฐ์ฒด ์์ฑํ๊ธฐ
- '<QueryClientProvider>' ์์ฑ ํ ๊ฐ์ธ์ฃผ๊ธฐ
- ์ฌ์ฉ๋ฒ
- fetcher ํจ์ ์์ฑํ๊ธฐ
- ๋ฌด์กฐ๊ฑด fetch promise(json)๋ฅผ return ํด์ฃผ์ด์ผ ํจ
- 'useQuery' Hook์ ์ฌ์ฉํด API ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
- ๊ธฐ๋ณธํ : const { isLoading, data } = useQuery<์ ๋ค๋ฆญ>([์ฟผ๋ฆฌํค๋ช
], fetcherํจ์, ?์ต์
);
- queryKey : (string[]) query์ ๊ณ ์ ์๋ณ์
- fetcherํจ์ : ๋งค๊ฐ๋ณ์ ํ์ ์ ์ต๋ช ํ์ดํํจ์๋ก ์์ฑ
- ์ต์
: (object)
- refetchInterval : (number) ์๋์ผ๋ก refetchํ๋ ์๊ฐ ์ฃผ๊ธฐ๋ฅผ ์ค์
- isLoading : ๋ก๋ฉ์ ๋ํ boolean๊ฐ์ return
- data : fetch๊ฐ ๋๋ ํ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค
- ๊ธฐ๋ณธํ : const { isLoading, data } = useQuery<์ ๋ค๋ฆญ>([์ฟผ๋ฆฌํค๋ช
], fetcherํจ์, ?์ต์
);
- fetcher ํจ์ ์์ฑํ๊ธฐ
- [ํต์ฌ] ๋ค๋ฅธ ํ์ด์ง์ ๊ฐ๋ค๊ฐ ๋ค์ ๋์์๋ API๋ฅผ ์ฌ์์ฒญํ์ง ์์
- React-Query๊ฐ ๋ฐ์ดํฐ๋ฅผ ์บ์์ ์ ์ฅํด๋๊ธฐ ๋๋ฌธ
- ํธ๋ฆฌํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ fetch ํ ์ ์๋ ํจํค์ง
- React-Query-Devtools
- ์บ์์ ์กด์ฌํ๋ Query๋ฅผ ๋ณผ ์ ์์
- React-Query์ ๋ชจ๋ ๋ด๋ถ ๋์์ ์๊ฐํํ๋ ๋ฐ ๋์
- ๋ฌธ์ ๋ฐ์์ ๋๋ฒ๊น ์๊ฐ์ ์ ์ฝํ ์ ์์
- ๊ธฐ๋ณธ์ ์ผ๋ก React-Query-Devtools๋ process.env.NODE_ENV === 'development'์ธ ๊ฒฝ์ฐ์๋ง ๋ฒ๋ค์ ํฌํจ๋๋ฏ๋ก, production build ์ค์ ์ ์ธํ๋ ๊ฒ์ ๋ํด ๊ฑฑ์ ํ ํ์ ์์
- ์ค์น๋ฒ : 'npm i @tanstack/react-query-devtools'
- ์ค์ ๋ฐ ์ฌ์ฉ๋ฒ : 'App.tsx'์์ 'ReactQueryDevtools'๋ฅผ importํ ํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉ
- 'initialIsOpen={true}' ์์ฑ ํ์
- ์บ์์ ์กด์ฌํ๋ Query๋ฅผ ๋ณผ ์ ์์
- React-Query
- 23-08-28 : #5.12 ~ #5.16 / ApexCharts + React-Helmet
- useOutletContext()
- ์ค์ฒฉ๋ ๋ผ์ฐํธ(Nested Routes)์์ ๋ถ๋ชจ์์ ์์ ์ปดํฌ๋ํธ์๊ฒ ์ํ(state)๋ฅผ ๊ณต์ ํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ๋ Hook
- <Outlet /> ์ฌ์ฉ ์ ํ์ Route์๊ฒ ๋ณ์๋ฅผ ๋๊ฒจ์ค ์ ์๋ ๊ธฐ๋ฅ
- ์ก์ ๊ธฐ๋ณธํ : <Outlet context={{ ... }}> (object ํ์์ผ๋ก ์ฌ์ฉ)
- ์์ ๊ธฐ๋ณธํ
- import { useOutletContext } from "react-router-dom";
- const ๋ณ์๋ช = useOutletContext<์ ๋ค๋ฆญ>();
- ApexCharts.js
- API๋ก ๋ฐ์์จ ๋ฐ์ดํฐ ๋ฑ์ ์๊ฐํ ํ ์ ์๋ ํจํค์ง
- ์ค์น๋ฒ : 'npm i react-apexcharts apexcharts'
- ์ฌ์ฉ๋ฒ
- import ApexChart from "react-apexcharts";
- <ApexChart ์์ฑ๋ค />
- type : ๊ทธ๋ํ ํ์ (string)
- series : ๋ณด์ฌ์ค ๋ฐ์ดํฐ (object[]) { name: string, data: [] }
- options : ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ ์ฌ์ฉ (object)
- React-Helmet
- ๋ฌธ์์ <head>๋ก ๋ ๋๋งํ๋ ์ปดํฌ๋ํธํ ํจํค์ง
- ์ค์น๋ฒ : 'npm i react-helmet'
- ์ฌ์ฉ๋ฒ
- import { Helmet } from "react-helmet";
- <Helmet> ... </Helmet>
- 'Using UNSAFE_componentWillMount in strict mode is not recommended
and may indicate bugs in your code.'์ Error ์ 'react-helmet-async' ํจํค์ง๋ฅผ ์ฌ์ฉ
- 'App.tsx'์์ <Router />๋ฅผ <HelmetProvider>๋ก ๊ฐ์ธ์ค ํ ์ฌ์ฉ
- 'react-helmet' ํจํค์ง๋ ๋ค์ด ๋ฐ์์ผ ํจ
- useOutletContext()
- 23-08-29 : [Code Challenge] use ApexChart
- 23-08-30 : #6.0 ~ #6.4 / Recoil Atom
- Recoil
- React์์ ์ฌ์ฉํ ์ ์๋ ์ํ ๊ด๋ฆฌ(state management) ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ์ค์น๋ฒ : 'npm i recoil'
- ์ค์ ๋ฒ : 'index.tsx'์์ <RecoilRoot>๋ก ์ฑ์ ๊ฐ์ธ์ฃผ๊ธฐ
- atom : ํน์ ์ปดํฌ๋ํธ์ ์ข
์๋์ง ์์ผ๋ฉฐ, global state๋ฅผ ์ ์ฅํ๋ ๊ธฐ๋ณธ ๋จ์
- Recoil์์๋ state๊ฐ์ 'atom'์ด๋ผ๋ ๋ฒ๋ธ์ ๋ด์์ ์ฌ์ฉ
- atom์ด ๋ณ๊ฒฝ๋๋ฉด ์ปดํฌ๋ํธ๋ ๋ณ๊ฒฝ๋ ๊ฐ์ผ๋ก re-rendering ๋จ
- ์ฌ์ฉ๋ฒ
- 'atom'์ ๊ด๋ฆฌํ๋ ํ์ผ ์์ฑํ๊ธฐ
- '/src/atoms.ts'
- 'atom' ์์ฑํ๊ธฐ
- import { atom } from "recoil";
- ๊ธฐ๋ณธํ : export const ๋ณ์๋ช = atom({ key: ํค๊ฐ, default: ์ด๊ธฐ๊ฐ});
- [Read] ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ 'atom' ์ฐ๊ฒฐํ๊ธฐ
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช = useRecoilValue(์ํฐ๋ช );
- [Write] ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ 'atom' ์์ ํ๊ธฐ
- ๊ธฐ๋ณธํ : const Setterํจ์ = useRecoilState(์ํฐ๋ช );
- 'useState()'์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋จ
- 'atom'์ ๊ด๋ฆฌํ๋ ํ์ผ ์์ฑํ๊ธฐ
- Recoil
- 23-08-31 : #6.5 ~ #6.7 / React-Hook-Form(1)
- React Hook Form
- React์์ <form>์ ๊ฐ๋จํ๊ฒ ์ ์ํ ์ ์๊ฒ ๋์์ฃผ๋ฉฐ, ๊ฒ์ฆ๊น์ง ํ ์ ์๋ ํจํค์ง
- ๊ธฐ์กด React์์ <form> ์ ์ ์ setState(), value, onChange, onSubmit ๋ฑ์ ์ฌ์ฉํด์ผ ํ์
- ์ค์น๋ฒ : 'npm i react-hook-form'
- ๊ธฐ๋ณธํ
- import { useForm} from "react-hook-form";
- const { register, watch, handleSubmit, formState ๋ฑ } = useForm<์ ๋ค๋ฆญ>();
- register : <input>์ value์ onChange๋ฅผ ๋์ฒดํ๋ ์์ฑ
- name, onBlur, onChange, ref ๋ฑ์ ๊ฐ์ง๋ object๋ฅผ returnํ๋ ํจ์
- ์ฌ์ฉ๋ฒ : <input {...register(์ด๋ฆ, {๊ฒ์ฆ ์์ฑ})} />
- ES6 ๋ฌธ๋ฒ์ ์ฌ์ฉํด register ํจ์๊ฐ ๋ฐํํ๋ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ๋ค๊ฐ props๋ก ์ฌ์ฉ
- HTML ๋ฟ๋ง์๋๋ผ JS์์๋ ๊ฒ์ฆ์ ํ ์ ์๋๋ก ํจ
- ์ ํจํ์ง ์์ ์ ์๋์ผ๋ก ํด๋น element๋ก focus ํด์ค
- ex. <input {...register("email", { required: true, minLength: 10 })} placeholder="Email" />
- handleSubmit : <form>์ 'onSubmit'์ ๋์ฒดํ๊ณ , validation์ ๋ด๋นํจ
- ๊ธฐ์กด onSubmit์ 'event.preventDefault()์ setModifierํจ์'๋ฅผ ๋์ฒด
- ์ฌ์ฉ๋ฒ : <form onSubmit={handleSubmit(๋ฐ์ดํฐ ์ ํจ ์ ํธ์ถํจ์, ?๋ฐ์ดํฐ ๋ฌดํจ ์ ํธ์ถ ํจ์)}>
- watch : <form>์ ์
๋ ฅ๊ฐ๋ค์ ๋ณํ๋ฅผ ๊ด์ฐฐํ ์ ์๊ฒ ํด์ฃผ๋ ํจ์
- registerํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ element๋ค์ ๊ฐ์ object๋ก ๋ฐํ
- ex. console.log(watch());
- formState : ๋ฐ์ดํฐ ๊ฒ์ฆ์ด ์ ํจํ์ง ์์ ์ 'formState.errors'๋ฅผ ์ฐธ์กฐํด error ๋ด์ฉ ํ์ธ ๊ฐ๋ฅ
- ์ด๋ค ์ข ๋ฅ์ error๊ฐ ๋ฐ์ํ๋์ง ์ ์ ์์
- error ๋ฐ์ ์ message๋ฅผ ๋ณด๋ผ ์ ์์
- ex. <input {...register("pw", { required: "PW is Required", minLength: { value: 5, message: "Your PW is too short" }})} />
- React์์ <form>์ ๊ฐ๋จํ๊ฒ ์ ์ํ ์ ์๊ฒ ๋์์ฃผ๋ฉฐ, ๊ฒ์ฆ๊น์ง ํ ์ ์๋ ํจํค์ง
- React Hook Form
- 23-09-02 : #6.8 ~ #6.10 / React-Hook-Form(2)
- React-Hook-Form
- React-Hook-Form์์๋ register ์ฌ์ฉ ์ ๋ฌธ์์ด์ return ํ๋ฉด, error ๋ฉ์์ง๋ฅผ returnํ๋ค๋ ๋ป
- setValue : ํ๋ ๊ฐ์ ์
๋ฐ์ดํธํ๋ ํจ์
- ๊ธฐ๋ณธํ : const { setValue } = useForm();
- ์ฌ์ฉ๋ฒ : setValue(์ด๋ฆ, ํ๋๊ฐ);
- reset : <form>์ ๋ชจ๋ ํ๋๊ฐ์ ์ด๊ธฐํํ๋ ํจ์
- ๊ธฐ๋ณธํ : const { reset } = useForm();
- ์ฌ์ฉ๋ฒ : reset();
- React-Hook-Form - ๊ฒ์ฆ
- ๊ฒ์ฆ ์ ์ ๊ท์(Regular Expression)์ ์ฌ์ฉํ ์ ์์ : 'pattern' ํ๋กํผํฐ ์ฌ์ฉ
- ๊ฒ์ฆ ์กฐ๊ฑด๋ง๋ค message๋ฅผ ์ ์ด๋จ๋ค๋ฉด, error ๋ฌธ๊ตฌ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅ ๊ฐ๋ฅ
- ์ฌ์ฉ์๊ฐ ์ ์ถํ ์ดํ์๋ message๊ฐ ์ค์๊ฐ์ผ๋ก ๋ณํจ
- 'useForm()'์ ์ ๋ค๋ฆญ์ ๋ช ์ํ์ง ์์์์ string์ผ๋ก ํ์ ์ ๋ช ์ํด์ฃผ์ด์ผ ํจ (as string)
- React-Hook-Form - ์ปค์คํ
๊ฒ์ฆ
- 'handleSubmit()'์ ์ธ์๋ก ์ฌ์ฉํ๋ ์ฝ๋ฐฑํจ์์ ์ปค์คํ ๊ฒ์ฆ์ด ๊ฐ๋ฅํจ
- ๊ฒ์ฆ ์ฝ๋ฐฑํจ์์ ๋งค๊ฐ๋ณ์๋ ์๋์ผ๋ก <form>์ element๋ค์ ๋ฐ์
- ํด๋น ๋งค๊ฐ๋ณ์์ ํ๋กํผํฐ๋ฅผ ์ฌ์ฉ
- useForm()์ 'setError' ํ๋กํผํฐ๋ ํน์ ํ error๋ฅผ ๋ฐ์์์ผ ์ค
- ๊ธฐ๋ณธํ : setError(๋ ์ง์คํฐ๋ช , { message: ์๋ฌ๋ฉ์์ง });
- interface์ ์ต์ ํญ๋ชฉ์ ๋ฃ์ด์, ์ถ๊ฐ์ ์ธ error๋ฅผ ํ๊ธฐํ ์ ์์
- <form>์์ ์๋์ผ๋ก error ํญ๋ชฉ์ผ๋ก focus๋๊ฒ ํ ์ ์์
- setError()์ 3๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก '{ shouldFocus: true }'๋ฅผ ๋ถ์ฌํ์ฌ ์๋
- register์ ๊ฒ์ฆ ์์ฑ๋ค ์ค 'validate' ์ต์
์ ์ฌ์ฉํด ์ปค์คํ
๊ท์น์ ๊ฒ์ฌํ ์ ์์
- validate : ํ์ฌ value๊ฐ์ ์ธ์๋ก ๋ฐ๋ ์ฝ๋ฐฑํจ์๋ฅผ ๊ฐ์ผ๋ก ๊ฐ์ง๋ฉฐ, boolean๊ฐ์ ๋ฐํํจ (๊ฒ์ฆ์ ํต๊ณผ/๋ถํต๊ณผ)
- ์ผํญ์กฐ๊ฑด์ฐ์ฐ์(๋๋ ||)๋ฅผ ์ฌ์ฉํด error ๋ฉ์์ง๋ฅผ return ํ๋๋ก ํจ
- ์ฌ๋ฌ ํจ์(๊ฒ์ฆ)๊ฐ ์๋ object ํํ๊ฐ ๋ ์ ์์
- async ๋น๋๊ธฐ๋ก ๋ง๋ค์ด์ ์๋ฒ์๋ค๊ฐ ํ์ธํ๊ณ ์๋ต์ ๋ฐ์ ์ ์์
- React-Hook-Form
- 23-09-04 : #6.11 ~ #6.15 / To-Do List
- Recoil - useRecoilState()
- atom์ 'useState()'์ฒ๋ผ ์ฌ์ฉํ๋ ๋ฉ์๋
- 'useRecoilValue()'์ 'useSetRecoilState()'๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ ํ์๊ฐ ์์
- ๊ธฐ๋ณธํ : const [๊ฐ, ์ธํฐํจ์] = useRecoilState(์ํฐ๋ช );
- atom์ 'useState()'์ฒ๋ผ ์ฌ์ฉํ๋ ๋ฉ์๋
- ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง๋ Event Listener ์ฌ์ฉ๋ฒ
- element์์ event๋ฅผ ์ต๋ช
ํจ์๋ฅผ ์ฌ์ฉํด ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ
- ex. const onClick = (newCategory: IToDo["category"]) => { ย ย console.log("I wanna to", newCategory); }; <button onClick={() => onClick("DOING")}>Doing</button>
- element์ name ์์ฑ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ
- 'name' ์์ฑ์ ์ง์ ํ์ ์ ์ค ์ ์๊ธฐ ๋๋ฌธ์, 1๋ฒ ๋ฐฉ๋ฒ์ ์ ํธ
- ex. const onClick = (event: React.MouseEvent<HTMLButtonElement>) => { ย ย console.log("I wanna to", event.currentTarget.name); }; <button name="DOING" onClick={onClick}>Doing</button>
- element์์ event๋ฅผ ์ต๋ช
ํจ์๋ฅผ ์ฌ์ฉํด ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ
- To-Do List์์ ํ๋์ to-do๋ฅผ ์์ ํ๋ ๋ฐฉ๋ฒ
- ๋ฐฐ์ด์ mutateํ์ง ์์ผ๋ฉด์, 'useSetRecoilState()'๋ก ์ํ๋ ํ๋์ ๊ฐ์ ์์ ํด์ผ ํจ
- 'id' ๋ฑ์ ์์ฑ์ ์ฌ์ฉํ์ฌ, ๋ฐฐ์ด ์ ์ํ๋ element์ index ์ฐพ๊ธฐ
- ๊ฐ ์์ฒด๋ฅผ ์ฐพ์ ํ์๋ ์๊ณ , ๋ฐฐ์ด index๊ฐ๋ง ์๋ฉด ๋จ
- ๋ฐฐ์ด์ '.findIndex(์ฝ๋ฐฑํจ์)' ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์กฐ๊ฑด์ ๋ง๋ index๋ฅผ ์์๋
- element๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ
- ์ฐพ์ index์ ๋ํ element๋ฅผ ์๋ก์ด ๊ฐ์ผ๋ก ๋์ฒด(replace)ํด์ผ ํจ
- element๋ฅผ ๊ต์ฒดํ๋ ์ด์ ๋ ํด๋น element์ ์์น๊ฐ ๋ฐ๋์ง ์๊ธธ ๋ฐ๋ผ๊ธฐ ๋๋ฌธ
- ๊ต์ฒดํ element์ ์ ๋ถ๋ถ์ ์๋ก์ด ๋ฐฐ์ด๋ก ์์ฑํ๊ธฐ
- '.slice(์์์ธ๋ฑ์ค, ๋์ธ๋ฑ์ค)' ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์๋ก์ด ๋ฐฐ์ด ์์ฑ
- ๊ต์ฒดํ element์ ๋ท ๋ถ๋ถ์ ์๋ก์ด ๋ฐฐ์ด๋ก ์์ฑํ๊ธฐ
- '.slice()' ๋ฉ์๋์ ๋์ธ๋ฑ์ค๋ฅผ ์ง์ ํ์ง ์์ ์ ๋๊น์ง ์๋ผ์ ๋ฐํํจ
- ๊ต์ฒด ํ ์๋ก์ด ๋ฐฐ์ด๋ก ์์ฑํ๊ธฐ
- ๋ฐฐ์ด์ ์คํ๋ ๋ ์ฐ์ฐ์(...)๋ฅผ ์ด์ฉํ์ฌ, ์๋ถ๋ถ๊ณผ ๋ท๋ถ๋ถ ์ฌ์ด์ ์ ์์๋ฅผ ๋ฃ์
- Recoil - useRecoilState()
- 23-09-05 : #6.16 ~ #7.1 / Recoil Selector + Recoil-Persist
- Recoil - Selector
- atom์ output์ ๋ณํ์ํค๋ ๋๊ตฌ (atom์ state ์์ฒด๋ฅผ ๋ฐ๊พธ๋ ๊ฒ์ด ์๋๋ผ, ๊ทธ์ output์ ๋ฐ๊พธ๋ ๊ฒ)
- ์ ์ธ๋ฒ
const ์
๋ ํฐ๋ช
= selector({
ย ย key: ํค๊ฐ,
ย ย get: ({ get }) => {
ย ย ย ย const ๋ณ์๋ช
= get(์ํฐ๋ช
); // atom์ ๊ฐ์ ธ์ค๋ ๋ฌธ (ํ์x)
ย ย ย ย ......
ย ย ย ย return ๊ฐ;
ย ย },
});
- get
- ์ธ์๋ก ๋ฐ๋ ์ต์ ์ค์ 'get'ํจ์๋ฅผ ์ด์ฉํด atom์ ๋ฐ์์ฌ ์ ์์ผ๋ฉฐ, ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํด ์ฌ๋ฌ ๊ฐ์ atom์ ๊ฐ์ ธ์ฌ ์ ์์
- returnํ๋ ๊ฐ์ ํด๋น selector์ value๊ฐ ๋จ
- get
- ์ฌ์ฉ๋ฒ : const ๋ณ์๋ช = useRecoilValue(์ ๋ ํฐ๋ช );
- atom์ด ๋ณํ๋ฉด, selector๋ ๋ณํจ
- Recoil - Selector์ 'set' ์์ฑ
- selector์ 'set' ์์ฑ์ ์ฌ์ฉํ์ฌ, selector์์ atom์ state๋ฅผ ์์ ํ ์ ์์
- ๊ธฐ๋ณธํ
ย ย set: ({ set }, ์๋ก์ด๊ฐ) => {
ย ย ......
ย ย set(์ํฐ๋ช
, ์์ ํ ๊ฐ); // ์๋ก์ด ๊ฐ๊ณผ ์์ ํ ๊ฐ์ ๊ฐ์ ํ์ x
}
- ์ฌ๋ฌ ๊ฐ์ atom์ ๋ํด 'set()'ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์
- 'useRecoilState()'๋ก selector๋ฅผ ๋ถ๋ฌ์์ ์ฌ์ฉํ ์ ์์
- ์ฒซ ๋ฒ์งธ ์์ : get ํ๋กํผํฐ๋ก๋ถํฐ returnํ ๊ฐ
- ๋ ๋ฒ์งธ ์์ : set ํ๋กํผํฐ๋ก๋ฅผ ์คํ์ํค๋ ํจ์
- 'useRecoilState()'๋ฅผ atom๊ณผ selector ๋๋ค์์ ์ฌ์ฉ ๊ฐ๋ฅ
- enum
- ์ด๊ฑฐํ(enumeration)์ผ๋ก ์ ์ํ๋ ๋ฐ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ ํ์
- ๊ธฐ๋ณธํ: enum ๋ณ์๋ช
{ ๊ฐ1, ๊ฐ2, ๊ฐ3, ... }
- ๋ณ์๋ช ์ ๋๋ฌธ์๋ก ์์
- ์ปดํจํฐ๋ ์ด๊ฑฐ ์์๋ก ์ฌ์ฉํจ
- ์ด๊ฑฐ ์์๋ 0๋ถํฐ ์์ํ์ฌ ์์๋๋ก 1์ฉ ์ฆ๊ฐํ ๊ฐ์ ๊ฐ์ง๋ฉฐ, ์์๋ฅผ ๋ฐ๋ก ์ง์ ํ์ง ์๋ ํ ์๋์ผ๋ก ํ ๋น๋จ (์๋ ํ ๋น ๊ฐ๋ฅ, ์ซ์๋ ๋ฌธ์์ด ๊ฐ๋ฅ)
- ์ง์ ๊ฐ์ ๊ฐ์ผ๋ก ์ฃผ์ด์ ์ฌ์ฉํ ์ ์์ (ex. enum Categories { "TO_DO" = "TO_DO" })
- ์ฌ์ฉ ์ ํ๋กํผํฐ ๋ฐฉ์์ผ๋ก ์ฌ์ฉ (ex. Categories.TO_DO)
- Recoil-Persist ํจํค์ง
- Recoil ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Local Storage๋ฅผ ํตํฉํ์ฌ, Recoil ์ํ๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๊ณ ๋ณต์ํ๋ ๋ฐ ๋์์ ์ฃผ๋ ํจํค์ง
- ๋ธ๋ผ์ฐ์ ์ Local Storage๋ฅผ ํ์ฉํ์ฌ, Recoil ์ํ๋ฅผ ์ง์์ ์ผ๋ก ์ ์งํ ์ ์์
- ์ค์น๋ฒ : 'npm i recoil-persist'
- ์ค์ ๋ฒ
- atom ํ์ผ์์ 'recoilPersist' ๊ฐ์ฒด ์์ฑํ๊ธฐ
- const { persistAtom } = recoilPersist();
- 'recoilPersist()'์ ์ต์
- key : Local Storage์ ๋ฐ์ดํฐ ์ ์ฅ ์ ์ฌ์ฉ๋๋ key๋ช
- ๊ธฐ๋ณธ๊ฐ : "recoil-persist"
- storage : ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์ฅ์ ์ค์
- ๊ธฐ๋ณธ๊ฐ : localStorage
- converter : ์ ์ฅ์์์ ๊ฐ์ ์ง๋ ฌํ/์ญ์ง๋ ฌํํ๋ ๋ฐฉ๋ฒ์ ๊ตฌ์ฑ
- ๊ธฐ๋ณธ๊ฐ : JSON
- key : Local Storage์ ๋ฐ์ดํฐ ์ ์ฅ ์ ์ฌ์ฉ๋๋ key๋ช
- 'recoilPersist()'์ ์ต์
- const { persistAtom } = recoilPersist();
- Local Storage๋ฅผ ์ฌ์ฉํ๊ณ ์ ํ๋ atom์ 'effect_UNSTABLE' ์์ฑ ํ ๋นํ๊ธฐ
- 'effect_UNSTABLE: [persistAtom]' ์์ฑ๋ง ์ถ๊ฐํ๋ฉด ๋จ
- atom ํ์ผ์์ 'recoilPersist' ๊ฐ์ฒด ์์ฑํ๊ธฐ
- ์ค์ ๋ง ํด๋์ผ๋ฉด Local Storage์ ์ ์ฅ(set)๊ณผ ๋ถ๋ฌ์ค๊ธฐ(get)๋ฅผ ์๋์ผ๋ก ์คํํจ
- Recoil ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Local Storage๋ฅผ ํตํฉํ์ฌ, Recoil ์ํ๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๊ณ ๋ณต์ํ๋ ๋ฐ ๋์์ ์ฃผ๋ ํจํค์ง
- Recoil - Selector
- 23-09-06 : #7.2 ~ #7.4 / React-Beautiful-Dnd(1)
- react-beautiful-dnd ํจํค์ง (@hello-pangea/dnd)
- ํ๋ฉด์์ drag&drop์ ํ ์ ์๊ฒ ํด์ฃผ๋ ํจํค์ง
- ์ค์น๋ฒ : 'npm i @hello-pangea/dnd'
- 'react-beautiful-dnd'๋ ์ข ์์ฑ ๋ฌธ์ ๋๋ฌธ์ React 18๋ฒ์ ์์ ์ ๋๋ก ๋์ํ์ง ์๊ณ , ํด๋น ํจํค์ง๊ฐ ๋ ์ด์ ์๋์ด๋ฏ๋ก ๋น์ถ์ฒ
- ์น ํ์ด์ง์์ ์๋์ด ๋์ง ์์ ์ 'React.StrictMode'๋ฅผ ์ ๊ฑฐํ ๊ฒ
- <DragDropContext />
- drag&drop์ ๊ฐ๋ฅํ๊ฒ ํ๊ณ ์ถ์ ์ฑ์ ํ ๋ถ๋ถ(์์ญ)
- ์ฑ ์ ์ฒด๊ฐ ์๋ ์ฌ์ฉ์๊ฐ drag&drop์ ํ ํน์ ์์ญ์๋ง ์์ฑํ๋๋ก ํจ
- ๊ธฐ๋ณธํ : <DragDropContext onDragEnd={์ฝ๋ฐฑํจ์}> ... </DragDropContext>
- onDragEnd : [ํ์] ์ฌ์ฉ์๊ฐ drag๋ฅผ ๋๋ธ ์์ ์ ์คํํ๋ ์ฝ๋ฐฑํจ์
- ์ฝ๋ฐฑํจ์์ ๋ด์ฉ์ด ์๋ค๋ฉด, drag&drop์ด ๋๋ ์ ์๋๋๋ก ๋์์ด
- [ํ์] ํ๊ทธ ์ฌ์ด์๋ children(ํ์ ํ๊ทธ)์ด ์์ด์ผ ํจ
- onDragEnd : [ํ์] ์ฌ์ฉ์๊ฐ drag๋ฅผ ๋๋ธ ์์ ์ ์คํํ๋ ์ฝ๋ฐฑํจ์
- drag&drop์ ๊ฐ๋ฅํ๊ฒ ํ๊ณ ์ถ์ ์ฑ์ ํ ๋ถ๋ถ(์์ญ)
- <Droppable />
- ์ด๋ค ๊ฒ์ ๋๋กญํ ์ ์๋ ์์ญ(list)
- ๊ธฐ๋ณธํ : <Droppable droppableId="์ด๋ฆ"> ... </Droppable>
- droppableId : [ํ์] dropํ ์ ์๋ ์์ญ์ด ์ฌ๋ฌ ๊ฐ์ผ ์ ์๊ธฐ ๋๋ฌธ
- [ํ์] ํ๊ทธ ์ฌ์ด์๋ children(์ฝ๋ฐฑํจ์ JSX)์ด ์์ด์ผ ํจ
- ์ฝ๋ฐฑํจ์์ ์ฒซ ๋ฒ์งธ ์ธ์๋ก 'provided'๋ฅผ ๋ฐ์ (์์น๋ง ์ค์ํ์ง, ์ด๋ฆ์ ์๋ฌด๊ฑฐ๋ ๊ฐ๋ฅ)
- ์ฝ๋ฐฑํจ์์์ ์ฌ์ฉ๋๋ JSX์ 'provided.droppableProps'์ ๋ชจ๋ ํ๋กํผํฐ์ 'ref'์์ฑ์ผ๋ก 'provided.innerRef'๋ฅผ ์ฃผ์ด์ผ ํจ
- ex. {(provided) => <ul ref={provided.innerRef} {...provided.droppableProps}> ... </ul>}
- 'provided.placeholder' : </Droppable>์ด ๋๋ ๋ ๋์ด, drag&drop ์ <Droppable>์ ์ฌ์ด์ฆ๊ฐ ์ด์ํ๊ฒ ๋ณํ๋ ๊ฒ์ ๋ง์
- <Droppable>์ ํ์ ์ฝ๋ฐฑํจ์์์ ์ฌ์ฉ๋ JSX์ ๋ซํ ํ๊ทธ ๋ฐ๋ก ์์์ ์ฌ์ฉ
- <Draggable />
- ์ฌ์ฉ์๊ฐ dragํ ์ ์๋ ์์ญ(list์ ์๋ item)
- ๊ธฐ๋ณธํ : <Draggable draggableId="์ด๋ฆ" index={์ธ๋ฑ์ค}> ...... </Draggable>
- draggableId : [ํ์] dropํ ์ ์๋ ์์ญ์ด ์ฌ๋ฌ ๊ฐ์ผ ์ ์๊ธฐ ๋๋ฌธ
- 'key'์์ฑ ์ฌ์ฉ ์ 'draggableId'์ ์์ฑ๊ฐ๊ณผ ๊ฐ์์ผ ํจ (drag&drop ํ ์ ์๊ธฐ ๋๋ฌธ)
- index : [ํ์] sorting์ ์ํ ํ๋กํผํฐ
- [ํ์] ํ๊ทธ ์ฌ์ด์๋ children(์ฝ๋ฐฑํจ์ JSX)์ด ์์ด์ผ ํจ
- ์ฝ๋ฐฑํจ์์ ์ฒซ ๋ฒ์งธ ์ธ์๋ก 'provided'๋ฅผ ๋ฐ์ (์์น๋ง ์ค์ํ์ง, ์ด๋ฆ์ ์๋ฌด๊ฑฐ๋ ๊ฐ๋ฅ)
- provided.draggableProps : ํด๋น element์ drag ๊ธฐ๋ฅ
- provided.dragHandleProps : dragํ๊ธฐ ์ํด ์ก์ ์ ์๋ ์์ญ
- ํด๋น ํ๋กํผํฐ๋ฅผ ์ค element๋ฅผ drag ํด์ผํจ
- ์ฝ๋ฐฑํจ์์์ ์ฌ์ฉ๋๋ JSX์ 'provided.draggableProps'์ ๋ชจ๋ ํ๋กํผํฐ์ 'provided.dragHandleProps'์ ๋ชจ๋ ํ๋กํผํฐ์ 'ref'์์ฑ์ผ๋ก 'provided.innerRef'๋ฅผ ์ฃผ์ด์ผ ํจ
- ex. {(provided) => <li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}> ...... </li>}
- draggableId : [ํ์] dropํ ์ ์๋ ์์ญ์ด ์ฌ๋ฌ ๊ฐ์ผ ์ ์๊ธฐ ๋๋ฌธ
- react-beautiful-dnd ํจํค์ง (@hello-pangea/dnd)
- 23-09-07 : #7.5 ~ #7.11 / React-Beautiful-Dnd(2)
- <DragDropContext> - 'onDragEnd'
- 'onDragEnd' ํ๋กํผํฐ๋ drag&drop์ด ๋๋ ๋ ์คํํ๋ ํจ์
- ์ด๋ค ์ผ์ด ์ผ์ด๋ฌ๋์ง์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ argument๋ฅผ ์ ๊ณตํจ
- 'onDragEnd'์ ์ธ์์ ํ์ ์ 'DropResult' (ํ์ ๋ฌธ์ ์ฐธ์กฐ)
- draggableId : drag&drop์ ํ element๋ฅผ ์๋ ค์ค
- destination : ๋ชฉ์ ์ง์ ์ ๋ณด { droppableId, index }
- source : ์ถ๋ฐ์ง์ ์ ๋ณด { droppableId, index }
- drag&drop์ ํ element๋ฅผ ์ ์ฅํ ๋ฐฐ์ด์์๋ ๋๊ฐ์ด ๋ง๋ค์ด์ฃผ์ด์ผ ํจ
- ๋ฐฐ์ด์์ dragํ element๋ฅผ ์ญ์ ํ๊ธฐ
- 'source.index'๋ฅผ ์ฐธ์กฐํด ์ญ์
- ๋ฐฐ์ด.splice(์์์ธ๋ฑ์ค, ์ญ์ ํ ๊ฐฏ์) : ํด๋น item์ ๋ฐฐ์ด์์ ์ญ์ ํ๋ ๋ฉ์๋
- ์๋ณธ ๋ฐฐ์ด์ด ๋ฐ๋๋ mutation์ด ์ผ์ด๋๋ฏ๋ก, ๋จผ์ ์๋ณธ์ ๋ณต์ฌํ ํ ์ฌ์ฉํด์ผ ํจ
- ๋ฐฐ์ด ๋ณต์ฌ ์ ์คํ๋ ๋ ์ฐ์ฐ์(...)๋ฅผ ์ฌ์ฉํ '[...๋ฐฐ์ด๋ช
]' ๋ฐฉ์์ผ๋ก ๋ณต์ฌํด์ผ ํจ
- ๋ฐฐ์ด์ ๊ทธ๋ฅ ๋ณต์ฌ ์ ์ฃผ์๊ฐ๋ง ์ฐ๊ฒฐ๋๊ธฐ ๋๋ฌธ
- ์ญ์ ํ element๋ฅผ ๋ฐฐ์ด์์ dropํ ์์น์ ์์ฑํ๊ธฐ
- 'destination.index'์ 'draggableId'๋ฅผ ์ฐธ์กฐํด ์์ฑ
- ๋ฐฐ์ด.splice(์์์ธ๋ฑ์ค, ์ญ์ ํ ๊ฐฏ์, ์์ดํ ) : ์ญ์ ํ ๊ฐฏ์๋ฅผ 0์ผ๋ก ํ๊ณ ์์ดํ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฃ์ด์ฃผ๋ฉด, ๋ฐฐ์ด์ ํด๋น ์ธ๋ฑ์ค์ ์์ดํ ์ ์ถ๊ฐํ ์ ์์
- 'destination'์ด ์์ ์๋ ์์ (๊ฐ์ ์๋ฆฌ์ drag&drop์ ํ ์๋ ์๊ธฐ ๋๋ฌธ)
- ๋ฐฐ์ด์์ dragํ element๋ฅผ ์ญ์ ํ๊ธฐ
- ๋ฌธ์ ๋ฐ์ : drag&drop ์ ๊ฐ๋ content๊ฐ ๋จ๋ฆฌ๋ ํ์ (์ต์ ํ ๋ฌธ์ )
- ์ด์ : ๋ถ๋ชจ component์ state๊ฐ ๋ณํ๋ฉด, ์์ component๋ค๊น์ง ์ ๋ถ re-rendering๋๊ธฐ ๋๋ฌธ (React์ ๊ธฐ๋ณธ ๋์ ๋ฐฉ์)
- dragํ๋ component ์ด์ธ์๋, ํ์ component ๋ฑ ์ ๋ถ๊ฐ re-rendering ๋จ
- console.log()๋ก ํ์ธ ๊ฐ๋ฅ
- ํด๊ฒฐ : 'React.memo()'๋ฅผ ์ฌ์ฉํด ํด๊ฒฐ
- ํด๋น component์ prop์ด ๋ณํ์ง ์์๋ค๋ฉด, re-renderingํ์ง ์๋๋ก ํจ
- 'export default React.memo(์ปดํฌ๋ํธ๋ช );'์ ์ฌ์ฉ
- ์ด์ : ๋ถ๋ชจ component์ state๊ฐ ๋ณํ๋ฉด, ์์ component๋ค๊น์ง ์ ๋ถ re-rendering๋๊ธฐ ๋๋ฌธ (React์ ๊ธฐ๋ณธ ๋์ ๋ฐฉ์)
- ๋ค์์ ๋ณด๋(<Droppable>) ์ฌ์ฉํ๊ธฐ
- ์ค๋น ๊ณผ์
- ๋ค์์ ๋ณด๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด, atomState๋ฅผ ์ฌ๋ฌ ๊ฐ์ Array๋ฅผ ๊ฐ์ง๋ Object๋ก ์ฌ์ฉํ๊ธฐ
- '.map()'์ ์ฌ์ฉํด ์ฌ๋ฌ ๊ฐ์ ๋ณด๋๋ฅผ renderingํ๊ธฐ
- 'Object.keys(๊ฐ์ฒด๋ช
)'์ผ๋ก ํด๋น ๊ฐ์ฒด์ key๋ช
๋ค์ ๋ฐฐ์ด๋ก ๋ฝ์๋ผ ์ ์์
- 'Object.keys(๊ฐ์ฒด๋ช ).map(ํค๋ช => ๊ฐ์ฒด๋ช [ํค๋ช ])'์ ์ฌ์ฉํด ๊ฐ key์ ๊ฐ๋ค์ ๋ฝ์๋ผ ์ ์์
- ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ ๊ฐ์ ๋ณด๋๋ฅผ ์์ฑ/์ญ์ ํ ์ ์์ผ๋, interface๋ฅผ ์ค์ ํด์ค์ผ ํจ
- ex. interface IToDoState { [key: string]: string[]; }
- 'Object.keys(๊ฐ์ฒด๋ช
)'์ผ๋ก ํด๋น ๊ฐ์ฒด์ key๋ช
๋ค์ ๋ฐฐ์ด๋ก ๋ฝ์๋ผ ์ ์์
- drag&drop์ ์ฌ์ฉํ๋ ค๋ฉด, ํ์ฌ ์์น์ Array์์ ์ ๊ฑฐํ ํ ์๋ก์ด ์์น์ Array์ ๋ถ์ฌ๋๊ธฐ
- 'source'์ 'destination'์ ์ฐธ์กฐ ๊ฐ๋ฅ
- source์ ๋ณด๋์ destination์ ๋ณด๋๊ฐ ๊ฐ์์ง ์ฒดํฌํ๊ธฐ
- ๊ฐ๊ฐ '.droppableId'๋ฅผ ํตํด ์ฐธ์กฐ ๊ฐ๋ฅ
- ๊ฐ์ ๋ณด๋์์ ์ด๋์ด ์ผ์ด๋ ์
- ์์ ์ด ์ผ์ด๋ ๋ณด๋๋ง ๋ณต์ฌํ๊ธฐ
- ๋ณต์ฌํ ๋ณด๋๋ฅผ ์์ ํ ํ, ๋ค๋ฅธ ๋ณด๋๋ค ์์ ๋๊ธฐ
- ๋ค๋ฅธ ๋ณด๋๋ก ์ด๋์ด ์ผ์ด๋ ์
- ์ถ๋ฐ์ง ๋ณด๋์ ๋์ฐฉ์ง ๋ณด๋๋ฅผ ๋ณต์ฌํ๊ธฐ
- ๋ณต์ฌํ ๋ณด๋๋ค์ ์์ ํ ํ, ๋ค๋ฅธ ๋ณด๋๋ค ์์ ๋๊ธฐ
- ๋ค๋ฅธ ๋ณด๋๋ค ์์ ๋๊ธฐ ์ํด ์คํ๋ ๋์ฐ์ฐ์(...)์ computed property name์ ์ด์ฉํด์ผ ํ๋ฉฐ(ES6), ์ ์ฒด ๊ฐ์ฒด๊ฐ ๋จผ์ ์ฌ์ฉ๋์ด์ผ ํจ
- ex. return { ...allBoards, [source.droppableId]: boardCopy }
- ์ค๋น ๊ณผ์
- ๋ฌธ์ ๋ฐ์ : item์ ๊ฐ์ง๊ณ ์์ง ์๋ ๋ณด๋์๋ ํด๋น ๋ณด๋ ๋งจ ์๋ก ์ด๋ํด์ผ์ง๋ง item์ ๋ฐ์ ์ ์๋ ๋ถํธํจ
- CSS๋ก ๋ฐ๋ ๋ถ๋ถ(<Droppable>์ ํ์ element)์ ์์ญ์ ๋ถ๋ชจ element์ ๊ฐ๋ ์ฐจ๊ฒ๋ ๋ง๋ค๊ธฐ
- ํด๊ฒฐ ๋ฐฉ๋ฒ
- ๋ถ๋ชจ element์ 'display: flex;' CSS๋ฅผ ๋ถ์ฌํ๊ธฐ
- ์์ element์ 'flex-grow: 1;' CSS๋ฅผ ๋ถ์ฌํ๊ธฐ
- ์์ element์ ์์ญ์ด ๋ถ๋ชจ element์ ๊ฐ๋ ์ฐจ๊ฒ๋ ๋ง๋ฆ
- <Droppable>, <Draggable> - snapshot
- <Droppable> ๋๋ <Draggable>์ children์ผ๋ก ์ฌ์ฉ๋๋ ์ฝ๋ฐฑํจ์์ ๋ ๋ฒ์งธ ์ธ์๋ก 'snapshot'์ ์ฌ์ฉํ ์ ์์
- ๋ ๋ฒ์งธ ์๋ฆฌ๊ฐ ์ค์ํจ, ์ด๋ฆ์ ์๋ฌด๊ฑฐ๋ ์๊ด์์
- 'snapshot'์ ํ๋กํผํฐ๋ ํ์ ์ ์ ํ์ผ์์ ํ์ธ ๊ฐ๋ฅ
- isDraggingOver : ๋ณด๋๋ก ๋ค์ด์ฌ ๋์ dragging์ ๋ํ boolean๊ฐ์ ๋ฐ์
- ์ฌ์ฉ์๊ฐ ๋ณด๋ ์๋ก dragํด์ ๋ค์ด์ค๊ณ ์๋์ง ์๋ ค์ค
- draggingFormThisWith : ์ฌ์ฉ์๊ฐ ํด๋น ๋ณด๋๋ก๋ถํฐ drag๋ฅผ ์์ํ๋์ง๋ ์๋ ค์ค
- ์ฌ์ฉ์๊ฐ ์ด๋ค ๋ณด๋๋ฅผ ๋ ๋๋ค๋ฉด, ๊ทธ ๋ณด๋๋ก๋ถํฐ drag๋ฅผ ์์ํ๋ค๋ ๋ป
- DraggableId | undefined
- 'Boolean(draggingFormThisWith)'๋ก ์ฌ์ฉ ์ dragํ์ฌ ํด๋น ๋ณด๋๋ฅผ ๋ ๋๋ฉด true
- isDraggingOver : ๋ณด๋๋ก ๋ค์ด์ฌ ๋์ dragging์ ๋ํ boolean๊ฐ์ ๋ฐ์
- ์ฌ์ฉ๋ฒ : <Droppable>์ ์ฝ๋ฐฑํจ์ JSX element์์ ํ๋กํผํฐ๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์ฉ
- element๋ snapshot์ ํ๋กํผํฐ๋ฅผ ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์, ํ์ ์ ์๋ฅผ ํด์ฃผ์ด์ผ ํจ
- ํ๋กํผํฐ๋ฅผ ์ด์ฉํด styled-components๋ก CSS ์ ์ฉ ๊ฐ๋ฅ
- <Droppable> ๋๋ <Draggable>์ children์ผ๋ก ์ฌ์ฉ๋๋ ์ฝ๋ฐฑํจ์์ ๋ ๋ฒ์งธ ์ธ์๋ก 'snapshot'์ ์ฌ์ฉํ ์ ์์
- <DragDropContext> - 'onDragEnd'
- 23-09-08 : #7.12 ~ #7.16 / React-Beautiful-Dnd(3) + Code Challenge(1)
- React - ref ์์ฑ
- React.js component๋ฅผ ํตํด HTML element๋ฅผ ๊ฐ์ ธ์์ ๊ทธ๊ฑธ ๋ณํ์ํฌ ์ ์๋๋ก ํด์ค
- aka. vanilla JS์ document.getElementById()
- ๊ธฐ๋ณธํ
const ๋ณ์๋ช
= useRef<์ ๋ค๋ฆญ>(null);
<ํ๊ทธ๋ช
ref={๋ณ์๋ช
} />
- ๋ณดํต 'useRef()'์ ์ธ์๋ก null๊ฐ์ ์ฌ์ฉํจ (์ฐธ์กฐํ element๋ฅผ ์ด๊ธฐํํ ํ ์ฌ์ฉํ๊ธฐ ์ํจ)
- React.js component๋ฅผ ํตํด HTML element๋ฅผ ๊ฐ์ ธ์์ ๊ทธ๊ฑธ ๋ณํ์ํฌ ์ ์๋๋ก ํด์ค
- To-Do์ ํ์
๊ต์ฒด
- 'string[]'์์ '{ id:number, text: string }[]'์ธ ํ์ ์ผ๋ก ๊ต์ฒด
- ๋ฌธ์ ๋ฐ์ : 'onDragEnd'์์ 'draggableId'๋ฅผ ์ฐธ์กฐํด ์ฌ๋ฐฐ์ด ํ ๊ฒ์ ๋ฐ๊พธ์ด์ค์ผ ํจ
- ๊ธฐ์กด์ string[] ํ์ ์์๋ draggableId = item ์ด์ด์ ์ฌ์ ์
- ์ง๊ธ์ draggableId๋ stringํ์ ์ id ํ๋กํผํฐ ๊ฐ์
- ํด๊ฒฐ ๋ฐฉ๋ฒ : '.index' ํ๋กํผํฐ๋ฅผ ์ด์ฉํด์ item์ ์ฐพ๊ธฐ
- 'source.index'๋ฅผ ํตํด ํ๊ฒ(Object)์ ์ฐพ์ ํ ์ฌ์ฉ
- React - ref ์์ฑ
- 23-09-09 ~ 10 : Code Challenge(2)
- 23-09-12 : #8.0 ~ #8.6 / Animations(1)
- Framer-Motion ํจํค์ง
- ์ ๋๋ฉ์ด์
์ ๋ง๋ค ์ ์๋ React.js์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- CSS transition ์ฝ๋ ์์ด ์๋ํ๋ ์ ๋๋ฉ์ด์
- ์ค์น๋ฒ : 'npm i framer-motion'
- ์ค์ ๋ฒ : 'import { motion } from "framer-motion";'
- ๊ท์น : 'motion'์ ํ๋กํผํฐ๋ฅผ HTMLํ๊ทธ๋ช
์ผ๋ก ์ฌ์ฉํด์ผ์ง ์ ๋๋ฉ์ด์
์ ๋ง๋ค ์ ์์
- ๊ธฐ๋ณธํ : <motion.HTMLํ๊ทธ๋ช >
- 'styled-components'์์ motion ์ ๋๋ฉ์ด์
ํ๊ทธ ์์ฑ๋ฒ
- ๊ธฐ๋ณธํ : const ์ปดํฌ๋ํธ๋ช = styled(ํ๊ทธ๋ช )` ... `;
- ์ ๋๋ฉ์ด์
์ ๋ง๋ค ์ ์๋ React.js์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- motion์ props
- ๊ธฐ๋ณธ์ ์ผ๋ก 'style'ํ๋กํผํฐ์ฒ๋ผ ์ฐ์ํ๋ 2๊ฐ์ ์ค๊ดํธ๋ฅผ ์ฌ์ฉ ({{, }})
- initial : element์ ์ด๊ธฐ ์ํ๋ฅผ ๋ํ๋
- animate : element์ ์ต์ข ์ํ๋ฅผ ๋ํ๋
- transition : ํธ๋์ง์
ํ๋กํผํฐ
- delay : n์ด ํ์ ์ ๋๋ฉ์ด์ ๋ฐ์ [seconds]
- duration : n์ด ๋์ ์ ๋๋ฉ์ด์ ๋ฐ์ [seconds]
- ์ ๋๋ฉ์ด์
๋ง๋ค๊ธฐ
- ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด์ง ํ๊ธฐ๋ ๋ฏํ ๋๋์ผ๋ก 'type: "spring"'์ด ์ ์ฉ๋์ด ์์
- stiffness : [number] ๊ฒฝ์ง๋จ
- damping : [number] ๋ฐ๋๋ ฅ
- bounce : ํ๊ธฐ๋ ์ ๋
- mass : ๋ฌผ์ฒด์ ์ง๋
- 'type: "tween"' : ์ ํ์ (linear)
- ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด์ง ํ๊ธฐ๋ ๋ฏํ ๋๋์ผ๋ก 'type: "spring"'์ด ์ ์ฉ๋์ด ์์
- Variants
- ์ ๋๋ฉ์ด์ ์ค์ ์ ๋ถ๋ฆฌ๋ Object๋ก ์ฎ๊ฒจ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- ์ด๊ธฐ ์ํ์ ์ต์ข
์ํ๋ฅผ ๋ช
์ํด์ผ ํจ
- ์ด๋ฆ์ ์๋ฌด๊ฑฐ๋ ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง, ์ฌ์ฉ ์ ์ด๋ฆ์ด ๊ฐ์์ผ ํจ
- ์ต์ข ์ํ์ ์ต์ ์ผ๋ก 'transition'์ ๋ฃ์ด์ค ์ ์์
- Variants์ ํ๋กํผํฐ๋ฅผ ๋ ์ถ๊ฐํ ์ ์์ผ๋ฉฐ, :hover ๋ฑ์ ์ฌ์ฉํ ์ ์์
- ๊ธฐ๋ณธํ const ๋ณ์๋ช = { ย ย ์ด๊ธฐ์ํ๋ช : { ... }, ย ย ์ต์ข ์ํ๋ช : {}, } <ํ๊ทธ๋ช variants={๋ณ์๋ช } initial="์ด๊ธฐ์ํ๋ช " animate="์ต์ข ์ํ๋ช ">
- Variants - ์์ ์ปดํฌ๋ํธ
- ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ variants, initial, animate๋ฅผ ๊ฐ๊ณ ์์ ๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์์ ์ปดํฌ๋ํธ์๋ ์ ์ฉ๋จ
- ์๋์ผ๋ก ๋ถ๋ชจ์ initial, animate์ ๊ฐ์ ์์๋ฐ์
- ์์ ์ปดํฌ๋ํธ์ ์๋ก์ด variants ์ ์ฉ ์ variants๋ง ์ ์ด์ฃผ๋ฉด ๋จ
- ๋จ, ์ํ๋ช
์ ๋ถ๋ชจ variants์ ๊ฐ์ ์ด๋ฆ์ ์ฌ์ฉํด์ผ ํจ
- ์๋์ผ๋ก initial, animate๋ฅผ ๋ณต๋ถํ๊ธฐ ๋๋ฌธ
- ๋จ, ์ํ๋ช
์ ๋ถ๋ชจ variants์ ๊ฐ์ ์ด๋ฆ์ ์ฌ์ฉํด์ผ ํจ
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ์ 'delay'๋ฅผ ์กฐ์ ๊ฐ๋ฅ
- delayChildren : ์์ ์ปดํฌ๋ํธ๋ค์ delay ์๊ฐ [seconds]
- staggerChildren : ๊ฐ๊ฐ์ ์์ ์ปดํฌ๋ํธ๋ค์ delay ์์ฐจ [seconds]
- ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ variants, initial, animate๋ฅผ ๊ฐ๊ณ ์์ ๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์์ ์ปดํฌ๋ํธ์๋ ์ ์ฉ๋จ
- event์ ๋ฐ๋ฅธ animation
- 'while์ด๋ฒคํธ๋ช
' ์์ฑ์ ์ฌ์ฉ
- { whileHover, whileTap, whileDrag, whileFocus, whileInView }
- variants์์ ์ํ๋ช ํค-๊ฐ์ ์ฌ์ฉ ๊ฐ๋ฅ
- 'while์ด๋ฒคํธ๋ช
' ์์ฑ์ ์ฌ์ฉ
- Dragging (๋๋๊น
)
- ์ฌ์ฉ๋ฒ : ์ปดํฌ๋ํธ์ 'drag'์์ฑ์ ๋ถ์ฌํ๋ฉด ๋จ
- 'drag="x"' : x์ถ์ผ๋ก๋ง drag ๊ฐ๋ฅ
- 'drag="y"' : y์ถ์ผ๋ก๋ง drag ๊ฐ๋ฅ
- whileDrag : dragํ๋ ๋์ ๋ํ๋๋ animation ์์ฑ
- ์์ ์ฌ์ฉํ ๋ number๊ฐ(rgb, hex ๋ฑ)์ ์ฌ์ฉํด์ผ์ง ์๋์ผ๋ก transition์ด ์๋ํจ
- ๊ทธ๋ ์ง ์์ผ๋ฉด(string ๋ฑ), ์ฆ๊ฐ์ ์ผ๋ก ์์ด ๋ณํจ
- ์์ ์ฌ์ฉํ ๋ number๊ฐ(rgb, hex ๋ฑ)์ ์ฌ์ฉํด์ผ์ง ์๋์ผ๋ก transition์ด ์๋ํจ
- dragConstraints : drag๊ฐ ํ์ฉ๋ ์ ์๋ ์์ญ
- { top, bottom, left, right }
- ์ ํ๋ ์์ญ์ ๋์ด์๋ฉด, ์์ญ ๋ด๋ก ๋๋์๊ฐ
- dragSnapToOrigin : drag๊ฐ ๋๋๋ฉด ์ฒ์ ์์น๋ก ๋๋์๊ฐ๋ ์์ฑ
- dragElastic : drag ์์ ๋น๊ธฐ๋ ํ
- 0~1 ์ฌ์ด์ ๊ฐ์ ๊ฐ์ง (๊ธฐ๋ณธ๊ฐ 0.5)
- 0 : drag์์๊ฐ ์ ํ๋ ์์ญ ๋ฐ์ผ๋ก ๋๊ฐ ์ ์์
- 1 : drag์์์ ๋ง์ฐ์ค ์ปค์๊ฐ ๊ฐ์ ์์น๋ก dragํจ
- ์ ํ๋ ์์ญ ๋ด์์๋ง dragging์ ํ์ฉ์ํค๋ ๋ฐฉ๋ฒ
- ์ ํ๋ ์์ญ์ ๊ฐ์ง๋ div ์ปดํฌ๋ํธ ๋ด์ dragํ ์ ์๋ ์ปดํฌ๋ํธ ์์ฑํ๊ธฐ
- ์ ํ๋ ์์ญ(๋ถ๋ชจ) ์ปดํฌ๋ํธ์ CSS 'overflow: hidden' ์์ฑ ๋ถ์ฌํ๊ธฐ
- 'overflow: hidden' : ํ์ ์์๊ฐ ๋ถ๋ชจ ์์์ ๋ด์์๋ง ๋ณด์ด๋๋ก ํจ (์ด๊ณผ๋ ๋ถ๋ถ์ ๋ณด์ด์ง ์์)
- ์ ํ๋์์ญ ๋ด์์๋ง dragํ ์ ์๋๋ก ์ ์ฝ์ ๋ฃ๊ธฐ
- ์ํ์ ๋ฐฉ๋ฒ
- ex. ์ ํ์์ญ=600x600 / ๋๋๊ทธ์์=200x200 dragConstraints={{ top: -200, bottom: 200, left: -200, right: 200 }}
- 'ref' ์์ฑ์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ
- ์ ํ๋ ์์ญ์ 'ref'๋ฅผ ์ค์ ํ ํ, ๋๋๊น ์์์ 'dragConstraints'์ ์ ํ๋ ์์ญ์ ref๋ฅผ ํ ๋น
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช = useRef<์ ๋ค๋ฆญ>(null);
- ์ํ์ ๋ฐฉ๋ฒ
- ์ฌ์ฉ๋ฒ : ์ปดํฌ๋ํธ์ 'drag'์์ฑ์ ๋ถ์ฌํ๋ฉด ๋จ
- Framer-Motion ํจํค์ง
- 23-09-14 : #8.7 ~ #8.13 / Animations(2)
- Motion Value
- ์ ๋๋ฉ์ด์
๋ด์ ์์น๋ฅผ trackingํ ๋ ํ์ํ ๋ณ์
- dragging์ ์ผ์ชฝ/์ค๋ฅธ์ชฝ์ ๋ฐ๋ผ ๋ฐฐ๊ฒฝ์์ ๋ฐ๊พธ๋ ๋ฑ
- Motion Value๊ฐ ๋ฐ๋์ด๋, ์ปดํฌ๋ํธ๊ฐ re-renderingํ์ง ์์ (state๊ฐ์ด ์๋๊ธฐ ๋๋ฌธ)
- ์ฌ์ฉ๋ฒ
const ๋ณ์๋ช
= useMotionValue(์ด๊ธฐ๊ฐ);
<ํ๊ทธ๋ช
style={{ ์์ฑ: ๋ณ์๋ช
}} />
- ๋ณ์๋ช .get() : ํ์ฌ ๊ฐ์ ๋ถ๋ฌ์ค๋ ๋ฉ์๋
- ๋ณ์๋ช .set() : ํด๋น ๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฉ์๋
- ex. const x = useMotionValue(); <button onClick={() => x.set(200)}>Click me</button> <Box style={{ x }} drag="x" />
- ์ ๋๋ฉ์ด์
๋ด์ ์์น๋ฅผ trackingํ ๋ ํ์ํ ๋ณ์
- useMotionValueEvent()
- 'motion value'๊ฐ์ ์์๋ณด๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฉ์๋
- ๊ธฐ๋ณธํ : useMotionValueEvent(๋ณ์๋ช
, ์ด๋ฒคํธ๋ช
, ์ฝ๋ฐฑํจ์);
- ์ด๋ฒคํธ๋ช ์ค "change"๋ ์ฝ๋ฐฑํจ์์ ์ธ์๋ก 'latest'๊ฐ(= .get()) ์ ๊ณต
- useTransform()
- 'motion value' ์
๋ ฅ ๋ฒ์๋ฅผ ์ถ๋ ฅ ๋ฒ์๋ก ๋งคํํ๋ ๋ฐ ์ฌ์ฉํ๋ ๋ฉ์๋
- ์ ๋๋ฉ์ด์ ๊ณผ ์ํธ์์ฉ์ ๋์ ์ผ๋ก ์ ์ดํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉใ
- ๊ธฐ๋ณธํ : const ๋ณ์๋ช
= useTransform(๋ชจ์
๊ฐ๋ณ์๋ช
, ์
๋ ฅ๋ฐฐ์ด, ์ถ๋ ฅ๋ฐฐ์ด);
- ๋ชจ์ ๊ฐ๋ณ์๋ช : 'useMotionValue' ๊ฐ
- ์ ๋ ฅ ๋ฐฐ์ด : ์ ๋ ฅ ๊ฐ ๋ฒ์
- ์ถ๋ ฅ ๋ฐฐ์ด : ์ถ๋ ฅ ๊ฐ ๋ฒ์
- ์
๋ ฅ ๋ฒ์ ๋ด์ ์์ ๋, ํด๋น ๊ฐ์ ๋ํ ์ถ๋ ฅ ๋ฒ์ ๊ฐ์ ๋ฐํํจ
- ์ ๋ ฅ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์ต์/์ต๋ ์ถ๋ ฅ ๋ฒ์๋ฅผ ๋ฐํํจ
- ๋ฒ์ ๋ด์์๋ ์ ์ง์ ์ผ๋ก ๊ฐ์ ๋ฐํํจ
- 'motion value' ์
๋ ฅ ๋ฒ์๋ฅผ ์ถ๋ ฅ ๋ฒ์๋ก ๋งคํํ๋ ๋ฐ ์ฌ์ฉํ๋ ๋ฉ์๋
- useScroll()
- ํ์ฌ ํ์ด์ง ์คํฌ๋กค์ 'motion value'๊ฐ์ ๋ฐํํ๋ ๋ฉ์๋
- ๊ธฐ๋ณธํ : const { ํ๋กํผํฐ๋ช
} = useScroll();
- scrollX, scrollY : ์ ๋ ์คํฌ๋กค ์์น๊ฐ [px]
- scrollXProgress, scrollYProgress : ์๋ ์คํฌ๋กค ์์น๊ฐ (0~1)
- SVG Animation
- <path>
- 'fill' ์์ฑ : svg์ ์๊น์ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- 'fill="currentColor"' : <svg>์ ์์์ ์ฌ์ฉํ๋ฉฐ, ๊ธฐ๋ณธ๊ฐ์ 'black'
- <svg>์ 'color'์์ฑ์ ํตํด ๋ณ๊ฒฝ ๊ฐ๋ฅ
- 'fill="transparent"' : ํฌ๋ช
- 'fill="currentColor"' : <svg>์ ์์์ ์ฌ์ฉํ๋ฉฐ, ๊ธฐ๋ณธ๊ฐ์ 'black'
- 'stroke' ์์ฑ : ํ ๋๋ฆฌ์ ์๊น์ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- 'strokeWidth' ์์ฑ : ํ ๋๋ฆฌ์ ๋๊ป๋ฅผ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- 'stroke', 'strokeWidth', 'fill' ๋ฑ์ CSS๋ก ์ฎ๊ธธ ์ ์์
- 'fill' ์์ฑ : svg์ ์๊น์ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- SVG๋ฅผ animateํ๊ธฐ ์ํด <motion.path>๋ก ๋์ฒดํ์ฌ ์ฌ์ฉ
- 'pathLength' ์์ฑ : ํ์ฌ ์์น๊น์ง์ path์ ๊ธธ์ด๋ฅผ ๋ํ๋ (0~1)
- ์ด๋ฅผ ์ฌ์ฉํด ํ ๋๋ฆฌ๋ฅผ ๊ทธ๋ ค์ฃผ๋ animation ์ฌ์ฉ ๊ฐ๋ฅ
- 'pathLength' ์์ฑ : ํ์ฌ ์์น๊น์ง์ path์ ๊ธธ์ด๋ฅผ ๋ํ๋ (0~1)
- <path>
- ํน์ ํ๋กํผํฐ์ transition ์๊ฐ์ ์ ํ๋ ๋ฐฉ๋ฒ
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ฒด ์์ฑ์ animation ์๊ฐ์ด ํต์ผ๋์ด ์์์
- ๊ฐ๊ฐ์ ํ๋กํผํฐ๋ง๋ค duration์ด๋ delay ์๊ฐ์ ์ง์ ํ ์ ์์
- ์ฌ์ฉ๋ฒ : transition์์ ๋ฐ๊พธ๊ณ ์ ํ๋ ํ๋กํผํฐ์ ์ง์ ํ ๋น
- ์ ์ฒด์ ๋ํ animation์ 'default'์ ํ ๋น
- ex. transition : { default : {duration : 5}, fill : {duration : 1} }
- AnimatePresence
- React์์ ์ฌ๋ผ์ง๋(์์ด์ง๋) ์ปดํฌ๋ํธ๋ฅผ animateํ ์ ์๋๋ก ํจ
- ๊ธฐ๋ณธ React์์๋ ์ฌ๋ผ์ง๋ ์ปดํฌ๋ํธ๋ฅผ animation์ ๋ถ์ฌํ ์ ์์
- ๊ท์น
- <AnimatePresence>๋ ํญ์ 'visible' ์ํ์ด์ด์ผ ํจ
- <AnimatePresence>์ ๋ด๋ถ์๋ ์กฐ๊ฑด๋ฌธ์ด ์กด์ฌํด์ผ ํจ
- ๊ธฐ๋ณธํ : <AnimatePresence>{์กฐ๊ฑด ? ์ฐธ : ๊ฑฐ์ง}</AnimatePresence>
- ์กฐ๊ฑด๋ฌธ์ ๋ค์ด๊ฐ๋ ์ปดํฌ๋ํธ์ animation์ ์ ์ฉํจ
- ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ผ์ง ๋์ animation์ 'exit' ์์ฑ์ ์ฌ์ฉ
- ์ปดํฌ๋ํธ์ 'custom' ์์ฑ๊ฐ์ ๋ฃ์ด, variants์์ ์กฐ๊ฑด๋ฌธ ์ฌ์ฉ ๊ฐ๋ฅ
- <motion.ํ๊ทธ>์ <AnimatePresence> ๋๋ค 'custom' ์์ฑ์ ๋ถ์ฌํ์ฌ ์ฌ์ฉ
- 'variants'๋ฅผ Object๋ฅผ returnํ๋ ์ฝ๋ฐฑํจ์๋ก ์ฌ์ฉํด์ผํจ
- ex. <AnimatePresence custom={isBack}> ย ย <Box custom={isBack} ... /> </AnimatePresence> const boxVariants = { ย ย entry : (isBack: Boolean) => ({ ย ย ย ย x : isBack ? -500 : 500; ย ย }), };
- <AnimatePresence>์ 'mode="wait"' ํ๋กํผํฐ
- exit๊ฐ ์ผ์ด๋ ํ entry๊ฐ ์ผ์ด๋๋๋ก ์ค์ ํ๋ ํ๋กํผํฐ
- ๋ฏธ ์ฌ์ฉ ์ exit์ entry๊ฐ ๋์์ ์ผ์ด๋จ
- React์์ ์ฌ๋ผ์ง๋(์์ด์ง๋) ์ปดํฌ๋ํธ๋ฅผ animateํ ์ ์๋๋ก ํจ
- Motion Value
- 23-09-15 : #8.14 ~ #9.4 / Animations(3)
- Layout Animation
- ์ปดํฌ๋ํธ์ 'layout' ์์ฑ์ ๋ถ์ฌ ์ CSS์์ํด ํด๋น ์ปดํฌ๋ํธ์ layout์ด ๋ฐ๋ ๋ ๋ง๋ค ์๋์ผ๋ก animation์ด ๋ง๋ค์ด์ง
- ๊ธฐ๋ณธํ : <์ปดํฌ๋ํธ๋ช layout />
- Shared Layout Animation
- ์๋ก ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ๊ฐ์ ๊ฐ์ ๊ฐ์ง๋ 'layoutId' ์์ฑ์ ๋ถ์ฌ ์ ํ๋์ ์ปดํฌ๋ํธ๋ก ์ฐ๊ฒฐํ์ฌ animation์ ๋ณด์ฌ์ค
- ์๊ฐ์ ์ผ๋ก ํ๋์ ์ฐ์ ์ปดํฌ๋ํธ๋ก ์ฒ๋ฆฌ๋จ
- ๊ธฐ๋ณธํ : <์ปดํฌ๋ํธ๋ช layoutId="๊ฐ" />
- ์๋ก ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ๊ฐ์ ๊ฐ์ ๊ฐ์ง๋ 'layoutId' ์์ฑ์ ๋ถ์ฌ ์ ํ๋์ ์ปดํฌ๋ํธ๋ก ์ฐ๊ฒฐํ์ฌ animation์ ๋ณด์ฌ์ค
- Layout Animation
- 23-09-19 : #9.0 ~ #9.1 / NETFLIX Clone(1) - Setup
- Font ํ์ผ ์ ์ฉ
- .woff2, .ttf ๋ฑ์ fontํ์ผ ์ ์ฉ ์ 'src'ํด๋ ๋ด์ ํ์ผ์ ์์น์์ผ์ผ ํจ
- '@font-face'๋ฌธ์ ํตํด font๋ฅผ ์ ์
- 'globalStyle' ๋ณด๋ค๋ 'cssํ์ผ'์ ์ ์ํด์ผ ์ด๊ธฐ ๋ก๋ฉ ์ฑ๋ฅ์ด ํฅ์๋จ
- Font ํ์ผ ์ ์ฉ
- 23-09-21 : #9.2 / NETFLIX Clone(2) - Header
- [Framer-Motion] Animation ๋ฐ๋ณต
- CSS์ ๊ฐ์ ๋ฐฐ์ดํ์์ผ๋ก ์ง์ ์ ์๋์ผ๋ก ๊ฐ์ ๋ณํํ๋ Animation์ฒ๋ผ ์๋ํจ
- 'transition'์ 'repeat'ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํด Animation ๋ฐ๋ณต ํ์ ์ง์ ๊ฐ๋ฅ
- number, "Infinity" ๋ฑ
- [React-Router-Dom] useMatch()
- useMatch(URL์ฃผ์) : ํ์ฌ ํ์ด์ง๊ฐ ์ด๋์ ์์นํ๋์ง ์๋ ค์ฃผ๋ Hook
- ํ์ฌ ์ฃผ์์ ๊ฐ์ ์ Object๋ฅผ ๋ฐํ
- ํ์ฌ ์ฃผ์์ ๋ค๋ฅผ ์ 'null'๊ฐ์ ๋ฐํ
- ์๋ ๊ฒฝ๋ก, ์ ๋ ๊ฒฝ๋ก ๋๋ค ์ฌ์ฉ ๊ฐ๋ฅ
- useMatch(URL์ฃผ์) : ํ์ฌ ํ์ด์ง๊ฐ ์ด๋์ ์์นํ๋์ง ์๋ ค์ฃผ๋ Hook
- [Framer-Motion] Animation ๋ฐ๋ณต
- 23-09-22 : #9.3 ~ #9.8 / NETFLIX Clone(3) - Header & Home
- [CSS] 'transform-origin' : ๋ณํ๊ฐ ์์ํ๋ ์์น๋ฅผ ์ง์ ํ๋ ์์ฑ
- ๊ธฐ๋ณธํ : transform-origin: ์ธ๋ก ๊ฐ๋ก;
- [Framer-Motion] ํจ์๋ก animation ์ ์ดํ๊ธฐ
- useAnimation() : animation์ ์ ์ด๋ฅผ ํจ์๋ก ์ ์ดํ๋๋ก ํจ
- ๊ธฐ์กด์๋ ์ปดํฌ๋ํธ์ ์์ฑ์ ๋ถ์ฌํ์์
- ์ฌ๋ฌ ๊ฐ์ animation์ ๋์์ ์คํ์ํค๊ณ ์ถ์ ๋ ์ ์ฉํจ
- ๊ธฐ๋ณธํ ์ ์ธ : const ๋ณ์๋ช = useAnimation(); ์ฌ์ฉ : ๋ณ์๋ช .ํ๋กํผํฐ({์คํ์ผ}) ์ ์ฉ : <์ปดํฌ๋ํธ๋ช animate={๋ณ์๋ช } />
-
const inputAnimation = useAnimation(); const toggleSearch = () => { ย ย if (searchOpen) { ย ย ย ย inputAnimation.start({ scaleX: 0 }); ย ย } else { ย ย ย ย inputAnimation.start({ scaleX: 1 }); ย ย } ย ย ย ย setSearchOpen((prev) => !prev); }; <Input initial={{ scaleX: 0 }} animate={inputAnimation} />
- 'useMotionValueEvent()' ๋ฉ์๋๋ฅผ ํตํ ์ ์ด๋ก, variants ์ฌ์ฉ๋ ๊ฐ๋ฅ
- useAnimation() : animation์ ์ ์ด๋ฅผ ํจ์๋ก ์ ์ดํ๋๋ก ํจ
- [React-Query] ์ํ API
- 'TMDB'์ฌ์ดํธ๋ก๋ถํฐ ์ํ API๋ฅผ ์ฌ์ฉ
- React-Query ์ฌ์ฉ๋ฒ
- 'query client'๋ฅผ ๋ง๋ค๊ณ , <QueryClientProvider>๋ก ๊ฐ์ธ์ฃผ๊ธฐ
- fetcherํจ์ ์ ์ํ๊ธฐ
- 'useQuery()'๋ฅผ ์ฌ์ฉํด API ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
- ๊ธฐ๋ณธํ : const { isLoading, data, error } = useQuery<์ ๋ค๋ฆญ>([์ฟผ๋ฆฌํค๋ช ], fetcherํจ์, ?์ต์ );
- ๋ฐ์ ๋ฐ์ดํฐ์ ํ์
์ค๋ช
ํ๊ธฐ
- 'interface'๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐํ์ ์ ์
- [Framer-Motion] ๋น ๋ฅธ ์๊ฐ ๋ด์ ์ฐ์๋ animation ์ค์ฒฉ ํ์ ๋ฌธ์
- <AnimatePresence>์ 'onExitComplete'์์ฑ์ exit๊ฐ ๋๋ ํ ์คํ๋จ
- animation์ด ์คํ๋๋ ๋์ ๋์์ ๋ง๋๋ก ํจ
- <AnimatePresence>์ 'onExitComplete'์์ฑ์ exit๊ฐ ๋๋ ํ ์คํ๋จ
- [Framer-Motion] ์ต์ด ๋ ๋๋ง ์์ animation ํด์
- <AnimatePresence>์ 'initial'์์ฑ๊ฐ์ 'false'๋ก ์ค ์ ์ต์ด ๋ ๋๋ง ์์ animation์ ํด์ ํจ
- ์ํ ๋ฆฌ์คํธ์ ์ํ๋ค ๋ฃ๊ธฐ
- ์ํ๋ฆฌ์คํธ ๋ฐฐ์ด์ ์ฌ๋ผ์ด๋ ๋ฐฐ์ด์ ๊ฐฏ์(offset)๋งํผ ์๋ผ์ ์ฌ๋ฌ ํ์ด์ง(page)๋ฅผ ๋ง๋ค์ด์ผ ํจ
- ๋ฐฐ๊ฒฝ์ผ๋ก ์ฌ์ฉํ ์ฒซ ๋ฒ์งธ ์ธ๋ฑ์ค๋ ์ ์ธ : data?.results.slice(1);
- '๋ฐฐ์ด.slice(์์์ธ๋ฑ์ค, ๋์ธ๋ฑ์ค)' ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ฐฐ์ด์ ์๋ผ์ ์ฌ์ฉ
- '๋ฐฐ์ด.slice(offset*page, offset*page+offset);'์ ์ฌ์ฉํ๊ณ , page๋ฅผ 1์ฉ ์ฆ๊ฐ
- ์ํ๋ฆฌ์คํธ ๋ฐฐ์ด์ ์ฌ๋ผ์ด๋ ๋ฐฐ์ด์ ๊ฐฏ์(offset)๋งํผ ์๋ผ์ ์ฌ๋ฌ ํ์ด์ง(page)๋ฅผ ๋ง๋ค์ด์ผ ํจ
- [CSS] 'transform-origin' : ๋ณํ๊ฐ ์์ํ๋ ์์น๋ฅผ ์ง์ ํ๋ ์์ฑ
- 23-09-23 : #9.9 ~ #9.10 / NETFLIX Clone(4) - Box animations
- [CSS] ์ํ ๋ฆฌ์คํธ์์ ์ํ๋ฅผ hover ์ scale์ ์ปค์ง๊ฒ ํ ๋, ์ ๋์ ์กด์ฌํ๋ ์ํ๊ฐ ํ๋ฉด์ด ์๋ฆฌ๋ ํ์
- ์ฒซ ๋ฒ์งธ, ๋ง์ง๋ง ์์์ 'transform-origin' ์์ฑ์ ์ฌ์ฉ
- [CSS] ์ํ ๋ฆฌ์คํธ์์ ์ํ๋ฅผ hover ์ scale์ ์ปค์ง๊ฒ ํ ๋, ์ ๋์ ์กด์ฌํ๋ ์ํ๊ฐ ํ๋ฉด์ด ์๋ฆฌ๋ ํ์
- 23-09-25 : #9.11 ~ #9.15 / NETFLIX Clone(5) - Movie modal & Search Form
- 23-09-26 : NETFLIX Clone(6)
- Update : [Header] ๊ฒ์์ฐฝ์ ์ด ์ ์๋์ผ๋ก ์ ๋ ฅ์ฐฝ์ focus
- 23-09-27 : NETFLIX Clone(7)
- Update
- [Header]
- ๊ฒ์์ฐฝ ๋ฐ๊นฅ์ ํด๋ฆญ ์ ์ด๋ ค์๋ ๊ฒ์์ฐฝ์ ๋ซ๋๋ก ํจ + ๋ซ์ ์ ์ ๋ ฅ๋ ๋ด์ฉ ์ด๊ธฐํ
- ๊ฒ์ ๊ธฐ๋ฅ ์ ๋ฐ์ดํธ
- ๊ฒ์์ฐฝ์ ๊ฐ์ด ์์ ์์๋ง ๊ฒ์๊ฐ์ ๋น์ธ ์ ์๋ 'x'๋ฒํผ ์์ฑ
- ๊ฒ์ ์กฐ๊ฑด ๋ฏธ๋ฌ ์ error๋ฅผ ๋ณด์ฌ์ฃผ๋ message ์์ฑ
- [Search]
- API ๊ฒฐ๊ณผ๊ฐ์ interface ์ ์
- ์ธ๋ฌผ ๊ฒ์ ์ ๊ด๋ จ ์ํ์ '.map()'์ ํตํด ํ์ด์ ํ๋ฉด์ ๋ณด์ฌ์ค
- ์ํ, tv์ผ ๋ฑ ์ฌ๋ฌ API๋ฅผ ๊ฐ๊ฐ ์ฌ์ฉํ ํ์์์ด ํ๋์ API ์ฌ์ฉ์ผ๋ก ํด๊ฒฐ
- [Header]
- Fix
- [Header] ๊ฒ์์ฐฝ์ด ์ด๋ฆด ์ ์ฐ์ธก ๋ถ๋ถ์ด form์ ๊ฐ๋ ค์ ธ input์ด ํด๋ฆญ์ด ์ ๋๋ ํ์ ์์
- [Search] ๊ฒ์ ์ ๋์ผ ์ํ์ด ์ค๋ณต๋๋ ํ์ ์์ (์ค๋ณต ๊ฒฐ๊ณผ ์ ๊ฑฐ)
- ๋ฉํฐ ๊ฒ์์ด๊ธฐ ๋๋ฌธ์, ์ํ/์ธ๋ฌผ ๊ฒ์์ผ๋ก ์ธํด ์ค๋ณต๋๋ ๊ฒ์ผ๋ก ์ถ์
- ์ค๋ณต ์ ํ๋๋ง ๋ณด์ฌ์ฃผ๋๋ก ์์
Issue : [Search] ์ฅ๋ฅด ๊ฒ์ ๊ด๋ จ ์ค์ ํ๊ธฐ
- Update
- 23-09-28 : NETFLIX Clone(8)
- Update : [Search] ๊ฒ์ UI ์ ๋ฐ์ดํธ & ํฌ์คํฐ ์ด๋ฏธ์ง๊ฐ ์๋ ์ํ์ ๋์ฒด ์ด๋ฏธ์ง
- Fix : [Header] ๊ฒ์์ฐฝ์ ๊ฒ์ ํค์๋๊ฐ ์ฐ์ธก์ 'x'๋ฒํผ๊ณผ ๊ฒน์น๋ ํ์ ์์
Issue : [Search] ๊ฒ์ ๊ฒฐ๊ณผ์ ๊ฐฏ์
- 23-09-29 : NETFLIX Clone(9)
- Update : [Search] ์์ดํ hover ์์ infoBox ์ ๋ฐ์ดํธ
- Fix : [Search] ๊ฒ์ ๊ฒฐ๊ณผ์ ๊ฐฏ์
- 23-10-02 : NETFLIX Clone(10)
- Update
- [Slider] ์ฌ๋ผ์ด๋ ์ข/์ฐ ๋ฒํผ ์์ฑ ๋ฐ ์ ๋๋ฉ์ด์ ์ ๋ฐ์ดํธ
- [Banner] ์ํ/TV ๋ ๋ค ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ ๋ฐ์ดํธ
- Fix : [Modal-Box] z-index ์์
- Update
- 23-10-04 : NETFLIX Clone(11)
- Fix : [Slider] absolute์ธ ์์ ์์์ height๋ฅผ ๋ถ๋ชจ ์์์ height์ ์ ์ฉ(padding-bottom ์ฌ์ฉ)ํด ์ฌ๋ผ์ด๋ ์ข/์ฐ ๋ฒํผ์ ๋์ด๋ฅผ ๊ฐ๊ฒ ๋ง๋ฆ
- Update : [Slider] hover ์ infoBox / click ์ infoBox ๊ธฐ์ด์์
- 23-10-05 : NETFLIX Clone(12)
- Update
- [Modal-Box]
- ๋ฐ์ดํฐ ์ถ๊ฐ ๋ฐ UI ์ ๋ฐ์ดํธ
- Nested-Router ๋ถํ ์ ๋ณต
- ํ์์ ์ด๋ฏธ API data๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด ๋ฐ๋ก ์ฌ์ฉ / URL ๊ฒ์ ์ API ๊ฒ์
- [Modal-Box]
Issue : [Modal-Box] URL ์ง์ ์ ๋ ฅ ์ 404 Error ํด๊ฒฐํ๊ธฐ
- Update
- 23-10-08 : NETFLIX Clone(13)
Issue : [Modal-Box] ์คํฌ๋กค๋ฐ ๋ฌธ์
- 23-10-11 : NETFLIX Clone(14)
- Fix : [Modal-Box]
- recommendationData๊ฐ ์์ ๊ฒฝ์ฐ ์คํฌ๋กค๋ฐ๊ฐ ์๊ธฐ์ง ์๋ ๋ฌธ์ ํด๊ฒฐ
- ์์ ์ปจํ ์ด๋ <Movie>์๊ฒ 'recommendationData?.result.length'์ ๋ฐ๋ผ ์ฐธ์ผ ๊ฒฝ์ฐ, 'height:100%'๋ฅผ ์ ์ธ
- URL ์ง์ ์
๋ ฅ ์ 404 Error ๋ฌธ์ ํด๊ฒฐ
- detail๊ณผ credit API data๊ฐ ์์ ์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ์ง ์๊ฒ ์กฐ๊ฑด๋ฌธ ์ฌ์ค์
- recommendationData๊ฐ ์์ ๊ฒฝ์ฐ ์คํฌ๋กค๋ฐ๊ฐ ์๊ธฐ์ง ์๋ ๋ฌธ์ ํด๊ฒฐ
- Update
- [Modal-Box] ์ถ์ฒ ํ์ด์ง ์ ๋ฐ์ดํธ
- [Slider] ๋ถํ ์ ๋ณต
- Fix : [Modal-Box]
- 23-10-12 : NETFLIX Clone(15)
- Update
- [Slider]
- ์ฌ๋ผ์ด๋ ํ์ดํ ์ถ๊ฐ
- ์ฒซ ์ฌ๋ผ์ด๋๋ง 2๋ฒ์งธ ๋ฐ์ดํฐ๋ถํฐ ๋ณด์ฌ์ค (์ฒซ๋ฒ์งธ ๋ฐ์ดํฐ๋ Banner๋ก ์ฌ์ฉ)
- [Modal-Box] movie, tv ๋ ๋ค์์ ๋ชจ๋ฌ๋ฐ์ค๊ฐ ๋์ค๊ฒ ์ ๋ฐ์ดํธ
- [Home, TV] ๋ ๋ง์ ์ฌ๋ผ์ด๋ ์ ๋ฐ์ดํธ
- [Footer] ์ ๋ฐ์ดํธ
- [Slider]
- Fix : [Slider] ๊ฐ๊ธฐ ๋ค๋ฅธ ์ฌ๋ผ์ด๋์์ ๊ฐ์ ์ํ๊ฐ ์์ ์ ์๋ก ์ ๋๋ฉ์ด์
์ ๊ฐ์ญํ๋ ํ์ ์์
- 'LayoutId'๊ฐ ์ค๋ณต๋์ง ์๋๋ก ์์
- Update
- 23-10-13 : NETFLIX Clone(16)
- Update
- [Banner] ์ฌ์ ๋ฒํผ, ์์ธ ์ ๋ณด ๋ฒํผ ์ ๋ฐ์ดํธ
- [Item] ์ฌ์ ๋ฒํผ ํด๋ฆญ ์ ์๊ณ ํธ ์์(YouTube) ๋ณด์ฌ์ฃผ๊ธฐ ์ ๋ฐ์ดํธ
- Update
- 23-10-16 : NETFLIX Clone(17)
- Fix
- [Info-Box] hover ์ ํ๋จ์ ์ฌ๋ผ์ด๋๋ณด๋ค ๋ ์ด์ด๊ฐ ๋ค์ ์๋ ํ์ ์์
- [Iframe] ์ ๊ฐ์ด๋ฐ ์ ๋ ฌ
- Update
- [ClickedItem] ์ํ์ tv๋ฅผ ํ ์ปดํฌ๋ํธ๋ก ํตํฉ
- [Search] ๊ฒ์๋ item ํด๋ฆญ ์ modal-box ๋ณด์ฌ์ฃผ๊ธฐ
- Issue : [Search] ๊ฒ์๋ item์ modal-box์์ recommendItem ํด๋ฆญ ์ URL ์ด๋ํ๋ ํ์
- ๊ฒ์ ์ ๊ฒ์์ด๋ฅผ Query String ๋์ ํ์ ๋์ URL ๋ฐ nested route ์ฌ์ฉ?
- Fix
- 23-10-17 : NETFLIX Clone(18)
- Fix : [Search] ๊ฒ์๋ item ํด๋ฆญ ์ overlay ์์น ์์
- 23-10-18 : NETFLIX Clone(19)
- Fix
- [Iframe] ์ค๋ณต๋ key๊ฐ์ ๊ฐ์ง๋ ํ์ ์์
- [Modal-Box] recommend item๋ค์ height๊ฐ ์์ (max-height)
- Update : [TV, Modal-Box] Season ๋ฐ Episode ์
๋ฐ์ดํธ (1/2)
- API ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์ ์ฒด์ ์ธ ํฐ ํ ์ ์
- Issue : [TV, Season] select svg ์ ๋๋ฉ์ด์ ๋ง๋ค๊ธฐ
- Fix
- 23-10-20 : NETFLIX Clone(20)
- Update
- [Modal-Box] ์คํฌ๋กค๋ฐ ๊ธฐ๋ฅ์ ์ ์งํ๋ฉด์, ํ๋ฉด์ ์จ๊ธฐ๊ธฐ
- [TV, Modal-Box] Season ๋ฐ Episode ์
๋ฐ์ดํธ (2/2)
- ์ธ๋ถ์ฌํญ ๊พธ๋ฏธ๊ธฐ
- ์ฌ์ง์ด ์์ ์ ํ ์คํธ๋ก ๋์ฒด
- ์ฒ์ 10๊ฐ ๊น์ง๋ง ๋ณด์ฌ์ฃผ๊ณ , ๋ฒํผ ํด๋ฆญ ์ ํด๋น ์์ฆ์ ์ํผ์๋ ๋ค ๋ณด์ฌ์ฃผ๊ธฐ
- [Head] <title> ์ ๋ฐ์ดํธ
- Update
- 23-10-21 : NETFLIX Clone(21)
- Update : [Head] favicon ์ ๋ฐ์ดํธ
- Fix
- [Head] 'description' ๋ณ๊ฒฝ
- [Banner] ๋ฐฐ๋ ์ด๋ฏธ์ง ์ค์์ผ๋ก ์ ๋ ฌ
- [Slider] ๋ฒํผ ์๊น ๋ณ๊ฒฝ
- [Footer] github ๋งํฌ ๋ฐ 'MenuBox'์ <li>์ width ์์
- To-Do
- 'swiper.js' ํจํค์ง์ ์ฌ๋ผ์ด๋ ์์๋ณด๊ธฐ
- [Head] meta๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ธ๋ค์ผ) ์ ๋ฐ์ดํธ
- [Season] svg ์ ๋๋ฉ์ด์ ๊ณ ์น๊ธฐ
- [Modal-Box] footer ๋ฐ ๊ด๋ ๋ฑ๊ธ ์ ๋ฐ์ดํธ
๋
ธ๋ง๋ ์ฝ๋ ์ ์ฑ
์ ๊ฐ์์์ฝ์ ๊ด์ฐฎ์ผ๋, ์ฝ๋์ ํ๊ธฐ๋ ๊ณต๊ฐ์ ์ธ ๊ณณ์ ์ฌ๋ฆฌ๋ฉด ์ ๋จ.
ํ๊ธฐ ์์ฝ์ง๋ ์ํธํ๋ .zip ํ์ผ๋ก ์ ์ฅํจ.