Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 리뷰 상세페이지 query 훅 분리 및 HTTP 요청 테스트 진행 #216

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
21f0449
refactor: DetailedPage/index.tsx 리팩토링
BadaHertz52 Aug 3, 2024
184afa4
feat: useGetDetailedReview 훅 생성 및 DetailedReviewPageContents에 적용
BadaHertz52 Aug 3, 2024
a21774b
feat: useSearchParamAndQuery 훅 생성 및 DetailedPageContent에 적용
BadaHertz52 Aug 3, 2024
a06d7e9
refactor: 리뷰 상세페이지에서 id라고 사용했던 key값, params의 key를 reviewId로 변경
BadaHertz52 Aug 3, 2024
e37938c
ci: dependencies에서 jest 삭제 및 ts-jest 설치
BadaHertz52 Aug 3, 2024
b0facf0
ci: jest에서 절대 경로 사용할 수 있도록 jest.config.js 추가
BadaHertz52 Aug 3, 2024
e3c54d4
chore: eslint적용 제외 파일에 jest.config.js, tsconfig.json 추가
BadaHertz52 Aug 3, 2024
a3f1705
ci: jest의 testEnvioronment를 jsdom으로 설정
BadaHertz52 Aug 3, 2024
7e90af0
refactor: useGetDetailedReview에서 query 결과를 모두 반환하는 방식으로 변경
BadaHertz52 Aug 3, 2024
4553c2c
fix: jest에서 msw ver2를 목서버로 사용 시 생기는 오류 수정
BadaHertz52 Aug 3, 2024
76747b5
ci : jest에서 env 파일 읽을 수 있도록 dotenv 설치 및 jest에 적용
BadaHertz52 Aug 3, 2024
7399b2a
fix: mock 핸들러인 getDetailedReview 에서 중복된 쿼리 매개 변수 사용 수정
BadaHertz52 Aug 3, 2024
7cafda8
refactor: getWrongDetailedReview 목서버 핸들러 및 관련 상수 삭제
BadaHertz52 Aug 3, 2024
476c753
feat: queryClientWrapper 생성
BadaHertz52 Aug 3, 2024
83f2bfb
test:리뷰 상세 페이지 api 요청 성공에 대한 테스트 추가
BadaHertz52 Aug 3, 2024
1df5228
fix: groupAccessCodeAtom의 기본값 원래대로 복구
BadaHertz52 Aug 3, 2024
cb756fc
chore:queryClientWrapper 네이밍 표기법을 파스칼 케이스로 변경
BadaHertz52 Aug 4, 2024
4564173
fix: 머지 충돌 방지를 위해 yarn.lock 삭제
BadaHertz52 Aug 6, 2024
5a703ec
Merge branch 'develop' of https://github.com/woowacourse-teams/2024-r…
BadaHertz52 Aug 6, 2024
99f182d
fix: 머지 시 yarn.lock 충돌 해결
BadaHertz52 Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion frontend/src/apis/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const DETAILED_REVIEW_API_PARAMS = {
},
};

export const DETAILED_REVIEW_API_URL = `${process.env.API_BASE_URL}/${DETAILED_REVIEW_API_PARAMS.resource}`;

export const REVIEW_WRITING_API_PARAMS = {
queryString: {
reviewRequestCode: 'reviewRequestCode',
Expand All @@ -14,7 +16,7 @@ export const REVIEW_WRITING_API_PARAMS = {
const endPoint = {
postingReview: `${process.env.API_BASE_URL}/reviews`,
gettingDetailedReview: (reviewId: number, memberId: number) =>
`${process.env.API_BASE_URL}/${DETAILED_REVIEW_API_PARAMS.resource}/${reviewId}?${DETAILED_REVIEW_API_PARAMS.queryString.memberId}=${memberId}`,
`${DETAILED_REVIEW_API_URL}/${reviewId}?${DETAILED_REVIEW_API_PARAMS.queryString.memberId}=${memberId}`,
gettingDataToWriteReview: (reviewRequestCode: string) =>
`${process.env.API_BASE_URL}/reviews/write?${REVIEW_WRITING_API_PARAMS.queryString.reviewRequestCode}=${reviewRequestCode}`,
gettingReviewList: `${process.env.API_BASE_URL}/reviews`,
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as useSidebar } from './useSidebar';
export { default as useModalClose } from './useModalClose';
export { default as useGroupAccessCode } from './useGroupAccessCode';
export { default as useGetDetailedReview } from './review/useGetDetailedReview';
export { default as useSearchParamAndQuery } from './useSearchParamAndQuery';
33 changes: 33 additions & 0 deletions frontend/src/hooks/review/useGetDetailedReview/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { getDetailedReviewApi } from '@/apis/review';
import { REVIEW_QUERY_KEYS } from '@/constants';
import { DetailReviewData } from '@/types';

interface UseGetDetailedReviewProps {
reviewId: number;
memberId: number;
groupAccessCode: string;
}

interface FetchDetailedReviewParams {
reviewId: number;
memberId: number;
groupAccessCode: string;
}

const useGetDetailedReview = ({ reviewId, memberId, groupAccessCode }: UseGetDetailedReviewProps) => {
const fetchDetailedReview = async ({ reviewId, memberId, groupAccessCode }: FetchDetailedReviewParams) => {
const result = await getDetailedReviewApi({ reviewId, memberId, groupAccessCode });
return result;
};

const result = useSuspenseQuery<DetailReviewData>({
queryKey: [REVIEW_QUERY_KEYS.detailedReview, reviewId, memberId],
queryFn: () => fetchDetailedReview({ reviewId, memberId, groupAccessCode }),
});

return result;
};

export default useGetDetailedReview;
26 changes: 26 additions & 0 deletions frontend/src/hooks/review/useGetDetailedReview/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { renderHook, waitFor } from '@testing-library/react';

import { DETAILED_PAGE_MOCK_API_SETTING_VALUES } from '@/mocks/mockData/detailedReviewMockData';
import QueryClientWrapper from '@/queryTestSetup/QueryClientWrapper';

import useGetDetailedReview from '.';
// 아래의 테스트는 로그인이 유효하다는 가정하에서 진행

describe('리뷰 상세페이지 데이터 요청 테스트', () => {
const GROUND_ACCESS_CODE = '1234';

it('유효힌 id,memberId 사용해야 라뷰 상세 페이지 데이터를 불러온다.', async () => {
const { reviewId, memberId } = DETAILED_PAGE_MOCK_API_SETTING_VALUES;

const { result } = renderHook(
() => useGetDetailedReview({ reviewId, memberId, groupAccessCode: GROUND_ACCESS_CODE }),
{ wrapper: QueryClientWrapper },
);

await waitFor(() => {
expect(result.current.status).toBe('success');
});

expect(result.current.data).toBeDefined();
});
});
22 changes: 22 additions & 0 deletions frontend/src/hooks/useSearchParamAndQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useLocation, useParams } from 'react-router';

interface UseSearchParamAndQueryProps {
paramKey: string;
queryStringKey?: string;
}
/**
* url에서 원하는 param, queryString의 값을 가져온다.
* @param paramKey: 가져오고 싶은 param의 key
* @param queryStringKey: 가져오고 싶은 queryString의 key (옵셔널)
*/
const useSearchParamAndQuery = ({ paramKey, queryStringKey }: UseSearchParamAndQueryProps) => {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);

return {
param: useParams()[`${paramKey}`],
queryString: queryStringKey ? queryParams.get(queryStringKey) : null,
};
};

export default useSearchParamAndQuery;
2 changes: 1 addition & 1 deletion frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const router = createBrowserRouter([
element: <ReviewPreviewListPage />,
},
{
path: 'user/detailed-review/:id',
path: 'user/detailed-review/:reviewId',
element: <DetailedReviewPage />,
},
{
Expand Down
37 changes: 15 additions & 22 deletions frontend/src/mocks/handlers/review.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { http, HttpResponse } from 'msw';

import endPoint from '@/apis/endpoints';
import endPoint, { DETAILED_REVIEW_API_PARAMS, DETAILED_REVIEW_API_URL } from '@/apis/endpoints';

import {
DETAILED_REVIEW_MOCK_DATA,
DETAILED_PAGE_MOCK_API_SETTING_VALUES,
DETAILED_PAGE_ERROR_API_VALUES,
} from '../mockData/detailedReviewMockData';
import { DETAILED_REVIEW_MOCK_DATA, DETAILED_PAGE_MOCK_API_SETTING_VALUES } from '../mockData/detailedReviewMockData';
import { REVIEW_PREVIEW_LIST } from '../mockData/reviewPreviewList';
import { REVIEW_WRITING_DATA } from '../mockData/reviewWritingData';

Expand All @@ -18,23 +14,20 @@ export const PAGE = {
};

const getDetailedReview = () =>
http.get(
endPoint.gettingDetailedReview(
DETAILED_PAGE_MOCK_API_SETTING_VALUES.reviewId,
DETAILED_PAGE_MOCK_API_SETTING_VALUES.memberId,
),
async () => {
http.get(new RegExp(`^${DETAILED_REVIEW_API_URL}/\\d+$`), async ({ request }) => {
//요청 url에서 reviewId, memberId 추출
const url = new URL(request.url);
const urlReviewId = url.pathname.replace(`/${DETAILED_REVIEW_API_PARAMS.resource}/`, '');
const urlMemberId = url.searchParams.get(DETAILED_REVIEW_API_PARAMS.queryString.memberId);

const { reviewId, memberId } = DETAILED_PAGE_MOCK_API_SETTING_VALUES;
// 유효한 reviewId, memberId일 경우에만 데이터 반환
if (Number(urlReviewId) == reviewId && Number(urlMemberId) === memberId) {
return HttpResponse.json(DETAILED_REVIEW_MOCK_DATA);
},
);
}

const getWrongDetailReview = () =>
http.get(
endPoint.gettingDetailedReview(DETAILED_PAGE_ERROR_API_VALUES.reviewId, DETAILED_PAGE_ERROR_API_VALUES.memberId),
async () => {
return HttpResponse.json({ error: '잘못된 상세리뷰 요청' }, { status: 404 });
},
);
return HttpResponse.json({ error: '잘못된 상세리뷰 요청' }, { status: 404 });
});

const getDataToWriteReview = () =>
http.get(endPoint.gettingDataToWriteReview(10), async ({ request }) => {
Expand Down Expand Up @@ -68,6 +61,6 @@ const getReviewPreviewList = () => {
});
};

const reviewHandler = [getDetailedReview(), getWrongDetailReview(), getReviewPreviewList(), getDataToWriteReview()];
const reviewHandler = [getDetailedReview(), getReviewPreviewList(), getDataToWriteReview()];

export default reviewHandler;
5 changes: 0 additions & 5 deletions frontend/src/mocks/mockData/detailedReviewMockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ export const DETAILED_PAGE_MOCK_API_SETTING_VALUES = {
memberId: 2,
};

export const DETAILED_PAGE_ERROR_API_VALUES = {
reviewId: 0,
memberId: 0,
};

const ANSWER =
'림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. \n 바람은 여전히 그를 감싸며, 그의 마음 속 깊은 곳에 있는 꿈과 희망을 불러일으켰습니다.\n 림순은 미소 지으며 앞으로 나아갔습니다.림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. 림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. \n 바람은 여전히 그를 감싸며, 그의 마음 속 깊은 곳에 있는 꿈과 희망을 불러일으켰습니다.\n 림순은 미소 지으며 앞으로 나아갔습니다.림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. ';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { useLocation, useParams } from 'react-router';

import { DETAILED_REVIEW_API_PARAMS } from '@/apis/endpoints';
import { getDetailedReviewApi } from '@/apis/review';
import { LoginRedirectModal } from '@/components';
import { REVIEW_QUERY_KEYS } from '@/constants';
import { useGetDetailedReview, useSearchParamAndQuery } from '@/hooks';
import { ReviewDescription, ReviewSection, KeywordSection } from '@/pages/DetailedReviewPage/components';
import { DetailReviewData } from '@/types';

import * as S from './styles';

interface DetailedReviewPageContentsProps {
groupAccessCode: string;
}
const DetailedReviewPageContents = ({ groupAccessCode }: DetailedReviewPageContentsProps) => {
const { id } = useParams<{ id: string }>();
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const memberId = queryParams.get(DETAILED_REVIEW_API_PARAMS.queryString.memberId);

const fetchDetailedReview = async (reviewId: number, memberId: number, groupAccessCode: string) => {
const result = await getDetailedReviewApi({ reviewId, memberId, groupAccessCode });
return result;
};
const { param: reviewId, queryString: memberId } = useSearchParamAndQuery({
paramKey: 'reviewId',
queryStringKey: DETAILED_REVIEW_API_PARAMS.queryString.memberId,
});

const { data: detailedReview } = useSuspenseQuery<DetailReviewData>({
queryKey: [REVIEW_QUERY_KEYS.detailedReview, id, memberId],
queryFn: () => fetchDetailedReview(Number(id), Number(memberId), groupAccessCode),
const { data: detailedReview } = useGetDetailedReview({
reviewId: Number(reviewId),
memberId: Number(memberId),
groupAccessCode,
});

if (!detailedReview) throw new Error(' 상세보기 리뷰 데이터를 가져올 수 없어요.');
if (!groupAccessCode) return <LoginRedirectModal />;
// TODO: 리뷰 공개/비공개 토글 버튼 기능
return (
<S.DetailedReviewPageContents>
Expand Down
14 changes: 5 additions & 9 deletions frontend/src/pages/DetailedReviewPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@ import { DetailedReviewPageContents } from './components';
const DetailedReviewPage = () => {
const { groupAccessCode } = useGroupAccessCode();

if (!groupAccessCode) return <LoginRedirectModal />;

return (
<>
{groupAccessCode ? (
<ErrorSuspenseContainer>
<DetailedReviewPageContents groupAccessCode={groupAccessCode} />
</ErrorSuspenseContainer>
) : (
<LoginRedirectModal />
)}
</>
<ErrorSuspenseContainer>
<DetailedReviewPageContents groupAccessCode={groupAccessCode} />
</ErrorSuspenseContainer>
);
};

Expand Down
11 changes: 11 additions & 0 deletions frontend/src/queryTestSetup/QueryClientWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { EssentialPropsWithChildren } from '@/types';

const queryClient = new QueryClient();

const QueryClientWrapper = ({ children }: EssentialPropsWithChildren) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);

export default QueryClientWrapper;