-
Notifications
You must be signed in to change notification settings - Fork 2
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] 리뷰 상세 페이지에 react-query 추가 및 리팩토링 #161
Changes from 30 commits
e3eaa42
0cb8d19
0a4cec6
19fa32b
339c22c
90f1784
f2e96a7
9721bda
9e31285
251af38
e45ddb4
41d8fb8
54d927c
df18d3a
da20f40
3783ed7
80ca5a2
528eb22
6af1a65
91d0a55
d13fdfd
f20698f
f552fcf
d6d7696
9745350
67e2c5a
4415a36
872ff0b
bc93a51
3032874
52f8de6
c148246
0da75a9
88a2c23
79ee76b
abe9154
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,14 @@ | ||
export const DETAILED_REVIEW_API_PARAMS = { | ||
resource: 'reviews', | ||
queryString: { | ||
memberId: 'memberId', | ||
}, | ||
}; | ||
|
||
const endPoint = { | ||
postingReview: `${process.env.API_BASE_URL}/reviews`, | ||
gettingDetailedReview: (reviewId: number, memberId: number) => | ||
`${process.env.API_BASE_URL}/reviews/${reviewId}?memberId=${memberId}`, | ||
`${process.env.API_BASE_URL}/${DETAILED_REVIEW_API_PARAMS.resource}/${reviewId}?${DETAILED_REVIEW_API_PARAMS.queryString.memberId}=${memberId}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추후에 필요한 queryString을 위와 같이 객체 형태로 받아, 요청 URL을 만들어주는 유틸 함수를 만들어도 좋을 것 같아요! |
||
gettingDataToWriteReview: (reviewerGroupId: number) => | ||
`${process.env.API_BASE_URL}/reviews/write?reviewerGroupId=${reviewerGroupId}`, | ||
gettingReviewList: (revieweeId: number, lastReviewId: number, memberId: number) => | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import { ReviewData, WritingReviewInfoData } from '@/types'; | ||
//리뷰 작성 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 주석이 추가된 이유가 있을까용? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. develop 받을 때 부터 있던 거라 누군가의 의도일거라 생각했는데 충돌 과정에서 제거되어야하는 주석이 살아있었나보네요. 삭제했어요. |
||
import { DetailReviewData, ReviewData, WritingReviewInfoData } from '@/types'; | ||
|
||
import createApiErrorMessage from './apiErrorMessageCreator'; | ||
import endPoint from './endpoints'; | ||
|
@@ -46,7 +47,7 @@ export const getDetailedReviewApi = async ({ reviewId, memberId }: { reviewId: n | |
throw new Error(createApiErrorMessage(response.status)); | ||
} | ||
|
||
const data = await response.json(); | ||
const data: DetailReviewData = await response.json(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 단순 궁금증인데 저는 두 번째 방법을 주로 사용해요. 첫 번째 방법으로 사용하신 이유가 있을까요 // 첫 번째 방법
const data: DetailReviewData = await response.json();
return data; // 두 번째 방법
const data = await response.json();
return data as DetailReviewData; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 올리의 질문을 받고 첫 번째와 두 번째 방법의 차이와 어느 쪽이 더 나은 방향인지 찾아봤어요. 둘 다 타입 강제이지만 첫 번째 방법은 타입 추론이 정확하지 않을 수도 있다고 하네요. 타입을 강제하는 방면에서는 두 번째 코드가 나아서 두 번째 코드로 수정했어요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (단순 궁금증) data의 타입이 자동으로 추론되지 않아서 추가한 부분인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞습니다 |
||
return data; | ||
}; | ||
|
||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import * as S from './styles'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comments 👍👍 전혀 몰랐읍니다 |
||
|
||
interface RevieweeCommentsProps { | ||
comment: string; | ||
} | ||
|
||
const DEFAULT_COMMENTS = '안녕하세요! 리뷰 잘 부탁드립니다.'; | ||
|
||
const RevieweeComments = ({ comment }: RevieweeCommentsProps) => { | ||
return <S.RevieweeComments>{comment || DEFAULT_COMMENTS}</S.RevieweeComments>; | ||
}; | ||
|
||
export default RevieweeComments; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { FallbackProps } from 'react-error-boundary'; | ||
import { useNavigate } from 'react-router'; | ||
|
||
import ErrorSection from '../ErrorSection'; | ||
|
||
const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => { | ||
const navigate = useNavigate(); | ||
const handleGoHome = () => { | ||
resetErrorBoundary(); | ||
navigate('/'); //TODO : 홈 페이지 경로가 결정되면 변경 | ||
}; | ||
Comment on lines
+6
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. errorBoundary에 전달되는 |
||
|
||
return <ErrorSection errorMessage={error.message} handleGoHome={handleGoHome} handleReload={resetErrorBoundary} />; | ||
}; | ||
|
||
export default ErrorFallback; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,16 +37,15 @@ const ErrorSection = ({ errorMessage, handleReload, handleGoHome }: ErrorSection | |
</S.ErrorLogoWrapper> | ||
<S.ErrorMessage>{errorMessage}</S.ErrorMessage> | ||
<S.Container> | ||
{buttons.map((button, index) => ( | ||
<S.ButtonContainer key={index}> | ||
<Button | ||
buttonType={button.buttonType} | ||
text={button.text} | ||
image={button.image} | ||
imageDescription={button.imageDescription} | ||
onClick={button.onClick} | ||
/> | ||
</S.ButtonContainer> | ||
{buttons.map((button) => ( | ||
<Button | ||
key={button.text} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. key에 button.text가 들어간 이유가 궁금합니다! 인덱스보다 더 안정적으로 렌더링하기 위해서인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞아요. index를 사용하지 않을 수 있다면 이거를 최대한 피하려고 해요. 한글로도 key를 사용할 수 있지만 일반된 사용을 아니라서, key를 추가했고 buttons의 네이밍을 buttonList로 변경했어요. |
||
buttonType={button.buttonType} | ||
text={button.text} | ||
image={button.image} | ||
imageDescription={button.imageDescription} | ||
onClick={button.onClick} | ||
/> | ||
))} | ||
</S.Container> | ||
</S.Layout> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { QueryErrorResetBoundary } from '@tanstack/react-query'; | ||
import { Suspense } from 'react'; | ||
import { ErrorBoundary } from 'react-error-boundary'; | ||
|
||
import LoadingPage from '@/pages/LoadingPage'; | ||
import { EssentialPropsWithChildren } from '@/types'; | ||
|
||
import ErrorFallback from '../ErrorFallback'; | ||
|
||
const ErrorSuspenseContainer = ({ children }: EssentialPropsWithChildren) => { | ||
return ( | ||
<QueryErrorResetBoundary> | ||
{({ reset }) => ( | ||
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={reset}> | ||
<Suspense fallback={<LoadingPage />}>{children}</Suspense> | ||
</ErrorBoundary> | ||
)} | ||
</QueryErrorResetBoundary> | ||
); | ||
}; | ||
|
||
export default ErrorSuspenseContainer; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as ErrorSection } from './ErrorSection'; | ||
export { default as ErrorSuspenseContainer } from './ErrorSuspenseContainer'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './layouts'; | ||
export * from './common'; | ||
export * from './error'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './page'; | ||
export * from './apiErrorMessage'; | ||
export * from './review'; | ||
export * from './queryKeys'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const REVIEW_QUERY_KEYS = { | ||
detailedReview: 'detailedReview', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,32 @@ import { http, HttpResponse } from 'msw'; | |
|
||
import endPoint from '@/apis/endpoints'; | ||
|
||
import { DETAILED_REVIEW_MOCK_DATA } from '../mockData/detailedReviewMockData'; | ||
import { | ||
DETAILED_REVIEW_MOCK_DATA, | ||
DETAILED_PAGE_MOCK_API_SETTING_VALUES, | ||
DETAILED_PAGE_ERROR_API_VALUES, | ||
} from '../mockData/detailedReviewMockData'; | ||
import { REVIEW_PREVIEW_LIST } from '../mockData/reviewPreviewList'; | ||
import { REVIEW_WRITING_DATA } from '../mockData/reviewWritingData'; | ||
|
||
const getDetailedReview = () => | ||
http.get(endPoint.gettingDetailedReview(123456, 123456), async ({ request }) => { | ||
return HttpResponse.json(DETAILED_REVIEW_MOCK_DATA); | ||
}); | ||
http.get( | ||
endPoint.gettingDetailedReview( | ||
DETAILED_PAGE_MOCK_API_SETTING_VALUES.reviewId, | ||
DETAILED_PAGE_MOCK_API_SETTING_VALUES.memberId, | ||
), | ||
async () => { | ||
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 }); | ||
}, | ||
); | ||
Comment on lines
+24
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 잘못된 요청에 대한 에러를 반환하는 핸들러를 만들었군요!
Comment on lines
+24
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러가 발생했을 때의 핸들러도 만들어주셨네요👍 |
||
|
||
const getDataToWriteReview = () => | ||
http.get(endPoint.gettingDataToWriteReview(10), async ({ request }) => { | ||
|
@@ -21,6 +39,6 @@ const getReviewPreviewList = () => | |
return HttpResponse.json(REVIEW_PREVIEW_LIST); | ||
}); | ||
|
||
const reviewHandler = [getDetailedReview(), getReviewPreviewList(), getDataToWriteReview()]; | ||
const reviewHandler = [getDetailedReview(), getWrongDetailReview(), getReviewPreviewList(), getDataToWriteReview()]; | ||
|
||
export default reviewHandler; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,27 @@ | ||
import { DetailReviewData } from '@/types'; | ||
|
||
export const DETAILED_PAGE_MOCK_API_SETTING_VALUES = { | ||
reviewId: 5, | ||
memberId: 2, | ||
}; | ||
|
||
export const DETAILED_PAGE_ERROR_API_VALUES = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 상수화가 잘 되어있어서 아래 모킹 핸들러에서 의도를 파악하기가 더 쉬웠어요~~ 굿굿 |
||
reviewId: 0, | ||
memberId: 0, | ||
}; | ||
|
||
const ANSWER = | ||
'림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. \n 바람은 여전히 그를 감싸며, 그의 마음 속 깊은 곳에 있는 꿈과 희망을 불러일으켰습니다.\n 림순은 미소 지으며 앞으로 나아갔습니다.림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. 림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. \n 바람은 여전히 그를 감싸며, 그의 마음 속 깊은 곳에 있는 꿈과 희망을 불러일으켰습니다.\n 림순은 미소 지으며 앞으로 나아갔습니다.림순의 바람은 그윽한 산들바람처럼 잔잔하게 흘러갔습니다. \n 눈부신 햇살이 그의 어깨를 감싸며, 푸른 하늘 아래 펼쳐진 들판을 바라보았습니다.\n 그의 마음은 자연의 아름다움 속에서 평온을 찾았고, 그 순간마다 삶의 소중함을 느꼈습니다.\n 그는 늘 그러한 순간들을 기억하며, 미래의 나날들을 기대했습니다. '; | ||
|
||
export const DETAILED_REVIEW_MOCK_DATA: DetailReviewData = { | ||
id: 123456, | ||
createdAt: new Date('2024-07-16'), | ||
revieweeName: 'badahertz52', | ||
isPublic: false, | ||
reviewerGroup: { | ||
id: 123456, | ||
name: 'review-me', | ||
description: 'vite 쓰고 싶다.', | ||
description: '', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅋㅋㅋㅋㅋㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vite 쓰게 해주세요. |
||
thumnailUrl: '', | ||
}, | ||
reviews: [ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
렌더링 시 API를 불러오는 페이지가 있고 그렇지 못한 페이지가 있을 수 있어요.
그래서
ErrorBoundary, Suspense
를 렌더링 시 API를 요청하는 페이지에만 적용하는 것을 시도해봤어요. 렌더링 시 API를 요청하는 페이지에서 아래처럼 시도를 해봤는데Suspense
가 Loading을 잡지 못했어요. 그레서Outlet
을ErrorSuspenseContainer
로 감싸는 방식을 선택하게 되었어요.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
라우팅 페이지에서 아래처럼 개별로 감싸 줘도 작동하지 않나요? 리액트 라우터 버전이 예전 거긴 하지만 혹시나 해서 가져왔습니다!
참고한 글
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오! 😮 router의 element안에서 필요한 페이지마다 적용하도록 하면 되네요!!