Skip to content
Merged
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
248f260
[refactor] 무한스크롤 tanstack
mtm-git1018 Oct 14, 2025
2960ca0
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 14, 2025
e7b7e3b
[refactor] 레시피페이지 리팩토링
mtm-git1018 Oct 14, 2025
6945950
[refactor] 아코디언박스
mtm-git1018 Oct 14, 2025
3e5306e
[feat]필터링 뒤로가기 스크롤 저장
mtm-git1018 Oct 15, 2025
311548b
[chore] 머지 전 커밋
mtm-git1018 Oct 15, 2025
d134c55
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
0e5ac99
[chore]머지 전 커밋 누락 내용 커밋
mtm-git1018 Oct 15, 2025
7dacb80
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
b38afe6
[refactor]리팩토링 커밋
mtm-git1018 Oct 15, 2025
f02f555
[refactor] 정렬 중복아이템문제
mtm-git1018 Oct 15, 2025
2b825cb
[feat] 칵테일 정렬기능
mtm-git1018 Oct 15, 2025
d8caccc
[fix]댓글 알림 수정
mtm-git1018 Oct 15, 2025
bc6d1a9
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
ff02efa
[chore] 충돌사항 수정
mtm-git1018 Oct 15, 2025
c3452c4
Merge branch 'dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
0befdd8
[docs] 필요없는 폰트파일 정리
mtm-git1018 Oct 15, 2025
a864f98
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
86fb7f6
[feat]sse설정
mtm-git1018 Oct 15, 2025
84384ef
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
a629822
[feat]sse알림설정
mtm-git1018 Oct 15, 2025
deefde9
[chore]머지후 커밋
mtm-git1018 Oct 15, 2025
8de2150
[style] 메인페이지 아코디언 오류 수정
mtm-git1018 Oct 15, 2025
2052704
[feat]알림 SSE기능
mtm-git1018 Oct 15, 2025
44ca357
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
06f78da
[chore] console.log 삭제
mtm-git1018 Oct 17, 2025
dd44e1d
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 17, 2025
85c586e
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 20, 2025
21a9de5
[docs]마이페이지 폴더구조 정리
mtm-git1018 Oct 21, 2025
73367e3
[refactor] 전역상태에서 필터링 삭제
mtm-git1018 Oct 23, 2025
d9e4130
refactor/이미지 개선
mtm-git1018 Nov 7, 2025
00c30b0
chore/포매팅
mtm-git1018 Nov 7, 2025
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
5 changes: 4 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
reactStrictMode: false,
experimental: {
scrollRestoration: false,
},
images: {
// 외부 이미지 최적화 완전 비활성화 (Vercel 유료 기능 회피)
unoptimized: true,
// unoptimized: true,
domains: [
'team2-app-s3-bucket.s3.ap-northeast-2.amazonaws.com',
'team2-app-s3-bucket.s3.amazonaws.com',
Expand All @@ -15,12 +16,14 @@ const nextConfig: NextConfig = {
{
protocol: 'https',
hostname: 'www.thecocktaildb.com',
pathname: '/images/**',
},
],
},
env: {
NPUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
},

// webpack 설정
webpack: (config) => {
// @ts-expect-error 타입 에러 무시
Expand Down
4 changes: 2 additions & 2 deletions src/app/(with-layout)/mypage/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MyNav from '@/domains/mypage/main/MyNav';
import MyProfile from '@/domains/mypage/main/MyProfile';
import MyNav from '@/domains/mypage/components/main/MyNav';
import MyProfile from '@/domains/mypage/components/main/MyProfile';
import SkeletonLayout from '@/domains/mypage/skeleton/main/SkeletonLayout';
import { Suspense } from 'react';

Expand Down
3 changes: 1 addition & 2 deletions src/app/(with-layout)/mypage/my-setting/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import MySetting from '@/domains/mypage/main/MySetting';

import MySetting from '@/domains/mypage/components/main/MySetting';
import { Metadata } from 'next';
export const metadata: Metadata = {
title: '마이페이지',
Expand Down
3 changes: 2 additions & 1 deletion src/domains/community/hook/useItemVirtualizer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useVirtualizer } from '@tanstack/react-virtual';
import { CommentType, Post } from '../types/post';
import { Cocktail } from '@/domains/recipe/types/types';

export function useItemVirtualizer(
items: CommentType[] | Post[] | null,
items: CommentType[] | Post[] | Cocktail[] | null,
parentRef: React.RefObject<HTMLElement | null>
) {
return useVirtualizer({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import Help from '@/shared/assets/icons/help_24.svg';
import ToolTip from '@/shared/components/tool-tip/ToolTip';
import useMedia from '../hook/useMedia';
import useMedia from '../../hook/useMedia';

function MyAbv({ abv }: { abv: number }) {
const isMd = useMedia('(min-width:768px)');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';
import TabMenu from '@/domains/mypage/main/TabMenu';

import Link from 'next/link';
import { usePathname } from 'next/navigation';
import TabMenu from './TabMenu';

const MAIN_TABMENU = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import AbvGraph from '@/domains/shared/components/abv-graph/AbvGraph';
import MyAbv from './MyAbv';
import SsuryImage from './SsuryImage';
import useFetchProfile from '../api/fetchProfile';

import { useQuery } from '@tanstack/react-query';
import useFetchProfile from '../../api/fetchProfile';

function MyProfile() {
const { fetchProfile } = useFetchProfile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import ToggleBtn from '@/domains/mypage/components/ToggleBtn';
import WithdrawModal from '@/domains/mypage/components/WithdrawModal';
import TextButton from '@/shared/components/button/TextButton';
import { useEffect, useState } from 'react';
import useFetchProfile from '../api/fetchProfile';

import { useQuery } from '@tanstack/react-query';
import useFetchProfile from '../../api/fetchProfile';

function MySetting() {
const { fetchProfile } = useFetchProfile();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import useProfileSsury from '../hook/useProfileSsury';
import Image from 'next/image';
import useProfileSsury from '../../hook/useProfileSsury';

function SsuryImage({ abvLevel }: { abvLevel: number }) {
const profileImage = useProfileSsury(abvLevel);
Expand Down
3 changes: 2 additions & 1 deletion src/domains/mypage/components/pages/my-active/MyComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function MyComment() {
credentials: 'include',
});
const json = await res.json();

setMyComment(json.data.items);
};

Expand All @@ -31,7 +32,7 @@ function MyComment() {

return (
<section>
{CommentList.length !== 0 ? (
{myComment.length !== 0 ? (
<CommentList
comments={myComment}
isLoading={isLoading}
Expand Down
34 changes: 28 additions & 6 deletions src/domains/recipe/api/fetchRecipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface PageParam {
lastValue: number | string;
}

// 로그인 한 유저의 킵 칵테일을 Get으로 불러옴
const fetchKeep = async (): Promise<Set<number>> => {
const res = await fetch(`${getApi}/me/bar`, {
method: 'GET',
Expand All @@ -41,6 +42,7 @@ const fetchKeep = async (): Promise<Set<number>> => {
return new Set(myKeep.map((v: { cocktailId: number }) => v.cocktailId));
};

// 비 로그인 유저도 볼 수 있는 칵테일 API fetch 각 종 정렬 파라미터를 문자열로 받아서 정렬함
const fetchRecipe = async (
pageParam: PageParam | null,
size: number,
Expand Down Expand Up @@ -68,6 +70,7 @@ const fetchRecipe = async (
return json.data ?? [];
};

// 검색전용 API 여기서 필터링 토글도 받음
const searchCocktails = async (filters: SearchFilters): Promise<Cocktail[]> => {
const body = {
keyword: filters.keyword?.trim() ?? '',
Expand All @@ -90,6 +93,7 @@ const searchCocktails = async (filters: SearchFilters): Promise<Cocktail[]> => {
return json.data ?? [];
};

// 적용된 필터
const hasActiveFilters = (filters: SearchFilters): boolean => {
return !!(
filters.keyword?.trim() ||
Expand All @@ -99,10 +103,24 @@ const hasActiveFilters = (filters: SearchFilters): boolean => {
);
};

export const useKeepQuery = () => {
const user = useAuthStore((state) => state.user);

return useQuery({
queryKey: ['keeps', user?.id],
queryFn: fetchKeep,
enabled: !!user,
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
});
};

// 무한스크롤 fetch
export const useCocktailsInfiniteQuery = (size: number = 20, sortBy?: Sort) => {
const user = useAuthStore((state) => state.user);
const queryClient = useQueryClient();
const prevSortBy = useRef(sortBy);
const { data: keepIds } = useKeepQuery();

useEffect(() => {
if (prevSortBy.current !== undefined && prevSortBy.current !== sortBy) {
Expand All @@ -118,11 +136,10 @@ export const useCocktailsInfiniteQuery = (size: number = 20, sortBy?: Sort) => {
queryFn: async ({ pageParam }) => {
const cocktails = await fetchRecipe(pageParam, size, sortBy);

if (user) {
const keepId = await fetchKeep();
if (user && keepIds) {
return cocktails.map((item) => ({
...item,
isKeep: keepId.has(item.cocktailId),
isKeep: keepIds.has(item.cocktailId),
}));
}

Expand Down Expand Up @@ -159,31 +176,36 @@ export const useCocktailsInfiniteQuery = (size: number = 20, sortBy?: Sort) => {
initialPageParam: null as PageParam | null,
refetchOnMount: false,
refetchOnWindowFocus: false,
staleTime: 2 * 60 * 1000,
});
};

// 검색용 fetch
export const useCocktailsSearchQuery = (filters: SearchFilters) => {
const user = useAuthStore((state) => state.user);
const isActive = hasActiveFilters(filters);
const { data: keepIds } = useKeepQuery();

return useQuery({
queryKey: ['cocktails', 'search', filters, user?.id],
queryFn: async () => {
const cocktails = await searchCocktails(filters);
if (user && cocktails.length > 0) {
const keepId = await fetchKeep();
if (user && cocktails.length > 0 && keepIds) {
return cocktails.map((item) => ({
...item,
isKeep: keepId.has(item.cocktailId),
isKeep: keepIds.has(item.cocktailId),
}));
}
return cocktails;
},
enabled: isActive,
refetchOnMount: false,
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
});
};

// 검색모드를 전환하여 어떤 fetch를 하는지 결정
export const useCocktails = (
filters: CocktailFilter,
infiniteScrollSize: number = 20,
Expand Down
20 changes: 18 additions & 2 deletions src/domains/recipe/components/main/CocktailFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SelectBox from '@/shared/components/select-box/SelectBox';
import { useRouter } from 'next/navigation';
import { useRouter, useSearchParams } from 'next/navigation';

interface Props {
cocktailsEA: number;
Expand All @@ -12,18 +12,34 @@ function CocktailFilter({ cocktailsEA }: Props) {
댓글순: 'comments',
};

const searchParams = useSearchParams();
const router = useRouter();

const getCurrentSort = () => {
const sortBy = searchParams.get('sortBy') || 'recent';

const entry = Object.entries(sortMap).find(([_, value]) => value === sortBy);
return entry ? entry[0] : '최신순';
};

const handleChange = (selectTitle: string) => {
const sortValue = sortMap[selectTitle as keyof typeof sortMap];

const params = new URLSearchParams(searchParams.toString());
params.set('sortBy', sortValue);

router.push(`?sortBy=${sortValue}`);
};

return (
<div className="h-10 flex justify-between items-center mt-3 border-b-1 border-gray-light">
<p>{cocktailsEA}개+</p>
<SelectBox option={['최신순', '댓글순', '인기순']} title="최신순" onChange={handleChange} />
<SelectBox
option={['최신순', '댓글순', '인기순']}
title="최신순"
onChange={handleChange}
value={getCurrentSort()}
/>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';

import SelectBox from '@/shared/components/select-box/SelectBox';
import { Dispatch, SetStateAction, useEffect } from 'react';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { Dispatch, SetStateAction } from 'react';

interface Props {
setAlcoholBaseTypes: Dispatch<SetStateAction<string[]>>;
Expand Down Expand Up @@ -64,26 +64,21 @@ const SELECT_OPTIONS = [
},
];

function Accordion({ setAlcoholBaseTypes, setCocktailTypes, setAlcoholStrengths }: Props) {
function CocktailFilterRadios({
setAlcoholBaseTypes,
setAlcoholStrengths,
setCocktailTypes,
}: Props) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();

// url 파라미터에서 값을 가져와 POST를 도와줌
useEffect(() => {
const abv = searchParams.get('abv');
const base = searchParams.get('base');
const glass = searchParams.get('glass');

setAlcoholStrengths(abv ? [abv] : []);
setAlcoholBaseTypes(base ? [base] : []);
setCocktailTypes(glass ? [glass] : []);
}, [searchParams, setAlcoholStrengths, setAlcoholBaseTypes, setCocktailTypes]);

// 파라미터 값을 한글로 역 변환해주는 함수
const getDisplayValue = (id: string, code: string | null): string => {
// 파라미터에서 가져오는 값 없다면 전체로 표시 이 값을 전체가 아닌 타이틀이 나와야함
if (!code) return '전체';

// 파라미터에서 가져오는 아이디가 선택한 아이디와 일치하는지
const optionGroup = SELECT_OPTIONS.find((opt) => opt.id === id);
if (!optionGroup) return '전체';

Expand Down Expand Up @@ -147,9 +142,8 @@ function Accordion({ setAlcoholBaseTypes, setCocktailTypes, setAlcoholStrengths
<SelectBox
option={option}
title={title}
id={id}
groupKey="filter"
value={currentValue}
align="left"
onChange={(value) => handleSelect(id, value)}
/>
</li>
Expand All @@ -159,4 +153,4 @@ function Accordion({ setAlcoholBaseTypes, setCocktailTypes, setAlcoholStrengths
);
}

export default Accordion;
export default CocktailFilterRadios;
Loading
Loading