-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 마이페이지 프로필 편집 API 연동 #84
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
Changes from all commits
d7f4344
b3a77d4
c48ec07
388c638
16974a8
689674c
bb7c68e
7a1e9e8
4df9a2a
04c4ccb
e781b96
c02ad7f
4fff389
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { apiClient } from '../index'; | ||
|
|
||
| export interface DeleteFeedPostResponse { | ||
| isSuccess: boolean; | ||
| code: number; | ||
| message: string; | ||
| data: null; | ||
| } | ||
|
|
||
| export const deleteFeedPost = async (feedId: number) => { | ||
| const response = await apiClient.delete<DeleteFeedPostResponse>(`/feeds/${feedId}`); | ||
| return response.data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { apiClient } from '../index'; | ||
|
|
||
| export interface PostFeedLikeRequest { | ||
| type: boolean; | ||
| } | ||
|
|
||
| export interface PostFeedLikeResponse { | ||
| isSuccess: boolean; | ||
| code: number; | ||
| message: string; | ||
| data: { | ||
| feedId: number; | ||
| isLiked: boolean; | ||
| }; | ||
| } | ||
|
|
||
| export const postFeedLike = async (feedId: number, type: boolean) => { | ||
| const response = await apiClient.post<PostFeedLikeResponse>(`/feeds/${feedId}/likes`, { | ||
| type, | ||
| }); | ||
| return response.data; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { apiClient } from '../index'; | ||
|
|
||
| export interface GetMyProfileResponse { | ||
| code: number; | ||
| status: string; | ||
| message: string; | ||
| data: { | ||
| profileImageUrl: string; | ||
| nickname: string; | ||
| aliasName: string; | ||
| aliasColor: string; | ||
| }; | ||
| } | ||
|
|
||
| export const getMyProfile = async () => { | ||
| const response = await apiClient.get<GetMyProfileResponse>('/users/my-page'); | ||
| return response.data.data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { apiClient } from '../index'; | ||
|
|
||
| export interface PatchProfileRequest { | ||
| nickname: string | null; | ||
| aliasName: string; | ||
| } | ||
|
|
||
| export interface PatchProfileResponse { | ||
| isSuccess?: boolean; | ||
| code: number; | ||
| message: string; | ||
| } | ||
|
|
||
| export const patchProfile = async (data: PatchProfileRequest): Promise<PatchProfileResponse> => { | ||
| try { | ||
| const response = await apiClient.patch<PatchProfileResponse>('/users', data); | ||
| return response.data; | ||
| } catch (error) { | ||
| let errorMessage = '프로필 편집 중 오류가 발생했어요.'; | ||
| let errorCode = 0; | ||
|
|
||
| if (error && typeof error === 'object' && 'response' in error) { | ||
| const axiosError = error as { response?: { data?: { code?: number; message?: string } } }; | ||
| if (axiosError.response?.data) { | ||
| const serverData = axiosError.response.data; | ||
| errorCode = serverData.code || 0; | ||
|
|
||
| switch (errorCode) { | ||
| case 70004: | ||
| errorMessage = '현재 닉네임과 같은 닉네임이에요.'; | ||
| break; | ||
| case 70005: | ||
| errorMessage = '닉네임은 6개월에 한번 변경할 수 있어요.'; | ||
| break; | ||
| case 70006: | ||
| errorMessage = '이미 사용중인 닉네임이에요.'; | ||
| break; | ||
| default: | ||
| errorMessage = serverData.message || '프로필 편집에 실패했어요.'; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| isSuccess: false, | ||
| code: errorCode, | ||
| message: errorMessage, | ||
| }; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| import { useNavigate, useLocation } from 'react-router-dom'; | ||
| import styled from '@emotion/styled'; | ||
| import Fab from './Fab'; | ||
| import type { FabProps } from '../../types/fab'; | ||
| import FeedIcon from '../../assets/navbar/feed.svg'; | ||
| import GroupIcon from '../../assets/navbar/group.svg'; | ||
| import SearchIcon from '../../assets/navbar/search.svg'; | ||
|
|
@@ -72,7 +71,12 @@ const items: RouteItem[] = [ | |
| { path: '/mypage', label: '내 정보', icon: MyIcon, activeIcon: MyIconActive }, | ||
| ]; | ||
|
|
||
| const NavBar = ({ src, path }: FabProps) => { | ||
| interface NavBarProps { | ||
| src?: string; | ||
| path?: string; | ||
| } | ||
|
|
||
| const NavBar = ({ src, path }: NavBarProps) => { | ||
|
Comment on lines
+74
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Fab 렌더링 가드 및 Prop 타입 강화 제안 path만 있고 src가 없는 경우 Fab에 undefined가 전달될 수 있습니다. 최소한 렌더링 가드로 방지하고, 가능하면 타입을 더 엄격하게 해주세요. 렌더링 가드(권장, 간단): - {path && <Fab src={src} path={path} />}
+ {path && src && <Fab src={src} path={path} />}타입을 엄격하게(선택): -interface NavBarProps {
- src?: string;
- path?: string;
-}
+type NavBarProps = { src: string; path: string } | { src?: undefined; path?: undefined };Also applies to: 97-97 🤖 Prompt for AI Agents |
||
| const navigate = useNavigate(); | ||
| const { pathname } = useLocation(); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import save from '../../../assets/feed/save.svg'; | |
| import activeSave from '../../../assets/feed/activeSave.svg'; | ||
| import lockIcon from '../../../assets/feed/lockIcon.svg'; | ||
| import { postSaveFeed } from '@/api/feeds/postSave'; | ||
| import { postFeedLike } from '@/api/feeds/postFeedLike'; | ||
|
|
||
| const Container = styled.div<{ isDetail: boolean }>` | ||
| width: 100%; | ||
|
|
@@ -70,9 +71,21 @@ const PostFooter = ({ | |
| const [likeCount, setLikeCount] = useState<number>(initialLikeCount); | ||
| const [saved, setSaved] = useState(isSaved); | ||
|
|
||
| const handleLike = () => { | ||
| setLiked(!liked); | ||
| setLikeCount(prev => (liked ? prev - 1 : prev + 1)); | ||
| const handleLike = async () => { | ||
| try { | ||
| const response = await postFeedLike(feedId, !liked); | ||
|
|
||
| if (response.isSuccess) { | ||
| // 성공 시 상태 업데이트 | ||
| setLiked(response.data.isLiked); | ||
| setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1)); | ||
| console.log('좋아요 상태 변경 성공:', response.data.isLiked); | ||
| } else { | ||
| console.error('좋아요 상태 변경 실패:', response.message); | ||
| } | ||
| } catch (error) { | ||
| console.error('좋아요 API 호출 실패:', error); | ||
| } | ||
|
Comment on lines
+74
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 좋아요 토글 동시 클릭(중복 요청) 시 레이스/역순 응답으로 카운트가 틀어질 수 있습니다
다음과 같이 in-flight 가드와 안전한 델타 계산을 적용해 주세요. 적용 diff(해당 범위 내 수정): - const handleLike = async () => {
- try {
- const response = await postFeedLike(feedId, !liked);
-
- if (response.isSuccess) {
- // 성공 시 상태 업데이트
- setLiked(response.data.isLiked);
- setLikeCount(prev => (response.data.isLiked ? prev + 1 : prev - 1));
- console.log('좋아요 상태 변경 성공:', response.data.isLiked);
- } else {
- console.error('좋아요 상태 변경 실패:', response.message);
- }
- } catch (error) {
- console.error('좋아요 API 호출 실패:', error);
- }
- };
+ const handleLike = async () => {
+ if (liking) return; // 중복 클릭 방지
+ setLiking(true);
+ const prevLiked = liked;
+ try {
+ const response = await postFeedLike(feedId, !prevLiked);
+
+ if (response.isSuccess) {
+ const nextLiked = response.data?.isLiked ?? !prevLiked;
+ // 상태 변화가 있는 경우에만 카운트 변경
+ setLiked(nextLiked);
+ setLikeCount(prev => {
+ if (nextLiked === prevLiked) return prev;
+ const next = nextLiked ? prev + 1 : prev - 1;
+ return Math.max(next, 0); // 음수 방어
+ });
+ console.log('좋아요 상태 변경 성공:', nextLiked);
+ } else {
+ console.error('좋아요 상태 변경 실패:', response.message);
+ }
+ } catch (error) {
+ console.error('좋아요 API 호출 실패:', error);
+ } finally {
+ setLiking(false);
+ }
+ };범위 밖 추가(상단 state 정의에 추가): // 추가: 중복 요청 방지용 상태
const [liking, setLiking] = useState(false);🤖 Prompt for AI Agents |
||
| }; | ||
|
|
||
| const handleSave = async () => { | ||
|
|
||
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.
💡 Verification agent
🧩 Analysis chain
API 래퍼 일관성 점검 권장
프로젝트 내 다른 API 래퍼들이 response.data 전체를 반환하는지, 혹은 data.data만 반환하는지 일관성을 확인해 주세요. 혼재 시 사용처에서 접근 경로가 달라져 버그 요인이 됩니다.
다음 스크립트로 API 래퍼들의 반환 패턴을 빠르게 점검할 수 있습니다.
🏁 Script executed:
Length of output: 3050
API 래퍼 반환 형태 통일 필요
현재 대부분의 API 래퍼는
response.data전체를 반환하고 있으나, 오직getMyProfile만 중첩된response.data.data를 반환하고 있어 사용처에서 접근 경로가 달라질 수 있습니다. 아래 파일을 점검해 반환 형태를 일관되게 맞춰 주세요.• 파일: src/api/users/getMyProfile.ts
위치: 16줄
변경 전:
변경 후 (다른 래퍼와 동일하게):
필요에 따라 모든 래퍼가
data.data만 반환하도록 통일하는 방안도 고려 가능합니다. 일관된 반환 구조를 선택해 전체 코드베이스에서 동일하게 적용해 주세요.🤖 Prompt for AI Agents