[Nomad] ReactJS로 영화 웹 서비스 만들기 5

웹/React.js 2024. 1. 6. 16:00

노마드코더 강의 5일차.

심심해서 만들어보는 React JS 강의 수강

 - ReactJS로 영화 웹 서비스 만들기

  1. 준비물

     > react app 설치 (React JS, React-Dom, Babel)

     > propTypes : npm i prop-types 

git production 배포 설정
1. npm i gh-pages (결과물을 github pages에 업로드 할 수 있게 해주는 패키지)
2. npm run build (production을 위해 compressed 코드 생성
3. package.json 설정 추가 (deploy시 build 후 gh-pages -d build 하도록 설정)
   "deploy": "gh-pages -d build",    "predeploy": "npm run build"
   - build 폴더를 미리 설정한 git remote 에 배포한다는 의미
4. npm run deploy 시 npm run build -> gh-pages -d build 순으로 처리됨
5. package.json에 설정한 homepage 경로에서 배포소스확인
 - "homepage" : https://walterpark5193.github.io/react-for-beginners/
* git 이 없는 경우 https://git-scm.com/download 에서 git을 설치해야한다.
** vscode와 git을 연결하는 방법은 브라우저에 github 로그인해두고 터미널에서 git 실행 시 자동으로 브라우저와 연동되어 로그인된다.
*** 그 뒤 vscode에서 remote를 연결하면 된다. (이 때 remote는 github에서 미리 repository를 생성해두어야 연결 가능)

 

2. Movie App publishing 결과

 - https://walterpark5193.github.io/react-for-beginners/

 

[Nomad] ReactJS로 영화 웹 서비스 만들기 4

웹/React.js 2024. 1. 5. 17:42

노마드코더 강의 4일차.

심심해서 만들어보는 React JS 강의 수강

 - ReactJS로 영화 웹 서비스 만들기

  1. 준비물

     > react app 설치 (React JS, React-Dom, Babel)

     > propTypes : npm i prop-types 

npm 플러그인 설치
1. npm install react-router-dom (router 기능)
2. npm i gh-pages (결과물을 github pages에 업로드 할 수 있게 해주는 패키지)

 

 

* 연습

 1. Coin Tracker

import { useEffect, useState } from "react";

function App() {
  const [loading, setLoading] = useState(true);
  const [coins, setCoins] = useState([]);
  useEffect(() => {
    fetch("https://api.coinpaprika.com/v1/tickers?limit=5")
      .then((response) => response.json())
      .then((json) => {
        setCoins(json);
        console.log("json = ", json);
        setLoading(false);
      });
  }, []);
  return (
    <div>
      <h1>The Coins! {loading ? "" : `(${coins.length})`}</h1>
      {loading ? (
        <strong>loading.valueOf.apply.</strong>
      ) : (
        <select>
          {coins.map((coin, index) => (
            <option key={index} value={coin.quotes.USD.price}>
              {coin.name} ({coin.symbol}): ${coin.quotes.USD.price} USD
            </option>
          ))}
        </select>
      )}
    </div>
  );
}

export default App;

 

2. Movie App 만들기 (BrowserRouter, Routes, Route, useParams 사용)

 - App 내에 구현되어 있던 영화 Component -> Movie.js로 이동

 - Components, routes 폴더 생성

 2.1 App.js 

import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; //BrowserRouter = 일반적인 url 생김새, hashRouter = url에 #과 같은 형태가 붙음
import Home from "./routes/Home";
import Detail from "./routes/Detail";

function App() {
  //이제 App에서는 요청된 url에 따라 해당하는 component를 보여주는 역할을 수행 (=router)
  //한번에 1개의 Router만 렌더링하기 위해 Routes 컴포넌트를 사용함(기존에는 Switch였으나 react-router-dom 버전6에서 Switch->Routes로 변경됨)
  return (
    <Router>
      <Routes>
        <Route path="/hello" element={<h1>Hello</h1>}></Route>
        <Route path="/movie/:id" element={<Detail />}></Route>
        <Route path="/" element={<Home />}></Route>
      </Routes>
    </Router>
  );
}

export default App;

 

 2.2 Home.js 

import { useEffect, useState } from "react";
import Movie from "../components/Movie";

function Home() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovies = async () => {
    const responseJson =
      await //await 내부에 또다른 await 가 있음. awiat는 .then 과 동일한 의미
      (
        await fetch(
          `https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year`
        )
      ).json();
    setMovies(responseJson.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovies();
  }, []);
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <Movie
              key={movie.id}
              id={movie.id}
              coverImg={movie.medium_cover_image}
              title={movie.title}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  );
}

export default Home;

 

 2.3 Detail.js 

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

function Detail() {
  //Movie의 title 클릭 시 <a href="urlPath">로 상세 페이지로 이동하게 되면 페이지 전체가 다시 실행된다! (=Router의 Link를 사용하는 이유)
  const { id } = useParams();
  const getMovieDetail = async () => {
    const responseJson = await //await는 async 함수 내부에서만 사용이 가능하다.
    (
      await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
    ).json();
    // setMovies(responseJson.data.movies);
    // setLoading(false);
    console.log(responseJson);
  };
  useEffect(() => {
    getMovieDetail();
  }, []);
  return (
    <h1>
      [마지막 단계 : 코드 챌린지] - Home에서 해줬던 loading을 Detail에 해주기 -
      movie가 State에 없음. 현재 API에서 json을 받아와서 아무것도 안 하고 있는
      상태. -> 힌트: json을 state에 넣어보기
    </h1>
  );
}

export default Detail;

 

 2.4 Movie.js 

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

function Moive({ id, coverImg, title, summary, genres }) {
  return (
    <div>
      <div>
        <img src={coverImg} alt={title} />
        <h2>
          <Link to={`/movie/${id}`}>{title}</Link>
        </h2>
        <p>{summary}</p>
        {
          //hasOwnProperty("genres") ? ( //genres prop이 있을 때에만 아래 로직 수행 (genres가 없을 시 cannot read properties of undefined 에러 발생)
          genres !== "" ? (
            <ul>
              {genres.map((g) => (
                <li key={g}>{g}</li>
              ))}
            </ul>
          ) : null
        }
        {/*   <ul>
          {genres.map((g) => (
            <li key={g}>{g}</li>
          ))}
        </ul> */}
      </div>
    </div>
  );
}

Moive.propTypes = {
  id: PropTypes.number.isRequired,
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Moive;

 

* 챌린지 : 영화 상세 내용 표시 페이지를 개발해보자!! (강의 다 듣고...)

[Nomad] ReactJS로 영화 웹 서비스 만들기 3

웹/React.js 2024. 1. 4. 23:04

노마드코더 강의 3일차.

심심해서 만들어보는 React JS 강의 수강

 - ReactJS로 영화 웹 서비스 만들기

  1. 준비물

     > react app 설치 (React JS, React-Dom, Babel)

     > propTypes : npm i prop-types 

 

react app 설치 순서
1. node js 설치 (nodejs.org 에서 LTS 버전 설치)
2. node 버전 확인 (node -v)
3. react-app 설치 (npm install create-react-app)
4. 사용할 Application 폴더 구조 생성 (npx create-react-app react-for-beginners)
5. cd react-for-beginners
6. npm start
7. 로컬서버가 실행되고, http://localhost:3000/로 이동하여 React 로고가 나오면 설정 완료!
8. 특별히 지정하지 않으면 react-app, myApp의 경우 react-app을 설치한 폴더가 root로 설정이 된다
* vscode에서 terminal이 동작하지 않는다면 기본 설정이 Command Prompt로 되어있는지 확인하자
   - 설정방법 : 터미널 -> 터미널 우측 상단 ∨ 클릭 -> 기본 프로필 선택 -> Command Prompt > vs code 재시작

 

react-app 최초 설정 (index.js)
react-app 최초 설정 (App.js)

 

 

* 각 모듈별 css 설정

1. index.js

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

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

 

2. App.js

import Button from "./Button";
import styles from "./App.module.css";

function App() {
  return (
    <div>
      <h1 className={styles.tilte}>Welcome back!</h1>
      <Button text={"Continue"} />
    </div>
  );
}

export default App;

 

3. App.module.css

.tilte {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
  font-size: 18px;
}

 

4. Button.js

import PropTypes from "prop-types";
import styles from "./Button.module.css";

function Button({ text }) {
  //Button 모듈을 사용할 때만 css 로딩하고, 내가 원하는 Button에만 관련 Style을 적용할 수 있다.
  //여기서 정의한 className은 렌더링 시 무작위적인 랜덤 class name을 가지게 된다!!
  return <button className={styles.btn}>{text}</button>;
}

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

 

5. Button.module.css

.btn {
  color: white;
  background-color: tomato;
}

 

** 간단한 React app 구조 만들어보기 완료. 

*effect :  특정 component가 맨 처음 render 될 때만 실행하고, 다른 때는 동작하지 않도록 실행 제어하는 방법

1. App.js (effect 기초)

import Button from "./Button";
import styles from "./App.module.css";
import { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = useState(0);
  const [keyword, setKeyword] = useState("");
  const onClick = () => setValue((prev) => prev + 1);
  const onChange = (event) => setKeyword(event.target.value);
  useEffect(() => {
    console.log("i run only one"); //처음 render될 때만 실행되는 코드 (1회만 호출되는 로직)
  }, []); //watch대상이 없으므로 최초 1번만 실행됨
  useEffect(() => {
    console.log("i run when 'keyword' : ", keyword, " changes");
  }, [keyword]); //keyword가 변할 때만 useEffect 실행 (watch 대상)
  useEffect(() => {
    console.log("i run when 'counter' : ", counter, " changes");
  }, [counter]);
  useEffect(() => {
    console.log("i run when keyword & counter changes");
  }, [keyword, counter]); //한개 이상의 변수에 대해서 watch 설정이 가능하다
  return (
    <div>
      <input
        value={keyword}
        onChange={onChange}
        type="text"
        placeholder="Search here..."
      />
      <h1>{counter}</h1>
      <button onClick={onClick}>Click me</button>
    </div>
  );
}

export default App;

 

2. App.js (effect, destroy 사용)

import { useState, useEffect } from "react";

function Hello() {
  //useEffect 사용 Case1 : useEffect에서 호출하는 로직을 각각 분리해서 선언
  function byeFn() {
    console.log("1 bye :(");
  }
  function hiFn() {
    console.log("1 hi :)");
    return byeFn; //component가 destoryed 될 때 실행하고 싶은 function을 리턴
  }
  useEffect(hiFn, []);

  //**useEffect 사용 Case2 : useEffect에서 () => 사용해서 선언 (제일 자주 사용하는 표현식)
  useEffect(() => {
    console.log("2 hi :)");
    return () => console.log("2 bye :(");
  }, []);

  //useEffect 사용 Case3 : useEffect에서 호출하는 로직을 function으로 선언해서 사용
  useEffect(function () {
    console.log("3 hi :)");
    return function () {
      console.log("3 bye :(");
    };
  }, []);
  return <h1>Hello</h1>;
}

function App() {
  const [showing, setShowing] = useState(false);
  const onClick = () => setShowing((prev) => !prev);
  return (
    <div>
      {showing ? <Hello /> : null}
      <button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
    </div>
  );
}

export default App;

 

3. todoList

import { useState } from "react";

function App() {
  const [toDo, setTodo] = useState("");
  const [toDos, setToDos] = useState([]);
  const onChange = (event) => setTodo(event.target.value);
  const onSubmit = (event) => {
    event.preventDefault();
    if (toDo === "") {
      return;
    }
    setToDos((currentArray) => [toDo, ...currentArray]); //...의 역할은 해당 Array의 elements를 리턴한다 (Array 형태가 아닌 enum 나열로 리턴됨)
    setTodo(""); //값 초기화 방법
  };
  return (
    <div>
      <h1>My To Dos {toDos.length}</h1>
      <form onSubmit={onSubmit}>
        <input
          onChange={onChange}
          value={toDo}
          type="text"
          placeholder="Write your todo..."
        ></input>
        <button>Add To Do</button>
      </form>
      <hr />
      <ul>
        {toDos.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;