2차 공부/TIL

24.09.09 개인프로젝트 / useCustomQuery, QueryKey분리

공대탈출 2024. 9. 9. 22:01
const Test = () => {
    const { testid } = useParams();
    const targetQuestions = questions[testid];
    const [answers, setAnswers] = useState(Array.from({ length: targetQuestions.length }));
    const navigate = useNavigate();
    const { userId, nickname } = useUserInfo();

    const handleAnswers = (idx, selected) => {
        setAnswers([...answers].with(idx, selected));
    };

    const getMBTIResult = async () => {
        const isAnswersSelected = answers.every((answer) => answer != undefined);
        if (!isAnswersSelected) {
            alert("모든 문항에 답을 해주세요!");
            return;
        }
        const result = calculateMBTI(testid, answers);
        return result;
    };

    const postMyTestResult = async (resultObj) => {
        await testInstance.post(`/${testid}`, resultObj);
    };

    const { mutate } = useMutation({
        mutationFn: postMyTestResult,
        onSuccess: (data) => navigate(`/testresult/${testid}`, { state: data }),
        onError: (error) => console.log("error :>> ", error),
    });

    const handleSubmit = async (e) => {
        e.preventDefault();
        const result = await getMBTIResult();
        const resultObj = { nickname, userId, result, visibility: false };
        if (result) {
            mutate(resultObj);
        }
    };

    return (
    	<>
        	//...
        </>
    );
};

export default Test;

useMutation을 사용하기 위해 문항체크함수, 서버 통신함수, useMutation사용, Submit함수를 작성하다보니 로직이 너무 길어져 컴포넌트 코드의 가독성이 떨어진다고 느껴졌다.

 

 

그래서 일단 axios요청부터 모두 분리해보았다.

 

export const testInstance = axios.create({
    baseURL: "http://localhost:5000",
});

testInstance.interceptors.response.use(
    (response) => {
        return response;
    },
    (error) => {
        alert("서버연결에 실패했습니다!  잠시후 재시도해주세요!");
        return Promise.reject(error);
    }
);

export const postMyTestResult = async ({ testTitle, resultObj }) => {
    await testInstance.post(`/${testTitle}`, resultObj);
};

export const getAllTestResults = async (testid, userId) => {
    const response = await testInstance.get(`/${testid}`);
    const filteredData = response.data.filter((el) => el.userId == userId || el.visibility);
    return filteredData;
};

export const toggleTestResult = async ({ testid, testResultObj }) => {
    await testInstance.patch(`/${testid}/${testResultObj.id}`, testResultObj);
};

export const deleteTestResult = async ({ testid, testResultId }) => {
    await testInstance.delete(`/${testid}/${testResultId}`);
};

테스트 관련 데이터 요청함수를 모두 한 파일에 모아주어 관심사가 같은 api함수끼리 뭉쳐있도록 하였다. 이제 tanstack Query를 사용하는 함수들도 분리해줄 필요가 있었다. 물론 재사용성은 적겠지만 코드의 직관성을 높이기 위함이다.

export const usePostMyTestMutation = (testid) => {
    const navigate = useNavigate();
    return useMutation({
        mutationFn: postMyTestResult,
        onSuccess: () => navigate(`/testresult/${testid}`),
        onError: (error) => console.log("error :>> ", error),
    });
};

export const useGetAllResultQuery = (testid, userId) => {
    return useQuery({
        queryKey: queryKeys.boardController.mbtis(),
        queryFn: () => getAllTestResults(testid, userId),
    });
};

export const useToggleVisibilityMutation = () => {
    const queryClient = useQueryClient();
    return useMutation({
        mutationFn: toggleTestResult,
        onSuccess: () => queryClient.invalidateQueries(queryKeys.boardController.mbtis()),
        onError: (error) => console.log("error :>> ", error),
    });
};

export const useDeleteDataMutation = () => {
    const queryClient = useQueryClient();
    return useMutation({
        mutationFn: deleteTestResult,
        onSuccess: () => queryClient.invalidateQueries(queryKeys.boardController.mbtis()),
        onError: (error) => console.log("error :>> ", error),
    });
};

queryKey를 넣어야 하는 부분도 따로 queryKey를 관리하는 객체를 분리하여 사용했다.

queryKey를 분리하는 이유는 협업시 같이 처리되어야 하는 데이터를 불필요하게 다른 이름의 queryKey로 관리되는 것을 사전에 막고, 휴먼에러를 방지하기 위함이다.

물론 개인프로젝트에서는 MBTIS라는 queryKey하나만 쓰였지만, 연습하는 셈 치고 따로 queryKey를 관리하기로 하였다.

export const queryKeys = {
    boardController: {
        mbtis: () => ["MBTIS"],
    },
};

 형태는 챌린지반에서 배웠던 대로 사용하였다.

 

const Test = () => {
    const { testid } = useParams();
    const targetQuestions = questions[testid];
    const { userId, nickname } = useUserInfo();
    const { mutate } = usePostMyTestMutation(targetQuestions.testTitle);
    const [answers, setAnswers] = useState(Array.from({ length: targetQuestions.queList.length }));

    const handleAnswers = (idx, selected) => {
        setAnswers([...answers].with(idx, selected));
    };

    const transformResult = async () => {
        const isAnswersSelected = answers.every((answer) => answer != undefined);
        if (!isAnswersSelected) {
            alert("모든 문항에 답을 해주세요!");
            return;
        }
        const result = calculateMBTI(testid, answers);
        return result;
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        const result = await transformResult();
        const resultObj = { nickname, userId, result, visibility: false };
        if (result) {
            mutate({ testTitle: targetQuestions.testTitle, resultObj });
        }
    };

    return (
        <form onSubmit={(e) => handleSubmit(e)}>
        	//...
        </form>
    );
};

axios와 tanstack query로직을 커스텀훅으로 분리하니 조금 더 나아진 것으로 보인다.

여기서 한번 더 커스텀 훅을 분리하여 함수2개와 state, statehandler, 등을 분리하면 괜찮을 것으로 생각한다.


이제 tailwind로 style을 입혀야하는데 너무 귀찮은 작업이다.

styled-component로 했을 때는 불편하다는 생각이 든 적이 별로 없었는데, tanstack query는 하나 정하려면 공식문서켜서 검색하고 그걸 또 가져와서 이리저리 rem과 px을 비교해보고 사용한다.

좀 불편하다.