Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2303149
feat : APIResponse 하나 정의
Feb 25, 2026
8335098
feat : 모임생성페이지 주소
Feb 25, 2026
2e0b84f
chore : 모임생성 type 정의
Feb 25, 2026
d2f45cb
feat : 모임생성페이지 API 연동
Feb 25, 2026
e5dd1d8
fix : s3에서 주는 그림을 쓰려면 이렇게 해야한다고 한다.
Feb 25, 2026
57ddc1a
chore : 더미데이터 제거
Feb 25, 2026
e4c38eb
feat : 검색페이지 API 연결
Feb 25, 2026
6eb7efb
feat : API 연결 + 연결에 따른 UI수정
Feb 25, 2026
f4f6348
fix : create페이지 수정
Feb 25, 2026
8e6bf10
chore : type error
Feb 25, 2026
4b3d938
feat: implement book story like API and integrate into main pages
shinwokkang Feb 26, 2026
13c5c98
feat: refine book story like detail, navigation and UI
shinwokkang Feb 26, 2026
68fca5b
feat: implement member subscription (follow/unfollow) API and integra…
shinwokkang Feb 26, 2026
7615276
feat: 좋아요 및 구독 API 구현
shinwokkang Feb 26, 2026
d6d27fd
fix : 사진부분 수정
Feb 26, 2026
40d450c
Merge branch 'dev' into feat-108-group-api
hongik-luke Feb 26, 2026
1e43d6b
Merge pull request #140 from checkmo2025/feat-108-group-api
hongik-luke Feb 26, 2026
f2087c3
fix : build 오류 해결
Feb 26, 2026
cf28319
style : 내 모임쪽 스크롤 안생기게끔 수정
Feb 26, 2026
6f250c0
Merge pull request #144 from checkmo2025/142-fixs/tyle
hongik-luke Feb 26, 2026
02e9e82
refactor: apply code review feedbacks
shinwokkang Feb 27, 2026
cf1ff80
fix: optimistically update comment count in story lists
shinwokkang Feb 27, 2026
25b3d26
feat: add routing to other profile from book story cards
shinwokkang Feb 27, 2026
5314381
feat: implement other profile api and display dynamic info
shinwokkang Feb 27, 2026
d5f04e2
fix: remove undefined follow mutation and fix lint
shinwokkang Feb 27, 2026
23165de
Merge pull request #137 from checkmo2025/feat/136-like-subscribe-api
shinwokkang Feb 27, 2026
fd8ca73
fix: resolve conflicts and restore toggle follow mutation
shinwokkang Feb 27, 2026
8ffeccb
style: match following button layout width to primary button
shinwokkang Feb 27, 2026
cf5b7cb
fix: resolve scoping error and adjust following button theme
shinwokkang Feb 27, 2026
de50874
style: apply Subbrown_4 background and primary-3 text color for follo…
shinwokkang Feb 27, 2026
0bbffe8
feat: 상대방 프로필 조회 API 구현
shinwokkang Feb 27, 2026
8644e86
fix: apply optimistic update for other member story likes and comments
shinwokkang Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const nextConfig: NextConfig = {
{
protocol: "https",
hostname: "checkmo-s3-presigned.s3.ap-northeast-2.amazonaws.com",
pathname: "/**",

},
],
},
Expand Down
1 change: 1 addition & 0 deletions src/app/(main)/books/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default function BookDetailPage() {
className="cursor-pointer"
>
<BookStoryCardLarge
id={story.id}
authorName={story.authorName}
createdAt={story.createdAt}
viewCount={story.viewCount}
Expand Down
39 changes: 33 additions & 6 deletions src/app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { useAuthStore } from "@/store/useAuthStore";
import { useStoriesQuery } from "@/hooks/queries/useStoryQueries";
import { useRecommendedMembersQuery } from "@/hooks/queries/useMemberQueries";
import { useMyClubsQuery } from "@/hooks/queries/useClubQueries";
import { useToggleStoryLikeMutation } from "@/hooks/mutations/useStoryMutations";
import { useToggleFollowMutation } from "@/hooks/mutations/useMemberMutations";

export default function HomePage() {
const router = useRouter();
Expand All @@ -23,6 +25,8 @@ export default function HomePage() {
const { data: storiesData, isLoading: isLoadingStories } = useStoriesQuery();
const { data: membersData, isLoading: isLoadingMembers, isError: isErrorMembers } = useRecommendedMembersQuery(isLoggedIn);
const { data: myClubsData } = useMyClubsQuery();
const { mutate: toggleLike } = useToggleStoryLikeMutation();
const { mutate: toggleFollow } = useToggleFollowMutation();

const groups = myClubsData?.clubList || [];

Expand Down Expand Up @@ -85,7 +89,8 @@ export default function HomePage() {
key={u.nickname}
name={u.nickname}
profileSrc={u.profileImageUrl}
onSubscribeClick={() => console.log("subscribe", u.nickname)}
isFollowing={u.isFollowing}
onSubscribeClick={() => toggleFollow({ nickname: u.nickname, isFollowing: u.isFollowing })}
/>
))}
</div>
Expand All @@ -100,6 +105,7 @@ export default function HomePage() {
{stories.slice(0, 3).map((story) => (
<BookStoryCardLarge
key={story.bookStoryId}
id={story.bookStoryId}
authorName={story.authorInfo.nickname}
profileImgSrc={story.authorInfo.profileImageUrl}
createdAt={story.createdAt}
Expand All @@ -108,8 +114,11 @@ export default function HomePage() {
content={story.description}
likeCount={story.likes}
commentCount={story.commentCount}
likedByMe={story.likedByMe}
coverImgSrc={story.bookInfo.imgUrl}
subscribeText="구독"
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
onClick={() => router.push(`/stories/${story.bookStoryId}`)}
/>
Expand All @@ -136,7 +145,12 @@ export default function HomePage() {
<div className="flex gap-6 justify-center">
<HomeBookclub groups={groups} />
{isLoggedIn && (
<ListSubscribeLarge height="h-[424px]" users={recommendedUsers} isError={isErrorMembers} />
<ListSubscribeLarge
height="h-[424px]"
users={recommendedUsers}
isError={isErrorMembers}
onSubscribeClick={(nickname, isFollowing) => toggleFollow({ nickname, isFollowing })}
/>
)}
</div>
</section>
Expand All @@ -147,6 +161,7 @@ export default function HomePage() {
{stories.slice(0, 4).map((story) => (
<BookStoryCard
key={story.bookStoryId}
id={story.bookStoryId}
authorName={story.authorInfo.nickname}
profileImgSrc={story.authorInfo.profileImageUrl}
createdAt={story.createdAt}
Expand All @@ -155,8 +170,11 @@ export default function HomePage() {
content={story.description}
likeCount={story.likes}
commentCount={story.commentCount}
likedByMe={story.likedByMe}
coverImgSrc={story.bookInfo.imgUrl}
subscribeText="구독"
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
onClick={() => router.push(`/stories/${story.bookStoryId}`)}
/>
Expand All @@ -175,7 +193,12 @@ export default function HomePage() {
<HomeBookclub groups={groups} />
{isLoggedIn && (
<div className="pt-6">
<ListSubscribeLarge height="h-[380px]" users={recommendedUsers} isError={isErrorMembers} />
<ListSubscribeLarge
height="h-[380px]"
users={recommendedUsers}
isError={isErrorMembers}
onSubscribeClick={(nickname, isFollowing) => toggleFollow({ nickname, isFollowing })}
/>
</div>
)}
</section>
Expand All @@ -196,6 +219,7 @@ export default function HomePage() {
{stories.slice(0, 3).map((story) => (
<BookStoryCard
key={story.bookStoryId}
id={story.bookStoryId}
authorName={story.authorInfo.nickname}
profileImgSrc={story.authorInfo.profileImageUrl}
createdAt={story.createdAt}
Expand All @@ -204,8 +228,11 @@ export default function HomePage() {
content={story.description}
likeCount={story.likes}
commentCount={story.commentCount}
likedByMe={story.likedByMe}
coverImgSrc={story.bookInfo.imgUrl}
subscribeText="구독"
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
onClick={() => router.push(`/stories/${story.bookStoryId}`)}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(main)/profile/[nickname]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default async function OtherUserProfilePage({ params }: PageProps) {
</div>

<div className="flex flex-col items-center w-full max-w-[1440px] px-4 md:px-0 gap-[24px] mt-[10px] md:mt-[72px]">
<OtherUserProfileTabs />
<OtherUserProfileTabs nickname={nickname} />
</div>
</div>
);
Expand Down
9 changes: 9 additions & 0 deletions src/app/(main)/stories/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import Image from "next/image";
import { isValidUrl } from "@/utils/url";
import { useParams } from "next/navigation";
import { useStoryDetailQuery } from "@/hooks/queries/useStoryQueries";
import { useToggleStoryLikeMutation } from "@/hooks/mutations/useStoryMutations";
import { useToggleFollowMutation } from "@/hooks/mutations/useMemberMutations";

export default function StoryDetailPage() {
const params = useParams();
const id = params?.id as string;
const { data: story, isLoading, isError } = useStoryDetailQuery(Number(id));
const { mutate: toggleLike } = useToggleStoryLikeMutation();
const { mutate: toggleFollow } = useToggleFollowMutation();

if (isLoading) {
return (
Expand Down Expand Up @@ -81,6 +85,11 @@ export default function StoryDetailPage() {
createdAt={story.createdAt}
viewCount={story.viewCount}
likeCount={story.likes}
likedByMe={story.likedByMe}
onLikeClick={() => toggleLike(story.bookStoryId)}
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
/>
</StoryNavigation>
Expand Down
29 changes: 23 additions & 6 deletions src/app/(main)/stories/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ import { useInfiniteStoriesQuery } from "@/hooks/queries/useStoryQueries";
import { useRecommendedMembersQuery } from "@/hooks/queries/useMemberQueries";
import { useMyClubsQuery } from "@/hooks/queries/useClubQueries";
import { useInView } from "react-intersection-observer";
import { useToggleStoryLikeMutation } from "@/hooks/mutations/useStoryMutations";
import { useToggleFollowMutation } from "@/hooks/mutations/useMemberMutations";

export default function StoriesPage() {
const router = useRouter();
const { isLoggedIn } = useAuthStore();
const { mutate: toggleLike } = useToggleStoryLikeMutation();
const { mutate: toggleFollow } = useToggleFollowMutation();
const [selectedCategory, setSelectedCategory] = useState("all");

const {
Expand Down Expand Up @@ -90,10 +94,10 @@ export default function StoriesPage() {
{allStories.slice(0, 4).map((story) => (
<div
key={story.bookStoryId}
onClick={() => handleCardClick(story.bookStoryId)}
className="cursor-pointer shrink-0"
className="shrink-0"
>
<BookStoryCardLarge
id={story.bookStoryId}
authorName={story.authorInfo.nickname}
profileImgSrc={story.authorInfo.profileImageUrl}
createdAt={story.createdAt}
Expand All @@ -102,9 +106,15 @@ export default function StoriesPage() {
content={story.description}
likeCount={story.likes}
commentCount={story.commentCount}
likedByMe={story.likedByMe}
coverImgSrc={story.bookInfo.imgUrl}
subscribeText={story.authorInfo.following ? "구독중" : "구독"}
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
onProfileClick={() => router.push(`/profile/${story.authorInfo.nickname}`)}
onClick={() => handleCardClick(story.bookStoryId)}
onLikeClick={() => toggleLike(story.bookStoryId)}
/>
</div>
))}
Expand All @@ -114,17 +124,18 @@ export default function StoriesPage() {
<ListSubscribeLarge
height="h-[380px]"
users={recommendedMembers}
onSubscribeClick={(nickname, isFollowing) => toggleFollow({ nickname, isFollowing })}
/>
)}

{/* 나머지 카드들 */}
{allStories.slice(4).map((story) => (
<div
key={story.bookStoryId}
onClick={() => handleCardClick(story.bookStoryId)}
className="cursor-pointer shrink-0"
className="shrink-0"
>
<BookStoryCardLarge
id={story.bookStoryId}
authorName={story.authorInfo.nickname}
profileImgSrc={story.authorInfo.profileImageUrl}
createdAt={story.createdAt}
Expand All @@ -133,9 +144,15 @@ export default function StoriesPage() {
content={story.description}
likeCount={story.likes}
commentCount={story.commentCount}
likedByMe={story.likedByMe}
coverImgSrc={story.bookInfo.imgUrl}
subscribeText={story.authorInfo.following ? "구독중" : "구독"}
subscribeText={story.authorInfo.following ? "구독 중" : "구독"}
isFollowing={story.authorInfo.following}
onSubscribeClick={() => toggleFollow({ nickname: story.authorInfo.nickname, isFollowing: story.authorInfo.following })}
hideSubscribeButton={story.writtenByMe}
onProfileClick={() => router.push(`/profile/${story.authorInfo.nickname}`)}
onClick={() => handleCardClick(story.bookStoryId)}
onLikeClick={() => toggleLike(story.bookStoryId)}
/>
</div>
))}
Expand Down
8 changes: 8 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,11 @@ body {
@utility animate-slide-down {
animation: slide-down 0.3s ease-out;
}

@utility no-scrollbar {
-ms-overflow-style: none; /* IE/Edge */
scrollbar-width: none; /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
Comment on lines +179 to +185
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

WebKit scrollbar rule should be inside the @utility block.

The -ms-overflow-style and scrollbar-width properties are inside @utility no-scrollbar, but the ::-webkit-scrollbar pseudo-element rule (lines 183-185) is defined as a separate class selector outside the utility. This inconsistency may cause the WebKit rule to not apply correctly when using the utility class.

🔧 Proposed fix to consolidate the rules
 `@utility` no-scrollbar {
   -ms-overflow-style: none; /* IE/Edge */
   scrollbar-width: none; /* Firefox */
+  &::-webkit-scrollbar {
+    display: none;
+  }
 }
-.no-scrollbar::-webkit-scrollbar {
-  display: none;
-}

Note: The static analysis errors about @utility are false positives—this is valid Tailwind CSS v4 syntax, which the linters aren't configured to recognize.

📝 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.

Suggested change
@utility no-scrollbar {
-ms-overflow-style: none; /* IE/Edge */
scrollbar-width: none; /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
`@utility` no-scrollbar {
-ms-overflow-style: none; /* IE/Edge */
scrollbar-width: none; /* Firefox */
&::-webkit-scrollbar {
display: none;
}
}
🧰 Tools
🪛 Biome (2.4.4)

[error] 179-182: Tailwind-specific syntax is disabled.

(parse)

🪛 Stylelint (17.3.0)

[error] 179-179: Unexpected unknown at-rule "@Utility" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/globals.css` around lines 179 - 185, Move the WebKit scrollbar
pseudo-element rule inside the Tailwind utility definition so all no-scrollbar
styles are together: update the `@utility` no-scrollbar block (the existing block
containing -ms-overflow-style and scrollbar-width) to also include the
.no-scrollbar::-webkit-scrollbar rule instead of having it defined separately;
ensure the pseudo-element selector (.no-scrollbar::-webkit-scrollbar) is
nested/placed within the same `@utility` no-scrollbar declaration so the WebKit
rule is applied wherever the utility is used.

Loading