# TIL-20210326
youtube clone coding (toy)
# getting start
- Storybook: 개발 과정 눈으로 확인하기
- React Developer Tools
# story book
Storybook은 react, vue, angular등을 통해 UI컴포넌트를 독립적으로 테스트 해볼수 있는 오픈소스 툴입니다.
체계적이고 효과적으로 UI 컴포넌트를 만들 수 있다.
테스트 케이스 작성과 비슷한 방식으로 구현할 수 있고,
컴포넌트 단위로 여러가지 상태에 따라 보이는 화면을 보여주기 때문에,
전체적인 플로우에 상관 없이 해당 화면을 확인해 볼 수 있다.
# React Developer Tools
리엑트 컴포넌트를 디버깅하는데 도움을 주는 크롬 브라우저 익스텐션이다. 컴포넌트 트리와 state변화를 쉽게 알아 볼 수 있다.
# BM Requirements
# 1. VideoList
및 VideoListEntry
컴포넌트 만들기
- 기본 상태
// 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>
//..
해야할 일
fakeData
라는 임의의 비디오 리스트를 통해 list를 만들기- 각 비디오 데이터를 받은
VideoListEntry
가 필요한 정보를 컴포넌트에서 보여주기.
다음과 같이 구현했다. 순서는 역순으로 진행했다.
VideoListEntry
가 보여줘야하는 화면을 만들기
- 각
VideoListEntry
는 하나의 데이터를 받는다 => fakeData[0]으로 가정 할 수 있다.
fakeData에서 필요한 요소를 찾아 넣어준다.
img.src
:fakeData[0].snippet.thumbnails.default.url
title
:fakeData[0].snippet.title
description
:fakeData[0].snippet.description
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>
// ...
)
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
를 적절한 위치에 뿌려주면 된다.
- 정해진 데이터를 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>
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
에 표시되어야 하는 비디오의 정보
- 핸들러 만들기 핸들러는 다음을 만족해야 한다.
App
에 있는state
를 변경해야 한다. -> 핸들러는 App에서 선언되어야 한다.VideoListEntry
의props
인video
데이터를 알 수 있어야 한다. -> 핸들러는 Entry에서 동작해야 한다.
- App에 핸들러 붙이기
// App.js
constructor() {
// ...
this.playerHandler = this.playerHandler.bind(this)
}
playerHandler() {
this.setState((state) => {
return {
...state,
currentVideo: '???'
}
})
}
// ...
App Components
에서 palyerHandler
를 생성,
this.setState
를 통해 App
의 스테이트를 변경하도록 설정 해주었다.
'???'
는 현재 어떤 값이 올지 모르기 때문에 비워 두었다.
handler
는VidoeListEntry
에서 어떤 값을 가지고 와야 한다.handler
가 어떠한 변수를 받아서state
를 변경해주면 될 것이라 생각 했다.
playerHandler(video) {
this.setState((state) => {
return {
...state,
currentVideo: video
}
})
}
일단 핸들러에 비디오라고 하는 정보를 넣어줄 것이고,
해당 인자를 통해서 state
의 currentVideo
의 값을 변경해주도록 했다.
이제 핸들러를 자식 컴포넌트에게 전달하여 비디오를 건내줄 수 있도록 해준다.
// 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
까지 올라올 수 있게 된다.
# 결론
간단한 방법을 통해 스테이트를 다른 컴포넌트에서 변경할 수 있는 방법을 알아 보았다.
이런식으로 스테이트를 올려서 적용하는게 가능하다.