본문 바로가기

코드스테이츠 FE 45기

[React] 과제: React Twittler State & Props

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를 모두 보여줘야 합니다.새로 추가된 트윗을 포함하여 보여줘야 합니다.새로 추가된 트윗이 최상단에 위치하여야 합니다.

 

알맞게 import 하기, Router하기 관련 주제는 저번 과제에서 다뤘으므로 생략하고 가장 어려웠던 Tweet.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 [user, setUser] = useState("parkhacker");
  const [msg, setMsg] = useState("");
  const [tweets, setTweets] = useState(dummyTweets);
  const getRandomNumber = (min, max) => {
    return parseInt(Math.random() * (Number(max) - Number(min) + 2));
  };

  const handleButtonClick = (event) => {
    const tweet = {
      id: tweets.length + 1,
      username: user,
      picture: `https://randomuser.me/api/portraits/women/${getRandomNumber(
        1,
        98
      )}.jpg`,
      content: msg,
      createdAt: new Date().toLocaleDateString("ko-KR"),
      updatedAt: new Date().toLocaleDateString("ko-KR"),
    };

    setTweets([tweet, ...tweets]);
    console.log(dummyTweets);
    console.log([tweet, ...tweets]);
  };
  // TODO : Tweet button 엘리먼트 클릭시 작동하는 함수를 완성하세요.
  // 트윗 전송이 가능하게 작성해야 합니다.

  const handleChangeUser = (event) => {
    setUser(event.target.value);
    // TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
  };

  const handleChangeMsg = (event) => {
    setMsg(event.target.value);
    // TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
  };

  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"
                  defaultValue="parkhacker"
                  placeholder="your username here.."
                  className="tweetForm__input--username"
                  onChange={handleChangeUser}
                  value={user}
                ></input>
                <textarea
                  type="text"
                  defaultValue={""}
                  placeholder="your tweet here.."
                  className="tweetForm__input--message"
                  onChange={handleChangeMsg}
                  value={msg}
                ></textarea>
              </div>
              <div className="tweetForm__count" role="status">
                <span className="tweetForm__count__text">
                  {/* TODO : 트윗 총 개수를 보여줄 수 있는 Counter를 작성하세요. */}
                  {"total: " + tweets.length}
                </span>
              </div>
            </div>
            <div className="tweetForm__submit">
              <div className="tweetForm__submitIcon"></div>
              <button
                className="tweetForm__submitButton"
                onClick={handleButtonClick}
              >
                Tweet
              </button>
            </div>
          </div>
        </div>
      </div>
      <div className="tweet__selectUser"></div>
      <ul className="tweets">
        {/* TODO : 하나의 트윗이 아니라, 주어진 트윗 목록(dummyTweets) 갯수에 맞게 보여줘야 합니다. */}
        {tweets.map((tweet) => {
          return <Tweet tweet={tweet} />;
        })}
      </ul>
      <Footer />
    </React.Fragment>
  );
};

export default Tweets;

 

state, useState를 이해하기가 주요 핵심 과제였던 것 같다. 

과제를 처음 접했을때 나는 개념에 대한 정확한 이해를 하기보단 읽은대로 대충 때려맞췄다. 

특히

const [user, setUser] = useState("parkhacker");
  const [msg, setMsg] = useState("");

const handleChangeUser = (event) => {
    setUser(event.target.value);
    // TODO : Tweet input 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
  };

  const handleChangeMsg = (event) => {
    setMsg(event.target.value);
    // TODO : Tweet textarea 엘리먼트에 입력 시 작동하는 함수를 완성하세요.
  };

 

위와 같은 경우엔 힌트가 주어졌고, 대충 유저 닉네임과 트윗 내용에 관해 useState 를 사용해보라는 이야기여서 그냥 따라해보는 것만으로 테스트 통과가 됐다. 

문제는 form 관련한 것이었다. 이미 useState를 사용한 예를 보았으므로 트윗도 useState를 사용하라는 거구나, 까지는 눈치껏 알수 있었지만 어떻게? 가 문제였던 것 같다. . 객체 형식으로 끼워맞춰보자! 해놓고 또 setTweets 부분에서 tweets..? dummytweets? 하며 헷갈려하는 내가 있었다.

 

페어분의 친절한 설명과 던져주신 관련 영상과 글들로 state 관련해 더 개념정리를 할 수 있었다 

// 업데이트 후 링크 추가하기

 

하루하루 배운 개념 관련해서 개념정리! 하는 것, 그걸 다른사람에게 설명할 정도로 이해하기 가 굉장히 중요하단 생각이 들었다

'코드스테이츠 FE 45기' 카테고리의 다른 글

[사용자 친화 웹] UI / UX  (1) 2023.06.13
[HTTP/Network] REST API  (0) 2023.05.26
[React] 과제: React Twittler SPA  (0) 2023.05.19
[React] React SPA  (0) 2023.05.19
[React] 과제: React Twittler Intro  (1) 2023.05.19