Jieunny์˜ ๋ธ”๋กœ๊ทธ

S2) Unit 6. [์‹ค์Šต] React Twittler State & Props ๋ณธ๋ฌธ

CodeStates/Training

S2) Unit 6. [์‹ค์Šต] React Twittler State & Props

Jieunny 2023. 1. 27. 09:54

๐Ÿ“ฃ  ๋ฆฌ์•กํŠธ๋กœ Twitter ๊ตฌํ˜„ํ•˜๊ธฐ

โœ”๏ธ react-router-dom ์‚ฌ์šฉํ•˜๊ธฐ

โœ”๏ธ state & props ์‚ฌ์šฉํ•ด์„œ ์ „์†ก ํŠธ์œ— ๋งŒ๋“ค๊ธฐ

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

โœ”๏ธ font awesome ์‚ฌ์šฉํ•˜๊ธฐ

 

๐Ÿ“ฃ ์ฃผ์š” ์ปดํฌ๋„ŒํŠธ

1๏ธโƒฃ App.js

import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// TODO : React Router DOM์„ ์„ค์น˜ ํ›„, import ๊ตฌ๋ฌธ์„ ์ด์šฉํ•˜์—ฌ BrowserRouter, Routes, Route ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

import Sidebar from './Sidebar';
import Tweets from './Pages/Tweets';
import MyPage from './Pages/MyPage';
import About from './Pages/About';
// TODO : MyPage, About ์ปดํฌ๋„ŒํŠธ๋ฅผ import ํ•ฉ๋‹ˆ๋‹ค.

import './App.css';
import './global-style.css';

const App = (props) => {
  return (
    <BrowserRouter>
      <div className="App">
        <main>
          <Sidebar />
          <section className="features">
            {/* TODO : ์œ ์–ดํด๋ž˜์Šค๋ฅผ ์ฐธ๊ณ ํ•ด์„œ, ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ†ต๊ณผํ•˜์„ธ์š”.
              TODO : React Router DOM ์„ค์น˜ ํ›„ BrowserRouter, Routes, Route์˜ ์ฃผ์„์„ ํ•ด์ œํ•˜๊ณ  Routes, Route ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. */}
            {/* Route ์˜ˆ์‹œ: <Route path="/" element={<Tweets />}></Route> */}
            <Routes>
              <Route path="/" element={<Tweets />} />
              <Route path="/about" element={<About />} />
              <Route path="/mypage" element={<MyPage />} />
            </Routes>
          </section>
        </main>
      </div>
    </BrowserRouter>
  );
};

// ! ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
export default App;

โžฐ BrowserRouter, Routes, Route ์‚ฌ์šฉํ•ด์„œ ํŠน์ • ๊ฒฝ๋กœ์—๋Š” ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋งŒ ๋„์šฐ๊ฒŒ ํ•œ๋‹ค.

 

2๏ธโƒฃ Sidebar.js

import React from 'react';
import { Link } from 'react-router-dom';
// TODO : React Router DOM์˜ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ import ํ•ฉ๋‹ˆ๋‹ค.

const Sidebar = () => {
  return (
    <section className="sidebar">
      {/* TODO : Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , to ์†์„ฑ์„ ์ด์šฉํ•˜์—ฌ ๊ฒฝ๋กœ(path)๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. */}
      <Link to="/"><i className="far fa-comment-dots"></i></Link>
      <Link to="/about"><i className="far fa-question-circle"></i></Link>
      <Link to="/mypage"><i className="far fa-user"></i></Link>
    </section>
  );
};

export default Sidebar;

โžฐ font awesome ์‚ฌ์šฉํ•ด์„œ ์•„์ด์ฝ˜ ๋ถˆ๋Ÿฌ์˜ค๊ณ , Route์™€ ๋™์ผํ•œ ๊ฒฝ๋กœ๋ฅผ Link์— ์„ค์ •ํ•ด์„œ ํŽ˜์ด์ง€ ์ด๋™ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ

 

3๏ธโƒฃ Tweets.js

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

const Tweets = () => {
  // TODO : ์ƒˆ๋กœ ํŠธ์œ—์„ ์ž‘์„ฑํ•˜๊ณ  ์ „์†กํ•  ์ˆ˜ ์žˆ๊ฒŒ useState๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜์„ธ์š”.

  const [inputText, setInputText] = useState('');
  const [textareaContent, setTextareaContent] = useState('');
  const [totalTweets, setTotalTweets] = useState(dummyTweets);

  const handleButtonClick = (event) => {
    const getRandomNumber = (min, max) => {
      return parseInt(Math.random() * (Number(max) - Number(min) + 2));
    };
    
    const tweet = {
      id: totalTweets.length+1,
      username: inputText,
      picture: `https://randomuser.me/api/portraits/women/${getRandomNumber(
        1,
        98
      )}.jpg`,
      content: textareaContent,
      createdAt: new Date().toLocaleDateString('ko-kr'),
      updateAt: new Date().toLocaleDateString('ko-kr')
    };
    // TODO : Tweet button ์—˜๋ฆฌ๋จผํŠธ ํด๋ฆญ์‹œ ์ž‘๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    // ํŠธ์œ— ์ „์†ก์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    setTotalTweets([tweet, ...totalTweets]);
  };

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

  const handleChangeMsg = (event) => {
    // TODO : Tweet textarea ์—˜๋ฆฌ๋จผํŠธ์— ์ž…๋ ฅ ์‹œ ์ž‘๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์™„์„ฑํ•˜์„ธ์š”.
    setTextareaContent(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">
                <input
                  type="text"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                ></input>
                <textarea
                  placeholder="your text here.."
                  className="tweetForm__input--message"
                  onChange={handleChangeMsg}>    
                </textarea>
              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  {/* TODO : ํŠธ์œ— ์ด ๊ฐœ์ˆ˜๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” Counter๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. */}
                  {'total: ' + totalTweets.length}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              {/* TODO : ์ž‘์„ฑํ•œ ํŠธ์œ—์„ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” button ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. */}
              <button
                className="tweetForm__submitButton"
                onClick={handleButtonClick}>์ „์†ก
              </button>
            </div>
          </div>
        </div>
      </div>
      <div className="tweet__selectUser"></div>
      <ul className="tweets">
        {/* TODO : ํ•˜๋‚˜์˜ ํŠธ์œ—์ด ์•„๋‹ˆ๋ผ, ์ฃผ์–ด์ง„ ํŠธ์œ— ๋ชฉ๋ก(dummyTweets) ๊ฐฏ์ˆ˜์— ๋งž๊ฒŒ ๋ณด์—ฌ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
        {totalTweets.map((tweet) => {
          return <Tweet tweet={tweet} key={tweet.id} />
        })}
      </ul>
      <Footer />
    </React.Fragment>
  );
};

export default Tweets;

โžฐ useState() ์‚ฌ์šฉํ•ด์„œ ๋‹‰๋„ค์ž„ ์“ฐ๋Š” ์ž…๋ ฅ์นธ, ํŠธ์œ— ๋‚ด์šฉ ์“ฐ๋Š” ํ…์ŠคํŠธ์นธ, ์ „์ฒด ํŠธ์œ— ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ด์ค€๋‹ค.

โžฐ ์ƒˆ๋กœ ํŠธ์œ—์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ „์ฒด ํŠธ์œ—์ด ๋Š˜์–ด๋‚˜์•ผ ํ•˜๋ฏ€๋กœ, ์ „์†ก ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ „์ฒด ํŠธ์œ— ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”๊ฐ€ํ•œ ํŠธ์œ—์ด ๋”ํ•ด์ง„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋˜์•ผํ•œ๋‹ค.

โžฐ ๋‹‰๋„ค์ž„ ์ž…๋ ฅ์นธ, ํ…์ŠคํŠธ ์ž…๋ ฅ์นธ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ ์„ ๋•Œ๋งˆ๋‹ค ๋ณ€ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, event ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ’์„ ๋ฐ›์•„์˜ค๊ณ , onChange์— ๊ทธ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ด์ค€๋‹ค.

 

๐Ÿ“ฃ ์‹œ์—ฐํ™”๋ฉด

css๋Š” ์•ˆํ• ๋žญ..