Skip to content

Commit 1540163

Browse files
kimnamheeeec0912jyclaude
authored
배포 (#135)
* ✨ feat(msw): warn unhandled requests for debugging * ✨ feat(gitignore): add env * ✨ feat(providers): add query client provider * ✨ feat(api): add test (health check) handler * ✨ feat(route): add test route for api connection test * ✨ feat: 액션 바 (상호작용) ui 구현 * ✨ feat: env 삭제 * 💎 style(sidebar): update layout stability * ✨ feat(sidebar): updage search drawer toggle logic * ✨ feat(sidebar): remain page width when opening search drawer * ✨ feat(components): add pagination * ✨ feat(components): add shared components lazyimage / card * ✨ feat(msw): add post mock data * ✨ feat(api): add feed schema / types * ✨ feat(api): add handler / api function * ✨ feat(home): add feed ui * ✨ feat(actions): add env variable on cd workflow * 💎 style(sidebar): collapse sidebar on search drawer toggle * 🐛 fix(route): update route from profile_id to post_id * ✨ feat(feed): link to post detail on click * ✨ feat(components): add dropdown * ✨ feat(msw): add album handler * ✨ feat(api): add album api call functions * ✨ feat(api): add album api call function * 🐛 fix(msw): adjust handler order * 💎 style(dropdown): remove circle icon * ✨ feat(album): add album dropdown * 🧪 test(album): add album dropdown tests * 🐛 fix(actions): apply secrets * 🧹 chore: update test:ci command option * ✨ feat(msw): add bookmarks handler * ✨ feat(bookmarks): add bookmarked posts ui * ✨ feat(postdetail): return to previous page when closing page * ✨ feat(postdetail): validate search params with zod * ✨ feat: 하단 푸터 - 위치 화면 구현 * ✨ feat: 하단 푸터 수정 (Instagram Lite 추가) * ✨ feat: 푸터 완성 * ✨ feat: 푸터 ui, 기능, 경로 설정 완성 * ✨ feat: 로그인 msw 설정 * ✨ feat: 로그인 검사 로직 추가 및 카카오톡 로그인 ui 구현 * ✨ feat: 파일 구조 변경 실제 구현과 유사하게 경로 변경 & 유지보수 및 관리 용이 목적 * ✨ feat: 푸터 완성 * ✨ feat: 비밀번호 찾기 창 ui 수정 * 💎 style(createmodal): fix modal ratio * ✨ feat(sidebar): 만들기 버튼을 게시글/스토리 업로드로 분리 - 기존 '만들기' 버튼을 '게시글 업로드'와 '스토리 업로드'로 분리 - 아이콘 구분: SquarePlus(게시글), CirclePlus(스토리) - 모바일 하단바 배경색 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(create-story): 스토리 업로드 기능 구현 - 사진 1장만 업로드 가능 (초과 시 toast 알림) - 9:16 비율 미리보기, 검은 배경으로 빈 공간 채움 - 본문/앨범 단계 없이 바로 공유하기 - 이미지 업로드 후 헤더 타이틀 숨김 처리 - 이탈 시 확인 다이얼로그 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(ui): 모달 외곽선 제거 및 안내 문구 word-break 추가 - DialogContent 기본 스타일에서 border 제거 - 이미지 업로드 안내 문구에 break-keep 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(story): 레이아웃 수정 - xs 너비에서 미리보기, dropzone 영역의 높이가 지나치게 높아지는 문제 해결 * 🧹 chore(constants): add constant for api errors * ✨ feat(msw): add handlers for image upload * ✨ feat(msw): add handlers for stories * ♻️ refactor(component): change component name * ✨ feat(msw): add search handler * ✨ feat(explore): separate page component * ♻️ refactor: update variable names * 🧹 chore(msw): add mock data * ✨ feat(explore): add ui / api handling * ✨ feat(profile): add albums tab * ✨ feat(profile): add albums summary ui * 💎 style(album): update badge background color * ✨ feat(msw): add relations to msw mock data * ✨ feat(msw): add like related handlers * ✨ feat(msw): add bookmark toggle handler * ✨ feat: 비밀번호 찾기 기능 구현 * ✨ feat(like/bookmark): add like, bookmark toggle functionality * ✨ feat: footer 파일 위치 변경 * ✨ feat(home): add footer * ✨ feat(album): integrate get album detail api * ✨ feat: 컨플릭트 수정 * 🐛 fix(route): fix routeTree syntax error * ✨ feat: 회원가입 ui 구현 * ✨ feat: 회원가입 아이디 중복 체크 로직 구현 * ✨ feat: 회원가입 컴포넌트 분리 및 아이디 랜덤 생성 로직 수정 * ✨ feat: reset.tsx 구조 분리 * ✨ feat(msw): add post create handler * ✨ feat(create-post): add functionality and ui for creating post * ✨ feat(create-post): show close button on post create finish * ✨ feat(utils): add crop image util function * ✨ feat(create-story): integrate upload story api / image crop logic * 🐛 fix(test): add react query provider to tests * ✨ feat: 회원가입 정보 입력 화면 구현 * ✨ feat(stories): implement story feed api / integrate with component * 🐛 fix(route): update route tree file * 💎 style: layout stability * ✨ feat: 회원가입 구현 아직 토큰 관리 미흡, 이메일 전송 로직 미구현 * ✨ feat: 회원가입 라우트 간 보안 강화 로직 추가 * ✨ feat: 회원가입 푸터 라우트 연결 * ✨ feat: 푸터 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat(msw): add search, recent search api handler * ✨ feat(search): add api function for search * ✨ feat(api): add followed / name to search api * ✨ feat(recent): add recent search keyword management * 🧹 chore: remove unused files * ✨ feat: 댓글 api 연결 * 🐛 fix(entity): update schema make name, profileImageUrl nullable * ✨ feat(ui): add fallback for null profileImages * ✨ feat: 댓글 추가 및 좋아요 api 연결 * ✨ feat(follow): integrate follow/unfollow apis * ✨ feat(user): add GET profile api function * ✨ feat(search): add staletime for cache control * 🐛 fix(entity): update schema make bio, name, profileImageUrl nullable * ✨ feat(routes): update profile routes * 🐛 fix(msw): fix field name success -> isSuccess * ♻️ refactor(schema): rename success to isSuccess in API response schemas Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(api): update isSuccess field references in API functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(auth): update isSuccess field references in auth components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(user): add fallback UI for null profileImageUrl Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(search): update search UI components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(profile): update profile page and navigation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat: 로그인 토큰 구현 * 🐛 fix(msw): fix handler order * 🐛 fix(test): update api field name for test * ✨ feat: 댓글 api 연결 수정 및 로그인 토큰 작업, 회원가입 시 자동 로그인 구현 * ✨ feat: 로그인 버그 수정 * ✨ feat: 게시글 날짜 표현 방식 수정 * ✨ feat: 댓글창 ui 수정 * ✨ feat: 비행기 ui 삭제 * ✨ feat(]): 댓글 삭제 api * ✨ feat: 댓글 신고 기능 구현 * ✨ feat: 댓글 신고 기능 1차 구현 * ✨ feat: 댓글 아이콘 클릭 기능 구현 * ✨ feat: 댓글 수정 구현 * ✨ feat(]): 대댓글 로직 삭제 * ✨ feat: 버그 수정 * ✨ feat(shared): add query client * ✨ feat(entities): add getCurrentUser api and schema * ✨ feat(auth): integrate useCurrentUser and session state * ✨ feat(auth): add refresh flow in ky * ✨ feat(auth): integrate useAuth, AuthProvider to current user * 🧹 chore(app): use shared queryClient * ♻️ refactor(components): use useCurrentUser in auth/post * ✨ feat(msw): add /users/me handler * 🐛 fix(auth): remove cookie from request and fix retry logic * 🐛 fix: fix path * 🐛 fix(fix api field): followed -> isFollowed * ✨ feat: 좋아요, 북마크 api 연결 * ✨ feat: 좋아요 바 애니메이션 수정 * ✨ feat: 게시글 컴포넌트 ui, 기능, api 연결 완료 * ✨ feat: 좋아요, 북마크, 댓글 관련 api 연결 및 기능 구현 * ✨ feat: 말풍선 아이콘 좌우반전 * ✨ feat: ... 모달 분기 구현 * ✨ feat: 내 계정 정보 모달 추가 * 🐛 fix(album): add logedInUser query parameter / update api endpoint * 🐛 fix(feed): update field name liked -> isLiked / bookmarked -> isBookmarked * 🐛 fix(post): update field name liked -> isLiked / bookmarked -> isBookmarked * 🐛 fix(create-post): fix upload strategies * 🐛 fix(story): fix story upload api content-type * 🐛 fix(follow): add loggedInUser query parameter * 🐛 fix(user): update field name me -> isMe * 🐛 fix(post / user): update field name * 🐛 fix(auth): add refresh token * ✨ feat(posts): add profile posts api * 🐛 fix(post): constrain mime types * ✨ feat: 수정 * ✨ feat: 게시글 삭제 기능 구현 * ✨ feat: 최종 수정 * 🐛 fix(msw): update api endpoint * ✨ feat(auth): improve user session handling * ✨ feat: post 수정 기능 구현 * ✨ feat(follow): update follower list handling * ✨ feat(profile): add empty states * ✨ feat(navigation): add profile api to sidebar * ✨ feat(create-post): add modal store * ✨ feat(follow): add remove follower * ✨ feat(profile): mock apis for user posts / albums * 🐛 fix(api): update comment api schema field * ✨ feat(album): improve album validation / error handling * 🐛 fix(follow): remove cache strategy * 🐛 fix(comment): update type * ✨ feat(feed): add empty state * 🐛 fix(album): update query key for album edit * 🐛 fix(api): update zod schema and sync with backend * 🐛 fix(msw): update mock handlers to follow zod schema * ✨ feat(route): add profile edit route * ✨ feat(profile-edit): add profile edit ui * ✨ feat(profile-edit): add api functions * ✨ feat(sidebar): add profile edit, logout menu * ♻️ refactor(profile-edit): update api function --------- Co-authored-by: c0912jy <c0912jy@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 3151ac5 commit 1540163

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+614
-105
lines changed

src/components/post/CommentItem.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ interface Comment {
1212
userId: number
1313
nickname: string
1414
content: string
15-
profileImageUrl: string
15+
profileImageUrl: string | null
1616
createdAt: string
1717
updatedAt: string
1818
parentId: number | null
1919
likeCount: number
20-
liked: boolean
20+
isLiked: boolean
2121
likedUserIds: number[]
2222
}
2323

@@ -93,7 +93,7 @@ export default function CommentItem({
9393
>
9494
<Avatar className="h-8 w-8">
9595
<AvatarImage
96-
src={comment.profileImageUrl}
96+
src={comment.profileImageUrl ?? undefined}
9797
alt={comment.nickname}
9898
className="object-cover"
9999
/>

src/components/post/PostInfoSection.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ interface Comment {
2020
postId: number
2121
userId: number
2222
nickname: string
23-
profileImageUrl: string
23+
profileImageUrl: string | null
2424
content: string
2525
createdAt: string
2626
updatedAt: string
2727
parentId: number | null
2828
likeCount: number
29-
liked: boolean
29+
isLiked: boolean
3030
likedUserIds: number[]
3131
}
3232

@@ -78,15 +78,15 @@ const PostInfoSection = forwardRef<PostInfoSectionRef, PostInfoSectionProps>(
7878
.get(`api/v1/posts/${postId}/comments`, {
7979
searchParams: { page: pageNum },
8080
})
81-
.json<{ data: Comment[]; success: boolean }>()
81+
.json<{ data: Comment[]; isSuccess: boolean }>()
8282

83-
if (response.success && response.data.length > 0) {
83+
if (response.isSuccess && response.data.length > 0) {
8484
const newComments = response.data
8585

8686
setLikedComments((prev) => {
8787
const nextLiked = { ...prev }
8888
newComments.forEach((c: Comment) => {
89-
nextLiked[c.id] = c.liked
89+
nextLiked[c.id] = c.isLiked
9090
})
9191
return nextLiked
9292
})
@@ -202,9 +202,9 @@ const PostInfoSection = forwardRef<PostInfoSectionRef, PostInfoSectionProps>(
202202
.put(`api/v1/posts/${postId}/comments/${editingComment.id}`, {
203203
json: { content },
204204
})
205-
.json<{ data: Comment; success: boolean }>()
205+
.json<{ data: Comment; isSuccess: boolean }>()
206206

207-
if (response.success) {
207+
if (response.isSuccess) {
208208
setComments((prev) =>
209209
prev.map((c) => (c.id === editingComment.id ? response.data : c))
210210
)
@@ -215,13 +215,13 @@ const PostInfoSection = forwardRef<PostInfoSectionRef, PostInfoSectionProps>(
215215
.post(`api/v1/posts/${postId}/comments`, {
216216
json: { content },
217217
})
218-
.json<{ data: Comment; success: boolean }>()
218+
.json<{ data: Comment; isSuccess: boolean }>()
219219

220-
if (response.success) {
220+
if (response.isSuccess) {
221221
setComments((prev) => [response.data, ...prev])
222222
setLikedComments((prev) => ({
223223
...prev,
224-
[response.data.id]: response.data.liked,
224+
[response.data.id]: response.data.isLiked,
225225
}))
226226
}
227227
}

src/components/post/PostMenuModal.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState, useEffect } from 'react'
22
import { useNavigate } from '@tanstack/react-router'
3+
import { useQueryClient } from '@tanstack/react-query'
34
import ReportModal from './ReportModal'
45
import AccountInfoModal from './AccountInfoModal'
56
import EmbedModal from './EmbedModal'
@@ -42,6 +43,7 @@ export default function PostMenuModal({
4243
profileUserId: authorId,
4344
})
4445

46+
const queryClient = useQueryClient()
4547
const isMe = currentUserId === authorId
4648
const isFollowing =
4749
followingList?.some((user) => user.userId === authorId) ?? false
@@ -73,6 +75,7 @@ export default function PostMenuModal({
7375
.json<{ isSuccess: boolean; code: string; message: string }>()
7476

7577
if (response.isSuccess) {
78+
void queryClient.invalidateQueries({ queryKey: ['albums', 'user'] })
7679
onClose()
7780
navigate({ to: `/${authorId}` })
7881
}

src/entities/album/model/hooks/useCreateAlbumMutation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function useCreateAlbumMutation() {
1111
mutationFn: (payload: CreateAlbumRequest) =>
1212
createAlbum({ payload, loggedInUser }),
1313
onSuccess: () => {
14-
queryClient.invalidateQueries({ queryKey: ['albums', 'my'] })
14+
queryClient.invalidateQueries({ queryKey: ['albums', 'user'] })
1515
},
1616
})
1717
}

src/entities/album/model/hooks/useUpdateAlbumTitleMutation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ export function useUpdateAlbumTitleMutation() {
1616
payload: CreateAlbumRequest
1717
}) => updateAlbumTitle({ albumId, payload, loggedInUser }),
1818
onSuccess: () => {
19-
queryClient.invalidateQueries({ queryKey: ['albums', 'my'] })
19+
queryClient.invalidateQueries({
20+
queryKey: ['albums', 'user', loggedInUser],
21+
})
2022
},
2123
})
2224
}

src/entities/album/model/schema.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { z } from 'zod'
22

33
export const CreateAlbumRequestSchema = z.object({
4-
title: z.string(),
4+
title: z.string().min(1).max(50),
55
})
66

77
export const AlbumPostSchema = z.object({
88
postId: z.number(),
9-
imageUrl: z.string().min(1),
9+
imageUrl: z.string(),
10+
likeCount: z.number(),
11+
commentCount: z.number(),
1012
})
1113

1214
export const AlbumDetailSchema = z.object({
@@ -18,7 +20,10 @@ export const AlbumDetailSchema = z.object({
1820
export const AlbumSummarySchema = z.object({
1921
albumId: z.number(),
2022
title: z.string(),
21-
thumbnailImageUrl: z.string().default(''),
23+
thumbnailImageUrl: z
24+
.string()
25+
.nullable()
26+
.transform((val) => val ?? ''),
2227
postCount: z.number().int().nonnegative(),
2328
})
2429

src/entities/album/ui/AlbumSummaryCard.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { AlbumSummary } from '@/entities/album/model/types'
22
import { cn } from '@/shared/lib/utils'
33
import { Card, CardHeader, CardTitle, CardContent } from '@/shared/ui/card'
4+
import { ImageFallback } from '@/shared/ui/image-fallback'
45
import LazyImage from '@/shared/ui/lazyImage'
56

67
type AlbumSummaryCardProps = {
@@ -36,12 +37,16 @@ export function AlbumSummaryCard({
3637

3738
<CardContent className="px-0 pb-0">
3839
<div className="relative aspect-square w-full overflow-hidden rounded-b-2xl">
39-
<LazyImage
40-
src={thumbnailImageUrl}
41-
alt={title}
42-
wrapperClassName="absolute inset-0"
43-
className="h-full w-full object-cover"
44-
/>
40+
{thumbnailImageUrl ? (
41+
<LazyImage
42+
src={thumbnailImageUrl}
43+
alt={title}
44+
wrapperClassName="absolute inset-0"
45+
className="h-full w-full object-cover"
46+
/>
47+
) : (
48+
<ImageFallback className="absolute inset-0" ariaLabel={title} />
49+
)}
4550

4651
<div className="pointer-events-none absolute inset-0 h-full w-full bg-linear-to-t from-black/35 via-black/5 to-transparent" />
4752

src/entities/feed/model/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const FeedAuthorSchema = z.object({
99
export const FeedItemSchema = z.object({
1010
postId: z.number(),
1111
author: FeedAuthorSchema,
12-
thumbnailImageUrl: z.string().min(1),
12+
thumbnailImageUrl: z.string().nullable(),
1313
likeCount: z.number().nonnegative(),
1414
commentCount: z.number().nonnegative(),
1515
createdAt: z.string(),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Users } from 'lucide-react'
2+
3+
export function EmptyFeedState() {
4+
return (
5+
<div className="flex flex-col items-center justify-center py-16">
6+
<div className="flex size-20 items-center justify-center rounded-full border-2 border-black">
7+
<Users className="size-10" strokeWidth={1} />
8+
</div>
9+
<h2 className="mt-6 text-3xl font-extrabold">피드가 비어 있습니다</h2>
10+
<p className="mt-3 text-center text-sm text-gray-500">
11+
다른 사람들을 팔로우하면 팔로우한 사람들의 게시물이
12+
<br />
13+
여기에 표시됩니다.
14+
</p>
15+
</div>
16+
)
17+
}

src/entities/feed/ui/FeedCard.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ export function FeedCard({ item, className, onOpenPost }: FeedCardProps) {
102102
onClick={handleOpenPost}
103103
className="block w-full focus-visible:outline-none"
104104
>
105-
<LazyImage
106-
src={item.thumbnailImageUrl}
107-
alt={`Post by ${item.author.nickname}`}
108-
wrapperClassName="w-full bg-black/5 aspect-square"
109-
/>
105+
{item.thumbnailImageUrl ? (
106+
<LazyImage
107+
src={item.thumbnailImageUrl}
108+
alt={`Post by ${item.author.nickname}`}
109+
wrapperClassName="w-full bg-black/5 aspect-square"
110+
/>
111+
) : (
112+
<div className="aspect-square w-full bg-black/5" />
113+
)}
110114
</button>
111115

112116
<CardFooter className="flex items-center justify-between px-2 py-1">

0 commit comments

Comments
 (0)