노마드코더 ReactJS로 영화 웹 서비스 만들기 #7

릿·2022년 3월 10일
0

Pratice Movie App

#7.0 To Do List part One

간단한 to-do-list를 만들어 보자!

  1. toDo를 작성할 input태그를 만든다.
  2. input값을 컨트롤 하기 위해서 useState()를 쓰고, value를 받아오는 값, 그걸 수정하는 modified함수를 작성한다.
  3. onChange이벤트를 만들고, modified함수로 input태그에 작성된 값을 받아오는 event.target.value를 받아오게 한다.
  4. input태그를 form태그로 감싸고 button태그를 만든다.
  5. onSubmit이벤트를 만들고, button을 클릭했을 때 setToDO()를 사용해 내용이 지워지도록 하고, 새로고침이 되는 걸 막기 위해 preventDefault()를 쓰고, 값이 비었을 때는 작동하지 않게 if문을 적는다. (onSubmit이벤트는 form에 걸것!)
function App() {
  const [toDo, setToDo] = useState("");
  const onChange = (event) => setToDo(event.target.value);
  const onSubmit = (event) => {
    event.preventDefault();
    if (toDo === "") {
      return;
    }
    setToDo("");
  }
  return (
    <div>
      <form onSubmit={onSubmit}>
        <input 
          value={toDo}
          type="text"
          placeholder="Write your to do..."
          onChange={onChange}
        />
        <button>Add To Do</button>
      </form>
    </div>
  );
}

우리가 해야할 건 여러 개의 toDo를 받을 수 있는 array이기 때문에 useState 기본값이 빈 배열인 것을 선언해준다.
일반 자바스크립트라면 배열에 push를 하겠지만 state를 직접적으로 수정하면 안되기에 배열을 직접적으로 수정하지 않으면서 setToDos 함수로 배열에 요소를 추가하는 방법을 사용할 것이다.
1. setToDos 함수의 첫번째 인자로 현재의 state를 받아온다.
2. return값으로는 새로운 배열에는 state에 있는 toDo와 모든 이전의 toDos을 적는다.

기존 배열과 새로운 요소를 추가하려면

var food = [1, 2, 3, 4];
[6, food]

이런 식으로 가져오면 [6, Array(4)], 즉 요소 1개와 배열 1개를 가진 배열을 반환받게 된다.
따라서 배열의 요소를 가져오려면

[6, ...food]

-와 같이 사용해야한다. 현재 하고 있는 예제라면

setToDos(currentArray => [toDo, ...currentArray]);

-와 같이 사용해야 배열의 값을 가져올 수 있다.
그럼 toDos배열에 입력한 input값이 들어있는 걸 확인할 수 있다.

h1태그로 페이지 타이틀을 적어주고, 그 옆에 현재 toDo가 몇 개인지 숫자로 나타내보자.

<h1>My To Dos {toDos.length}</h1>

#7.1 To Do List part Two

useState의 modified함수를 사용할 때 두가지 옵션이 있다는 걸 기억하자.
1. setToDo("");
우리가 저장한 data를 보낼 수 있다. 따라서 위와 같은 코드를 작성하면 toDo가 빈 String을 반환할 것이다.
2. setToDo((currentArray) => [toDo, ...currentArray]);
함수를 보낼 때는 함수의 첫번째 매개변수를 현재 state로 보낸다. 그러면 현재 state를 계산하거나 새로운 state를 만드는 데 사용할 수 있게 된다. 위와 같은 코드를 작성하면 현재 toDos를 받아와서 새로운 toDo의 배열로 반환하게 된다.

그렇다면 우리가 저장한 toDos배열을 각각의 컴포넌트로 만들기 위해선 어떻게 해야할까?

map함수를 사용하자!

map함수란?

배열의 각각의 요소를 바꾸고 싶고, 새로운 배열을 반환받을 때 사용한다.
map함수 괄호안에 함수를 사용할 수 있는데 만약에 배열 길이가 6개라면 총 6번 실행된다.

['there', 'are', 'you', 'are', 'how', 'hello'].map(() => ":)")
> [':)', ':)', ':)', ':)', ':)']

다만, 이런 식으로 사용하게 되면 기존의 배열값이 사라지게 된다. 하지만 함수의 매개변수를 사용하여 현재의 값을 가져올 수 있다.

['there', 'are', 'you', 'are', 'how', 'hello'].map((item) => item.toUpperCase())
> ['THERE', 'ARE', 'YOU', 'ARE', 'HOW', 'HELLO']

map함수를 이용해서 toDos배열을 li태그에 뿌려주자!

<ul>
	{toDos.map((item) => <li>{item}</li>)}
</ul>

이때, 콘솔에 같은 컴포넌트의 li를 읽어올때는 key라고 하는 prop을 넣어야 한다는 경고문이 떠있을 것이다.
따라서 해당 li에 key라는 속성값을 넣어주면 되는데 이 key는 고유의 값이어야 한다. map의 두번째 매개변수에 index를 넣어주고, key값으로 index를 넣어주자.

<ul>
	{toDos.map((item, index) => <li key={index}>{item}</li>)}
</ul>

#7.2 Coin Tracker

암호화폐 가격을 나열하는 트래커 만들기!

  1. 로딩을 위한 state와 코인리스트를 임시저장하기 위한 state를 작성한다.
  2. 코인 가격을 불러오기 위한 api(https://api.coinpaprika.com/v1/tickers)를 함수에 넣고, 컴포넌트가 가장 처음 랜더되었을 때 이 함수를 즉시 실행시키기 위해 useEffect를 아래와 같이 입력한다. 한번만 실행할 것이므로 배열 쪽은 비워둔다.
    (useState는 끝에 배열부분이 빈 배열이면 한 번만 작동한다.)
    개발자 도구->network란에 가면 해당 api가 제대로 작동하는 걸 알 수 있다.
  3. 받아온 데이터를 컴포넌트에 출력하기 위해 useState배열을 사용한다.
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
useEffect(() => {
	fetch("https://api.coinpaprika.com/v1/tickers")
    .then((response) => response.json())
    .then((json) => {
    	setCoins(json);
        setLoading(false);
    });
}, [])
  1. map함수를 사용하여 li태그를 사용하여 컴포넌트에 출력해준다. 매개변수는 하나로 충분하다. 받아오는 api에서 id값을 key값으로 받아오면 되기 때문!
    li태그 안에는 코인명, 코인심볼, USD가격을 받아온다.
<ul>
	{coins.map((coin) =>
    	<li>
        	{coin.name} ({coin.symbol}): ${coin.quotes.USD.price} USD
        </li>
    )}
</ul>
  1. 코인 갯수를 가져오려면 .length를 사용한다. 로딩되는 동안에만 Loading이라고 표시되도록 삼항연산자로 써준다.
<h1>The Coins! ({coins.length})</h1>
{loading ? <strong>Loading...</strong> : null}

#7.3 Movie App part One

영화정보를 보여주는 앱 만들기.

  1. 평점이 8.5이상인 최신순으로 정렬한 영화 정보들을 받아올 수 있는 api(https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year)를 가져온다.
  2. loading을 표시하는 useState를 작성하고, useEffect를 써서 api값을 받아온다.
  3. 영화정보를 가져오는 useState배열을 작성하고, 로딩이 끝나면 loading값을 false로 바꾼다.
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
useEffect(() => {
  fetch(`https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year`)
  .then((response) => response.json())
  .then((json) => {
    setMovies(json.data.movies);
    setLoading(false);
  });
}, []);

요즘은 then보다 async-await를 좀 더 보편적으로 사용하기 때문에 아래와 같이 코드를 고쳐보자.

const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async () => {
  const response = await fetch(
    `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year`
  );
  const json = await response.json();
  setMovies(json.data.movies);
  setLoading(false);
}
useEffect(() => {
  getMovies();
}, []);

그리고 아래 코드가 더 짧은 버젼이다.

const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async () => {
  const json = await (
    await fetch(
    `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year`
    )
  ).json();
  setMovies(json.data.movies);
  setLoading(false);
}
useEffect(() => {
  getMovies();
}, []);
  1. 최상위 div태그 아래 loading을 출력하는 삼항연산자를 쓰고, true일때는 loading이라는 글자를, false일때는 map함수를 사용하여 컴포넌트에 movie정보들을 출력해준다.
    장르의 경우, 리스트로 되어있기 때문에 한번 더 map함수를 써서 가져온다.
return (
  <div>
    {loading ? <h1>Loading...</h1> : <div>{movies.map(movie => 
        <div key={movie.id}>
          <img src={movie.medium_cover_image}></img>
          <h2>{movie.title}</h2>
          <p>{movie.summary}</p>
          <ul>
            {movie.genres.map((g) => (
              <li key={g}>{g}</li>
            ))}
          </ul>
        </div>
      )}</div>}
  </div>
);

결론 : state로부터 받은 데이터를 보여준다. -> state는 데이터를 API로부터 받아온다.

로딩이 되었을 때 setLoading을 false로 바꿔준다. -> setMovies로 moives를 받아오면 API로부터 얻은 데이터를 state로 변경한다. -> return문 안에서 movies.map을 하고 각각의 movie에 접근해서 그 값을 변환할 수 있다.

#7.4 Movie App part Two

react로 페이지 전환 추가하기!

  1. App컴포넌트 코드가 너무 길어지므로 Movie컴포넌트를 만들어서 return값에 있는 영화정보 받아오는 코드를 붙여넣어준다.
    컴포넌트의 속성값은 임의로 이름을 지어도 상관없지만, api에서 가져오는 값들은 정확한 이름을 기재해야 한다.
// Movie.js
function Movie({coverImg, title, summary, genres}) {
  return <div>
  <img src={coverImg} alt={title} />
  <h2>{title}</h2>
  <p>{summary}</p>
  <ul>
    {genres.map((g) => (
      <li key={g}>{g}</li>
    ))}
  </ul>
  </div>
}

//App.js
return (
  <div>
    {loading ? (
      <h1>Loading...</h1>
     ) : (
      <div>
        {movies.map((movie) => (
          <Movie 
            key={movie.id}
            coverImg={movie.medium_cover_image}
            title={movie.title}  
            summary={movie.summary}
            genres={movie.genres}
          />
        ))}
      </div>
    )}
  </div>
);

key는 react에서만, map안에서 컴포넌트를 랜더할 때 사용한다.

  1. 우리가 어떤 props를 가지고 있는 지 알고 싶다면 propTypes를 사용하면 된다.
    Movie컴포넌트의 propTypes를 설정해보자.
Movie.propTypes = {
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
  1. React Router를 사용하여 페이지를 전환해보자. 터미널 창에 npm install react-router-dom를 입력한다.
    작성한 코드를 바꾸거나 이동시켜야 하는데 이제는 스크린(=페이지, route) 단위로 생각해야 한다.
    home route에서는 모든 영화를 보여줄 것이고, movie route에서는 영화 하나만 보여줄 것이다.
  2. src폴더 안에 components폴더와 routes폴더를 생성한 후에, components폴더 안에 Movie.js파일을 옮기고, routes폴더 안에 Home.js파일을 생성한다.
  3. App컴포넌트에 있던 코드들을 Home컴포넌트로 이동시킨다.
  4. routes폴더 안에 Detail.js파일을 생성한다.
function Detail(){
  return <h1>Detail</h1>;
}

#7.5 React Router

react-router-dom 사용법 알아보기!

  1. react-router-dom을 사용하기 위해서는 아래의 import코드를 App컴포넌트에 붙여넣어준다.
import {
  BrowserRouter as Router, // router는 Hash Router와 Browser Router가 있다.
  Routes, // Routes이 하는 일은 route를 찾는 것! 여기서 route는 url(localhost:3000/movies/123)을 말한다. 그리고 route를 찾으면 컴포넌트를 렌더링한다.
  Route,
} from "react-router-dom";

function App() {
  return <Router>
    <Routes>
      <Route path="/movie" element={<Detail />}>
      </Route>
      <Route path="/" element={<Home/>}>
      </Route>
    </Routers>
  </Router>
  ;
}
  1. Route태그 안에 Route에 path="/"라는 속성을 써주면 home으로 간다는 의미이다. 그리고 element속성을 사용하여 Home컴포넌트을 써준다.
  2. 해당 Route태그 위에 새로운 Route태그를 생성하고, Route에 path="/movie"속성과 element={}을 써준다.
    (메인url 뒤에 /movie라고 입력하면 Detail컴포넌트를 실행해준다.)

Hash Router와 Browser Router

Hash Router를 쓰면 url뒤에 #이 붙는다.
대부분은 Browser Router을 사용한다. url뒤에 #가 붙지 않으므로.
ex) localhost:3000/#/movie

profile
항상 재밌는 뭔가를 찾고 있는 프론트엔드 개발자

0개의 댓글