diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c239d990..e2d73509 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,6 +25,9 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
+ - name: Set environment variables
+ run: echo "NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}" >> .env
+
- name: Install dependencies
run: npm install
@@ -35,6 +38,8 @@ jobs:
run: npm test
- name: Build Next.js app
+ env:
+ NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
run: npm run build
- name: Build Storybook
diff --git a/README.md b/README.md
index e215bc4c..d6c3cff8 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,113 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+

-## Getting Started
+> π λΉμ μ λ
μ μνμ μλ‘μ΄ νμ΄μ§λ₯Ό μ΄μ΄λ³΄μΈμ!
+
μλ‘μ΄ μ¬λλ€κ³Ό ν¨κ» μ½κ³ λλλ νΉλ³ν λ
μ κ²½ν, **λΆμ½**κ° ν¨κ»ν©λλ€.
+>
+
[](https://bookco.vercel.app/)
+
+
+
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
+## π― Bookcoμμ ν μ μλ μΌ
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+- **π₯ λ
μ λͺ¨μ**
+
+ λΉμ·ν μ·¨ν₯μ κ°μ§ μ¬λλ€κ³Ό ν¨κ» μ±
μ μ½κ³ μ΄μΌκΈ°λ₯Ό λλ μ μμ΅λλ€.
+ - μ ν΄μ§ μ±
μΌλ‘ λ
μ λͺ¨μμ μ°Έμ¬νκ±°λ, μ§μ λͺ¨μμ λ§λ€ μ μμ΅λλ€.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+- π¬Β **μ±ν
νκΈ°**
+
+ λ€λ₯Έ λΆμ½ μ μ λ€κ³Ό μ±ν
κΈ°λ₯μ ν΅ν΄ μν΅ν μ μμ΅λλ€.
+ - λͺ¨μμ νΈμ€νΈλ κ΅ννκ³ μΆμ μ±
μ κ°μ§ μ μ μ λνλ₯Ό λλ μ μμ΅λλ€.
+
+- **π κ΅ννκΈ° (μΆν κ°λ° μμ ..)**
+
+ μ λ³΄κ² λ μ±
μ λ±λ‘νλ©΄, λ€λ₯Έ μ¬λμ μ±
κ³Ό λ°κΏ μ½μ μ μμ΅λλ€.
+ - μ§μμ λ°©μΉλλ μ±
μ λ€λ₯Έ μ μ μ 곡μ ν μ μμ΅λλ€.
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+
-## Learn More
+## π μλΉμ€ μκ°
-To learn more about Next.js, take a look at the following resources:
+
+
+
+
+
+
+
+
+
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+## π οΈ κΈ°μ μ€ν
-## Deploy on Vercel
+### π» Core
+
+
+
+### π μν κ΄λ¦¬
+
+
+
+### π ν΅μ
+
+
+
+
+### π¨ μ€νμΌλ§
+
+
+### βοΈ μ νΈλ¦¬ν°
+
+
+### π§ͺ ν
μ€ν
+
+
+
+
+### π μ½λ νμ§
+
+
+
+
+
+
+
+## π€ ν νμ
λ°©μ, λΈλμΉ μ λ΅
+
+### β
**PR 리뷰 λ°©μ**
+- **2λͺ
Approve** λ°©μ
+- PR νμΈ μκ° κ³ μ : `09:00`, `13:00`, `18:00`
+- **Pn λ£°**κ³Ό **Dn λ£°** μ μ©
+- **λ°μΌλ¦¬ μ€ν¬λΌ** μ§ν
+
+### β
**λΈλμΉ μ λ΅**
+- **GitHub Flow** μ μ©
+ - `feature` β `develop` β `main`
+ - `hotfix` λ Mainμμ κΈνκ² μμ ν μΌ μμ λ μ¬μ©
+
+### β
**CI/CD μ λ΅**
+- **Husky**λ₯Ό ν΅ν μ½λ νμ§ κ΄λ¦¬
+ - 컀λ°μ λ¦°νΈ κ²μ¬
+- **λμ€μ½λ μΉν
μ°κ²°**λ‘ μ€μκ° μλ¦Ό
+- PR μμ±μ Lint κ²μ¬, test μ½λ μ€ν, μ€ν λ¦¬λΆ λΉλ, νλ‘λμ
λΉλ μ€ννμ¬ κ²μ¬
+
+
+
+
+## π₯ νμ ꡬμ±
+
+|FE|FE|FE|FE|
+|:---:|:---:|:---:|:---:|
+|
|
|
|
|
+|[κΉμ ꡬ](https://github.com/haegu97)|[κΉλ―Όκ²½](https://github.com/wynter24)|[μ μ ](https://github.com/sunnwave)|[κΉμ νΈ](https://github.com/cloud0406)|
+
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/jest.config.js b/jest.config.js
index 2d756f45..970b1b93 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -11,6 +11,9 @@ const config = {
coverageProvider: 'v8',
testEnvironment: 'jsdom',
// setupFilesAfterEnv: ['/src/setupTests.ts'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ },
};
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
diff --git a/src/api/auth/react-query/customHooks.ts b/src/api/auth/react-query/customHooks.ts
index 58834975..5b8ca5d6 100644
--- a/src/api/auth/react-query/customHooks.ts
+++ b/src/api/auth/react-query/customHooks.ts
@@ -1,5 +1,6 @@
import { useMutation } from '@tanstack/react-query';
import { showToast } from '@/components/toast/toast';
+import { TOAST_MESSAGES } from '@/constants/messages/toast';
import { authClientAPI } from '../authClientAPI';
import { getUserInfo } from '@/features/auth/api/auth';
@@ -9,10 +10,16 @@ export function useEditInfoMutation() {
mutationFn: (formData: FormData) => authClientAPI.editInfo(formData),
onSuccess: () => {
getUserInfo();
- showToast({ message: 'νλ‘ν μμ μ΄ μλ£λμμ΅λλ€.', type: 'success' });
+ showToast({
+ message: TOAST_MESSAGES.SUCCESS.PROFILE_EDIT,
+ type: 'success',
+ });
},
onError: (error) => {
- showToast({ message: 'νλ‘ν μμ μ μ€ν¨νμμ΅λλ€', type: 'error' });
+ showToast({
+ message: TOAST_MESSAGES.ERROR.PROFILE_EDIT_FAILED,
+ type: 'error',
+ });
console.error(error);
},
});
diff --git a/src/api/book-club/react-query/customHooks.ts b/src/api/book-club/react-query/customHooks.ts
index dcfdc23e..0e8cd82f 100644
--- a/src/api/book-club/react-query/customHooks.ts
+++ b/src/api/book-club/react-query/customHooks.ts
@@ -1,6 +1,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { bookClubs } from './queries';
import { showToast } from '@/components/toast/toast';
+import { TOAST_MESSAGES } from '@/constants/messages/toast';
import {
bookClubLikeAPI,
bookClubMainAPI,
@@ -10,6 +11,7 @@ import {
import { WriteReviewParams } from '../types';
import { AxiosError } from 'axios';
import { likeOnError, likeOnMutate } from './likeOptimisticUpdate';
+import { BookClubParams } from '@/types/bookclubs';
export function useBookClubCreateMutation() {
const queryClient = useQueryClient();
@@ -25,7 +27,10 @@ export function useBookClubCreateMutation() {
});
},
onError: () => {
- showToast({ message: 'λΆν΄λ½ μμ±μ μ€ν¨νμ΅λλ€.', type: 'error' });
+ showToast({
+ message: TOAST_MESSAGES.ERROR.CLUB_CREATE_FAILED,
+ type: 'error',
+ });
},
});
}
@@ -74,12 +79,18 @@ export function useWriteReview() {
queryClient.invalidateQueries({
queryKey: bookClubs.my()._ctx.reviews().queryKey,
});
- showToast({ message: '리뷰 μμ±μ μλ£νμμ΅λλ€', type: 'success' });
+ showToast({
+ message: TOAST_MESSAGES.SUCCESS.REVIEW_CREATE,
+ type: 'success',
+ });
},
onError: (error) => {
console.error(error);
- showToast({ message: '리뷰 μμ±μ μ€ν¨νμμ΅λλ€.', type: 'error' });
+ showToast({
+ message: TOAST_MESSAGES.ERROR.REVIEW_CREATE_FAILED,
+ type: 'error',
+ });
},
});
}
@@ -103,21 +114,22 @@ export function useCancelBookClub() {
});
}
-export function useLikeBookClub() {
+export function useLikeBookClub(filter: BookClubParams) {
const queryClient = useQueryClient();
return useMutation, number>({
mutationFn: (id: number) => bookClubLikeAPI.like(id),
onMutate: async (id) => {
- return likeOnMutate(queryClient, id, true);
+ return likeOnMutate(queryClient, id, true, filter);
},
//TODO: λ‘μ§ νμΈ ν λ³κ²½ νμ
- onSuccess: () => {
- queryClient.invalidateQueries({
- queryKey: bookClubs._def,
- });
- },
+ // onSuccess: () => {
+ // queryClient.invalidateQueries({
+ // queryKey: ['bookClubs', 'list', DEFAULT_FILTERS],
+ // });
+ // // console.log(bookClubs._def)
+ // },
onError: (_error, id, context) => {
if (context) {
@@ -127,21 +139,21 @@ export function useLikeBookClub() {
});
}
-export function useUnLikeBookClub() {
+export function useUnLikeBookClub(filter: BookClubParams) {
const queryClient = useQueryClient();
return useMutation, number>({
mutationFn: (id: number) => bookClubLikeAPI.unlike(id),
onMutate: async (id) => {
- return likeOnMutate(queryClient, id, false);
+ return likeOnMutate(queryClient, id, false, filter);
},
//TODO: λ‘μ§ νμΈ ν λ³κ²½ νμ
- onSuccess: () => {
- queryClient.invalidateQueries({
- queryKey: bookClubs._def,
- });
- },
+ // onSuccess: () => {
+ // queryClient.invalidateQueries({
+ // queryKey: bookClubs._def,
+ // });
+ // },
onError: (_error, id, context) => {
if (context) {
diff --git a/src/api/book-club/react-query/likeOptimisticUpdate.ts b/src/api/book-club/react-query/likeOptimisticUpdate.ts
index 86a0a536..dce81855 100644
--- a/src/api/book-club/react-query/likeOptimisticUpdate.ts
+++ b/src/api/book-club/react-query/likeOptimisticUpdate.ts
@@ -1,5 +1,5 @@
import { QueryClient } from '@tanstack/react-query';
-import { BookClub } from '@/types/bookclubs';
+import { BookClub, BookClubParams } from '@/types/bookclubs';
import { bookClubs } from './queries';
import { DEFAULT_FILTERS } from '@/lib/constants/filters';
@@ -7,39 +7,39 @@ export const likeOnMutate = async (
queryClient: QueryClient,
id: number,
isLiked: boolean,
+ filter?: BookClubParams,
) => {
- const listQueryKey = bookClubs.list(DEFAULT_FILTERS).queryKey;
+ const listQueryKey = ['bookClubs', 'list', filter || DEFAULT_FILTERS];
const detailQueryKey = bookClubs.detail(id).queryKey;
- const previousBookClubs = queryClient.getQueryData<{
- bookClubs: BookClub[];
- }>(listQueryKey);
+ await queryClient.cancelQueries({ queryKey: listQueryKey });
+ await queryClient.cancelQueries({ queryKey: detailQueryKey });
+
+ // console.log('π μμ λ listQueryKey:', listQueryKey);
+ // console.log('π νμ¬ νμ±νλ λͺ¨λ 쿼리ν€:', queryClient.getQueriesData({}));
+ const previousBookClubs = queryClient.getQueryData<{ bookClubs: BookClub[] }>(
+ listQueryKey,
+ );
const previousDetail = queryClient.getQueryData(detailQueryKey);
- // λͺ©λ‘ μΊμ μ
λ°μ΄νΈ
+ // if (!previousBookClubs) {
+ // console.warn('β οΈ μΊμλ λ°μ΄ν°κ° μμ΅λλ€. queryKey νμΈ νμ:', listQueryKey);
+ // queryClient.invalidateQueries({ queryKey: listQueryKey });
+ // }
+
if (previousBookClubs) {
- queryClient.setQueryData(listQueryKey, {
- ...previousBookClubs,
- bookClubs: previousBookClubs.bookClubs.map((club) =>
+ queryClient.setQueryData(listQueryKey, (old: any) =>
+ old?.map((club: BookClub) =>
club.id === id ? { ...club, isLiked } : club,
),
- });
+ );
}
- // μμΈ μΊμ μ
λ°μ΄νΈ
if (previousDetail) {
- queryClient.setQueryData(detailQueryKey, {
- ...previousDetail,
- isLiked,
- });
+ queryClient.setQueryData(detailQueryKey, { ...previousDetail, isLiked });
}
- //TODO: λ‘μ§ νμΈ ν λ³κ²½ νμ
- queryClient.invalidateQueries({
- queryKey: bookClubs._def,
- });
-
return { previousBookClubs, previousDetail };
};
diff --git a/src/app/bookclub/page.tsx b/src/app/bookclub/page.tsx
index 91208bd1..8e0d78c2 100644
--- a/src/app/bookclub/page.tsx
+++ b/src/app/bookclub/page.tsx
@@ -1,5 +1,23 @@
import BookClubMainPage from '@/features/bookclub/components/BookClubMainPage';
+import { DEFAULT_FILTERS } from '@/lib/constants/filters';
+import { fetchBookClubs } from '@/lib/utils/fetchBookClubs';
+import {
+ dehydrate,
+ HydrationBoundary,
+ QueryClient,
+} from '@tanstack/react-query';
-export default function Home() {
- return ;
+export default async function Home() {
+ const queryClient = new QueryClient();
+
+ await queryClient.prefetchQuery({
+ queryKey: ['bookClubs', 'list', DEFAULT_FILTERS],
+ queryFn: () => fetchBookClubs(DEFAULT_FILTERS),
+ });
+
+ return (
+
+
+
+ );
}
diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx
index 39860903..f1b2a8bf 100644
--- a/src/app/chat/[id]/page.tsx
+++ b/src/app/chat/[id]/page.tsx
@@ -178,61 +178,70 @@ function ChatRoomPage() {
};
return (
-
-
-
-
-
-
}
- onClick={handleGoBack}
- className="bg-gray-light-02"
- />
-
μ±ν
-
-
-
-
}
- onClick={() => {}}
- className="bg-gray-light-02"
- />
+
+
+
+
+
+
+
}
+ onClick={handleGoBack}
+ className="bg-gray-light-02"
+ />
+
μ±ν
+
+
+
+ }
+ onClick={() => {}}
+ className="bg-gray-light-02"
+ />
+
+
router.push(`/bookclub/${chatId}`),
+ }}
+ />
- router.push(`/bookclub/${chatId}`),
- }}
+
+
+
+ {}}
/>
-
-
- {}}
- />
-
-
-
-
}
- aria-label="λ©μμ§ μ μ‘"
- className="h-[52px] w-[52px] bg-green-light-01"
- onClick={handleSubmit}
- />
+
+
);
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index a7ff9915..48a6dc59 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -31,7 +31,6 @@ export default function RootLayout({
{children}
-