e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}>
GPT 기능은
diff --git a/front-end/src/app/api/playlistApi.ts b/front-end/src/app/api/playlistApi.ts
index 7ee16258..41698730 100644
--- a/front-end/src/app/api/playlistApi.ts
+++ b/front-end/src/app/api/playlistApi.ts
@@ -79,7 +79,7 @@ export const addVideo = async (
// 재생 목록에 영상 삭제
export const deleteVideos = async (
- videoId: string,
+ videoId: string | null,
playlistId: string,
token: string | undefined,
) => {
@@ -108,7 +108,7 @@ export const deleteVideos = async (
// 재생목록 삭제
-export const deletepPlaylist = async (playlistId: string, token: string | undefined) => {
+export const deletePlaylist = async (playlistId: string, token: string | undefined) => {
try {
const response = await axios.delete(`${baseUrl}/api/playlists/${playlistId}`, {
headers: {
diff --git a/front-end/src/app/api/postAPI.ts b/front-end/src/app/api/postAPI.ts
new file mode 100644
index 00000000..97bb1fe4
--- /dev/null
+++ b/front-end/src/app/api/postAPI.ts
@@ -0,0 +1,65 @@
+import apiClient from '@/components/axios/apiClient';
+
+// 게시글 목록 조회
+export const fetchPosts = async (category: string, query: string, page: number) => {
+ try {
+ const apiUrl = query
+ ? `/post?category=${category}&query=${query}&page=${page}`
+ : `/post/category/${category}?page=${page}`;
+
+ const response = await apiClient.get(apiUrl);
+ return response.data;
+ } catch (error) {
+ console.error('게시글을 불러오는 중 오류 발생:', error);
+ throw error;
+ }
+};
+
+// 게시글 단건 조회
+export const fetchPostDetail = async (id: string) => {
+ try {
+ console.log(`Fetching post detail for ID: ${id}`);
+ const response = await apiClient.get(`/post/${id}`);
+ console.log(`Fetched post detail: ${JSON.stringify(response.data)}`);
+ return response.data;
+ } catch (error) {
+ console.error(`게시글 ${id} 불러오기 실패:`, error);
+ throw error;
+ }
+};
+
+// 게시글 작성
+export const createPost = async (newPost: {
+ title: string;
+ content: string;
+ category: 'FREE' | 'QNA';
+}) => {
+ try {
+ const response = await apiClient.post('/post', newPost);
+ return response.data;
+ } catch (error) {
+ console.error('게시글 작성 중 오류 발생:', error);
+ throw error;
+ }
+};
+
+// 게시글 수정
+export const updatePost = async (id: string, updatedPost: { title: string; content: string }) => {
+ try {
+ const response = await apiClient.put(`/post/${id}`, updatedPost);
+ return response.data;
+ } catch (error) {
+ console.error(`게시글 ${id} 수정 실패:`, error);
+ throw error;
+ }
+};
+
+// 게시글 삭제
+export const deletePost = async (id: string) => {
+ try {
+ await apiClient.delete('/post', { data: { postIds: [id] } });
+ } catch (error) {
+ console.error(`게시글 ${id} 삭제 실패:`, error);
+ throw error;
+ }
+};
diff --git a/front-end/src/app/api/socialLoginApi.ts b/front-end/src/app/api/socialLoginApi.ts
new file mode 100644
index 00000000..68c48ed0
--- /dev/null
+++ b/front-end/src/app/api/socialLoginApi.ts
@@ -0,0 +1,22 @@
+import socialLogin from '@/components/axios/socialLogin';
+const redirect_uri = 'http://localhost:3000/socialLoginCallback'; // 로그인 이후 리다이렉트 될 Url 이후 수정 예정
+
+// 구글 로그인
+export const loginWithGoogle = async () => {
+ const googleLoginUrl = `/oauth2/authorization/google?mode=login&redirect_uri=${redirect_uri}`;
+ try {
+ window.location.href = `${socialLogin.defaults.baseURL}${googleLoginUrl}`;
+ } catch (error) {
+ console.error('구글 로그인 실패', error);
+ }
+};
+// 네이버 로그인
+export const loginWithNaver = async () => {
+ const naverLoginUrl = `/oauth2/authorization/naver?mode=login&redirect_uri=${redirect_uri}`;
+ try {
+ window.location.href = `${socialLogin.defaults.baseURL}${naverLoginUrl}`;
+ } catch (error) {
+ console.error('네이버 로그인 실패', error);
+ }
+};
+// 추후 Url 로 받은 토큰 값을 디코딩하는 과정 필요
diff --git a/front-end/src/app/categories/[id]/page.tsx b/front-end/src/app/categories/[id]/page.tsx
deleted file mode 100644
index bf95c7db..00000000
--- a/front-end/src/app/categories/[id]/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const Categories = () => {
- return (
-
-
카테고리 페이지
-
- );
-};
-
-export default Categories;
diff --git a/front-end/src/app/favicon.ico b/front-end/src/app/favicon.ico
index 718d6fea..11ebad3e 100644
Binary files a/front-end/src/app/favicon.ico and b/front-end/src/app/favicon.ico differ
diff --git a/front-end/src/app/gpt-histories/page.tsx b/front-end/src/app/gpt-histories/page.tsx
deleted file mode 100644
index 5ee4a3e3..00000000
--- a/front-end/src/app/gpt-histories/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const Gpthistories = () => {
- return (
-
-
gpt 기록
-
- );
-};
-
-export default Gpthistories;
diff --git a/front-end/src/app/layout.tsx b/front-end/src/app/layout.tsx
index 38e0c3b2..3329717f 100644
--- a/front-end/src/app/layout.tsx
+++ b/front-end/src/app/layout.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import React from 'react';
import '@/styles/common/common.scss';
import Header from '@/components/Header';
@@ -11,6 +9,20 @@ import '@/styles/pages/chatbot/chatbot.scss';
import ChatbotLayout from '@/components/ChatbotLayout';
import ClientProvider from '@/components/ClientProvider';
import { ReactQueryProvider } from '@/providers/ReactQueryProvider';
+import { Metadata } from 'next';
+
+export const metadata: Metadata = {
+ title: 'Techie',
+ description: '테키와 함께 유튜브 강의를 보며 메모하고, GPT와 함께 학습을 더 효율적으로 해보세요!',
+ openGraph: {
+ title: 'Techie',
+ description:
+ '테키와 함께 유튜브 강의를 보며 메모하고, GPT와 함께 학습을 더 효율적으로 해보세요!',
+ },
+ icons: {
+ icon: '/favicon.ico',
+ },
+};
export default function RootLayout({
children,
diff --git a/front-end/src/app/login/page.tsx b/front-end/src/app/login/page.tsx
index 79657480..85f64375 100644
--- a/front-end/src/app/login/page.tsx
+++ b/front-end/src/app/login/page.tsx
@@ -1,21 +1,26 @@
'use client';
import React, { useState } from 'react';
-import { useDispatch } from 'react-redux';
-import { useRouter } from 'next/navigation';
+
import { AxiosError } from 'axios';
-import Cookies from 'js-cookie';
-import { loginUser } from '@/app/api/loginUserApi';
import '@/styles/pages/login/login.scss';
-import { setUserInfo } from '@/redux/reducer';
+
import { devConsoleError } from '@/utils/logger';
+import { performLogin, decodeJWT } from '@/components/authservice/AuthLogin';
+import { useDispatch } from 'react-redux';
+import { useRouter } from 'next/navigation';
+import { setUserInfo } from '@/redux/reducer';
+import { loginWithGoogle, loginWithNaver } from '../api/socialLoginApi';
+import Image from 'next/image';
const Login = () => {
const [formData, setFormData] = useState({ email: '', password: '' });
- const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState('');
+
const router = useRouter();
+
const dispatch = useDispatch();
// 입력값 변경 시 formData 업데이트
@@ -31,57 +36,19 @@ const Login = () => {
setIsLoading(true);
try {
- await performLogin();
+ const token = await performLogin(formData);
+ window.dispatchEvent(new Event('loginStatusChanged'));
+ router.push('/'); // 로그인 성공 시 메인 페이지로 이동
+
+ // JWT 디코딩
+ const decodedJWT = decodeJWT(token);
+ dispatch(setUserInfo(decodedJWT));
} catch (loginError) {
handleLoginError(loginError);
} finally {
setIsLoading(false);
}
};
-
- // 로그인 요청 전송 함수
- const performLogin = async () => {
- try {
- const res = await loginUser(formData);
- if (res.status === 200) {
- const token = res.headers['authorization']?.split(' ')[1];
- if (token) {
- // 쿠키에 토큰을 저장
- Cookies.set('token', token, { expires: 1, path: '/' });
- window.dispatchEvent(new Event('loginStatusChanged'));
- router.push('/'); // 로그인 성공 시 메인 페이지로 이동
-
- // JWT 디코딩
- const decodedJWT = decodeJWT(token);
- dispatch(setUserInfo(decodedJWT));
- } else {
- devConsoleError('Token is undefined in the response headers');
- }
- } else {
- devConsoleError('Failed to log in, unexpected response status');
- }
- } catch (error) {
- handleLoginError(error);
- }
- };
-
- // JWT 디코딩 함수
- const decodeJWT = (token: string) => {
- const base64Payload = token.split('.')[1];
- const base64 = base64Payload.replace(/-/g, '+').replace(/_/g, '/');
- return JSON.parse(
- decodeURIComponent(
- window
- .atob(base64)
- .split('')
- .map(function (c) {
- return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
- })
- .join(''),
- ),
- );
- };
-
// 로그인 오류 처리 함수
const handleLoginError = (error: unknown) => {
const axiosError = error as AxiosError;
@@ -120,6 +87,19 @@ const Login = () => {