스포트라이트 (Spotlight)
- 풀스택 : Next.js
- 팀 : 5인개발 ( 본인 포함 풀스택 개발 5인 )
- 개발 기간 : 7월 24일 - 8월 29일
- 깃허브 : https://github.com/Programmers-SpotLight/SpotLight
- 배포 주소 : https://spot-light.ovh/
✨ 최애의 이야기가 있는 곳! 스포트라이트
영화에 나온 그 장소, 애니메이션에서 등장한 그 배경, 내 최애가 다녔던 그 맛집! 혹시 어딘지 궁금하진 않으셨나요?
스포트라이트는 셀렉션을 통해 여러분의 특별한 장소를 공유할 수 있는 지도 중심의 커뮤니티 웹 서비스입니다.
사용자는 자신이 아끼는 스팟들을 하나의 셀렉션으로 묶어 다른 사람들과 공유 할 수 있습니다.
스포트라이트와 함께 일상 속의 최애 장소를 기록하고, 공유하며, 새로운 경험을 해보세요!
- UI, UX 디자인
- 메인 페이지 기능 구현 ( 인기 셀렉션, 해시태그 맞춤 셀렉션 )
- 검색 페이지 기능 구현 ( 카테고리 필터링, 해시태그 검색, 자동완성, 해시태그 추천 )
- 마이 페이지 기능 구현 ( 프로필 변경, 셀렉션 상태 설정, 삭제, 관심태그 설정 )
세션 태그 저장과 태그 기록 동기화 문제
useEffect(() => {
if (tagInputRef.current) tagInputRef.current?.focus();
const addtagList = []; // 검색 태그 기록을 받을 임시 배열
const HeaderSearchTag = searchParams.get(QUERY_STRING_NAME.tags);
if (HeaderSearchTag) addtagList.push(HeaderSearchTag);
// 만약 헤더의 검색 바를 통해 검색하는 경우, 해당 태그 추가
const storedTags = sessionStorage.getItem(QUERY_STRING_NAME.tags);
if (storedTags) {
const parseStoredTags = JSON.parse(storedTags);
parseStoredTags.forEach((tag: string) => {
addtagList.push(tag);
});
}
// 초기값으로 세션에 있는 데이터들을 전부 받아와 데이터 동기화
if (addtagList.length > 0) {
addtagList.forEach((tag: string) => {
addQueryString(QUERY_STRING_NAME.tags, tag);
});
setTagList(addtagList);
}
}, []);
// 검색 기록에 해당하는 태그들을 쿼리스트링에 적용함으로 필터,
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
// 태그 필터 시 동작
e.preventDefault();
const addTag = tagValue.replace(/\s+/g, "");
if(searchValidator(addTag, tagList)){ // 생성 유효성 검사
const updatedTagList = [...tagList, addTag];
setTagList(updatedTagList);
sessionStorage.setItem(
QUERY_STRING_NAME.tags,
JSON.stringify(updatedTagList)
);
// 검색 기록 태그리스트에 데이터 추가, 이후 세션에 저장
addQueryString(QUERY_STRING_NAME.tags, addTag);
// 쿼리스트링에 태그 추가
setVisibleAutoCompletion(false);
} else {
setTagValue("");
setVisibleAutoCompletion(false);
}
};
다음과 같이 세션에 저장한 태그리스트와 검색 기록 태그리스트를 분리함으로 해결하였다.
검색 기록 태그리스트를 세션 값을 초기값으로 하는 useState로 처리하고, 해당 데이터만을 세션에 담는 식으로 구상을 하니 태그 중복과 불필요한 재할당 문제를 해결 할 수 있었다.
서버사이드 렌더링 데이터 빌드 중 누락, 불일치 오류
export const dynamic = "force-dynamic";
export default async function Home() {
try {
const popularSelections = await fetchHandler("api/selections/popular");
return (
<Suspense fallback={<PageLoading />}>
// 내부 컴포넌트에서 UseSearchParams를 통해 데이터를 처리함으로,
// Suspense를 통해 해당 데이터가 삽입되기까지 fallBack 제공
<main className="...(style)">
<BannerSection />
<DisplaySection />
<PopularSection selections={popularSelections} />
<RecommendationSection />
</main>
</Suspense>
);
} catch (error) {
console.error(error)
return <PageError/>
}
- 빌드 과정 이후에도 Fetch를 통해 데이터를 받는 컴포넌트를 force-dynamic을 통해 동적 컴포넌트임을 명시하였다.
- 또한 useSearchParams에 데이터가 들어오기 전 빌드가 되어 HTML이 생성되는 오류를 방지하기 위해 Next에서 제공하는 Suspense 컴포넌트를 활용하여 데이터를 기다리도록 하였다.
- 두 방법을 통해 빌드 과정에서 생기는 오류를 해결할 수 있었다.
1. 구체적인 기획
2. 명확한 업무 분담
3. 풀 사이클 경험
1. 새로운 기술 스택(Next, tailwindCSS) 학습
2. 백엔드 로직에 대한 이해
1. 현업보다는 분업 느낌
2. 부족한 코드 리딩 실력
1. 담당 업무에 대한 책임감
2. 개발에 대한 흥미