[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 - Movie App

강경서·2023년 6월 27일
0
post-thumbnail

🎬 React를 이용한 Movie App

React를 이용한 Movie App

  • 팀 과제 : 영화 소개 웹페이지 만들기

Movie App : Githup 링크


Home Page

  • src/pages/Home.js
  const { movieData, loading } = useMoive({
    url: "list_movies.json?minimum_rating=8&limit=20&sort_by=like_count",
  });
{movieData.map((movie) => (
	<Movie
		key={movie.id}
        id={movie.id}
        image={movie.medium_cover_image}
        title={movie.title}
        rating={movie.rating}
        runtime={movie.runtime}
        year={movie.year}
	/>
))}

커스텀 훅을 만들어 영화 데이터를 fetch하였습니다. 커스텀 훅을 통해 받아온 데이터는 map을 통해 Movie 컴포넌트의 prop으로 전달해줍니다.


  • src/api/useMovie.js
import { useEffect, useState } from "react";

const useMoive = ({ url }) => {
  const BASE_URL = "https://yts.mx/api/v2/";
  const [movieData, setMovieData] = useState();
  const [loading, setLoading] = useState(true);

  const getMovie = async () => {
    const data = await (await fetch(BASE_URL + url)).json();
	setMovieData(data.data.movies);
  };

  useEffect(() => {
    if (movieData) setLoading(false);
  }, [movieData]);

  useEffect(() => {
    if (url) {
      getMovie();
    }
  }, [url]);

  return { movieData, loading };
};

export default useMoive;

커스텀 훅인 useMovie는 데이터와 로딩값을 return합니다. API url을 받아 fetch한 후 데이터를 전달하고 데이터가 존재할 시 로딩값을 변경해줍니다. 이렇게 반환하는 데이터와 로딩값을 받는컴포넌트는 로딩중일시 데이터 출력을 막아줄 수 있습니다.


Home Page 결과


Detail Page

  • src/pages/Detail.js
const { id } = useParams();
const { movieData, loading, refetcher } = useMoive({
  url: `movie_details.json?movie_id=${id}&with_cast=true`,
  type: "detail",
});

Detail Page는 useParams을 사용하여 파라미터값을 구해 영화 데이터를 fetch하였습니다. 단 Detail 정보를 주는 API url의 데이터는 홈 페이지의 데이터와 형태가 달라 type이라는 parameter를 주어 커스텀 훅 내에서 구별하도록 만들어주었습니다.


  • src/api/useMovie.js
import { useEffect, useState } from "react";

const useMoive = ({ url, type = "movies" }) => {
  const BASE_URL = "https://yts.mx/api/v2/";
  const [movieData, setMovieData] = useState();
  const [loading, setLoading] = useState(true);

  const getMovie = async () => {
    const data = await (await fetch(BASE_URL + url)).json();
    console.log(data);
    if (type === "movies") {
      setMovieData(data.data.movies);
    } else if (type === "detail") {
      setMovieData(data.data.movie);
    }
  };

  useEffect(() => {
    if (movieData) setLoading(false);
  }, [movieData]);

  useEffect(() => {
    if (url) {
      getMovie();
    }
  }, [url]);

  return { movieData, loading };
};

export default useMoive;

커스텀 훅은 type parameter이용해 데이터를 형태에 맞게 저장시켜줍니다.


Detail Page 결과


Search Page

  • src/pages/Search.js
const { movieData, loading, refetcher } = useMoive({ url: null });

Search Page는 쿼리 파라미터가 변경 할 때마다 커스텀 훅의 refetcher사용하여 API를 다시 fetch합니다.


  • src/api/useMovie.js
import { useEffect, useState } from "react";

const useMoive = ({ url = null, type = "movies" }) => {
  const BASE_URL = "https://yts.mx/api/v2/";
  const [movieData, setMovieData] = useState();
  const [loading, setLoading] = useState(true);

  const getMovie = async () => {
    const data = await (await fetch(BASE_URL + url)).json();
    console.log(data);
    if (type === "movies") {
      setMovieData(data.data.movies);
    } else if (type === "detail") {
      setMovieData(data.data.movie);
    }
  };

  const refetcher = async (refetch) => {
    if (url && loading) return;
    if (url) {
      getMovie();
    } else {
      const data = await (
        await fetch(BASE_URL + `list_movies.json?query_term=${refetch}`)
      ).json();
      setMovieData(data.data.movies);
    }
  };

  useEffect(() => {
    if (movieData) setLoading(false);
  }, [movieData]);

  useEffect(() => {
    if (url) {
      getMovie();
    }
  }, [url]);

  return { movieData, loading, refetcher };
};

export default useMoive;

커스텀 훅의 refetch를 실행시키면 refetch가 받은 parameter를 이용하여 fetch를 진해합니다.

Search Page 결과


🍰 React를 이용한 서비스

React를 이용한 서비스

  • React로 내 위치의 날씨 만들기
  • React를 이용하여 할 일 목록 만들기

React로 내 위치의 날씨 만들기

Github 링크

  • component/weather.jsx
import React, { useEffect, useState } from "react";

const Weather = () => {
  const [data, setData] = useState();
  const API_KEY = "8b46d1d2fb5be43ae110f89a6e57cab0";

  const getWeather = async (lat, lon) => {
    const weatherData = await (
      await fetch(
        `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric`
      )
    ).json();
    setData(weatherData);
  };

  const handleSuccess = (pos) => {
    const lat = pos.coords.latitude;
    const lon = pos.coords.longitude;
    getWeather(lat, lon);
  };

  const handleError = (error) => {
    console.log(error);
  };

  const getGeoLocation = () => {
    navigator.geolocation.getCurrentPosition(handleSuccess, handleError);
  };

  useEffect(() => {
    getGeoLocation();
  }, []);

  return (
    <>
      <div style={{ padding: 10, paddingRight: 20 }}>
        {data
          ? `${data.name} ${data.weather[0].main} ${data.main.temp.toFixed(
              1
            )}°C`
          : "loading"}
      </div>
    </>
  );
};

export default Weather;

useEffect를 이용하여 컴포넌트가 랜더시 위치를 불러오고, 해당 위치를 이용하여 날씨 API를 통해 날씨를 불러옵니다.


React를 이용하여 할 일 목록 만들기

Github 링크

  • component/todo.jsx
import React, { useState } from "react";
import List from "./list";

const Todo = () => {
  const [todos, setTodos] = useState([]);
  const [todo, setTodo] = useState("");
  const onSubmit = (evnet) => {
    evnet.preventDefault();
    setTodos((pre) => [...pre, { content: todo, id: Date.now() }]);
    setTodo("");
  };
  const onChange = (event) => {
    const {
      target: { value },
    } = event;
    setTodo(value);
  };
  const onDelete = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };
  return (
    <div
      style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
    >
      <ul>
        {todos.map((todo) => (
          <List
            key={todo.id}
            content={todo.content}
            id={todo.id}
            onDelete={onDelete}
          />
        ))}
      </ul>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={todo}
          onChange={onChange}
          style={{
            width: 400,
            fontSize: 32,
            color: "#fff",
            padding: "10px",
            border: "none",
            borderBottom: "2px solid #fff",
            backgroundColor: "inherit",
            outline: "none",
          }}
        />
      </form>
    </div>
  );
};

export default Todo;

useState를 통해 객체 및 배열도 state로 만들 수 있습니다. 그러나 state를 직접 변경하면 안되기 때문에 배열을 추가하는 과정에서 push를 사용하지않고 새로운 배열을 만드는 방법으로 배열을 추가하였습니다. 배열의 삭제또한 할 일 목록에 미리 id를 부여하고 filter통해 state를 새로 만들었습니다. 그리곤 map을 이용하여 배열의 내용들을 출력할 수 있습니다.


📝후기

API를 관리하기 위해 커스텀 훅을 만들어보았습니다. 페이지가 늘어날수록 처음 계획했던 커스텀 훅 형태와는 많이 달라졌습니다. 중복되는 fetch 사용 제어, refetch를 하기 위한 함수 생성등 커스텀 훅은 점점 많은 기능이 필요해졌습니다. 이렇듯 좀 더 효율적인 코딩을 위한 커스텀 훅을 다시 만든다면 효율적인 기능을 위한 제어와 방식을 먼저 생각해서 만들어야겠습니다.



본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.

#프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프

profile
기록하고 배우고 시도하고

1개의 댓글

comment-user-thumbnail
2024년 2월 14일

As I settled into the plush comfort of my favorite armchair, I prepared for another cinematic escapade into the realms of imagination and emotion. Tonight's selection on https://kinogo-la.zone/ promised a diverse array of films, each offering a unique perspective on life and humanity. With a bowl of popcorn in hand, I eagerly awaited the opening credits to roll, signaling the beginning of my journey.

답글 달기