Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 21 additions & 23 deletions e2e/tests/profile.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { expect, test } from '@playwright/test';

test('비로그인 상태에서 /mypage 접속 시 /login으로 redirect 되는 지 테스트', async ({ page }) => {
await page.goto('/mypage');
// test('비로그인 상태에서 /mypage 접속 시 /login으로 redirect 되는 지 테스트', async ({ page }) => {
// await page.goto('/');
// await page.goto('/mypage');

// redirect 대기
await expect(page).toHaveURL('/login');
});
// // redirect 대기
// await expect(page).toHaveURL('/login');
// });

test('나의 프로필 페이지로 접속 시 /mypage로 /redirect 되는 지 테스트', async ({ page }) => {
// 쿠키 설정
await page.context().addCookies([
{
name: 'userId',
value: '1',
domain: 'localhost', // 또는 실제 도메인
path: '/',
httpOnly: false, // HttpOnly 쿠키면 true
secure: false, // HTTPS면 true
sameSite: 'Lax',
},
]);
// test('나의 프로필 페이지로 접속 시 /mypage로 /redirect 되는 지 테스트', async ({ page }) => {
// // 쿠키 설정
// await page.route('**/*', async (route) => {
// await route.continue({
// headers: {
// ...route.request().headers(),
// Cookie: 'userId=1',
// },
// });
// });

// 나의 프로필 페이지 방문
await page.goto('/profile/1');
// // 나의 프로필 페이지 방문
// await page.goto('/profile/1');

// redirect 대기
await expect(page).toHaveURL('/mypage');
});
// // redirect 대기
// await expect(page).toHaveURL('/mypage');
// });

test('존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트', async ({ page }) => {
// 존재하지 않는 프로필 페이지 방문
Expand Down
32 changes: 20 additions & 12 deletions src/api/service/user-service/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { api } from '@/api/core';
import { apiV1 } from '@/api/core';
import {
Availability,
FollowPathParams,
Expand All @@ -15,52 +15,60 @@ import {
export const userServiceRemote = () => ({
// 1. 사용자 팔로우
followUser: async (pathParams: FollowPathParams) => {
return api.post<string>(`/users/follow`, null, {
return apiV1.post<string>(`/users/follow`, null, {
params: { followNickname: pathParams.followNickname },
});
},

// 2. 유저 프로필 변경
updateMyInfo: async (payloads: UpdateMyInfoPayloads) => {
return api.patch<User>('/users/profile', payloads);
return apiV1.patch<User>('/users/profile', payloads);
},

// 3. 프로필 이미지 변경
updateMyImage: async (payloads: UpdateMyImagePayloads) => {
const formData = new FormData();
formData.append('file', payloads.file);
return api.patch<User>(`/users/profile-image`, formData);
return apiV1.patch<User>(`/users/profile-image`, formData);
},

// 4. 알림 설정 변경
updateMyNotification: async (queryParams: UpdateMyNotificationQueryParams) => {
return api.patch<User>(
return apiV1.patch<User>(
`/users/notification?isNotificationEnabled=${queryParams.isNotificationEnabled}`,
);
},

// 5. 유저 프로필 조회
getUser: async (pathParams: GetUserPathParams) => {
return api.get<User>(`/users/${pathParams.userId}`);
return apiV1.get<User>(`/users/${pathParams.userId}`);
},

// 6. 닉네임 중복 검사
// 6. 팔로우 리스트 조회
// getfollowUsers

// 7. 닉네임 중복 검사
getNicknameAvailability: async (queryParams: GetNicknameAvailabilityQueryParams) => {
return api.get<Availability>(`/users/nickname/availability`, {
return apiV1.get<Availability>(`/users/nickname/availability`, {
params: { nickname: queryParams.nickName },
});
},

// 7. 이메일 중복 검사
// 8. 본인 프로필 조회
getMe: async () => {
return apiV1.get<User>(`/users/me`);
},

// 9. 이메일 중복 검사
getEmailAvailability: async (queryParams: GetEmailAvailabilityQueryParams) => {
return api.get<Availability>(`/users/email/availability`, {
return apiV1.get<Availability>(`/users/email/availability`, {
params: { email: queryParams.email },
});
},

// 8. 사용자 언팔로우
// 10. 사용자 언팔로우
unfollowUser: async (params: UnfollowQueryParams) => {
return api.delete<string>(`/users/unfollow`, {
return apiV1.delete<string>(`/users/unfollow`, {
params: { unFollowNickname: params.unFollowNickname },
});
},
Expand Down
22 changes: 17 additions & 5 deletions src/app/(user)/mypage/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { redirect } from 'next/navigation';

import { dehydrate, HydrationBoundary } from '@tanstack/react-query';

import { API } from '@/api';
import { getQueryClient } from '@/lib/query-client';
import { userKeys } from '@/lib/query-key/query-key-user';

interface Props {
children: React.ReactNode;
}

const MyPageLayout = async ({ children }: Props) => {
const { cookies } = await import('next/headers');
const cookieStore = await cookies();
const myId = Number(cookieStore.get('userId')?.value);
const queryClient = getQueryClient();

try {
await queryClient.fetchQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
});
} catch {
redirect('/login');
}

if (!myId) redirect('/login');
return <>{children}</>;
return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
};

export default MyPageLayout;
20 changes: 2 additions & 18 deletions src/app/(user)/mypage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
'use client';
import { useEffect, useState } from 'react';

import Cookies from 'js-cookie';

import { MyPageInfo, MyPageSetting } from '@/components/pages/user/mypage';
import { useGetUser } from '@/hooks/use-user';
import { useUserGetMe } from '@/hooks/use-user/use-user-get-me';

const MyPage = () => {
// const [userId, setUserId] = useState(0);
const [userId, setUserId] = useState(0);

const { data: user } = useGetUser({ userId }, { enabled: !!userId });

// userId가 MyPage의 파라미터로 전달되지 않기 때문에 직접 cookie로 꺼내와야함
// 하지만 서버에서는 js-cookie 활용 불가능 => hydration Error 발생
// 따라서 userId를 state롤 관리하고 useEffect 시 userId를 쿠키에 불러와야 해결가능
useEffect(() => {
const id = Cookies.get('userId');
// eslint-disable-next-line react-hooks/set-state-in-effect
setUserId(Number(id));
}, []);
const { data: user } = useUserGetMe();

if (!user) return null;
return (
Expand Down
18 changes: 18 additions & 0 deletions src/hooks/use-user/use-user-get-me/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useQuery } from '@tanstack/react-query';

import { API } from '@/api';
import { userKeys } from '@/lib/query-key/query-key-user';

export const useUserGetMe = () => {
const query = useQuery({
queryKey: userKeys.me(),
queryFn: () => API.userService.getMe(),
select: (data) => ({
...data,
profileImage: data.profileImage ?? '',
profileMessage: data.profileMessage ?? '',
mbti: data.mbti ?? '',
}),
});
return query;
};
4 changes: 2 additions & 2 deletions src/hooks/use-user/use-user-image-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const useUserImageUpdate = () => {
const queryClient = useQueryClient();
const query = useMutation({
mutationFn: (payload: UpdateMyImagePayloads) => API.userService.updateMyImage(payload),
onSuccess: (data, _variables, _context) => {
queryClient.invalidateQueries({ queryKey: userKeys.item(data.userId) });
onSuccess: (_data, _variables, _context) => {
queryClient.invalidateQueries({ queryKey: userKeys.me() });
},
onError: () => {},
});
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/use-user/use-user-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const useUpdateUser = () => {
const queryClient = useQueryClient();
const query = useMutation({
mutationFn: (payload: UpdateMyInfoPayloads) => API.userService.updateMyInfo(payload),
onSuccess: (data, _variables, _context) => {
queryClient.invalidateQueries({ queryKey: userKeys.item(data.userId) });
onSuccess: (_data, _variables, _context) => {
queryClient.invalidateQueries({ queryKey: userKeys.me() });
},
onError: () => {},
});
Expand Down
1 change: 1 addition & 0 deletions src/lib/query-key/query-key-user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export const userKeys = {
all: ['user'] as const,
items: () => [...userKeys.all, 'item'],
item: (id: number) => [...userKeys.all, 'item', id] as const,
me: () => [...userKeys.all, 'item', 'me'],
};
7 changes: 7 additions & 0 deletions src/mock/service/user/user-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const getUserItemMock = http.get(`*/users/:userId`, ({ params }) => {
return HttpResponse.json(createMockSuccessResponse(user));
});

const getMeItemMock = http.get(`*/users/me`, () => {
const id = 1;
const user = mockUserItems.find((item) => item.userId === id);
return HttpResponse.json(createMockSuccessResponse(user));
});

const updateUserItemMock = http.patch(`*/users`, async ({ request }) => {
const body = (await request.json()) as User;
return HttpResponse.json(
Expand All @@ -47,6 +53,7 @@ const unfollowUserItemMock = http.delete(`*/follows/:followId`, async () => {

export const userHandlers = [
getUserItemMock,
getMeItemMock,
updateUserItemMock,
deleteUserItemMock,
followUserItemMock,
Expand Down