CSR + SSR
src/
└── app/
├── layout.tsx // 공통 레이아웃 (RootLayout & ServerLayout 조합)
├── RootLayout/ // 클라이언트 전용 레이아웃
│ └── layout.tsx
│ └── css/ // 클라이언트 전용 CSS
│ └── fonts/ // 클라이언트 폰트
├── ServerLayout/ // 서버 전용 레이아웃
│ └── layout.tsx
│ └── css/ // 서버 전용 CSS
│ └── fonts/ // 서버 폰트
├── (site)/ // 실제 페이지가 위치하는 곳
│ ├── page.tsx // 홈 페이지
│ ├── (pages)/ // 서브 페이지들
│ │ └── Topbar/
│ │ └── page.tsx
│ │ └── mypage/
│ │ └── signup/
│ └── layout.tsx // 사이트 전용 레이아웃 (필요 시)
└── context/ // 전역 상태 관리 (예: ModalContext)










"use client"; : 이 파일이 클라이언트 사이드에서 실행됨을 명시.
Breadcrumb : 페이지 상단에 위치한 내비게이션 경로 표시용 컴포넌트.
Link : Next.js의 링크 컴포넌트로, 페이지 간 이동을 할 때 사용.
useState : React의 상태 관리 훅으로, 폼 데이터(email, password)를 관리.
axios : HTTP 요청을 보내기 위한 라이브러리.
useRouter : Next.js의 내비게이션 기능을 사용할 수 있는 훅.
"use client"
// import Breadcrumb from "@/components/ClientComponent/Common/Breadcrumb";
import Link from "next/link";
import React, { useState, useEffect } from "react";
import axios from "axios";
import { useRouter } from "next/navigation";
const Signin = () => {
const [formData, setFormData] = useState({
email: "",
password: "",
});
const router = useRouter();
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};
// 토큰 없이
// const handleSubmit = async (e) => {
// e.preventDefault();
// const formDataToSend = new FormData();
// formDataToSend.append("email", formData.email);
// formDataToSend.append("password", formData.password);
// try {
// const response = await axios.post(
// "http://47.130.76.132:8080/auth/login",
// formDataToSend,
// {
// headers: {
// "Content-Type": "multipart/form-data",
// },
// }
// );
// console.log("Login successful:", response.data);
// alert("Login successful!");
// setFormData({
// email: "",
// password: "",
// });
// router.push("/");
// } catch (error) {
// console.error("Login failed:", error.response?.data || error.message);
// alert("Login failed. Please try again.");
// }
// };
// 토큰 있을때
const handleSubmit = async (e) => {
e.preventDefault();
const formDataToSend = new FormData();
formDataToSend.append("email", formData.email);
formDataToSend.append("password", formData.password);
try {
const response = await axios.post(
"http://47.130.76.132:8080/auth/login",
formDataToSend,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
// 서버 응답 전체 출력 (디버깅용)
console.log("Full response:", response);
// 서버 응답 헤더 또는 본문에서 AccessToken 추출
const accessToken = response.headers['accesstoken'] || response.data.accessToken; // 소문자 키 사용
if (accessToken) {
// 로컬 스토리지에 AccessToken 저장
localStorage.setItem('accessToken', accessToken);
console.log("Login successful:", response.data);
alert("Login successful!");
setFormData({
email: "",
password: "",
});
router.push("/");
} else {
throw new Error("AccessToken not received");
}
} catch (error) {
console.error("Login failed:", error.response?.data || error.message);
alert("Login failed. Please try again.");
}
};
// 토큰 들어왔는지 테스트
// useEffect(() => {
// const token = localStorage.getItem('accessToken');
// if (token) {
// console.log("Token exists in localStorage:", token);
// } else {
// console.log("No token found in localStorage.");
// }
// }, []);
return (
<>
{/* <Breadcrumb title={"Signin"} pages={["Signin"]} /> */}
<section className="overflow-hidden py-20 bg-gray-2">
<div className="max-w-[1170px] w-full mx-auto px-4 sm:px-8 xl:px-0">
<div className="max-w-[570px] w-full mx-auto rounded-xl bg-white shadow-1 p-4 sm:p-7.5 xl:p-11">
<div className="text-center mb-11">
<h2 className="font-semibold text-xl sm:text-2xl xl:text-heading-5 text-dark mb-1.5">
Sign In to Your Account
</h2>
<p>Enter your detail below</p>
</div>
<div>
<form onSubmit={handleSubmit}>
<div className="mb-5">
<label htmlFor="email" className="block mb-2.5">
Email
</label>
<input
type="email"
name="email"
id="email"
placeholder="Enter your email"
value={formData.email}
onChange={handleChange}
className="rounded-lg border border-gray-3 bg-gray-1 placeholder:text-dark-5 w-full py-3 px-5 outline-none duration-200 focus:border-transparent focus:shadow-input focus:ring-2 focus:ring-blue/20"
/>
</div>
<div className="mb-5">
<label htmlFor="password" className="block mb-2.5">
Password
</label>
<input
type="password"
name="password"
id="password"
placeholder="Enter your password"
value={formData.password}
onChange={handleChange}
autoComplete="on"
className="rounded-lg border border-gray-3 bg-gray-1 placeholder:text-dark-5 w-full py-3 px-5 outline-none duration-200 focus:border-transparent focus:shadow-input focus:ring-2 focus:ring-blue/20"
/>
</div>
<button
type="submit"
className="w-full flex justify-center font-medium text-white bg-dark py-3 px-6 rounded-lg ease-out duration-200 hover:bg-blue mt-7.5"
>
Sign in to account
</button>
<a
href="#"
className="block text-center text-dark-4 mt-4.5 ease-out duration-200 hover:text-dark"
>
Forget your password?
</a>
<span className="relative z-1 block font-medium text-center mt-4.5">
<span className="block absolute -z-1 left-0 top-1/2 h-px w-full bg-gray-3"></span>
<span className="inline-block px-3 bg-white">Or</span>
</span>
<p className="text-center mt-6">
Don't have an account?
<Link
href="/signup"
className="text-dark ease-out duration-200 hover:text-blue pl-2"
>
Sign Up Now!
</Link>
</p>
</form>
</div>
</div>
</div>
</section>
</>
);
};
export default Signin;




http://localhost:3000 , http://localhost:3000
그래서 cors정책이 그런경우에는 허용하지않아서 오류발생시키네요
프론트 하나 끄고 다시보내니까
요청은오는데 다른메시지가와서
그냥 토큰 헤더로 보내는거는 포기하고
바디로 받아서 사용하기로함