(React API 프로젝트) Router & HeaderBar & NavBar

IRISH·2024년 5월 30일

ReactJS-API-Project

목록 보기
8/8
post-thumbnail

학습 목적

  • Header와 Navigation Bar를 적용한다.
  • Navigation Bar의 Menu 목록들을 통해 Router 기능을 이용한다.
  • Header와 Navigation Bar를 고정한다.
  • 전체적인 화면이 화면의 축소/확대 여부와 상관없이 항상 화면 가운데를 유지하게 한다.

코드

src/componets/Header.css

h1 {
  text-align: center;
}

.header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100px;
  background-color: #fff;
  z-index: 1000; /* 다른 요소들 위에 표시되도록 z-index 설정 */
  padding: 0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 약간의 그림자 추가 */
  text-align: center;
}
  • position은 고정 / 세로 길이인 height는 100px

src/components/Header.js

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

function Header() {
  return (
    <div className="header">
      <h1>IrishNoah Bit-Coin Information</h1>
    </div>
  );
}

export default Header;

src/components/Nav.css

.nav {
  position: fixed;
  top: 100px; /* Header 아래에 위치하도록 설정 */
  left: 0;
  width: 100%;
  background-color: #000;
  z-index: 999; /* Header 아래에 표시되도록 z-index 설정 */
  padding: 0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 약간의 그림자 추가 */
  text-align: center;
  align-items: center; /* 수직 중앙 정렬 */
  justify-content: center; /* 수평 중앙 정렬 */
}

.navbar {
  width: 1500px; /* 너비를 960px로 고정 */
  max-width: 100%; /* 최대 너비를 100%로 설정하여 작은 화면에서도 잘 보이도록 함 */
  margin: 0 auto; /* 상하 마진 0, 좌우 마진 자동 조정으로 중앙 정렬 */
  background-color: black;
  padding: 15px 0px;
  text-align: center;
  font-size: x-large;
}

@media (max-width: 768px) {
  .navbar {
    width: 100%; /* 작은 화면에서는 너비를 100%로 설정 */
    padding: 10px 0px; /* 패딩을 줄여서 공간을 절약 */
  }
}

.navbarMenu {
  color: white;
  margin: 30px;
  text-decoration: none;
}

.navbarMenu:hover {
  color: orange; /* 마우스 오버 시 글자 색상을 파란색으로 변경 */
}
  • position은 고정 / Header로 인해 Navigation Bar의 시작 위치 top은 100px에서 시작
  • 마우스가 네비게이션 바의 특정 메뉴에 있을 때 orange 색 / 아닐 때는 흰 색

src/components/Nav.js

import { Link } from "react-router-dom";
import React from "react";
import "./Nav.css";

function Nav() {
  return (
    <div className="nav">
      <div className="navbar">
        <Link className="navbarMenu" to={"/"}>
          Main
        </Link>
        <Link className="navbarMenu" to={"/posting"}>
          Posting
        </Link>
        <Link className="navbarMenu" to={"/contact"}>
          Contact
        </Link>
      </div>
    </div>
  );
}

export default Nav;
  • 네비게이션 바에 총 3개의 메뉴를 등록했고, 이 중 Main은 HomeCoin과 동일하게 구성

src/components/Posting.js

import React from "react";

function Posting() {
  return (
    <div>
      <h3>This is Posting Page!</h3>
    </div>
  );
}

export default Posting;

src/components/Contact.js

import React from "react";

function Contact() {
  return (
    <div>
      <h3>This is Contact Page!</h3>
    </div>
  );
}

export default Contact;

src/components/HomeCoinInfo.js

import PropTypes from "prop-types";
import { Link } from "react-router-dom";

function HomeCoinInfo({ rank, id, name, symbol, first_data_at }) {
  return (
    <div>
      <h2>
        <Link to={`/DetailCoin/${id}`}>{id}</Link>
      </h2>
      <ul>
        <li>rank : {rank}</li>
        <li>name : {name}</li>
        <li>symbol : {symbol}</li>
        <li>first_data_at : {first_data_at}</li>
      </ul>
    </div>
  );
}

HomeCoinInfo.propTypes = {
  rank: PropTypes.string.isRequired,
  id: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  symbol: PropTypes.string.isRequired,
  first_data_at: PropTypes.string.isRequired,
};

export default HomeCoinInfo;
  • 저번 실습과 달라진 것이 없음

src/components/Layout.module.css

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh; /* 화면 전체 높이를 차지하도록 설정 */
  padding: 150px 20px 50px; /* 고정된 Header와 Nav의 높이를 고려하여 padding-top을 설정 */
  box-sizing: border-box;
}
  • Posting 이나 Contact 등의 화면의 화면 크기와 상관 없이 화면 가운데 나오게 하기 위한 css

src/components/Layout.js

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

const Layout = ({ children }) => {
  return <div className={styles.container}>{children}</div>;
};

export default Layout;

src/routes/DetailCoin.js

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

function DetailCoin() {
  const { id } = useParams(); // URL에서 id 파라미터를 추출합니다.
  const [coin, setCoin] = useState(null);

  useEffect(() => {
    fetch(`https://api.coinpaprika.com/v1/tickers/${id}`) // 코인 정보를 가져오는 API (예시 URL)
      .then((response) => response.json())
      .then((data) => {
        setCoin(data);
        console.log(data);
      })
      .catch((error) => console.error("Error fetching coin data:", error));
  }, [id]); // id가 변경될 때마다 이 effect를 다시 실행합니다.

  if (!coin) {
    return <p>Loading...</p>;
  }

  const quotes = coin.quotes.USD;

  return (
    <div>
      <h1>
        {coin.name} ({coin.id})
      </h1>
      <ul>
        <li>Rank: {coin.rank}</li>
        <li>symbol: {coin.symbol}</li>
        <li>total_supply: {coin.total_supply}</li>
        <li>first_data_at: {coin.first_data_at}</li>
        <li>last_updated: {coin.last_updated}</li>
        {Object.keys(quotes).map((key) => (
          <li key={key}>
            {key}: {quotes[key]}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default DetailCoin;
  • 저번 실습과 동일

src/routes/HomeCoin.module.css

.container {
  width: 1500px; /* 너비를 960px로 고정 */
  max-width: 100%; /* 최대 너비를 100%로 설정하여 작은 화면에서도 잘 보이도록 함 */
  margin: 0 auto; /* 상하 마진 0, 좌우 마진 자동 조정으로 중앙 정렬 */
  padding: 20px; /* 내부 여백 추가 */
}

.coinsMainText {
  text-align: center; /* 텍스트 중앙 정렬 */
}

.coins {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 한 행당 3개의 열 */
  gap: 100px; /* 그리드 항목 간의 간격 */
  justify-content: center; /* 중앙 정렬 */
  padding: 150px;
  width: 100%; /* 부모 컨테이너의 너비에 맞춤 */
  padding-top: 20px;
}

.coins > div {
  width: 400px; /* 각 코인 항목의 너비를 고정 */
  height: 250px; /* 각 코인 항목의 높이를 고정 */
  border: 1px solid #ccc; /* 각 코인 항목에 경계선 추가 */
  padding: 10px; /* 경계선과 내용 사이에 여백 추가 */
  border-radius: 8px; /* 경계선의 모서리를 둥글게 만듦 */
  background-color: #fff; /* 배경색을 흰색으로 설정 */
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 약간의 그림자 추가 */
}

@media screen and (max-width: 1090px) {
  .coins {
    grid-template-columns: 1fr;
    width: 100%;
  }
}

.eachCoin:hover {
  background-color: green;
  transform: scale(1.1); /* 5%만큼 크기를 증가시킴 */
}
  • 저번 실습과 동일

src/routes/HomeCoin.js

import React, { useState, useEffect } from "react";
import styles from "./HomeCoin.module.css";
import HomeCoinInfo from "../components/HomeCoinInfo";

function HomeCoin() {
  const [coin, setCoin] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchCoinData() {
      try {
        const response = await fetch("https://api.coinpaprika.com/v1/tickers");
        const coins = await response.json();

        setCoin(coins); // >>> 모두 다 불러옴
      } catch (error) {
        setError(error);
        console.error("Error fetching coin data:", error);
      }
    }

    fetchCoinData();
  }, []);

  if (error) {
    return <div>Error fetching coin data: {error.message}</div>;
  }

  if (!coin) {
    return <div>Loading...</div>;
  }

  return (
    <div className={styles.container}>
      <div className={styles.coins}>
        {coin.map((data) => (
          <div className={styles.eachCoin}>
            <div>
              <HomeCoinInfo
                rank={data.rank}
                id={data.id}
                name={data.name}
                symbol={data.symbol}
                first_data_at={data.first_data_at}
              />
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

export default HomeCoin;
  • 저번 실습과 동일

src/App.js

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Header from "./components/Header";
import Nav from "./components/Nav";
import DetailCoin from "./routes/DetailCoin";
import HomeCoin from "./routes/HomeCoin";
import Main from "./routes/HomeCoin";
import Posting from "./components/Posting.js";
import Contact from "./components/Contact.js";
import Layout from "./components/Layout";

function App() {
  return (
    <Router>
      <Header />
      <Nav />

      <Layout>
        <Switch>
          <Route path="/hello">
            <h1>Hello</h1>
          </Route>
          <Route path="/DetailCoin/:id">
            <DetailCoin />
          </Route>
          <Route exact path="/">
            <HomeCoin />
          </Route>
          <Route exact path="/">
            <Main />
          </Route>
          <Route path="/posting">
            <Posting />
          </Route>
          <Route path="/contact">
            <Contact />
          </Route>
        </Switch>
      </Layout>
    </Router>
  );
}

export default App;
  • App에 import 할 파일 넣어주기
  • && - 각 css 에서 이미 고정했기 때문에 별도로 뺀다.
  • 나머지
    • 을 적용해 가운데 적용을 하도록 한다.

src/styles.css

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
  background-color: #eff3f7;
  height: 100%;
}
  • 저번 실습과 동일

src/index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./styles.css";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
  • 저번 실습과 동일

결과

⇒ 화면 비율 100%

⇒ 화면 비율 25%

⇒ Posting / Contact

profile
#Software Engineer #IRISH

0개의 댓글