본문 바로가기

React

[React] MVC 패턴으로 코드짜기

MVC패턴

모델Model : 모델은 앱이 포함해야할 데이터가 무엇인지를 정의. ( ==데이터베이스 )
뷰View: 레이아웃과 화면을 처리합니다.(==화면 jsx, vue)
컨트롤러Controller: 명령을 모델과 뷰 부분으로 라우팅합니다. (==비동기통신하는 부분, composable)

mvc패턴으로 짜주려면 누구 하나가 모든것을 다 처리하면 안된다.
각각의 역할 만큼만 할 수 있도록 만들어주어야한다. 

따라서 view에서 비동기통신과 관련된 것들을 모~두 하지 않는것이 좋고, 따로 services 폴더를 만들어 관리한다.

 

 


수정 코드 app.jsx

import './App.css';
import React, {useState, useEffect} from 'react';
import VideoList from './components/video_list/index.jsx';
import Search from './components/search/index.jsx';

function App({ youtube }) {
  const [ videos, setVideos ] = useState([]);
  const apikey = '123456789testkey';

  const requestOptions = {
    method: 'GET',
    redirect: 'follow'
  };
  
  useEffect(() => {
    console.log('effect')
    fetch(
      `https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&maxResults=25&type=video&key=${apikey}`
      , requestOptions
    )
      .then(response => response.json())
      .then(result => setVideos(result.items))
      .catch(error => console.log('error', error));
  }, [])

  const handleSearch = (data) => {
    const url = 'https://www.googleapis.com/youtube/v3/search'

    fetch(
      `${url}?part=snippet&q=${data}&maxResults=25&type=video&key=${apikey}`
      , requestOptions
    )
      .then(response => response.json())
      .then(result => result.items.map(item => ({...item, id: item.id.videoId })))
      .then(items => setVideos(items))
      .catch(error => console.log('error', error));
  }

  return (
    <React.Fragment>
      <Search handleSearch={handleSearch}></Search>
      <VideoList videos={videos}></VideoList>
    </React.Fragment>
  );
}

export default App;
언뜻봐도 알겠지만 모든 통신을 app.jsx 파일에서, 즉 view에서 모든 것을 컨트롤하고 있는 상태이다.

 

 


MVC 패턴으로 수정하기


1. src밑에 services 폴더 만들고 하위에 index.ts 파일 만들기

//services/index.ts
class Youtube {
  constructor(key) {
    this.key = key;
    this.requestOptions = {
      method: 'GET',
      redirect: 'follow'
    };
  }

  async mostPopular() {
    console.log('key', this.key)

    try {
      const response = await fetch(
        `https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&maxResults=25&type=video&key=${this.key}`,
        this.requestOptions
      );
      const result_1 = await response.json();
      return result_1.items;
    } catch (error) {
      return console.log('error', error);
    }
  }

  async search(query) {
    const url = 'https://www.googleapis.com/youtube/v3/search'

    try {
      const response = await fetch(
        `${url}?part=snippet&q=${query}&maxResults=25&type=video&key=${this.key}`,
        this.requestOptions
      );
      const result_1 = await response.json();
      return result_1.items;
    } catch (error) {
      return console.log('error', error);
    }
  }
}

export default Youtube;

 


2. src/index.js 에서 인스턴스 새로 만들어서 선언해두기 (각 페이지에서 하는것은 좋지 않음)

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Youtube from './services/youtube';

const youtube = new Youtube(process.env.REACT_APP_YOTUBE_API_KEY);

ReactDOM.render(
  <React.StrictMode>
    <App youtube={youtube} />
  </React.StrictMode>,
  document.getElementById('root')
);
youtube 를 선언해서 그 안에 services에서 받아온 Youtube class를 새로 만들어서 App 컴포넌트로 넘겨준다.
여기서 new Youtube() 안의 apikey부분은 env 파일을 만들어서 따로 관리한다. (관련 내용은 아래 url 참고)

 

 

[React] 깃허브에 올리면 안되는 apikey 설정하는법

이슈 api key는 소스작업시에 올라가면 안되는 아이므로 꽁꽁 숨겨두어야하는데 어떻게 숨겨두는지 강의를 통해 알았다. 해결 1. 루트폴더에 .env파일을 만든다. 2. .env 파일안에 대문자로 이루어진

joannashin.tistory.com

 


3. src/app.jsx 에서 (필요한 페이지에서 ) 넘긴 youtube 가져다가 사용하기

import './App.css';
import React, {useState, useEffect} from 'react';
import VideoList from './components/video_list/index.jsx';
import Search from './components/search/index.jsx';

function App({ youtube }) {
  const [ videos, setVideos ] = useState([]);
  const handleSearch = (query) => {
    youtube
      .search(query)
      .then(items => setVideos(items));
  }

  useEffect(() => {
    youtube
      .mostPopular()
      .then(items => setVideos(items));
  }, []);
  
  return (
    <React.Fragment>
      <Search handleSearch={handleSearch}></Search>
      <VideoList videos={videos}></VideoList>
    </React.Fragment>
  );
}

export default App;

 

반응형