-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 피드 모킹 데이터 북마크 여부 수정 * feat: 북마크 버튼 분리 * feat: 북마크 모킹 API 추가 * feat: isBookmark -> isBookmarked 네이밍 변경 * feat: FeedsQueryData 공용 폴더로 이동 * feat: 피드 북마크 기능 * feat: useBookmark public api 적용 * feat: bookmark -> boormkars 경로 수정 * feat: 피드 북마크 API 분리 * feat: 북마크 API 분리 * feat: 북마크 취소 API 연결 * feat: 피드 북마크 취소 API uri 수정 * feat: 주석 추가 및 함수명 수정 * test: 피드 북마크 테스트 코드 * style: 주석 수정 Closes #PW-298
- Loading branch information
Showing
19 changed files
with
255 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { http } from 'msw'; | ||
|
||
import { | ||
createHttpErrorResponse, | ||
createHttpSuccessResponse, | ||
} from '../dir/response'; | ||
import { feeds } from '../consts/feed'; | ||
|
||
export const bookmarkHandlers = [ | ||
// 1️⃣ 피드 북마크 | ||
http.put('/feeds/:feed_id/bookmarks', ({ params }) => { | ||
const { feed_id } = params; | ||
|
||
if (isNaN(Number(feed_id))) { | ||
return createHttpErrorResponse('4220'); | ||
} | ||
|
||
const formattedFeedId = Number(feed_id); | ||
|
||
if (!feeds[formattedFeedId]) { | ||
return createHttpErrorResponse('4040'); | ||
} | ||
|
||
feeds[formattedFeedId].isBookmarked = true; | ||
|
||
return createHttpSuccessResponse({ isBookmarked: true }); | ||
}), | ||
|
||
// 2️⃣ 피드 북마크 취소 | ||
http.delete('/feeds/:feed_id/bookmarks', ({ params }) => { | ||
const { feed_id } = params; | ||
|
||
if (isNaN(Number(feed_id))) { | ||
return createHttpErrorResponse('4220'); | ||
} | ||
|
||
const formattedFeedId = Number(feed_id); | ||
|
||
if (!feeds[formattedFeedId]) { | ||
return createHttpErrorResponse('4040'); | ||
} | ||
|
||
feeds[formattedFeedId].isBookmarked = false; | ||
|
||
return createHttpSuccessResponse({ isBookmarked: false }); | ||
}), | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { useBookmarks } from './useBookmarks'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
|
||
import { requestBookmarkFeed, requestUnbookmarkFeed } from '@/shared/axios'; | ||
import { FeedsQueryData } from '@/shared/consts'; | ||
import { QUERY_KEYS } from '@/shared/react-query'; | ||
import { isErrorResponse } from '@/shared/utils'; | ||
|
||
import { updateBookmarkStatusInFeeds } from '../lib'; | ||
|
||
export const useBookmarks = (feedId: number, isBookmarked: boolean) => { | ||
const queryClient = useQueryClient(); | ||
|
||
const { mutate: handleBookmarkFeed, isPending } = useMutation({ | ||
mutationFn: () => | ||
isBookmarked | ||
? requestUnbookmarkFeed(feedId) | ||
: requestBookmarkFeed(feedId), | ||
onMutate: async () => { | ||
await queryClient.cancelQueries({ | ||
queryKey: [QUERY_KEYS.feeds], | ||
}); | ||
|
||
// 이전 쿼리값의 스냅샷 | ||
const previousQueryData = queryClient.getQueryData<FeedsQueryData>([ | ||
QUERY_KEYS.feeds, | ||
]); | ||
|
||
if (!previousQueryData) return; | ||
|
||
// 업데이트 될 쿼리값 | ||
const updatedQueryData = updateBookmarkStatusInFeeds( | ||
previousQueryData, | ||
feedId, | ||
); | ||
|
||
// setQueryData 함수를 사용해 newTodo로 Optimistic Update를 실시한다. | ||
await queryClient.setQueryData([QUERY_KEYS.feeds], updatedQueryData); | ||
|
||
return { previousQueryData }; | ||
}, | ||
onError: (_, __, context) => { | ||
queryClient.setQueryData([QUERY_KEYS.feeds], context?.previousQueryData); | ||
}, | ||
onSuccess: (response, _, context) => { | ||
if (isErrorResponse(response)) { | ||
queryClient.setQueryData([QUERY_KEYS.feeds], context.previousQueryData); | ||
return; | ||
} | ||
|
||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.feed, feedId] }); | ||
}, | ||
}); | ||
|
||
return { handleBookmarkFeed, isPending }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { BookmarkButton } from './ui'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { FeedsQueryData } from '@/shared/consts'; | ||
|
||
export function updateBookmarkStatusInFeeds( | ||
previousQueryData: FeedsQueryData, | ||
feedId: number, | ||
) { | ||
const { pages: previousPages } = previousQueryData; | ||
|
||
return { | ||
...previousQueryData, | ||
pages: previousPages.map((pageData) => { | ||
const { data } = pageData; | ||
const updateFeeds = data.feeds.map((feed) => | ||
feed.id === feedId | ||
? { | ||
...feed, | ||
isBookmarked: !feed.isBookmarked, | ||
} | ||
: feed, | ||
); | ||
const updatedData = { ...data, feeds: updateFeeds }; | ||
|
||
return { ...pageData, data: updatedData }; | ||
}), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { renderHook, act, waitFor } from '@testing-library/react'; | ||
import { expect, test, vi } from 'vitest'; | ||
|
||
import * as bookmarkModule from '@/shared/axios'; | ||
import { createQueryClientWrapper } from '@/shared/tests'; | ||
|
||
import { useBookmarks } from '../api'; | ||
|
||
test('북마크 상태가 아닐 때, 북마크 버튼을 클릭하면 북마크 요청이 발생한다.', async () => { | ||
// given | ||
// requestBookmarkFeed 함수를 스파이한다. | ||
const spy = vi.spyOn(bookmarkModule, 'requestBookmarkFeed'); | ||
const { result } = renderHook(() => useBookmarks(1, false), { | ||
wrapper: createQueryClientWrapper(), | ||
}); | ||
|
||
// requestBookmarkFeed가 호출되지 않았는지 확인 | ||
await waitFor(() => expect(spy).not.toHaveBeenCalled()); | ||
|
||
// when | ||
// 좋아요 버튼 클릭 | ||
await act(async () => result.current.handleBookmarkFeed()); | ||
|
||
// then | ||
// requestBookmarkFeed가 호출되었는지 확인 | ||
await waitFor(() => expect(spy).toHaveBeenCalled()); | ||
}); | ||
|
||
test('북마크 상태일 때, 북마크 버튼을 클릭하면 북마크 취소 요청이 발생한다.', async () => { | ||
// given | ||
const spy = vi.spyOn(bookmarkModule, 'requestUnbookmarkFeed'); | ||
const { result } = renderHook(() => useBookmarks(1, true), { | ||
wrapper: createQueryClientWrapper(), | ||
}); | ||
|
||
await waitFor(() => expect(spy).not.toHaveBeenCalled()); | ||
|
||
// when | ||
await act(async () => result.current.handleBookmarkFeed()); | ||
|
||
// then | ||
await waitFor(() => expect(spy).toHaveBeenCalled()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ICON_ACTIVE_COLOR } from '@/shared/consts'; | ||
import { Icon } from '@/shared/ui'; | ||
|
||
import { useBookmarks } from '../api'; | ||
|
||
interface BookmarkButtonProps { | ||
feedId: number; | ||
isBookmarked: boolean; | ||
} | ||
|
||
export const BookmarkButton: React.FC<BookmarkButtonProps> = ({ | ||
feedId, | ||
isBookmarked, | ||
}) => { | ||
const { handleBookmarkFeed, isPending } = useBookmarks(feedId, isBookmarked); | ||
|
||
return ( | ||
<button | ||
className='icon icon-btn' | ||
onClick={() => handleBookmarkFeed()} | ||
disabled={isPending} | ||
> | ||
<Icon | ||
name='bookmark' | ||
width='20' | ||
height='20' | ||
color={isBookmarked ? ICON_ACTIVE_COLOR : 'none'} | ||
/> | ||
</button> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { BookmarkButton } from './BookmarkButton'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { axiosInstance } from '../config'; | ||
|
||
/** | ||
* 피드 북마크 API | ||
* @param feedId 피드 아이디 | ||
* @returns 피드 북마크 상태 | ||
*/ | ||
export async function requestBookmarkFeed(feedId: number) { | ||
const { data } = await axiosInstance.put(`/feeds/${feedId}/bookmarks`); | ||
|
||
return data; | ||
} | ||
|
||
/** | ||
* 피드 북마크 취소 API | ||
* @param feedId 피드 아이디 | ||
* @returns 피드 북마크 상태 | ||
*/ | ||
export async function requestUnbookmarkFeed(feedId: number) { | ||
const { data } = await axiosInstance.delete(`/feeds/${feedId}/bookmarks`); | ||
|
||
return data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './bookmark'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { axiosInstance } from './config'; | ||
export * from './like'; | ||
export * from './bookmark'; |
Oops, something went wrong.