npx create-react-app ํ๋ก์ ํธ๋ช
shop ํ์ผ ๋ง๋ค๊ธฐnpm run start ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ฐฝ ๋์ฐ๊ธฐhtml, css๋ก ๋ฒํผ, ๋ฉ๋ด๊ฐ์ UI๋ค์ ์ง์ ๋์์ธ ํ์ง์๊ณ ๋ ์ด์์์ ์์ฑํ ์ ์๋ค. โก๏ธ npm install react-bootstrap bootstrap
(์์ฑ๋ฒ์ด ๋ฐ๋์๋ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ดํธ ๋ค์ด๊ฐ์ ํ์ธํ๋ ๊ฒ์ด ์ข์)
(1) โก๏ธ
import 'bootstrap/dist/css/bootstrap.min.css';
(App.jsx์ ์์ฑ)
(2) โก๏ธ
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
(index.htmlํ์ผ์<head>ํ๊ทธ ์์ ๋ฃ์ด์ฃผ๊ธฐ)
import ํ๊ธฐ. ๋ค์ ์ค๋ ์ปดํฌ๋ํธ๋ค์ import ์ ํด๋ ๋๋คimport "./App.css";
import { Navbar, Container, Nav } from "react-bootstrap"; //์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ ๋ค import ํด์ผ ํ๋ค
function App() {
return (
<div>
<Navbar bg="dark" variant="dark">
<Container>
<Navbar.Brand href="#home">Navbar</Navbar.Brand>
<Nav className="me-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#features">Features</Nav.Link>
<Nav.Link href="#pricing">Pricing</Nav.Link>
</Nav>
</Container>
</Navbar>
</div>
);
}
export default App;
<ํ์ผ ํ ๊ฐ์ ์ฌ๋ฌ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ด๊ธฐ ํ ๋>
//ํด๋น ํ์ผ
const data1 = 10;
const data2 = 20;
export { data1, data2 }; //๋ณ์๋ช
์ ์ ์ด์ฃผ๋ฉด ๋๋ค(๋ณ์๊ฐ ์ฌ๋ฌ๊ฐ ์ผ ๋๋ export๋ง ์ ๊ธฐ)
//import ๋ถ๋ฌ์ฌ ํ์ผ
import { data1, data2 } from "./data"; //{์ค๊ดํธ} ์ฌ์ฉํด์ import ํ๊ธฐ
<img src={"https://codingapple1.github.io/shop/shoes" + i + ".jpg"}
width="80%"/>
{
shoes.map((a, i) => {
return <Card shoes={shoes[i]} i={i + 1} />;
})}
i + 1์ธ ์ด์ ๋ map ์ธ์ i๊ฐ 0๋ถํฐ ์์ํ๋ ์ ์์ฌ์ ์ด๋ฏธ์ง 1,2,3 ์ด ๋ณด์ฌ์ง๊ฒ ํ๋ ค๋ฉด +1์ ํด์ฃผ๋ฉด ๋๋ค<img src={"https://codingapple1.github.io/shop/shoes" + (i + 1) + ".jpg"}
width="80%"/>
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ์ง ์์ ๋์๋ htmlํ์ผ์ ๋ฐ๋ก ๋ง๋ค์ด์ ๋ด์ฉ์ ์ฑ์ ๋ฃ์๋๋ฐ ๋ฆฌ์กํธ์์๋
Single Page Application, index.html ํ๋๋ง ์ฌ์ฉํ๋ค ์๋ฅผ ๋ค์ด์ detailํ์ด์ง์ ์ ์ํ๋ค๊ณ ํ์ ๋ /detail ์ ์ด์ฃผ๊ฒ ๋๋ฉด ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด๋ค !
< 2๋ฒ๊น์ง๋ ํฐ๋ฏธ๋์ ์์ฑํ๊ธฐ >
ynpm install react-router-dom@6<App />์ปดํฌ๋ํธ <BrowserRouter>๋ก ๊ฐ์ธ์ฃผ๊ธฐ (์๋์์ ์ฐธ๊ณ )<BrowserRouter>
<App />
</BrowserRouter> //์ด๋ ๊ฒ ์์ฑํ์ ์ ์๋์ผ๋ก ์๋จ์ ๋ถ๋ฌ์์ง๋ ๊ฒฝ๋ก
// import { BrowserRouter } from 'react-router-dom'; //๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถ๋ฌ์ค๋ ๋ฐฉ๋ฒ !
//โ์ถ๊ฐ๋ด์ฉ : `import App from './App';` ์ด๋ฐ์์ผ๋ก ./App ๋ถ๋ฌ์์ง๋ ๊ฒฝ๋ก๋ ๋ด๊ฐ ๋ง๋ jsํ์ผ์ด๋ฉฐ
// `'react-router-dom'` ์ด๋ ๊ฒ ๋ถ๋ฌ์์ง๋ ๊ฑด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋คApp.js ํ์ผ ์๋จ > import { Routes, Route, Link } from "react-router-dom"; ์ ๊ธฐRoute๋ ํ์ด์ง์ด๊ณ Routes๋ก ๊ฐ์ธ์ค์ผ ํ๋ค. <Route path="/url๊ฒฝ๋ก" element={ <๋ณด์ฌ์คhtml> } /> ์ด๋ ๊ฒ ์์ฑํ๋ฉด ๋๋๋ฐ ๊ฒฝ๋ก /๋ง ์ ์์ ๊ฒฝ์ฐ์๋ ๋ฉ์ธํ์ด์ง์ด๋ค
โ ๋ฉ์ธํ์ด์ง์์ ๋ณด์ฌ์ผ ํ ๋ด์ฉ๋ค์ element ์์๋ง ๋ฃ์ด์ฃผ๋ฉด ๋ฉ์ธํ์ด์ง ๋ด์ฉ์ ๋ฉ์ธํ์ด์ง์์๋ง ๋ณด์ฌ์ง๊ธฐ ๋๋ฌธ์ ์์ธํ์ด์ง์ ์ ์ํ์ ๋ ๋ฉ์ธํ์ด์ง ๋ด์ฉ์ ๋ณผ ์ ์๋ค ! (๋ด์ฉ์ ์ ์ ๋ ์ปดํฌ๋ํธํ ํด์ ์ ๊ธฐ)
๋ฒํผ์ ๋ง๋ค ๋์๋ ์๋์ฒ๋ผ ์์ฑ
<Link to="/">ํ</Link> //๋ฉ์ธํ์ด์ง๋ก ์ด๋ํ๋ ํ๋ฒํผ
<Link to="/detail">์์ธํ์ด์ง</Link> //์์ธํ์ด์ง๋ก ์ด๋ํ๋ ๋ฒํผ
// Link๋ aํ๊ทธ ์ฒ๋ผ ๋ณด์ฌ์ง๋ค ๊ทธ๊ฒ ์ซ์ผ๋ฉด ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋์ nav ํ๊ทธ์ onClick์ ๋ฃ์ด ์๋์ฒ๋ผ ์์ฑํด์ค ์ ๋ ์๋ค
<Nav.Link
onClick={() => {
navigate("/detail");
}}
>
Detail
</Nav.Link>
// navigate์ ๋ป์ ์๋ ์ฐธ๊ณ
http://localhost:3000/detail //detail ํ์ด์ง ์ฃผ์
http://localhost:3000/detail4546 // ํ์ด์ง ์ฃผ์ ๋ค์ ์์์๋ ๋ฌธ์, ์ซ์๊ฐ ๋ถ์ ๋
<Route path="*" element={<div>์๋ ํ์ด์ง</div>} />
// ์ด ์ธ์ ๋ชจ๋ ๊ฒ์ธ "*"์ ๋ถ์ด๋ฉด ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ์ ์ํ์ ๋ ์๋ ํ์ด์ง๋ผ๊ณ , ํ๋ฉด์ ๋ํ๋๊ฒ ๋๋ค
<Route>์์ <Route>๋ฅผ ๋ฃ์ ์ ์๋ ๋ฐฉ๋ฒ<์ฅ์ >
<Route path="/about" element={<About />} />
<Route path="/about/member" element={<About />} />
<Route path="/about/location" element={<About />} />
// ์ด๋ฐ ๋ฐฉ๋ฒ์ผ๋ก ์์ฑํด๋ ๋์ง๋ง Nested Routes ๋ฐฉ๋ฒ์ ์๋์ฒ๋ผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ...
<Route path="/about" element={<About />}>
<Route path="member" element={<About />} />
<Route path="location" element={<About />} />
</Route>
// Nested Routes ๋ฐฉ๋ฒ์ผ๋ก ์๋ธ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค ์ ์๋ค
const About = () => {
return (
<div>
<h4>ํ์ฌ ์ ๋ณด</h4>
<Outlet><Outlet> //Nested routes์์ element๋ฅผ ๋ณด์ฌ์ฃผ๋ ์๋ฆฌ
</div>
);
};
<Route path="/about" element={<About />}>
<Route path="member" element={<div>๊ฒฝ์์ง ์๊ฐ</div>} />
<Route path="location" element={<div>ํ์ฌ ์์น<div>} />
</Route>
Element์ <About /> ์ปดํฌ๋ํธ๋ง ๋ฃ๊ฒ ๋๋ฉด Route ๋ด์ฉ์ด ๊ฐ์ผ๋๊น About ์ปดํฌ๋ํธ์ Outlet์ ๋ฃ์ด์ ๋ด์ฉ์ ๋ฃ์ด์ฃผ๋ฉด ํ๋ฉด์ ๋ ๊ฐ์ Element๊ฐ ๋ณด์ฌ์ง๋ค(ํด๋น ๊ฒฝ๋ก(member, location)๋ก ์ ์ํด ์์ ๋ ์๋ก ๋ค๋ฅธ ๋ด์ฉ์ด ๋ณด์ฌ์ง๋ ๋ถ๋ถ ! )<Route>๋ฅผ 100๊ฐ ๋ง๋ค๊ณ ๊ตฌ๋ถํ๊ธฐ ์ํด์ ํ๋ํ๋ ๋ฒํธ๋ฅผ ๋งค๋ฒ ๋ถ์ฌํ ์๋ ์๋ค ๊ทธ๋์ URlํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋๋ฐ ๋ฌธ๋ฒ์ ํ์ผ๊ฒฝ๋ก ๋ค์ /:id ๋ฅผ ๋ถ์ฌ์ฃผ๋ฉด ๋๋ค. <Route path="/detail/:id" element={<Detail shoes={shoes} />} />
//id๋ ์๋ช
ํ ๊ฒ์
useParams() hook์ ์ฌ์ฉํ๋ฉด ๋๋ค !/0, /1, /2 ์ด๋ ๊ฒ ์
๋ ฅํด์ฃผ๋ฉด ํด๋น data์ ๋ง๊ฒ ๋ณด์ธ๋ค ํ์ง๋ง ์ฌ์ฉ์๊ฐ ์๋ฌด๊ฑฐ๋ ์
๋ ฅํ์ ๋์๋ ํ๋ฉด์ ๋ณด์ด์ง ์๊ฒ ๋๋ค. ๊ทธ๋ด ๋ โ์กฐ๊ฑด๋ฌธ์ ์ฌ์ฉํ๋ฉด ๋๋คโ const data = [ //์ํ 3๊ฐ์ ๋ํ data
{ //๊ฒ์ ์ ๋ฐ
id: 0, // ์๊ตฌ๋ฒํธ๋ก ๊ณ ์ !
title: "White and Black",
content: "Born in France",
price: 120000,
},
{//๋นจ๊ฐ์ ๋ฐ
id: 1,
title: "Red Knit",
content: "Born in Seoul",
price: 110000,
},
{//ํ์์ ๋ฐ
id: 2,
title: "Grey Yordan",
content: "Born in the States",
price: 130000,
},
];
export default data;
// ์ํํ์ด์ง Detail ์ปดํฌ๋ํธ
const Detail = ({ shoes }) => {
const { id } = useParams(); // ๋ด๊ฐ ์์ฑํ๋ ํ์ผ ๊ฒฝ๋ก์ ์๋ช
ํ๋ id๊ฐ์ ๋ถ๋ฌ์ด !
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img
src="https://codingapple1.github.io/shop/shoes1.jpg"
width="100%"
/>
</div>
<div className="col-md-6">
<h4 className="pt-5">{shoes[id].title}</h4> //id ๊ฐ์ ์ ์ด์ค๋ค
<p>{shoes[id].content}</p>
<p>{shoes[id].price}</p>
<button className="btn btn-danger">์ฃผ๋ฌธํ๊ธฐ</button>
</div>
</div>
</div>
);
};
export default Detail;
โ ๏ธ ์ค๋ฆ์ฐจ์/๋ด๋ฆผ์ฐจ์ ๋ฒํผ์ด ์๋ค๊ณ ๊ฐ์ ํ์ ๋ ๋ฐฐ์ด์ ์์๊ฐ ๋ฐ๋์ด์ 0๋ฒ์งธ ๊ฒ์ ์ ๋ฐ์ด ํ๋ฉด์ ๋ณด์์ผ๋ฉด ์ข๊ฒ ์ผ๋ 2๋ฒ์งธ ํ์์ ๋ฐ์ด ๋ณด์ฌ์ง๊ฒ ๋๋ ๊ฒฝ์ฐ๊ฐ ์๋ค ์ด๊ฑธ ๋ฐฉ์งํ๊ธฐ ์ํด์๋ data์ ์๋
โ์ํ๋ฐ์ดํฐ์ id๊ฐ์ ์๊ตฌ๋ฒํธ๋ก ๊ณ ์ โํด๋์ผ๋ฉด ๋๋ค
// Detail ์ปดํฌ๋ํธ์ ์ ์ฒด ์ฝ๋
import { useParams } from "react-router-dom"; //
const Detail = ({ shoes }) => {
const { id } = useParams();
const products = shoes.find((product) => product.id === parseInt(id));
// findํจ์๋ก data์ id์ ๋์ผํ ๊ฒ ์ฐพ์์ฃผ๊ธฐ
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img
src="https://codingapple1.github.io/shop/shoes1.jpg"
width="100%"
/>
</div>
<div className="col-md-6">
<h4 className="pt-5">{product.title}</h4>
<p>{product.content}</p>
<p>{product.price}</p>
<button className="btn btn-danger">์ฃผ๋ฌธํ๊ธฐ</button>
</div>
</div>
</div>
);
};
export default Detail;
product๋ฅผ ๋ง๋ค์ด์ ์ปดํฌ๋ํธ ๋ด์ฉ์ product.id(data์ key๊ฐ)์ ์ ์ด์ฃผ๋ฉด ๋ฐฐ์ด์ ์์๊ฐ ์๋ฌด๋ฆฌ ๋ณ๊ฒฝ๋์ด๋ ์๊ตฌ๋ฒํธ๊ฐ ๊ณ ์ ๋์ด ์์ด์ ํ๋ฉด์ ์ ์์ ์ผ๋ก ์ ์๋์ด ๋๋คconst products = shoes.find((product) => product.id === parseInt(id));
parseInt๋ฅผ ์ฌ์ฉํ ์ด์ ๋ ์์์ ๊ฒฝ๋ก๋ค์ /:id๊ฐ ๋ฌธ์์ด์ด๊ธฐ ๋๋ฌธ์ ์ซ์===๋ฌธ์ ์๊ฒฉ ๋น๊ตํ๋ฉด ์๋ฌ๊ฐ ๋๋ค ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ parseInt๋ฅผ ์ฌ์ฉํด์ ๋ฌธ์์ด์ ์ซ์๋ก ๋ณ๊ฒฝ์ ํด์คฌ๋ค == ๋์จ๋น๊ต๋ฅผ ํ๋ฉด parseInt๋ฅผ ์ ์ด์ฃผ์ง ์์๋ ๊ด์ฐฎ์ง๋ง ๋น๊ต๋ฅผ ํ ๋์๋ ===์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋คNested Routes ์ฌ์ฉํ ๋ ๋ง์ด ์ฌ์ฉํ๋ styled-components ๋ผ์ด๋ธ๋ฌ๋ฆฌnpm install styled-components ์ค์นimport styled from 'styled-components'; import ํด์ ๋ถ๋ฌ์ค๊ธฐ//์์ฑ๋ฐฉ๋ฒ
const YellowBtn = styled.button`
background: yellow;
color: black;
padding: 10px;
`;
<YellowBtn>๋ฒํผ</YellowBtn> // style์ด ์
ํ์ง ๋ฒํผ์ ๋ ๋๋ง ํ ์ ์๋ค
<styled-components ์ฅ์ >
<style> ๋ฃ์ ๊ฒ๊ณผ ๊ฐ๋ค)<styled-components ๋จ์ >
// css ์๋ช
๋ฐฉ๋ฒ
์ปดํฌ๋ํธ๋ช
.module.css
// import ํด์ ๋ค๋ฅธ jsํ์ผ์์ ์ฌ์ฉํ๋ฉด ๊ทธ ์คํ์ผ๋ค์ ์ปดํฌ๋ํธ๋ช
.js ํ์ผ์๋ง ์ ์ฉ๋๋ค
const YellowBtn = styled.button`
background : ${ (props) => props.bg }; //props ์ ๋ฌ๋ฐฉ๋ฒ
color : ${ (props) => props.bg == 'yellow' ? 'white' : 'black' }; //์ผํญ์ฐ์ฐ์ ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค
padding : 10px;
`;
<div>
<YellowBtn bg="yellow">Yellow</YellowBtn>
<YellowBtn bg="orange">Orange</YellowBtn>
</div>
Life Cycle ์ด๋ฒคํธ๋ผ๊ณ ํ๋ค Life Cycle ์ด๋ฒคํธ๋ ์ปดํฌ๋ํธ ๋์์ ์ ์ดํ๊ณ , ์์
์ํ์ ํฅ์ ์ํค๋ ์ฌ์ฉ์ ์ ์ ๋ก์ง์ ๊ตฌํํด๋ด๋ ๊ฒ์ ์๋ฏธํ๋ค<๋ผ์ดํ ์ฌ์ดํด์ ์ฅ์ >
โ<๋ผ์ดํ ์ฌ์ดํด์ ์ด๋ฒคํธ ๋ถ๋ฅ>โ: ์์ฑ(Mount)โก๏ธ์ฌ์ฉ(Update)โก๏ธ์๋ฉธ(Unmount)
โ๏ธ ์์ฑ(Mount) : ํ ๋ฒ๋ง ์คํ
โ๏ธ ์ฌ์ฉ(Update) : ์ฌ๋ฌ๋ฒ ์คํ
โ๏ธ ์๋ฉธ(Unmount) : ํ ๋ฒ๋ง ์คํ
โ๏ธ ๋ฆฌ๋ ๋๋ง๋ง๋ค ์ฝ๋ ์คํ
useEffect(() => {
//์ปดํฌ๋ํธ๊ฐ mount & update์ ์คํ๋๋ค
});
console.log ์ถ๋ ฅ ์ ๋๋ฒ ์ถ๋ ฅ์ด ๋๋๋ฐ ๊ทธ ์ด์ ๋ index.js์ <React.StrictMode>๋ผ๋ ํ๊ทธ๊ฐ ์์ผ๋ฉด ๋๋ฒ ์ถ๋ ฅ์ด ๋๋ค
โ useEffect ๋ฐ๊นฅ์ ์ ์ด๋ ๋๊ฐ์ด ์ปดํฌ๋ํธ mount & update์ ์คํ์ด ๋๋ค ๊ทธ๋ฐ๋ฐ ์ useEffect๋ฅผ ์ฌ์ฉํ๋๋ฉด ? useEffect ์์ ์ ์ ์ฝ๋๋ html ๋ ๋๋ง ์ดํ์ ๋์์ ํ๋๋ฐ html์ด ๋จผ์ ๋น ๋ฅด๊ฒ ๋ณด์ด๊ณ ์ถ์ ์ฌ์ดํธ๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด ์๋ ์ด๋ฏธ์ง ์ฐธ๊ณ !

๐ก ์ปดํฌ๋ํธ ์์ฑ(Mount) 1ํ๋ง ์คํ
useEffect(() => {
// ์คํํ ์ฝ๋
}, []);
๐ก useEffect ์์ ์ฝ๋ ์คํ๋๊ธฐ ์ ์ ํญ์ ์คํ
useEffect(()=>{
return ()=>{
// ์คํํ ์ฝ๋
}
})
๐ก ์ปดํฌ๋ํธ ์๋ฉธ(Unmount) 1ํ ์คํ
useEffect(()=>{
return ()=>{
// ์คํํ ์ฝ๋
}
}, [])
๐ก state1์ด ๋ณ๊ฒฝ๋ ๋๋ง ์คํ
useEffect(()=>{
// ์คํํ ์ฝ๋
}, [state1])
setTimeout( ()=>{
1์ด ํ ์คํํ ์ฝ๋
}, 1000); // 1000์ 1์ด
Get์์ฒญ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋, Post ์์ฒญ์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ ๋ ์ฌ์ฉ๋๋คarray object๋ ์ฃผ๊ณ ๋ฐ๋๋ฐ "๋ฐ์ดํ" ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐฐ์ด์ด๋ ๊ฐ์ฒด๋ ์ฃผ๊ณ ๋ฐ๊ธฐ๊ฐ ๊ฐ๋ฅํ๋ค ์ด๊ฒ์ JSON ์ด๋ผ๊ณ ํ๋ค< GET ์์ฒญํ๋ ๋ฐฉ๋ฒ >

GET/POST ์์ฒญ์ด ๊ฐ๋ฅํ๋คโก๏ธ npm install axios
โก๏ธ import axios from 'axios';
axios.get('URL๊ฒฝ๋ก').then(() => {}).catch(() => {})<button onClick={() => {
axios.get('https://codingapple1.github.io/shop/data2.json') //url ๋ถ๋ฌ์ค๊ธฐ
.then((result) =>{ // get์์ฒญํ ๋ฐ์ดํฐ๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์จ๋ค
//console.log(result.data) // get์์ฒญ ๊ฒฐ๊ณผ๋ url๋ก ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค
const copy = [...shoes, ...result.data]; //๋ค๋ฅธ๋ฐ์ดํฐ ์ถ๊ฐ
setShoes(copy); //shoes ๋ณ๊ฒฝํจ์์ copy๋ณ์๋ฅผ ๋ฃ์ด์ค
}) //์ถ๋ ฅํด์ ํ์ธ ํ ๋ .then์ ์ฌ์ฉ
.catch(() => { // ๋ฐ์ดํฐ ์์ฒญ์ ์คํจํ์ ๋ .catch ์ฌ์ฉ
console.log('์์ฒญ์ ์คํจํ์ต๋๋ค') //URL์ด ์๋ชป๋์ ๋ ๋ณด์ฌ์ง ์ฝ๋
})
}}>๋๋ณด๊ธฐ</button>

Promise.all([axios.get('/url1'), axios.get('/url2')])

(1) html/css๋ก ๋ฏธ๋ฆฌ ๋์์ธ ํด๋๊ธฐ
(2) UI ํ์ฌ ์ํ๋ฅผ ์ ์ฅ ํ state ํ๋ ๋ง๋ค๊ธฐ
(3) state์ ๋ฐ๋ผ์ Ui๊ฐ ์ด๋ป๊ฒ ๋ณด์ผ ์ง ์์ฑ
//html/css๋ก ๋ฏธ๋ฆฌ ๋์์ธ ํด๋๊ธฐ
//App.css
.start {
opacity: 0;
}
.end {
opacity: 1;
transition: opacity 0.5s;
}
//Detail.jsx
<Nav variant="tabs" defaultActiveKey="link0"> //defaultActiveKey๋ ๊ธฐ๋ณธ์ผ๋ก ์ฒซ ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๋ฒํผ
<Nav.Item>
<Nav.Link eventKey="link0">๋ฒํผ0</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="link1">๋ฒํผ1</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="link2">๋ฒํผ2</Nav.Link>
</Nav.Item>
</Nav>
//UI ํ์ฌ ์ํ๋ฅผ ์ ์ฅ ํ state ํ๋ ๋ง๋ค๊ธฐ
const [tab, setTab] = useState(0); // 0, 1, 2๋ฅผ ์ฌ์ฉํด ๋ด์ฉ์ ๋ณ๊ฒฝํ๊ฒ ํ ๊ฒ
const TabContent = ({tab}) => {
if (tab === 0) {
<div>๋ด์ฉ0</div>;
} else if (tab === 1) {
<div>๋ด์ฉ1</div>;
} else if (tab === 2) {
<div>๋ด์ฉ2</div>;
}
};
<TabContent tab={tab}/> // ์ธ๋ถ์ ๋ง๋ ์ปดํฌ๋ํธ์ด๊ธฐ ๋๋ฌธ์ ํ๋กญ์ค ์ ๋ฌ์ ํด์ผํ๋ค
function TabContent({tab}){
return [ <div>๋ด์ฉ0</div>, <div>๋ด์ฉ1</div>, <div>๋ด์ฉ2</div> ][ํญ]
} //์ฝ๋ ์ถ์ฝํด์ ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค
//state์ ๋ฐ๋ผ์ Ui๊ฐ ์ด๋ป๊ฒ ๋ณด์ผ ์ง ์์ฑ
const TabContent = ({ tab }) => {
const [fade, setFade] = useState(""); //์ด๊ธฐ๊ฐ ๋น ๋ฌธ์์ด
useEffect(() => {
setTimeout(() => { //0.1์ด ํ์ class end๋ฅผ fade์์ ๋ฃ๊ธฐ
setFade("end");
}, 100);
return () => {
setFade(""); // useEffect์ฝ๋ ์คํ ์ ์ end์ ๊ฐ์ด ''๋ก ๋๋ค
};
}, [tab]);
return (
<div className={`start ${fade}`}> //fade๋ณ์ ๋ฃ๋๋ฐฉ๋ฒ
{[<div>๋ด์ฉ0</div>, <div>๋ด์ฉ1</div>, <div>๋ด์ฉ2</div>][tab]}
</div>
);
};
setTimeout์ ์ฌ์ฉํ ์ด์ ๋ ๋ฆฌ์กํธ 18๋ฒ์ ์ด์๋ถํฐ๋ automatic batch ๊ธฐ๋ฅ์ด ์๊ฒผ๋๋ฐ state ๋ณ๊ฒฝํจ์๊ฐ ์ฐ๋ฌ์์ ์ฌ๋ฌ๊ฐ ์ฒ๋ฆฌ๋์ด์ผ ํ๋ค๊ณ ๊ฐ์ ํ์ ๋ ๋ณ๊ฒฝํจ์๋ฅผ ๋ค ์ฒ๋ฆฌํ๊ณ ๋ง์ง๋ง์ ํฉ์ณ์ ํ ๋ฒ๋ง ๋ฆฌ๋ ๋๋ง์ด ๋๋ค ๊ทธ๋์ setTimeout ์ด์ฉํด์ 'end', '๋น ๋ฌธ์์ด' ์๊ฐ์ฐจ๋ฅผ ๋์๋ค //ํญ๊ธฐ๋ฅ onClick ๋ฒํผ setTab(ํด๋น๋ฒํธ) ๋ฃ์ด์ฃผ๊ธฐ
<Nav variant="tabs" defaultActiveKey="link0">
<Nav.Item>
<Nav.Link
onClick={() => {
setTab(0); // 0๋ฒ
}}
eventKey="link0"
>
๋ฒํผ0
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link
onClick={() => {
setTab(1); // 1๋ฒ
}}
eventKey="link1"
>
๋ฒํผ1
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link
onClick={() => {
setTab(2); // 2๋ฒ
}}
eventKey="link2"
>
๋ฒํผ2
</Nav.Link>
</Nav.Item>
</Nav>
<TabContent tab={tab} />
< Context APi ๋จ์ >
- state ๋ณ๊ฒฝ ์ ๋ถํ์ํ ๊ฒ๋ค ๊น์ง ๋ฆฌ๋ ๋๋ง ๋๋ค
- ๋์ค์ ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ์ด ์ด๋ ค์์ง ๋ฟ๋ง ์๋๋ผ ๋ค๋ฅธํ์ด์ง์์ ์ฌ์ฌ์ฉ ํ์ ๊ฒฝ์ฐ์๋ ์ด์ํ๊ฒ ์๋ํ ์๋ ์์(state ๋ก ๋ฐ์์จ ๋ณ์์ ๊ฐ์ด ์๋ค๊ณ ๋์ฌ ์๋ ์๋ค)
โ์ด๋ฌํ ๋จ์ ๋ค ๋๋ฌธ์context๋ณด๋ค ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธredux๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค
(1) createContext() ๋ง๋ค๊ธฐ
export const Context1 = createContext(); //์๋จ์ import๋ ํด์ผํจ
(2) Context๋ก ์ํ๋ ์ปดํฌ๋ํธ ๊ฐ์ธ๊ธฐ
<Route
path="/detail/:id" element={
<Context1.Provider value={{ ์ฌ๊ณ , shoes }}> // value์์ ์ฌ์ฉ ํ state ๊ณต์
<Detail shoes={shoes} /> // ๋ํ
์ผ ์ปดํฌ๋ํธ์์๋ state ์์ ๋กญ๊ฒ ์ฌ์ฉ ๊ฐ๋ฅํด์ง
</Context1.Provider>
}/>
(3) Context ์์ ์๋ state ์ฌ์ฉ
//Detail.jsx
import { useEffect, useState, useContext } from "react"; //useContext ๋ถ๋ฌ์ค๊ธฐ
import { Context1 } from "../App"; //๋ง๋ค์ด๋ ์ปดํฌ๋ํธ import ํด์ค๊ธฐ
const { ์ฌ๊ณ } = useContext(Context1); //[10, 11, 12]state ๋ถ๋ฌ์ด
Redux๋ props ์์ด state ๋ฅผ ๊ณต์ ํ๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋คRedux๋ฅผ ์ค์นํ๋ฉด js ํ์ผ ํ๋์ state๋ค์ ๋ณด๊ดํ ์ ์๋๋ฐ ๋ชจ๋ ์ปดํฌ๋ํธ์ state ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค (์ฌ์ดํธ๊ฐ ์ปค์ง๋ฉด ์ฌ์ฉํ ์ ๋ฐ์ ์์ด์ ์ค์ํ๋ค ! )<์ฅ๋ฐ๊ตฌ๋ํ์ด์ง ๋ง๋ค๊ธฐ>
// Cart.jsx
import { Table } from "react-bootstrap";
const Cart = () => {
return (
<Table>
<thead>
<tr>
<th>#</th>
<th>์ํ๋ช
</th>
<th>์๋</th>
<th>๋ณ๊ฒฝํ๊ธฐ</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>์๋
</td>
<td>์๋
</td>
<td>์๋
</td>
</tr>
</tbody>
</Table>
);
};
export default Cart;
// App.jsํ์ผ์ import ๋ฐ ์ปดํฌ๋ํธํ ํ๊ธฐ
(1) ์ค์น๋ฐฉ๋ฒ โก๏ธ npm install @reduxjs/toolkit@1.8.1 react-redux
(2) package.json ํ์ผ > "react", "react-dom" ํญ๋ชฉ์ ๋ฒ์ ์ ํ์ธํ๋ค ๋ฒ์ ์ 18.1 ๋ฒ์ ์ด์์ธ ์ง ํ์ธํ๋ค
(3) ๋ฒ์ ํ์ธ ๋๋๋ฉด ํฐ๋ฏธ๋์ ์
๋ ฅ โก๏ธ npm install
// store.jsx
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
reducer: { }
})
//index.js
import { Provider } from "react-redux";
import store from './store.js'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}> //store ๊ฐ์ ธ์ค๊ธฐ
<BrowserRouter>
<App /> //App์ ๋ฌผ๋ก ์์ ์ปดํฌ๋ํธ๋ค์ store.js์ ์๋ state๋ฅผ ๋ง์๋๋ก ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค
</BrowserRouter>
</Provider>
</React.StrictMode>
);
//store.jsx
import { configureStore, createSlice } from "@reduxjs/toolkit";
const user = createSlice ({ //state ์์ฑ (useState์ ๋น์ทํ๋ค)
name: 'user', // state ์ด๋ฆ
initialState : 'kim' //state ๊ฐ
})
export default configureStore({ // state ๋ฑ๋ก
reducer: {
user: user.reducer, // ์๋ช
: state์์ฑ.reducer
cart: cart.reducer,
},
});
//Cart.jsx
import { useSelector } from "react-redux"
const a = useSelector((state) => {
return state; // store์ ์๋ ๋ชจ๋ state๋ฅผ ๊ฐ์ ธ์จ๋ค
});
<Redux์ state ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ>
(1) store.jsx์ state๋ณ๊ฒฝ ํด์ฃผ๋ ํจ์ ๋ง๋ค๊ธฐ
(2) export ํ๊ธฐ
(3) ํ์ํ ๋ import ํด์ค๊ณ , dispatch()๋ก ๊ฐ์ธ์ ์ฌ์ฉํด์ผ ํ๋ค
//(1) store.jsx์ state๋ณ๊ฒฝ ํด์ฃผ๋ ํจ์
let user = createSlice({
name : 'user',
initialState : 'kim', // state ๊ฐ์ 'kim'
reducers : { //์คํ ์ ์๊ธฐ๊ฒ ์ฃผ์
changeName(state){ // ์์์ export ๋ถ๊ฐ
return 'john ' + state // ๋ฒํผ ํด๋ฆญ ์ 'john kim' ์ถ๋ ฅ
}
}
})
//(2) export ํ๊ธฐ
export const { changeName } = user.actions; //์ค๋ฅธ์ชฝ ์๋ฃ๋ฅผ ๋ณ์๋ก ๋ด๋ณด๋ด๊ธฐ
//Cart.jsx
//(3) ํ์ํ ๋ import ํด์ค๊ณ , dispatch()๋ก ๊ฐ์ธ์ ์ฌ์ฉํด์ผ ํ๋ค
import { useDispatch, useSelector } from "react-redux";
import { changeName } from "../store";
const Cart = () => {
const state = useSelector((state) => state); //๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ฃผ๋ state๋ ์ ์ฒด state๋ฅผ ๋ถ๋ฌ์ด
const dispatch = useDispatch(); //store.jsx ์์ฒญ์ ๋ณด๋ด์ฃผ๋ ํจ์
<button
onClick={() => {
dispatch(changeName()); //dispatch(state๋ณ๊ฒฝํจ์())
}}
>
+
</button>
dispatch(changeName())๋ฅผ ์ ์๋ค๊ณ ํด์ ์ ์๋ฆฌ์์ ํจ์๋ฅผ ๋ฐ๋ก ์คํํด์ฃผ๋ ๊ฒ ์๋๋ผ ๋ฒํผ ํด๋ฆญ ์ store.jsx ํ์ผ์ ํจ์๋ฅผ ์คํํด๋ฌ๋ผ๊ณ ์์ฒญํ๋ ์ฝ๋์ด๋คlet user = createSlice({
name : 'user',
initialState : 'kim',
reducers : {
changeName(state){
state.name = 'park' // ๋ฒํผ ํด๋ฆญ ์ park๋ก ๋ณ๊ฒฝ๋๋ค
}
}
})
< state ๋ณ๊ฒฝํจ์๊ฐ ์ฌ๋ฌ๊ฐ ํ์ํ ๋? >
const user = createSlice({
name: "user",
initialState: { name: "kim", age: 20 },
reducers: {
increase(state, action) {
state.age += action.payload; //payload๋ ํ๋ฐฐ, ํ๋ฌผ ์ด๋ฐ๋ป
},
},
});