아티클 추천(메인) 페이지에서 구현할 기능은 다음과 같다.
추천받기
버튼 클릭시 랜덤으로 하나의 아티클 추천Done
에 체크할 경우 해당 아티클은 다시 추천되지 않음리스트 아이콘
을 클릭시 데이터 페이지로 이동📄 container.tsx
export default function MainPage() {
const [listsData, setListsData] = useState<any[]>([]);
const [selectedArticle, setSelectedArticle] = useState({
id: '',
isRead: false,
title: '',
link: '',
});
useEffect(() => {
const fetchLists = async () => {
const queryLists = query(
collection(db, 'list'),
where('isRead', '==', false) // isRead가 false인 doc만 불러오기
);
const result = await getDocs(queryLists);
const datas = result.docs.map((el) => el.data());
setListsData(datas);
};
fetchLists();
}, []); // 처음 렌더링 되었을 때 실행
const clickCreateRandomArticle = () => {
const dataNum = listsData.length;
const randomNum = Math.floor(Math.random() * dataNum); // 데이터의 수가 최대값일 때 그 범위 내에서 랜덤숫자 고르기
setSelectedArticle(listsData[randomNum]); // 해당 번호의 데이터 객체를 selectedArticle에 넣고 이 변수를 활용한다.
};
데이터 불러오기
useEffect
를 사용하여 페이지가 처음 렌더링될 때 한번 데이터를 불러온다.query()
메소드를 사용하여 isRead
필드값이 false
인 데이터만 불러오도록 했다. 따라서 사용자가 읽음 처리한 아티클은 추천되지 않는다.랜덤으로 하나의 데이터 뽑기
clickCreateRandomArticle
함수를 통해 랜덤으로 하나의 아티클 데이터의 정보를 뽑는다.selectedArticle
에 넣고 이 변수를 활용한다.💬 기사 생성
을 여러번 클릭 했을 때 같은 아티클이 연속으로 두번 이상 추천되지 않도록 보완할 것
📄 unit.ts
export const getRandomImage = () => {
const seed = Math.floor(Math.random() * 1000);
const image = `https://picsum.photos/200/300?seed=${seed}`;
return image;
};
📄 container.tsx
...
const clickCreateRandomArticle = () => {
const dataNum = listsData.length;
const randomNum = Math.floor(Math.random() * dataNum);
setSelectedArticle(listsData[randomNum]);
const image = getRandomImage();
setRandomImage(image);
};
...
return (
<MainPageUI
clickCreateRandomArticle={clickCreateRandomArticle}
selectedArticle={selectedArticle}
randomImage={randomImage}
/>
);
📄 presenter.tsx
export default function MainPageUI(props: IPropsMainPageUI) {
return (
...
<a href={props.selectedArticle.link} target='_blank'>
<s.Article
hoverable
style={{ width: 240 }}
cover={
<img
alt='example'
src={props.randomImage}
height={150}
style={{ objectFit: 'cover' }}
/>
}
>
<Meta
title={props.selectedArticle.title}
description={props.selectedArticle.link}
style={{ textAlign: 'left' }}
/>
</s.Article>
</a>
...
selectedArticle
state
selectedArticle
데이터를 props
로 불러와서 링크와 타이틀 등에 사용했다.randomImage
state
getRandomImage
함수를 생성하고, 추후 다른 부분에서도 활용도가 높을 것 같아 unit 디렉토리에 따로 빼두었다.clickCreateRandomArticle
함수 안에서 getRandomImage
함수를 호출했다.🤬 에러일지 : 웹브라우저 이미지 캐싱
처음에는getRandomImage
함수를 다음과 같이 정의했다.<export const getRandomImage = () => { const seed = Math.floor(Math.random() * 1000); const image = `https://picsum.photos/200`; 👈👈 return image; };
그런데 함수를 처음 호출할 때는 이미지가 제대로 생성되고, 이후 호출부터는 첫 이미지가 계속 유지되는 문제가 있었다. 원인은 웹브라우저의 이미지 캐시 문제였다.
- 웹브라우저가 초기에 'https://picsum.photos/200' 라는 주소로 서버로부터 이미지를 받아온다.
- 해당 이미지를 브라우저 캐시에 저장해놓는다.
- 다시 함수를 호출해 'https://picsum.photos/200' 로 이미지를 받아오고 API 서버는 이번에 다른 이미지를 보내준다.
- 하지만 웹브라우저는 같은 주소이므로 기존에 캐시에 저장해놓은 이미지를 띄운다.
😇 해결방법const image = `https://picsum.photos/200/300?seed=${seed}`;
위와 같이 주소 뒤에
?특정값
을 추가하여 웹브라우저에 매번 새로운 주소를 보내게 하여 문제를 해결했다.
참고
- phind에 질문하기
- 웹브라우저 이미지 캐쉬(캐싱) 막기
- Generate random imgae by url
📄 container.tsx
...
const onChangeIsRead = async () => {
const changedIsRead = !selectedArticle.isRead;
setSelectedArticle({ ...selectedArticle, isRead: changedIsRead }); // ui에 변화 반영
await updateDoc(doc(db, 'list', selectedArticle.id), { // db에 변화 반영
isRead: changedIsRead,
});
};
📄 presenter.tsx
...
<s.IsRead
checked={props.selectedArticle.isRead}
onChange={props.onChangeIsRead}
>
Done
</s.IsRead>
IsRead컴포넌트
)에 체크하면 onChange
함수 실행selectArticle
에 변경사항 업데이트updateDoc
메소드 활용하여 db에 변경사항 업데이트const onClickMoveToData = () => {
router.push('/list');
};
useRouter()
와 push()
메소드를 사용해 아이콘 클릭시 list 페이지로 이동하도록 구현
드디어 기초공사가 끝났다!
어떻든 일단은 하나의 서비스를 완성했다.
이제 버그와 개선사항을 쫌쫌따리 해결해나가보자.
간단한 서비스 하나에도 이렇게 많은 기능이 필요하고, 신경쓸 점이 많다.
예상치 못한 곳에서 오류가 나 새로운 배움도 많이 얻었고,
역시 직접 만들어보는 것이 최고의 복습이고 공부인가보다🥹