[코뮤니티 모각코] 웹 리액트 과정 - 2주차

54115 단어 React모각코React

2021-12-27 MON~2021-12-31 FRI의 기록 💻

📌 6일차 데이터 준비

✅ 오늘의 문제 | 데이터 구조 파악하기

⭐ 정답 예시 보충 <id, channelId, channelUrl의 사용 방법>

https://www.youtube.com/watch?v=id
https://www.youtube.com/channel/channelId
https://www.youtube.com/c/channelUrl

  • for문을 활용하여 youtubeData['data']의 모든 배열 요소에 대한 각각의 열두가지 정보를 conole.log를 활용하여 출력

🔽 src/App.js

import './App.css';
import youtubeData from './data/youtubeData.json';

function App() {
  for (var i=0; i<youtubeData['data'].length; i++) {
    console.log("id :\n", youtubeData['data'][i]['id'], "-> 영상의 링크 주소");
    console.log("channelId :\n", youtubeData['data'][i]['channelId'], "-> 채널의 링크 주소");
    console.log("date :\n", youtubeData['data'][i]['date'], "-> 영상의 업로드 일시");  
    console.log("title :\n", youtubeData['data'][i]['title'], "-> 영상의 제목");
    console.log("thumbnail :\n", youtubeData['data'][i]['thumbnail'], "-> 영상의 썸네일");
    console.log("description :\n", youtubeData['data'][i]['description'], "-> 영상의 설명");
    console.log("channelTitle :\n", youtubeData['data'][i]['channelTitle'], "-> 채널의 이름");
    console.log("category :\n", youtubeData['data'][i]['category'], "-> 영상의 종류");
    console.log("viewCount :\n", youtubeData['data'][i]['viewCount'], "-> 영상의 조회 수");
    console.log("likeCount :\n", youtubeData['data'][i]['likeCount'], "-> 영상의 좋아요 수");
    console.log("channelUrl :\n", youtubeData['data'][i]['channelUrl'], "-> 채널의 링크 주소");
    console.log("channelThumbnail :\n", youtubeData['data'][i]['channelThumbnail'], "-> 채널의 썸네일");
  }
  
  return <div>리액트로 데이터 불러오기</div>;
}

export default App;   

🔽 Console 출력결과 (일부)

📌 7일차 레이아웃 구성

✅ 오늘의 문제 | 조건부 렌더링 ① - 버튼 클릭 이벤트

⭐ 핵심 <조건부 렌더링>

return (
    <div>
      {true && <div>True일 때 출력되는 메세지</div>}
      {false && <div>False일 때 출력되는 메세지</div>}
    </div>
  );
  • src/Layout.js 와 src/Button.js 는 5일차 코드와 동일한 코드 활용
  • src/App.js 에 isTrue 변수와 setIsTrue 함수를 useState를 활용하여 선언
    • isTrue 변수 값을 true일 경우 false로, false일 경우 true로 바꿔주는 onClick 함수 생성하고 Button 컴포넌트에 연동
    • Message 컴포넌트에 props로 isTrue 값을 넘겨줌
  • src/Message.js 에서는 props로 넘겨받은 isTrue 값을 검토하여 true일 경우와 false일 경우에 따라 조건부 렌더링

🔽 src/App.js

import React, { useState } from 'react';
import Layout from './Layout';
import Button from './Button';
import Message from './Assignment07/Message';

function App() {
  const [isTrue, setIsTrue] = useState(true);

  function onClick() {
    setIsTrue(!isTrue);
  }

  return (
    <Layout>
      <Button onClick={onClick} text="True?" />
      <Message isTrue={isTrue}/>
    </Layout>
  );
}

export default App;

🔽 src/Assignment07/Message.js

function Message(props) {
    return (
        <div>
            {props.isTrue === true && <div>True!</div>}
            {props.isTrue === false && <div>False...</div>}
        </div>
    );
}

export default Message;

📌 8일차 아이콘과 헤더

✅ 오늘의 문제 | 조건부 렌더링 ② - 삼항연산자

⭐ CSS 관련 추가 공부

👉 Module CSS 활용 시 styles 객체 내의 값 조회 방법
  • 기본
    <div className={styles.클래스이름}></div>
  • 클래스 이름에 - 가 들어가 있는 경우
    <div className={styles['클래스-이름']}></div>
  • 클래스 여러 개를 적용하고 싶은 경우
    <div className={'${styles.첫번째} ${styles.두번째}'></div>
👉 box-sizing
  • content-box일 경우 테두리 미포함, border-box일 경우 테두리 포함하여 width 및 height 계산
👉 flex
  • 첫 번째 값은 flex-grow(기본값 0)
    • 0일 경우 flex-basis보다 커질 수 없음, 1일 경우 있음
    • 0 보다 큰 숫자로 지정 시 컨테이너의 크기가 늘어날 경우 지정된 숫자의 비율로 아이템의 크기를 증가시킴 (여백 부분을 채운다는 느낌)
  • 두 번째 값은 flex-shrink(기본값 1)
    • 0일 경우 flex-basis보다 작아질 수 없음, 1일 경우 있음
    • 0 보다 큰 숫자로 지정 시 컨테이너의 크기가 줄어들 경우 지정된 숫자의 비율로 아이템의 크기를 감소 시킴
  • 세 번째 값은 flex-basis(기본값 auto)
    • 아이템의 기본 크기 설정(flex-direction이 row일 경우 너비, column일 경우 높이 설정)

⭐ 정답 예시 보충

두 가지 이상의 클래스에 공통적으로 적용하고 싶은 CSS에 대해서
공통의 클래스이름을 하나 추가적으로 정의할 수도 있지만,
아래와 같이 콤마(,)로 적용할 수도 있음!

.blue,
.red {
  color: white;
  border: 1px solid black;
  width: 300px;
  height: 300px;
}
  • src/Assignment08/Day8.module.css 추가 및 src/Layout.module.css 수정
  • src/App.js에 삼항연산자를 활용하여 조건에 따라 div의 className을 다르게 지정

🔽 src/App.js

import React, { useState } from 'react';
import Layout from './Layout';
import Button from './Button';
import styles from './Assignment08/Day8.module.css';

function App() {
  const [isTrue, setIsTrue] = useState(true);

  function onClick() {
    setIsTrue(!isTrue);
  }

  return (
    <Layout>
      {isTrue === true ? <div className={`${styles.box} ${styles.blue}`}>True에용</div> : <div className={`${styles.box} ${styles.red}`}>False에용</div>}
      <Button onClick={onClick} text="색바꾸기!" />
    </Layout>
  );
}

export default App;

🔽 src/Assignment08/Day8.module.css

.box {
    color: white;
    width: 400px;
    height: 400px;
    margin-bottom: 10px;
    padding: 40px;
    box-sizing: border-box;
    font-size: 20px;
}

.blue {
    background-color: blue;
}

.red {
    background-color: red;
}

🔽 src/Layout.module.css

.container {
    width: 400px;
    margin: 20px auto;
    padding: 10px;
    border: 1px solid black;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
}

📌 9일차 SPA와 링크

✅ 오늘의 문제 | 새로운 페이지 생성

⭐ 핵심 <Routes, Route, Link>

  • Routes Route 컴포넌트를 묶어주는 컴포넌트
  • Route 링크를 생성하는 컴포넌트 (주소 값 변경 시 Route 컴포넌트 중 path 속성이 겹치는 컴포넌트를 렌더링)
  • Link 페이지 이동을 구현 (to 속성에 이동할 주소 값을 넣어줌)
  • 아래 첨부된 스크린샷과 같이 8일차 과제에 활용된 파일들을 모아 Assignment09 폴더 구성 (8일차의 App.js 는 Assignment.js 로 변경)
  • 그 외 부연 설명에 대해서 오늘부터는 코드에 주석으로 표시 🙌

🔽 src/App.js

import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import Explore from './pages/Explore';
import Subscription from './pages/Subscription';
import Assignment from './Assignment09/Assignment';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/explore" element={<Explore />} />
      <Route path="/subscription" element={<Subscription />} />
      <Route path="/assignment" element={<Assignment />} /> // Assignment 컴포넌트로의 링크를 생성하는 Route 컴포넌트 추가 
    </Routes>
  );
}

export default App;

🔽 src/components/shared/Menu.js

import { Link } from 'react-router-dom';
import styles from './Menu.module.css';

function Menu() {
    return (
        <div>
            <div>
                <Link to="/"></Link>
            </div>
            <div>
                <Link to="/explore">탐색</Link>
            </div>
            <div>
                <Link to="/subscription">구독</Link>
            </div>
            <div>
                <Link to="/assignment">과제</Link> // Assignment 페이지로 이동하는 Link 컴포넌트 추가
            </div>
        </div>
    );
}

export default Menu;

🔽 src/Assignment09/Assignment.js

import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import Layout from './Layout';
import Button from './Button';
import styles from './Day8.module.css';

function Assignment() {
  const [isTrue, setIsTrue] = useState(true);

  function onClick() {
    setIsTrue(!isTrue);
  }

  return (
    <div>
        <Layout>
            /* 8일차 과제의 정답 예시를 보고 보다 간결하게 삼항연산자를 수정해본 부분*/
            <div className={isTrue ? `${styles.box} ${styles.blue}` : `${styles.box} ${styles.red}`}>{isTrue ? "True에용" : "False에용"}</div>
            /***********************************************************************/
            <Button onClick={onClick} text="색바꾸기!" />
            <div>
                <Link to="/">홈으로 돌아가기</Link> // Home 페이지로 이동할 수 있는 Link 컴포넌트 추가
            </div>
        </Layout>
        
    </div>
  );
}

export default Assignment;

📌 10일차 메뉴 구현

✅ 오늘의 과제 | 메뉴 없애기 ①

  • props로 변수 뿐만 아니라 함수도 넘길 수 있음을 생각해내야 하는 것이 포인트!
  • 그 외 부연 설명은 코드에 주석으로 표시 🙌

🔽 src/components/shared/Layout.js

import { useState } from 'react';
import styles from './Layout.module.css';
import Header from './Header';
import Menu from './Menu';

function Layout({ children, activeMenu }) {
    /* useState를 활용하여 Menu의 visibility를 관리하는 변수 및 함수 선언 */  
    const [isMenuOn, setIsMenuOn] = useState(true);

    function menuOnOff() {
        setIsMenuOn(!isMenuOn);
    }
    /*********************************************************************/

    return (
        <div className={styles.container}>
            <Header menuOnOff={menuOnOff}/> // Header 컴포넌트에 menuOnOff 함수를 props로 넘겨줌
            <div className={styles.layout}>
                {isMenuOn ? <Menu activeMenu={activeMenu}/> : null} // isMenuOn 변수의 값에 따라 Menu 컴포넌트의 표시 여부 다르게 조건부 렌더링
                <div className={styles.contents}>{children}</div>
            </div>
        </div>
    );
}

export default Layout;

🔽 src/components/shared/Header.js

import styles from './Header.module.css';
import youtube_logo from '../../data/youtube_logo.png';
import { FiMenu } from 'react-icons/fi';
import { IoSearchOutline } from 'react-icons/io5';
import { BsGrid3X3Gap } from 'react-icons/bs';
import { HiOutlineDotsVertical } from 'react-icons/hi';

function Header( {menuOnOff} ) { // props로 menuOnOff 함수 넘겨받음
    return (
        <div className={styles.header}>
            <div className={styles.tab}>
                <FiMenu className={styles.icon} onClick={menuOnOff}/> // FiMenu 아이콘 클릭 시 menuOnOff 함수 실행되도록 연동
                <img src={youtube_logo} alt="로고" className={styles.logo} />
            </div>
            <div className={styles['center-tab']}>
                <input className={styles.input} />
                <IoSearchOutline className={styles['search-icon']} />
            </div>
            <div className={styles.tab}>
                <BsGrid3X3Gap className={styles.icon} />
                <HiOutlineDotsVertical className={styles.icon} />
            </div>
        </div>
    );
}

export default Header;

좋은 웹페이지 즐겨찾기