[React ์ฝ”๋”ฉ์• ํ”Œ๐ŸŽ] ํ”„๋กœ์ ํŠธ shop 1-20

์ด๋‹ค์˜ยท2024๋…„ 6์›” 28์ผ

React

๋ชฉ๋ก ๋ณด๊ธฐ
10/31

๐ŸŽ shop ํด๋” ์ƒ์„ฑ

  1. npx create-react-app ํ”„๋กœ์ ํŠธ๋ช… shop ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ
  2. npm run start ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ฐฝ ๋„์šฐ๊ธฐ

๐ŸŽ React-Bootstrap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • html, css๋กœ ๋ฒ„ํŠผ, ๋ฉ”๋‰ด๊ฐ™์€ UI๋“ค์„ ์ง์ ‘ ๋””์ž์ธ ํ•˜์ง€์•Š๊ณ  ๋ ˆ์ด์•„์›ƒ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

โญ 1. React-Bootstrap ์„ค์น˜๋ฐฉ๋ฒ•

โžก๏ธ npm install react-bootstrap bootstrap
(์ž‘์„ฑ๋ฒ•์ด ๋ฐ”๋€”์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์ดํŠธ ๋“ค์–ด๊ฐ€์„œ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ)

โญ 2. ์‚ฌ์šฉ ์ „ ์‚ฌ์šฉ๋ฐฉ๋ฒ• 2๊ฐ€์ง€

(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> ํƒœ๊ทธ ์•ˆ์— ๋„ฃ์–ด์ฃผ๊ธฐ)

โญ 3. ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์‚ฌ์šฉํ•  ๋•Œ import ํ•˜๊ธฐ

  • ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ „๋ถ€ 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;

๐ŸŽ import export (์ปดํฌ๋„ŒํŠธ ๋‚ด๋ณด๋‚ด๊ธฐ)

  • ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ๊ฒƒ ๊ฐ™๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ์˜ ํŒŒ์ผ์˜ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ๊ฒƒ ๊ฐ™์„ ๋•Œ์—๋Š” ํŒŒ์ผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ํŽธํ•˜๋‹ค

<ํŒŒ์ผ ํ•œ ๊ฐœ์— ์—ฌ๋Ÿฌ๊ฐœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด๊ธฐ ํ•  ๋•Œ>

//ํ•ด๋‹น ํŒŒ์ผ
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%"/>

๐ŸŽ map ๋Œ๋ฆด ๋•Œ ์ธ์ž i ?

{
  shoes.map((a, i) => {
   return <Card shoes={shoes[i]} i={i + 1} />;  
})}
  • i + 1์ธ ์ด์œ ๋Š” map ์ธ์ž i๊ฐ€ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ์ •์ˆ˜์—ฌ์„œ ์ด๋ฏธ์ง€ 1,2,3 ์ด ๋ณด์—ฌ์ง€๊ฒŒ ํ•˜๋ ค๋ฉด +1์„ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค
    โž• map์•ˆ์— ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ์— ์ข‹๋‹ค
<img src={"https://codingapple1.github.io/shop/shoes" + (i + 1) + ".jpg"}
 width="80%"/>

๐ŸŽ React์—์„œ ํŽ˜์ด์ง€ ๋‚˜๋ˆ„๋Š” ๋ฒ•

๋ฆฌ์•กํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ์—๋Š” htmlํŒŒ์ผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์„œ ๋‚ด์šฉ์„ ์ฑ„์›Œ ๋„ฃ์—ˆ๋Š”๋ฐ ๋ฆฌ์•กํŠธ์—์„œ๋Š”
Single Page Application, index.html ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•œ๋‹ค ์˜ˆ๋ฅผ ๋“ค์–ด์„œ detailํŽ˜์ด์ง€์— ์ ‘์†ํ•œ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ /detail ์ ์–ด์ฃผ๊ฒŒ ๋˜๋ฉด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค !

๐ŸŽ React ๋ผ์šฐํ„ฐ ์„ค์น˜/์„ธํŒ…

< 2๋ฒˆ๊นŒ์ง€๋Š” ํ„ฐ๋ฏธ๋„์— ์ž‘์„ฑํ•˜๊ธฐ >

  1. ^C^C์ผ๊ด„ ์ž‘์—…์„ ๋๋‚ด์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ (Y/N)? y
  2. \ํŒŒ์ผ๊ฒฝ๋กœ\shop> npm install react-router-dom@6
  3. src > index.jsํŒŒ์ผ > <App />์ปดํฌ๋„ŒํŠธ <BrowserRouter>๋กœ ๊ฐ์‹ธ์ฃผ๊ธฐ (์•„๋ž˜์˜ˆ์‹œ ์ฐธ๊ณ )
    <BrowserRouter>
     <App />
    </BrowserRouter> //์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ–ˆ์„ ์‹œ ์ž๋™์œผ๋กœ ์ƒ๋‹จ์— ๋ถˆ๋Ÿฌ์™€์ง€๋Š” ๊ฒฝ๋กœ
    
    // import { BrowserRouter } from 'react-router-dom'; //๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ๋ฒ• !
    
    //โž•์ถ”๊ฐ€๋‚ด์šฉ : `import App from './App';` ์ด๋Ÿฐ์‹์œผ๋กœ ./App ๋ถˆ๋Ÿฌ์™€์ง€๋Š” ๊ฒฝ๋กœ๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“  jsํŒŒ์ผ์ด๋ฉฐ
    // `'react-router-dom'` ์ด๋ ‡๊ฒŒ ๋ถˆ๋Ÿฌ์™€์ง€๋Š” ๊ฑด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค
  1. App.js ํŒŒ์ผ ์ƒ๋‹จ > import { Routes, Route, Link } from "react-router-dom"; ์ ๊ธฐ

โญ React ๋ผ์šฐํ„ฐ ์‚ฌ์šฉ

  1. Route๋Š” ํŽ˜์ด์ง€์ด๊ณ  Routes๋กœ ๊ฐ์‹ธ์ค˜์•ผ ํ•œ๋‹ค. <Route path="/url๊ฒฝ๋กœ" element={ <๋ณด์—ฌ์ค„html> } /> ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ๋˜๋Š”๋ฐ ๊ฒฝ๋กœ /๋งŒ ์ ์—ˆ์„ ๊ฒฝ์šฐ์—๋Š” ๋ฉ”์ธํŽ˜์ด์ง€์ด๋‹ค

    โž• ๋ฉ”์ธํŽ˜์ด์ง€์—์„œ ๋ณด์—ฌ์•ผ ํ•  ๋‚ด์šฉ๋“ค์€ element ์•ˆ์—๋งŒ ๋„ฃ์–ด์ฃผ๋ฉด ๋ฉ”์ธํŽ˜์ด์ง€ ๋‚ด์šฉ์€ ๋ฉ”์ธํŽ˜์ด์ง€์—์„œ๋งŒ ๋ณด์—ฌ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ธํŽ˜์ด์ง€์— ์ ‘์†ํ–ˆ์„ ๋•Œ ๋ฉ”์ธํŽ˜์ด์ง€ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์—†๋‹ค ! (๋‚ด์šฉ์„ ์ ์„ ๋• ์ปดํฌ๋„ŒํŠธํ™” ํ•ด์„œ ์ ๊ธฐ)

  2. ๋ฒ„ํŠผ์„ ๋งŒ๋“ค ๋•Œ์—๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑ

<Link to="/">ํ™ˆ</Link> //๋ฉ”์ธํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋Š” ํ™ˆ๋ฒ„ํŠผ
<Link to="/detail">์ƒ์„ธํŽ˜์ด์ง€</Link> //์ƒ์„ธํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋Š” ๋ฒ„ํŠผ
// Link๋Š” aํƒœ๊ทธ ์ฒ˜๋Ÿผ ๋ณด์—ฌ์ง„๋‹ค ๊ทธ๊ฒŒ ์‹ซ์œผ๋ฉด ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋†“์€ nav ํƒœ๊ทธ์— onClick์„ ๋„ฃ์–ด ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•ด์ค„ ์ˆ˜ ๋„ ์žˆ๋‹ค

<Nav.Link
  onClick={() => {
  navigate("/detail");
  }}
 >
   Detail
 </Nav.Link>
// navigate์˜ ๋œป์€ ์•„๋ž˜ ์ฐธ๊ณ 

๐ŸŽ Hook useNavigate ?

  • ํŽ˜์ด์ง€ ์ด๋™์„ ๋„์™€์ฃผ๋Š” ๊ฒƒ !
  • navigate๋Š” ์ˆซ์ž๋กœ๋„ ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค
    navigate(2) ์ˆซ์ž ๋„ฃ์œผ๋ฉด ์•ž์œผ๋กœ 2๋ฒˆ ๊ฐ€๊ธฐ ๊ธฐ๋Šฅ, navigate(-1) ๋’ค๋กœ 1๋ฒˆ ๊ฐ€๊ธฐ ๊ธฐ๋Šฅ์„ ์“ธ ์ˆ˜ ์žˆ๋‹ค

๐ŸŽ 404ํŽ˜์ด์ง€ ?

  • ์œ ์ €๊ฐ€ ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ์ ‘์†ํ–ˆ์„ ๋•Œ "์—†๋Š” ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค" ๋‚˜ํƒ€๋‚ด ์ค„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•
http://localhost:3000/detail //detail ํŽ˜์ด์ง€ ์ฃผ์†Œ
http://localhost:3000/detail4546 // ํŽ˜์ด์ง€ ์ฃผ์†Œ ๋’ค์— ์•Œ์ˆ˜์—†๋Š” ๋ฌธ์ž, ์ˆซ์ž๊ฐ€ ๋ถ™์„ ๋•Œ

<Route path="*" element={<div>์—†๋Š” ํŽ˜์ด์ง€</div>} /> 
// ์ด ์™ธ์— ๋ชจ๋“  ๊ฒƒ์ธ "*"์„ ๋ถ™์ด๋ฉด ๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ์ ‘์†ํ–ˆ์„ ๋•Œ ์—†๋Š” ํŽ˜์ด์ง€๋ผ๊ณ , ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋œ๋‹ค

๐ŸŽ ์„œ๋ธŒ ๊ฒฝ๋กœ Nested Routes ?

  • <Route>์•ˆ์— <Route>๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•

<์žฅ์ >

  • ์ž‘์„ฑํ•  ๋•Œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค
  • Nested Routes ์ ‘์† ์‹œ์— Element 2๊ฐœ๊ฐ€ ๋ณด์ธ๋‹ค
  • ํŽ˜์ด์ง€ ์ด๋™์ด ์‰ฝ๋‹ค
<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)๋กœ ์ ‘์†ํ•ด ์žˆ์„ ๋•Œ ์„œ๋กœ ๋‹ค๋ฅธ ๋‚ด์šฉ์ด ๋ณด์—ฌ์ง€๋Š” ๋ถ€๋ถ„ ! )

๐ŸŽ ํŽ˜์ด์ง€ ์—ฌ๋Ÿฌ๊ฐœ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ

โญ < URL ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ >

  • ํŽ˜์ด์ง€๊ฐ€ 100๊ฐœ๋ผ๊ณ  ํ–ˆ์„ ๋•Œ <Route>๋ฅผ 100๊ฐœ ๋งŒ๋“ค๊ณ  ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ๋ฒˆํ˜ธ๋ฅผ ๋งค๋ฒˆ ๋ถ€์—ฌํ•  ์ˆ˜๋Š” ์—†๋‹ค ๊ทธ๋ž˜์„œ URlํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š”๋ฐ ๋ฌธ๋ฒ•์€ ํŒŒ์ผ๊ฒฝ๋กœ ๋’ค์— /:id ๋ฅผ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค.
  • /: ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด "์•„๋ฌด ๋ฌธ์ž"๋ฅผ ์˜๋ฏธํ•จ. ๋’ค์—๋Š” ์ž‘๋ช…์„ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค (ex. 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๊ฐ’)์„ ์ ์–ด์ฃผ๋ฉด ๋ฐฐ์—ด์˜ ์ˆœ์„œ๊ฐ€ ์•„๋ฌด๋ฆฌ ๋ณ€๊ฒฝ๋˜์–ด๋„ ์˜๊ตฌ๋ฒˆํ˜ธ๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์–ด์„œ ํ™”๋ฉด์— ์ •์ƒ์ ์œผ๋กœ ์ž˜ ์ž‘๋™์ด ๋œ๋‹ค

โš ๏ธ ์˜ค๋ฅ˜ (product ๊ฐ’ undefined)

const products = shoes.find((product) => product.id === parseInt(id));
  • parseInt๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” ์•ž์—์„œ ๊ฒฝ๋กœ๋’ค์— /:id๊ฐ€ ๋ฌธ์ž์—ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ˆซ์ž===๋ฌธ์ž ์—„๊ฒฉ ๋น„๊ตํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— parseInt๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฌธ์ž์—ด์„ ์ˆซ์ž๋กœ ๋ณ€๊ฒฝ์„ ํ•ด์คฌ๋‹ค == ๋А์Šจ๋น„๊ต๋ฅผ ํ•˜๋ฉด parseInt๋ฅผ ์ ์–ด์ฃผ์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์ง€๋งŒ ๋น„๊ต๋ฅผ ํ•  ๋•Œ์—๋Š” ===์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค

๐ŸŽ Nested Routes ์‚ฌ์šฉํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

โญ <styled-components์„ค์น˜>

  1. npm install styled-components ์„ค์น˜
  2. import styled from 'styled-components'; import ํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
  3. ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— App์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ์ž‘์„ฑํ•˜๊ณ , ์ฒซ๊ธ€์ž๋Š” ๋Œ€๋ฌธ์ž๋กœ ํ•œ๋‹ค
//์ž‘์„ฑ๋ฐฉ๋ฒ•
const YellowBtn = styled.button`
  background: yellow;
  color: black;
  padding: 10px;
`; 

<YellowBtn>๋ฒ„ํŠผ</YellowBtn> // style์ด ์ž…ํ˜€์ง„ ๋ฒ„ํŠผ์„ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค

<styled-components ์žฅ์ >

  • CSS ํŒŒ์ผ์— ๋”ฐ๋กœ ์ž‘์„ฑ ํ•  ํ•„์š”์—†์ด JsํŒŒ์ผ์—์„œ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค
  • ํŽ˜์ด์ง€ ๋กœ๋”ฉ์‹œ๊ฐ„์ด ๋‹จ์ถ•๋œ๋‹ค (html์— <style> ๋„ฃ์€ ๊ฒƒ๊ณผ ๊ฐ™๋‹ค)

<styled-components ๋‹จ์ >

  • JS ํŒŒ์ผ์ด ๋ณต์žกํ•ด์ง„๋‹ค(์ปดํฌ๋„ŒํŠธ๊ฐ€ styled์ธ ์ง€ ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ์ธ ์ง€ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง„๋‹ค)

๐ŸŽ CSSํŒŒ์ผ, JS ํŒŒ์ผ์— ๊ฐ„์„ญ๋ฐ›์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•

  • ๋ชจ๋“ˆํ™” ๊ธฐ๋Šฅ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค
// css ์ž‘๋ช…๋ฐฉ๋ฒ•
์ปดํฌ๋„ŒํŠธ๋ช….module.css 
// import ํ•ด์„œ ๋‹ค๋ฅธ jsํŒŒ์ผ์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๊ทธ ์Šคํƒ€์ผ๋“ค์€ ์ปดํฌ๋„ŒํŠธ๋ช….js ํŒŒ์ผ์—๋งŒ ์ ์šฉ๋œ๋‹ค

๐ŸŽ ๋น„์Šทํ•œ UI ์ผ ๊ฒฝ์šฐ props ์‚ฌ์šฉ์˜ˆ์‹œ

โญ ๋…ธ๋ž€์ƒ‰๋ฒ„ํŠผ๊ณผ ์˜ค๋ Œ์ง€์ƒ‰ ๋ฒ„ํŠผ์ด ํ•„์š” ํ•  ๊ฒฝ์šฐ

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>

๐ŸŽ Lifecycle(๋ผ์ดํ”„์‚ฌ์ดํด)

  • ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์—๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์žˆ๋‹ค ์ƒ๋ช…์ฃผ๊ธฐ๋ž€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ, ์‚ฌ์šฉ, ์†Œ๋ฉธ ๋  ๋•Œ๊นŒ์ง€์˜ ์ผ๋ จ์˜ ๊ณผ์ •์„ ๋งํ•œ๋‹ค
  • ์ƒ๋ช…์ฃผ๊ธฐ ์•ˆ์—์„œ๋Š” ํŠน์ •์‹œ์ ์— ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ,, ๊ทธ๊ฒƒ์„ Life Cycle ์ด๋ฒคํŠธ๋ผ๊ณ  ํ•œ๋‹ค
  • Life Cycle ์ด๋ฒคํŠธ๋ž€ ์ปดํฌ๋„ŒํŠธ ๋™์ž‘์„ ์ œ์–ดํ•˜๊ณ , ์ž‘์—…์ˆ˜ํ–‰์„ ํ–ฅ์ƒ ์‹œํ‚ค๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด๋‚ด๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค

<๋ผ์ดํ”„ ์‚ฌ์ดํด์˜ ์žฅ์ >

  • ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค
  • ์›ํ•˜๋Š” ์‹œ์ ์— ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜, DOM ์ด๋ฒคํŠธ ๋˜๋Š” ๋‹ค๋ฅธ ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ†ตํ•ฉํ•  ๋•Œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค

โ—<๋ผ์ดํ”„ ์‚ฌ์ดํด์˜ ์ด๋ฒคํŠธ ๋ถ„๋ฅ˜>โ—: ์ƒ์„ฑ(Mount)โžก๏ธ์‚ฌ์šฉ(Update)โžก๏ธ์†Œ๋ฉธ(Unmount)

โœ”๏ธ ์ƒ์„ฑ(Mount) : ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰

  • ํ™”๋ฉด์— ์ฒ˜์Œ ๋ Œ๋”๋ง์ด ๋œ๋‹ค

โœ”๏ธ ์‚ฌ์šฉ(Update) : ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰

  • ์ปดํฌ๋„ŒํŠธ์˜ ์†์„ฑ, ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ Œ๋”๋ง์ด ๋œ๋‹ค

โœ”๏ธ ์†Œ๋ฉธ(Unmount) : ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰

  • DOM ๋…ธ๋“œ์—์„œ ์ œ๊ฑฐ ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง„๋‹ค

๐ŸŽ Lifecycle hook (useEffect)

โœ๏ธ ๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ์ฝ”๋“œ ์‹คํ–‰

useEffect(() => { 
    //์ปดํฌ๋„ŒํŠธ๊ฐ€ mount & update์‹œ ์‹คํ–‰๋œ๋‹ค
  }); 
  • console.log ์ถœ๋ ฅ ์‹œ ๋‘๋ฒˆ ์ถœ๋ ฅ์ด ๋˜๋Š”๋ฐ ๊ทธ ์ด์œ ๋Š” index.js์— <React.StrictMode>๋ผ๋Š” ํƒœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ๋‘๋ฒˆ ์ถœ๋ ฅ์ด ๋œ๋‹ค

  • โž• useEffect ๋ฐ”๊นฅ์— ์ ์–ด๋„ ๋˜‘๊ฐ™์ด ์ปดํฌ๋„ŒํŠธ mount & update์‹œ ์‹คํ–‰์ด ๋œ๋‹ค ๊ทธ๋Ÿฐ๋ฐ ์™œ useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋ƒ๋ฉด ? useEffect ์•ˆ์— ์ ์€ ์ฝ”๋“œ๋Š” html ๋ Œ๋”๋ง ์ดํ›„์— ๋™์ž‘์„ ํ•˜๋Š”๋ฐ html์ด ๋จผ์ € ๋น ๋ฅด๊ฒŒ ๋ณด์ด๊ณ  ์‹ถ์€ ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜ ์ด๋ฏธ์ง€ ์ฐธ๊ณ  !

๐Ÿ’ก ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ(Mount) 1ํšŒ๋งŒ ์‹คํ–‰

useEffect(() => { 
   // ์‹คํ–‰ํ• ์ฝ”๋“œ
  }, []);
  • ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ง‰์•„์ค€๋‹ค

๐Ÿ’ก useEffect ์•ˆ์˜ ์ฝ”๋“œ ์‹คํ–‰๋˜๊ธฐ ์ „์— ํ•ญ์ƒ ์‹คํ–‰

useEffect(()=>{ 
  return ()=>{
   // ์‹คํ–‰ํ• ์ฝ”๋“œ
  }
})
  • clean up function. ๊ธฐ์กด์ฝ”๋“œ๋ฅผ ์—†์• ๊ณ , ์‹คํ–‰ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค

๐Ÿ’ก ์ปดํฌ๋„ŒํŠธ ์†Œ๋ฉธ(Unmount) 1ํšŒ ์‹คํ–‰

useEffect(()=>{ 
  return ()=>{
  // ์‹คํ–‰ํ• ์ฝ”๋“œ
  }
}, [])
  • ์ƒ์„ฑ(mount)์‹œ ์‹คํ–‰์ด ์•ˆ ๋˜๊ณ , ์†Œ๋ฉธ(Unmount) ์‹œ ์‹คํ–‰๋œ๋‹ค

๐Ÿ’ก state1์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์‹คํ–‰

useEffect(()=>{ 
 // ์‹คํ–‰ํ• ์ฝ”๋“œ
}, [state1])

๐ŸŽ setTimeout

  • ๋ช‡ ์ดˆ ํ›„์— ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค
setTimeout( ()=>{  
  1์ดˆ ํ›„ ์‹คํ–‰ํ•  ์ฝ”๋“œ 
}, 1000); // 1000์€ 1์ดˆ

๐ŸŽ ์„œ๋ฒ„์™€ ํ†ต์‹ 

  • ์„œ๋ฒ„๋ž€ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค ์˜ˆ๋ฅผ ๋“ค์–ด์„œ youtube์„œ๋ฒ„์˜ ๋™์˜์ƒ์„ ์š”์ฒญํ–ˆ์„ ๋•Œ ๋™์˜์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋งํ•œ๋‹ค
  • ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ๋•Œ (Get/Post)๋ฐฉ๋ฒ•๊ณผ URL ์ž๋ฃŒ ํ˜•์‹์„ ์ง€์ผœ์„œ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค
  • Get์š”์ฒญ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ, Post ์š”์ฒญ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค
  • ์„œ๋ฒ„์™€ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ์—๋Š” ๋ฌธ์ž๋งŒ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค
    ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด array object๋„ ์ฃผ๊ณ  ๋ฐ›๋Š”๋ฐ "๋”ฐ์˜ดํ‘œ" ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฐ์—ด์ด๋‚˜ ๊ฐ์ฒด๋„ ์ฃผ๊ณ ๋ฐ›๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค ์ด๊ฒƒ์„ JSON ์ด๋ผ๊ณ  ํ•œ๋‹ค

< GET ์š”์ฒญํ•˜๋Š” ๋ฐฉ๋ฒ• >

๐ŸŽ Ajax ๋ฐ์ดํ„ฐ ์š”์ฒญ

  • ์ƒˆ๋กœ๊ณ ์นจ ์—†์ด GET/POST ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•˜๋‹ค
    < Ajax ์‚ฌ์šฉ, 3๊ฐœ์˜ ๋ฐฉ๋ฒ• >
    (1) XMLHttpRequest
    (2) fetch()
    (3) axios (์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ณ , ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค)

๐ŸŽ axios ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ

โญ 1. axios ์„ค์น˜

โžก๏ธ npm install axios

โญ 2. App.jsx์— import ํ•ด์˜ค๊ธฐ

โžก๏ธ import axios from 'axios';

โญ 3. axios Get์š”์ฒญ ์˜ˆ์‹œ

  • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ 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>

  1. ๋™์‹œ์— ์—ฌ๋Ÿฌ๊ฐœ ajax Get์š”์ฒญ ์˜ˆ์‹œ
Promise.all([axios.get('/url1'), axios.get('/url2')])

๐ŸŽ Tab UI ๋งŒ๋“ค๊ธฐ

(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

  • props ์ „์†ก ์—†์ด state ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค

< Context APi ๋‹จ์  >

  • state ๋ณ€๊ฒฝ ์‹œ ๋ถˆํ•„์š”ํ•œ ๊ฒƒ๋“ค ๊นŒ์ง€ ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค
  • ๋‚˜์ค‘์— ์ปดํฌ๋„ŒํŠธ ์žฌ์‚ฌ์šฉ์ด ์–ด๋ ค์›Œ์งˆ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธํŽ˜์ด์ง€์—์„œ ์žฌ์‚ฌ์šฉ ํ–ˆ์„ ๊ฒฝ์šฐ์—๋Š” ์ด์ƒํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜๋„ ์žˆ์Œ(state ๋กœ ๋ฐ›์•„์˜จ ๋ณ€์ˆ˜์˜ ๊ฐ’์ด ์—†๋‹ค๊ณ  ๋‚˜์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค)
    โž•์ด๋Ÿฌํ•œ ๋‹จ์ ๋“ค ๋•Œ๋ฌธ์— context๋ณด๋‹ค ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ redux๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค

โญ <Context ์‚ฌ์šฉ๋ฐฉ๋ฒ•>

(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 ๋ถˆ๋Ÿฌ์˜ด
  • Detail ์ปดํฌ๋„ŒํŠธ ์ž์‹์ธ TabContent ์—์„œ๋„ useContext๋ฅผ ์“ฐ๊ฒŒ๋˜๋ฉด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค

๐ŸŽ Redux๋กœ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

  • 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 ๋ฐ ์ปดํฌ๋„ŒํŠธํ™” ํ•˜๊ธฐ

โญ <Redux ์„ค์น˜>

(1) ์„ค์น˜๋ฐฉ๋ฒ• โžก๏ธ npm install @reduxjs/toolkit@1.8.1 react-redux
(2) package.json ํŒŒ์ผ > "react", "react-dom" ํ•ญ๋ชฉ์˜ ๋ฒ„์ „์„ ํ™•์ธํ•œ๋‹ค ๋ฒ„์ „์€ 18.1 ๋ฒ„์ „ ์ด์ƒ์ธ ์ง€ ํ™•์ธํ•œ๋‹ค
(3) ๋ฒ„์ „ ํ™•์ธ ๋๋‚˜๋ฉด ํ„ฐ๋ฏธ๋„์— ์ž…๋ ฅ โžก๏ธ npm install

โญ <Redux ์„ธํŒ…>

  • store.jsx ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ์„ธํŒ…ํ•ด๋‘๊ธฐ
  • store.jsx ํŒŒ์ผ์˜ ์šฉ๋„๋Š” state๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ํŒŒ์ผ์ด๋‹ค
// store.jsx

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
}) 
  • index.js ํŒŒ์ผ > Provider, store.jsx ํŒŒ์ผ import ํ•ด์˜ค๊ธฐ
//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.js, state ๋ณด๊ด€

//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,
  },
});
  • ๋“ฑ๋กํ•œ state๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค

โญ store.js, state ์‚ฌ์šฉ๋ฐฉ๋ฒ•

//Cart.jsx

import { useSelector } from "react-redux"

  const a = useSelector((state) => { 
    return state; // store์— ์žˆ๋˜ ๋ชจ๋“  state๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค
  });

โญ store.js, 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>
  • state๋ณ€๊ฒฝ ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ dispatch(changeName())๋ฅผ ์ ์—ˆ๋‹ค๊ณ  ํ•ด์„œ ์ € ์ž๋ฆฌ์—์„œ ํ•จ์ˆ˜๋ฅผ ๋ฐ”๋กœ ์‹คํ–‰ํ•ด์ฃผ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ store.jsx ํŒŒ์ผ์— ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค

๐ŸŽ Redux state๊ฐ€ ๋ฐฐ์—ด ๋˜๋Š” ๊ฐ์ฒด๋ผ๋ฉด ?

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๋Š” ํƒ๋ฐฐ, ํ™”๋ฌผ ์ด๋Ÿฐ๋œป 
    },
  },
});

์ถœ์ฒ˜

์ฝ”๋”ฉ์• ํ”Œ ๋ฆฌ์•กํŠธ

0๊ฐœ์˜ ๋Œ“๊ธ€