심화프로젝트 발표가 끝났다. 우리 조가 처음 목표로 가장 낮은 난이도의 필수구현사항인
redux-toolkit
적용react-query
로 서버 상태 관리하기TypeScript
React Query
의 enabled
옵션과 select
옵션을 이용하여 useQuery
를 구현React Query
의 mutate
+ invalidateQueries
조합을 사용하여 데이터 변경에 따른 실시간 갱신을 구현debounce
또는 throttle(Lodash)
함수를 사용하여 API 요청이나 이벤트 핸들링 성능을 개선중 mutate
+ invalidateQueries
, firebase를 활용하여 회원가입 및 로그인 기능을 추가하여 마무리했다.
하지만 아직까지 부족한 점이 너무 많다. 우선 과제 요구사항은 충족을 했을지 모르지만, 전체적인 UI와 코드컨벤션,
상태관리, 컴포넌트 분리까지 너무나도 미숙했다고 생각한다. 주어진 시간이 충분하니, 차근차근 진행해보자 라는 마음을 가지고 접근했던 것이 패착이 되지 않았나 생각했다.
문제점이 무엇이었을까 되짚어 본다.
우선 첫 번째, 프로젝트 구조나 방향에 대한 세밀한 상의가 이루어지지 않았다.
명확한 컨셉을 정하고 우리가 구상하는 프로젝트에 대한 구현이 가능할 것인지, 가능하다면 어떤 라이브러리를 사용할건지 상태관리는 어떻게 할 것인지에 대한 충분한 조사와 이해가 수반되지 않았기 때문에 우리가 예상했던 데드라인을 맞출 수 없었던 것 같다.
두 번째, 역할 분담이 제대로 이루어지지 않았다. 역할의 세세한 분류가 없었다. 명확한 데드라인이 없었다.
프로젝트 끝나고 이것 저것 찾아보니 아니 왜 이걸 이렇게 어렵게 생각했지 싶은게 너무 많은건 왜일까..?
스타일컴포넌트를 사용하여 CSS를 구현하고, 파일을 분리하는 것. 의미를 유추할 수 있는 파일명 짓기, 함수는 동사, 변수는 명사,, 이런 기본적인 것들이 막상 코드를 칠 때 기억이 안나고 모호하다는 점. 코드의 흐름이 일관되지 못하다는 점.
기능을 구현하는데만 급급해서 코드를 곱씹지 않는 건 특히 고쳐야 할 사항이 아닐까...?!
확실히 알게된 내용들 정리해보기.
useQuery
로 firebase 데이터 중 오늘 날짜로 입력된 데이터 가져오기. const { data: fetchedData } = useQuery(['contents', { date: today }], async () => {
const contentsRef = collection(db, 'contents');
const midnight = new Date(today);
const queryContents = query(contentsRef, where('createdAt', '>=', midnight), orderBy('createdAt', 'asc'));
const querySnapshot = await getDocs(queryContents);
const newData = querySnapshot.docs.map((doc) => ({
id: doc.id,
contents: doc.data().contents,
isCompleted: doc.data().isCompleted,
createdAt: doc.data().createdAt.toDate()
}));
return newData;
});
firebaseWhere
문 : 첫번째 인자로 적힌 createdAt에서 자정, 즉 금일의 시작보다 큰 경우(시간적으로 뒤의 경우) 작성된 글을 필터링해서 가져온다는 것임. 오늘자로 등록된 글만 보여주세요~ 이거다. 인자 3개 들어가는데
파이어베이스에 등록된 테이블, 조건, 비교할 놈 이렇게 받아들이믄 됨.
useMutation
을 이용해 데이터 등록, 업데이트, 삭제하기.// 등록
const mutation = useMutation(
async (newContent: NewContent) => {
const contentsRef: CollectionReference = collection(db, 'contents');
const createdAt = serverTimestamp();
const docRef = await addDoc(contentsRef, { ...newContent, createdAt });
return docRef;
},
{
onSuccess: () => {
queryClient.invalidateQueries('contents');
},
onError: (error: Error) => {
console.error('데이터 전송 에러:', error.message);
}
}
);
useMutation
으로 db
의 contents
라는 컬렉션에 새로운 데이터와 작성 시간을 추가할 수 있는 로직.
데이터 추가에 성공하면 queryClient.invalidateQueries
메서드로 캐시를 무효화시켜 데이터를 다시 가져올 수 있도록 한다. 그니까 받아온 거 stale
인지 어떤지 상관 안쓰고 걍 새로 한 개 다시 줘~ 라는 의미임.
그럼 뭐냐!? 그 때 그 때 새로 받아온 걸 바로바로 렌더링 해줄 수 있다는 의미임.
const onSubmitHandler = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
const createdAt = serverTimestamp();
await mutation.mutateAsync({ contents, createdAt, isCompleted });
setContents('');
} catch (error) {
console.error('데이터 전송 에러:', error);
}
};
위의 함수를 인풋 버튼 혹은 form
에 걸어준다면 작성된 글에 대한 제출이 이루어질 때 마다 db에서 데이터 바로바로 받아와서 보여줄 수 있다는 것. 실시간으로 데이터가 추가되는 걸 보여지게 해준다~~
// 삭제
const deleteItem = async (id: string) => {
try {
const docRef = doc(db, 'contents', id);
await deleteDoc(docRef);
queryClient.invalidateQueries('contents');
} catch (error) {
console.error('문서 삭제 중 오류가 발생했습니다', error);
}
};
이제 구조가 보인다 그치? queryClient.invalidateQueries
이거는 데이터를 실시간으로 반영되게 해줄 수 있는 친구라고 이해를 하면 편할 거 같다. 물론 데이터 상태를 새로 받아오기 때문에 가능한거라고 이해하면 더 좋을 듯!
// 수정 하기
const editItemHandler = async (id: string) => {
setEditItemId(id); // 현재 편집 중이 항목 ID 설정
if (fetchedData) {
const itemToEdit = fetchedData.find((item: any) => item.id === id);
if (itemToEdit) {
setEditContents(itemToEdit.contents); // 수정 하고 싶은 항목을 수정 상태로 변경
}
}
};
input
창에 contents
가 value
값으로 연동되게 하고, 선택된 글의 id가 db
에 저장된 글의 id와 같은지 비교 후 같다면 내가 입력한 글대로 적용될 수 있도록 하는거~~
진짜 많이 쳐봐야겠다. 내가 뭐라고 신경 써주시는 분도 있는데 부지런하게 코드 쳐 정신차려라.