[개인프젝] Napflix - 배운 내용 정리 Part. 1

Haiin·2021년 3월 23일
0
post-thumbnail

참고

개인적으로 프로젝트를 하면서 그동안 배운것도 정리하고 좀 더 효율적인 코드작성을 위해 napflix 라는 프로젝트를 시작하였다. 니꼬 강의 (React.js, Hooks 기반에 API를 이용하는 간단한 웹페이지 구현 프로젝트) 이지만 더 정확하게 알게 된 내용들과, 깨알같은 팁 들을 정리해보고자 한다.



Part 1. React.js



1. API 구조

API 를 이용하여 서버에 데이터를 요청할 때 기본적인 세팅은 공통적이기 때문에 api.js 파일에 instance 를 생성하여 API 들을 미리 만들어 놓으면 필요한 곳에서 불러 쓰기 용이하다.

1-1. instance 생성

axios.create([config])
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

1-2. api 객체화

export const api = {
  apiA: () => api.get("data/apiA"),
  apiB: () => api.get("data/apiB"),
  apiC: () => api.get("data/apiC"),
};

1-3. api 요청

 const {
      data: { results: apiA },
    } = api.apiA();

실제 적용

api.js 에 만들어 놓은 기본 api 인스턴스를 활용하여 각각의 movie, tv 별로 api를 호출하는 객체를 만들어 주었다. movieContainer.js 에서 api에서 호출한 데이터를 사용하는 로직을 처리하기 위해 구조분해할당으로 간단히 정제한 nowPlaying 이라고 명명한 데이터를 사용할 수 있다.

/* api.js */

const api = axios.create({
  baseURL: "baseURL",
  params: {
    api_key: "api_key",
    language: "en-US",
  },
});
export default api;

export const moviesApi = {
  nowPlaying: () => api.get("movie/now_playing"),
  upcoming: () => api.get("movie/upcoming"),
  popular: () => api.get("movie/popular"),
};

/* movieContainer.js */

 const {
      data: { results: nowPlaying },
    } = movieApi.topRated();


2. Container + Presenter Pattern

로직을 수행하는 컴포넌트와, markup을 통해 ui를 보여주는 컴포넌트가 분리된 패턴.

Container - data, state, getting APIs, logics
Presenter - JUST SHOWING

장점 :
1. 앱의 기능과 ui에 대한 구분이 쉬워진다.
2. 같은 state를 다른 container에게 props 내림으로 상태를 공유 할 수 있다.
3. 로직수행, markUp이 다른 컴포넌트에서 하기 때문에 유지보수가 쉽고, 재사용성이 가능하다. 특히, markup 변경에 매우 유연하다.
4. props.children 사용이 가능하다.


2-1. children prop

React는 강력한 합성 모델을 가지고 있으며, 상속 대신 합성을 사용하여 컴포넌트 간에 코드를 재사용하는 것이 좋습니다.
어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올 지 미리 예상할 수 없는 경우,
특수한 children prop을 사용하여 자식 엘리먼트를 출력에 그대로 전달하는 것이 좋습니다. -React 공식문서-

기존에 컴포넌트를 재사용하거나 자식컴포넌트로 정보를 넘겨주는 상황에서 props 로만 데이터를 넘겨주는 것(<Section title="Now Playing"> 에서 title 이라는 이름으로 Now Playing 이라는 문자열을 props 로 넘겨주었다.) 과는 다르게,
<Section>{모든 것}</Section> 안에 있는 모든 것을 전달할 수 있다.
공식문서의 설명과 같이 다른 컴포넌트가 올 수도 있고, 메서드를 이용하여 여러 컴포넌트를 렌더링 할 수도 있는 어떤 자식 엘리먼트가 들어올지 미리 예상할 수 없는 경우 모두를 포괄하여 데이터를 전달 할 수 있다는 뜻으로 해석된다.


실제 적용

자식 컴포넌트 또는 재사용 컴포넌트를 사용할 때 <Section/> 이라고 쓰지 않고 <Section>{children}</Section> 태그를 양쪽에 온전히 써주고 그 안에 {} 작성하게 되면 children prop 을 이용하여 출력 그대로를 전할 수 있다.

import Section from "../../Components/Section/Section";

const TVPresenter = () =>
    <Container>
      <Section title="Now Playing">
            {nowPlaying?.map((movie) => (
              <Poster
                key={movie.id}
                id={movie.id}
                imageUrl={movie.poster_path}
                title={movie.original_title}
                rating={movie.vote_average}
                year={movie.release_date.substring(0, 4)}
                isMovie={true}
              />
            ))}
       </Section>
    </Container>
const Section = ({ title, children }) => {
  console.log(children); // 여러개의 <Poster/> 
  return (
    <Container>
      <Title>{title}</Title>
      <Grid>{children}</Grid>
    </Container>
  );
};


3. 구조분해할당

기존에는 정말 간단하게 const {a, b} = obj 형태로만 사용했었는데 더 깊은 하위항목을 부를때가 진정 구조분해할당의 꽃🌺 인것 같다.

  • 객체의 깊은 하위항목까지 간단하게 불러올 수 있고,
  • 동시에 객체 값의 이름도 바꿔줄 수 있다.
const obj = {
  name: "haiin",
  favFood: {
    morning: "apple",
    lunch: "banana",
    dinner: { early: "bab", late: "chicken" },
  },
};

console.log(obj);
/*
{
  name: 'haiin',
  favFood: {
    morning: 'apple',
    lunch: 'banana',
    dinner: { early: 'bab', late: 'chicken' }
  }
}
*/
console.log(obj.favFood);
/*
{
  morning: 'apple',
  lunch: 'banana',
  dinner: { early: 'bab', late: 'chicken' }
}
*/

const { favFood } = obj;
console.log(favFood);
/*
{
  morning: 'apple',
  lunch: 'banana',
  dinner: { early: 'bab', late: 'chicken' }
}
*/
const {
  favFood: { dinner },
} = obj;

console.log(dinner);
/*
{ early: 'bab', late: 'chicken' }
*/
const {
  favFood: {
    dinner: { early: earlyDinner },
  },
} = obj;
console.log(earlyDinner);
//'bab'

실제적용

Before
이전까지는 데이터를 불러오는 코드에서 보통은 아래와 같이 작성하였다.
그러면 request.data.results 이런식으로 원하는 값에 도달하여 적어도 1-2줄 정도 코드가 길어지고 가독성도 떨어졌다.

const request = await moviesApi.nowPlaying();
console.log(request);

After
위의 코드를 구조분해할당으로 아래와 같이 작성하면 불러온 초기모양의 데이터에서 어떻게 원하는 값에 도달하는지 명시적으로 보일 뿐 만 아니라 nowPlaying 이라는 원하는 이름으로 데이터 이름을 바꾸어 주기까지 했다.

const {
        data: { results: nowPlaying },
      } = await moviesApi.nowPlaying();
console.log(nowPlaying);



4. PropTypes

당신의 앱이 커짐에 따라 타입 확인을 통하면 많은 버그를(bug) 잡을 수 있습니다. 특정 애플리케이션에서는 전체 애플리케이션의 타입 확인을 위하여 Flow 또는 TypeScript와 같은 JavaScript 도구(Extensions)를 사용할 수 있습니다. 당신이 이러한 것들을 사용하지 않더라도 React는 내장된 타입 확인 기능들을 가지고 있습니다. 컴포넌트의 props에 타입 확인을 하려면 다음과 같이 특별한 프로퍼티인 propTypes를 선언할 수 있습니다. -React 공식문서-

4-1. 설치

npm i prop-types


4-2. import

import PropTypes from "prop-types"


4-3. 실제 적용

Container 에서 처리한 로직으로 받은 데이터의 타입을 확인하기 위해 presenter 에서 PropTypes 를 import 하여 확인해 주었다.
isRequired 는 prop 이 제공되지 않았을 때 경고메세지를 보이게 해준다.

import PropTypes from 'prop-types';

const TVPresenter = ({ topRated, loading }) => (
  <Loader />
  ...
);

TVPresenter.propTypes = {
  topRated: PropTypes.array,
  loading: PropTypes.bool.isRequired,
  ...
};


5. React-helmet

This reusable React component will manage all of your changes to the document head. -React-helmet github-

  • 헬멧 컴포넌트는 재사용 컴포넌트로 import 하여 사용하는데 모든 head 태그들 (title, base, meta, link, script, noscript, and style tags) 이 사용 가능하다.
  • body, html and title tags 어디 위치에서도 사용 가능하다.
  • 서버 사이드 렌더링을 가능하게 한다.
  • 중복 사용이 가능하며 템플릿 리터럴 표기법 사용도 가능하다.

5-1. 설치

npm i react-helmet


5-2. import

import Helmet from "react-helmet"


5-3. 실제 적용

presenter 마다 helmet 을 import 하여 페이지가 바뀔 때 마다 헤더의 title을 바꿔 주었다.
DetailPresenter.js 는 두 가지 타입의 데이터를 구분하기 위해 title 안에 삼항자를 넣어 주었는데 {} 안에 템플릿 리터럴 표기법을 사용하면 잘 적용되는 것을 볼 수 있다.

import Helmet from "react-helmet";

const DetailPresenter = ({result}) => (
  <Helmet>
        <title>
          {`${
            result && result?.original_title
              ? result?.original_title
              : result?.original_name
          } | Nomflix`}
        </title>
  </Helmet>
  ...
)


0개의 댓글