)}
@@ -41,7 +41,7 @@ export function InfoContents() {
return (
<>
{tabLabel === '가게 정보' && (
-
+
)}
diff --git a/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx b/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx
index 26d0106..c16366c 100644
--- a/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx
+++ b/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx
@@ -12,7 +12,7 @@ export function InfoStoreDetail() {
if (!storeDetail) return null;
return (
-
+
{edit ? : }
);
diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx
new file mode 100644
index 0000000..53988a3
--- /dev/null
+++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx
@@ -0,0 +1,31 @@
+import { FieldContainer } from '@/components/store-info';
+
+const VideoDescription = [
+ {
+ label: '제목',
+ value: '추천 멘트',
+ },
+ {
+ label: '상세 설명',
+ value:
+ '📍 신촌에서 찾은 숨은 맛집, 가문의 우동!매일 직접 뽑는 쫄깃한 면발과 깊은 국물 맛이 일품이에요.\n따뜻한 우동 한 그릇으로 오늘 하루를 마무리해보세요🍲\n✅ 위치: 서울 서대문구 ○○로 ○○\n✅ 영업시간: 매일 11:00 ~ 21:00 (브레이크타임 15:00~17:00)\n✅ 전화: 02-000-0000\n#신촌맛집 #가문의우동 #우동맛집 #신촌데이트코스 #신촌점심추천 #신촌저녁추천 #신촌핫플 #서울맛집',
+ },
+ {
+ label: '메뉴',
+ value: '아메리카노\n라떼\n티\n커피라떼',
+ },
+];
+
+export function VideoCheckDescription() {
+ return (
+
+ {VideoDescription.map(item => (
+
+ ))}
+
+ );
+}
diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx
new file mode 100644
index 0000000..308f2b2
--- /dev/null
+++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { useState } from 'react';
+import { LoadingSpinner } from '@/components/loading-spinner';
+import { useRouter } from 'next/navigation';
+
+export function VideoPlayer({ videoId }: { videoId: string }) {
+ const router = useRouter();
+ const videoUrl =
+ 'https://f002.backblazeb2.com/file/creatomate-c8xg3hsxdu/6683c14f-7d35-45ea-9402-d87fd498f5a7.mp4';
+
+ return (
+ <>
+ {/* ReactPlayer - 전체 화면 꽉 채우기 */}
+
+ {videoUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx
index fc70184..c6efa44 100644
--- a/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx
+++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx
@@ -1,18 +1,35 @@
-'use client';
-
import { GradientProgressBar } from '@/components/gradient-progress-bar';
-
+import TinyArrowOrangeIcon from '@/public/svg/mypage/tiny-arrow-orange.svg';
+import Link from 'next/link';
const FILE_PROGRESS = 72;
const FILE_NAME = '파일명파일명파일명파일명파일명파일명파일명파일명파일명';
+const FILE_RESULT = true;
export function VideoProgressHeader() {
return (
- <>
- {true && (
-
-
- 새로운 영상이 제작중이에요!
-
+
+
+ 새로운 영상이 제작중이에요!
+
+ {FILE_RESULT ? (
+ <>
+
+
+ {FILE_NAME}
+
+
+
+ 영상 확인하기
+
+
+
+
+ >
+ ) : (
+ <>
{FILE_NAME}
@@ -20,8 +37,8 @@ export function VideoProgressHeader() {
{FILE_PROGRESS}%
-
+ >
)}
- >
+
);
}
diff --git a/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx b/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx
index 1b51078..4c9d9ec 100644
--- a/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx
+++ b/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx
@@ -12,7 +12,7 @@ export const tabs: Record
= {
store: '가게 정보',
'store-detail': '상세 정보',
owner: '사장님 정보',
- video: '영상 관리',
+ video: '영상관리',
} as const;
const DEFAULT_TAB: TabType = 'store';
@@ -32,7 +32,11 @@ export function useInfoQuery() {
'tab',
parseAsStringLiteral(validTabs).withDefault(DEFAULT_TAB),
);
+
+ // 가게 상세 페이지 정보를 위한 쿼리 파라미터
const [storeName, setStoreName] = useQueryState('storeName', parseAsString);
+
+ // 가게 상세 정보 편집 여부
const [edit, setEdit] = useQueryState(
'edit',
parseAsBoolean.withDefault(false),
diff --git a/src/app/(pages)/(main)/mypage/page.tsx b/src/app/(pages)/(main)/mypage/page.tsx
index 9e5e521..eec57e5 100644
--- a/src/app/(pages)/(main)/mypage/page.tsx
+++ b/src/app/(pages)/(main)/mypage/page.tsx
@@ -2,6 +2,7 @@ import Link from 'next/link';
import StoreIcon from '@/public/svg/mypage/store.svg';
import OwnerIcon from '@/public/svg/mypage/owner.svg';
import VideoIcon from '@/public/svg/mypage/video.svg';
+import { LogoutButton } from '@/components/logout-button';
const LinkItems = [
{
@@ -15,7 +16,7 @@ const LinkItems = [
icon: ,
},
{
- label: '영상 관리',
+ label: '영상관리',
herf: '/mypage/manage-video',
icon: ,
},
@@ -39,6 +40,7 @@ export default function MyPage() {
{item.label}
))}
+
);
diff --git a/src/app/(pages)/layout.tsx b/src/app/(pages)/layout.tsx
index da3e8ee..bac3c80 100644
--- a/src/app/(pages)/layout.tsx
+++ b/src/app/(pages)/layout.tsx
@@ -22,9 +22,30 @@ export const metadata: Metadata = {
'creatomate',
'홍보',
'마케팅',
+ 'AI 숏폼 제작',
+ '숏폼 영상 제작',
+ '쇼츠 제작 플랫폼',
+ 'SNS 마케팅 자동화',
+ 'AI 마케팅 도구',
+ '음식점 홍보',
+ '외식업 마케팅',
+ '자영업 마케팅',
+ '식당 홍보',
+ '소상공인 마케팅',
+ '인스타그램 마케팅',
+ '틱톡 음식점 홍보',
+ '유튜브 숏츠 제작',
+ 'SNS 광고',
+ '바이럴 마케팅',
+ '신촌 음식점 홍보',
+ '서대문구 마케팅',
+ '이대 상권 마케팅',
+ '신촌 외식업',
+ '자영업 사장님 마케팅',
+ '푸드 크리에이터 제휴',
],
openGraph: {
- title: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스',
+ title: '쇼츠테이블',
description: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스',
type: 'website',
siteName: '쇼츠테이블',
@@ -43,7 +64,7 @@ export const metadata: Metadata = {
},
twitter: {
card: 'summary_large_image',
- title: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스',
+ title: '쇼츠테이블',
description: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스',
images: ['https://shorts-table.com/img/og/og-image.png'],
creator: '@shorts-table',
diff --git a/src/app/api/cookie-set/route.ts b/src/app/api/cookie-set/route.ts
new file mode 100644
index 0000000..e561799
--- /dev/null
+++ b/src/app/api/cookie-set/route.ts
@@ -0,0 +1,30 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { cookies } from 'next/headers';
+
+export async function POST(request: NextRequest) {
+ try {
+ const { token } = await request.json();
+
+ if (!token) {
+ return NextResponse.json({ error: '토큰이 필요합니다' }, { status: 400 });
+ }
+
+ const cookieStore = await cookies();
+
+ // 토큰 쿠키 설정
+ cookieStore.set('accessToken', token as string, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'strict',
+ maxAge: 60 * 60 * 24 * 7, // 7일
+ path: '/',
+ });
+
+ return NextResponse.json({ success: true });
+ } catch (error) {
+ return NextResponse.json(
+ { error: '토큰 설정 중 오류가 발생했습니다' },
+ { status: 500 },
+ );
+ }
+}
diff --git a/src/components/header.tsx b/src/components/header.tsx
index d463bcf..a2b291b 100644
--- a/src/components/header.tsx
+++ b/src/components/header.tsx
@@ -110,7 +110,7 @@ export function Header() {
};
case 'VIDEO_MANAGEMENT':
return {
- title: '영상 관리',
+ title: '영상관리',
showBackButton: true,
showEditButton: false,
};
diff --git a/src/components/loading-spinner.tsx b/src/components/loading-spinner.tsx
index 220f1fd..4a1ea86 100644
--- a/src/components/loading-spinner.tsx
+++ b/src/components/loading-spinner.tsx
@@ -1,9 +1,27 @@
-import { Loader2 } from 'lucide-react';
+// import { Loader2 } from 'lucide-react';
+
+// export function LoadingSpinner() {
+// return (
+//
+//
+//
+// );
+// }
+
+'use client';
+
+import Lottie from 'lottie-react';
+import loadingAnimation from '@/public/lottie/loading.json';
export function LoadingSpinner() {
return (
-
+
);
}
diff --git a/src/components/logout-button.tsx b/src/components/logout-button.tsx
new file mode 100644
index 0000000..2c457df
--- /dev/null
+++ b/src/components/logout-button.tsx
@@ -0,0 +1,51 @@
+'use client';
+
+import { cn } from '@/lib/utils/cn';
+import LogoutIcon from '@/public/svg/mypage/logout.svg';
+import clsx from 'clsx';
+import { useState } from 'react';
+
+const LOGOUT_BUTTON_STYLE =
+ 'py-2 flex-1 w-full h-full text-bodySmall text-white000 rounded-[8px]';
+export function LogoutButton() {
+ const [isLogout, setIsLogout] = useState(false);
+ return (
+ <>
+
+ {isLogout && (
+
void setIsLogout(false)}
+ >
+
+ 로그아웃 하시겠습니까?
+
+
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/src/components/store-info/index.tsx b/src/components/store-info/index.tsx
index 5008a82..ee4183e 100644
--- a/src/components/store-info/index.tsx
+++ b/src/components/store-info/index.tsx
@@ -23,7 +23,7 @@ export const FieldContainer = memo(
diff --git a/src/lib/api/config.ts b/src/lib/api/config.ts
new file mode 100644
index 0000000..268c9bc
--- /dev/null
+++ b/src/lib/api/config.ts
@@ -0,0 +1,31 @@
+import axios from 'axios';
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000';
+
+export const apiClient = axios.create({
+ baseURL: API_BASE_URL,
+ timeout: 10000,
+ withCredentials: true,
+});
+
+// Request 인터셉터 - 토큰 자동 추가
+apiClient.interceptors.request.use(
+ config => {
+ // 로직 추가 예정 -> header에다가 token 아마 ?
+ return config;
+ },
+ error => Promise.reject(error as Error),
+);
+
+// Response 인터셉터 - 에러 처리
+apiClient.interceptors.response.use(
+ response => response,
+ error => {
+ // if (error.response?.status === 401) {
+ // // 토큰 만료 시 처리
+ // Cookies.remove('accessToken');
+ // window.location.href = '/login';
+ // }
+ return Promise.reject(error as Error);
+ },
+);
diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts
new file mode 100644
index 0000000..c8bc44b
--- /dev/null
+++ b/src/lib/api/types.ts
@@ -0,0 +1,19 @@
+export interface ApiResponse {
+ data: T;
+ message?: string;
+ status: number;
+}
+
+export interface LoginRequest {
+ email: string;
+ password: string;
+}
+
+export interface LoginResponse {
+ token: string;
+ user: {
+ id: string;
+ email: string;
+ name: string;
+ };
+}
diff --git a/src/lib/auth/auth.service.ts b/src/lib/auth/auth.service.ts
new file mode 100644
index 0000000..9fc3a3f
--- /dev/null
+++ b/src/lib/auth/auth.service.ts
@@ -0,0 +1,17 @@
+import { apiClient } from '../api/config';
+import { ApiResponse, LoginResponse } from '../api/types';
+
+export const authService = {
+ // ... 기존 코드
+
+ async kakaoLogin(code: string): Promise {
+ const response = await apiClient.post>(
+ '/auth/login',
+ { code },
+ );
+
+ // 토큰 저장
+
+ return response.data.data;
+ },
+};
diff --git a/src/lib/auth/kakao.service.ts b/src/lib/auth/kakao.service.ts
new file mode 100644
index 0000000..4b4c343
--- /dev/null
+++ b/src/lib/auth/kakao.service.ts
@@ -0,0 +1,22 @@
+export const kakaoAuthService = {
+ // 카카오 로그인 URL 생성
+ getAuthUrl: () => {
+ const clientId = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID;
+ const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI;
+
+ const params = new URLSearchParams({
+ response_type: 'code',
+ client_id: clientId!,
+ redirect_uri: redirectUri!,
+ scope: 'profile_nickname,profile_image', // 필요한 권한
+ });
+
+ return `https://kauth.kakao.com/oauth/authorize?${params.toString()}`;
+ },
+
+ // 카카오 로그인 실행
+ login: () => {
+ const authUrl = kakaoAuthService.getAuthUrl();
+ window.location.href = authUrl;
+ },
+};
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 88e6a9c..a992f02 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -222,4 +222,11 @@
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: var(--color-orange400); /* 호버시 색상 */
}
+
+ /* ReactPlayer 비디오 전체 화면 스타일 */
+ .video-player-container .react-player video {
+ object-fit: cover !important;
+ width: 100% !important;
+ height: 100% !important;
+ }
}