본문 바로가기

공부일지/Project

비전공자의 프로젝트 만들기) disneyplus App 만들기 part1 - 구조만들기

728x90
반응형

DisneyPlus App 만들기

 

패스트캠퍼스에 디즈니 서비스 앱 만들기 프로젝트를 하였다.

 

프로젝트 진행 전 the movie db를 사용해서 영화 정보를 받아와 작업해야 되기에 api를 발급받아야 된다.

발급방법은 구글에 검색하면 자세한 설명이 있는 글들이 많은 거 같다.

https://www.themoviedb.org/

 

The Movie Database (TMDB)

Welcome. Millions of movies, TV shows and people to discover. Explore now.

www.themoviedb.org

먼저 이번에는 api통신을 fetch가 아니라 axios를 사용해서 작업한다.

fetch와 axios의 차이점을 상세히 알진 못하지만 axios가 사용하기 좀 더 편하고 json을 자동으로 response객체로 반환한다 정도로 알고 있다.

 

그럼 먼저 CRA를 해서 리액트 구조를 생성하고 axios를 설치해 준다.

api 폴더를 생성 후 movieAxios, request를 생성해 준다.

movieAxios는 axios에서 사용될 기본 url, api키등 기본정보를 작성해 주었다.

import axios from 'axios'

const instance = axios.create({
    baseURL: "http://api.themoviedb.org/3",
    params: {
        api_key:"API_KEY",
        language:"ko-KR"
    }
});

export default instance

API_KEY라고 적힌 곳에 발급받은 api키를 넣어주면 된다.

공통으로 계속 사용될 api 정보이기 때문에 오타가 없이 작성해야 된다.

 

request 페이지에는 원하는 정보의 데이터를 불러올 수 있는 정보를 작성해 두었다.

const requests ={
    fetchNowPlaying : "movie/now_playing",
    fetchTrending : "/trending/all/week",
    fetchTopRated : "/movie/top_rated",
    fetchActionMovies : "/discover/movie?with_genres=28",
    fetchComedyMovies : "/discover/movie?with_genres=35",
    fetchHorrorMovies : "/discover/movie?with_genres=27",
    fetchRomanceMovies : "/discover/movie?with_genres=10749",
    fetchDocumentaries : "/discover/movie?with_genres=99"
}

export default requests

현재 방영작품, 인기작품, 최신작품, 액션 카테고리 등 movieAxios에서 적은 기본정보를 토대로 영화의 정보를 불러올 수 있게 해주는 소스이다.

 

이번 프로젝트에서는 Styled Component를 사용할 건데 Styled Component는 컴포넌트 내에 style 및 el를 선언해서 사용할 수 있다. sass와 작성 패턴은 비슷하고 state에 따라 css를 다양하게 지정해 줄 수 있고 css를 한 곳에 적는 방식과 다르게 어느 컴포넌트에서 문제가 생겼는지 바로 알 수 있는 게 장점 같다. styled-componet도 설치해 주었다.

 

내가 styled-components를 설치 중 오타로 마지막 s를 빼고 설치했는데 (설치가 되다니....) 그래서 다시 styled를 설치하려니 터미널에 Cannot read properties of null (reading 'edgesOut') 이런 오류가 뜨길래 이것저것 삭제도 해보고 캐시도 지워보고 다했는데 계속 해당 오류로 설치가 안되었는데 우연히 구글링 중 찾은 게시글로 설치가 되었다.

https://stackoverflow.com/questions/70810819/npm-err-cannot-read-properties-of-null-reading-edgesout

 

npm ERR! Cannot read properties of null (reading 'edgesOut')

PS C:\Users\Ahmet\Desktop\İrem\7\üü> npm i (any package-name) npm ERR! Cannot read properties of null (reading 'edgesOut') npm ERR! A complete log of this run can be found in: npm ERR! C:\Us...

stackoverflow.com

styled 설치할 때 해당 오류가 있는 거 같은데 설치히 @latest를 붙여 최신버전으로 설치하니 잘 설치가 되었다.

이런 걸로... 20분 넘게 잡아먹다니.....

 

 

styled-components가 설치를 하면서 앱의 구조를 작성했다.

Banner, Category, Nav, Row를 작성해 주었다. css파일이 있는 이유는 styled로 작성하기 힘든 것들이 나 엘리먼트들이 아닌 전체적인 틀을 잡거나 할 때 사용하기 위해 만들었다.

  • Nav: 상단 로고 및 계정정보가 표시 영역
  • Banner : 디즈니앱을 접속하면 상단에 보이는 영상의 썸네일 및 미리 보기 영역

  • Category : 각 영상의 카테고리 영역 영화, 드라마 등 각 영상의 영역

  • Row : 카테고리 별 영상리스트를 말한다.

 

이렇게 구조가 끝났다. Modal은 영상을 클릭 시 나올 상세페이지 팝업을 만들 예정이라 일단 폴더만 만들었다.

 

위에서부터 구조 코드를 작성해서 Nav부터 작업하였다.

import React from "react";
import styled from "styled-components";

const Nav = () => {
    return (
        <NavWrapper>
            <Logo>
                <img
                    alt="Disney Plus Logo"
                    src="/images/logo.svg"
                    onClick={() => (window.location.href = "/")}
                />
            </Logo>
        </NavWrapper>
    );
};

export default Nav;

const NavWrapper = styled.nav`
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 70px;
    background-color: "#090b13";
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 36px;
    letter-spacing: 16px;
    z-index: 3;
`;

const Logo = styled.a`
    padding: 0;
    width: 80px;
    margin-top: 4px;
    max-height: 70px;
    font-size: 0;
    display: inline-block;
    img {
        display: block;
        width: 100%;
    }
`;

아직 까진 Nav에 작성할 코드가 많이 없기 때문에 NavWrapper을 만들고 logo 엘리먼트를 만들고 안에 img태그를 넣어주었다.

src는 완성코드에 깃 주소를 첨부할 예정이라 깃 주소에서 다운로드하여서 사용하면 된다.

 

Nav의 스크롤 이벤트를 등록하였다.

    const [show, setShow] = useState(false)

    useEffect(() => {
        window.addEventListener("scroll",()=>{
            if(window.scrollY > 1){
                setShow(true);
            }else{
                setShow(false);
            }
        });
        return (
            window.removeEventListener("scroll",()=>{})
        )
    }, [])
    
            <NavWrapper show={show}>

 

useEffect를 사용해서 window 이벤트에 scroll이벤트의 변화를 감지하고 실행되게 하였다.

return에 remove를 넣은 이유는 remove가 없으면 이벤트가 계속 쌓인다고 한다. 그럼 스크롤을 할 때마다 계속 스크롤 이벤트 함수가 쌓인다고 한다. 그렇기 때문에 필요 없는 이벤트는 지우고 이벤트를 감지했을 때 실행 후 바로 제거되는 형태이다.

 

그럼 이제 styled에서 nav색상을 변경해 주는 작업을 진행한다.

const NavWrapper = styled.nav`
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 70px;
    background-color: ${(props) => (props.show ? "#090b13" : "transparent")};
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 36px;
    letter-spacing: 16px;
    z-index: 3;
`;

props로 상태를 받고 해당 상태가 ture면 색상 아니면 투명이 되게 하였다.

 

Nav를 작성했으니 이제 main에서 어떻게 보일지 app.js에 임시로 스타일 작업을 했다.

 

import './App.css';
import Nav from './components/Nav';
import { styled } from 'styled-components';

function App() {
  return (
    <Container>
      <Nav />
    </Container>
  );
}

export default App;

const Container = styled.main`
    position: relative;
    min-height: calc(100vh - 250px);
    overflow-x: hidden;
    display: block;
    top: 72px;
    padding: 0 calc(3.5vh + 5px);

    &:after {
        content: "";
        background: url("/images/home-background.png") center / cover no-repeat fixed;
        position: absolute;
        inset: 0px;
        opacity: 1;
        z-index: -1;
    }
`;

프로젝트 내에서는 딱히 css부분은 중요한 부분 빼고는 설명이 없을 거 같다. 

 

이제 배너에 영화를 출력하는 부분을 작업해야 되는데 해당 부분은 api로 정보를 가져와서 보여주는 부분이다.

먼저 그럼 아까 만들었던 movieAxios와 requests를 불러와 주어야 한다.

import movieAxios from '../api/movieAxios'
import React, { useEffect } from 'react'
import requests from './../api/request';

const Banner = () => {
  
  useEffect(() => {
    fetchData();
  }, [])
  
  const fetchData = async () => {
    const request = await movieAxios.get(requests.fetchNowPlaying);
    console.log(request)
  }

  return (
    <div>
      
    </div>
  )
}

export default Banner

비동기 처리를 하기 위해 async, await를 사용해 주었다.

await뒤에 있는 api속성을 자세히 보자면 api페이지에서 만들었던 movieAxios를 불어와 get요청으로 requests페이지에서 작성한 하나의 key를 들고 온다.

 

배너에는 현재 상영하고 있는 작품을 출력시키도록 하였다. 

axios코드를 풀어보자면

//axios
const response ={
  url ='http://api.themoviedb.org/3//movie/now_playing`
   method:'GET',
   params:{
     api_key:'api_key',
     language:"ko-KR"
  },
  data:{
  	
  }

이런 식이라고 한다.

 

그럼 데이터를 잘 받아 왔는지 확인하기 위해 console.log에 출력하라고 했는데 출력이 잘되었는지 보자면

이렇게 출력이 됐다면 성공이다. async, await를 안 적고 콘솔도 한번 찍어보길 바란다.

 

그럼 promise라고 뜰 텐데 구글링 해서 얻은 정보로는 비동기 통신할 때 사용되는데 데이터를 다 받기 전에 출력하지 못하게 하고 데이터를 다 받아왔을 때 출력하게 하는 기능이라고 한다.

aynce를 사용하면 이 pormise를 자동으로 된다고 한다. async안에 pormise코드를 작성해도 되고 작성 하지 않아도 자동으로 pomise를 자동 반환한다고 한다.

 

async를 사용하지 않았을때의 console 출력 화면이다. 아 그리고 anyce가 없는데 await를 사용할 수 없다.

Promise<pending>이라고 적힌 이유는 pending 아직 미행 이런뜻인데 respnose되지 않았는데 console에 찍어서 확인을 해서 그렇다. 

그래서 위에 이미지를 보면 async,await를 사용해서 await 가 다되고 console을 작성하였기 때문에 데이터가 콘솔에 출력이 된다.

 

그리고 우리가 받은 정보의 data리스트 중에 하나만 골라서 출력하려고 한다.

 

  const [movie, setMovie] = useState([]);

  useEffect(() => {
    fetchData();
  }, [])
  
  const fetchData = async () => {
    const request = await movieAxios.get(requests.fetchNowPlaying);

    const movieId = request.data.results[
      Math.floor(Math.random() * request.data.results.length)
    ].id;

    const {data:movieDetail} = await movieAxios.get(`/movie/${movieId}`,{
      params:{
        append_to_response:"videos"
      }
    })
    setMovie(movieDetail)
  }

그러기 위해선 movie의 정보를 저장할 state를 하나 만든다.

그리고 request에서 받은 data의 results중에 하나만을 선택하기 위해서 floor와 random의 메소드를 사용 하였다. floor으로 소수점을 없애고 random은 0과 1사이의 랜덤함 난수를 반환한다.

random과 data의 리스트를 곱해서 소수점을 없애는 방식이다. 그래서 얻은 id를 다시 api 요청을 한다.

data:movieDetail은 data의 명청을 movieDetail로 지정해주는 방식이다. 

axios에서 movie의 정보를 얻고 params에 append_to_reponse는 the movie db 에서 영상의 정보를 주겠다고 지정해 놓은 값이다. 

이렇게 얻은 값을 setMoive에 저장한다.

 

배너출력하는건 다음일지에서 마저 작성 하도록 한다.

 

 

728x90
반응형