티스토리 뷰
본론으로 들어가기 전 이번 과제 후기
본 내용을 시작해봅시다 후후
이번 과제는 첫 팀 과제였다! 프엔독기걸 민주랑 함께 과제를 하게 되었다 ㅎㅎ 첫 협업이라 맞춰갈 부분이 많았지만 2주 간 서로의 방식에 적응이 된 기분이었다.그리고 무엇보다 민주한테서 많이 배우기도 했다:) 리코일장인 민주초이
5, 6차에 거쳐 한 미션은 넷플릭스 클론 코딩이었다.
[ 미션 목표 ]
- Next.js 사용법을 공부해봅니다.
- Figma로 주어지는 디자인으로 스타일링 하는 방식에 익숙해집니다.
- Git을 이용한 협업 방식에 익숙해집니다.
[ 필수 요건 ]
- 결과화면의 상세 페이지와 검색 페이지를 구현합니다.
- 상세 페이지는 동적 라우팅을 이용해 구현합니다.
- 검색 페이지는 실시간 키워드 검색으로 구현합니다.
- Figma의 디자인을 그대로 구현합니다.
- SSR을 적용해서 구현합니다.
- Open api를 사용해서 데이터 패칭을 진행합니다. (ex. themoviedb API)
[ 선택 사항 ]
- 검색 페이지 무한스크롤을 구현합니다.
- 검색 페이지 스켈레톤 컴포넌트를 구현합니다.
- 성능 최적화를 위한 방법을 적용해봅니다.
구현한 부분들
1. Home 화면 Header
랜덤 이미지 렌더링
헤더 부분에 포스터 이미지가 계속 랜덤으로 렌더링되는 것을 구현했다. 생각보다 애를 많이 먹었던 부분..! 조금 더 간단하게 표현할 수 있었을 것 같기도 한데 조금 아쉽다.
그리고 useEffect를 썼을 때 원래는 빈 배열 []로 두지 않고 [setPopularMovies]로 지정을 했었다. 하지만 한 번 PopularMovies를 불러오면 바뀔 일은 없으므로 빈 배열로 변경하였다.
// header random picture
const randomMovie = Math.floor(Math.random() * 10);
const [popularMovies, setPopularMovies] = useRecoilState(popularMoviesRecoil);
const backdropPath = popularMovies?.[randomMovie]?.backdrop_path;
const imageSrc = backdropPath ? `https://image.tmdb.org/t/p/original${backdropPath}` : '';
...
useEffect(() => {
const fetchRandomMovies = async() => {
try {
const {data} = await MovieApi.popular();
setPopularMovies(data.results);
}catch(error){
console.error(error);
}
};
fetchRandomMovies();
}, []);
폰트 스타일링
폰트 스타일을 기본으로 설정해놓은 게 있었는데, 예외가 발생하는 부분이 있었다.
그래서 이를 styled로 처리해보지 않고 다르게 적용을 해볼 수 있을까 싶어서 찾아보니, 아래와 같이 구현도 할 수 있었다.
//Play font style
const boldText = {
fontWeight: 'bold',
color: '#000000',
lineHeight: '30px',
letterSpacing:'-0.06px',
fontSize: '20.46px'
}
<div style={boldText}>Play</div>
2. SearchPage
코드 너무 길다.. 워어어..
Data Fetching
원래는 필요한 데이터를 ssr 방식으로 ServerSideProps()를 사용해 미리 가지고 오는 걸로 구현을 하려 했지만, vercel 배포 오류가 와장창 나서 클라이언트 사이드에서 fetch를 할 수 밖에 없었다.
//Fetch SearchData
const fetchSearchData = async ({ queryKey, pageParam = 1 }: { queryKey: [string]; pageParam?: number | undefined }) => {
const searchInput = queryKey[0];
const apiKey = "process.env.NEXT_PUBLIC_KEY"; //이 부분도 vercel 배포할 때 에러나서 눈물 흘리면서 원래 데이터로 바꿔줌..
const res = await fetch(`https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&query=${searchInput}&page=${pageParam}`);
const searchData = await res.json() as IMovie[];
return searchData;
};
무한스크롤 with useInfiniteQuery
너 때문에 나 좀 힘들었다.
useInfiniteQuery를 사용해봤다. 처음 사용해보는거라 약간 억지로 맞춘 느낌도 들고, 완벽히 빠삭하게 이해를 했다고는 못할 것 같다. 구현할 때 좀 재밌었던 부분이라..! 공부를 더 해보고 잘 써보고 싶다:)
내가 이해한 바로는, searchInput에 따라 SearchData를 가져오거나 PopularData를 가져온다. 이때, pageParam은 페이지 번호를 나타내고 다음 페이지를 가져오기 위해 사용이 된다. 더불어 getNextPageParam 함수는 이전 페이지의 데이터를 인자로 받고 다음 페이지 번호를 계산한다. (마지막 페이지 번호를 확인한 후, 1을 더해준 값을 반환한다.)
아, 그리고 꼭 마지막에 ref를 반환해야 무한 스크롤이 먹혔다.
//Search/page.tsx
//useState
const [searchInput, setSearchInput] = useState('');
//useInfiniteQuery
const {
data,
fetchNextPage,
isFetching,
isLoading : isQueryLoading,
} = useInfiniteQuery(
['searchData', searchInput],
({ pageParam }) => {
return searchInput
? fetchSearchData({ queryKey: [searchInput], pageParam })
: fetchPopularData({ pageParam });
},
{
getNextPageParam: (lastPage) => {
const currentPage = lastPage?.[lastPage.length - 1]?.page || 1;
return currentPage + 1;
}
}
);
const onIntersect = ([entry]: IntersectionObserverEntry[]) => entry.isIntersecting && fetchNextPage()
// Scroll로 bottom ref와 onIntersect를 넘겨줌
Scrolling({
target: bottom,
onIntersect,
})
//Search/Scrolling.tsx
import { useEffect } from "react";
export const Scrolling = ({
target,
onIntersect,
root = null,
rootMargin = "0px",
threshold = 1.0,
}: {
target: React.RefObject<Element>;
onIntersect: IntersectionObserverCallback;
root?: Element | null;
rootMargin?: string;
threshold?: number | number[];
}) => {
useEffect(() => {
let observer: IntersectionObserver | undefined;
if (target && target.current) {
observer = new IntersectionObserver(onIntersect, {
root,
rootMargin,
threshold,
});
observer.observe(target.current);
}
return () => {
if (observer) {
observer.disconnect();
}
};
}, [target, root, rootMargin, threshold]);
};
Key Questions
1. 정적 라우팅(Static Routing)/동적 라우팅(Dynamic Routing)이란?
- 정적 라우팅
- 라우팅 경로와 해당 경로에 대한 페이지를 미리 사전에 정의 해주는 방식
- 렌더링 되기 전 주소가 정해져 있는 것
- Nextjs 13에서는 app 경로를 기준으로 app/Home 폴더 안에 page.tsx가 있을 경우, page.tsx에 대한 경로가 자동으로 /Home으로 생성이 됨
- 동적 라우팅
- 변화 가능한 경로 주소를 활용하는 방법
- 렌더링 될 때 정확한 주소가 정해짐
- Nextjs 13에서는 대괄호를 사용해 동적 라우팅이 가능함. 따라서 app/Home/[id]에 page.tsx가 있는 경우 실제 렌더링 후 주소가 /Home/[영화id]로 설정이 됨
2. 성능 최적화를 위해 사용한 방법
- Home page에서 PosterBox 컴포넌트 활용
- 지난주 과제에서는 홈 화면에서 뜨는 원 이미지와 사각형 이미지를 각각 다른 컴포넌트로 처리를 했는데, 이번주에는 PosterBox 라는 하나의 컴포넌트로 처리해 성능을 개선했음!
- Next Image 컴포넌트 활용
- 이미지 최적화를 위해 Nextjs에서 제공하는 Image 컴포넌트를 활용함. Navigation 컴포넌트, Header 컴포넌트에서 활용하신 것을 확인 가능함.
- 공식 문서
- Nextjs Image 장단점
이번에도 발표를 해서 발표 자료 뿅
https://half-catsup-728.notion.site/6-4b54af9c77644a4989e1a3cee0d89d35
6주차 과제 발표
시연
half-catsup-728.notion.site
이번 과제도 고생많았다잉
'💻 Web > CEOS' 카테고리의 다른 글
[이슈] Cookie에 Refresh Token이 저장이 안되는 이슈와 삽질기🍪 (0) | 2023.09.03 |
---|---|
[React] 알아두면 쓸데있는 로그인 기능 구현 지식들✨(ft. JWT 토큰, Access Token, Refresh Token, Cookie, 로컬스토리지) (0) | 2023.08.29 |
[회고] CEOS 프론트엔드 17기 프로젝트 바리바리 회고 (1) | 2023.08.21 |
[Github] Pull request template 만들기 (0) | 2023.07.02 |
[React] CEOS 4주차 미션: React-messenger 구현하기 (1) | 2023.05.08 |
- Total
- Today
- Yesterday
- Subnet
- 이분탐색
- 그리디
- 로그인 기능 구현
- DOM
- 세오스
- VPC
- AwsCloudClubs
- 프론트엔드
- jwt
- 리액트
- NaCl
- react
- vpc peering
- TypeScript
- cloud
- access token
- AWS
- 투포인터
- IGW
- ceos
- 바리바리
- JWT 토큰
- route table
- 쿠키
- 정렬
- 로컬스토리지
- 리액트를 다루는 기술
- refresh token
- 면접을 위한 CS 전공지식 노트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |