diff --git a/app/(dashboard)/_ui/list-header/index.tsx b/app/(dashboard)/_ui/list-header/index.tsx
index c1edf917..b8ace106 100644
--- a/app/(dashboard)/_ui/list-header/index.tsx
+++ b/app/(dashboard)/_ui/list-header/index.tsx
@@ -4,12 +4,19 @@ import styles from './styles.module.scss'
const cx = classNames.bind(styles)
-const LIST_HEADER = ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독']
+const LIST_HEADER = {
+ default: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '구독'],
+ my: ['전략', '분석', 'MDD', 'SM SCORE', '수익률', '공개', '관리'],
+}
+
+interface Props {
+ type?: 'default' | 'my'
+}
-const ListHeader = () => {
+const ListHeader = ({ type = 'default' }: Props) => {
return (
-
- {LIST_HEADER.map((category) => (
+
+ {LIST_HEADER[type].map((category) => (
{category}
diff --git a/app/(dashboard)/_ui/list-header/styles.module.scss b/app/(dashboard)/_ui/list-header/styles.module.scss
index 0050d4c6..ee5053ef 100644
--- a/app/(dashboard)/_ui/list-header/styles.module.scss
+++ b/app/(dashboard)/_ui/list-header/styles.module.scss
@@ -5,6 +5,10 @@
height: 42px;
margin: 20px 0 10px;
+ &.my {
+ grid-template-columns: 3fr 1.5fr 1.7fr 1.3fr 1.5fr 1.2fr 1.2fr;
+ }
+
.category {
display: flex;
align-items: center;
diff --git a/app/(dashboard)/my/_api/get-my-strategy-list.ts b/app/(dashboard)/my/_api/get-my-strategy-list.ts
new file mode 100644
index 00000000..3c072bcc
--- /dev/null
+++ b/app/(dashboard)/my/_api/get-my-strategy-list.ts
@@ -0,0 +1,19 @@
+import axiosInstance from '@/shared/api/axios'
+import { StrategiesModel } from '@/shared/types/strategy-details-data'
+
+// 실제 api 나오면 수정 필요함
+// totalElements 사용해서 hasmore값 계산해야될 것 같음
+
+interface StrategiesResponseModel {
+ result: {
+ strategies: StrategiesModel[]
+ hasMore: boolean
+ }
+}
+
+export const getMyStrategyList = async ({ page = 1, size = 4 }: { page: number; size: number }) => {
+ const response = await axiosInstance.get
(
+ `/api/my-strategies/page=${page}&size=${size}`
+ )
+ return response.data.result
+}
diff --git a/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts
new file mode 100644
index 00000000..8b87cc7c
--- /dev/null
+++ b/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list.ts
@@ -0,0 +1,24 @@
+import { getMyStrategyList } from '@/app/(dashboard)/my/_api/get-my-strategy-list'
+import { useInfiniteQuery } from '@tanstack/react-query'
+
+import { StrategiesModel } from '@/shared/types/strategy-details-data'
+
+interface StrategiesPageModel {
+ strategies: StrategiesModel[]
+ hasMore: boolean
+}
+
+export const useGetMyStrategyList = () => {
+ return useInfiniteQuery({
+ queryKey: ['myStrategies'],
+ queryFn: async ({ pageParam = 1 }) => {
+ const page = typeof pageParam === 'number' ? pageParam : 1
+ return getMyStrategyList({ page, size: 4 })
+ },
+ getNextPageParam: (lastPage, pages) => {
+ if (!lastPage.hasMore) return undefined
+ return pages.length + 1
+ },
+ initialPageParam: 1,
+ })
+}
diff --git a/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx
new file mode 100644
index 00000000..29e5edc1
--- /dev/null
+++ b/app/(dashboard)/my/strategies/_ui/my-strategy-list/index.tsx
@@ -0,0 +1,42 @@
+'use client'
+
+import { useCallback, useRef } from 'react'
+
+import StrategiesItem from '@/app/(dashboard)/_ui/strategies-item'
+import { useGetMyStrategyList } from '@/app/(dashboard)/my/_hooks/query/use-get-my-strategy-list'
+
+import { useIntersectionObserver } from '@/shared/hooks/custom/use-intersection-observer'
+
+const MyStrategyList = () => {
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetMyStrategyList()
+
+ const loadMoreRef = useRef(null)
+
+ const onIntersect = useCallback(
+ (entry: IntersectionObserverEntry) => {
+ if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) {
+ fetchNextPage()
+ }
+ },
+ [fetchNextPage, hasNextPage, isFetchingNextPage]
+ )
+
+ useIntersectionObserver({
+ ref: loadMoreRef,
+ onIntersect,
+ })
+
+ const strategies = data?.pages.flatMap((page) => page.strategies) || []
+
+ return (
+ <>
+ {strategies.map((strategy) => (
+
+ ))}
+
+ {isFetchingNextPage && 로딩 중...
}
+ >
+ )
+}
+
+export default MyStrategyList
diff --git a/app/(dashboard)/my/strategies/page.tsx b/app/(dashboard)/my/strategies/page.tsx
index 6f7e77b5..7010a8a5 100644
--- a/app/(dashboard)/my/strategies/page.tsx
+++ b/app/(dashboard)/my/strategies/page.tsx
@@ -1,5 +1,25 @@
+import { Suspense } from 'react'
+
+import classNames from 'classnames/bind'
+
+import Title from '@/shared/ui/title'
+
+import ListHeader from '../../_ui/list-header'
+import MyStrategyList from './_ui/my-strategy-list'
+import styles from './styles.module.scss'
+
+const cx = classNames.bind(styles)
+
const MyStrategiesPage = () => {
- return <>>
+ return (
+
+
+
+ Loading...}>
+
+
+
+ )
}
export default MyStrategiesPage
diff --git a/app/(dashboard)/my/strategies/styles.module.scss b/app/(dashboard)/my/strategies/styles.module.scss
new file mode 100644
index 00000000..f481a8ff
--- /dev/null
+++ b/app/(dashboard)/my/strategies/styles.module.scss
@@ -0,0 +1,3 @@
+.container {
+ margin-top: 80px;
+}
diff --git a/shared/hooks/custom/use-intersection-observer.ts b/shared/hooks/custom/use-intersection-observer.ts
new file mode 100644
index 00000000..0ffbf5bf
--- /dev/null
+++ b/shared/hooks/custom/use-intersection-observer.ts
@@ -0,0 +1,37 @@
+import { RefObject, useEffect } from 'react'
+
+interface UseIntersectionObserverProps {
+ ref: RefObject
+ onIntersect: (entry: IntersectionObserverEntry) => void
+ threshold?: number
+ rootMargin?: string
+}
+
+export const useIntersectionObserver = ({
+ ref,
+ onIntersect,
+ threshold = 0.1,
+ rootMargin = '0px',
+}: UseIntersectionObserverProps) => {
+ useEffect(() => {
+ if (!ref.current) return
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => onIntersect(entry))
+ },
+ {
+ threshold,
+ rootMargin,
+ }
+ )
+
+ observer.observe(ref.current)
+
+ return () => {
+ if (ref.current) {
+ observer.unobserve(ref.current)
+ }
+ }
+ }, [ref, threshold, rootMargin, onIntersect])
+}
diff --git a/shared/types/auth.ts b/shared/types/auth.ts
index 1025dc33..6b24d3cf 100644
--- a/shared/types/auth.ts
+++ b/shared/types/auth.ts
@@ -53,10 +53,10 @@ export interface TokenStatusModel {
export const isAdmin = (user: UserModel | null): boolean => {
if (!user) return false
- return user.role.includes('admin')
+ return user.role.includes('ADMIN')
}
export const isTrader = (user: UserModel | null): boolean => {
if (!user) return false
- return user.role.includes('trader')
+ return user.role.includes('TRADER')
}