Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6조 과제 제출 (강해경, 강현주, 김지원, 안가을) #7

Open
wants to merge 119 commits into
base: main
Choose a base branch
from

Conversation

iziz9
Copy link

@iziz9 iziz9 commented Jan 20, 2023

리액트 토이프로젝트(6조) - 유튜브 클론코딩

프로젝트 소개

✨배포사이트 - 🔗 6tube - Toy Project


  • 유튜브 클론코딩 사이트로 메인/검색/영상상세/채널상세 페이지로 구성되어 있습니다.
  • 메인페이지에서는 핫트렌드 영상 및 각 태그에 맞는 영상 목록을 불러올 수 있습니다.
  • 헤더에서 검색어를 입력하면 검색된 영상의 목록을 불러옵니다.
  • 검색페이지에서는 무한 스크롤 기능을 추가하였습니다.
  • 영상 상세페이지에서는 해당영상 플레이가 가능하며, 관련영상 목록과 댓글을 확인할 수 있습니다.
  • 채널명 클릭시 채널에 대한 정보와 해당영상 채널의 목록을 불러오는 채널 상세 페이지로 이동할 수 있습니다.
  • 영상 목록의 썸네일 이미지에 마우스를 올릴 경우 영상이 작은화면으로 플레이되어 미리보기를 할 수 있습니다.
  • 영상 목록에서 채널등의 정보 호버시 툴팁을 확인할 수 있습니다.
  • 각 페이지는 화면크기에 따라 반응형으로 작업하였습니다.

👩‍💻 팀원 소개

강해경 강현주 김지원 안가을

⚙기술 스택



📆 과제 기간

  • 2023.1. 16 ~ 2022. 1. 20.


📌 작업영역 및 구현 기능 설명

강해경
  • 헤더와 사이드바
    반응형 디자인에 초점을 맞춰 헤더와 사이드바를 구현했습니다.
  • 상세페이지 동영상 플레이 구간
    iframe태그를 이용해 해당영상을 플레이하고 채널과 영상에 대한 정보를 화면에 출력했습니다.
  • 메인페이지 태그별 버튼
    메인페이지에서 해당 태그별 영상목록을 불러올 수 있는 버튼을 구현했습니다. '전체'와 '최근업로드된동영상' 클릭시에는 핫트렌드 영상을 불러오고 이 외 버튼은 해당내용을 검색했을 경우 나오는 영상목록을 출력해 줍니다.

강현주
  • 상세페이지 관련 동영상 사이드바
    현재 재생중인 영상과 관련된 영상 리스트가 출력되도록 구현했습니다.
  • 상세페이지 댓글 섹션
    현재 재생중인 영상의 댓글과 작성자 정보가 출력되도록 구현했습니다.
김지원
  • 메인화면
    반응형 디자인에 초점을 맞춰 영상 목록을 구현했습니다. 썸네일 이미지에 마우스가 호버될 경우 영상이 자동재생됩니다.
  • 채널 페이지
    채널 타이틀을 클릭할 경우 채널의 상세 페이지로 이동합니다. 주어진 API를 이용해 채널 정보, 해당 채널의 플레이리스트를 보여주고 있습니다.
안가을
  • 검색 페이지
    메인에서 키워드 검색 시 검색결과 페이지로 이동합니다.

💦 어려웠던 점

  • 처음 세팅(같은 작업환경을 만드는 부분)이 어려웠습니다.

  • API 요청의 할당량이 정해져 있어 원하는 만큼 테스트를 해볼 수 없었던 점이 어려웠습니다.

  • 영상목록을 불러오는 api가 여러가지이고 응답데이터 양식과 뎁스가 가지각색이라 혼란스러웠습니다.

  • 페이지에 다른 컴포넌트가 들어오거나 구조의 변경이 생기는 경우 다시 그 구조에 맞게 반응형 스타일로 변경시켜주어야 하는 부분이 어려웠습니다.

  • 영상플레이 영역의 크기를 조절하고 반응형으로 구현하는 것이 어려웠습니다.
    .



💡 질문사항 및 미해결 에러

  • 영상과 댓글정보를 불러올 때 일부 특수문자가 html 태그나 entity로 출력되는 현상
  • 같은 API 요청이 여러 번 전송되는 문제
  • 메인화면에서 썸네일을 호버했을 때 자동재생 되도록 구현한 HoverVideo 컴포넌트를 구현하는 것이 어려웠습니다. 지금처럼 이벤트 핸들러를 작성하는 것이 맞는건지 의문이 듭니다 .. 가끔씩 일부 영상이 setTimeOut이 작동하지 않는 오류가 나기도 합니다. HoverVideo가 오류가 나지 않도록 확실한 조건을 걸고 싶은데 어떤 조건을 걸어야 할지 조언을 구하고 싶습니다

iziz9 and others added 30 commits January 16, 2023 14:18
jiwon) feat: create VideoCard component
create header, nav ui
🏗️ Edit: router structure, Delete: Router.jsx
Complete related video bar layout
Comment on lines +32 to +43
return (
<div className='hidden md:flex p-4 gap-2'>
<Button buttonName={'전체'} clicked={clicked} />
<Button buttonName={'실시간'} clicked={clicked} />
<Button buttonName={'음악'} clicked={clicked} />
<Button buttonName={'뉴스'} clicked={clicked} />
<Button buttonName={'믹스'} clicked={clicked} />
<Button buttonName={'게임'} clicked={clicked} />
<Button buttonName={'요리'} clicked={clicked} />
<Button buttonName={'축구'} clicked={clicked} />
<Button buttonName={'최근에 업로드된 동영상'} clicked={clicked} />
</div>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가독성을 위해 반복문을 통해서 Button을 렌더링하도록 바꾸는것을 추천합니다.

Comment on lines +4 to +30
const Buttons = ({ setVideos, setType }) => {
const [clicked, setClicked] = useState('전체');

const Button = ({ buttonName, clicked }) => {
if (clicked === buttonName)
return (
<button className='bg-white h-8 text-xs px-3 rounded-xl text-black'>{buttonName}</button>
);
return (
<button
className='bg-[#272727] hover:bg-[#3d3d3d] h-8 text-xs px-3 rounded-xl text-white'
onClick={() => {
setClicked(buttonName);
if (buttonName === '전체' || buttonName === '최근에 업로드된 동영상') {
setType('');
getMostPopularVideos().then((data) => {
setVideos(data);
});
}
// setType('search');
getSearchVideos(buttonName, '').then((data) => setVideos(data.items));
}}
>
{buttonName}
</button>
);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

video 값의 변경을 Buttons.jsx에서 진행하고 있어 필요없는 결합이 생겼습니다.
이는 추후 Button 컴포넌트를 변경해야하거나 로직이 변경될 경우 수정이 어렵게 만드는 요인으로 작용합니다.

Button의 역할은 아래 두가지만 가지고 있는것이 맞습니다.

  1. 선택된 type에 맞추어 버튼을 렌더링하는것
  2. 클릭할 경우 type을 변경시켜주는것

따라서 아래와 같은 순서로 분리하는것을 추천합니다.

  1. Home.jsx에서 useEffect를 통하여 type이 변경될 경우
    getMostPopularVideos 또는 getSearchVideos가 실행되도록 합니다.
  2. Buttons.jsx의 경우 클릭시 setType을 변경하도록 수정합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가로 if(clicked === buttonName)를 통하여 분기처리하기보다는
변수를 한개 추가하여 아래와 같이 변경하는것을 추천합니다.
(위 코멘트를 참고하여 click과 관련된 동작을 고려하여 코드를 수정해야 정상 작동합니다.)

const buttonClass = (clicked === buttonName) ? "bg-white" : "bg-[#272727] hover:bg-[#3d3d3d]";
return (
    <button className=`${buttonClass}  h-8 text-xs px-3 rounded-xl text-black`>{buttonName}</button>
);

Comment on lines +11 to +15
useEffect(() => {
getMostPopularVideos().then((data) => {
setVideos(data);
});
}, []);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/KDT1-FE/KDT-Youtube/pull/7/files#r1084010614
를 참고하여 수정을 진행하시는것을 추천합니다.

Comment on lines +62 to +63
part: 'snippet',
part: 'contentDetails',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[youtube API 문서]

params를 object 형식으로 여러번 선언해줄 경우 가장 아래있는 part만 정상적으로 전달됩니다.
이는 object key는 유일해야하기 때문에 발생되는 현상입니다.
문서에 따르면 comma(,)를 통해 구분지어 여러 값을 전달할 수 있다 나와있으므로
해당 코드를 제외하고도 이와 같은 형식을 가지고 있는 모든 코드를 아래와 같이 수정하는것을 추천합니다.
(Dev tool의 Network 탭을 확인해보시거나 console로 결과를 확인해보면 전달되는 값이 다른것을 확인해보실 수 있습니다.)

Suggested change
part: 'snippet',
part: 'contentDetails',
part: 'snippet, contentDetails',

)}
</div>
{video && (
<VideoCardInfo video={video.snippet} videoId={videoId} chVideoId={chVideoId} type={type} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chVideoId 값은 사용하지 않는데 명시되어있는것 같습니다.

Comment on lines +5 to +12

const VideoDuration = ({ videoId }) => {
const [videoLength, setVideoLength] = useState('');

useEffect(() => {
getVideoDuration(videoId).then((data) => setVideoLength(data));
}, []);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

videoId가 object일 경우 분기 처리 필요 (VideoCardInfo.jsx처럼)
또는 reqeust.js에서 미리 데이터를 나눠주는것도 좋을것 같음

</div>
</div>
<div>
<div className='text-sm mt-1 w-fit'>{text}</div>
Copy link

@KDT-sihyeon KDT-sihyeon Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

영상과 댓글정보를 불러올 때 일부 특수문자가 html 태그나 entity로 출력되는 현상을 해결하기 위해서는
링크 와 같은 방식으로 렌더링을 진행해야합니다.
이는 기본 문법의 경우 innerText로 변경되고
위 링크에서 설명하는 방식은 innerHTML로 변경되기에 제대로 변경사항이 적용되는것입니다.
다만, 링크에도 나와있듯이 이는 보안에 취약하므로 해당 텍스트의 값을 전달하는 주체가 안전한지 판단한 후 사용하는것을 추천합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

youtube api를 살펴보니
textDisplay 대신 textOriginal를 사용하면 위 방식 없이도 해결이 가능하네요

Comment on lines +15 to +19
getChannelData(channelId).then((data) => setChannelData(data));
getPlayListId(channelId)
.then((data) => getPlayListItems(data))
.then((data) => setPlayListItems(data));
getSubscriberInfo(channelId).then((data) => setSubscriberCount(data));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여러개의 비동기 요청을 순서와 상관없이 보내야할 경우에는
Promise.all을 사용하는것이 보다 빠릅니다.

@KDT-sihyeon
Copy link

💦 어려웠던 점 코멘트

영상목록을 불러오는 api가 여러가지이고 응답데이터 양식과 뎁스가 가지각색이라 혼란스러웠습니다.
유튜브에서 제공해주는것과 같은 서드파티 API의 경우 활용성을 높이고자 데이터를 리치하게 보내줍니다.
따라서 이를 잘못 운용하면 코드가 복잡해져 가독성이 떨어지게 됩니다.
따라서 서드파티 API를 활용하는 개발에서는 초기 서드파티 API 문서를 기반으로
필요한 데이터가 무엇인지 어디서 받아올지를 정리한 후 시작해야합니다.
이후 각 용도별로 다시한번 API를 재구성한다는 생각으로 함수를 선언하면 보다 깔끔하게 관리할 수 있습니다.

페이지에 다른 컴포넌트가 들어오거나 구조의 변경이 생기는 경우 다시 그 구조에 맞게 반응형 스타일로 변경시켜주어야 하는 부분이 어려웠습니다.
반응형으로 페이지를 만들 때 무조건적으로 컴포넌트를 재사용할 필요는 없습니다.
사실 사용자의 화면 크기는 잘 변하지 않습니다.
따라서 화면 크기별로 맞춤형 컴포넌트를 만든 후 조건부로 렌더링해주게 되면 보다 쉽게 반응형 웹 페이지를 만들 수 있습니다.

다만, 무조건 해당 방식을 사용한다면 동일한 기능을 하는 컴포넌트가 여러개로 분산되어 관리가 오히려 어려워질 수 있습니다.
따라서 용도와 목적에 맞추어 병행하여 사용하는것을 추천합니다.

이 외 다른 어려웠던 점의 경우 모두 잘 해결되어 있어 코멘트를 남기지 않았습니다.

@KDT-sihyeon
Copy link

💡 질문사항 및 미해결 에러

영상과 댓글정보를 불러올 때 일부 특수문자가 html 태그나 entity로 출력되는 현상
답변 코멘트

같은 API 요청이 여러 번 전송되는 문제
중복된 요청이 전송되는 케이스를 발견하지 못했습니다.
해당 에러 재현 방법 혹은 발생하는 컴포넌트등을 알려주시면 답변 드리도록하겠습니다.

메인화면에서 썸네일을 호버했을 때 자동재생 ... 확실한 조건을 걸고 싶은데 어떤 조건을 걸어야 할지 조언을 구하고 싶습니다
setTimeout이 작동하지 않는 오류가 정확히 무엇인지 모르겠으나 가끔 hover되자마자 영상이 재생되는것을 말하는것이라면
우선 마우스 이벤트와 setTimeout을 사용하는것은 실제 유튜브 동작 방식과 동일하므로 해당 방식처럼 구현하시면 됩니다.
해당 문제의 원인을 설명하기 앞서 해결 방법부터 먼저 설명하자면 timer 변수를 useState로 선언하면됩니다.
원인은 변수값이 변경됨에 따라 HoverVideo 컴포넌트가 새롭게 render될 때(reRender)
let timer로 선언된 변수는 완전 새롭게 생성되기 때문입니다. 따라서 clearTimeout이 정상적으로 동작하지 못하여
isHover 값이 true로 남아 있는 상황이 생길 수 있습니다.
(버그를 재현시켜보자면 HoverVideo가 켜지려고 할 때 마우스를 나갔다 들어왔다 한 번 반복하면 됩니다.)

@KDT-sihyeon
Copy link

해당 프로젝트의 경우 지난번과 비교하였을 때 너무 코드가 깔끔해져서 놀랐습니다.
덕분에 수월하게 리뷰 진행할 수 있었습니다. 👍
이번 과제도 진행하시느라 수고 많으셨습니다.

P.S.
검색페이지에 무한 스크롤 기능이 동작하지 않고 있습니다. 코드를 살펴보면 주석 처리 되어있던데
2차 리뷰 제출 전에는 해당 내용 확인 부탁드리겠습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants