2023.09.11~13(React JS필기 정리)

SaGo_MunGcci·2023년 9월 14일
0

ReactJS

목록 보기
7/9

Today do list

  • Nomad Coder - ReactJS로 영화 웹 서비스 만들기
  • coinAppChallenge
  • movieApp 만들기
    • route


TIL

CoinTrackerApp.js

import { useEffect, useState } from 'react'

function CoinTrackerApp() {
  const [loading, setLoading] = useState(true)
  const [coins, setCoins] = useState([])
  useEffect(() => {
    fetch('https://api.coinpaprika.com/v1/tickers?limit=100')
      //fetch('https://api.coinpaprika.com/v1/tickers')
      .then((response) => response.json())
      .then((json) => {
        console.log(json)
        setCoins(json)
        setLoading(false)
      })
  }, [])
  return (
    <div>
      <h1>The Coins! {loading ? '' : `(${coins.length})`}</h1>
      {loading ? <strong>Loding...</strong> : null}
      <ul>
        {coins.map((coins) => (
          <li key={coins.id}>
            {coins.name}({coins.symbol}) : ${coins.quotes.USD.price}
            <span> </span>
            Coin Rank : {coins.rank}
          </li>
        ))}
      </ul>
    </div>
  )
}

export default CoinTrackerApp

coninTrackerChallengeApp.js

import { useEffect, useState } from 'react'
import coinChallenge from './CoinChallenge.module.css'

function CoinTrackerChallengeApp() {
  const [loading, setLoading] = useState(true)
  const [coins, setCoins] = useState([])
  const [limit, setLimit] = useState(100) // `https://api.coinpaprika.com/v1/tickers?limit=${limit}&&quotes=KRW`
  useEffect(() => {
    // const interval = setInterval(() => {
    //   fetch(`https://api.coinpaprika.com/v1/tickers?limit=${limit}&&quotes=KRW`)
    //     .then((response) => response.json())
    //     .then((json) => {
    //       console.log(json)
    //       setCoins(json)
    //       setLoading(false)
    //       console.log('1')
    //     })
    // }, 1000)
    // return () => clearInterval(interval)
    fetch(`https://api.coinpaprika.com/v1/tickers?limit=${limit}&&quotes=KRW`)
      .then((response) => response.json())
      .then((json) => {
        console.log(json)
        setCoins(json)
        setLoading(false)
        console.log('1')
      })
  }, [limit])

  const onClick = () => {
    setLimit((current) => current + 100)
  }

  const onChange = () => {}

  return (
    <div>
      <div className={coinChallenge.title}>
        <h1>Cryptocurrency in real time!</h1>
      </div>
      <div className={coinChallenge.search}>
        <form>
          <input
            type="text"
            placeholder="search Cryptocurrency"
            onChange={onChange}
          ></input>
        </form>
        {loading ? <strong>Loding...</strong> : null}
      </div>
      <hr></hr>
      <div className={coinChallenge.tablecontainer}>
        <table>
          <thead>
            <tr>
              <th>순위</th>
              <th>종목</th>
              <th>기호</th>
              <th>가격(KRW)</th>
              <th>총시가</th>
              <th>거래량(24H)</th>
              <th>변동(24H)</th>
              <th>변동(7D)</th>
            </tr>
          </thead>
          <tbody>
            {coins.map((coins) => (
              <tr key={coins.id}>
                <td className={coinChallenge.rank}>{coins.rank}</td>
                <td className={coinChallenge.dataColorWhite}>
                  {coins.name.toUpperCase()}
                </td>
                <td className={coinChallenge.dataColorWhite}>{coins.symbol}</td>
                <td className={coinChallenge.dataColorWhite}>
                  {parseFloat(
                    coins.quotes.KRW.price.toFixed(3),
                  ).toLocaleString() + '원'}
                </td>
                <td className={coinChallenge.dataColorWhite}>
                  {parseFloat(
                    coins.quotes.KRW.ath_price.toFixed(3),
                  ).toLocaleString() + '원'}
                </td>
                <td className={coinChallenge.dataColorWhite}>
                  {parseFloat(
                    coins.quotes.KRW.volume_24h.toFixed(3),
                  ).toLocaleString() + '개'}
                </td>
                <td
                  className={
                    parseFloat(coins.quotes.KRW.percent_change_24h) < 0
                      ? coinChallenge.dataColorRed
                      : coinChallenge.dataColorBlue
                  }
                >
                  {parseFloat(
                    coins.quotes.KRW.percent_change_24h.toFixed(3),
                  ).toLocaleString()}
                </td>
                <td
                  className={
                    parseFloat(coins.quotes.KRW.percent_change_7d) < 0
                      ? coinChallenge.dataColorRed
                      : coinChallenge.dataColorBlue
                  }
                >
                  {parseFloat(
                    coins.quotes.KRW.percent_change_7d.toFixed(3),
                  ).toLocaleString()}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <div className={coinChallenge.moreBtndiv}>
        <button className={coinChallenge.moreBtn} onClick={onClick}>
          {loading ? <strong>Loding...</strong> : '더 보기'}
        </button>
      </div>
    </div>
  )
}

export default CoinTrackerChallengeApp

CoinChallenge.module.css

body {
  background-color: #333;
}

.title {
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0;
  font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}
.search {
  display: flex;
  justify-content: center;
  align-items: center;
}

.search select {
  width: 70px;
  margin-right: 20px;
}

.tablecontainer {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

/* @media (max-width: 330px) {
  .tablecontainer {
    flex-direction: column;
  }
} */

.tablecontainer table {
  border-collapse: collapse;
}

.tablecontainer table thead {
  color: wheat;
  border-bottom: 2px solid brown;
}

.tablecontainer table th {
  padding-bottom: 5px;
}

.tablecontainer table td {
  padding-top: 10px;
}

.moreBtndiv {
  display: flex;
  justify-content: center;
  align-items: center;
}
.moreBtn {
  background-color: transparent; /* 배경색을 투명하게 설정 */
  border: 2px solid brown; /* 테두리 스타일 및 색상 지정 */
  color: white;
  margin-top: 10px;
  padding: 15px;
  min-height: 30px;
  min-width: 600px;
}

.rank {
  color: chocolate;
}

.dataColorWhite {
  color: White;
}

.dataColorRed {
  color: red;
}
.dataColorBlue {
  color: green;
}
  • MovieApp.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import MovieHome from '../src/routes/MovieHome'
import Detail from './routes/Detail'

// react-router사용할 건데 터미널에 npm install react-router-dom을 입력한다.
// movieApp.js는 더이상 영화를 보여주지 않고 router를 render한다.
// 즉 이곳은 url을 바라보면서 해당 url주소에 따라서 MovieHome, Details 보여줄것이다.
function MovieApp() {
  //react-router-dom 5버전 -> 버전6 바뀐 부분
  // 1. Switch컴포넌트가 Routes컴포넌트로 대체되었습니다.
  // Switch -> Routes
  // 2. Route컴포넌트 사이에 자식 컴포넌트를 넣지 않고, element prop에 자식 컴포넌트를 할당하도록 바뀌었습니다.
  // <Routes>가 하는 일은 Route를 찾는데 Route는 url이다.
  // HashRouter는 주소 앞에 #를 붙여서 사용한다. 대부분 BrowserRouter를 사용하기 때문에 참고만 하자.

  // Movie 컴포넌트에서 타이틀을 클릭해서 movie페이지로 이동할때 a herf='' 로 이동하게 되면 우리 어플리케이션에 있는 모든 페이지가 리프레시 된다.
  // 딱 movie페이지만 렌더링, 리렌더링 되기를 원하는데, 이때 Link를 사용한다.  다시 Movie.js에서 보기

  //path="/movie/:id" :id라고 입력해야 페이지넘기면서 데이터를 전달할 수 있다.
  // 다시한번  path="/movie/:id" 경로 사이에는 항상 "/"이거 붙여야 한다.
  // :variable 즉 :뒤에가 변수이다.
  return (
    <Router>
      <Routes>
        <Route path="/movie/:id" element={<Detail />}></Route>
        <Route path="/" element={<MovieHome />}></Route>
      </Routes>
    </Router>
  )
}

export default MovieApp
  • component/Movie.js
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import '../MovieIntro.css' // 추가된 CSS 파일
// react-router사용할 건데 터미널에 npm install react-router-dom을 입력한다.
// Movie 컴포넌트에서 타이틀을 클릭해서 movie페이지로 이동할때 a herf='' 로 이동하게 되면 우리 어플리케이션에 있는 모든 페이지가 리프레시 된다.
// 딱 movie페이지만 렌더링, 리렌더링 되기를 원하는데, 이때 Link를 사용한다.
// MovieHome에서 id받아서 props에 넘겨주고 여기서 id값을 받아서 Link to로 연동한다.

// 여기서 <Link to={`/movie/${id}`}>{title}</Link> 작성을 해야,
// 부모 컴포넌트의 path="/movie:id" :id라고 입력한 부분이 페이지넘기면서 데이터를 전달할 수 있다.
function Movie({ id, medium_cover_image, title, summary, genres }) {
  // return (
  //   <div>
  //     <img src={medium_cover_image} alt={title}></img>
  //     <h2>
  //       <Link to={`/movie/${id}`}>{title}</Link>
  //     </h2>
  //     <p>{summary.slice(0, 300)}...</p>
  //     <ul>
  //       {genres.map((g) => (
  //         <li key={g}>{g}</li>
  //       ))}
  //     </ul>
  //   </div>
  // )
  return (
    <div className="movie-intro">
      <img className="movie-intro-image" src={medium_cover_image} alt={title} />
      <h2 className="movie-intro-title">
        <Link to={`/movie/${id}`}>{title}</Link>
      </h2>
      <p className="movie-intro-summary">{summary.slice(0, 300)}...</p>
      <ul className="movie-intro-genres">
        {genres.map((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  )
}
// propTypes 선언하는것 잘보자
// 항상 소문자
Movie.propTypes = {
  id: PropTypes.number.isRequired,
  medium_cover_image: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
}

export default Movie
  • routes/MovieHome.js
import { useEffect, useState } from 'react'
import Movie from '../components/Movie'
import '../MovieHome.css' // 추가된 CSS 파일

// react-router사용할 건데 터미널에 npm install react-router-dom을 입력한다.
function MovieHome() {
  const [loading, setLoading] = useState(true)
  const [movies, setMovies] = useState([])

  // async, await를 사용하자
  // https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year
  const getMovies = async () => {
    const response = await fetch(
      //`https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=rating&limit=20`,
      `https://yts.mx/api/v2/list_movies.json?sort_by=year&limit=21&page=2`,
    )
    const json = await response.json()
    setMovies((current) => json.data.movies)
    setLoading((current) => false)
  }

  useEffect(() => {
    getMovies()
  }, [])

  console.log(movies)

  return (
    <div>
      <h1>MovieApp</h1>
      <hr></hr>
      {loading ? (
        <h1>Loding...</h1>
      ) : (
        <div className="container">
          {movies.map((movie) => (
            <Movie
              key={movie.id}
              id={movie.id}
              medium_cover_image={movie.medium_cover_image}
              title={movie.title}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  )
}

export default MovieHome
  • component/Detail.js(미완성)
// MovieHome에서 <Link to={`/movie/${id}`}>{title}</Link> 작성을 했을때
// 부모 컴포넌트의 path="/movie:id" :id라고 입력한 부분이 Detail페이지넘기면서 데이터를 넘겨주는데
// Detailpage에서 어떻게 id부분만 받을 수 있나?
// 아래와 같이 useParams를 사용하자.

import { useEffect } from 'react'
import { useParams } from 'react-router-dom'

function Detail() {
  //  path="/movie/:id" 경로 사이에는 항상 "/"이거 붙여야 한다.
  // :variable 즉 :뒤에가 변수이다.
  //   const id = useParams()
  //   console.log(id.id)

  //이 형식의 코드는 현재 라우트의 URL 매개변수를 가져오는 데 사용된다.
  // URL이 "/movies/53528"일 때, useParams를 사용하여 id 값을 추출할 때 아래와 같은 형식의 코드로 가져올수있다.
  const { id } = useParams()
  console.log(id)
  // https://yts.mx/api/v2/movie_details.json?movie_id=

  const getMovieDetail = async () => {
    const response = await fetch(
      `https://yts.mx/api/v2/movie_details.json?movie_id=${id}`,
    )
    const json = response.json()

    console.log(json)
  }

  useEffect(() => {
    getMovieDetail()
  }, [])
  return <h1>Detail</h1>
}

export default Detail

Retrospection

  • 코드트래커 첼린지에서 많이 아쉬웠다.

  • 전체적인 디자인이나 셀렉트 태그로 원,달러,엔,위안화를 선택해서 사용자가 총 몇개를 가질수 있는지 표현 하고 싶었으나 잘 안되었다.

  • Naver 쇼핑 API를 사용해서 최저가를 표현하는 나만의 프로젝트를 실행했으나 알고 봤더니 클라이언트 자체에서는 네이버API의 내용을 가져올수가 없다(하루 허비함 아...........)

  • 그래서 스프링부트와 연동해서 실행해보니 어느정도 성과가 있었다.
    (코인앱첼린지와 마찬가지로 부족한 면이 많았다.)

  • 컴포넌트와 라우트를 사용해서 전반적인 리액트 앱이 어떻게 구동되는지 큰틀을 알게되었다.

  • 리액트 돔을 설치하는등 생각보다 리엑트에 좋은 라이브러리가 많았다.

    • import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
    • import { Link } from 'react-router-dom'
  • 디자인이 중요하다! 자유자재로 만들수 있어야 한다.

  • css또한 엄청 중요하다는 것을 알게 되었다.

  • 최종본 : https://github.com/pmjn1025/react_study



Tommorrow do list

  • 포트폴리오 템플릿 공부하기


profile
이리저리 생각만 많은 사고뭉치입니다.

0개의 댓글