๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Frontend/React.js

React ์‚ฌ์šฉํ•˜๊ธฐ

๐Ÿ“’ ์™œ? (React ์‚ฌ์šฉ ์ด์œ )

โ—ผ Single Page Application์„ ๋งŒ๋“ค ๋•Œ, ์‚ฌ์šฉํ•œ๋‹ค.

   ๐Ÿ’ฌ Single Page Application์€ ์ƒˆ๋กœ๊ณ ์นจ ์—†์ด ๋Œ์•„๊ฐ€๋Š” ํŽ˜์ด์ง€๋กœ ์•ฑ์ฒ˜๋Ÿผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

โ—ผ๋ฐ”๋‹๋ผ JS๋กœ ์›น ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค ๊ฒฝ์šฐ, ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€๊ณ  ๋„ˆ๋ฌด ๋ณต์žกํ•ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ React๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด HTML ์žฌ์‚ฌ์šฉ์ด ํŽธ๋ฆฌํ•ด์ง„๋‹ค.

โœจ React๋Š” HTML, CSS, JavaScript ์›น ๊ฐœ๋ฐœ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.


๐Ÿ“’ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ธํŒ…ํ•˜๊ธฐ

1.  node js๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

2.  ํ„ฐ๋ฏธ๋„์—์„œ ์›ํ•˜๋Š” ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•œ ๋’ค, npx create-react-app {ํ”„๋กœ์ ํŠธ ํด๋”๋ช…} ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. 

   ๐Ÿ’ฌ create-react-app(cra)๋Š” React๋กœ ๊ฐœ๋ฐœ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ, ์›นํŒฉ, ๋ฐ”๋ฒจ ๋“ฑ ๊ธฐ๋ณธ์ ์ธ ํ”„๋กœ์ ํŠธ ์„ธํŒ…์„ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

   ๐Ÿ’ฌ ์›นํŒฉ(webpack), ๋ฐ”๋ฒจ(babel)์˜ ์—ญํ• ์€ ES ์ตœ์‹  ๋ฒ„์ „์˜ ์ฝ”๋“œ๋“ค์„ ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

3. ๊ฐœ์ธ IDE๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ Open ํ•˜๊ณ , ํ„ฐ๋ฏธ๋„์—์„œ npm start ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด localhost:3000์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.  

โœ” ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

โ—ผ node_modules ํด๋”๋Š” ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฝ”๋“œ ๋ณด๊ด€ํ•จ์ด๋‹ค.

โ—ผ public ํด๋”๋Š” static ํŒŒ์ผ์„ ๋ณด๊ด€ํ•˜๋Š” ๊ณณ์ด๋‹ค.

   ๐Ÿ’ฅ public ํด๋” ์•ˆ์— ์žˆ๋Š” ํŒŒ์ผ๋“ค์€ bundling ์ž‘์—…์ด ์ง„ํ–‰๋  ๋•Œ, ์••์ถ•๋˜์ง€ ์•Š๋Š”๋‹ค.

โ—ผ src/App.js๊ฐ€ ๋ฉ”์ธ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์žˆ๋Š” JS ํŒŒ์ผ์ด๋‹ค.

   ๐Ÿ’ฅ ๋ณดํ†ต์€ HTML ํŒŒ์ผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ์ด์ง€๋งŒ, src ํด๋” ์•„๋ž˜ html ํŒŒ์ผ์ด ์—†๋Š” ์ด์œ ๋Š” public ํด๋” ์•„๋ž˜ index.html์ด ์กด์žฌํ•˜๊ณ , App.js์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ index.js์—์„œ root์— render ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

โ—ผ package.json์€ ํ”„๋กœ์ ํŠธ ์ •๋ณด๋“ค์ด ๊ธฐ์ž…๋˜๋Š” ํŒŒ์ผ์ด๋‹ค.


๐Ÿ“’ JSX

โ—ผ JavaScript ์•ˆ์—์„œ HTML๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.

โ—ผ ์›๋ž˜ React์—์„œ div ํƒœ๊ทธ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ React.createElement("{ํƒœ๊ทธ๋ช…}", null, "{๊ฐ’}") ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋Š”๋ฐ, ์ด๋ฅผ JSX๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

 

โœ” ์‚ฌ์šฉ๋ฒ•

import "./App.css";

function App() {
  return (
    <div className="App">
      <div className="black-nav">
        <h4>Nav</h4>
      </div>
    </div>
  );
}

export default App;

โ—ผ css ํŒŒ์ผ์˜ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” class="{ํด๋ž˜์Šค ๋ช…}"์„ ์‚ฌ์šฉํ–ˆ์—ˆ๋Š”๋ฐ, JSX ์—์„œ๋Š” className์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

โ—ผ class๋Š” ์ด๋ฏธ JavaScript์—์„œ class ๋ฌธ๋ฒ•์œผ๋กœ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, className์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

import "./App.css";

function App() {
  let title = "์ œ๋ชฉ์ž…๋‹ˆ๋‹ค.";

  return (
    <div className="App">
      <div className="black-nav">
        <h4>Nav</h4>
      </div>
      <h4>{title}</h4>
    </div>
  );
}

export default App;

โ—ผ JSX์—์„œ๋Š” ์ค‘๊ด„ํ˜ธ ( {} )๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ณ€์ˆ˜ ๊ฐ’์„ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ ์ค‘๊ด„ํ˜ธ ์•ˆ์—์„œ๋Š” JavaScript์˜ if๋ฌธ, for๋ฌธ ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

โ—ผ ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด๋ผ๊ณ  ํ•œ๋‹ค.

 

				...
<h4 style={ { color: "red", fontSize: "50px" } }>{title}</h4>
				...

โ—ผ JSX์—์„œ style ์†์„ฑ์„ style={ {color : "red"} } ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ font-size์˜ ๊ฒฝ์šฐ -๋Š” JavaScript์—์„œ ๋นผ๊ธฐ๋กœ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์นด๋ฉœ ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด์„œ ๊ธฐ์ž…ํ•ด์•ผ ํ•œ๋‹ค. 

 

import "./App.css";

function App() {
  let title = "์ œ๋ชฉ์ž…๋‹ˆ๋‹ค.";

  return (
    <div></div>
    <div></div>
  );
}

export default App;

โ—ผ return () ์•ˆ์—๋Š” ๋ณ‘๋ ฌ๋กœ ํƒœ๊ทธ 2๊ฐœ ์ด์ƒ์„ ๊ธฐ์ž…ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

โ—ผ ์ตœ์ƒ์œ„ ๋ถ€๋ชจ ํƒœ๊ทธ๋Š” 1๊ฐœ๋กœ ํ†ต์ผ์‹œ์ผœ์•ผ ํ•œ๋‹ค.

 

return (
  <>
    <div></div>
    <div></div>
  </>
)

โ—ผ return() ์•ˆ์— ํ•˜๋‚˜์˜ ํƒœ๊ทธ๊ฐ€ ๋ฃจํŠธ์—ฌ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์—ฌ๋Ÿฌ ํƒœ๊ทธ๋ฅผ ๋ฌถ๊ธฐ ์œ„ํ•ด์„œ ์˜๋ฏธ ์—†์ด ์‚ฌ์šฉํ•˜๋Š” ํƒœ๊ทธ๋ฅผ <></>์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ useState

โ—ผ ์ž๋ฃŒ๋ฅผ ์ž ๊น ์ €์žฅํ•  ๋•Œ๋Š” ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” useState๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค.

 

โœ” ๋ณ€์ˆ˜์™€ State์˜ ์ฐจ์ด์ 

โ—ผ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ HTML์— ๋ฐ˜์˜ํ•ด์„œ ํ™”๋ฉด์— ์ถœ๋ ฅ๋œ ์ดํ›„, ํ•ด๋‹น ๋ณ€์ˆ˜ ๊ฐ’์„ ์ˆ˜์ •ํ•ด๋„ ํ™”๋ฉด์˜ ๊ฐ’์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค.

โ—ผ ๋ฐ˜๋ฉด, State๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  State ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด, ์ˆ˜์ • ๋ถ€๋ถ„์˜ ์žฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์—ฌ ํ™”๋ฉด์˜ ๊ฐ’๋„ ๋ณ€๊ฒฝ๋œ๋‹ค.

๐Ÿ’ฅ ์ฆ‰, ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ๋•Œ, HTML์— ๋ฐ˜์˜๋˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์œผ๋ฉด State๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

โœ” ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

import { useState } from "react";
import "./App.css";

function App() {
  let [title, setTitile] = useState("์ œ๋ชฉ์ž…๋‹ˆ๋‹ค.");
  return (
    <div className="App">
      <div className="black-nav">
        <h4>Blog</h4>
      </div>

      <div className="content-list">
        <h4>{title}</h4>
      </div>
    </div>
  );
}

export default App;

โ—ผ [{์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ช…}, {๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ช…}] = useState("{์ดˆ๊ธฐํ™” ๊ฐ’}")์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ state ๊ฐ’์„ ๋“ฑํ˜ธ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด HTML์— ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์žฌ๋ Œ๋”๋ง์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋“ฑ๋กํ•œ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

โ—ผ ์—ฌ๊ธฐ์„œ title ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, setTitle("{์›ํ•˜๋Š” ๊ฐ’}") ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ’ฅ set~~~ ํ•จ์ˆ˜๋Š” ๋Šฆ๊ฒŒ ์‹คํ–‰๋œ๋‹ค. ๊ทธ๋ž˜์„œ, set~~ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋‚˜๋ฉด ์•„๋ž˜ ์ฝ”๋“œ ๋ถ€ํ„ฐ ๋‹ค ์‹คํ–‰ํ•œ ๋’ค, set~~ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

 

						...
let [title, setTitile] = useState(["์ œ๋ชฉ1", "์ œ๋ชฉ2", "์ œ๋ชฉ3"]);
<div className="content-list">
  <h4>{title[0]}</h4>
  <p>2์›” 28์ผ ๋ฐœํ–‰</p>
</div>
<div className="content-list">
  <h4>{title[1]}</h4>
  <p>2์›” 28์ผ ๋ฐœํ–‰</p>
</div>
<div className="content-list">
  <h4>{title[2]}</h4>
  <p>2์›” 28์ผ ๋ฐœํ–‰</p>
</div>
						...

โ—ผ ์œ„์™€ ๊ฐ™์ด ๋ฐฐ์—ด์„ ๋‹ด๊ณ , ์ธ๋ฑ์Šค ์ ‘๊ทผ์œผ๋กœ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โœ” state ๋ณ€๊ฒฝ ํ•จ์ˆ˜ ํŠน์ง•

const [titles, setTitile] = useState(["์ œ๋ชฉ1", "์ œ๋ชฉ2", "์ œ๋ชฉ3"]);

const changeTitle = (idx, title) => {
    titles[idx] = title;
    setTitile(titles);
  };

โ—ผ ๊ธฐ์กด state์™€ ์‹ ๊ทœ state๊ฐ€ ๊ฐ™์„ ๊ฒฝ์šฐ(๊ธฐ์กด state == ์‹ ๊ทœ state) ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค.

โ—ผ ๋”ฐ๋ผ์„œ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

const [titles, setTitile] = useState(["์ œ๋ชฉ1", "์ œ๋ชฉ2", "์ œ๋ชฉ3"]);

// 1๋ฒˆ
const changeTitle = (idx, title) => {
    const copy = [...titles];
    copy[idx] = title;
    setTitile(copy);
  };
  
 // 2๋ฒˆ
 const changeTitle = (idx, title) => {
    titles[idx] = title;
    setTitles([...titles]);
  };

โ—ผ ์œ„์™€ ๊ฐ™์ด 1๋ฒˆ, 2๋ฒˆ ๋ฐฉ๋ฒ•์œผ๋กœ ์ƒˆ๋กœ์šด Array๋ฅผ ๋งŒ๋“ค์–ด์„œ state๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ์ ์šฉ๋œ๋‹ค.

โ—ผ ์ฆ‰, state๊ฐ€ Array๋‚˜ Object์ด๋ฉด์„œ state๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ, ๋…๋ฆฝ์ ์ธ ์นดํ”ผ๋ณธ์„ ๋งŒ๋“ค์–ด์„œ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค. 


๐Ÿ“’ Component

โ—ผ div ํƒœ๊ทธ์˜ ํ•œ ๋ฉ์–ด๋ฆฌ๋ฅผ ํ•˜๋‚˜์˜ ๋‹จ์–ด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ฌธ๋ฒ•

 

โœ” Component๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

1. function์„ ๋งŒ๋“ ๋‹ค.

2. return () ์•ˆ์— html์„ ๋‹ด๋Š”๋‹ค.

3. <ํ•จ์ˆ˜๋ช…></ํ•จ์ˆ˜๋ช…>์œผ๋กœ ์ž‘์„ฑํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

 

โœ” Component ์–ด๋–ค ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ• ๊นŒ?

1. ๋ฐ˜๋ณต์ ์ธ HTML์„ ์ถ•์•ฝํ•  ๋•Œ

3. ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” HTML์ธ ๊ฒฝ์šฐ

 

function Modal() {
  return (
    <div className="modal">
      <h4>์ œ๋ชฉ</h4>
      <p>๋‚ ์งœ</p>
      <p>์ƒ์„ธ ๋‚ด์šฉ</p>
    </div>
  );
}

โ—ผ ์œ„์™€ ๊ฐ™์ด Component๋ฅผ ๋งŒ๋“ค๊ณ , <Modal></Modal> or <Modal /> ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ ๋ฐ˜๋ณต๋ฌธ

โ—ผ ์ค‘๊ด„ํ˜ธ ์•ˆ์— JavaScript ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, for๋ฌธ ๋Œ€์‹ , map()์„ ์‚ฌ์šฉํ•ด์„œ ๋ฐ˜๋ณต๋˜๋Š” HTML์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

{titles.map((title, idx) => {
    return (
      <div className="content-list" key={idx}>
        <h4
          onClick={() => {
            modal ? setModal(false) : setModal(true);
          }}
        >
          {title + " "}
          <span
            onClick={(event) => {
              addLikeCount(idx);
              event.stopPropagation();
            }}
          >
            ๐Ÿคž{" "}
          </span>
          {likeCounts[idx]}
        </h4>
      </div>
    );
  })}

 

โ—ผ map ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ธ€ ์ œ๋ชฉ ๊ฐœ์ˆ˜๋งŒํผ, div ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค.

โ—ผ HTML์„ ๋ฐ˜๋ณต ์ƒ์„ฑํ•˜๋ฉด, key ์†์„ฑ์„ ํ†ตํ•ด Unique ํ•จ์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.


๐Ÿ“’ Props

โ—ผ ๋ณ€์ˆ˜์˜ Scope์— ์˜ํ•ด์„œ, ๋ถ€๋ชจ Component์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด ์ง€์›ํ•˜๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.

โ—ผ ๋ถ€๋ชจ Component์˜ state๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์ „๋‹ฌํ•ด, ์ž์‹ Component์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.

โ—ผ ํ˜•์ œ Component๋‚˜, ์ž์‹ Component์—์„œ ๋ถ€๋ชจ Component๋กœ์˜ ์ „๋‹ฌ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

 

<Modal titles={titles} days={days} contentIdx={contentIdx} />

const Modal = ({ titles, days, contentIdx }) => {
  return (
    <div className="modal">
      <h4>{titles[contentIdx]}</h4>
      <p>{days[contentIdx]}</p>
      <p>์ƒ์„ธ ๋‚ด์šฉ</p>
    </div>
  );
};

โ—ผ ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด Modal Component์˜ ์†์„ฑ์œผ๋กœ ์ „๋‹ฌํ•œ Props ๋ณ€์ˆ˜๋ช…์„ ์„ ์–ธ, ํ• ๋‹น์„ ํ•˜๊ณ , Modal ํ•จ์ˆ˜์—์„œ { {Props ๋ณ€์ˆ˜๋ช…} }์œผ๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ ์ด๋ฏธ์ง€ ์ฒจ๋ถ€ํ•˜๊ธฐ

โœ” src ํด๋” ์•„๋ž˜ ์กด์žฌํ•˜๋Š” ์ด๋ฏธ์ง€ ์ฒจ๋ถ€ํ•˜๊ธฐ

import mainBg from "./img/shopping.png";

function App() {
  return (
    <div className="App">
      <Nav></Nav>
      <div
        style={{
          backgroundImage: `url(${mainBg})`,
          backgroundSize: "cover",
          backgroundPosition: "center",
          height: "300px",
        }}
      ></div>
    </div>
  );
}

โ—ผ src ํด๋” ๋‚ด์— ์žˆ๋Š” ์‚ฌ์ง„๋“ค์€ import๋ฅผ ์‚ฌ์šฉํ•ด์„œ src ์•„๋ž˜ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์ž…ํ•˜๊ณ  ์‚ฌ์šฉํ•œ๋‹ค.

 

โœ” public ํด๋” ์•„๋ž˜ ์กด์žฌํ•˜๋Š” ์ด๋ฏธ์ง€ ์ฒจ๋ถ€ํ•˜๊ธฐ

function App() {
  return (
    <div className="App">
      <Nav></Nav>
      
      <div className="container">
        <div className="content">
          <img src="/car.png" width="50%"></img>
          <h4>Car</h4>
          <p>์ž๋™์ฐจ์ด๋‹ค.</p>
        </div>
        <div className="content">a</div>
        <div className="content">a</div>
      </div>
    </div>
  );
}

โ—ผ src="/{ํŒŒ์ผ๋ช…}" ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

   ๐Ÿ’ฅ .com์— ๋ฐœํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ. com/{์„œ๋ธŒ ๊ฒฝ๋กœ}/ ์— React ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

   ๐Ÿ’ฅ ์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” src={process.env.PUBLIC_URL + "/car.png"} ํ˜•์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.


๐Ÿ“’ Router

โ—ผ React๋Š” SPA๋กœ index.html ํŒŒ์ผ ํ•˜๋‚˜๋งŒ์„ ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ ๋”ฐ๋ผ์„œ, Router๋Š” ์„ค์ •ํ•œ uri ๋งˆ๋‹ค ํŽ˜์ด์ง€๋ฅผ ๋น„์šฐ๊ณ , ๋“ฑ๋กํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. (๋ผ์šฐํŒ…)

โ—ผ npm install react-router-dom์œผ๋กœ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ”  react-router-dom ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉํ•˜๊ธฐ

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import { Routes, Route } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />} />
      <Route path="/detail" element={<div>์ƒ์„ธ ํŽ˜์ด์ง€</div>} />
      <Route path="*" element={<div>์—†๋Š” ํŽ˜์ด์ง€</div>}></Route>
    </Routes>
  </BrowserRouter>
);

โ—ผ index.js ํŒŒ์ผ์—์„œ <BrowserRouter> -> <Routes> -> <Route>๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ Routes, Route๋ฅผ import ํ•œ๋‹ค.

โ—ผ Route ์ปดํฌ๋„ŒํŠธ๋Š” uri ๋ณ„๋กœ ํŽ˜์ด์ง€๋ฅผ ๋‚˜๋ˆŒ ๋•Œ, ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ path๋Š” uri ์„ค์ •, element๋Š” ํ•ด๋‹น uri๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ, ๋ณด์—ฌ์ค„ element๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ path๋ฅผ "*"๋กœ ์„ค์ •ํ•˜๋ฉด, ์œ„์— ์„ค์ •ํ•œ path ์ œ์™ธ ๋‹ค๋ฅธ uri๋ฅผ ์ž…๋ ฅ ์‹œ, ํ•ด๋‹น element๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

 

import { Link } from "react-router-dom";

const Item = ({ item }) => {
  return (
    <div className="content">
      <img src={item.imageSrc} width="50%" alt="error"></img>
      <h4>{item.title}</h4>
      <p>{item.price}</p>
      <Link to="/detail">์ƒ์„ธํŽ˜์ด์ง€</Link>
    </div>
  );
};

โ—ผ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ˆŒ๋ €์„ ๋•Œ, ์›ํ•˜๋Š” uri๋กœ ์ด๋™ํ•˜๊ฒŒ ํ•œ๋‹ค.

โ—ผ to๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ˆŒ๋ €์„ ๋•Œ, ์ด๋™ํ•  ๊ฒฝ๋กœ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

โ—ผ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด a ํƒœ๊ทธ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

 

import { useNavigate } from "react-router-dom";

const Item = ({ item }) => {
  const navigate = useNavigate();
  return (
    <div
      className="content"
      onClick={() => {
        navigate("/detail");
      }}
    >
      <img src={item.imageSrc} width="50%" alt="error"></img>
      <h4>{item.title}</h4>
      <p>{item.price}</p>
    </div>
  );
};

โ—ผ useNavigate()๋Š” ํŽ˜์ด์ง€ ์ด๋™์„ ๋„์™€์ฃผ๋Š” Hook์ด๋‹ค. 

   ๐Ÿ’ฅ Hook์€ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

โ—ผ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด a ํƒœ๊ทธ๊ฐ€ ์ƒ์„ฑ๋˜๋ฏ€๋กœ, useNavigate()๋กœ ์ƒ์„ฑํ•œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ onClick ์†์„ฑ์— ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ navigate ํ•จ์ˆ˜์— ์ธ์ž๋กœ ์ˆซ์ž๋ฅผ ๋„ฃ์œผ๋ฉด, ํ•ด๋‹น ์ˆซ์ž๋งŒํผ ์ด๋™ํ•˜๊ณ , ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์œผ๋ฉด ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•œ๋‹ค.

โ—ผ BrowserRouter ์ปดํฌ๋„ŒํŠธ ์•„๋ž˜์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <>
    <BrowserRouter>
      <Routes>
        <Route path="/detail" element={<Detail />}>
          <Route path="one" element={<div>1์ž…๋‹ˆ๋‹ค.</div>}></Route>
          <Route path="two" element={<div>2์ž…๋‹ˆ๋‹ค.</div>}></Route>
        </Route>
      </Routes>
    </BrowserRouter>
  </>
);

โ—ผ Route ์ปดํฌ๋„ŒํŠธ ์•ˆ์— Route๋ฅผ ๋˜ ์ •์˜ํ•˜๋ฉด, nested routes๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ nested routes๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ๋ถ€๋ชจ elements๋„ ํ™”๋ฉด์— ์ถœ๋ ฅ๋œ๋‹ค.

โ—ผ /detail/one, /detail/two๋กœ ๋™์ž‘ํ•œ๋‹ค.

   ๐Ÿ’ฅ nested routes์˜ path ์•ž์—๋Š” "/"๋ฅผ ๋ถ™์ด๋ฉด ์•ˆ ๋œ๋‹ค.

โ—ผ nested routes๋Š” ๋ถ€๋ชจ element์™€ ํฐ ์ฐจ์ด๊ฐ€ ์—†์„ ๋•Œ, ์‚ฌ์šฉํ•œ๋‹ค.

 

import { Outlet } from "react-router-dom";

const Detail = ({ imageSrc, title, content, price }) => {
  return (
    <div>
      <div>
        <img src={imageSrc} alt="error" width="100%"></img>
        <h4>{title}</h4>
        <p>{content}</p>
        <p>{price}</p>
        <Outlet></Outlet>
        <button>์ฃผ๋ฌธํ•˜๊ธฐ</button>
      </div>
    </div>
  );
};

โ—ผ nested routes์˜ element๋Š” ๋ถ€๋ชจ element์˜ ์–ด๋””์— ๋ณด์—ฌ์ค„์ง€ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

โ—ผ ๋”ฐ๋ผ์„œ, Outlet์„ ์‚ฌ์šฉํ•˜๋ฉด, ํ•ด๋‹น ์ž๋ฆฌ์— nested routes์˜ element๊ฐ€ ์ ์šฉ๋œ๋‹ค.

 

โœ”  URL ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ

import "./detail.css";
import { useParams } from "react-router-dom";
import { useState } from "react";
import data from "../../data";

const Detail = () => {
  const [items] = useState(data);
  const { id } = useParams();

  return (
    <div className="container">
      <img src={items[id].imageSrc} alt="error" width="100%"></img>
      <h4>{items[id].title}</h4>
      <p>{items[id].content}</p>
      <p>{items[id].price}</p>
      <button>์ฃผ๋ฌธํ•˜๊ธฐ</button>
    </div>
  );
};

export default Detail;

โ—ผ useParams๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ Styled Component

โ—ผ npm install styled-compnents๋กœ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ css ํŒŒ์ผ๋กœ ๊ฐ€์ง€ ์•Š๊ณ , JavaScript ๋‚ด์—์„œ ๊ฐ„ํŽธํ•˜๊ฒŒ Componenet๋กœ ์Šคํƒ€์ผ๋ง์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ” ์‚ฌ์šฉ๋ฒ•

import styled from "styled-components";

const Detail = () => {
  return (
    <DetailBtn bg="black">์ฃผ๋ฌธํ•˜๊ธฐ</DetailBtn>
  );
};

const DetailBtn = styled.button`
  background: ${(props) => props.bg};
  color: ${(props) => (props.bg === "black" ? "white" : "black")};
  padding: 10px;
`;

export default Detail;

โ—ผ styled.{ํƒœ๊ทธ}`{style}` ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ด์„œ ์Šคํƒ€์ผ ๋œ ํƒœ๊ทธ Component๋ฅผ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ Styled Component๋Š” ๋‹ค๋ฅธ js ํŒŒ์ผ๋กœ ์˜ค์—ผ๋˜์ง€ ์•Š๋Š” ์žฅ์ ์„ ๊ฐ€์ง„๋‹ค.

   ๐Ÿ’ฅ React๋Š” ๋ชจ๋“  css ํŒŒ์ผ์„ bundling ์ž‘์—…์—์„œ ํ•˜๋‚˜๋กœ ํ•ฉ์น˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค์—ผ๋  ์ˆ˜ ์žˆ๋‹ค.

   ๐Ÿ’ฅ Styled Component๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด, ์˜ค์—ผ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด {์ปดํฌ๋„ŒํŠธ}.module.css๋กœ ํŒŒ์ผ๋ช…์„ ์ง€์–ด์•ผ ํ–ˆ๋‹ค. 

โ—ผ style ํƒœ๊ทธ๋กœ์จ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์‹œ๊ฐ„์ด ๋‹จ์ถ•๋œ๋‹ค.

โ—ผ props๋ฅผ ์‚ฌ์šฉํ•ด์„œ style์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ ๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

โœ” ์• ๋‹ˆ๋ฉ”์ด์…˜

const fadeInOut = keyframes`
0% {
  opacity: 0;
}
100% {
  opacity: 1;
}
`;

const DetailBtn = styled.button`
  background: ${(props) => props.bg};
  color: ${(props) => (props.bg === "black" ? "white" : "black")};
  padding: 10px;
  animation: ${fadeInOut} 1s;
`;

โ—ผ keyframes``๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌ์„ฑํ•˜๊ณ , ์›ํ•˜๋Š” ๊ณณ์—์„œ animation: ${๋ณ€์ˆ˜๋ช…} {์‹œ๊ฐ„}์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ useEffect

๐Ÿ’ฌ Component Lifecycle

   โ—ผ Component์˜ ์ƒ๋ช…์ฃผ๊ธฐ: mount, update, unmount

   โ—ผ Component๊ฐ€ ํ™”๋ฉด์— ๋ณด์ด๋Š” ๊ฒƒ์„ mount ๋˜์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค.

   โ—ผ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ƒํƒœ์— ๋”ฐ๋ผ ๊ฐ„์„ญํ•  ์ˆ˜ ์žˆ๊ณ , useEffect Hook์„ ์‚ฌ์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ๊ฐ„์„ญํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ” ๊ธฐ๋ณธ ์ฝ”๋“œ์™€์˜ ์ฐจ์ด์ 

import { useEffect } from "react";

const Detail = () => {
  useEffect(() => {
    console.log("Hello"); // 1๋ฒˆ
  });

  console.log("Hello"); // 2๋ฒˆ
  return (
  <div />
  );
};

โ—ผ useEffect๋Š” ์ ์šฉ๋œ Component์˜ mount, update ์‹œ์ ์— ์‹คํ–‰๋œ๋‹ค.

โ—ผ 1๋ฒˆ ์ฝ”๋“œ๋‚˜, 2๋ฒˆ ์ฝ”๋“œ ๋‘˜ ๋‹ค Detail Component์˜ mount, update ์‹œ์ ์— ์‹คํ–‰๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋‘˜์˜ ์ฐจ์ด๋Š” 1๋ฒˆ ์ฝ”๋“œ๋Š” Html ๋ Œ๋”๋ง์ด ๋‹ค ๋œ ์‹œ์ ์—์„œ ์‹คํ–‰๋œ๋‹ค.

โ—ผ ๋”ฐ๋ผ์„œ, useEffect ์•ˆ์— ์ ๋Š” ์ฝ”๋“œ๋“ค์€ ์–ด๋ ค์šด ์—ฐ์‚ฐ์ด๋‚˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ž‘์—… ๊ฐ™์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์— ์‚ฌ์šฉํ•œ๋‹ค. Html ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ๋ณด๋‹ค ๋œ ์ค‘์š”ํ•œ ์ž‘์—…๋“ค...

 

โœ” ์‚ฌ์šฉ๋ฒ•

import { useEffect } from "react";

const Detail = () => {
  const [showAlert, setShowAlert] = useState(true);
  
  useEffect(() => { 
    console.log(1); // 1๋ฒˆ
    setTimeout(() => {
      setShowAlert(false);
    }, 2000);
  }, []);
  
  useEffect(() => {
    console.log(2); // 2๋ฒˆ
  }, [showAlert]);

  return (
    {showAlert ? <div>2์ดˆ ์ด๋‚ด ๊ตฌ๋งค์‹œ, ํ• ์ธ!!!!</div> : null}
  );
};

โ—ผ useEffect({์ฝœ๋ฐฑ ํ•จ์ˆ˜}, {์‹คํ–‰ ์กฐ๊ฑด})์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ ์‹คํ–‰ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•˜๋ฉด, Component๊ฐ€ mount ๋  ๋•Œ, ์‹คํ–‰ ์กฐ๊ฑด์ด update ๋  ๋•Œ, ์‹คํ–‰๋œ๋‹ค.

โ—ผ ์‹คํ–‰ ์กฐ๊ฑด์„ ๋นˆ Array๋กœ ๋„ฃ์œผ๋ฉด, Component๊ฐ€ mount ๋  ๋•Œ๋งŒ ์‹คํ–‰๋œ๋‹ค.

โ—ผ ์ฆ‰, 1๋ฒˆ ์ฝ”๋“œ๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๊ณ , 2๋ฒˆ ์ฝ”๋“œ๋Š” Component๊ฐ€ mount ๋  ๋•Œ์™€ showAlert ๊ฐ’์ด ๋ณ€ํ™”๋˜๋ฉด์„œ ์ด 2๋ฒˆ ์‹คํ–‰๋œ๋‹ค.

 

import { useEffect } from "react";

const Detail = () => {
  useEffect(() => { 
    setTimeout(() => {
      setShowAlert(false);
    }, 2000);
    
    return () => {} // 1๋ฒˆ
  }, []);
  
  return (
    <div />
  );
};

โ—ผ 1๋ฒˆ ์ฝ”๋“œ์ฒ˜๋Ÿผ return () => {}์„ ์‚ฌ์šฉํ•˜์—ฌ useEffect์— ๋“ฑ๋กํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ๋•Œ, ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ ๋˜ํ•œ, 1๋ฒˆ ์ฝ”๋“œ๋Š” mount ๋  ๋•Œ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š๊ณ , unmount ๋  ๋•Œ ์‹คํ–‰๋œ๋‹ค.


๐Ÿ“’ axios

โ—ผ node ํ™˜๊ฒฝ์—์„œ Promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ฃผ๋Š” Http Client์ด๋‹ค.

 

โœ” ์‚ฌ์šฉ๋ฒ•

import axios from "axios";

<button
  onClick={() => {
    axios
      .get("https://codingapple1.github.io/shop/data2.json")
      .then((res) => {
        console.log(res.data);
      })
      .catch((err) => {
        console.log(err);
      });
  }}
>
  ๋”๋ณด๊ธฐ
</button>

โ—ผ axios.get({uri}) ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ƒˆ๋กœ๊ณ ์นจ ์—†์ด, ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ Promise ๊ฐ์ฒด๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ then()์„ ํ†ตํ•ด ์‘๋‹ต ๋ฐ์ดํ„ฐ๋ฅผ, catch()๋ฅผ ํ†ตํ•ด ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

axios.post("uri", {key: "value"})
.then()
.catch()

โ—ผ ์œ„์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ post ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Promise.all([axios.get("uri1"), axios.get("uri2")])
.then()
.catch()

โ—ผ ์œ„์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ uri1๊ณผ uri2์— ๋™์‹œ์— ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ณ , ๋ชจ๋“  ์š”์ฒญ์— ์‘๋‹ต์ด ์˜ค๊ฑฐ๋‚˜, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด then(), catch()์—์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

โœ” async ํ•จ์ˆ˜

<button onClick={()=>{
  setCount(count + 1);
  if (count < 10) {
    setLimitCount(limitCount + 1);
  }   
}}></button>

โ—ผ ํ•ด๋‹น ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, count๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ณ  count์˜ ๊ฐ’์ด 10๋ณด๋‹ค ์ž‘์„ ๋•Œ๋งŒ limitCount ๊ฐ’์ด ์ฆ๊ฐ€ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” limitCount ๊ฐ’์€ 10์„ ๋„˜์„ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ state ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋Š” asyncํ•˜๊ฒŒ ๋™์ž‘๋˜๋Š” ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์—, ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ๋จผ์ € ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋ ์ง€ ๋ชจ๋ฅธ๋‹ค.

โ—ผ ๋”ฐ๋ผ์„œ, useEffect๋ฅผ ์‚ฌ์šฉํ•ด์„œ, count๋ฅผ ์‹คํ–‰ ์กฐ๊ฑด์— ๋„ฃ์–ด์„œ count๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด useEffect์— ๋“ฑ๋กํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

 

โœ” async/await

  let [limit, setLimit] = useState(1);
  const add = async () => {
    let data = 0;
    await axios.get("url").then((res) => {
      data = res.data;
    });

    if (data < 10) {
      setLimit(0);
    } else {
      setLimit(100);
    }
  };

โ—ผ axios ํ•จ์ˆ˜๋Š” async๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, axios ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋ฐ”ํƒ•์œผ๋กœ ์–ด๋–ค ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜, ์ž‘์—…์„ ์ง„ํ–‰ํ•ด์•ผ ํ• ๋•Œ async, await์„ ์‚ฌ์šฉํ•ด์„œ ์‹คํ–‰์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“’ Automatic batching

โ—ผ React 18 ๋ฒ„์ „ ์ดํ›„์— ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ์œผ๋กœ, ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ์•ˆ์— ๋‘ ๊ฐœ์˜ state ๋ณ€๊ฒฝ ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๋ฉด, state๋ฅผ ๋‹ค ๋ณ€๊ฒฝ์‹œํ‚จ ํ›„, ํ•œ ๋ฒˆ ์žฌ๋ Œ๋”๋ง์„ ์‹œ์ผœ์ค€๋‹ค.

โ—ผ ์žฌ๋ Œ๋”๋ง์„ state ๋ณ€๊ฒฝ๋งˆ๋‹ค ์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” state ๋ณ€๊ฒฝ์„ setTimeout ์•ˆ์— ์„ค์ •ํ•ด, ์ผ์ • ์‹œ๊ฐ„ ๋’ค์— ๋ณ€ํ™”ํ•˜๊ฒŒ๋” ํ•œ๋‹ค.


๐Ÿ“’ Redux Toolkit

โœ” Single Page Application ๋‹จ์ 

โ—ผ Component ๊ฐ„์˜ state ๊ณต์œ ๊ฐ€ ์–ด๋ ต๋‹ค.

โ—ผ ๋ถ€๋ชจ-์ž์‹ 1 Depth ๊ด€๊ณ„๋ฉด props๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‹ค๋ฅธ ๊ด€๊ณ„์˜ Component ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ ๋Š” ์–ด๋ ต๋‹ค. 

โ—ผ Component ๊ฐ„์˜ State ๊ณต์œ  ๋ฌธ์ œ๋Š” React์˜ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์ธ Context API ์‚ฌ์šฉ์ด๋‚˜ Redux ๋“ฑ์˜ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

   ๐Ÿ’ฅ Context API๋Š” ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. (์„ฑ๋Šฅ ์ด์Šˆ(์ž์‹ Component๋Š” ํ•ด๋‹น State๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์žฌ๋ Œ๋”๋ง๋œ๋‹ค.), Component ์žฌํ™œ์šฉ์ด ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ)

 

โœ” Redux ์‚ฌ์šฉ ์ด์œ 

โ—ผ Redux๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ปดํฌ๋„ŒํŠธ๋“ค์ด props ์—†์ด state๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

   ๐Ÿ’ฌ Redux Store์šฉ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ state๋ฅผ ๋ณด๊ด€ํ•˜๊ณ , ๋ชจ๋“  Component๋“ค์ด ํ•ด๋‹น js ํŒŒ์ผ์—์„œ state๋ฅผ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

 

โœ” ์„ค์ •

import { configureStore } from "@reduxjs/toolkit";

export default configureStore({
  reducer: {},
});

โ—ผ npm install @reduxjs/toolkit react-redux๋กœ ์„ค์น˜ํ•œ๋‹ค.

โ—ผ src ์•„๋ž˜, store.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ ๋’ค, ์œ„ ์ฝ”๋“œ๋ฅผ ๋ถ™์—ฌ ๋„ฃ๋Š”๋‹ค.

 

import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
  	<App />
  </Provider>
);

โ—ผ index.js ํŒŒ์ผ์—์„œ <Provider> Component๋ฅผ root component๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

 

โœ” ์ €์žฅํ•˜๊ธฐ

import { configureStore, createSlice } from "@reduxjs/toolkit";

const {๋ณ€์ˆ˜๋ช…} = createSlice({
  name: "{state ๋ณ€์ˆ˜๋ช…}",
  initialState: "{state ๋ณ€์ˆ˜๊ฐ’}",
});

export default configureStore({
  reducer: {
    {๋ณ€์ˆ˜๋ช…}: {๋ณ€์ˆ˜๋ช…}.reducer,
  },
});

โ—ผ store.js ํŒŒ์ผ์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

โ—ผ createSlice๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ „์—ญ state๋ฅผ ์„ ์–ธํ•œ๋‹ค. (useState๋ž‘ ๋น„์Šทํ•œ ์—ญํ• )

โ—ผ ๋งŒ๋“ค์–ด์ง„ state๋ฅผ reducer ์•ˆ์— ์œ„์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋ณด๊ด€ํ•œ๋‹ค.

 

โœ” ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ

const Test = () => {
  const states = useSelector((state) => {
    return state;
  });
  
  return (<div />);
}

โ—ผ useSelector Hook์„ ์‚ฌ์šฉํ•ด์„œ Redux Store์˜ ์ €์žฅ๋œ ๋ชจ๋“  state ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ useSelector((state) => { return state.{์›ํ•˜๋Š” state ๋ณ€์ˆ˜๋ช…}; } ์œผ๋กœ ์›ํ•˜๋Š” ๊ฐ’๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

โ—ผ states.{์ €์žฅ๋œ state ๋ณ€์ˆ˜๋ช…}์œผ๋กœ ์›ํ•˜๋Š” ๊ฐ’์„ ์ฐพ์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ” Store ๊ฐ’ ๋ณ€๊ฒฝํ•˜๊ธฐ

import { configureStore, createSlice } from "@reduxjs/toolkit";

const {๋ณ€์ˆ˜๋ช…} = createSlice({
  name: "{state ๋ณ€์ˆ˜๋ช…}",
  initialState: "{state ๋ณ€์ˆ˜๊ฐ’}",
  reducers: {
    changeName(state, data) { // state๋Š” ๊ธฐ์กด ๊ฐ’์„ ์˜๋ฏธํ•˜๊ณ  data๋Š” ์ž…๋ ฅ ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
      return state + data.payload;
    }
  }
});

export let { changeName } = {๋ณ€์ˆ˜๋ช…}.actions;

export default configureStore({
  reducer: {
    {๋ณ€์ˆ˜๋ช…}: {๋ณ€์ˆ˜๋ช…}.reducer,
  },
});

โ—ผ reducers ์•„๋ž˜ state ์ˆ˜์ •ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ , ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ˆ˜์ •ํ•œ๋‹ค.

โ—ผ ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” .payload๋กœ ์ ‘๊ทผํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

โ—ผ ๋งŒ๋“  ํ•จ์ˆ˜๋ฅผ export ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

import { useDispatch } from "react-redux";
import { addData } from "../store";

function App() {
  const dispatch = useDispatch();
  
  dispatch(addData());
  
  return ( <div /> );
}

โ—ผ useDispatch Hook์„ ์‚ฌ์šฉํ•ด์„œ, store.js์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด ๊ฐ’์„ ์ˆ˜์ •ํ•œ๋‹ค.

๐Ÿ’ฅ Redux ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ , ์ƒˆ๋กœ๊ณ ์นจ์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ Route ์ด๋™์ด ์•„๋‹Œ href๋กœ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์ดˆ๊ธฐ ๊ฐ’์œผ๋กœ ๋‹ค์‹œ ๋Œ์•„๊ฐ„๋‹ค.

 

๐Ÿ’ฌ redux-persist ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Redux Store์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๋ชจ๋“  State ๋“ค์„ localStorage์— ์ž๋™์œผ๋กœ ์ €์žฅํ•ด์„œ ์˜๊ตฌํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 


๐Ÿ“’ useQuery

โ—ผ ajax ์„ฑ๊ณต/์‹คํŒจ ์‹œ html ๋ณด์—ฌ์ฃผ๊ธฐ, ๋ช‡ ์ดˆ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ajax ์š”์ฒญํ•˜๊ธฐ, ์‹คํŒจ ์‹œ ๋ช‡์ดˆ ํ›„ ์š”์ฒญ ์žฌ์‹œ๋„, prefetch ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

โ—ผ npm install react-query๋กœ ์„ค์น˜ํ•œ๋‹ค.

 

โœ” ์„ค์ •ํ•˜๊ธฐ

import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);

โ—ผ index.js ํŒŒ์ผ์—์„œ QueryClient๋ฅผ ๋งŒ๋“  ํ›„, QueryClientProvider Component๋ฅผ root component๋กœ ์ž‘์„ฑํ•œ๋‹ค.

 

โœ” ์‚ฌ์šฉํ•˜๊ธฐ

import { useQuery } from "react-query";

function App() {
  const result = useQuery({queryKey}, () => {
    return axios.get("{uri}").then((res) => {
      return res.data;
    });
  });
  
  console.log(result.data); // ์‘๋‹ต ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ
  console.log(result.isLoading); // ๋กœ๋”ฉ ์ค‘์ด๋ฉด true
  console.log(result.error); // ์—๋Ÿฌ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ
  
  return (
    <div>
      { result.isLoading && <Loding />
      { result.data && <Page data={result.data.data}/> }
      { result.error && <ErrorPage data={error} />
    </div> 
  );
}

โ—ผ ajax ์š”์ฒญ์ด ์„ฑ๊ณต์ธ์ง€, ์‹คํŒจ์ธ์ง€, ๋กœ๋”ฉ ์ค‘์ธ์ง€ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ ๋กœ๋”ฉ ํŽ˜์ด์ง€, ์„ฑ๊ณต ํŽ˜์ด์ง€, ์‹คํŒจ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ์ถœ๋ ฅํ•  ๋•Œ, ์œ ์šฉํ•˜๋‹ค.

โ—ผ useQuery ์•ˆ์˜ ajax ์š”์ฒญ์ด ์„ฑ๊ณตํ•˜๋ฉด, ๊ณ„์† refetchํ•œ๋‹ค.

โ—ผ useQuery ์•ˆ์˜ ajax ์š”์ฒญ์ด ์‹คํŒจํ•˜๋ฉด, ์•Œ์•„์„œ retryํ•œ๋‹ค.

โ—ผ ์บ์‹ฑ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์บ์‹ฑ๋œ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ์šฐ์„  ๋ฐ˜ํ™˜ํ•˜๊ณ  refetch ํ•œ๋‹ค.


๐Ÿ“’ PWA (Progressive Web Apps)

โ—ผ ๋ชจ๋ฐ”์ผ ์•ฑ์ฒ˜๋Ÿผ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์›น ์‚ฌ์ดํŠธ

โ—ผ ์•ฑ ์Šคํ† ์–ด๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋ฐฉ๋ฌธ๋งŒ์œผ๋กœ ์„ค์น˜ํ•˜๊ฒŒ ์œ ๋„ํ•  ์ˆ˜ ์žˆ์–ด, ์„ค์น˜ ๋น„์šฉ์ด ์ ๋‹ค.

โ—ผ html, css, js ๋งŒ์œผ๋กœ ์•ฑ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ณ , ํ‘ธ์‹œ ์•Œ๋ฆผ, ์„ผ์„œ ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โœ” ์ƒ์„ฑ ๋ฐฉ๋ฒ•

โ—ผ npx create-react-app {ํ”„๋กœ์ ํŠธ๋ช…} --template cra-template-pwa๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ manifest.json ํŒŒ์ผ๊ณผ service-worker.js ํŒŒ์ผ์ด ์ค‘์š”ํ•˜๋‹ค. ๋‘ ํŒŒ์ผ์ด ์กด์žฌํ•˜๋ฉด, ์•ฑ์œผ๋กœ ๋‹ค์šด ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ manifest.json ํŒŒ์ผ์€ ๋งŒ๋“ค์–ด์ ธ ์žˆ์œผ๋ฉฐ, ์•ฑ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๊ณ  ์•ฑ ์ •๋ณด๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—ผ service-worker.js ํŒŒ์ผ์€ index.js ํŒŒ์ผ์—์„œ serviceWorkerRegistration.register()๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋นŒ๋“œํ•ด์•ผ ์ƒ์„ฑ๋œ๋‹ค.

โ—ผ service-worker.js ํŒŒ์ผ์€ ์˜คํ”„๋ผ์ธ์—์„œ๋„ ์‚ฌ์ดํŠธ๋ฅผ ์—ด ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค. ๊ตฌ๋™์— ํ•„์š”ํ•œ ๋ชจ๋“  html, js ,css ํŒŒ์ผ์„ ํ•˜๋“œ์— ์ €์žฅํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

'Frontend > React.js' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

CSR๊ณผ SSR, SSG  (0) 2023.05.07
React ์„ฑ๋Šฅ ๊ฐœ์„ ํ•˜๊ธฐ  (0) 2023.05.01