-
Notifications
You must be signed in to change notification settings - Fork 3
Feat/107/my gathering api #117
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
Conversation
Walkthroughμ΄ ν 리νμ€νΈλ μ¬μ©μ κ°μ λ° λ‘κ·ΈμΈ API νΈμΆμ μν ν¨μμ νμμμ λ° μλ΅ μ²λ¦¬ λ§€κ°λ³μλ₯Ό μΆκ°νκ³ , μλ‘μ΄ νΈμ€ν λ° μ°Έμ¬ λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λ κΈ°λ₯μ μΆκ°ν©λλ€. λν κΈ°μ‘΄μ λͺ¨μ κ΄λ ¨ API λ° μΏΌλ¦¬ ν¨μκ° μμ λκ±°λ μμ λμμΌλ©°, μλ‘μ΄ νμ΄μ§ μ»΄ν¬λνΈκ° μΆκ°λμμ΅λλ€. μ λ°μ μΌλ‘ API μμ² λ° λ°μ΄ν° μ²λ¦¬ λ°©μμ΄ κ°μ λμμ΅λλ€. Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? πͺ§ TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
πstorybook: https://67206cc6ff9d7a05a3528ff8-bdslaqwktn.chromatic.com/ |
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.
Actionable comments posted: 12
π§Ή Outside diff range and nitpick comments (12)
src/app/(crew)/my-gathering/page.tsx (1)
4-5: λλ¬ν μ μλ return λ¬Έμ΄ μμ΅λλ€.
redirectν¨μ νΈμΆ μ΄νμreturn nullꡬ문μ μ€νλμ§ μμ΅λλ€. μ½λμ λͺ νμ±μ μν΄ λ€μκ³Ό κ°μ΄ μμ νλ κ²μ΄ μ’μ΅λλ€:- redirect('/my-gathering/joined'); - return null; + redirect('/my-gathering/joined');src/_queries/my-gathering/hosted-gathering-list-queries.ts (1)
4-10: μΊμ± μ€μ μΆκ°λ₯Ό κ³ λ €ν΄λ³΄μΈμ.μ±λ₯ μ΅μ νλ₯Ό μν΄
staleTimeκ³ΌcacheTimeμ€μ μ μΆκ°νλ κ²μ΄ μ’μ΅λλ€.export function useGetHostedGatheringListQuery(startDate: string) { return { queryKey: ['gatheringDetail', startDate], queryFn: () => getHostedGatheringList(startDate), select: (data: GatheringCardProps[]) => data, + staleTime: 5 * 60 * 1000, // 5λΆ + cacheTime: 30 * 60 * 1000, // 30λΆ }; }src/_queries/my-gathering/joined-gathering-list-queries.ts (1)
6-6: 쿼리 ν€ κ΅¬μ‘° κ°μ μ΄ νμν©λλ€.νμ¬ μΏΌλ¦¬ ν€κ° λ무 μΌλ°μ μ λλ€. λ ꡬ체μ μΈ ν€λ₯Ό μ¬μ©νμ¬ μΊμ μΆ©λμ λ°©μ§νλ κ²μ΄ μ’μ΅λλ€.
- queryKey: ['gatheringDetail', startDate], + queryKey: ['joinedGatheringList', startDate],src/_apis/my-gathering/hosted-gathering-list-apis.ts (1)
4-12: μ±λ₯ μ΅μ νλ₯Ό μν μΊμ± μ λ΅ μ μλͺ¨μ λͺ©λ‘ λ°μ΄ν°λ μμ£Ό λ³κ²½λμ§ μμ κ²μΌλ‘ μμλλ―λ‘, React Queryμ μΊμ± κΈ°λ₯μ νμ©νλ©΄ μ’μ κ² κ°μ΅λλ€. λ€μκ³Ό κ°μ μ€μ μ κ³ λ €ν΄λ³΄μΈμ:
- staleTime μ€μ μΌλ‘ λΆνμν API νΈμΆ κ°μ
- cacheTime μ€μ μΌλ‘ μΊμ μ μ§ κΈ°κ° μ΅μ ν
- νμν κ²½μ° μλ μΊμ 무ν¨ν λ‘μ§ μΆκ°
src/utils/format-date.ts (2)
22-28: μ λ ₯κ° μ ν¨μ± κ²μ¬ μΆκ° νμλ μ§ λ¬Έμμ΄μ΄ μ ν¨νμ§ μμ κ²½μ° μ²λ¦¬κ° νμν©λλ€. μλͺ»λ μ λ ₯κ°μΌλ‘ μΈν μ€λ₯λ₯Ό λ°©μ§νκΈ° μν΄ λ€μκ³Ό κ°μ κ°μ μ μ μν©λλ€.
export function getDayOfWeek(dateString: string) { const daysInKorean = ['μΌμμΌ', 'μμμΌ', 'νμμΌ', 'μμμΌ', 'λͺ©μμΌ', 'κΈμμΌ', 'ν μμΌ']; + if (!dateString || isNaN(new Date(dateString).getTime())) { + throw new Error('μ ν¨νμ§ μμ λ μ§ νμμ λλ€.'); + } const date = new Date(dateString); const dayIndex = date.getDay(); return daysInKorean[dayIndex]; }
30-36: μ λ ₯κ° μ ν¨μ± κ²μ¬ λ° νμ μμ μ± κ°μ νμDate κ°μ²΄μ μ ν¨μ±μ κ²μ¬νκ³ νμ μμ μ±μ λμ΄κΈ° μν κ°μ μ΄ νμν©λλ€.
-export function formatDateToRequest(date: Date) { +export function formatDateToRequest(date: Date | null | undefined) { + if (!date || !(date instanceof Date) || isNaN(date.getTime())) { + throw new Error('μ ν¨ν Date κ°μ²΄κ° νμν©λλ€.'); + } + const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }src/_apis/auth/auth-apis.tsx (1)
5-16: νμμμ κ°μ μμν νμνλμ½λ©λ νμμμ κ°(5000ms)μ μμλ‘ λΆλ¦¬νμ¬ κ΄λ¦¬νλ κ²μ΄ μ’μ΅λλ€. μ΄λ μ μ§λ³΄μμ±μ ν₯μμν€κ³ λ€λ₯Έ API νΈμΆκ³Όμ μΌκ΄μ±μ 보μ₯ν μ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ μμλ₯Ό μΆκ°νλ κ²μ μ μν©λλ€:
+const AUTH_API_TIMEOUT = 5000; export function signupUser(data: SignupRequest): Promise<{ data: SignupResponse }> { return fetchApi<{ data: SignupResponse; headers: Headers }>( '/auths/signup', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }, - 5000, + AUTH_API_TIMEOUT, true, ).then((response) => {src/app/(crew)/my-gathering/hosted/page.tsx (2)
1-10: μν¬νΈ ꡬ문 μ λ¦¬κ° νμν©λλ€μν¬νΈ ꡬ문μ λ€μκ³Ό κ°μ μμλ‘ μ 리νλ©΄ μ½λμ κ°λ μ±μ΄ ν₯μλ κ² κ°μ΅λλ€:
- μΈλΆ λΌμ΄λΈλ¬λ¦¬ (
react,@tanstack/react-query)- 컀μ€ν 쿼리/API
- μ νΈλ¦¬ν°
- μ»΄ν¬λνΈ
- νμ
'use client'; import { useEffect, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useGetHostedGatheringListQuery } from '@/src/_queries/my-gathering/hosted-gathering-list-queries'; import { formatDateToRequest } from '@/src/utils/format-date'; import GatheringListWithDate from '@/src/app/(crew)/my-gathering/_component/gathering-list-with-date'; import PopOverCalendar from '@/src/components/common/input/pop-over-calendar'; import { GatheringCardProps } from '@/src/types/gathering-data';
27-35: μ κ·Όμ± κ°μ μ΄ νμν©λλ€μλ§¨ν± HTMLκ³Ό μ κ·Όμ± μμ±μ μΆκ°νμ¬ μ¬μ©μ κ²½νμ κ°μ ν μ μμ΅λλ€.
return ( - <div> + <main role="main" aria-label="λ΄κ° μ£Όμ΅ν λͺ¨μ"> - <div className="py-4 md:py-6"> + <section className="py-4 md:py-6" aria-label="λ μ§ μ ν"> <PopOverCalendar value={selectedDate} onChange={(d) => setSelectedDate(d)} /> - </div> + </section> {hostedGatheringList && <GatheringListWithDate gatheringList={hostedGatheringList} />} - </div> + </main> );src/app/(crew)/my-gathering/joined/page.tsx (1)
19-25: useEffect μ΅μ ν νμνμ¬ κ΅¬νλ λ κ°μ useEffectμ κ°μ μ΄ νμν©λλ€.
λ€μκ³Ό κ°μ κ°μ μ¬νμ μ μλ립λλ€:
- joinedGatheringList μνλ λΆνμνλ―λ‘ μ κ±°νκ³ dataλ₯Ό μ§μ μ¬μ©
- refetchλ₯Ό μμ‘΄μ± λ°°μ΄μ μΆκ°
- useEffect(() => { - setJoinedGatheringList(data); - }, [data]); useEffect(() => { refetch(); - }, [selectedDate]); + }, [selectedDate, refetch]);src/components/common/header/presenter.tsx (1)
29-29: μ€νμΌλ§ μμνλ₯Ό κ³ λ €ν΄λ³΄μΈμ.νμ¬ νλμ½λ©λ spacing κ°λ€(space-x-2, space-x-5 λ±)μ theme μμλ μ€μ νμΌλ‘ λΆλ¦¬νλ©΄ μΌκ΄μ± μλ λμμΈ μμ€ν ꡬμΆμ λμμ΄ λ κ² κ°μ΅λλ€.
Also applies to: 31-31
tailwind.config.ts (1)
45-45: λμ΄ μ νΈλ¦¬ν° μΆκ°κ° μ μ ν΄ λ³΄μ λλ€.height-13 μ νΈλ¦¬ν°μ μΆκ°κ° κΈ°μ‘΄ ν¨ν΄μ μ λ°λ₯΄κ³ μμ΅λλ€. λ€λ§, μ΄ νΉμ λμ΄κ°(52px)μ΄ μ΄λ€ UI μμλ₯Ό μν΄ μΆκ°λμλμ§ μ£ΌμμΌλ‘ λ¬Έμννλ©΄ μ’μ κ² κ°μ΅λλ€.
λ€μκ³Ό κ°μ΄ μ£Όμμ μΆκ°νλ κ²μ μ μν©λλ€:
height: { 7.5: '30px', 12.5: '50px', - 13: '52px', + 13: '52px', // λͺ¨μ κ΄λ ¨ UI μ»΄ν¬λνΈμ κΈ°λ³Έ λμ΄κ° 27.5: '110px', },
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
π Files selected for processing (18)
src/_apis/auth/auth-apis.tsx(1 hunks)src/_apis/gathering/gathering-apis.tsx(0 hunks)src/_apis/my-gathering/hosted-gathering-list-apis.ts(1 hunks)src/_apis/my-gathering/joined-gathering-list-apis.ts(1 hunks)src/_queries/gathering/gathering-queries.tsx(0 hunks)src/_queries/my-gathering/hosted-gathering-list-queries.ts(1 hunks)src/_queries/my-gathering/joined-gathering-list-queries.ts(1 hunks)src/app/(crew)/my-gathering/_component/gathering-list-with-date.tsx(2 hunks)src/app/(crew)/my-gathering/creation/page.tsx(0 hunks)src/app/(crew)/my-gathering/hosted/page.tsx(1 hunks)src/app/(crew)/my-gathering/joined/page.tsx(1 hunks)src/app/(crew)/my-gathering/layout.tsx(1 hunks)src/app/(crew)/my-gathering/page.tsx(1 hunks)src/app/(crew)/my-gathering/participation/page.tsx(0 hunks)src/components/common/header/presenter.tsx(1 hunks)src/utils/api.ts(2 hunks)src/utils/format-date.ts(1 hunks)tailwind.config.ts(1 hunks)
π€ Files with no reviewable changes (4)
- src/_apis/gathering/gathering-apis.tsx
- src/_queries/gathering/gathering-queries.tsx
- src/app/(crew)/my-gathering/creation/page.tsx
- src/app/(crew)/my-gathering/participation/page.tsx
π Additional comments (15)
src/app/(crew)/my-gathering/page.tsx (1)
4-4: 리λ€μ΄λ μ
ꡬνμ΄ μ μ ν©λλ€.
Next.jsμ redirect ν¨μλ₯Ό μ¬μ©νμ¬ κΈ°λ³Έ κ²½λ‘μμ '/my-gathering/joined'λ‘ λ¦¬λ€μ΄λ μ
νλ ꡬνμ΄ κΉλν©λλ€.
src/_queries/my-gathering/hosted-gathering-list-queries.ts (1)
4-10: ꡬνμ΄ κΉλνκ³ νμ
μ΄ μ μ μλμ΄ μμ΅λλ€!
React Query ν¨ν΄μ μ λ°λ₯΄κ³ μμΌλ©°, νμ μ€ν¬λ¦½νΈλ₯Ό μ μ ν νμ©νκ³ μμ΅λλ€.
src/_queries/my-gathering/joined-gathering-list-queries.ts (1)
1-2: LGTM! νμν μμ‘΄μ±λ€μ΄ μ μν¬νΈλμ΄ μμ΅λλ€.
νμ κ³Ό API ν¨μκ° μ μ νκ² μν¬νΈλμ΄ μμ΅λλ€.
src/_apis/my-gathering/hosted-gathering-list-apis.ts (1)
1-2: μν¬νΈ κ΅¬λ¬Έμ΄ μ μ ν ꡬμ±λμ΄ μμ΅λλ€!
νμν μ νΈλ¦¬ν°μ νμ μ΄ λͺ ννκ² μν¬νΈλμ΄ μμ΅λλ€.
src/_apis/my-gathering/joined-gathering-list-apis.ts (2)
1-2: μν¬νΈ κ΅¬λ¬Έμ΄ μ μ ν©λλ€!
νμν μ νΈλ¦¬ν°μ νμ μ μ λ κ²½λ‘λ‘ μ¬λ°λ₯΄κ² μν¬νΈνκ³ μμ΅λλ€.
5-5: API μλν¬μΈνΈ μ¬μ© κ²μ¦μ΄ νμν©λλ€.
μ΄ API μλν¬μΈνΈκ° νλ‘μ νΈ μ 체μμ μΌκ΄λκ² μ¬μ©λλμ§ νμΈμ΄ νμν©λλ€.
β Verification successful
API μλν¬μΈνΈ μ¬μ©μ΄ νλ‘μ νΈ μ 체μμ μΌκ΄λκ² νμΈλμμ΅λλ€.
π Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# API μλν¬μΈνΈ μ¬μ© κ²μ¦
rg -g '!*.{json,md}' "/api/gatherings/joined" .
Length of output: 165
src/app/(crew)/my-gathering/layout.tsx (2)
8-9: λ€μ΄λ°μ΄ λ λͺ
νν΄μ‘μ΅λλ€! π
'participation'μμ 'joined'λ‘, 'creation'μμ 'hosted'λ‘μ λ³κ²½μ κ° νμ κΈ°λ₯μ λ μ§κ΄μ μΌλ‘ ννν©λλ€. μ΄λ μ½λμ κ°λ μ±κ³Ό μ μ§λ³΄μμ±μ ν₯μμν΅λλ€.
8-9: λΌμ°νΈ λ³κ²½μ¬ν κ²μ¦μ΄ νμν©λλ€.
μλ‘μ΄ λΌμ°νΈ κ²½λ‘λ‘μ λ³κ²½μ΄ λ€λ₯Έ μ»΄ν¬λνΈλ νμ΄μ§μ μν₯μ λ―ΈμΉ μ μμ΅λλ€.
λ€μ μ€ν¬λ¦½νΈλ‘ λΌμ°νΈ μ°Έμ‘°λ₯Ό νμΈν΄μ£ΌμΈμ:
β Verification successful
λΌμ°νΈ λ³κ²½μ¬νμ΄ λ€λ₯Έ μ»΄ν¬λνΈλ νμ΄μ§μ μν₯μ λ―ΈμΉμ§ μμμ νμΈνμ΅λλ€.
π Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: μ΄μ λΌμ°νΈμ μλ‘μ΄ λΌμ°νΈ μ°Έμ‘° κ²μ¬
echo "μ΄μ λΌμ°νΈ μ°Έμ‘° κ²μ¬:"
rg -l "my-gathering/(participation|creation)"
echo "\nμλ‘μ΄ λΌμ°νΈ ꡬν νμΈ:"
fd -e tsx -e ts "joined|hosted" src/app/\(crew\)/my-gathering/
Length of output: 192
src/_apis/auth/auth-apis.tsx (1)
5-34: μΈμ¦ κ΄λ ¨ νμ
μμ μ± κ°μ νμ
isAuth νλΌλ―Έν°κ° μΆκ°λμμ§λ§, μ΄μ λν νμ
μ μκ° λͺ
ννμ§ μμ΅λλ€. λν μλ΅ ν€λμ νμ
μμ μ±λ κ°μ μ΄ νμν©λλ€.
ν€λ νμ μ¬μ©μ νμΈνκΈ° μν΄ λ€μ μ€ν¬λ¦½νΈλ₯Ό μ€νν©λλ€:
src/app/(crew)/my-gathering/hosted/page.tsx (1)
11-14: μν κ΄λ¦¬κ° μ ꡬνλμ΄ μμ΅λλ€
λ μ§ μ νκ³Ό λͺ¨μ λͺ©λ‘μ λν μν κ΄λ¦¬κ° μ μ νκ² κ΅¬νλμ΄ μμΌλ©°, νμ μμ μ±λ μ κ³ λ €λμ΄ μμ΅λλ€.
src/app/(crew)/my-gathering/joined/page.tsx (1)
1-10: νμν λͺ¨λ μμ‘΄μ±μ΄ μ¬λ°λ₯΄κ² μν¬νΈλμμ΅λλ€.
λͺ¨λ μν¬νΈκ° μ μ νκ² κ΅¬μ±λμ΄ μμΌλ©°, λΆνμν μν¬νΈκ° μμ΅λλ€.
src/app/(crew)/my-gathering/_component/gathering-list-with-date.tsx (2)
4-4: κΉλν import ꡬ문 ꡬμ±μ
λλ€!
formatDateμ getDayOfWeek ν¨μλ₯Ό ν μ€λ‘ κΉλνκ² import νμ΅λλ€.
30-32: μμΌ νμ κ°μ μ΄ μ μ΄λ£¨μ΄μ‘μ΅λλ€!
νλμ½λ©λ μμΌ λμ getDayOfWeek ν¨μλ₯Ό μ¬μ©νμ¬ λμ μΌλ‘ μμΌμ νμνλλ‘ κ°μ ν κ²μ΄ μ’μ΅λλ€. μ΄λ λ€μκ³Ό κ°μ μ₯μ μ΄ μμ΅λλ€:
- μ μ§λ³΄μμ± ν₯μ
- λ μ§μ λ°λ₯Έ μ νν μμΌ νμ
- μ½λ μ¬μ¬μ©μ± μ¦κ°
src/components/common/header/presenter.tsx (2)
16-20: λ€λΉκ²μ΄μ
λ§ν¬ κ΅¬μ‘°κ° κ°μ λμμ΅λλ€!
λ§ν¬ μ 보λ₯Ό λ°°μ΄λ‘ λΆλ¦¬νμ¬ κ΄λ¦¬νλ κ²μ μ μ§λ³΄μμ±μ λμ΄λ μ’μ μ κ·Ό λ°©μμ λλ€.
16-51: λ€λΉκ²μ΄μ
κ²½λ‘ κ²μ¦μ΄ νμν©λλ€.
μλ‘ μΆκ°λ κ²½λ‘λ€μ΄ μ€μ νμ΄μ§ μ»΄ν¬λνΈμ μ¬λ°λ₯΄κ² λ§€νλμ΄ μλμ§ νμΈμ΄ νμν©λλ€.
β Verification successful
λ€λΉκ²μ΄μ κ²½λ‘κ° μ¬λ°λ₯΄κ² λ§€νλμμ΅λλ€.
π Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence of page components for each route
# Check for the existence of page components
echo "Checking page components for navigation routes..."
for route in "my-crew" "my-gathering" "my-favorite"; do
fd -t f "page.tsx" "src/app" | grep -i "${route}"
done
Length of output: 607
| export function useGetHostedGatheringListQuery(startDate: string) { | ||
| return { | ||
| queryKey: ['gatheringDetail', startDate], | ||
| queryFn: () => getHostedGatheringList(startDate), |
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.
π οΈ Refactor suggestion
μλ¬ μ²λ¦¬ λ‘μ§ μΆκ°λ₯Ό κ²ν ν΄μ£ΌμΈμ.
API νΈμΆ μ€ν¨ μμ μλ¬ μ²λ¦¬κ° λλ½λμ΄ μμ΅λλ€. queryFnμμ μλ¬λ₯Ό μ μ ν μ²λ¦¬νλ κ²μ΄ μ’μ΅λλ€.
- queryFn: () => getHostedGatheringList(startDate),
+ queryFn: async () => {
+ try {
+ return await getHostedGatheringList(startDate);
+ } catch (error) {
+ console.error('λͺ¨μ λͺ©λ‘μ λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€:', error);
+ throw new Error('λͺ¨μ λͺ©λ‘μ λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€.');
+ }
+ },π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| queryFn: () => getHostedGatheringList(startDate), | |
| queryFn: async () => { | |
| try { | |
| return await getHostedGatheringList(startDate); | |
| } catch (error) { | |
| console.error('λͺ¨μ λͺ©λ‘μ λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€:', error); | |
| throw new Error('λͺ¨μ λͺ©λ‘μ λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€.'); | |
| } | |
| }, |
| export function useGetJoinedGatheringListQuery(startDate: string) { | ||
| return { | ||
| queryKey: ['gatheringDetail', startDate], | ||
| queryFn: () => getJoinedGatheringList(startDate), | ||
| select: (data: GatheringCardProps[]) => data, | ||
| }; | ||
| } |
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.
π οΈ Refactor suggestion
μλ¬ μ²λ¦¬ λ° νμ μμ μ± κ°μ μ΄ νμν©λλ€.
- μλ¬ μ²λ¦¬ μ€μ μ΄ λλ½λμ΄ μμ΅λλ€.
- API μλ΅ νμ μ΄ λͺ μμ μΌλ‘ μ μλμ΄ μμ§ μμ΅λλ€.
- select ν¨μκ° λ°μ΄ν°λ₯Ό κ·Έλλ‘ λ°ννλ―λ‘ λΆνμν©λλ€.
λ€μκ³Ό κ°μ΄ κ°μ ν΄λ³΄μΈμ:
-export function useGetJoinedGatheringListQuery(startDate: string) {
+export function useGetJoinedGatheringListQuery(startDate: string) {
+ type ApiResponse = GatheringCardProps[];
+
return {
- queryKey: ['gatheringDetail', startDate],
+ queryKey: ['joinedGatheringList', startDate],
- queryFn: () => getJoinedGatheringList(startDate),
+ queryFn: async (): Promise<ApiResponse> => {
+ try {
+ return await getJoinedGatheringList(startDate);
+ } catch (error) {
+ throw new Error(`λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λλ° μ€ν¨νμ΅λλ€: ${error}`);
+ }
+ },
retry: 1,
staleTime: 5 * 60 * 1000, // 5λΆ
- select: (data: GatheringCardProps[]) => data,
};
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function useGetJoinedGatheringListQuery(startDate: string) { | |
| return { | |
| queryKey: ['gatheringDetail', startDate], | |
| queryFn: () => getJoinedGatheringList(startDate), | |
| select: (data: GatheringCardProps[]) => data, | |
| }; | |
| } | |
| export function useGetJoinedGatheringListQuery(startDate: string) { | |
| type ApiResponse = GatheringCardProps[]; | |
| return { | |
| queryKey: ['joinedGatheringList', startDate], | |
| queryFn: async (): Promise<ApiResponse> => { | |
| try { | |
| return await getJoinedGatheringList(startDate); | |
| } catch (error) { | |
| throw new Error(`λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λλ° μ€ν¨νμ΅λλ€: ${error}`); | |
| } | |
| }, | |
| retry: 1, | |
| staleTime: 5 * 60 * 1000, // 5λΆ | |
| }; | |
| } |
| export function getHostedGatheringList(startDate: string): Promise<GatheringCardProps[]> { | ||
| const url = `/api/gatherings/hosted?startDate=${startDate}`; | ||
| return fetchApi<{ data: GatheringCardProps[] }>(url, { | ||
| method: 'GET', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }).then((response) => response.data); | ||
| } |
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.
π οΈ Refactor suggestion
API ꡬνμ λν κ°μ μ μ
λ€μ μ¬νλ€μ κ³ λ €ν΄ μ£ΌμκΈ° λ°λλλ€:
- startDate λ§€κ°λ³μμ λν μ ν¨μ± κ²μ¬κ° νμν©λλ€
- URL νλΌλ―Έν°μ μμ ν μΈμ½λ©μ΄ νμν©λλ€
- μ€λ₯ μ²λ¦¬λ₯Ό 보κ°νλ©΄ μ’κ² μ΅λλ€
λ€μκ³Ό κ°μ΄ κ°μ ν΄ λ³΄μλ κ²μ μ μλ립λλ€:
-export function getHostedGatheringList(startDate: string): Promise<GatheringCardProps[]> {
- const url = `/api/gatherings/hosted?startDate=${startDate}`;
+export function getHostedGatheringList(startDate: string): Promise<GatheringCardProps[]> {
+ if (!startDate || !isValidDate(startDate)) {
+ throw new Error('μ ν¨νμ§ μμ λ μ§ νμμ
λλ€');
+ }
+
+ const url = `/api/gatherings/hosted?startDate=${encodeURIComponent(startDate)}`;
+
return fetchApi<{ data: GatheringCardProps[] }>(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
- }).then((response) => response.data);
+ })
+ .then((response) => response.data)
+ .catch((error) => {
+ console.error('λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λ μ€ μ€λ₯κ° λ°μνμ΅λλ€:', error);
+ throw error;
+ });μΆκ°λ‘ λ μ§ μ ν¨μ± κ²μ¬λ₯Ό μν ν¬νΌ ν¨μλ νμν©λλ€:
function isValidDate(dateString: string): boolean {
const date = new Date(dateString);
return date instanceof Date && !isNaN(date.getTime());
}| export function getJoinedGatheringList(startDate: string): Promise<GatheringCardProps[]> { | ||
| const url = `/api/gatherings/joined?startDate=${startDate}`; | ||
| return fetchApi<{ data: GatheringCardProps[] }>(url, { | ||
| method: 'GET', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }).then((response) => response.data); | ||
| } |
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.
π οΈ Refactor suggestion
API ꡬνμ λν λͺ κ°μ§ κ°μ μ¬νμ΄ νμν©λλ€.
λ€μ μ¬νλ€μ κ³ λ €ν΄μ£ΌμκΈ° λ°λλλ€:
- startDate νλΌλ―Έν°μ λν μ ν¨μ± κ²μ¬κ° νμν©λλ€.
- μλ¬ μ²λ¦¬ λ‘μ§μ΄ λλ½λμ΄ μμ΅λλ€.
- URL νλΌλ―Έν° κ΅¬μ± μ μΈμ½λ©μ΄ νμν©λλ€.
λ€μκ³Ό κ°μ΄ κ°μ νλ κ²μ μ μλ립λλ€:
export function getJoinedGatheringList(startDate: string): Promise<GatheringCardProps[]> {
+ if (!startDate || !/^\d{4}-\d{2}-\d{2}$/.test(startDate)) {
+ throw new Error('μ ν¨νμ§ μμ λ μ§ νμμ
λλ€.');
+ }
- const url = `/api/gatherings/joined?startDate=${startDate}`;
+ const url = `/api/gatherings/joined?startDate=${encodeURIComponent(startDate)}`;
return fetchApi<{ data: GatheringCardProps[] }>(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
- }).then((response) => response.data);
+ }).then((response) => response.data)
+ .catch((error) => {
+ console.error('λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λλ° μ€ν¨νμ΅λλ€:', error);
+ throw error;
+ });
}μΆκ°λ‘, μλ΅ νμ μ μμ μ±μ μν΄ zodλ λ€λ₯Έ λ°νμ νμ κ²μ¦ λꡬμ μ¬μ©μ κ³ λ €ν΄λ³΄μκΈ° λ°λλλ€.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function getJoinedGatheringList(startDate: string): Promise<GatheringCardProps[]> { | |
| const url = `/api/gatherings/joined?startDate=${startDate}`; | |
| return fetchApi<{ data: GatheringCardProps[] }>(url, { | |
| method: 'GET', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| }).then((response) => response.data); | |
| } | |
| export function getJoinedGatheringList(startDate: string): Promise<GatheringCardProps[]> { | |
| if (!startDate || !/^\d{4}-\d{2}-\d{2}$/.test(startDate)) { | |
| throw new Error('μ ν¨νμ§ μμ λ μ§ νμμ λλ€.'); | |
| } | |
| const url = `/api/gatherings/joined?startDate=${encodeURIComponent(startDate)}`; | |
| return fetchApi<{ data: GatheringCardProps[] }>(url, { | |
| method: 'GET', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| }).then((response) => response.data) | |
| .catch((error) => { | |
| console.error('λͺ¨μ λͺ©λ‘μ κ°μ Έμ€λλ° μ€ν¨νμ΅λλ€:', error); | |
| throw error; | |
| }); | |
| } |
src/_apis/auth/auth-apis.tsx
Outdated
| return fetchApi<{ data: LoginResponse; headers: Headers }>( | ||
| '/auths/login', | ||
| { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(data), | ||
| }, | ||
| body: JSON.stringify(data), | ||
| }).then((response) => { | ||
| 5000, | ||
| true, | ||
| ).then((response) => { |
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.
π οΈ Refactor suggestion
μλ¬ μ²λ¦¬ κ°μ νμ
νμμμμ΄λ λ€νΈμν¬ μ€λ₯ λ°μ μ μ μ ν μλ¬ μ²λ¦¬κ° νμν©λλ€. νμ¬λ κΈ°λ³Έμ μΈ Promise rejectionλ§ μ²λ¦¬λκ³ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ μλ¬ μ²λ¦¬λ₯Ό κ°μ νλ κ²μ μ μν©λλ€:
export function loginUser(data: LoginRequest): Promise<{ data: LoginResponse }> {
return fetchApi<{ data: LoginResponse; headers: Headers }>(
'/auths/login',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
},
5000,
true,
- ).then((response) => {
+ ).then((response) => {
const token = response.headers.get('Authorization');
+ if (!token) {
+ throw new Error('μΈμ¦ ν ν°μ΄ μμ΅λλ€');
+ }
return { data: { token } };
+ }).catch((error) => {
+ if (error.name === 'TimeoutError') {
+ throw new Error('λ‘κ·ΈμΈ μμ² μκ°μ΄ μ΄κ³Όλμμ΅λλ€');
+ }
+ throw error;
});
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return fetchApi<{ data: LoginResponse; headers: Headers }>( | |
| '/auths/login', | |
| { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(data), | |
| }, | |
| body: JSON.stringify(data), | |
| }).then((response) => { | |
| 5000, | |
| true, | |
| ).then((response) => { | |
| return fetchApi<{ data: LoginResponse; headers: Headers }>( | |
| '/auths/login', | |
| { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(data), | |
| }, | |
| 5000, | |
| true, | |
| ).then((response) => { | |
| const token = response.headers.get('Authorization'); | |
| if (!token) { | |
| throw new Error('μΈμ¦ ν ν°μ΄ μμ΅λλ€'); | |
| } | |
| return { data: { token } }; | |
| }).catch((error) => { | |
| if (error.name === 'TimeoutError') { | |
| throw new Error('λ‘κ·ΈμΈ μμ² μκ°μ΄ μ΄κ³Όλμμ΅λλ€'); | |
| } | |
| throw error; | |
| }); |
| return ( | ||
| <div> | ||
| <div className="py-4 md:py-6"> | ||
| <PopOverCalendar value={selectedDate} onChange={(d) => setSelectedDate(d)} /> | ||
| </div> | ||
| {joinedGatheringList && <GatheringListWithDate gatheringList={joinedGatheringList} />} | ||
| </div> | ||
| ); | ||
| } |
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.
π οΈ Refactor suggestion
UI μν μ²λ¦¬ λ° μ κ·Όμ± κ°μ νμ
λ‘λ© μν, μλ¬ μν νμ λ° μ κ·Όμ± μμ±μ΄ λλ½λμ΄ μμ΅λλ€.
λ€μκ³Ό κ°μ κ°μ μ μ μλ립λλ€:
return (
- <div>
+ <div role="main" aria-label="μ°Έμ¬ν λͺ¨μ λͺ©λ‘">
<div className="py-4 md:py-6">
<PopOverCalendar value={selectedDate} onChange={(d) => setSelectedDate(d)} />
</div>
+ {isLoading && <div>λ‘λ© μ€...</div>}
+ {error && <div role="alert">λ°μ΄ν°λ₯Ό λΆλ¬μ€λ μ€ μ€λ₯κ° λ°μνμ΅λλ€: {error.message}</div>}
- {joinedGatheringList && <GatheringListWithDate gatheringList={joinedGatheringList} />}
+ {!isLoading && !error && data && <GatheringListWithDate gatheringList={data} />}
</div>
);Committable suggestion skipped: line range outside the PR's diff.
| const { data, refetch } = useQuery( | ||
| useGetJoinedGatheringListQuery(formatDateToRequest(selectedDate)), | ||
| ); |
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.
π οΈ Refactor suggestion
useQuery ν μ΅μ ν νμ
νμ¬ κ΅¬νμμ μ€μν useQuery κΈ°λ₯λ€μ΄ λλ½λμ΄ μμ΅λλ€.
λ€μκ³Ό κ°μ΄ κ°μ νλ κ²μ μ μλ립λλ€:
- const { data, refetch } = useQuery(
+ const { data, error, isLoading, refetch } = useQuery(
useGetJoinedGatheringListQuery(formatDateToRequest(selectedDate)),
+ {
+ retry: 1,
+ onError: (error) => setError(error),
+ staleTime: 5 * 60 * 1000, // 5λΆ
+ }
);Committable suggestion skipped: line range outside the PR's diff.
src/utils/api.ts
Outdated
| } | ||
|
|
||
| return { data: await response.json() } as T; | ||
|
|
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.
π οΈ Refactor suggestion
νμ μμ μ± κ°μ νμ
νμ¬ κ΅¬νμ API μλ΅μ κ°μ λ‘ T νμ
μΌλ‘ λ³ννκ³ μμ΄ λ°νμ μλ¬κ° λ°μν μ μμ΅λλ€. μλ΅ λ°μ΄ν°μ ννλ₯Ό 보μ₯νκΈ° μν΄ λ€μκ³Ό κ°μ κ°μ μ΄ νμν©λλ€:
- return { data: await response.json() } as T;
+ const responseData = await response.json();
+ if (!responseData || typeof responseData !== 'object') {
+ throw new ApiError(500, 'μλͺ»λ μλ΅ νμ');
+ }
+ return { data: responseData } as T;λν μ λ€λ¦ νμ
Tμ λν μ μ½ μ‘°κ±΄μ μΆκ°νλ κ²μ΄ μ’μ΅λλ€:
export async function fetchApi<T extends { data: unknown }>(| url: string, | ||
| options: RequestInit = {}, | ||
| timeout = 5000, | ||
| isAuth = false, |
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.
μ¬μ©λμ§ μλ λ§€κ°λ³μ λ°κ²¬
isAuth λ§€κ°λ³μκ° ν¨μ λ΄λΆμμ μ ν μ¬μ©λμ§ μκ³ μμ΅λλ€. νμ¬ μΈμ¦μ useAuthStoreμμ κ°μ Έμ¨ ν ν°μ ν΅ν΄ μ΄λ―Έ μ²λ¦¬λκ³ μμ΅λλ€. λΆνμν λ§€κ°λ³μλ μ κ±°νλ κ²μ΄ μ’μ΅λλ€.
export async function fetchApi<T>(
url: string,
options: RequestInit = {},
timeout = 5000,
- isAuth = false,
): Promise<T> {π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| isAuth = false, | |
| isAuth = false, | |
| ``` | |
| β | |
| ```suggestion |
| {links.map(({ href, label }) => ( | ||
| <Link | ||
| key={href} | ||
| href={isAuth ? href : '/login'} | ||
| className={`${pathname.startsWith(href) ? 'text-white' : 'text-blue-300'} ml-4`} | ||
| > | ||
| {label} | ||
| </Link> | ||
| ))} |
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.
π οΈ Refactor suggestion
μΈμ¦ μ²λ¦¬ λ‘μ§ κ°μ μ΄ νμν©λλ€.
νμ¬ λΉμΈμ¦ μνμμ λͺ¨λ λ§ν¬κ° /loginμΌλ‘ 리λ€μ΄λ νΈλλλ°, λ€μκ³Ό κ°μ κ°μ μ¬νμ κ³ λ €ν΄λ³΄μΈμ:
- λ‘κ·ΈμΈ ν μλ μλνλ νμ΄μ§λ‘ 리λ€μ΄λ νΈ
- λΉμΈμ¦ μ¬μ©μμκ²λ λ§ν¬λ₯Ό λΉνμ±ννκ±°λ ν΄νμΌλ‘ μλ΄
μμ μ½λ:
- href={isAuth ? href : '/login'}
+ href={isAuth ? href : `/login?redirect=${encodeURIComponent(href)}`}
+ title={!isAuth ? 'λ‘κ·ΈμΈμ΄ νμν μλΉμ€μ
λλ€' : ''}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {links.map(({ href, label }) => ( | |
| <Link | |
| key={href} | |
| href={isAuth ? href : '/login'} | |
| className={`${pathname.startsWith(href) ? 'text-white' : 'text-blue-300'} ml-4`} | |
| > | |
| {label} | |
| </Link> | |
| ))} | |
| {links.map(({ href, label }) => ( | |
| <Link | |
| key={href} | |
| href={isAuth ? href : `/login?redirect=${encodeURIComponent(href)}`} | |
| className={`${pathname.startsWith(href) ? 'text-white' : 'text-blue-300'} ml-4`} | |
| title={!isAuth ? 'λ‘κ·ΈμΈμ΄ νμν μλΉμ€μ λλ€' : ''} | |
| > | |
| {label} | |
| </Link> | |
| ))} |
|
πstorybook: https://67206cc6ff9d7a05a3528ff8-ftisutgiha.chromatic.com/ |
yulrang
left a comment
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.
λ‘κ·Έμμλ νμ΄ν
μ
λλ€~~~
μκ³ νμ
¨μ΄μ~~
|
πstorybook: https://67206cc6ff9d7a05a3528ff8-diaxiujnra.chromatic.com/ |
HaeJungg
left a comment
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.
μκ³ νμ ¨μ΅λλ€!π
π Issue Ticket
Ticket
βοΈ Description
λμ λͺ¨μ API μΆκ°
ν€λμ μ°νμ½μ μΆκ°
β Checklist
PR
Test
Summary by CodeRabbit
μλ‘μ΄ κΈ°λ₯
λ²κ·Έ μμ
λ¬Έμν