# TIL-20210326

youtube clone coding (toy)

# getting start

  1. Storybook: 개발 과정 눈으로 확인하기
  2. React Developer Tools

# story book

Storybook은 react, vue, angular등을 통해 UI컴포넌트를 독립적으로 테스트 해볼수 있는 오픈소스 툴입니다.
체계적이고 효과적으로 UI 컴포넌트를 만들 수 있다.

테스트 케이스 작성과 비슷한 방식으로 구현할 수 있고,
컴포넌트 단위로 여러가지 상태에 따라 보이는 화면을 보여주기 때문에, 전체적인 플로우에 상관 없이 해당 화면을 확인해 볼 수 있다.

# React Developer Tools

리엑트 컴포넌트를 디버깅하는데 도움을 주는 크롬 브라우저 익스텐션이다. 컴포넌트 트리와 state변화를 쉽게 알아 볼 수 있다.

# BM Requirements

# 1. VideoListVideoListEntry 컴포넌트 만들기

  • 기본 상태
// VideoList.js
import {fakeData} from './components/__test__/fakeData'
// render
 <div>
   <VideosListEntry />
   <VideosListEntry />
   <VideosListEntry />
   <VideosListEntry />
   <VideosListEntry />
   <VideosListEntry />
 </div>
// VideoListEntry.js
//  thumbnails, title, description 하드코딩
 //...
   <img className="media" src="https://i.ytimg.com/vi/dQw4w9WgXcQ/default.jpg" alt="" />
 //...
   <div className="title">Video Title</div>
   <div className="detail">Video Description</div>
 //..

해야할 일

  1. fakeData라는 임의의 비디오 리스트를 통해 list를 만들기
  2. 각 비디오 데이터를 받은 VideoListEntry가 필요한 정보를 컴포넌트에서 보여주기.

다음과 같이 구현했다. 순서는 역순으로 진행했다.

  • VideoListEntry가 보여줘야하는 화면을 만들기
  1. VideoListEntry는 하나의 데이터를 받는다 => fakeData[0]으로 가정 할 수 있다.

fakeData에서 필요한 요소를 찾아 넣어준다.

  • img.src: fakeData[0].snippet.thumbnails.default.url
  • title: fakeData[0].snippet.title
  • description:fakeData[0].snippet.description
  1. fakeData[0]props.video를 비구조화 할당으로 해당 부분을 쉽게 가지고 온다.
const VideoListEntry = ({video}) => (
  // ...
  <img src={video.snippet.thumbnails.default.url} />
  <div className="title">{video.snippet.title}</div>
  <div className="description">{video.snippet.description}</div>
  // ...
)

  1. VideoList에서 VidoeListEntry에 각각 데이터 넘겨주기
  <div>
    {fakeData.map(video => <VideosListEntry video={video} />)
  </div>

매핑을 통해서 구현

# 2. 동적 VideoPlayer 컴포넌트 만들기

  • 기존 형태
// VideoPlayer.js
<div>
  <iframe src='영상 url' ></iframe>
  <div className="title">정해진 타이틀</div>
  <div className="description">정해진 디스크립션</div>
</div>
  • 해야할 일 아직 초기에 어떤 값을 줄지 정하진 않았지만, 플레이어 부분에서는 비디오에 대한 데이터 하나가 들어올 것이라고 확정할 수 있다.
    그럼 정해진 데이터가 들어왔을 때, 우리는 화면에 표시 할 수 있도록 props를 적절한 위치에 뿌려주면 된다.
  1. 정해진 데이터를 props로 받도록 한다.
const VideoPlayer = ({video}) => (
   // VideoPlayer JSX
)

props에서 video라는 키(key)에 데이터를 받아 온다. 2) props에서 필요한 데이터를 뿌려준다.

// VideoPlayer.js
 const link = `...${video.id.videoId}?autoplay=1`
// JSX
 <div>
   <h3>{video.snippet.title}</h3>
   <div>{video.snippet.description}</div>
 </div>
  1. VideoPlayer를 사용하는 App컴포넌트에서 데이터를 넘겨준다.
 // App.js
 <VideoPlayer video={fakeData[0]}>

# 3. VideoList와 VideoPlayer를 연결하기

기존에는 컴포넌트마다 데이터에 접근하는 형식이었지만,
player, Entry는 각각 부모 컴포넌트로 부터 데이터를 받는 형식으로 변경했다.

테스트 케이스상 App에서 전체적인 스테이트를 가지고 있고 해당 스테이트가 player, list를 구성하게 된다.

  • 구조 변경
// App.js
import {fakeData} from 'fakeData'
// funcitonal => class Components
class App extends React.Compnents {
  constructor() {
    super()
    this.state = {
      videos: fakeData,
      currentVideo: null
    }
  }
  render() {
    return (
      <div>
       {/* ... */}
       <VideoPlayer video={this.state.currentVideo} />
       <VideoList videos={this.state.videos} />
       {/* ... */}
     </div>
    )
  }
}
 // render
 
// Videoplayer.js
 const VideoPlayer = ({video}) => {
   // ... components
 }
// VideosList.js
 const VideoList = ({videos}) => {
   // ... components
   {videos.map(video => <VideoListEntry key={video.etag} video={video} />)}
 }
// VideoListEntry.js
 const VideoListEntry = ({video}) => {
   // ... components
 }  

테스트 케이스에 맞게 props내부에서의 키 값도 설정 해주었다.
App에서의 state는 2개를 설정 해주었으며, 각각
videos: 모든 비디오 리스트
currentVideo: VideoPlayer에 표시되어야 하는 비디오의 정보

  • 핸들러 만들기 핸들러는 다음을 만족해야 한다.
  1. App에 있는 state를 변경해야 한다. -> 핸들러는 App에서 선언되어야 한다.
  2. VideoListEntrypropsvideo데이터를 알 수 있어야 한다. -> 핸들러는 Entry에서 동작해야 한다.
  • App에 핸들러 붙이기
  // App.js
  constructor() {
    // ...
    this.playerHandler = this.playerHandler.bind(this)
  }

  playerHandler() {
    this.setState((state) => {
      return {
        ...state,
        currentVideo: '???'
      }
    })
  }
  // ...

App Components에서 palyerHandler를 생성,
this.setState를 통해 App의 스테이트를 변경하도록 설정 해주었다.

'???'는 현재 어떤 값이 올지 모르기 때문에 비워 두었다.

  • handlerVidoeListEntry에서 어떤 값을 가지고 와야 한다. handler가 어떠한 변수를 받아서 state를 변경해주면 될 것이라 생각 했다.
playerHandler(video) {
  this.setState((state) => {
    return {
      ...state,
      currentVideo: video
    }
  })
}

일단 핸들러에 비디오라고 하는 정보를 넣어줄 것이고,
해당 인자를 통해서 statecurrentVideo의 값을 변경해주도록 했다.

이제 핸들러를 자식 컴포넌트에게 전달하여 비디오를 건내줄 수 있도록 해준다.

 // App.js
 <VideoList videos={videos} handler={this.playerHandler}/>

 // VideoList.js
 const VideoList = ({videos, handler}) => {
   // ....
   {videos.map((video) => <VideoListEntry key={video.etag} video={video} handler={handler}/>)}
 }

 // VideoListEntry.js
 const VideoListEntry = ({ video, handler}) => {
   // ...
   <div className="title" onClick={() => handler(video)}>{video.....}</div>
   // ...
 }

App에서 정의된 핸들러는 다음 단계로 Entry에서 스테이트를 올려 받을 수 있게 된다. 함수 데이터 내리기
App => playerHandler => VideoList => VideoListEntry

스테이트 올리기
VideoListEntry => VideoList => playerHandler => App

실제로 중간에 거쳐가는 VideoList에서 컴포넌트의 변화는 생기지 않지만 playerHandler에 의해서 VideoListEntry의 데이터가 App까지 올라올 수 있게 된다.

# 결론

간단한 방법을 통해 스테이트를 다른 컴포넌트에서 변경할 수 있는 방법을 알아 보았다.
이런식으로 스테이트를 올려서 적용하는게 가능하다.