Unit6 - [React] React State & Props

๊ฐ•์„ฑ์ผยท2023๋…„ 5์›” 22์ผ
0
post-thumbnail

โœ… TIL


React States & props ์˜ ๊ฐœ๋…

์‡ผํ•‘๋ชฐ์—์„œ ์˜ท์„ ์ฃผ๋ฌธํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.

๊ทธ๋Ÿฐ๋ฐ ์›ํ•˜๋Š” ์˜ท์„ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ณ  ๊ฒฐ์ œ๋ฅผ ํ•˜๋ ค๊ณ  ๋ณด๋‹ˆ ๋‹ค๋ฅธ ์˜ท์ด ๋” ๋งˆ์Œ์— ๋“œ๋Š”๊ฒŒ ์•„๋‹Œ๊ฐ€...
๋ฐ”๋กœ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด์€ ์˜ท์„ ๋นผ๊ณ , ์ƒˆ๋กœ์šด ์˜ท์„ ๋‹ด์•„ ๊ฒฐ์ œ๋ฅผ ํ•œ๋‹ค.

์ด ์ƒํ™ฉ์—์„œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๊ธฐ๋Š” ์˜ท์€ ๋ฐ”๋€Œ๊ณ  ๋‚ด๊ฐ€ ๊ฒฐ์ œํ•ด์•ผ ํ•  ๊ธˆ์•ก๋„ ์ฆ‰๊ฐ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.

์ด๋ฅผ ์šฐ๋ฆฌ๋Š” ์žฅ๋ฐ”๊ตฌ๋‹ˆ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•œ๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฒฐ์ œ ํŽ˜์ด์ง€์— ๋ณ€๊ฒฝ๋œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์˜ ์ƒํƒœ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

์ด๊ฒƒ์ด State์™€ Props์— ๋Œ€ํ•œ ํ”„๋กค๋กœ๊ทธ์ด๊ณ , ์ด์ œ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž.



Props

  • ์ปดํฌ๋„ŒํŠธ์˜ ์†์„ฑ(property)์„ ์˜๋ฏธํ•œ๋‹ค.
    ์ด์ „ State & Props Intro์—์„œ ์ž˜ ์„ค๋ช…๋˜์—ˆ๋“ฏ, props๋Š” ์„ฑ๋ณ„์ด๋‚˜ ์ด๋ฆ„์ฒ˜๋Ÿผ
    ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์œผ๋กœ, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ€์ง„ ์†์„ฑ์— ํ•ด๋‹นํ•œ๋‹ค.
  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ(์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ)๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์ด๋‹ค.
    React ์ปดํฌ๋„ŒํŠธ๋Š” JavaScript ํ•จ์ˆ˜์™€ ํด๋ž˜์Šค๋กœ, props๋ฅผ ํ•จ์ˆ˜์˜ ์ „๋‹ฌ์ธ์ž(arguments)์ฒ˜๋Ÿผ ์ „๋‹ฌ๋ฐ›์•„
    ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™”๋ฉด์— ์–ด๋–ป๊ฒŒ ํ‘œ์‹œ๋˜๋Š”์ง€๋ฅผ ๊ธฐ์ˆ ํ•˜๋Š” React ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    ๋”ฐ๋ผ์„œ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ์ดˆ ๋ Œ๋”๋ง ๋  ๋•Œ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ์ฒด ํ˜•ํƒœ์ด๋‹ค.
    props๋กœ ์–ด๋–ค ํƒ€์ž…์˜ ๊ฐ’๋„ ๋„ฃ์–ด ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก props๋Š” ๊ฐ์ฒด์˜ ํ˜•ํƒœ๋ฅผ ๊ฐ€์ง„๋‹ค.
  • props๋Š” ์ฝ๊ธฐ ์ „์šฉ์ด๋‹ค.
    props๋Š” ์„ฑ๋ณ„์ด๋‚˜ ์ด๋ฆ„์ฒ˜๋Ÿผ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์•„ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ฐ’์ด๋‹ค.
    ๊ทธ๋ž˜์„œ props๋Š” ํ•จ๋ถ€๋กœ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์—†๋Š” ์ฝ๊ธฐ ์ „์šฉ(read-only) ๊ฐ์ฒด์ด๋‹ค.

์ฝ๊ธฐ ์ „์šฉ ๊ฐ์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด props๋ฅผ ์ „๋‹ฌ๋ฐ›์€ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ props๋ฅผ ์ง์ ‘ ์ˆ˜์ • ์‹œ props๋ฅผ ์ „๋‹ฌํ•œ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ’์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ์ฆ‰, ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜๋„ํ•˜์ง€ ์•Š์€ side effect๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜๊ณ  ์ด๋Š” React์˜ ๋‹จ๋ฐฉํ–ฅ, ํ•˜ํ–ฅ์‹ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ์›์น™(React is all about one-way data flow down the component hierarchy)์— ์œ„๋ฐฐ๋œ๋‹ค.


How to use props


props๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์ด 3๋‹จ๊ณ„ ์ˆœ์„œ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

  1. ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฐ’(data)๊ณผ ์†์„ฑ์„ ์ •์˜ํ•œ๋‹ค.
  2. props๋ฅผ ์ด์šฉํ•˜์—ฌ ์ •์˜๋œ ๊ฐ’๊ณผ ์†์„ฑ์„ ์ „๋‹ฌํ•œ๋‹ค.
  3. ์ „๋‹ฌ๋ฐ›์€ props๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.

import "./styles.css";

function Parent() {
  return (
    <div className="parent">
      <h1>I'm the parent</h1>
      <Child text={"I'm the eldest child"} />
      {/* Child ์ปดํฌ๋„ŒํŠธ์— ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ž์—ด์„ props ๋กœ ์ „๋‹ฌํ•ด ๋ณด์„ธ์š” */}
      <Child />
    </div>
  );
}

function Child(props) {
  // console ์„ ์—ด์–ด props ์˜ ํ˜•ํƒœ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜์„ธ์š”.
  console.log("props : ", props);
  return (
    <div className="child">
      <p>{props.text}</p>
    </div>
  );
}

export default Parent;

props.children

props๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ์—ฌ๋Š” ํƒœ๊ทธ์™€ ๋‹ซ๋Š” ํƒœ๊ทธ์˜ ์‚ฌ์ด์— value๋ฅผ ๋„ฃ์–ด ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
์ด ๊ฒฝ์šฐ props.children์„ ์ด์šฉํ•˜๋ฉด ํ•ด๋‹น value์— ์ ‘๊ทผํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


import "./styles.css";

const App = () => {
  const itemOne = "React๋ฅผ";
  const itemTwo = "๋ฐฐ์šฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.";

  return (
    <div className="App">
      {/* Learn ์ปดํฌ๋„ŒํŠธ์— itemOne ๊ณผ itemTwo ๋ฅผ
      props ๋กœ ์ „๋‹ฌํ•˜์„ธ์š” */}
      <Learn>{itemOne}</Learn>
      <Learn>{itemTwo}</Learn>

    </div>
  );
};

const Learn = (props) => {
  // ์ „๋‹ฌ๋ฐ›์€ props ๋ฅผ ์•„๋ž˜ <div> tag ์‚ฌ์ด์— ์‚ฌ์šฉํ•˜์—ฌ
  // "React๋ฅผ ๋ฐฐ์šฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค" ๋ผ๋Š” ๋ฌธ์žฅ์ด ๋ Œ๋”๋ง๋˜๋„๋ก ์ปดํฌ๋„ŒํŠธ๋ฅผ ์™„์„ฑํ•˜์„ธ์š”
  return <div className="Learn">
  <p>{props.children}</p></div>;
};

export default App;


State


์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ "์ƒํƒœ"

State & Props ์˜์ƒ์—์„œ state๋Š” Toggle Switch๋‚˜ Counter์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์ด๋ผ๊ณ  ํ–ˆ๋‹ค.

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋ฌด์—‡์ด "์ƒํƒœ"๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์‡ผํ•‘๋ชฐ ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณด๊ฒ ๋‹ค.
์‚ฌ์šฉ์ž๋Š” ๊ตฌ๋งคํ•  ๋ฌผ๊ฑด๊ณผ ๋‹น์žฅ์€ ๊ตฌ๋งคํ•˜์ง€ ์•Š์„ ๋ฌผ๊ฑด์„ ์ฒดํฌ๋ฐ•์Šค์— ์ฒดํฌํ•˜์—ฌ ๊ตฌ๋ถ„ ์ง“๋Š”๋‹ค.

์ด๋ฅผ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‚ด์—์„œ์˜ ์ƒํƒœ๋กœ ๊ตฌ๋ถ„ํ•ด ๋ณธ๋‹ค๋ฉด check ๋œ ์ƒํƒœ์™€ check ๋˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋‹ค.

์ฒดํฌ๋ฐ•์Šค๋ฅผ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ๋Š” ๋‹จ์ˆœํžˆ ์ฒดํฌ๋œ ์ƒํƒœ์— ๋”ฐ๋ผ ๋ณด์ด๋Š” ๊ธ€์”จ๊ฐ€ ๋‹ฌ๋ผ์ง€์ง€๋งŒ,
์ด๋ฅผ ์‡ผํ•‘๋ชฐ์— ์ ์šฉํ•˜๋ฉด ์ฒดํฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๊ตฌ๋งคํ•  ๋ฌผ๊ฑด์˜ ๊ฐœ์ˆ˜๋‚˜ ๊ตฌ๋งค ๊ธˆ์•ก์ด ๋ณ€๊ฒฝ๋˜๊ณ , ์ด์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด ๋˜ํ•œ ๋‹ฌ๋ผ์ง„๋‹ค.

์ด์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’, ์ฆ‰ ์ƒํƒœ๋Š” React state๋กœ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค.


import React, { useState } from "react";
import "./styles.css";

function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);

  const handleChecked = (event) => {
    setIsChecked(event.target.checked);
  };
  return (
    <div className="App">
      <input type="checkbox" checked={isChecked} onChange={handleChecked} />
      <span>{isChecked ? "Checked!!" : "Unchecked"}</span>
    </div>
  );
}

export default CheckboxExample;


State hook, useState


useState ์‚ฌ์šฉ๋ฒ•


React์—์„œ๋Š” state๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ useState๋ผ๋Š” ํŠน๋ณ„ํ•œ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
์ด ํ•จ์ˆ˜์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ์ž‘๋™ ๋ฐฉ์‹์„ ์œ„์˜ ์ฒดํฌ๋ฐ•์Šค ์˜ˆ๋กœ ๋“ค์–ด ์‚ดํŽด๋ณด๊ฒ ๋‹ค.

  • useState๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, React์—์„œ import ํ‚ค์›Œ๋“œ๋กœ useState ๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•œ๋‹ค.

    import { useState } from "react";
  • ์ดํ›„ useState๋ฅผ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ํ˜ธ์ถœํ•ด ์ค€๋‹ค.
    useState๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์€ "state"๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์œผ๋ฉฐ, ์ด ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์€ ์•„๋ฌด ์ด๋ฆ„์œผ๋กœ ์ง€์–ด๋„ ๋œ๋‹ค.
    ์ผ๋ฐ˜์ ์ธ ๋ณ€์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋๋‚  ๋•Œ ์‚ฌ๋ผ์ง€์ง€๋งŒ, state ๋ณ€์ˆ˜๋Š” React์— ์˜ํ•ด ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๋„ ์‚ฌ๋ผ์ง€์ง€ ์•Š๋Š”๋‹ค.

  • ๋ฌธ๋ฒ•์ ์œผ๋กœ ๋ณด๋ฉด ์•„๋ž˜ ์˜ˆ์‹œ์˜ isChecked, setIsChecked๋Š” useState์˜ ๋ฆฌํ„ด๊ฐ’์„ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•œ ๋ณ€์ˆ˜์ด๋‹ค.

    function CheckboxExample() {
    
      // ์ƒˆ๋กœ์šด state ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ , ์—ฌ๊ธฐ์„œ๋Š” ์ด๊ฒƒ์„ isChecked๋ผ ๋ถ€๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.
      const [isChecked, setIsChecked] = useState(false); // 1๋ฒˆ
    
      //...
    
      // 1๋ฒˆ ์ฝ”๋“œ๋ฅผ ํ’€์–ด์“ฐ๋ฉด, 2๋ฒˆ ์ฝ”๋“œ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
      const stateHookArray = useState(false); // 2๋ฒˆ
      const isChecked = stateHookArray[0];
      const setIsChecked = stateHookArray[1];
    }
  • useState๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ๋ฐฐ์—ด์˜ 0๋ฒˆ์งธ ์š”์†Œ๋Š” ํ˜„์žฌ state ๋ณ€์ˆ˜์ด๊ณ , 1๋ฒˆ์งธ ์š”์†Œ๋Š” ์ด ๋ณ€์ˆ˜๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ด๋‹ค. useState์˜ ์ธ์ž๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฐ’์€ state์˜ ์ดˆ๊นƒ๊ฐ’์ด๋‹ค.

    // useState ์ˆ˜๋„ ์ฝ”๋“œ
    const [state ์ €์žฅ ๋ณ€์ˆ˜, state ๊ฐฑ์‹  ํ•จ์ˆ˜] = useState(์ƒํƒœ ์ดˆ๊ธฐ ๊ฐ’);
  • ์ˆ˜๋„ ์ฝ”๋“œ๋ฅผ ์‹ค์ œ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด ๋ณด์ž.

    // useState ๋ฌธ๋ฒ• ์˜ˆ์‹œ
    function CheckboxExample() {
      const [isChecked, setIsChecked] = useState(false);
      // const [state ์ €์žฅ ๋ณ€์ˆ˜, state ๊ฐฑ์‹  ํ•จ์ˆ˜] = useState(state ์ดˆ๊นƒ๊ฐ’);
    • isChecked : state๋ฅผ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜
    • setIsChecked : state isChecked๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜
    • useState : state hook
    • false : state ์ดˆ๊นƒ๊ฐ’
  • ์ด state ๋ณ€์ˆ˜์— ์ €์žฅ๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด JSX ์—˜๋ฆฌ๋จผํŠธ ์•ˆ์— ์ง์ ‘ ๋ถˆ๋Ÿฌ์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
    ์—ฌ๊ธฐ์„œ๋Š” isChecked๊ฐ€ boolean ๊ฐ’์„ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— true or false ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋ณด์ด๋„๋ก ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

    // JSX์—์„œ ์‚ผํ•ญ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ ์˜ˆ์‹œ
    <span>{isChecked ? "Checked!!" : "Unchecked"}</span>

state ๊ฐฑ์‹ ํ•˜๊ธฐ


state๋ฅผ ๊ฐฑ์‹ ํ•˜๋ ค๋ฉด state ๋ณ€์ˆ˜๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ธ setIsChecked๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
์ด๋ฒˆ ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ, input[type=checkbox] JSX ์—˜๋ฆฌ๋จผํŠธ์˜ ๊ฐ’ ๋ณ€๊ฒฝ์— ๋”ฐ๋ผ์„œ isChecked๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•œ๋‹ค.

๋ธŒ๋ผ์šฐ์ €์—์„œ checked๋กœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๋ฉด, React์˜ isChecked๋„ ๋ณ€๊ฒฝ๋˜์–ด์•ผํ•œ๋‹ค !

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ฒดํฌ๋ฐ•์Šค ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด onChange ์ด๋ฒคํŠธ๊ฐ€ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜์ธ handleChecked๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ด ํ•จ์ˆ˜๊ฐ€ setIsChecked๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค.
    setIsChecked๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ํ˜ธ์ถœ๋œ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ isChecked ๋ณ€์ˆ˜๊ฐ€ ๊ฐฑ์‹ ๋˜๋ฉฐ, React๋Š” ์ƒˆ๋กœ์šด isChecked ๋ณ€์ˆ˜๋ฅผ CheckboxExample ์ปดํฌ๋„ŒํŠธ์— ๋„˜๊ฒจ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋ง ํ•œ๋‹ค.

import React, { useState } from "react";
import "./styles.css";

function CheckboxExample() {
  console.log("rerendered?");
  const [isChecked, setIsChecked] = useState(false);

  const handleChecked = (event) => {
    setIsChecked(event.target.checked);
  };

  return (
    <div className="App">
      <input type="checkbox" checked={isChecked} onChange={handleChecked} />
      <span>{isChecked ? "Checked!!" : "Unchecked"}</span>
    </div>
  );
}

export default CheckboxExample;


์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ


React์˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ(์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง; Event handling) ๋ฐฉ์‹์€ DOM์˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๋‹ค.
๋‹จ, ๋ช‡ ๊ฐ€์ง€ ๋ฌธ๋ฒ• ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

  • React์—์„œ ์ด๋ฒคํŠธ๋Š” ์†Œ๋ฌธ์ž ๋Œ€์‹  ์นด๋ฉœ ์ผ€์ด์Šค(camelCase)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • JSX๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ํ•จ์ˆ˜๋กœ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜(์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ; Event handler)๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด HTML์—์„œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ์•„๋ž˜์™€ ๊ฐ™๋‹ค๋ฉด,

<button onclick="handleEvent()">Event</button>


React์˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋ณธ ๋ฐฉ์‹์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

<button onClick={handleEvent}>Event</button>

onChange


<input> <textarea> <select>์™€ ๊ฐ™์€ ํผ(Form) ์—˜๋ฆฌ๋จผํŠธ๋Š” ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์„ ์ œ์–ดํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.
React์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ๊ฐ’์„ ์ผ๋ฐ˜์ ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ์˜ state๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

onChange ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด e.target.value๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ ๊ฐ์ฒด์— ๋‹ด๊ฒจ์žˆ๋Š” input ๊ฐ’์„ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
์ปดํฌ๋„ŒํŠธ return ๋ฌธ ์•ˆ์˜ input ํƒœ๊ทธ์— value์™€ onChange๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

onChange๋Š” input์˜ ํ…์ŠคํŠธ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋‹ค.

์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด handleChange ํ•จ์ˆ˜๊ฐ€ ์ž‘๋™ํ•˜๋ฉฐ, ์ด๋ฒคํŠธ ๊ฐ์ฒด์— ๋‹ด๊ธด input ๊ฐ’์„ setState๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด state๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.


function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
      <h1>{name}</h1>
    </div>)
};

onClick

onClick ์ด๋ฒคํŠธ๋Š” ๋ง ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญ์ด๋ผ๋Š” ํ–‰๋™์„ ํ•˜์˜€์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋‹ค.

๋ฒ„ํŠผ์ด๋‚˜ <a> tag๋ฅผ ํ†ตํ•œ ๋งํฌ ์ด๋™ ๋“ฑ๊ณผ ๊ฐ™์ด ์ฃผ๋กœ ์‚ฌ์šฉ์ž์˜ ํ–‰๋™์— ๋”ฐ๋ผ, ์•ฑ์ด ๋ฐ˜์‘ํ•ด์•ผ ํ•  ๋•Œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋‹ค.

๊ทธ๋Ÿผ ์œ„์˜ onChange ์˜ˆ์‹œ์— ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ,
input tag์— ์ž…๋ ฅํ•œ ์ด๋ฆ„์ด alert์„ ํ†ตํ•ด ์•Œ๋ฆผ ์ฐฝ์ด ํŒ์—… ๋˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ๋‹ค.


function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
      <button onClick={alert(name)}>Button</button>
      <h1>{name}</h1>
    </div>);
};


์œ„์™€ ๊ฐ™์ด onClick ์ด๋ฒคํŠธ์— alert(name) ํ•จ์ˆ˜๋ฅผ ๋ฐ”๋กœ ํ˜ธ์ถœํ•˜๋ฉด,
์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ ํ•จ์ˆ˜ ์ž์ฒด๊ฐ€ ์•„๋‹Œ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๊ฐ€ onClick์— ์ ์šฉ๋œ๋‹ค.

๋•Œ๋ฌธ์— ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ๊ฐ€ ์•„๋‹Œ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ์— alert์ด ์‹คํ–‰๋˜๊ณ 
๋”ฐ๋ผ์„œ ๊ทธ ๊ฒฐ๊ณผ์ธ undefined(ํ•จ์ˆ˜๋Š” ๋ฆฌํ„ด ๊ฐ’์ด ์—†์„ ๋•Œ undefined๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.)๊ฐ€
onClick์— ์ ์šฉ๋˜์–ด ํด๋ฆญํ–ˆ์„ ๋•Œ ์•„๋ฌด๋Ÿฐ ๊ฒฐ๊ณผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

๋”ฐ๋ผ์„œ onClick ์ด๋ฒคํŠธ์— ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๋•Œ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ,
์•„๋ž˜์™€ ๊ฐ™์ด ๋ฆฌํ„ด๋ฌธ ์•ˆ์—์„œ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ฑฐ๋‚˜, ์™ธ๋ถ€์—์„œ ํ•จ์ˆ˜๋ฅผ ์ •์˜ ํ›„ ์ด๋ฒคํŠธ์— ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.


import "./styles.css";
import React, { useState } from "react";

function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  };

  const handleClick = () => {
    alert(name);
  };
  return (
    <div className="App">
      <h1>Event handler practice</h1>
      <input type="text" value={name} onChange={handleChange}></input>
			
	  {/* ํ•จ์ˆ˜ ์ •์˜ํ•˜๊ธฐ */}
	  <button onClick={handleClick}>Button</button>

	  {/* ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ */}
      {/* <button onClick={() => alert(name)}>Button</button> */}

      <h3>{name}</h3>
    </div>
  );
}
export default NameForm;


Action item - select

// select tag๋Š” ์‚ฌ์šฉ์ž๊ฐ€ drop down ๋ชฉ๋ก์„ ์—ด์–ด ๊ทธ์ค‘ ํ•œ ๊ฐ€์ง€ ์˜ต์…˜์„ ์„ ํƒํ•˜๋ฉด, ์„ ํƒ๋œ ์˜ต์…˜์ด state ๋ณ€์ˆ˜์— ๋ณ€๊ฒฝ๋œ๋‹ค.

import React, { useState } from "react";
import "./styles.css";

function SelectExample() {
  const [choice, setChoice] = useState("apple");

  const fruits = ["apple", "orange", "pineapple", "strawberry", "grape"];
  const options = fruits.map((fruit) => {
    return <option value={fruit}>{fruit}</option>;
  });
  console.log(choice);
  const handleFruit = (event) => {
    //TODO : select tag ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    setChoice(event.target.value);
  };

  return (
    <div className="App">
      <select onChange={handleFruit}>{options}</select>
      <h3>You choose "{choice}"</h3>
    </div>
  );
}

export default SelectExample;

Action item - Pop up

// Pop up ์—ญ์‹œ Pop up์˜ open๊ณผ close๋ฅผ state๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

import React, { useState } from "react";
import "./styles.css";

function App() {
  const [showPopup, setShowPopup] = useState(false);

  const togglePopup = () => {
		// Pop up ์˜ open/close ์ƒํƒœ์— ๋”ฐ๋ผ
    // ํ˜„์žฌ state ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋„๋ก ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    setShowPopup(!showPopup);
  };

  return (
    <div className="App">
      <h1>Fix me to open Pop Up</h1>
			{/* ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ Pop up ์˜ open/close ๊ฐ€ ์ž‘๋™ํ•˜๋„๋ก
          button tag๋ฅผ ์™„์„ฑํ•˜์„ธ์š”. */}
      <button className="open" onClick={togglePopup}>
        Open me
      </button>
      {showPopup ? (
        <div className="popup">
          <div className="popup_inner">
            <h2>Success!</h2>
            <button className="close" onClick={togglePopup}>
              Close me
            </button>
          </div>
        </div>
      ) : null}
    </div>
  );
}

export default App;


๐Ÿ’ฌ Sprint Review


React Twittler State & Props ์Šคํ”„๋ฆฐํŠธ์—์„œ๋Š” ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด
state, props๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์œ ์ € parkhacker์˜ Twittler์—์„œ์˜ ํŠธ์œ— ์ „์†ก ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค.

์ด๋ฒˆ ์Šคํ”„๋ฆฐํŠธ์˜ ํ•ต์‹ฌ์€ ํŠธ์œ— ์ถ”๊ฐ€ ๋ฐ ์ถ”๊ฐ€๋œ ํŠธ์œ— ์กฐํšŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด๋‹ค.

์ด ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด์„œ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์— ์–ด๋–ค state๊ฐ€ ํ•„์š”ํ•˜๊ณ , ์ด state๋ฅผ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™”์‹œํ‚ฌ ๊ฒƒ์ธ์ง€ ๊ณ ๋ฏผํ•ด ๋ณด์ž.


๐Ÿ”ฅ Bare Minimum Requirement


โœ… React Router ์„ค์น˜

  • React Router๋ฅผ npm์œผ๋กœ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… ์ƒ์„ธ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ

  • ์›ํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ์— ๋ผ์›Œ ๋„ฃ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ƒ์„ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋จผ์ € ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

โœ… Sidebar ์ปดํฌ๋„ŒํŠธ (Sidebar.js)

  • Sidebar ์ปดํฌ๋„ŒํŠธ๋Š” ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. Sidebar.js ํŒŒ์ผ์—์„œ ์ง์ ‘ ํ™•์ธํ•˜์„ธ์š”.

โœ… Footer ์ปดํฌ๋„ŒํŠธ (Footer.js)

  • Footer ์ปดํฌ๋„ŒํŠธ์˜ ํ›„์† ์—˜๋ฆฌ๋จผํŠธ๋กœ ์‹œ๋ฉ˜ํ‹ฑ ์—˜๋ฆฌ๋จผํŠธ footer๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… Tweet ์ปดํฌ๋„ŒํŠธ (Tweet.js)

  • ๊ฐ ํŠธ์œ—์— ๊ผญ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (ํ”„๋กœํ•„ ์‚ฌ์ง„, ์œ ์ € ์ด๋ฆ„, ํŠธ์œ— ์ƒ์„ฑ ์ผ์ž, ํŠธ์œ— ๋ฉ”์‹œ์ง€)
    • ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ๋„ฃ๊ธฐ ์œ„ํ•ด img ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , src ์†์„ฑ์— ์ „๋‹ฌ๋ฐ›์€ props์˜ ์‚ฌ์ง„ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.
    • ์œ ์ € ์ด๋ฆ„์„ ๋„ฃ๊ธฐ ์œ„ํ•ด span ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , class ์ด๋ฆ„์„ tweet__username๋กœ ์ง€์ •ํ•˜์„ธ์š”.
    • ํŠธ์œ— ์ƒ์„ฑ ์ผ์ž๋ฅผ ๋„ฃ๊ธฐ ์œ„ํ•ด span์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , class ์ด๋ฆ„์„ tweet__createdAt์œผ๋กœ ์ง€์ •ํ•˜์„ธ์š”.
      • ๋‚ ์งœ ํ˜•์‹์€ yyyy. mm. dd. ์ด์–ด์•ผ ํ•˜๊ณ , parsedDate ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•˜์„ธ์š”.
    • ํŠธ์œ— ๋ฉ”์‹œ์ง€๋ฅผ ๋„ฃ๊ธฐ ์œ„ํ•ด div ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. class ์ด๋ฆ„์„ tweet__message๋กœ ์ง€์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.
      • ํŠธ์œ— ๋ฉ”์‹œ์ง€๋ฅผ div.tweet__message์˜ textContent๋กœ ๋„ฃ์Šต๋‹ˆ๋‹ค.
  • dummyTweets๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๊ฐ€ props๋กœ ์ „๋‹ฌ๋˜์–ด๋„ ํŠธ์œ— ์ •๋ณด๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ

  • ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋ฒˆ ๊ณผ์ œ์˜ ํ˜„์žฌ ์œ ์ €๋Š” parkhacker์ด๊ธฐ ๋•Œ๋ฌธ์—, Twittler์˜ MyPage์—์„œ parkhacker์˜ ํŠธ์œ—๋งŒ ๋ณด์—ฌ์•ผ ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… About ์ปดํฌ๋„ŒํŠธ (About.js)

  • About ์ปดํฌ๋„ŒํŠธ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ Footer ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์‹œ๊ณ , ์—†๋‹ค๋ฉด ์—ฐ๊ฒฐํ•ด ์ฃผ์„ธ์š”.

โœ… MyPage ์ปดํฌ๋„ŒํŠธ (MyPage.js)

  • ์ฃผ์–ด์ง„ ํŠธ์œ— ๋ชฉ๋ก(dummyTweets)์ค‘ ํ˜„์žฌ ์œ ์ €์ธ parkhacker์˜ ํŠธ์œ—๋งŒ ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • const ๋ณ€์ˆ˜๋กœ ์„ ์–ธ๋œ filteredTweets์„ ํ™œ์šฉํ•ด parkhacker์˜ ํŠธ์œ—์ด ๋ณด์ด๋„๋ก ๊ตฌํ˜„ํ•ด ์ฃผ์„ธ์š”.
  • MyPage ์ปดํฌ๋„ŒํŠธ์˜ ์ž์‹์ธ Tweet ์ปดํฌ๋„ŒํŠธ์— props๋กœ ๊ฐ ํŠธ์œ—์˜ ์ •๋ณด(dummyTweets์˜ ์š”์†Œ)๊ฐ€ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • MyPage ์ปดํฌ๋„ŒํŠธ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ Footer ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • MyPage๋Š” dummyTweets๋กœ ๊ตฌํ˜„๋œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
      State&Props๋กœ ๊ตฌ์„ฑํ•œ ์ปดํฌ๋„ŒํŠธ์™€ ๋น„๊ตํ•˜์—ฌ ์™œ State&Props๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์ดํ•ดํ•ด ๋ณด์„ธ์š”.

โœ… Tweets ์ปดํฌ๋„ŒํŠธ (Tweets.js)

  • ํ•˜๋‚˜์˜ ํŠธ์œ—์ด ์•„๋‹ˆ๋ผ, ์ฃผ์–ด์ง„ ํŠธ์œ—(dummyTweets) ๊ฐœ์ˆ˜์— ๋งž๊ฒŒ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Tweets ์ปดํฌ๋„ŒํŠธ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ Footer ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… React Router ์ ์šฉํ•˜๊ธฐ

  • ์ด์ „ React Twittler SPA ๊ณผ์ œ์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์•„๋ž˜ ๊ธฐ์ˆ  ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

โœ… React Router ์ปดํฌ๋„ŒํŠธ ์ ์šฉ

  • Route path๊ฐ€ "/"์ธ Tweets ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Route path๊ฐ€ "/about"์ธ About ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Route path๊ฐ€ "/mypage"์ธ MyPage ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • React Router์˜ Link ์ปดํฌ๋„ŒํŠธ๊ฐ€ 3๊ฐœ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Tweets ์•„์ด์ฝ˜์˜ Link ์ปดํฌ๋„ŒํŠธ๋Š” "/"๋กœ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • About ์•„์ด์ฝ˜์˜ Link ์ปดํฌ๋„ŒํŠธ๋Š” "/about"๋กœ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • MyPage ์•„์ด์ฝ˜์˜ Link ์ปดํฌ๋„ŒํŠธ๋Š” "/mypage"๋กœ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… React Router๋กœ SPA ๊ตฌํ˜„ํ•˜๊ธฐ

  • ์ฒ˜์Œ ์ ‘์† ์‹œ, URL path๊ฐ€ /์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • About ๋ฉ”๋‰ด๋ฅผ ๋ˆ„๋ฅด๋ฉด URL path๊ฐ€ /about์œผ๋กœ ๋ผ์šฐํŠธ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Mypage ๋ฉ”๋‰ด๋ฅผ ๋ˆ„๋ฅด๋ฉด URL path๊ฐ€ /mypage๋กœ ๋ผ์šฐํŠธ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ… State, Props ํ™œ์šฉ ํŠธ์œ— ์ „์†ก Form ๋งŒ๋“ค๊ธฐ

  • ์ด๋ฒˆ ๊ณผ์ œ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. Tweets ์ปดํฌ๋„ŒํŠธ์—์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ state๊ฐ€ ๋˜์–ด์•ผ ํ•˜๊ณ , ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ props๋กœ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•ด์•ผ ํ• ์ง€ ๊ณ ๋ฏผํ•˜๊ณ  ์•„๋ž˜ ๊ธฐ์ˆ  ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. State, Props ๋ ˆ์Šจ๊ณผ ์‹ค์Šต์ด ๋„์›€์ด ๋  ๊ฒ๋‹ˆ๋‹ค. ๐Ÿ˜‡

โœ… Tweets.js ํŠธ์œ— ์ „์†ก Form ํ…Œ์ŠคํŠธ

  • ์œ ์ € ์ด๋ฆ„์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” input ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (className : "tweetForm__input--username")
  • input์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ onChange ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ถˆ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํŠธ์œ—์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” textarea ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (className : "tweetForm__input--message")
  • textarea์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ onChange ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ถˆ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    ํŠธ์œ—์„ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” button ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (className : "tweetForm__submitButton")
  • button์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ onClick ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ถˆ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์ € ์ด๋ฆ„๊ณผ ํŠธ์œ—์„ ์ž‘์„ฑํ•˜๊ณ , ํŠธ์œ— ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ƒˆ๋กœ์šด ํŠธ์œ—์ด ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ธฐ์กด dummyTweets๋ฅผ ๋ชจ๋‘ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ํŠธ์œ—์„ ํฌํ•จํ•˜์—ฌ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ํŠธ์œ—์ด ์ตœ์ƒ๋‹จ์— ์œ„์น˜ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.


โš™๏ธ ์ฝ”๋“œ & ๊ตฌํ˜„ ํ™”๋ฉด


// TODO : useState๋ฅผ react๋กœ ๋ถ€ํ„ฐ import ํ•ฉ๋‹ˆ๋‹ค.
import React from "react";
import Footer from "../Footer";
import Tweet from "../Components/Tweet";
import "./Tweets.css";
import dummyTweets from "../static/dummyData";
import { useState } from "react";

const Tweets = () => {
// TODO : ์ƒˆ๋กœ ํŠธ์œ—์„ ์ž‘์„ฑํ•˜๊ณ  ์ „์†กํ•  ์ˆ˜ ์žˆ๊ฒŒ useState๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜์„ธ์š”.
// -> useState๋ฅผ ๋ช‡ ๊ณณ์— ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ์ƒ๊ฐํ•œ๋‹ค.
  const [newComment, setNewComment] = useState(dummyTweets);
  const [user, setUser] = useState("parkhacker");
  const [msg, setMsg] = useState("");

  const handleButtonClick = (event) => {
    // TODO : Tweet button ์—˜๋ฆฌ๋จผํŠธ ํด๋ฆญ์‹œ ์ž‘๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    // ํŠธ์œ— ์ „์†ก์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    // -> ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ์ž…๋ ฅํ•œ data ๊ฐ’์ด dummyTweets์— ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•œ๋‹ค.
    // -> tweet๋“ค์ด ๋ชจ์—ฌ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ ์•ˆ์— ๋‚ด๊ฐ€ ์ž…๋ ฅํ•œ tweet์„ ์ปดํฌ๋„ŒํŠธํ™”ํ•ด์„œ ๋„ฃ์–ด์ค€๋‹ค.
    // -> useState ์‚ฌ์šฉ, spread ๋ฌธ๋ฒ•์œผ๋กœ ๋ฐฐ์—ด์— ์ถ”๊ฐ€
    const tweet = {
      ...dummyTweets,
      id: dummyTweets.length,
      username: user,
      content: msg,
      createdAt: new Date().toLocaleDateString("ko-kr"),
      updatedAt: new Date().toLocaleDateString("ko-kr"),
    };
    setNewComment([tweet, ...newComment]);
  };

  const handleChangeUser = (event) => {
    // TODO : Tweet input ์—˜๋ฆฌ๋จผํŠธ์— ์ž…๋ ฅ ์‹œ ์ž‘๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    setUser(event.target.value);
  };

  const handleChangeMsg = (event) => {
    // TODO : Tweet textarea ์—˜๋ฆฌ๋จผํŠธ์— ์ž…๋ ฅ ์‹œ ์ž‘๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    setMsg(event.target.value);
  };

  return (
    <React.Fragment>
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile">
            <img src="https://randomuser.me/api/portraits/men/98.jpg" />
          </div>
          <div className="tweetForm__inputContainer">
            <div className="tweetForm__inputWrapper">
              <div className="tweetForm__input">
                {/* -> ํผ ์—˜๋ฆฌ๋จผํŠธ์—์„œ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ ๊ฐ’์€ onChange ์ด๋ฒคํŠธ๋กœ state ๊ด€๋ฆฌ & ์—…๋ฐ์ดํŠธ */}
                <input
                  type="text"
                  value={user}
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                ></input>
                
                <textarea
                  className="tweetForm__input--message"
                  value={msg}
                  placeholder="your message here.."
                  onChange={handleChangeMsg}
                />
              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  {/* TODO : ํŠธ์œ— ์ด ๊ฐœ์ˆ˜๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” Counter๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. */}
				  {/* -> dummyTweets๊ฐ€ ์•„๋‹Œ useState๊ฐ€ ์ƒํƒœ ๊ด€๋ฆฌํ•˜๋Š” element(๋ฐฐ์—ด)๊ฐ€ ์™€์•ผ ํ•œ๋‹ค. */}
                  {`Total: ${newComment.length}`}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              {/* TODO : ์ž‘์„ฑํ•œ ํŠธ์œ—์„ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” button ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. */}
              {/* -> ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญ์ด๋ผ๋Š” ํ–‰๋™์„ ํ•˜์˜€์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์ด๋ฏ€๋กœ onClick */}
              <button
                className="tweetForm__submitButton"
                onClick={handleButtonClick}
              >
                submit
              </button>
            </div>
          </div>
        </div>
      </div>
      <div className="tweet__selectUser"></div>
      <ul className="tweets">
        {/* TODO : ํ•˜๋‚˜์˜ ํŠธ์œ—์ด ์•„๋‹ˆ๋ผ, ์ฃผ์–ด์ง„ ํŠธ์œ— ๋ชฉ๋ก(dummyTweets) ๊ฐฏ์ˆ˜์— ๋งž๊ฒŒ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
        {/* -> `Total: ${newComment.length}`๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ™์€ element(๋ฐฐ์—ด)๋ฅผ ๊ฐ€์ ธ์™€ map์œผ๋กœ ๋ฟŒ๋ ค์ค€๋‹ค. */}
        {newComment.map((el) => {
          return <Tweet tweet={el} />;
        })}
      </ul>
      <Footer />
    </React.Fragment>
  );
};

export default Tweets;

import React from "react";
import Footer from "../Footer";
import Tweet from "../Components/Tweet";
import "./MyPage.css";
import dummyTweets from "../static/dummyData";

const MyPage = () => {
// TODO : ์ฃผ์–ด์ง„ ํŠธ์œ— ๋ชฉ๋ก(dummyTweets)์ค‘ ํ˜„์žฌ ์œ ์ ธ์ธ parkhacker์˜ ํŠธ์œ—๋งŒ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
// -> dummyTweets ๋ฐฐ์—ด์—์„œ filter๋กœ parkhacker ๊ฐ’๋งŒ ์ถ”์ถœ
  const filteredTweets = dummyTweets.filter((tweet) => {
    return tweet.username === "parkhacker";
  });

  return (
    <section className="myInfo">
      <div className="myInfo__container">
        <div className="myInfo__wrapper">
          <div className="myInfo__profile">
            <img src={filteredTweets[0].picture} />
          </div>
          <div className="myInfo__detail">
            <p className="myInfo__detailName">
              {filteredTweets[0].username} Profile
            </p>
            <p>28 ํŒ”๋กœ์›Œ 100 ํŒ”๋กœ์ž‰</p>
          </div>
        </div>
      </div>
      <ul className="tweets__mypage">
        <Tweet tweet={filteredTweets[0]} />
        {/* TODO : ์ฃผ์–ด์ง„ ํŠธ์œ— ๋ชฉ๋ก(dummyTweets)์ค‘ ํ˜„์žฌ ์œ ์ ธ์ธ parkhacker์˜ ํŠธ์œ—๋งŒ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
      </ul>
      <Footer />
    </section>
  );
};

export default MyPage;
profile
์•„์ด๋””์–ด๊ฐ€ ๋„˜์น˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ฟˆ๊ฟ‰๋‹ˆ๋‹ค ๐Ÿ”ฅ

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