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주차] Team 페달지니 윤영준 & 박지수 미션 제출합니다. #15

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

Conversation

jsomnium
Copy link

@jsomnium jsomnium commented Nov 16, 2024

배포 링크 ⭐

느낀점 🥬

이번 주차는 이래저래 에러 수정하느라 꽤나 시간 들였지만, 결과적으로 공부도 많이 하고 얻은 게 많은 주차였습니다! API 연동 하면서 계속 보게 되는 404 에러나, 500 에러 보면서 이게 왜 안되지 하고 한 두시간동안 코드만 뒤척거리다가 환경변수라던가 폴더 이름 문제라는걸 알고 나서는 나 자신이 너무 한심..ㅎㅎ;; 했지만 그래도 시간 많이 들인 만큼 몰랐던 것들 알아간 것 같습니다. 실제 페달지니 프로젝트 하면서도 같은 문제를 겪을 수도 있을 것 같은데, 이번에 미리 경험해봐서 다행이라는 생각이 드네요. 아무튼 이번 주차 공부하면서 실력이 제일 많이 는 것 같습니다! 최적화나 스켈레톤, 무한 로딩까지는 신경쓰지 못했지만 이번 주말 내에 시간이 난다면 코드 수정해서 다시 배포해보고 싶네요. 어렵지만 좋은 과제였고, 연동되는 KeyQuestion도 같이 공부돼서 좋았습니다. 다음주차도 파이팅....!!! 👍 그리고 영준오빠에게 무한 감사의 절을 올립니다... 감사합니다 🙇

Key Question

  • Key Questions
    • 무한 스크롤과 Intersection Observer API의 특징
      • 무한 스크롤 → 사용자가 페이지를 아래로 스크롤할 때마다 새로운 콘텐츠를 자동으로 로드해서 끝없이 내용을 제공해주는 기능.

      • 기본 방식 → 스크롤 이벤트를 감지해서 사용자가 페이지 하단에 도달하면 추가 데이터 가져오기

      • Intersection Observer API → 특정 요소(페이지의 하단 감시 요소)가 뷰포트에 진입할 때 추가 콘텐츠 로드

      • 끊김 없는 콘텐츠 제공, 모바일 친화적이라는 장점 → BUT 페이지 하단 - 푸터와 같은 정보에 도달하기 어려울 수 있으며 성능 이슈, SEO 문제

      • Intersection Observer API - 요소가 뷰포트 또는 지정한 부모 요소와 교차하는지 여부를 비동기적으로 감지하는 브라우저 API

        // 1. IntersectionObserver 콜백 함수 정의
        const callback = (entries, observer) => {
          entries.forEach(entry => {
            if (entry.isIntersecting) {
              // 요소가 뷰포트에 진입했을 때 실행할 코드
              console.log('요소가 화면에 나타났습니다.');
        
              // 필요한 경우 관찰 해제
              observer.unobserve(entry.target);
            }
          });
        };
        
        // 2. IntersectionObserver 옵션 설정 (필요에 따라 설정)
        const options = {
          root: null, // 뷰포트를 기준으로 관찰
          rootMargin: '0px',
          threshold: 0.1 // 요소가 10% 이상 보이면 콜백 실행
        };
        
        // 3. IntersectionObserver 인스턴스 생성
        const observer = new IntersectionObserver(callback, options);
        
        // 4. 대상 요소 선택 및 관찰 시작
        const targetElement = document.querySelector('#target');
        observer.observe(targetElement);
        
        // ** root - null -> 브라우저의 뷰포트(기본화면) -> 교차 여부 기준을 정하는 요소
        const options = {
          root: document.querySelector('#scrollable-container'), // 기준이 되는 요소 지정
          threshold: 0.5
          // 이러면 타겟이 container안에서 보일 때. 즉, container가 기준이 됨. 
        };
        // ** rootMargin - root 요소의 경계에 추가적인 마진을 적용해서 교차 영역을 확장, 축소
        const options = {
          root: null, // 브라우저 뷰포트를 기준으로 함
          rootMargin: '0px 0px -50% 0px', // 아래쪽 경계를 50% 위로 올림 top, right, bottom, left
          threshold: 0
          // 이 코드는 여기서 rootMargin: '0px 0px -50% 0px'은 뷰포트의 아래쪽 경계를 50% 위로 올려서, 타겟 요소가 뷰포트의 중간 지점에 도달했을 때 콜백이 실행되도록 합니다.
          // 영역 조정 detail한
        };
        // ** threshold 타겟의 가시성 비율 -> 0이면 조금이라도 들어오면 실행, 1.0이면 다 보여야 실행
        // 예시 -> 이러면 타겟이 나오는 지점보다 100px +영역이 다가오면 미리 콜백 실행
        // 즉, prefetching을 한다. -> 뚝뚝 안 끊기게 할 수 있을듯.
        const options = {
          root: null,
          rootMargin: '0px 0px 100px 0px', // 아래쪽으로 100px 확장
          threshold: 0
        };
        • 이벤트 리스너보다 효율적이며 스크롤 계산 없이 가시성 추적이 용이함
        • 효율적인 감시 - 비동기적으로 동작
        • 다양한 활용도와 간편한 설정
        • 호환성, 복잡한 경우의 처리에서 단점이 있을 수 있음. Threshold 설정이 중요함. 옵저버 관리 적절해야 함.
    • TanStack Query의 사용 이유(기존의 상태 관리 라이브러리와는 어떻게 다른지)와 사용방법(reactJS, nextJS)
      • 기존 상태 관리 라이브러리와의 차이점 → 우선 클라이언트 상태, 서버 상태를 분리했을 때 기존의 Redux, zustand 등은 클라이언트 상태관리를 위한 도구이며 application 내부에서 필요한 상태, 데이터를 관리한다. 반대로 Tanstack query는 서버 상태 관리를 전문으로 해서 데이터 페칭, 캐싱을 관리한다.
      • 선언적인 API를 제공하여 필요한 데이터가 컴포넌트에서 선언되면 라이브러리 내에서 데이터 페칭, 캐싱, 업데이트를 처리한다.
      • Query Key를 사용한 의존성 관리, UX와 관련없는 라이브러리 내 데이터 업데이트, 데이터 요청시 최신 상태로 가져옴.
      • 사용 이유 → 생산성 향상(간편한 사용으로 데이터 페칭, 캐싱, 관리가 가능), 캐싱과 백그라운드 업데이트로 성능 최적화, 로딩 상태를 관리해주고 자동으로 재시도 해주는 로직이 있어 에러발생에 대응이 가능함.
      • ReactJS → QueryClientProvider로 감싸는 것으로 시작, 리액트는 CSR이기 때문에 기타 설정없이 바로 사용가능. 주로 ReactJs는 클라이언트 상태관리가 주 목적이기에 다른 상태관리 라이브러리가 잘 맞을 수도 있음.
      • NextJS → SSR을 하므로 데이터의 사전 페칭과 use client와 같은 Hydration 설정이 필요함.
      • 두 스택에서의 차이는 CSR, SSR에 따라 설정이 필요하며 그에 따른 종속성, 페칭 구조가 달라질 수 있다.
    • 기본적인 git add, commit, push, pull, merge, rebase 등의 명령어에 대해 알아봅시다(+다른 git 명령어나 branch 전략도 좋음)
      • git init → 새로운 git 저장소를 초기화 - 현재 디렉토리를 Git 저장소로 만들 때

      • git add → 변경된 파일 Staging 영역에 추가. 커밋에 포함할 파일 선택

      • git commit → Staging 영역에 있는 변경사항 로컬 저장소에 기록.

      • git push → 로컬 저장소의 커밋을 원격 저장소에 업로드. 브랜치에 변경사항 반영

      • git pull → 원격 저장소의 변경 사항을 로컬 저장소로 가져옴. git fetch + git merge 합친거

      • git merge → 다른 브랜치의 변경 사항을 현재 브랜치에 병합

      • git rebase → 브랜치의 기반(base)를 다른 브랜치로 변경. 나의 커밋 히스토리는 유지하면서 다른 브랜치로 히스토리를 이어갈 때 ⇒ 현재 브랜치의 기반을 다른 브랜치의 최신 커밋으로 변경하여 히스토리를 직렬화할 때. 이미 푸시된 커밋을 rebase하는 것이 아닌 로컬에서 개인 작업 브랜치에서 rebase를 사용할 것.

        git checkout feature-branch
        git rebase main -> 작업하고 있는 feature-branch에 main의 내용을 이어붙임
        
        git add <충돌 수정한 파일>
        git rebase --continue -> 충돌 파일 수정  스테이징한 다음 이어나감
        git rebase --abort -> rebase 중단
      • git stash → 작업 중인 변경 사항을 임시로 저장하고, 워킹 디렉토리를 깨끗하게 유지. 작업 도중 브랜치 변경해야 하거나, 워킹 디렉토리 깨끗하게 유지할 때 변경사항을 임시로 저장

        1

      • 기타 git 명령어 (fetch, reset, revert, cherry-pick, log, diff)

        // git fetch
        git fetch origin -> 원격 저장소의 변경 사항을 로컬 저장소로 가져오지만, 자동 병합은  .
        
        // git reset - 커밋이나 파일의 상태를 이전으로 되돌림
        git reset --soft <커밋 해시>   # 스테이징 영역은 유지
        git reset --mixed <커밋 해시>  # 기본 옵션, 스테이징 영역 초기화
        git reset --hard <커밋 해시>   # 워킹 디렉토리까지 모두 초기화
        
        // git revert - 지정한 커밋의 변경 사항을 무효화하는 새로운 커밋 생성
        git revert <커밋 해시>
        
        // git cherry-pick - 특정 커밋을 선택하여 현재 브랜치에 적용
        git cherry-pick <커밋 해시>
        
        // git log - 커밋 히스토리 조회
        git log            # 기본 로그 출력
        git log --oneline  #  줄로 요약된 로그
        git log --graph    # 브랜치 구조를 그래프로 표시
        
        // git diff - 변경 사항 비교
        git diff              # 워킹 디렉토리와 스테이징 영역 비교
        git diff --staged     # 스테이징 영역과 마지막 커밋 비교
        git diff <브랜치1> <브랜치2> # 두 브랜치 간의 차이 비교
      • Git Branch 전략

        • Git Flow

        2

        • GitHub Flow

        3

yyj0917 and others added 30 commits November 10, 2024 15:24
… category에 따라 movie카테고리 다르게 가져오는 거 오류나는중 수정해야 함
Fix : rewrite수정, fetch 함수 생성
Fix : 배포시 환경변수 이슈 next.config.mjs destination 수정
Feat : details 페이지 구현완료.
…Chore : root layout.tsx metadata수정, Refactor : assets 폴더 삭제, public 폴더로 대체 + import경로 절대 경로로 수정
Fix : details api 500 bad request error 수정 -> hooks/useFetchDetails, …
@ddhelop
Copy link
Member

ddhelop commented Nov 16, 2024

배포 개발자모드 링크로 되어있어요. 도메인으로 올려주세요!

Copy link

@yyj0917 yyj0917 left a comment

Choose a reason for hiding this comment

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

고생많았습니다 😬 다음 과제도 파이팅 👋

Copy link

Choose a reason for hiding this comment

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

바로 적용해서 사용하는 적응력의 박수를... 👏

return useQuery({
queryKey: ['searchMovies', query],
queryFn: () => fetchSearchedMovies(query),
enabled: query.length > 0, // 쿼리가 있을 때만 요청 보내기
Copy link

Choose a reason for hiding this comment

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

이런 디테일 최고 👍


return (
<div className=''>
<SearchBar onSearch={setQuery} />
Copy link

Choose a reason for hiding this comment

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

나도 저번에 들었는데 set함수를 props로 내렸을 때 단점이 있다더라. handleQuery 함수 하나 만들어서 하는 것도 좋을 것 같습니다 😬

Copy link
Author

Choose a reason for hiding this comment

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

아! 안그래도 프롭스로 받아오기 싫어서 어떻게 할 지 좀 고민하다가 뭔가 뜯어고치거나 상태관리 방법 바꿔야 할 것 같아서 이대로 했는데 다음에는 함수 만들어서 써야겠다. 유용한 리뷰 넘 조하요😸

);

export default function SearchedMovie({ query }: SearchedProps) {
const { data: searchedData } = useFetchSearchedMovies(query);
Copy link

Choose a reason for hiding this comment

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

추후 무한스크롤까지 구현하게 된다면 key questions에 있는 Observer활용하면 더 편하게 할 수 있을 것 같아~

Copy link
Author

Choose a reason for hiding this comment

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

음음 다음을 위해서 그거까지 공부해서 해봐야겠다 😲

@jsomnium
Copy link
Author

배포 개발자모드 링크로 되어있어요. 도메인으로 올려주세요!

앗 수정했습니다!!!

@jsomnium
Copy link
Author

jsomnium commented Nov 17, 2024

SOS 요청하면 바로 도와줘서 감동의 눈물 좔좔 흘렸어...
설명도 잘 해줘서 뒤에 코드 짤 때 수월했던 것 같아!!!
고생 많았구 다음 과제도 파이팅!!!(✌ -͈᷅‎ ⌄ -͈᷄)✌

Copy link

@billy0904 billy0904 left a comment

Choose a reason for hiding this comment

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

전체적으로 코드 가독성도 높고 주석도 잘 달아주셔서 리뷰하기 좋았습니다 ㅎㅎ 저희 과제와 다른 로직들을 많이 배워갈 수 있었고 특히 배포 링크에서 데이터가 들어오는 동안 Loading 텍스트 걸어주신 점이 UX 측면에서 완성도도 높고 너무 좋았습니다.

이번 주 과제도 수고 많으셨습니다! 🍀

Comment on lines +15 to +25
"overrides": [
{
"files": [
"**/components/ui/*.tsx"
],
"rules": {
"react/prop-types": "off",
"react-refresh/only-export-components": "off"
}
}
],

Choose a reason for hiding this comment

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

TS 환경이라 UI 파일의 prop-types와 only-export-components 규칙을 꺼둔 점 너무 좋네요!👍

Comment on lines +14 to +22
useEffect(() => {
if (pathname) {
const pathParts = pathname.split('/');
if (pathParts.length >= 4) {
setType(pathParts[2] as 'movie' | 'tv');
setId(pathParts[3]);
}
}
}, [pathname]);

Choose a reason for hiding this comment

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

pathname으로 type과 id를 분리하는 방식을 잘 구조화하신 것 같습니다!

Comment on lines +9 to +16
export const useFetchDetails = (type: 'movie' | 'tv' | null, id: string | null) => {
return useQuery({
queryKey: ['details', type, id],
queryFn: () => fetchDetails(type, id),
staleTime: 1000 * 60 * 60,
enabled: !!type && !!id, // type과 id가 존재할 때만 실행
});
};

Choose a reason for hiding this comment

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

유틸 함수와 재사용 가능한 로직 분리가 너무 잘 되어있는 것 같습니다! useFetchDetails 훅으로 데이터 fetching 로직을 분리할 생각은 못했는데 이렇게 훅을 분리하면 재사용성이 훨씬 높아질 것 같아요 저도 다음에는 이렇게 구현해봐야겠습니다👍🌟

Copy link

Choose a reason for hiding this comment

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

tanstack/react-query를 사용하셔서 클라이언트 사이드의 데이터 처리를 간편하게 하시고 있네요! 저희는 tanstack/react-query를 사용하지 않아서 다음에 이렇게 구현해봐야 할 거 같습니다!

<h2 className="text-[26.75px] font-bold">
{type === 'movie' ? contentDetails.title : contentDetails.name}
</h2>
<p className="text-[11.14px] font-normal line-[14.17px]">

Choose a reason for hiding this comment

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

오 line 속성은 처음 보는데 tailwind에서 line은 무슨 역할을 하는 코드인가요?

Comment on lines +3 to +4
import SearchBarIcon from '../../../../public/svg/SearchBar.svg'
import Close from '../../../../public/svg/Close.svg'

Choose a reason for hiding this comment

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

앞선 파일들에서 절대경로로 파일을 추가해주신 것처럼 import를 절대경로로 통일해주시면 훨씬 깔끔한 코드 작성이 가능할 것 같습니다!

Copy link

@ryu-won ryu-won left a comment

Choose a reason for hiding this comment

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

vercel 배포할 때 환경변수설정, 루트폴더 설정을 몰라서 저도 몇시간동안 헤매었네요.....
이번 과제는 두분 협업이 돋보이는 과제였습니다👍수고 많으셨습니다!

Comment on lines +9 to +16
export const useFetchDetails = (type: 'movie' | 'tv' | null, id: string | null) => {
return useQuery({
queryKey: ['details', type, id],
queryFn: () => fetchDetails(type, id),
staleTime: 1000 * 60 * 60,
enabled: !!type && !!id, // type과 id가 존재할 때만 실행
});
};
Copy link

Choose a reason for hiding this comment

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

tanstack/react-query를 사용하셔서 클라이언트 사이드의 데이터 처리를 간편하게 하시고 있네요! 저희는 tanstack/react-query를 사용하지 않아서 다음에 이렇게 구현해봐야 할 거 같습니다!

Copy link

Choose a reason for hiding this comment

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

rewrite와 api라우팅을 2가지 모두 사용하신 이유가 있을까요? api 라우팅만 해도 api키를 숨길 수 있을 거 같아서요!

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