지난주 수요일부터 오늘 오전까지 뉴스피드 프로젝트를 진행했다.
나는 게시글을 supabase에 올리고 게시글을 수정하는 기능을 메인으로 맡았다. 또한 이를 구현한 후에 메인페이지 최신글 영역에 캐러셀을 적용하고 전체/카테고리별 게시글을 나열하는 것을 맡았고 마지막으로 게시글 상세페이지에 사진 캐러셀과 좋아요 기능을 추가했다.
직전 게시글은 css를 적절히 적용할 때 필요한 사항이었다. 또한 supabase도 이번 프로젝트의 백엔드 대신 사용했기 때문에 정리해두었다. 다른 팀원들도 내가 작성한 로직을 차용했다.
가장 먼저 든 생각은.. 로그인/회원가입을 맡은 분을 제외하고 가장 많은 기능을 구현해서 뿌듯했다. 로그인/회원가입은 아직 건들기 무서웠는데, 나서서 하겠다는 분이 계셨고, 그분은 기능 추가에 욕심이 많은 분이었다. 하지만 나를 포함해서 팀원들 실력도 부족했고 소셜 로그인과 DB에서 많은 시행착오를 겪어, 결국 기본 기능을 제외하면 아주 초기 기획에서 소셜 로그인 정도밖에 구현되지 않았다. 원래 '좋아요' 기능은 기획에 없었지만, 생각해보니 조금만 손보면 구현할 수 있을 것 같아서 도전했다.
이제 기능을 구현하며 생각한 것들에 대해 정리해보려고 한다.
1. 게시글 추가/수정
먼저 게시글을 추가하는 기능 자체는 구현이 쉬웠다. 처음에는 supabase storage를 사용할 계획이 없었고, 그 부분도 몰랐는데, 튜터님이 사용법을 짧게 소개해주셔서 사진 파일을 받아서 supabase storage에 업로드하게 되었다.
Table에 데이터를 추가하는 기능은 supabase 게시글에 나와있다. 삭제도 `insert`대신 `delete`를 사용하면 쉽게 할 수 있다. 그런데 게시글을 수정할 때 기존 데이터를 불러와서 바로 수정할 수 있게 하고 싶었는데, 띄우는게 쉽지 않았다.
원래는 `/detail?id=1`처럼 쿼리 스트링으로 상세페이지를 라우팅해놔서 supabase에서 데이터를 불러오게 했었다. 이 방법은 코드 내용에서 supabase 데이터를 적용하기 전에 이미 코드를 한바퀴 실행하기 때문에 에러가 발생했다. 이를 해결하기 위해 버튼에 게시글 정보를 담아서 전달했다. 이 방법은 `useNavigate`에서 prop을 전달하는 방법을 정리한 글에서 확인할 수 있다. 이렇게 게시글 내용을 수정하는 것은 완성할 수 있었다.
그런데 더 큰 문제점은 사진을 저장하고 불러오는 것이었다. 일단 사진은 최대 4개를 받기로 했기 때문에, imgs의 초기값은 `["","","",""]` 또는 `[null, null, null, null]`로 설정했다. 그런데 이 상태로 그냥 저장하려고 하니 duplicate 오류가 발생했다. 그래서 빈자리는 filter로 제거해주었다. 그런데 똑같은 파일을 저장하려니 똑같이 duplicate 오류가 발생했다. 즉 같은 이름으로 파일을 저장하면 파일탐색기가 에러를 보내주듯이 supabase에서도 같은 확장자에 동일한 이름을 가진 파일은 저장할 수 없었다. 그래서 파일 이름을 PostID와 인덱스 조합으로 바꿔서 동일한 사진도 여러 번 올릴 수 있도록 하였다.
다음으로는 게시글 수정할 때 이미지 파일을 다시 가져와서 띄워주는 것이었는데, 방법을 찾아보니 신경쓸 것이 많아보여 구현에 시간이 많이 뺏길 것 같아 포기하고, 게시글 수정할 때 사진을 모두 재업로드하도록 했다. 게시글 수정 완료 시 storage에서 파일을 모두 지우고 모두 새로 저장하도록 했다. 사진을 삭제하는 코드는 아래와 같다.
const deleteImgs = async (PostID) => {
try {
// 사진 데이터 목록 가져오기
let { data: curData, error: getError } = await supabase
.storage
.from(STORAGE_NAME)
.list(`${PostID}`);
if (getError) throw getError;
curData = curData.map(ele => `${PostID}/${ele.name}`);
// 사진 데이터 지우기
if (curData.length > 0) { // 저장된 사진이 있다면 삭제
const { error: deleteError } = await supabase
.storage
.from(STORAGE_NAME)
.remove(curData);
if (deleteError) throw deleteError;
console.log('Images deleted:', curData);
}
} catch (error) {
console.error("Error deleting images:", error.message);
}
};
2. 게시글 카드 보여주기
이 부분은 쉬웠다. 그런데 supabase 데이터를 불러오면 기본적으로 최신에 수정된 순으로 정렬이 된다. 그래서 최신글 캐러셀에서는 PostID순으로 불러온 데이터를 정렬해서 마지막 4개를 불러왔다. (PostID는 기본적으로 증가만하고 줄어들지는 않게 설정했다.)
메인 페이지 뿐만 아니라 지역별 카테고리 페이지에도 게시글 카드를 보여줘야 하므로, `PostListPage.jsx`와 `PostList.jsx`를 분리해 사용했다. `PostList.jsx`에서 카드를 나열하는데, 사이드바에서 카테고리 페이지로 넘어가면 리렌더링이 일어나지 않는 오류가 발생했다. 이를 해결하기 위해서 페이지가 전환되면 딱 한번만 바뀌는 변수를 `useEffect`의 dependency array로 넣어야겠다고 생각했다. 카테고리 페이지는 라우팅이 `/post-list?city="서울"`처럼 되어 있고, 메인페이지는 `/`이므로 페이지가 전환되면 한 번만 바뀌는 변수로 저장하기에 딱이라고 생각했다. 따라서 `useLocation`을 dependency array에 추가했다.
// PostList.jsx
const location = useLocation();
useEffect(() => {
const fetchData = async () => {
if (city) {
const { data, error } = await supabase.from("Post").select("*").eq('PostCity', city);
if (error) {
throw error;
} else {
setPosts(data)
}
} else {
const { data, error } = await supabase.from("Post").select("*");
if (error) {
throw error;
} else {
// console.log("data => ", data);
setPosts(data.reverse())
}
}
};
fetchData();
}, [location.search]);
3. 상세페이지 좋아요 기능 추가
위와 마찬가지로 좋아요 개수가 변할 때 리렌더링이 일어나야 바뀐 좋아요가 적용된다. 따라서 아래와 같이 코드를 작성했다.
const [like, setLike] = useState(0);
useEffect(() => {
const fetchPost = async () => {
const { data, error } = await supabase
.from("Post")
.select("*")
.eq("PostID", postId);
if (error) {
console.log("error=>", error);
} else {
// 좋아요 개수 적용
setLike(JSON.parse(data[0].PostLike).length);
}
};
fetchPost();
}, [like]);
좋아요 취소 기능을 구현하기 위해 좋아요를 누른 사람들의 UserId들을 게시글 정보에 배열로 저장했다.
아직 react hook을 적절히 사용하는 방법을 잘 모르기도 하고 우당탕탕 진행된 팀 프로젝트라 코드도 쓸데없이 많고 렌더링 최적화와는 거리가 먼 결과물이 나온 것 같다. 하지만 각자 맡은 부분을 성실히 수행했고, 결국 완성할 수 있어서 기분 좋게 마무리할 수 있었다.
+ 대표이미지는 팀원이 만들었다!
'Frontend > Projects' 카테고리의 다른 글
[CSS] Tailwindcss 사용하기 기초 (0) | 2024.09.20 |
---|---|
[React] mbti 테스트 완성 (새로고침 후 로그인 정보 유지) (0) | 2024.09.11 |
[React] mbti 테스트 TanStack Query 적용 (0) | 2024.09.10 |
[React] mbti 테스트 초기 상태 정리 (0) | 2024.09.09 |
[React] 올림픽 메달 정렬 페이지 (0) | 2024.08.12 |