Project
첫 프로젝트가 끝난 후 하루 이틀 정도 재정비 시간을 가지고 두 번째 프로젝트를 시작하게 되었다. 이번 사이트는 저번 블로그 글에서 소개하였던 영상 스트리밍을 이용하여 넷플릭스 사이트를 만들어 보았다. 처음 사이트를 살펴보려고 했을 때 결제를 해야 지만 볼 수 있어서 첫날 당황했던 기억이 있었다. 결제 후 본 넷플릭스 사이트는 전체적인 어려운 레이아웃은 없었지만 모달창을 어떻게 만들지 동영상을 어떻게 넣고 실행시킬지에 대한 고민이 많았다. 첫 프로젝트와 다르게 두번째 프로젝트는 어떠한 소통을 해야 하는지 어떤 것을 협의 해야 될지 경험을 통해 알고 있었기 때문에 소통에 전혀 어려움이 없었다.새로운 기술이었는데도 포기하지 않고 끝까지 멋진 프로젝트를 마무리해준 팀원들에게 감사하며 자세한 기술 내용과 어떤 소통을 했는지 블로그 밑에서 설명을 자세히 하려고 한다.
이번 프로젝트에서는 React Hooks
와 styled-component
를 이용하여 클론사이트를 만들어 보았다. 저번 프로젝트에서는 리액트를 왜 사용하는가에 대해 느꼈다면 이번 프로젝트에서는 hooks
에 편리함과 styled-component
의 사용이유에 대해 느끼게 되었다. hooks
를 사용함으로써 클래스형 컴포넌트와 비교하면 선언하기가 간편하였고 메모리 할당이 많은 스트리밍에는 메모리 자원을 클래스형 컴포넌트 보다는 덜 사용하는 hooks의 기술이 적합하였고 styled-component
를 통해 props
로 직접 스타일들에 관여할 수 있는 부분은 엄청나게 편리하였다.
💡 하이라이트 => 내가 구현한 기능 & 참여한 기능
Main 페이지 구성
위시리스트 모달창 구현
Main 창 setInterval를 활용한 동영상 재생
Nav bar 구현
footer 구현
Nav bar 를 이용한 동적 라우팅 구현
스트리밍 구현
modal창 스트리밍 연결
access token를 활용한 Router 이동
setInterval을 통하여 main 화면을 전환했다.
main 화면을 setInterval로 변환시키고 스트리밍
을 통해 동영상을 보여주고 있다.
Network 탭을 통하여 확인해보면 데이터가 차례차례 들어오는 것을 볼 수 있다.
스트리밍 같은 경우 기억에 남을 코드
에 적을 예정이지만 정말 생소한 기술이여서 많은 어려움이 있었다.
네브바를 통한 라우터 구현은 id 값이 아닌 카테고리별
로 묶여 있어 어떻게 연결해야 할지 고민이 많았고 TV 프로그램과 영화는 같은 component를
사용하고 있어 re-render
가 안되는 문제가 있었다. 이 부분도 밑에서 코드로 설명을 하려고 한다.
이 부분도 생각보다는 쉽지 않은 작업이었다. 밑에 정보와 리스트 같은 경우에는 팀원 분인 동희님이 연결하였고 나는 스트리밍을 연결하였는데 데이터가 id 값이 아닌 한 번 더 들어간 detail_id로 end-point가 되어있었다.
이 부분 같은 경우에는 Optional chaining을 통해 마우스를 올리면 Detail_id 값을 찾고 마우스를 클릭하면 Detail_id 값이 fetch 되도록 만들었다. 자세한 부분은 git hub통해 볼 수 있다.
이 부분도 main창과 동일한 형식으로 만들었고 스트리밍
으로 만들었다.
nav bar 변환같은 경우 scrolls시 최대한 render
덜 될 수 있도록 노력하였다.
이 부분은 팀원 모두의 코드가 들어가있다. 모달창은 어떻게 다시 열고 라우터는 어떻게 연결할지에 대한 고민이 많이 있었다.
넷플릭스에 들어갔을 때 제일 먼저 한 생각이 동영상 데이터로 어떻게 받아야 할지 였다. 처음에는 유튜브 링크로 영화를 연결하게 하면 되지 않겠느냐는 생각을 했는데 그게 과연 동영상 서비스를 만드는 취지에 맞을까? 라는 생각이 들었다. 결국, 팀원들 협의한 끝에 스트리밍 서비스를 만들기로 하였다.
백엔드 팀원인 준영님과 어떻게 데이터를 받아야 하고 어떤 방식으로 프론트에서 요청을 주면 되는지 정말 많이 고민하였고 바이트 단위로 데이터
를 쪼개서 프론트에 주면 그것을 프론트에서는 Codec 파일과 mediasource 통해 바이트 단위 데이터
를 받고 요청을 보낼 때는 바이트 단위로 요청을 보내기로 하였다. 이 부분에서도 팀원들과 구글링도 많이 해보면서 하루 이상이 걸렸던 거 같다.
useEffect(() => {
const video = videoRef.current;
const chunks = 30;
const streamingFile = "url";
const mediaSource = new MediaSource();
mediaSource.addEventListener("sourceopen", e => {
const ms = mediaSource.addSourceBuffer(
'video/mp4; codecs="avc1.4D4001,mp4a.40.2"'
);
ms.addEventListener(
"updateend",
() => {
if (i === chunks - 1) {
return;
}
loadChunk(++i);
},
false
);
loadChunk(0);
});
mediaSource.addEventListener("sourceclose", () => {
console.log("ended");
});
video.src = window.URL.createObjectURL(mediaSource);
일단 chunks
로 파일을 데이터
을 얼마나 주고받을 것인지 정한 후 mediasource 객체를 통해 받은 데이터를 열어준다. 여기서 addSourceBuffer를 사용하는데 여기서 어떤 형식의 파일인지 Codec 정보는 무엇인지 넣어줄 수 있다. (이 부분은 backend와 소통을 잘 해야 하고 codecs 같은 경우에는 사이트에 호환되는 codecs를 넣어주어야 한다.)
후에는 addEvevenListener를 사용하고 loadChunk를 함수를 통해 chunks를 요청한다. loadChunk 함수 같은 경우에는 이어서 설명을 하겠다.
const loadChunk = async i => {
const start = 0;
const length = 30000000;
const chunkSize = Math.ceil(length / chunks);
const startByte = parseInt(start + chunkSize * i);
const range =
"bytes=" + start + chunkSize * i + "-" + (startByte + chunkSize - 1);
const res = await fetch(streamingFile, {
headers: {
Range: range,
},
}).then(res => res.arrayBuffer());
mediaSource.sourceBuffers[0].appendBuffer(new Uint8Array(res));
};
loadChunk
같은 경우에는 start를 정해주고 chunkSize와 startByte를 정해주고 요청을 보낼 range를 보내준다. 그 후 데이터를 arrayBuffer를 통해 바이트 데이터를 받고 sourceBuffer를 통해 mediasource를 열어준다.
네브바를 통한 라우터 구현은 TV 프로그램과 영화를 한 component
를 사용하여 data fetch
를 다시 시켜줘야 하는데 그러지 못한 문제가 있었다.
useEffect(() => {
if (
match.params.genreCategory === "drama" ||
match.params.genreCategory === "movie"
) {
handleFetch(
`${props.url}&category=${match.params.genreCategory}`,
updateCardsData
);
} else if (match.params.genreCategory === undefined) {
handleFetch(`${props.url}`, updateCardsData);
}
}, [match.params.genreCategory]);
이 부분은 if을 통해 math.params 별로 rendering을 시켜 해결하였다. fetch 코드로 if 문을 사용할 수 있다는 부분에서 인상 깊었던 코드였다.
프론트엔드, 백엔드 2차 프로젝트 때문인지 좋은 팀원들 덕분인지 정말 원활한 소통이 이루어졌다. 애자일 하게 가져온 계획 덕분에 정말 일정에 맞춰 딱 끝낼 수 있었다! 1차 프로젝트 때 아쉬웠던 소통 방식은 2차에서 전부 풀었던 거 같다.
그리고 백엔드 팀원 중에 한 말 중에 기억나는 말이 있었다. frontend 팀원 분들이 이번에는 mock 데이터를 만들게 하지 않을 거라는 말이었다. 이러한 부분에서 프론트엔드팀에 대한 배려 & 시간 낭비가 없었고 좀 더 디테일한 기능 & 생소한 기술을 완성할 수 있었다. 그리고 첫 의사소통을 어떻게 해야 하는지 1차에서 초반에 못 만들었던 프로트와 백엔드의 연결고리를 잘 만들었던 부분은 나 자신에게 칭찬을 해줘야 될 부분인 거 같다.
이번 팀 프로젝트는 rebase
을 사용하였다. 처음으로 rebase를 사용하여 보았는데 이번에는 두려움보다는 실수해도 지금 해보자는 마음이 커서 혼자 rebase merge도 시켜보고 팀원들과 함께 conflict도 잡으며 git에 대해 두려워하기보다는 자신감도 생겼고 git 지식 부분에서도 많이 성장한 것 같다.
이번에 시간관리 면에서 주요기능만 계획을 잡았고 디테일 하게 팀원들과 업무분배
를 하였다. 자신의 업무가 끝나면 다른 일을 먼저 하는 거 보다는 팀원들의 그날 일을 도와주었고 프로젝트 기간을 앞당겨서 추가기능까지 구현할 수 있었다. 2주,3주는 프로젝트 하기에 정말 짧은 시간이라고 생각한다. 이런 짧은 프로젝트일 수록 철저한 시간관리가 필요한 거 같다.
넷플릭스 같은 경우 시간별로 필요한 바이트 단위 데이터를 랜덤하게 불러온다. 이 부분은 핑계일 수도 있겠지만 주어진 시간이 너무 조금밖에 있지 않아 구현하지 못하였다. 추후 기회가 된다면 영상 스트리밍에 관심이 있는 분들과 좀 더 깊이 있는 대화를 통하여 리팩토링 하고 싶다. (관심이 있는 분들 댓글 남겨 주세요!)
추석 포함 3주라는 짧은 시간 & 생소한 기술에 다들 정신이 없었을 만한 프로젝트였지만 웃음이 끊이질 않았던 프로젝트였다.
백엔드 팀원 분들은 프런트에 대한 배려가 컸고 프론트 팀원분들은 생소한 기술에 포기하고 싶은 순간이 여럿이었던 나에게 응원을 보내주고 "완성을 못 해도 되니깐 할 수 있는데 까지 하자" 라는 말은 나에게 너무 큰 응원이 되었다. 발표가 끝나고는 뿌듯한 마음도 있었지만, 팀원들과 다음 프로젝트가 없다는 것이 너무 아쉬웠다. 이번 프로젝트를 통해 다시 한 번 느낀 것은 개발 역량도 물론 중요하겠지만, 팀원들과의 소통 그리고 시너지라는 것이 정말 중요하다는 것을 느꼈다. 그리고 이번 프로젝트를 통해 나에게 한 마디 해주고 싶은 말은 "고통은 일시적이지만 포기는 평생 갑니다." 라는 말이다. 이번 프로젝트 하면서 잠을 줄여가면서 프로젝트를 하였다. 이 기간 동안 몸은 힘들었지만 내가 포기했다면 후에는 그게 더 힘들었을 거 같다는 생각이 든다.
유튜브 링크
잘봤습니다!