ReactJS로 영화 웹 서비스 만들기

30071 단어 ReactReact

ReactJS로 영화 웹 서비스 만들기


개발환경 세팅

node -v
npm -v
npx -v
git --version
  • 위의 명령 실행해서 없으면 설치하기

npx

npm에서 제공하는 패키지 실행 도구로, 패키지를 따로 설치하지 않고 일회성으로 사용할 수 있는 기능을 제공한다. (npm 5.2.0 버전 이상이면 같이 설치됨)


리액트 프로젝트 생성

  • create-react-app으로 리액트 프로젝트 생성
    npx create-react-app [폴더명]

    왜 나는 파일이 제대로 안 만들어질까....

    create-react-app만 쳐서 전역으로 설치되어 있는지 확인하고, 만약 설치되어 있으면 npm uninstall -g create-react-app으로 삭제한 후 다시 시도!


깃 연결하기

  • 깃 레포 생성 후에 프로젝트에 add
    git remote add origin [주소]
  • 그리고 commit & push

    .gitignore 파일이 세팅되어 있기 때문에 node_modules 폴더 등은 안 올라감!


Virtual DOM

참고
브라우저가 HTML을 전달받으면 DOM 노드 트리를 만드는데, DOM을 조작할 때마다 이 트리가 다시 생성되어 느려진다.
그런데 Virtual DOM은 실제 DOM에 적용하기 전에 가상 DOM에 먼저 조작을 한 다음 결과만을 반영하여 연산을 줄이고 성능을 개선시킨다.

  • 리액트가 항상 빠른 것은 아니며, 리액트는 유지보수를 용이하게 해주고 웬만한 경우에 !충분히! 빠르다는 게 장점이라고 한다.

props 유효성 검사

  • prop-types 설치
    npm i prop-types (iinstall)
  • import
    import PropTypes from 'prop-types';
  • 사용
// 컴포넌트.propTypes = {};
Food.propTypes = {
  name: PropTypes.string.isRequired,
  picture: PropTypes.string.isRequired,
  rating: PropTypes.number,
};

👉 결과는 문제없이 나와도 콘솔에서 오류를 출력해준다.


setState()에 정확한 값 전달하기

참고

  • setState() 호출은 비동기적이다.
  • 호출 직후 this.state에 새로운 값이 반영되는 것이 아니다.
    👉 this.statethis.props는 렌더링된(화면에 보이는) 값이기 때문!
  • 객체가 아니라 함수를 전달하면 이전 state 값에 접근할 수 있고, setState() 호출은 일괄적으로 처리되기 때문에 충돌 없이 값을 반영할 수 있다.

add = () => {
    this.setState({ count: this.state.count + 1 });
};

add = () => {
    this.setState((current) => ({ count: current.count + 1 }));
};

컴포넌트 라이프사이클

Mounting

컴포넌트 탄생

  • constructor(): 컴포넌트가 생성될 때 제일 먼저 실행
  • render(): 그 다음 실행
  • componentDidMount(): 컴포넌트가 처음 render 되었을 때 실행

Updating

업데이트

  • render(): setState() 호출 후 실행
  • componentDidUpdate(): 업데이트 render 이후 실행

Unmounting

컴포넌트 죽음 (페이지 넘길 때, 컴포넌트 교체될 때 ...)

  • componentWillUnmount(): 컴포넌트가 사라지기 전에 실행

비동기 요청 처리

  • axios 설치
    npm i axios
  • yts api
    • url이 계속 바뀌기 때문에 yts-proxy 주소를 사용할 것

async & awiat

기존에 콜백 함수로 처리하던 것을 비동기 함수로 만들어서 보기 좋은 코드로 만든다.

  • 함수 앞에 async 키워드를 붙이고 비동기 요청 앞에 await 키워드를 붙인다.
getMovies = async () => {
    const movies = await axios.get('https://yts-proxy.now.sh/list_movies.json');
};

componentDidMount() {
  this.getMovies();
}

👉 getMovies()가 비동기 함수라고 명시했기 때문에 이 요청이 처리될 때까지 기다린다.

getMovies = async () => {
  const movies = await axios.get('https://yts-proxy.now.sh/list_movies.json');
  console.log(movies.data.data.movies);
};

위의 코드를 ES6 문법으로 바꾸면 아래와 같다.

getMovies = async () => {
  const {
    data: {
      data: { movies },
    },
  } = await axios.get('https://yts-proxy.now.sh/list_movies.json');
  console.log(movies);
};

문자열(배열) 자르기

// 처음 인덱스부터 마지막 인덱스 전까지 자른 복사본을 리턴한다. (원본 수정 x)
slice(처음 인덱스, 마지막 인덱스)

<p className="movie__summary">{summary.slice(0, 180)}...</p>

깃헙 페이지 배포

  • gh-pages 설치
    npm i gh-pages

package.json

  • homepage 추가
    "homepage": "https://[사용자명].github.io/[프로젝트명]"
  • scriptsdeploy, predeploy 추가
    • 프로젝트를 build하면 build 폴더가 생성됨
 "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "deploy": "gh-pages -d build",
    "predeploy": "npm run build"	// deploy 명령을 하면 자동으로 실행됨
},
  • npm run deploy로 배포

라우터

  • react-router-dom 설치
    npm install react-router-dom
// App.js
import React from 'react';
// react-router-dom import
import { HashRouter, Route } from 'react-router-dom';
import Home from './routes/Home';
import About from './routes/About';

function App() {
  return (
    <HashRouter>
      <Route path="/" exact={true} component={Home} />
      <Route path="/about" component={About} />
    </HashRouter>
  );
}

export default App;
  • Route는 해당 패스에 일치하는 모든 컴포넌트를 렌더링
  • /about 패스에 / 패스가 포함되므로 /about으로 접속하면 HomeAbout이 모두 렌더링됨
    👉 exact={true} props를 주면 정확히 일치하는 주소일 때만 렌더링!

Link

  • 일반 a 태그를 사용하면 웹페이지가 새로고침되기 때문에 라우터가 제대로 동작하지 않는다.
    👉 react-router-dom의 Link를 사용해야 함!
// components/Navigation.js
import React from 'react';
import { Link } from 'react-router-dom';

function Navigation() {
  return (
    <div>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
    </div>
  );
}

export default Navigation;
// App.js
import React from 'react';
// BrowserRouter를 쓰면 주소 끝에 #(Hash)가 안 붙는 대신 깃헙 페이지에 배포가 까다로움
import { HashRouter, Route } from 'react-router-dom';
import Home from './routes/Home';
import About from './routes/About';
import Navigation from './components/Navigation';

function App() {
  return (
    <HashRouter>
      <Navigation />
      <Route path="/" exact={true} component={Home} />
      <Route path="/about" component={About} />
    </HashRouter>
  );
}

export default App;
  • 꼭 모든 컴포넌트가 라우터 안에 있어야 하는 건 아니지만 Link를 쓰려면 라우터 안에 있어야 함
<Link
  to={{
    pathname: '/about',
    state: {
      fromNavigation: true,
    },
  }}
>

이런 식으로 to에 오브젝트를 전달하면 해당 컴포넌트에 props로 전달된다.

이렇게 :id처럼 주소에 변수를 줄 수도 있다.

<Route path="/movie/:id" component={Detail} />
// 사용하는 곳에서 `pathname`에 변수 전달
<Link to={{pathname: `/movie/${id}`, ...}}>...</Link>

리다이렉팅

  • 요소를 클릭하지 않고 직접 url에 접속하면 props.state 값이 전달되지 않기 때문에 홈으로 리다이렉트를 해준다.
componentDidMount() {
  const { location, history } = this.props;
  if (location.state === undefined) {
    history.push('/');
  }
}
  • 상세 페이지에서 다시 주소를 호출하면 props.state 값이 전달되지 않기 때문에 null을 리턴하여 렌더링이 되지 않도록 한다.
render() {
  const { location } = this.props;
  if (location.state) {
    return <span>{location.state.title}</span>;
  } else {
    return null;
  }
}

근데 어떻게 홈으로 리다이렉트 되는지는 잘 모르겠음...

좋은 웹페이지 즐겨찾기