Unit4 - [React] Intro

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

โœ… TIL


๋ฆฌ์•กํŠธ๋ž€?

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ JavaScript ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

์„ ์–ธํ˜•์ด๊ณ , ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์ด๊ณ , ๋‹ค์–‘ํ•œ ๊ณณ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.

  • ์„ ์–ธํ˜•: ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด HTML / CSS / JS๋กœ ๋‚˜๋ˆ ์ ๊ธฐ ๋ณด๋‹ค๋Š”
    ํ•˜๋‚˜์˜ ํŒŒ์ผ์— ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ JSX๋ฅผ ํ™œ์šฉํ•œ ์„ ์–ธํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€ํ–ฅํ•œ๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜: ๋ฆฌ์•กํŠธ๋Š” ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ์ฝ”๋“œ๋ฅผ ๋ฌถ์–ด๋‘” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ•œ๋‹ค.
    ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด ์„œ๋กœ ๋…๋ฆฝ์ ์ด๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ธฐ๋Šฅ ์ž์ฒด์— ์ง‘์ค‘ํ•˜์—ฌ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฒ”์šฉ์„ฑ: ๋ฆฌ์•กํŠธ๋Š” JavaScript ํ”„๋กœ์ ํŠธ ์–ด๋””์—๋“  ์œ ์—ฐํ•˜๊ฒŒ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.
    Facebook์—์„œ ๊ด€๋ฆฌ๋˜์–ด ์•ˆ์ •์ ์ด๊ณ , ๊ฐ€์žฅ ์œ ๋ช…ํ•˜๋ฉฐ, ๋ฆฌ์•กํŠธ ๋„ค์ดํ‹ฐ๋ธŒ๋กœ ๋ชจ๋ฐ”์ผ ๊ฐœ๋ฐœ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.



JSX


DOM์œผ๋กœ ์—˜๋ฆฌ๋จผํŠธ ์ƒ์„ฑํ•˜๊ธฐ ์˜ˆ์ œ


DOM์—์„œ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋จผ์ € ๋ณด์ž.

์•„๋ž˜์™€ ๊ฐ™์ด Hello, HTML DOM์„ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด์„œ ์•„๋ž˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ
HTML(index.html)๊ณผ JavaScript(index.js)๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•ด์•ผ ํ•œ๋‹ค.


import "./styles.css";

const user = {
  firstName: "HTML",
  lastName: "DOM"
};

function formatName(user) {
  return user.firstName + " " + user.lastName;
}

const heading = document.createElement("h1");
heading.textContent = `Hello, ${formatName(user)}`;
document.body.appendChild(heading);


๊ทธ๋ ‡๋‹ค๋ฉด React์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์ž‘์—…ํ• ๊นŒ?


React๋กœ ์—˜๋ฆฌ๋จผํŠธ ์ƒ์„ฑํ•˜๊ธฐ ์˜ˆ์ œ


์•„๋ž˜ ์˜ˆ์ œ๋Š” JSX๋ฅผ ์“ฐ์ง€ ์•Š์€ React๋ฅผ ๋จผ์ € ๋‚˜ํƒ€๋‚ด๊ณ  ์žˆ๋‹ค.

JSX ์“ฐ์ง€ ์•Š์€ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์‹  ํ›„, ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜์ž.
์ฝ”๋“œ ๋ผ์ธ 17๋ฒˆ์งธ ์ค„ ์ฃผ์„์„ ํ•ด์ œํ•˜๋ฉด JSX๋ฅผ ํ™œ์šฉํ•œ React์˜ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ๋‹ค.


import React from "react";
import "./styles.css";

function App() {
  const user = {
    firstName: "React",
    lastName: "JSX Activity"
  };

  function formatName(user) {
    return user.firstName + " " + user.lastName;
  }
  // JSX ์—†์ด ํ™œ์šฉํ•œ React
  return React.createElement("h1", null, `Hello, ${formatName(user)}!`);

  // JSX ๋ฅผ ํ™œ์šฉํ•œ React
  // return <h1>Hello, {formatName(user)}!</h1>;
}

export default App;

์šฐ๋ฆฌ๋Š” App.js ์ด ํ•œ ๊ฐœ์˜ JavaScript ํŒŒ์ผ ์•ˆ์—์„œ HTML๊ณผ JavaScript๋กœ ๋‚˜๋ˆ„์–ด์กŒ๋˜ ๋‘ ๊ฐ€์ง€ ์ผ์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.

์ฒ˜์Œ์—๋Š” HTML๊ณผ JavaScript๋ฅผ ํ•œ ๊ณณ์— ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ์–ด์ƒ‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐ์™€ ๋™์ž‘์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ JSX๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋งŒ ๋ฐ”๋ผ๋ณด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ตฌ์กฐ๋ฅผ ๋ฐ”๋ผ๋ณด๊ฒŒ ๋•๋Š”๋‹ค.


JSX ๊ทœ์น™


ํ•˜๋‚˜์˜ ์—˜๋ฆฌ๋จผํŠธ ์•ˆ์— ๋ชจ๋“  ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ํฌํ•จ

JSX์—์„œ ์—ฌ๋Ÿฌ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒฝ์šฐ, ์ตœ์ƒ์œ„์—์„œ opening tag์™€ closing tag๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•œ๋‹ค.


// ํ‹€๋ฆฐ ์˜ˆ์‹œ

<div>
	<h1>Hello</h1>
</div>
<div>
	<h2>World</h2>
</div>

// ์˜ณ์€ ์˜ˆ์‹œ

<div>
	<div>
		<h1>Hello</h1>
	</div>
	<div>
		<h2>World</h2>
	</div>
</div>

์—˜๋ฆฌ๋จผํŠธ ํด๋ž˜์Šค ์‚ฌ์šฉ ์‹œ, className์œผ๋กœ ํ‘œ๊ธฐ

React์—์„œ CSS class ์†์„ฑ์„ ์ง€์ •ํ•˜๋ ค๋ฉด className ์œผ๋กœ ํ‘œ๊ธฐํ•ด์•ผ ํ•œ๋‹ค.

๋งŒ์•ฝ class๋กœ ์ž‘์„ฑํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด React์—์„œ๋Š”
์ด๋ฅผ html ํด๋ž˜์Šค ์†์„ฑ ๋Œ€์‹ , ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค๋กœ ๋ฐ›์•„๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.


// ํ‹€๋ฆฐ ์˜ˆ์‹œ

<div class="greeting">Hello!</div>

// ์˜ณ์€ ์˜ˆ์‹œ

<div className="greeting">Hello!</div>

JavaScript ํ‘œํ˜„์‹ ์‚ฌ์šฉ ์‹œ, ์ค‘๊ด„ํ˜ธ ({}) ์ด์šฉ

JSX์—์„œ JavaScript๋ฅผ ์“ฐ๊ณ ์ž ํ•œ๋‹ค๋ฉด, ๊ผญ ์ค‘๊ด„ํ˜ธ๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ค‘๊ด„ํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ์ธ์‹ํ•œ๋‹ค.


function App() {
  
  const name = 'Josh Perez';
  
  return (
    <div>
      Hello, {name};
    </div>
  );
)

์‚ฌ์šฉ์ž ์ •์˜ ์ปดํฌ๋„ŒํŠธ๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ (PascalCase)

React ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ JSX๋กœ ์ž‘์„ฑ๋˜๋ฉด "๋Œ€๋ฌธ์ž"๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
์†Œ๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜๋ฉด ์ผ๋ฐ˜์ ์ธ HTML ์—˜๋ฆฌ๋จผํŠธ๋กœ ์ธ์‹์„ ํ•˜๊ฒŒ ๋œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋Œ€๋ฌธ์ž๋กœ ์ž‘์„ฑ๋œ JSX ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋”ฐ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ์ปดํฌ๋„ŒํŠธ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.


// ํ‹€๋ฆฐ ์˜ˆ์‹œ

function hello() {
  
  return <div>Hello!</div>;
  
}

function HelloWrold() {
  
  return <hello />;
  
}

// ์˜ณ์€ ์˜ˆ์‹œ

function Hello() {
 
  return <div>Hello!</div>;
  
}

function HelloWrold() {
  
  return <Hello />;
  
}

์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์—๋Š” ์‚ผํ•ญ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ

์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์€ if๋ฌธ์ด ์•„๋‹Œ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.


<div>
  {
    (1+1 === 2) ? (<p>์ •๋‹ต</p>) : (<p>ํƒˆ๋ฝ</p>)
  }
</div>

์—ฌ๋Ÿฌ ๊ฐœ์˜ HTML ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ‘œ์‹œํ•  ๋•Œ, map() ํ•จ์ˆ˜๋ฅผ ์ด์šฉ

React ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ HTML ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ‘œ์‹œํ•  ๋•Œ๋Š” "map()" ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

map ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ "key" JSX ์†์„ฑ์„ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.
"key" JSX ์†์„ฑ์„ ๋„ฃ์ง€ ์•Š์œผ๋ฉด ๋ฆฌ์ŠคํŠธ์˜ ๊ฐ ํ•ญ๋ชฉ์— key๋ฅผ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.


const posts = [
  { id: 1, title: "Hello World", content: "Welcome to learning React" },
  { id: 2, title: "Installation", content: "You can install React from npm" }
];

export default function App() {
  // ํ•œ ํฌ์ŠคํŠธ์˜ ์ •๋ณด๋ฅผ ๋‹ด์€ ๊ฐ์ฒด post๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ 
  // obj๋ฅผ JSX ํ‘œํ˜„์‹์œผ๋กœ ๋ฐ”๊ฟ” ๋ฆฌํ„ดํ•ด์ฃผ๋Š” ํ•จ์ˆ˜ postToJSX
  const postToJSX = (post) => {
    return (
      <div key={post.id}>
        <h3>{post.title}</h3>
        <p>{post.content}</p>
      </div>
    );
  };

  // TODO: postToJSX ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์—˜๋ฆฌ๋จผํŠธ๋กค ํ‘œ์‹œํ•ด๋ณด์„ธ์š”.
  return (
    <div className="App">
      <h1>Hello JSX</h1>
      {posts.map(postToJSX)} // or posts.map((el) => postToJSX(el))
    </div>
  );
}


Create React App


๋ฆฌ์•กํŠธ SPA๋ฅผ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ง„ ํˆด ์ฒด์ธ์ด๋‹ค.

npx create-react-app@latest ํด๋”์ด๋ฆ„


๐Ÿ’ฌ Sprint Review


์ด๋ฒˆ React Twittler Intro ๊ณผ์ œ์—์„œ๋Š” ์œ ๋ช… SNS ์„œ๋น„์Šค์ธ Twitter์™€ ์œ ์‚ฌํ•œ Twittler๋ฅผ React๋กœ ๊ฐœ๋ฐœํ•œ๋‹ค.

์•ž์œผ๋กœ ์žˆ์„ React Twittler Router, React Twittler State Props ๊ณผ์ œ ํ†ต๊ณผ๋ฅผ ์œ„ํ•ด์„œ JSX ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์„ ์ˆ™์ง€ํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ๊ฐœ๋ฐœ์„ ์ฒ˜์Œ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ๊ณ„์ด๋‹ค.

npm install์„ ํ†ตํ•ด ํ•„์š”ํ•œ ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•œ๋‹ค.

  • npm run start : ์‹ค์ œ React Web App์„ ๊ฐœ๋ฐœ ๋ชจ๋“œ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰์‹œ์ผœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • npm run test : ๊ธฐ์ˆ  ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • npm run submit : ๊ณผ์ œ ์ œ์ถœ์„ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ์ด๋‹ค.

npm run start๋ฅผ ํ†ตํ•ด์„œ ๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•ด ๋ณด์ž.

import React from 'react';
import './App.css';
import './global-style.css';
import { dummyTweets } from './static/dummyData';
// ! ์œ„ ์ฝ”๋“œ๋Š” ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
console.log(dummyTweets); // ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋”๋ฏธ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

const Sidebar = () => {
  return (
    <section className="sidebar">
      {/* TODO : ๋ฉ”์„ธ์ง€ ์•„์ด์ฝ˜์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. */}
    </section>
  );
};

const Counter = () => {
  return (
    <div className="tweetForm__input">
      <div className="tweetForm__inputWrapper">
        <div className="tweetForm__count" role="status">
          TODO : dummyTweet๋กœ ์ „๋‹ฌ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ๊ฐฏ์ˆ˜๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
        </div>
      </div>
    </div>
  );
};

const Footer = () => {
  return (
    <div>
      <img id="logo" src={`${process.env.PUBLIC_URL}/codestates-logo.png`} />
      Copyright @ 2022 Code States
    </div>
  );
};
// TODO : Footer ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‹œ๋ฉ˜ํ‹ฑ ์—˜๋ฆฌ๋จผํŠธ footer๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

const Tweets = () => {
  return (
    <ul className="tweets">
      {dummyTweets.map((tweet) => {
        return (
          <li className="tweet" key={tweet.id}>
            <div className="tweet__profile">
              {/* TODO: ํŠธ์œ— ์ €์ž์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.  */}
            </div>
            <div className="tweet__content">
              <div className="tweet__userInfo">
                {/* TODO : ์œ ์ ธ ์ด๋ฆ„์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
                {/* TODO : ์ด๋ฆ„์ด "parkhacker"์ธ ๊ฒฝ์šฐ, ์ด๋ฆ„ ๋ฐฐ๊ฒฝ์ƒ‰์„ rgb(235, 229, 249)์œผ๋กœ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
                {/* TODO : ํŠธ์œ— ์ƒ์„ฑ ์ผ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
              </div>
              TODO : ํŠธ์œ— ๋ฉ”์„ธ์ง€๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
            </div>
          </li>
        );
      })}
    </ul>
  );
};

const Features = () => {
  return (
    <section className="features">
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile"></div>
          <Counter />
        </div>
      </div>
      <Tweets />
      TODO : Footer ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    </section>
  );
};

const App = () => {
  return (
    <div className="App">
      <main>
        TODO : Sidebar ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
        <Features />
      </main>
    </div>
  );
};

// ! ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
export { App, Sidebar, Counter, Tweets, Features, Footer };

ํ˜„์žฌ ์ด ํ™”๋ฉด์„ ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋”ฐ๋ผ ์ฝ”๋”ฉํ•˜๋‹ˆ ์–ด๋ ต์ง€ ์•Š์•˜๋‹ค.

import React from "react";
import "./App.css";
import "./global-style.css";
import { dummyTweets } from "./static/dummyData";
// ! ์œ„ ์ฝ”๋“œ๋Š” ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

const Sidebar = () => {
  return (
    <section className="sidebar">
      <div>
        <i className="far fa-comment-dots" />
      </div>
    </section>
  );
};

const Counter = () => {
  return (
    <div className="tweetForm__input">
      <div className="tweetForm__inputWrapper">
        <div className="tweetForm__count" role="status">
          total: {dummyTweets.length}
        </div>
      </div>
    </div>
  );
};

const Footer = () => {
  return (
    <footer>
      <img id="logo" src={`${process.env.PUBLIC_URL}/codestates-logo.png`} />
      Copyright @ 2022 Code States
    </footer>
  );
};

const Tweets = () => {
  return (
    <ul className="tweets">
      {dummyTweets.map((tweet) => {
        const isParkHacker = tweet.username === "parkhacker";
        const tweetUserNameClass = isParkHacker
          ? "tweet__username tweet__username--purple"
          : "tweet__username";
        return (
          <li className="tweet" key={tweet.id}>
            <div className="tweet__profile">
              <img src={tweet.picture} />
            </div>
            <div className="tweet__content">
              <div className="tweet__userInfo">
                <span className={tweetUserNameClass}>{tweet.username}</span>
                {/* TODO : ์ด๋ฆ„์ด "parkhacker"์ธ ๊ฒฝ์šฐ, ์ด๋ฆ„ ๋ฐฐ๊ฒฝ์ƒ‰์„ rgb(235, 229, 249)์œผ๋กœ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค. */}
                <span className="tweet__createdAt">{tweet.createdAt}</span>
              </div>
              <span className="tweet__message">{tweet.content}</span>
            </div>
          </li>
        );
      })}
    </ul>
  );
};

const Features = () => {
  return (
    <section className="features">
      <div className="tweetForm__container">
        <div className="tweetForm__wrapper">
          <div className="tweetForm__profile"></div>
          <Counter />
        </div>
      </div>
      <Tweets />
      <Footer />
    </section>
  );
};

const App = () => {
  return (
    <div className="App">
      <main>
        <Sidebar />
        <Features dummyTweets={dummyTweets} />
      </main>
    </div>
  );
};

// ! ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
export { App, Sidebar, Counter, Tweets, Features, Footer };

๐Ÿ’ก Point.


map ๋ฉ”์„œ๋“œ์— ์˜ํ•ด li ํƒœ๊ทธ์— ์‚ฝ์ž…๋˜๊ณ  ์žˆ๋Š” ์—˜๋ฆฌ๋จผํŠธ,

์ฆ‰, tweet์˜ ํƒ€์ž…์€ ๋ฌด์—‡์ผ๊นŒ?

๋ฐ”๋กœ ๊ฐ์ฒด๋‹ค. ๊ฐ์ฒด์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด dot notation์„ ์ด์šฉํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด ํฌ์ธํŠธ์˜€๋‹ค.


์ž, ์ด์ œ ์ค€๋น„๋Š” ๋ชจ๋‘ ๋๋‚ฌ์œผ๋‹ˆ parkhacker๋ฅผ ์ฐพ์•„๋ณด์ž.
์šฐ์„  ๊ฐ๊ฐ์˜ tweet ์ค‘์—์„œ usename์ด parkhacker์ธ ๊ฒฝ์šฐ๋ฅผ ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค.

const isParkHacker = tweet.username === 'parkhacker'


๋‹ค์Œ์€ ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•˜์—ฌ tweet.username์ด
parkhacker์ธ ๊ฒฝ์šฐ์™€ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ๋Œ€ํ•ด className์„ ์ง€์ •ํ•ด ์ค€๋‹ค.

const tweetUserNameClass = isParkHacker
          ? 'tweet__username tweet__username--purple'
          : 'tweet__username';


๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ๊ฐ์˜ tweet์ด ๋ Œ๋”๋ง ๋˜๊ณ  ์žˆ๋Š” li tag ์•ˆ์— username์— ๋”ฐ๋ผ ๋ฐฐ๊ฒฝ์ƒ‰์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋„๋ก ์ ์šฉํ•ด ๋ณด์ž.

์ค‘๊ด„ํ˜ธ {}๋ฅผ ์ด์šฉํ•œ JSX ๋ฌธ๋ฒ•์€ ํƒœ๊ทธ์™€ ํƒœ๊ทธ ์‚ฌ์ด๋Š” ๋ฌผ๋ก 
className์ฒ˜๋Ÿผ ํƒœ๊ทธ ์•ˆ์— ๋“ค์–ด๊ฐ€๋Š” ์†์„ฑ์˜ ๊ฐ’์—๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<li className="tweet" key={tweet.id}>
  <span className={tweetUserNameClass}>{tweet.username}</span>
</li>
profile
์•„์ด๋””์–ด๊ฐ€ ๋„˜์น˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ฟˆ๊ฟ‰๋‹ˆ๋‹ค ๐Ÿ”ฅ

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