From 37e0ba428461a405493e0d0b97b7dcdfa3cbc1c3 Mon Sep 17 00:00:00 2001 From: solp721 Date: Fri, 8 Aug 2025 04:44:21 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat=20:=20=EB=B0=B0=EB=9F=B4=20=EC=9D=B5?= =?UTF-8?q?=EC=8A=A4=ED=8F=AC=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[universityName]/[lectureId]/page.tsx | 8 +++----- cypress/e2e/home.cy.ts | 5 ----- domains/lecture/index.ts | 1 + domains/review/index.ts | 1 + 4 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 cypress/e2e/home.cy.ts diff --git a/app/[universityName]/[lectureId]/page.tsx b/app/[universityName]/[lectureId]/page.tsx index c1882a4..7324438 100644 --- a/app/[universityName]/[lectureId]/page.tsx +++ b/app/[universityName]/[lectureId]/page.tsx @@ -1,13 +1,11 @@ -import { ReviewCardListSkeleton } from '@/domains/review'; -import { LectureInfoSkeleton } from '@/domains/lecture'; -import { LectureInfoServer } from '@/domains/lecture/server/components/LectureInfoServer'; -import { ReviewListServer } from '@/domains/review/server/components/ReviewListServer'; +import { ReviewCardListSkeleton, ReviewListServer } from '@/domains/review'; +import { LectureInfoSkeleton, LectureInfoServer } from '@/domains/lecture'; import { Suspense } from 'react'; -import styles from '@/styles/global.module.css'; import { dehydrate, HydrationBoundary } from '@tanstack/react-query'; import { createQueryClient, prefetchLectureRating, prefetchAllSortOptions } from '@/utils'; import { universityNames } from '@/constants'; import { getLectureListStatic } from '@/lib'; +import styles from '@/styles/global.module.css'; export async function generateStaticParams() { const allParams = await Promise.allSettled( diff --git a/cypress/e2e/home.cy.ts b/cypress/e2e/home.cy.ts deleted file mode 100644 index 2949433..0000000 --- a/cypress/e2e/home.cy.ts +++ /dev/null @@ -1,5 +0,0 @@ -describe('Home', () => { - it('should display the home page', () => { - cy.visit('/'); - }); -}); diff --git a/domains/lecture/index.ts b/domains/lecture/index.ts index e7df9a8..c243d16 100644 --- a/domains/lecture/index.ts +++ b/domains/lecture/index.ts @@ -1,6 +1,7 @@ export { default as LectureCardHybrid } from './server/components/LectureCardHybrid'; export { default as LectureListHybrid } from './server/components/LectureListHybrid'; export { default as LectureInfoComponent } from './server/components/LectureInfo'; +export { LectureInfoServer } from './server/components/LectureInfoServer'; export { default as DynamicRating } from './client/components/DynamicRating'; export { default as DynamicReviewCount } from './client/components/DynamicReviewCount'; diff --git a/domains/review/index.ts b/domains/review/index.ts index b21e6a8..46bf438 100644 --- a/domains/review/index.ts +++ b/domains/review/index.ts @@ -1,4 +1,5 @@ export { default as ReviewCard } from './server/components/ReviewCard'; +export { ReviewListServer } from './server/components/ReviewListServer'; export { default as ReviewCardModal } from './client/components/ReviewCardModal'; export { default as WriteReviewModal } from './client/components/WriteReviewModal'; From d638c32e145d03b4f48b4eb9084237af9bf3c947 Mon Sep 17 00:00:00 2001 From: solp721 Date: Sat, 9 Aug 2025 02:01:26 +0900 Subject: [PATCH 02/18] =?UTF-8?q?refactor=20:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 8 ++++---- app/providers/AuthHydrationProvider.tsx | 7 ++++++- lib/auth/index.ts | 3 --- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 76efdfc..b2a915c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -27,11 +27,11 @@ export default function RootLayout({ return ( - - + +
{children}
-
-
+ + ); diff --git a/app/providers/AuthHydrationProvider.tsx b/app/providers/AuthHydrationProvider.tsx index a33c5de..5d6bf54 100644 --- a/app/providers/AuthHydrationProvider.tsx +++ b/app/providers/AuthHydrationProvider.tsx @@ -1,5 +1,6 @@ import { QueryClient, HydrationBoundary, dehydrate } from '@tanstack/react-query'; import { hydrateAuthState } from '@/utils/authHydration'; +import AppProviders from './AppProviders'; interface AuthHydrationProviderProps { children: React.ReactNode; @@ -10,5 +11,9 @@ export default async function AuthHydrationProvider({ children }: AuthHydrationP await hydrateAuthState(queryClient); - return {children}; + return ( + + {children} + + ); } diff --git a/lib/auth/index.ts b/lib/auth/index.ts index 0c4bfee..66ce350 100644 --- a/lib/auth/index.ts +++ b/lib/auth/index.ts @@ -1,6 +1,5 @@ import { HTTP_STATUS } from '@/constants'; import { LoginParams, LoginResponse } from '@/domains/auth'; -import { setToken } from '@/utils'; import { apiPost } from '../apiClient'; export async function postLogin({ id, password }: LoginParams): Promise { @@ -18,8 +17,6 @@ export async function postLogin({ id, password }: LoginParams): Promise Date: Sat, 9 Aug 2025 02:02:31 +0900 Subject: [PATCH 03/18] =?UTF-8?q?fix=20:=20=EA=B8=B0=EC=A1=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EB=B0=94=EC=9D=B4=EB=8D=94=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index b2a915c..ab7d0ce 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -28,9 +28,7 @@ export default function RootLayout({ - -
{children}
-
+
{children}
From 1c0543223d629b4bb855ae44b007ac034ebfe03d Mon Sep 17 00:00:00 2001 From: solp721 Date: Sat, 9 Aug 2025 02:32:13 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat=20:=20=EB=A6=AC=EB=B7=B0=20CRUD/?= =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=8B=9C=20ISR=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=A6=AC=EB=B0=B8=EB=A6=AC=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/revalidate/route.ts | 21 ++++++++++++- .../client/components/DeleteReviewButton.tsx | 15 ++++++++++ .../client/hooks/useLikeReviewAction.ts | 27 ++++++++++++++++- .../review/client/hooks/useReviewSubmit.ts | 17 +++++++++++ lib/apiClient.ts | 30 ++++++++++++++++++- lib/lecture/getLectureInfo.ts | 21 ++++++++----- lib/lecture/getLectureRating.ts | 18 +++++------ lib/review/review.ts | 18 +++++++---- 8 files changed, 140 insertions(+), 27 deletions(-) diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index f5e8ac0..4012046 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -3,7 +3,7 @@ import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { try { - const { universityName, action } = await request.json(); + const { universityName, action, lectureId, userNumber } = await request.json(); if (!universityName) { return NextResponse.json({ message: 'University name is required' }, { status: 400 }); @@ -14,6 +14,23 @@ export async function POST(request: NextRequest) { case 'lecture_removed': case 'lecture_modified': await revalidateTag(`lectures-${universityName}`); + if (lectureId) { + await revalidateTag(`lecture-info-${universityName}-${lectureId}`); + await revalidateTag(`reviews-${lectureId}`); + } + break; + + case 'review_added': + case 'review_updated': + case 'review_deleted': + if (lectureId) { + await revalidateTag(`reviews-${lectureId}`); + await revalidateTag(`lecture-info-${universityName}-${lectureId}`); + } + if (userNumber) { + await revalidateTag(`my-reviews-${userNumber}`); + await revalidateTag(`my-lectures-${userNumber}`); + } break; case 'university_added': @@ -29,6 +46,8 @@ export async function POST(request: NextRequest) { timestamp: new Date().toISOString(), action, universityName, + lectureId, + userNumber, }); } catch (error) { return NextResponse.json({ message: 'Internal server error' }, { status: 500 }); diff --git a/domains/mypage/client/components/DeleteReviewButton.tsx b/domains/mypage/client/components/DeleteReviewButton.tsx index 7757f3e..53be3a4 100644 --- a/domains/mypage/client/components/DeleteReviewButton.tsx +++ b/domains/mypage/client/components/DeleteReviewButton.tsx @@ -15,6 +15,21 @@ export function DeleteReviewButton({ review, userNumber }: ReviewCardProps) { }); await Promise.all([revalidateReviewsPage(), revalidateLecturesPage()]); + + try { + await fetch('/api/revalidate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'review_deleted', + universityName: review.lecture.university, + lectureId: String(review.lecture.lectureId), + userNumber, + }), + }); + } catch (error) { + console.error('리뷰 삭제 태그 재검증 실패', error); + } } catch (error) { alert('리뷰 삭제에 실패했습니다.'); } diff --git a/domains/review/client/hooks/useLikeReviewAction.ts b/domains/review/client/hooks/useLikeReviewAction.ts index 551fc2d..43d3add 100644 --- a/domains/review/client/hooks/useLikeReviewAction.ts +++ b/domains/review/client/hooks/useLikeReviewAction.ts @@ -4,6 +4,9 @@ import { useAuth } from '@/domains/auth'; import { useLikeReview } from './useLikeReview'; import { Review } from '../../shared/types/review'; import { Dispatch, SetStateAction } from 'react'; +import { usePathname } from 'next/navigation'; +import { getUserNumber } from '../../shared/utils'; +import type { UserData } from '@/domains/auth'; interface UseLikeReviewActionParams { review: Review; @@ -14,6 +17,7 @@ interface UseLikeReviewActionParams { export function useLikeReviewAction({ review, onSuccess, setReview }: UseLikeReviewActionParams) { const { user, isLoggedIn } = useAuth(); const { mutate: likeReview } = useLikeReview(); + const pathname = usePathname(); const handleLikeClick = (event?: React.MouseEvent) => { if (event) { @@ -27,7 +31,7 @@ export function useLikeReviewAction({ review, onSuccess, setReview }: UseLikeRev likeReview( { postId: review.postId, - userNumber: Number(user?.name), + userNumber: getUserNumber(user as UserData) ?? 0, }, { onSuccess: () => { @@ -41,6 +45,27 @@ export function useLikeReviewAction({ review, onSuccess, setReview }: UseLikeRev }); } + try { + const segments = pathname.split('/'); + const universityName = decodeURIComponent(segments[1] || ''); + const lectureId = segments[2]; + const userNumber = getUserNumber(user as UserData); + if (lectureId && userNumber) { + fetch('/api/revalidate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'review_updated', + universityName, + lectureId, + userNumber, + }), + }).catch(() => {}); + } + } catch (error) { + console.error('리뷰 좋아요 태그 재검증 실패', error); + } + onSuccess?.(); }, } diff --git a/domains/review/client/hooks/useReviewSubmit.ts b/domains/review/client/hooks/useReviewSubmit.ts index 968420b..417719b 100644 --- a/domains/review/client/hooks/useReviewSubmit.ts +++ b/domains/review/client/hooks/useReviewSubmit.ts @@ -75,6 +75,23 @@ export function useReviewSubmit({ ? '리뷰가 성공적으로 수정되었습니다.' : UI_MESSAGES.REVIEW.POST_SUCCESS; alert(successMessage); + try { + const pathSegments = pathname.split('/'); + const universityName = decodeURIComponent(pathSegments[1] || ''); + const action = mode === 'edit' ? 'review_updated' : 'review_added'; + fetch('/api/revalidate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action, + universityName, + lectureId, + userNumber, + }), + }).catch(() => {}); + } catch (error) { + console.error('리뷰 작성/수정 태그 재검증 실패', error); + } onSuccess?.(); } else { const errorMessage = diff --git a/lib/apiClient.ts b/lib/apiClient.ts index 0959227..9854f2b 100644 --- a/lib/apiClient.ts +++ b/lib/apiClient.ts @@ -6,7 +6,9 @@ const getHeaders = () => ({ 'Content-Type': 'application/json', }); -export async function apiRequest(endpoint: string, options: RequestInit = {}) { +type NextFetchOptions = RequestInit & { next?: { tags?: string[] } }; + +export async function apiRequest(endpoint: string, options: NextFetchOptions = {}) { const url = `${API_BASE_URL}${endpoint}`; const config = { @@ -92,3 +94,29 @@ export async function apiGetWithCache( cache: cacheOption, }); } + +export async function apiGetWithTags( + endpoint: string, + params?: Record, + tags?: string[], + cacheOption: RequestCache = 'force-cache' +) { + let url = endpoint; + + if (params) { + const searchParams = new URLSearchParams(); + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + searchParams.append(key, String(value)); + } + }); + const qs = searchParams.toString(); + if (qs) url += `?${qs}`; + } + + return apiRequest(url, { + method: 'GET', + cache: cacheOption, + next: tags && tags.length > 0 ? { tags } : undefined, + }); +} diff --git a/lib/lecture/getLectureInfo.ts b/lib/lecture/getLectureInfo.ts index d09087c..0c4de5d 100644 --- a/lib/lecture/getLectureInfo.ts +++ b/lib/lecture/getLectureInfo.ts @@ -1,17 +1,19 @@ import { ERROR_MESSAGES, HTTP_STATUS } from '@/constants'; import { LectureInfoResult, LectureInfo } from '@/domains/lecture'; import { ClassData } from '@/domains/mypage'; -import { apiGetWithCache } from '../apiClient'; +import { apiGetWithTags } from '../apiClient'; export async function getLectureInfo( universityName: string, lectureId: string ): Promise { try { - const response = await apiGetWithCache('/class', { - university: universityName, - lectureId: lectureId, - }); + const response = await apiGetWithTags( + '/class', + { university: universityName, lectureId }, + [`lecture-info-${universityName}-${lectureId}`], + 'force-cache' + ); if (response.status === HTTP_STATUS.OK && response.data) { return { @@ -41,9 +43,12 @@ export async function getLectureInfo( export async function getMyLectures(userNumber: number): Promise { try { - const response = await apiGetWithCache('/class/me', { - userNumber: userNumber.toString(), - }); + const response = await apiGetWithTags( + '/class/me', + { userNumber }, + [`my-lectures-${userNumber}`], + 'force-cache' + ); if (response.status === 200 && Array.isArray(response.data)) { return response.data; diff --git a/lib/lecture/getLectureRating.ts b/lib/lecture/getLectureRating.ts index 5853b8d..75d29f7 100644 --- a/lib/lecture/getLectureRating.ts +++ b/lib/lecture/getLectureRating.ts @@ -1,5 +1,5 @@ import { HTTP_STATUS } from '@/constants'; -import { apiRequest } from '../apiClient'; +import { apiRequest, apiGetWithTags } from '../apiClient'; export interface RatingResult { success: boolean; @@ -13,16 +13,12 @@ export async function getLectureRating( universityName: string ): Promise { try { - const response = await apiRequest('/class', { - method: 'GET', - cache: 'no-store', - }); - - const url = `/class?university=${universityName}&lectureId=${lectureId}`; - const data = await apiRequest(url, { - method: 'GET', - cache: 'no-store', - }); + const data = await apiGetWithTags( + '/class', + { university: universityName, lectureId }, + [`lecture-info-${universityName}-${lectureId}`], + 'force-cache' + ); if (data.status === HTTP_STATUS.OK && data.data) { if (data.data.averageStarLating !== undefined) { diff --git a/lib/review/review.ts b/lib/review/review.ts index 3d3be6c..5b37d03 100644 --- a/lib/review/review.ts +++ b/lib/review/review.ts @@ -1,7 +1,7 @@ import { ERROR_MESSAGES, HTTP_STATUS } from '@/constants'; import { ReviewResult, PostReviewRequest, PostReviewResult } from '@/domains/review'; import { MyReviewData, DeleteReviewRequest, DeleteReviewResult } from '@/domains/mypage'; -import { apiPost, apiPatch, apiDelete, apiGetWithCache } from '../apiClient'; +import { apiPost, apiPatch, apiDelete, apiGetWithTags } from '../apiClient'; export async function getReviewList({ lectureId, @@ -22,7 +22,12 @@ export async function getReviewList({ ...(recent && { recent }), }; - const response = await apiGetWithCache('/review', params); + const response = await apiGetWithTags( + '/review', + params, + [`reviews-${lectureId}`], + 'force-cache' + ); if (response.status === HTTP_STATUS.OK && Array.isArray(response.data)) { return { @@ -140,9 +145,12 @@ export async function deleteReview(request: DeleteReviewRequest): Promise { try { - const response = await apiGetWithCache('/review/me', { - userNumber: userNumber.toString(), - }); + const response = await apiGetWithTags( + '/review/me', + { userNumber }, + [`my-reviews-${userNumber}`], + 'force-cache' + ); if (response.status === 200 && Array.isArray(response.data)) { return response.data; From 9cc3848309574c5b04a519d6bbcb51722d2a938a Mon Sep 17 00:00:00 2001 From: solp721 Date: Mon, 11 Aug 2025 02:01:39 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat=20:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=B3=84=20test-id=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/starRating/InteractiveStarRating.tsx | 7 ++++++- components/ui/button/Button.tsx | 12 +++--------- components/ui/header/Header.tsx | 6 +++--- .../ui/universityList/DropdownUniversityList.tsx | 4 +++- components/ui/universityList/UniversityList.tsx | 2 ++ domains/auth/client/components/LoginModal.tsx | 9 +++++++-- .../lecture/server/components/LectureCardHybrid.tsx | 4 ++-- domains/lecture/server/components/LectureInfo.tsx | 8 ++++---- .../lecture/server/components/LectureListHybrid.tsx | 6 ++++-- .../mypage/client/components/DeleteReviewButton.tsx | 7 ++++++- .../mypage/client/components/PatchReviewButton.tsx | 2 +- domains/mypage/server/components/LectureCard.tsx | 2 +- domains/mypage/server/components/LectureList.tsx | 2 +- domains/mypage/server/components/ReviewCard.tsx | 10 +++++++--- domains/mypage/server/components/ReviewList.tsx | 4 ++-- domains/review/client/components/LikeReview.tsx | 4 ++-- domains/review/client/components/ReviewHeader.tsx | 2 +- domains/review/client/components/ReviewList.tsx | 4 ++-- .../review/client/components/ReviewSortSelector.tsx | 11 +++++++---- .../review/client/components/WriteReviewModal.tsx | 8 +++++--- domains/review/server/components/ReviewCard.tsx | 6 +++--- 21 files changed, 72 insertions(+), 48 deletions(-) diff --git a/components/common/starRating/InteractiveStarRating.tsx b/components/common/starRating/InteractiveStarRating.tsx index 3f36b91..0bac046 100644 --- a/components/common/starRating/InteractiveStarRating.tsx +++ b/components/common/starRating/InteractiveStarRating.tsx @@ -50,7 +50,11 @@ export default function InteractiveStarRating({ const displayRating = hoverRating || rating; return ( -
+
@@ -68,6 +72,7 @@ export default function InteractiveStarRating({ onMouseEnter={(event) => handleMouseEnter(star, event)} onClick={(event) => handleClick(star, event)} disabled={disabled} + data-test={`star-${star}`} > void; +interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'default' | 'primary' | 'secondary'; - disabled?: boolean; - type?: 'button' | 'submit' | 'reset'; className?: string; } export default function Button({ children, - onClick, variant = 'default', - disabled = false, - type = 'button', className = '', + ...rest }: ButtonProps) { const buttonClass = `${styles.button} ${styles[variant]} ${className}`.trim(); return ( -
- +
) => { if (isCurrent) { e.preventDefault(); diff --git a/domains/auth/client/components/LoginModal.tsx b/domains/auth/client/components/LoginModal.tsx index 940c968..95b0bab 100644 --- a/domains/auth/client/components/LoginModal.tsx +++ b/domains/auth/client/components/LoginModal.tsx @@ -50,7 +50,7 @@ export default function LoginModal() { return ( -
+
-
diff --git a/domains/lecture/server/components/LectureCardHybrid.tsx b/domains/lecture/server/components/LectureCardHybrid.tsx index 1dbf9de..c69eb9d 100644 --- a/domains/lecture/server/components/LectureCardHybrid.tsx +++ b/domains/lecture/server/components/LectureCardHybrid.tsx @@ -10,7 +10,7 @@ interface LectureCardHybridProps { export default function LectureCardHybrid({ staticData, universityName }: LectureCardHybridProps) { return ( -
+
@@ -49,7 +49,7 @@ export default function LectureCardHybrid({ staticData, universityName }: Lectur 평점 -
+
+
-

{lecture.lectureName}

-
{lecture.professor}
+

{lecture.lectureName}

+
{lecture.professor}
@@ -67,7 +67,7 @@ export default function LectureInfo({
- + +
{lectures.message || '강의를 불러오는데 실패했습니다.'}
); @@ -30,7 +30,7 @@ export default function LectureListHybrid({ universityName, lectures }: LectureL })); return ( -
+
{lectureList.map((lecture: StaticLectureData) => { const openedParam = lecture.opened ? 'true' : 'false'; @@ -39,6 +39,8 @@ export default function LectureListHybrid({ universityName, lectures }: LectureL href={`/${universityName}/${lecture.lectureId}?opened=${openedParam}`} key={lecture.lectureId} prefetch={true} + data-test="lecture-card" + data-lecture-id={lecture.lectureId} > diff --git a/domains/mypage/client/components/DeleteReviewButton.tsx b/domains/mypage/client/components/DeleteReviewButton.tsx index 53be3a4..7a5ef4e 100644 --- a/domains/mypage/client/components/DeleteReviewButton.tsx +++ b/domains/mypage/client/components/DeleteReviewButton.tsx @@ -37,7 +37,12 @@ export function DeleteReviewButton({ review, userNumber }: ReviewCardProps) { }; return ( - ); diff --git a/domains/mypage/client/components/PatchReviewButton.tsx b/domains/mypage/client/components/PatchReviewButton.tsx index 20bd082..df4809c 100644 --- a/domains/mypage/client/components/PatchReviewButton.tsx +++ b/domains/mypage/client/components/PatchReviewButton.tsx @@ -48,7 +48,7 @@ export function PatchReviewButton({ review, userNumber }: ReviewCardProps) { return ( <> - diff --git a/domains/mypage/server/components/LectureCard.tsx b/domains/mypage/server/components/LectureCard.tsx index c019918..2f9a605 100644 --- a/domains/mypage/server/components/LectureCard.tsx +++ b/domains/mypage/server/components/LectureCard.tsx @@ -13,7 +13,7 @@ export function LectureCard({ lecture }: LectureCardProps) { : `${styles.compactReviewBadge} ${styles.compactUnReviewedBadge}`; return ( -
+
diff --git a/domains/mypage/server/components/LectureList.tsx b/domains/mypage/server/components/LectureList.tsx index 6785e35..80b94a2 100644 --- a/domains/mypage/server/components/LectureList.tsx +++ b/domains/mypage/server/components/LectureList.tsx @@ -16,7 +16,7 @@ export function LectureList({ lectures }: LectureListProps) { } return ( -
+
{lectures.map((lecture) => ( ))} diff --git a/domains/mypage/server/components/ReviewCard.tsx b/domains/mypage/server/components/ReviewCard.tsx index 26743e0..d79617a 100644 --- a/domains/mypage/server/components/ReviewCard.tsx +++ b/domains/mypage/server/components/ReviewCard.tsx @@ -5,7 +5,7 @@ import styles from '../../styles/ReviewCard.module.css'; export function ReviewCard({ review, userNumber }: ReviewCardProps) { return ( -
+

{review.lecture.lectureName}

@@ -24,13 +24,17 @@ export function ReviewCard({ review, userNumber }: ReviewCardProps) {
-

{review.postTitle}

+

+ {review.postTitle} +

{review.starLating}
-

{review.postContent}

+

+ {review.postContent} +

diff --git a/domains/mypage/server/components/ReviewList.tsx b/domains/mypage/server/components/ReviewList.tsx index e505cf0..758b2d6 100644 --- a/domains/mypage/server/components/ReviewList.tsx +++ b/domains/mypage/server/components/ReviewList.tsx @@ -10,7 +10,7 @@ interface ReviewListProps { export function ReviewList({ reviews, userNumber }: ReviewListProps) { if (reviews.length === 0) { return ( -
+

작성한 리뷰가 없습니다.

강의를 수강한 후 리뷰를 작성해보세요!

@@ -18,7 +18,7 @@ export function ReviewList({ reviews, userNumber }: ReviewListProps) { } return ( -
+
{reviews.map((review) => ( ))} diff --git a/domains/review/client/components/LikeReview.tsx b/domains/review/client/components/LikeReview.tsx index 0cdc932..88d5133 100644 --- a/domains/review/client/components/LikeReview.tsx +++ b/domains/review/client/components/LikeReview.tsx @@ -9,13 +9,13 @@ export default function LikeReview({ review }: { review: Review }) { const { handleLikeClick, isLoggedIn } = useLikeReviewAction({ review }); return ( -
+
- {review.likes} + {review.likes}
); } diff --git a/domains/review/client/components/ReviewHeader.tsx b/domains/review/client/components/ReviewHeader.tsx index 94e7106..f85abce 100644 --- a/domains/review/client/components/ReviewHeader.tsx +++ b/domains/review/client/components/ReviewHeader.tsx @@ -41,7 +41,7 @@ export default function ReviewHeader({
-
diff --git a/domains/review/client/components/ReviewList.tsx b/domains/review/client/components/ReviewList.tsx index 178f418..3c68bd3 100644 --- a/domains/review/client/components/ReviewList.tsx +++ b/domains/review/client/components/ReviewList.tsx @@ -32,7 +32,7 @@ export default function ReviewList({ reviews, isLoading, isFetching, error }: Re if (!reviews || reviews.length === 0) { return ( -
+
@@ -43,7 +43,7 @@ export default function ReviewList({ reviews, isLoading, isFetching, error }: Re } return ( -
+
{reviews.map((review) => ( diff --git a/domains/review/client/components/ReviewSortSelector.tsx b/domains/review/client/components/ReviewSortSelector.tsx index fb53244..34bfd33 100644 --- a/domains/review/client/components/ReviewSortSelector.tsx +++ b/domains/review/client/components/ReviewSortSelector.tsx @@ -25,25 +25,27 @@ export default function ReviewSortSelector({ }; return ( -
-
+
+
{SORT_OPTIONS.map((option) => ( ))}
-
+
diff --git a/domains/review/client/components/WriteReviewModal.tsx b/domains/review/client/components/WriteReviewModal.tsx index f1bb244..5efcf02 100644 --- a/domains/review/client/components/WriteReviewModal.tsx +++ b/domains/review/client/components/WriteReviewModal.tsx @@ -74,7 +74,7 @@ const WriteReviewModal = forwardRef( size="medium" onClose={handleClose} > -
+
@@ -109,15 +110,16 @@ const WriteReviewModal = forwardRef( required rows={6} maxLength={1000} + data-test="review-content-input" />
{content.length}/1000
- -