diff --git a/src/App.css b/src/App.css
index 1f9bad4..83284eb 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,6 +1,6 @@
-:root {
- font-family: "SUIT Variable", sans-serif;
- text-align: center;
- width: 100vw;
- height: calc(var(--vh, 1vh) * 100);
-}
+:root {
+ font-family: "SUIT Variable", sans-serif;
+ text-align: center;
+ width: 100vw;
+ height: calc(var(--vh, 1vh) * 100);
+}
diff --git a/src/App.jsx b/src/App.jsx
index 8fb6e4a..5f659c4 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,70 +1,70 @@
-import "./App.css";
-import { useEffect } from "react";
-import { Routes, Route, BrowserRouter, useParams } from "react-router-dom";
-import Login from "./pages/Login/Login";
-import Join from "./pages/Join/Join";
-import Main from "./pages/Main/Main";
-import Frame from "./pages/Frame/Frame";
-import MakeFrame from "./pages/Frame/MakeFrame";
-import Photobook from "./pages/Photobook/Photobook";
-import PhotobookUuid from "./pages/Photobook/PhotobookUuid";
-import {
- BrowserView,
- MobileView,
-} from "react-device-detect";
-import FrameList from "./component/AllFrameCpn/FrameList";
-import PhotoSelect from "./pages/Photobook/PhotoSelect";
-// import { useAtom } from "jotai";
-// import axios from "axios";
-// import { accessTokenAtom, setAccessToken } from "./store/jotaiAtoms";
-
-function App() {
- function setScreenSize() {
- let vh = window.innerHeight * 0.01;
- document.documentElement.style.setProperty("--vh", `${vh}px`);
- }
- useEffect(() => {
- setScreenSize();
- });
-
- window.addEventListener("resize", setScreenSize);
-
- return (
- <>
-
-
-
-
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
- >
- );
-}
-
-export default App;
+import "./App.css";
+import { useEffect } from "react";
+import { Routes, Route, BrowserRouter, useParams } from "react-router-dom";
+import Login from "./pages/Login/Login";
+import Join from "./pages/Join/Join";
+import Main from "./pages/Main/Main";
+import Frame from "./pages/Frame/Frame";
+import MakeFrame from "./pages/Frame/MakeFrame";
+import ApplyFrame from "./pages/Frame/ApplyFrame";
+import Photobook from "./pages/Photobook/Photobook";
+import PhotobookUuid from "./pages/Photobook/PhotobookUuid";
+import { BrowserView, MobileView } from "react-device-detect";
+import FrameList from "./component/AllFrameCpn/FrameList";
+import PhotoSelect from "./pages/Photobook/PhotoSelect";
+// import { useAtom } from "jotai";
+// import axios from "axios";
+// import { accessTokenAtom, setAccessToken } from "./store/jotaiAtoms";
+
+function App() {
+ function setScreenSize() {
+ let vh = window.innerHeight * 0.01;
+ document.documentElement.style.setProperty("--vh", `${vh}px`);
+ }
+ useEffect(() => {
+ setScreenSize();
+ });
+
+ window.addEventListener("resize", setScreenSize);
+
+ return (
+ <>
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+ >
+ );
+}
+
+export default App;
diff --git a/src/apis/getFrame.js b/src/apis/getFrame.js
index d7f5b52..42fbdb0 100644
--- a/src/apis/getFrame.js
+++ b/src/apis/getFrame.js
@@ -1,13 +1,13 @@
-// import axios from "axios";
-
-// export const getAllFrames = async () => {
-// try {
-// const res = await axios.get(
-// "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/find"
-// );
-// return res.data; // API로부터 받은 데이터 반환
-// } catch (err) {
-// console.err("Error fetching frames:", err);
-// throw err;
-// }
-// };
+// import axios from "axios";
+
+// export const getAllFrames = async () => {
+// try {
+// const res = await axios.get(
+// "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/find"
+// );
+// return res.data; // API로부터 받은 데이터 반환
+// } catch (err) {
+// console.err("Error fetching frames:", err);
+// throw err;
+// }
+// };
diff --git a/src/apis/onLogin.js b/src/apis/onLogin.js
index 310355b..004acee 100644
--- a/src/apis/onLogin.js
+++ b/src/apis/onLogin.js
@@ -1,6 +1,6 @@
-// import axios from 'axios';
-// import { accessTokenAtom } from '../store/jotaiAtoms';
-// import { useAtomValue, useSetAtom } from 'jotai';
-
-export default function onLogin() {
+// import axios from 'axios';
+// import { accessTokenAtom } from '../store/jotaiAtoms';
+// import { useAtomValue, useSetAtom } from 'jotai';
+
+export default function onLogin() {
}
\ No newline at end of file
diff --git a/src/apis/onSignUp.js b/src/apis/onSignUp.js
index 02bcff9..dc462c2 100644
--- a/src/apis/onSignUp.js
+++ b/src/apis/onSignUp.js
@@ -1,19 +1,19 @@
-import axios from "axios";
-// import sha256 from "crypto-js/sha256";
-
-export const onSignUp = (userInfo, router) => {
- // let newUserInfo = {
- // ...userInfo,
- // // 비밀번호 암호화 로직
- // password: sha256(userInfo.password).toString(),
- // };
- axios
- .post("http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/user/join", userInfo)
- .then((res) => {
- console.log(res.data);
- router("/login");
- })
- .catch((err) => {
- console.log(err);
- });
-};
+import axios from "axios";
+// import sha256 from "crypto-js/sha256";
+
+export const onSignUp = (userInfo, router) => {
+ // let newUserInfo = {
+ // ...userInfo,
+ // // 비밀번호 암호화 로직
+ // password: sha256(userInfo.password).toString(),
+ // };
+ axios
+ .post("http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/user/join", userInfo)
+ .then((res) => {
+ console.log(res.data);
+ router("/login");
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+};
diff --git a/src/component/AllFrameCpn/FrameList.jsx b/src/component/AllFrameCpn/FrameList.jsx
index 25319bc..7f289c5 100644
--- a/src/component/AllFrameCpn/FrameList.jsx
+++ b/src/component/AllFrameCpn/FrameList.jsx
@@ -1,63 +1,72 @@
-import { useState, useEffect } from "react";
-import styles from "./FrameList.module.css";
-import useAxios from "../../apis/axiosWithToken";
-import { Link } from "react-router-dom";
-// import { getAllFrames } from "../../apis/getFrame";
-import { useAtom } from "jotai";
-import { accessTokenAtom } from "../../store/jotaiAtoms";
-
-const FrameList = () => {
- const [frames, setFrames] = useState([]);
- const [accessToken, ] = useAtom(accessTokenAtom);
- const axios = useAxios();
-
- useEffect(() => {
- // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
- if (accessToken) {
- console.log("Framelist ACT:", accessToken); // accessToken jotai에서 잘 불러오는지 확인
- // axios 요청 설정
- // const config = {
- // headers: {
- // Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
- // },
- // };
- // API 요청 보내기
- axios.get('/frame/list') // 인스턴스로 axios 요청 보내기
- .then((response) => setFrames(response.data))
- .catch((error) => console.error(error));
- }
- }, [accessToken]); // useEffect가 실행되는 조건을 accessToken이 변경될 때로 설정
-
- return (
-
-
-
새 프레임 만들기
-
-
- {frames.length === null ? ( // frames 배열이 비어 있는 경우
-
-
새 프레임을 추가해주세요
-
- ) : (
- // frames 배열에 프레임이 있는 경우
-
- {frames.map((frame, index) => (
-
-
-
-
-
- ))}
-
- )}
-
-
- );
-};
-
-export default FrameList;
+import { useState, useEffect } from "react";
+import styles from "./FrameList.module.css";
+import useAxios from "../../apis/axiosWithToken";
+import { Link } from "react-router-dom";
+// import { getAllFrames } from "../../apis/getFrame";
+import { useAtom } from "jotai";
+import { accessTokenAtom } from "../../store/jotaiAtoms";
+
+const FrameList = () => {
+ const [frames, setFrames] = useState([]);
+ const [accessToken] = useAtom(accessTokenAtom);
+ const axios = useAxios();
+
+ useEffect(() => {
+ // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
+ if (accessToken) {
+ console.log("Framelist ACT:", accessToken); // accessToken jotai에서 잘 불러오는지 확인
+ const config = {
+ headers: {
+ Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
+ },
+ };
+ axios
+ .get(
+ "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/list",
+ config
+ )
+ .then((res) => {
+ console.log(res.data);
+ // 성공적으로 데이터를 받아온 경우
+ setFrames(res.data); // 받아온 데이터로 frames 상태 업데이트
+ })
+ .catch((err) => {
+ // 오류 처리
+ console.error("API 요청 중 오류 발생:", err);
+ });
+ }
+ }, [accessToken]); // useEffect가 실행되는 조건을 accessToken이 변경될 때로 설정
+
+ return (
+
+
+
새 프레임 만들기
+
+
+ {frames.length === 0 ? ( // frames 배열이 비어 있는 경우
+
+
새 프레임을 추가해주세요
+
+ ) : (
+ // frames 배열에 프레임이 있는 경우
+
+ {frames.map((frame, index) => (
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+export default FrameList;
diff --git a/src/component/AllFrameCpn/FrameList.module.css b/src/component/AllFrameCpn/FrameList.module.css
index a4917c2..6fb239b 100644
--- a/src/component/AllFrameCpn/FrameList.module.css
+++ b/src/component/AllFrameCpn/FrameList.module.css
@@ -1,69 +1,70 @@
-ul {
- list-style-type: none;
- margin: 0px;
- padding: 0px;
-}
-
-.FrameList {
- width: 390px;
- height: 760px;
- background-color: #ffffff;
- display: flex;
- flex-direction: column;
-}
-
-.FrameList button {
- border: 1px solid var(--, #009eff);
-}
-
-.newFrame {
- width: 340px;
- height: 50px;
- margin-top: 20px;
- color: #009eff;
- background-color: #ffffff;
- border-radius: 10px;
- border-color: #009eff;
-}
-
-.FrameScroll {
- height: 619px;
-}
-
-.InfoMsg {
- height: 100%;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
-}
-
-.FrameUl {
- height: 100%;
- overflow-y: scroll;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- flex-wrap: wrap; /* 너비를 넘어가면 다음 줄로 넘어감 */
-}
-
-.FrameUl::-webkit-scrollbar {
- display: none;
-}
-
-.FrameItem {
- width: calc(40% - 10px);
- gap: 10px;
- margin: 20px 10px;
-}
-
-.FrameItem button {
- background: #ffffff;
- border: none;
-}
-
-.FrameImage {
- width: 100%;
- height: auto;
-}
+ul {
+ list-style-type: none;
+ margin: 0px;
+ padding: 0px;
+}
+
+.FrameList {
+ width: 390px;
+ height: 760px;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+}
+
+.FrameList button {
+ border: 1px solid var(--, #009eff);
+}
+
+.newFrame {
+ width: 340px;
+ height: 50px;
+ margin-top: 20px;
+ color: #009eff;
+ background-color: #ffffff;
+ border-radius: 10px;
+ border-color: #009eff;
+}
+
+.FrameScroll {
+ height: 619px;
+}
+
+.InfoMsg {
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.FrameUl {
+ height: 100%;
+ overflow-y: scroll;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap; /* 너비를 넘어가면 다음 줄로 넘어감 */
+}
+
+.FrameUl::-webkit-scrollbar {
+ display: none;
+}
+
+.FrameItem {
+ width: calc(40% - 10px);
+ gap: 10px;
+ margin: 20px 10px 0px 10px;
+}
+
+.FrameItem button {
+ background-color: #f7f7f7;
+ border: none;
+ height: auto;
+}
+
+.FrameImage {
+ width: 60%;
+ height: auto;
+}
diff --git a/src/component/MakeFrameCpn/AddPhoto.jsx b/src/component/MakeFrameCpn/AddPhoto.jsx
index 12cf300..7361f8c 100644
--- a/src/component/MakeFrameCpn/AddPhoto.jsx
+++ b/src/component/MakeFrameCpn/AddPhoto.jsx
@@ -1,43 +1,43 @@
-import React from "react";
-import addbutton from "../../img/addbutton.png";
-import styles from "./AddPhoto.module.css";
-
-const AddPhoto = ({ handleUploadedImage }) => {
- const fileInput = React.useRef(null);
-
- const handleButtonClick = () => {
- fileInput.current.click();
- };
-
- const handleChange = (e) => {
- const selectedFile = e.target.files[0];
-
- // FileReader를 사용하여 선택된 파일을 읽어 옴
- const reader = new FileReader();
- reader.onloadend = () => {
- // 이미지 데이터를 MakeFrame 컴포넌트로 전달
- handleUploadedImage(reader.result);
- };
- if (selectedFile) {
- reader.readAsDataURL(selectedFile);
- }
- };
-
- return (
-
-
-
-
-
-
-
-
- );
-};
-
-export default AddPhoto;
+import React from "react";
+import addbutton from "../../img/addbutton.png";
+import styles from "./AddPhoto.module.css";
+
+const AddPhoto = ({ handleUploadedImage }) => {
+ const fileInput = React.useRef(null);
+
+ const handleButtonClick = () => {
+ fileInput.current.click();
+ };
+
+ const handleChange = (e) => {
+ const selectedFile = e.target.files[0];
+
+ // FileReader를 사용하여 선택된 파일을 읽어 옴
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ // 이미지 데이터를 MakeFrame 컴포넌트로 전달
+ handleUploadedImage(reader.result);
+ };
+ if (selectedFile) {
+ reader.readAsDataURL(selectedFile);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AddPhoto;
diff --git a/src/component/MakeFrameCpn/AddPhoto.module.css b/src/component/MakeFrameCpn/AddPhoto.module.css
index 3fb4bbe..859327d 100644
--- a/src/component/MakeFrameCpn/AddPhoto.module.css
+++ b/src/component/MakeFrameCpn/AddPhoto.module.css
@@ -1,41 +1,41 @@
-.Bottom {
- width: 390px;
- height: 204px;
-}
-
-.ListView {
- display: inline-flex;
- height: 200px;
- padding: 15px 0px 15px 18px;
- flex-direction: row;
- gap: 18px;
-}
-
-.ListView button {
- background: #ffffff;
- outline: none;
- border: none;
- cursor: pointer;
- border: none;
-}
-
-.ListView button:visited {
- border: none;
- outline: none;
-}
-
-.ListView button:hover {
- border: none;
- outline: none;
-}
-
-.ListView button:active {
- border: none;
- outline: none;
-}
-
-.addbutton {
- display: flex;
- justify-content: center;
- align-items: center;
-}
+.Bottom {
+ width: 390px;
+ height: 204px;
+}
+
+.ListView {
+ display: inline-flex;
+ height: 200px;
+ padding: 15px 0px 15px 18px;
+ flex-direction: row;
+ gap: 18px;
+}
+
+.ListView button {
+ background: #ffffff;
+ outline: none;
+ border: none;
+ cursor: pointer;
+ border: none;
+}
+
+.ListView button:visited {
+ border: none;
+ outline: none;
+}
+
+.ListView button:hover {
+ border: none;
+ outline: none;
+}
+
+.ListView button:active {
+ border: none;
+ outline: none;
+}
+
+.addbutton {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/component/MakeFrameCpn/Background.jsx b/src/component/MakeFrameCpn/Background.jsx
index c8be919..48a7da8 100644
--- a/src/component/MakeFrameCpn/Background.jsx
+++ b/src/component/MakeFrameCpn/Background.jsx
@@ -1,46 +1,46 @@
-import color from "../../img/color.png";
-import colorpalette from "../../img/colorpalette.png";
-import frame_FFC700 from "../../assets/frame-FFC700.png";
-import frame_FFFDBA from "../../assets/frame-FFFDBA.png";
-import frame_FFDE7D from "../../assets/frame-FFDE7D.png";
-import frame_F69401 from "../../assets/frame-F69401.png";
-import frame_8BD3FF from "../../assets/frame-8BD3FF.png";
-import frame_0066FF from "../../assets/frame-0066FF.png";
-import frame_009EFF from "../../assets/frame-009EFF.png";
-import frame_F1FFAD from "../../assets/frame-F1FFAD.png";
-import styles from "./Background.module.css";
-
-const Background = ({ changeFrameImage }) => {
- const handleFrameButtonClick = (frameImage) => {
- changeFrameImage(frameImage); // MakeFrame 컴포넌트에서 프레임 이미지 업데이트
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- handleFrameButtonClick(frame_FFC700)}>
- handleFrameButtonClick(frame_FFFDBA)}>
- handleFrameButtonClick(frame_FFDE7D)}>
- handleFrameButtonClick(frame_F69401)}>
-
-
- handleFrameButtonClick(frame_8BD3FF)}>
- handleFrameButtonClick(frame_0066FF)}>
- handleFrameButtonClick(frame_009EFF)}>
- handleFrameButtonClick(frame_F1FFAD)}>
-
-
-
- );
-};
-
-export default Background;
+import color from "../../img/color.png";
+import colorpalette from "../../img/colorpalette.png";
+import frame_FFC700 from "../../assets/frame-FFC700.png";
+import frame_FFFDBA from "../../assets/frame-FFFDBA.png";
+import frame_FFDE7D from "../../assets/frame-FFDE7D.png";
+import frame_F69401 from "../../assets/frame-F69401.png";
+import frame_8BD3FF from "../../assets/frame-8BD3FF.png";
+import frame_0066FF from "../../assets/frame-0066FF.png";
+import frame_009EFF from "../../assets/frame-009EFF.png";
+import frame_F1FFAD from "../../assets/frame-F1FFAD.png";
+import styles from "./Background.module.css";
+
+const Background = ({ changeFrameImage }) => {
+ const handleFrameButtonClick = (frameImage) => {
+ changeFrameImage(frameImage); // MakeFrame 컴포넌트에서 프레임 이미지 업데이트
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ handleFrameButtonClick(frame_FFC700)}>
+ handleFrameButtonClick(frame_FFFDBA)}>
+ handleFrameButtonClick(frame_FFDE7D)}>
+ handleFrameButtonClick(frame_F69401)}>
+
+
+ handleFrameButtonClick(frame_8BD3FF)}>
+ handleFrameButtonClick(frame_0066FF)}>
+ handleFrameButtonClick(frame_009EFF)}>
+ handleFrameButtonClick(frame_F1FFAD)}>
+
+
+
+ );
+};
+
+export default Background;
diff --git a/src/component/MakeFrameCpn/Background.module.css b/src/component/MakeFrameCpn/Background.module.css
index 23359dd..0bfe117 100644
--- a/src/component/MakeFrameCpn/Background.module.css
+++ b/src/component/MakeFrameCpn/Background.module.css
@@ -1,95 +1,95 @@
-.Bottom {
- width: 390px;
- height: 204px;
-}
-
-.ListTop {
- height: 40px;
- padding: 10px;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- font-weight: 400;
-}
-
-.ListTop button {
- background-color: #ffffff;
- border: none;
-}
-
-.ListView {
- width: 100%;
- height: 120px;
- padding: 5px 0px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- gap: 20px;
-}
-
-.FirstLine {
- display: flex;
- flex-direction: row;
- justify-content: space-evenly;
-}
-
-.FirstLine button {
- width: 60px;
- height: 50px;
- border-radius: 10px;
-}
-
-.FirstLine button:nth-child(1) {
- background: #ffc700;
-}
-
-.FirstLine button:nth-child(2) {
- background: #fffdba;
-}
-
-.FirstLine button:nth-child(3) {
- background: #ffde7d;
-}
-
-.FirstLine button:nth-child(4) {
- background: #f69401;
-}
-
-.SecondLine {
- display: flex;
- flex-direction: row;
- justify-content: space-evenly;
-}
-
-.SecondLine button {
- width: 60px;
- height: 50px;
- border-radius: 10px;
-}
-
-.SecondLine button:nth-child(1) {
- background: #8bd3ff;
-}
-
-.SecondLine button:nth-child(2) {
- background: #0066ff;
-}
-
-.SecondLine button:nth-child(3) {
- background: #009eff;
-}
-
-.SecondLine button:nth-child(4) {
- background: #f1ffad;
-}
-
-button {
- outline: none;
-}
-
-button:hover {
- outline: none;
- color: #009eff;
- font-weight: 600;
-}
+.Bottom {
+ width: 390px;
+ height: 204px;
+}
+
+.ListTop {
+ height: 40px;
+ padding: 10px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ font-weight: 400;
+}
+
+.ListTop button {
+ background-color: #ffffff;
+ border: none;
+}
+
+.ListView {
+ width: 100%;
+ height: 120px;
+ padding: 5px 0px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 20px;
+}
+
+.FirstLine {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+}
+
+.FirstLine button {
+ width: 60px;
+ height: 50px;
+ border-radius: 10px;
+}
+
+.FirstLine button:nth-child(1) {
+ background: #ffc700;
+}
+
+.FirstLine button:nth-child(2) {
+ background: #fffdba;
+}
+
+.FirstLine button:nth-child(3) {
+ background: #ffde7d;
+}
+
+.FirstLine button:nth-child(4) {
+ background: #f69401;
+}
+
+.SecondLine {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+}
+
+.SecondLine button {
+ width: 60px;
+ height: 50px;
+ border-radius: 10px;
+}
+
+.SecondLine button:nth-child(1) {
+ background: #8bd3ff;
+}
+
+.SecondLine button:nth-child(2) {
+ background: #0066ff;
+}
+
+.SecondLine button:nth-child(3) {
+ background: #009eff;
+}
+
+.SecondLine button:nth-child(4) {
+ background: #f1ffad;
+}
+
+button {
+ outline: none;
+}
+
+button:hover {
+ outline: none;
+ color: #009eff;
+ font-weight: 600;
+}
diff --git a/src/component/MakeFrameCpn/Brush.jsx b/src/component/MakeFrameCpn/Brush.jsx
index 68e3b2a..2d3ea88 100644
--- a/src/component/MakeFrameCpn/Brush.jsx
+++ b/src/component/MakeFrameCpn/Brush.jsx
@@ -1,80 +1,63 @@
-import React, { useState } from "react";
-import styles from "./Brush.module.css";
-import color from "../../img/color.png";
-
-const Brush = () => {
- // 초기 색상은 검정색으로 설정
- const [selectedColor, setSelectedColor] = useState("#000000");
-
- const handleColorClick = (color) => {
- setSelectedColor(color);
- };
-
- return (
-
-
-
-
-
-
handleColorClick("#FFFFFF")}
- >
-
handleColorClick("#000000")}
- >
-
handleColorClick("#009EFF")}
- >
-
handleColorClick("#53DF50")}
- >
-
handleColorClick("#FFCB33")}
- >
-
handleColorClick("#FF9634")}
- >
-
handleColorClick("#FF6060")}
- >
-
handleColorClick("#FF93E1")}
- >
-
-
- );
-};
-
-export default Brush;
+import React, { useState, useEffect } from "react";
+import styles from "./Brush.module.css";
+import color from "../../img/color.png";
+
+const Brush = ({ ctx, setCtx }) => {
+ const [lineColor, setLineColor] = useState("#000000");
+ const [lineWidth, setLineWidth] = useState(5);
+ const [lineOpacity, setLineOpacity] = useState(0.1);
+
+ useEffect(() => {
+ // 캔버스 context의 설정을 변경합니다.
+ if (ctx) {
+ ctx.strokeStyle = lineColor;
+ ctx.lineWidth = lineWidth;
+ ctx.globalAlpha = lineOpacity;
+ }
+ }, [ctx, lineColor, lineWidth, lineOpacity]);
+
+ return (
+
+
+ Brush Color
+ {
+ setLineColor(e.target.value);
+ }}
+ />
+
+
+
+ );
+};
+
+export default Brush;
diff --git a/src/component/MakeFrameCpn/Brush.module.css b/src/component/MakeFrameCpn/Brush.module.css
index 02f78a1..7ac5e86 100644
--- a/src/component/MakeFrameCpn/Brush.module.css
+++ b/src/component/MakeFrameCpn/Brush.module.css
@@ -1,22 +1,68 @@
-.Bottom {
- width: 390px;
- height: 204px;
-}
-
-.Bottom button {
- width: 25px;
- height: 25px;
- border-radius: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.ColorContainer {
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- padding-top: 20px;
- gap: 10px;
-}
+.Bottom {
+ width: 390px;
+ height: 204px;
+ display: flex;
+ flex-direction: column;
+}
+
+.Bottom button {
+ width: 25px;
+ height: 25px;
+
+ border-radius: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.ColorContainer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ padding-top: 20px;
+ gap: 10px;
+}
+
+.ColorContainer input {
+ background-color: white;
+}
+
+.BrushContainer {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding-top: 10px;
+ gap: 10px;
+}
+
+.BrushContainer input {
+ width: 100px;
+}
+
+.brushwidth {
+ background: linear-gradient(
+ to right,
+ #ffe283 0%,
+ #ffe283 50%,
+ #ececec 50%,
+ #ececec 100%
+ );
+ border-radius: 8px;
+ outline: none;
+ accent-color: #ffca1d;
+}
+
+.brushopacity {
+ background: linear-gradient(
+ to right,
+ #ffe283 0%,
+ #ffe283 50%,
+ #ececec 50%,
+ #ececec 100%
+ );
+ border-radius: 8px;
+ outline: none;
+ accent-color: #ffca1d;
+}
diff --git a/src/component/MakeFrameCpn/Sticker.jsx b/src/component/MakeFrameCpn/Sticker.jsx
index 54ba356..2885b06 100644
--- a/src/component/MakeFrameCpn/Sticker.jsx
+++ b/src/component/MakeFrameCpn/Sticker.jsx
@@ -1,95 +1,95 @@
-import React, { useState, useEffect } from "react";
-import { useDrag } from "react-use-gesture";
-import styles from "./Sticker.module.css";
-import { useAtom } from "jotai";
-import { accessTokenAtom } from "../../store/jotaiAtoms";
-import axios from "axios";
-
-const Sticker = ({ handleStickerSelect }) => {
- const [stickers, setStickers] = useState([]);
- const [stickerPos, setstickerPos] = useState({ x: 0, y: 0 });
- const [selectedTheme, setSelectedTheme] = useState(1);
- const [selectedSticker, setSelectedSticker] = useState(null);
- const [stickerSize, setStickerSize] = useState(100); // 초기 스티커 크기
- const [accessToken] = useAtom(accessTokenAtom);
-
- const themes = [1, 2, 3, 4, 5];
-
- useEffect(() => {
- // API 요청 시 accessToken을 헤더에 추가하여 요청
- const config = {
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- };
- fetchStickers(selectedTheme, config); // fetchStickers 함수에 추가된 config 객체 전달
- }, [selectedTheme, accessToken]);
-
- // 서버로부터 스티커 데이터를 가져오는 함수
- const fetchStickers = async (theme, config) => {
- try {
- const res = await axios.get(
- `http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/stickerList/${theme}`,
- config
- );
- const imageStickers = res.data.filter((url, index) => index !== 0);
- console.log("이미지 URL 리스트:", imageStickers);
- setStickers(imageStickers); // 서버로부터 받아온 스티커 데이터 설정
- } catch (err) {
- console.error("Error fetching stickers:", err);
- }
- };
-
- const handleThemeClick = (themeNumber) => {
- setSelectedTheme(themeNumber); // 클릭된 테마 버튼의 테마 번호를 설정
- };
-
- const handleStickerClick = (stickerInfo) => {
- // 선택한 스티커 정보를 MakeFrame 컴포넌트로 전달
- handleStickerSelect(stickerInfo);
- setSelectedSticker(stickerInfo);
- };
-
- const handleSizeChange = (event) => {
- setStickerSize(event.target.value); // 스티커 크기 업데이트
- };
-
- return (
-
-
- {themes.map((theme, index) => (
- handleThemeClick(theme)}>
- 테마 {theme}
-
- ))}
-
-
- {stickers.map((sticker, index) => (
-
handleStickerClick(sticker)}
- />
- ))}
-
- {selectedSticker && (
-
-
- 스티커 크기 조절
-
- )}
-
- );
-};
-
-export default Sticker;
+import React, { useState, useEffect } from "react";
+import { useDrag } from "react-use-gesture";
+import styles from "./Sticker.module.css";
+import { useAtom } from "jotai";
+import { accessTokenAtom } from "../../store/jotaiAtoms";
+import axios from "axios";
+
+const Sticker = ({ handleStickerSelect }) => {
+ const [stickers, setStickers] = useState([]);
+ const [stickerPos, setstickerPos] = useState({ x: 0, y: 0 });
+ const [selectedTheme, setSelectedTheme] = useState(1);
+ const [selectedSticker, setSelectedSticker] = useState(null);
+ const [stickerSize, setStickerSize] = useState(100); // 초기 스티커 크기
+ const [accessToken] = useAtom(accessTokenAtom);
+
+ const themes = [1, 2, 3, 4, 5];
+
+ useEffect(() => {
+ // API 요청 시 accessToken을 헤더에 추가하여 요청
+ const config = {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ };
+ fetchStickers(selectedTheme, config); // fetchStickers 함수에 추가된 config 객체 전달
+ }, [selectedTheme, accessToken]);
+
+ // 서버로부터 스티커 데이터를 가져오는 함수
+ const fetchStickers = async (theme, config) => {
+ try {
+ const res = await axios.get(
+ `http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/stickerList/${theme}`,
+ config
+ );
+ const imageStickers = res.data.filter((url, index) => index !== 0);
+ console.log("이미지 URL 리스트:", imageStickers);
+ setStickers(imageStickers); // 서버로부터 받아온 스티커 데이터 설정
+ } catch (err) {
+ console.error("Error fetching stickers:", err);
+ }
+ };
+
+ const handleThemeClick = (themeNumber) => {
+ setSelectedTheme(themeNumber); // 클릭된 테마 버튼의 테마 번호를 설정
+ };
+
+ const handleStickerClick = (stickerInfo) => {
+ setSelectedSticker(stickerInfo);
+ // 선택한 스티커 정보를 MakeFrame 컴포넌트로 전달
+ handleStickerSelect(stickerInfo);
+ };
+
+ function handleSizeChange(event) {
+ setStickerSize(event.target.value); // 스티커 크기 업데이트
+ }
+
+ return (
+
+
+ {themes.map((theme, index) => (
+ handleThemeClick(theme)}>
+ 테마 {theme}
+
+ ))}
+
+
+ {stickers.map((sticker, index) => (
+
handleStickerClick(sticker)}
+ />
+ ))}
+
+ {selectedSticker && (
+
+
+ 스티커 크기 조절
+
+ )}
+
+ );
+};
+
+export default Sticker;
diff --git a/src/component/MakeFrameCpn/Sticker.module.css b/src/component/MakeFrameCpn/Sticker.module.css
index b0067e2..154b989 100644
--- a/src/component/MakeFrameCpn/Sticker.module.css
+++ b/src/component/MakeFrameCpn/Sticker.module.css
@@ -1,55 +1,56 @@
-.Bottom {
- width: 390px;
- height: 204px;
-}
-
-.ListTop {
- height: 40px;
- padding: 14px 14px 14px 18px;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- font-size: 10px;
- font-weight: 400;
-}
-
-.ListTop button {
- background-color: #ffffff;
- border: none;
-}
-
-.ListView {
- display: flex;
- overflow-x: scroll;
- -webkit-overflow-scrolling: touch;
- height: 80px;
- padding: 15px 0px 15px 18px;
- flex-direction: row;
- gap: 18px;
-}
-
-.ListView::-webkit-scrollbar {
- width: 12px; /* 스크롤 바의 너비 */
- height: 12px; /* 스크롤 바의 높이 */
-}
-
-.ListView::-webkit-scrollbar-thumb {
- background-color: #888; /* 스크롤 바 색상 */
- border-radius: 6px; /* 스크롤 바 모서리 둥글기 */
-}
-
-.ListView::-webkit-scrollbar-track {
- background-color: #f5f5f5; /* 스크롤 바의 배경색 */
- border-radius: 6px; /* 스크롤 바 배경 모서리 둥글기 */
-}
-
-button {
- outline: none;
-}
-
-button:hover {
- outline: none;
- color: #009eff;
- font-weight: 600;
-}
+.Bottom {
+ width: 390px;
+ height: 204px;
+}
+
+.ListTop {
+ height: 40px;
+ padding: 14px 14px 14px 18px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 10px;
+ font-weight: 400;
+}
+
+.ListTop button {
+ background-color: #ffffff;
+ border: none;
+}
+
+.ListView {
+ display: flex;
+ overflow-x: scroll;
+ -webkit-overflow-scrolling: touch;
+ height: 80px;
+ padding: 15px 0px 15px 18px;
+ flex-direction: row;
+ gap: 18px;
+}
+
+.ListView::-webkit-scrollbar {
+ width: 12px; /* 스크롤 바의 너비 */
+ height: 12px; /* 스크롤 바의 높이 */
+ display: none;
+}
+
+.ListView::-webkit-scrollbar-thumb {
+ background-color: #888; /* 스크롤 바 색상 */
+ border-radius: 6px; /* 스크롤 바 모서리 둥글기 */
+}
+
+.ListView::-webkit-scrollbar-track {
+ background-color: #f5f5f5; /* 스크롤 바의 배경색 */
+ border-radius: 6px; /* 스크롤 바 배경 모서리 둥글기 */
+}
+
+button {
+ outline: none;
+}
+
+button:hover {
+ outline: none;
+ color: #009eff;
+ font-weight: 600;
+}
diff --git a/src/component/MakeFrameCpn/Template.jsx b/src/component/MakeFrameCpn/Template.jsx
index 385228a..8a0cf6c 100644
--- a/src/component/MakeFrameCpn/Template.jsx
+++ b/src/component/MakeFrameCpn/Template.jsx
@@ -1,39 +1,39 @@
-import template0 from "../../assets/template-0.png";
-import template1 from "../../assets/template-1.png";
-import template2 from "../../assets/template-2.png";
-import template3 from "../../assets/template-3.png";
-import template4 from "../../assets/template-4.png";
-import styles from "./Template.module.css";
-
-const Template = ({ changeFrameImage }) => {
- const handleFrameButtonClick = (frameImage) => {
- changeFrameImage(frameImage); // MakeFrame 컴포넌트에서 프레임 이미지 업데이트
- };
-
- return (
-
-
- 테마 for.. Beta
-
-
-
handleFrameButtonClick(template0)}>
-
-
-
handleFrameButtonClick(template1)}>
-
-
-
handleFrameButtonClick(template2)}>
-
-
- {/*
-
- */}
-
handleFrameButtonClick(template4)}>
-
-
-
-
- );
-};
-
-export default Template;
+import template0 from "../../assets/template-0.png";
+import template1 from "../../assets/template-1.png";
+import template2 from "../../assets/template-2.png";
+import template3 from "../../assets/template-3.png";
+import template4 from "../../assets/template-4.png";
+import styles from "./Template.module.css";
+
+const Template = ({ changeFrameImage }) => {
+ const handleFrameButtonClick = (frameImage) => {
+ changeFrameImage(frameImage); // MakeFrame 컴포넌트에서 프레임 이미지 업데이트
+ };
+
+ return (
+
+
+ 테마 for.. Beta
+
+
+
handleFrameButtonClick(template0)}>
+
+
+
handleFrameButtonClick(template1)}>
+
+
+
handleFrameButtonClick(template2)}>
+
+
+ {/*
+
+ */}
+
handleFrameButtonClick(template4)}>
+
+
+
+
+ );
+};
+
+export default Template;
diff --git a/src/component/MakeFrameCpn/Template.module.css b/src/component/MakeFrameCpn/Template.module.css
index 93043c7..a720bf4 100644
--- a/src/component/MakeFrameCpn/Template.module.css
+++ b/src/component/MakeFrameCpn/Template.module.css
@@ -1,46 +1,46 @@
-.Bottom {
- width: 390px;
- height: 204px;
-}
-
-.ListTop {
- height: 40px;
- padding: 14px 14px 14px 18px;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- font-size: 10px;
- font-weight: 400;
-}
-
-.ListTop button {
- width: 100%;
- background-color: #ffffff;
- border: none;
- display: flex;
- justify-content: center;
-}
-
-.ListView {
- display: flex;
- height: 100px;
- padding: 0px 0px 0px 18px;
- flex-direction: row;
- gap: 6px;
-}
-
-.ListView button {
- background: #ffffff;
- border: none;
-}
-
-.ListView button img {
- width: 72px;
- height: 100px;
- display: block;
-}
-
-button {
- outline: none;
-}
+.Bottom {
+ width: 390px;
+ height: 204px;
+}
+
+.ListTop {
+ height: 40px;
+ padding: 14px 14px 14px 18px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 10px;
+ font-weight: 400;
+}
+
+.ListTop button {
+ width: 100%;
+ background-color: #ffffff;
+ border: none;
+ display: flex;
+ justify-content: center;
+}
+
+.ListView {
+ display: flex;
+ height: 100px;
+ padding: 0px 0px 0px 18px;
+ flex-direction: row;
+ gap: 6px;
+}
+
+.ListView button {
+ background: #ffffff;
+ border: none;
+}
+
+.ListView button img {
+ width: 72px;
+ height: 100px;
+ display: block;
+}
+
+button {
+ outline: none;
+}
diff --git a/src/index.css b/src/index.css
index b5e5210..34b3260 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,25 +1,25 @@
-@font-face {
- font-family: "SUIT Variable", sans-serif;
- src: url(https://cdn.jsdelivr.net/gh/sunn-us/SUIT/fonts/variable/woff2/SUIT-Variable.css);
-}
-
-:root {
- width: 100vw;
- height: calc(var(--vh, 1vh) * 100);
- font-family: "SUIT Variable", -apple-system, BlinkMacSystemFont, Pretendard,
- "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
- sans-serif;
- font-weight: 400;
- color: #1a1e27;
- margin: 0;
- overflow-x: hidden;
- overflow-y: hidden;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-button {
- font-family: -apple-system, BlinkMacSystemFont, Pretendard, "Segoe UI", Roboto,
- Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
-}
+@font-face {
+ font-family: "SUIT Variable", sans-serif;
+ src: url(https://cdn.jsdelivr.net/gh/sunn-us/SUIT/fonts/variable/woff2/SUIT-Variable.css);
+}
+
+:root {
+ width: 100vw;
+ height: calc(var(--vh, 1vh) * 100);
+ font-family: "SUIT Variable", -apple-system, BlinkMacSystemFont, Pretendard,
+ "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
+ sans-serif;
+ font-weight: 400;
+ color: #1a1e27;
+ margin: 0;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+button {
+ font-family: -apple-system, BlinkMacSystemFont, Pretendard, "Segoe UI", Roboto,
+ Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+}
diff --git a/src/main.jsx b/src/main.jsx
index c70b84f..a05b9cf 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -1,16 +1,16 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import axios from "axios";
-import App from "./App.jsx";
-import "./index.css";
-// import { Provider } from 'jotai';
-// import { accessTokenAtom } from './store/jotaiAtoms.js';
-
-ReactDOM.createRoot(document.getElementById("root")).render(
-
-
-
-)
-
-axios.defaults.baseURL = "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080";
+import React from "react";
+import ReactDOM from "react-dom/client";
+import axios from "axios";
+import App from "./App.jsx";
+import "./index.css";
+// import { Provider } from 'jotai';
+// import { accessTokenAtom } from './store/jotaiAtoms.js';
+
+ReactDOM.createRoot(document.getElementById("root")).render(
+
+
+
+)
+
+axios.defaults.baseURL = "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080";
axios.defaults.withCredentials = true;
\ No newline at end of file
diff --git a/src/pages/Frame/ApplyFrame.jsx b/src/pages/Frame/ApplyFrame.jsx
new file mode 100644
index 0000000..8127a23
--- /dev/null
+++ b/src/pages/Frame/ApplyFrame.jsx
@@ -0,0 +1,218 @@
+import axios from "axios";
+import React, { useState, useEffect } from "react";
+import styles from "../Frame/ApplyFrame.module.css";
+import edit from "../../img/edit.png";
+import trash from "../../img/trash.png";
+import photobook from "../../img/book.png";
+import frameline from "../../img/frame-line.png";
+import { Link, useLocation } from "react-router-dom";
+import { useAtom } from "jotai";
+import { accessTokenAtom } from "../../store/jotaiAtoms";
+import html2canvas from "html2canvas";
+import AddPhoto from "../../component/MakeFrameCpn/AddPhoto";
+
+const ApplyFrame = () => {
+ const location = useLocation();
+ console.log("", location);
+ const { selectedFrame } = location.state;
+ const [selectedButton, setSelectedButton] = useState("프레임 제작");
+ const [accessToken] = useAtom(accessTokenAtom);
+ const [uploadedImage, setUploadedImage] = useState(null);
+
+ useEffect(() => {
+ // uploadedImage 값이 변경될 때 로컬 스토리지에 이미지 데이터를 저장합니다.
+ if (uploadedImage) {
+ localStorage.setItem("uploadedImage", uploadedImage);
+ }
+ }, [uploadedImage]);
+
+ useEffect(() => {
+ // uploadedImage 값이 변경될 때 로컬 스토리지에 이미지 데이터를 저장합니다.
+ if (uploadedImage) {
+ localStorage.setItem("uploadedImage", uploadedImage);
+ }
+ }, [uploadedImage]);
+
+ const handleEditFrame = (frameUrl) => {
+ console.log("Edit Frame URL:", frameUrl);
+
+ // 수정할 프레임의 URL과 accessToken을 이용하여 PUT 요청을 생성
+ const editFrameRequest = {
+ url: `http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame`,
+ method: "PUT",
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ data: {
+ url: frameUrl, // 수정할 프레임의 URL
+ // 여기에 수정할 데이터 추가 (예: 수정된 프레임의 새로운 URL 등)
+ },
+ };
+
+ // Axios를 사용하여 PUT 요청 보내기
+ axios(editFrameRequest)
+ .then((response) => {
+ // 수정 요청이 성공했을 때의 동작
+ console.log("프레임 수정 요청 성공:", response.data);
+ // 필요에 따라 상태(state)를 업데이트하거나 다른 작업 수행
+ })
+ .catch((error) => {
+ // 수정 요청이 실패했을 때의 동작
+ console.error("프레임 수정 요청 실패:", error);
+ // 오류 처리 혹은 다른 처리 로직 구현
+ });
+ };
+
+ const handleDeleteFrame = (frameUrl) => {
+ console.log("Delete Frame URL:", frameUrl);
+
+ // 삭제할 프레임의 URL과 accessToken을 이용하여 DELETE 요청을 생성
+ const deleteFrameRequest = {
+ url: `http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame`,
+ method: "DELETE",
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ data: {
+ url: frameUrl,
+ },
+ };
+
+ // Axios를 사용하여 DELETE 요청 보내기
+ axios(deleteFrameRequest)
+ .then((response) => {
+ // 삭제 요청이 성공했을 때의 동작
+ console.log("프레임 삭제 요청 성공:", response.data);
+ // 필요에 따라 상태(state)를 업데이트하거나 다른 작업 수행
+ })
+ .catch((error) => {
+ // 삭제 요청이 실패했을 때의 동작
+ console.error("프레임 삭제 요청 실패:", error);
+ // 오류 처리 혹은 다른 처리 로직 구현
+ });
+ };
+
+ const handleUploadedImage = (event) => {
+ const file = event.target.files[0];
+ const reader = new FileReader();
+
+ reader.onload = () => {
+ setUploadedImage(reader.result);
+ };
+
+ if (file) {
+ reader.readAsDataURL(file);
+ }
+ };
+
+ const handleSaveClick = () => {
+ // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
+ if (accessToken) {
+ const frameElement = document.querySelector("img");
+ frameElement.src = selectedFrame;
+
+ html2canvas(frameElement).then((canvas) => {
+ canvas.toBlob((blob) => {
+ const formData = new FormData();
+ formData.append("photo", blob, "photo.png");
+
+ console.log(formData);
+
+ // axios 요청 설정
+ const config = {
+ headers: {
+ "Content-Type": "multipart/form-data",
+ Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
+ },
+ };
+
+ axios
+ .post(
+ "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/photo",
+ formData,
+ config
+ )
+ .then((res) => {
+ console.log("프레임 적용한 사진 저장 API 응답:", res.data);
+ })
+ .catch((err) => {
+ // 오류 처리
+ console.error("API 요청 중 오류 발생:", err);
+ });
+ }, "image/png");
+ });
+ }
+ };
+
+ return (
+
+
+
+
handleEditFrame(selectedFrame)}
+ >
+
+
+
handleDeleteFrame(selectedFrame)}
+ >
+
+
+
+
+
+
+
+ {uploadedImage && (
+
+ )}
+
+
+
+
+
+ 인생네컷 불러오기
+
+
+
+ 사진 저장하기
+
+
+
+
+
+
+ 전체 프레임
+
+
+
+
+
+ 포토북
+
+
+
+
+
+
+ );
+};
+
+export default ApplyFrame;
diff --git a/src/pages/Frame/ApplyFrame.module.css b/src/pages/Frame/ApplyFrame.module.css
new file mode 100644
index 0000000..e04bc4b
--- /dev/null
+++ b/src/pages/Frame/ApplyFrame.module.css
@@ -0,0 +1,171 @@
+body {
+ margin: 0px;
+ padding: 0px;
+}
+
+.ApplyFrame {
+ width: 390px;
+ height: calc(var(--vh, 1vh) * 100);
+ background: #ffffff;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin: 0;
+ text-decoration: none;
+}
+
+.ApplyFramebox {
+ display: flex;
+ flex-direction: column;
+}
+
+.Top {
+ height: 85px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+ border-bottom: 2px solid #c9c9c9;
+}
+
+.Top button {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.TextBtn {
+ width: 95px;
+ height: 24px;
+ background-color: #f7f8f8;
+ text-align: center;
+ font-size: 12px;
+ font-weight: 400;
+ border-radius: 19px;
+ border: none;
+}
+
+.ImgBtn {
+ width: 24px;
+ height: 24px;
+ background-color: #ffffff;
+ border: none;
+}
+
+.Middle {
+ width: 390px;
+ height: 479px;
+ display: inline-block;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: #f7f8f8;
+ position: relative;
+}
+
+.MiddleTop {
+ width: 390px;
+ height: 49px;
+ background-color: #ffffff;
+}
+
+.Frame {
+ position: relative;
+ width: 300px;
+ height: 430px;
+ object-fit: contain;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.Frame img {
+ width: 100%;
+ height: 100%;
+ display: block;
+ flex-direction: column;
+ justify-content: start;
+ align-items: center;
+ margin: 0 auto;
+ position: absolute;
+}
+
+.SelectedFrame {
+ position: absolute;
+ z-index: 2;
+}
+
+.UploadedImage {
+ position: absolute;
+ z-index: 1;
+}
+
+.ButtonList {
+ width: 390px;
+ height: 210px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 20px;
+}
+
+.addphoto {
+ width: 340px;
+ height: 50px;
+ border-radius: 10px;
+ border: 1px solid #009eff;
+ text-align: center;
+ font-size: 14px;
+ font-weight: 500;
+ background-color: #ffffff;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ position: relative;
+}
+
+.savephoto {
+ width: 340px;
+ height: 50px;
+ border-radius: 10px;
+ border: 1px solid #009eff;
+ text-align: center;
+ font-size: 14px;
+ font-weight: 500;
+ background-color: #ffffff;
+}
+
+.ListBottom {
+ width: 390px;
+ height: 70px;
+ background: #f7f8f8;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+ align-items: center;
+ gap: 20px;
+}
+
+.ListBottom button {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ font-size: 10px;
+ font-weight: 400;
+ border: none;
+ background-color: #f7f8f8;
+}
+
+.ListBottom button img {
+ width: 24px;
+ height: 24px;
+}
+
+.now {
+ text-decoration: none;
+}
diff --git a/src/pages/Frame/Frame.jsx b/src/pages/Frame/Frame.jsx
index eaff064..b74409d 100644
--- a/src/pages/Frame/Frame.jsx
+++ b/src/pages/Frame/Frame.jsx
@@ -1,71 +1,80 @@
-import React, { useState } from "react";
-import { useDrag } from "react-use-gesture";
-import { useSpring, animated } from "react-spring";
-import map from "../../img/map.png";
-import frame from "../../img/frame-line.png";
-import book from "../../img/book.png";
-import person from "../../img/person.png";
-import framelist from "../../img/framelist.png";
-import styles from "./Frame.module.css";
-import Template from "../../component/MakeFrameCpn/Template";
-import FrameList from "../../component/AllFrameCpn/FrameList";
-import Photobook from "../Photobook/PhotoSelect";
-import { Link } from "react-router-dom";
-
-const Frame = () => {
- const [selectedButton, setSelectedButton] = useState("프레임 제작");
- // const [logoPos, setlogoPos] = useState({ x: 0, y: 0 });
- const logoPos = useSpring({ x: 0, y: 0 });
- const bindLogoPos = useDrag((params) => {
- // setlogoPos({
- // x: params.offset[0],
- // y: params.offset[1],
- // });
- logoPos.x.set(params.offset[0]);
- logoPos.y.set(params.offset[1]);
- });
-
- const handleButtonClick = (button) => {
- setSelectedButton(button);
- };
-
- let middleContent;
-
- switch (selectedButton) {
- case "프레임 제작":
- middleContent = ;
- break;
- case "포토북":
- middleContent = ;
- break;
- default:
- middleContent = ;
- break;
- }
-
- return (
-
-
-
전체 프레임
-
-
-
-
-
{middleContent}
-
-
-
handleButtonClick("프레임 제작")}>
-
- 프레임 제작
-
-
handleButtonClick("포토북")}>
-
- 포토북
-
-
-
-
- );
-};
-
-export default Frame;
+import React, { useState } from "react";
+import { useDrag } from "react-use-gesture";
+import { useSpring, animated } from "react-spring";
+import home from "../../img/home.png";
+import map from "../../img/map.png";
+import frame from "../../img/frame-line.png";
+import book from "../../img/book.png";
+import person from "../../img/person.png";
+import framelist from "../../img/framelist.png";
+import styles from "./Frame.module.css";
+import Template from "../../component/MakeFrameCpn/Template";
+import FrameList from "../../component/AllFrameCpn/FrameList";
+import Photobook from "../Photobook/PhotoSelect";
+import { Link } from "react-router-dom";
+
+const Frame = () => {
+ const [selectedButton, setSelectedButton] = useState("프레임 제작");
+ // const [logoPos, setlogoPos] = useState({ x: 0, y: 0 });
+ const logoPos = useSpring({ x: 0, y: 0 });
+ const bindLogoPos = useDrag((params) => {
+ // setlogoPos({
+ // x: params.offset[0],
+ // y: params.offset[1],
+ // });
+ logoPos.x.set(params.offset[0]);
+ logoPos.y.set(params.offset[1]);
+ });
+
+ const handleButtonClick = (button) => {
+ setSelectedButton(button);
+ };
+
+ let middleContent;
+
+ switch (selectedButton) {
+ case "프레임 제작":
+ middleContent = ;
+ break;
+ case "포토북":
+ middleContent = ;
+ break;
+ default:
+ middleContent = ;
+ break;
+ }
+
+ const topSpan =
+ selectedButton === "프레임 제작" ? "전체 프레임" : "전체 포토";
+
+ return (
+
+
+
+
+
+
+
+
{topSpan}
+
+
+
+
+
{middleContent}
+
+
+
handleButtonClick("프레임 제작")}>
+
+ 프레임 제작
+
+
handleButtonClick("포토북")}>
+
+ 포토북
+
+
+
+
+ );
+};
+
+export default Frame;
diff --git a/src/pages/Frame/Frame.module.css b/src/pages/Frame/Frame.module.css
index 6f0871a..3e99cd2 100644
--- a/src/pages/Frame/Frame.module.css
+++ b/src/pages/Frame/Frame.module.css
@@ -1,117 +1,117 @@
-.Frame {
- width: 390px;
- height: calc(var(--vh, 1vh) * 100);
- background: #ffffff;
- display: flex;
- flex-direction: column;
- justify-content: center;
- user-select: none;
-}
-
-.Top {
- height: 86px;
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
- align-items: center;
-}
-
-.Top span {
- flex: 1;
- text-align: center;
- font-size: 18px;
- font-weight: 600;
- padding-left: 50px;
-}
-
-.Top button {
- width: 24px;
- height: 24px;
- background-color: #ffffff;
- border: none;
- padding-right: 45px;
-}
-
-.mylistBtn {
- width: 47px;
- height: 47px;
- font-size: 10px;
- background-color: #ffffff;
- border-radius: 30px;
- margin-left: auto;
- border: none;
-}
-
-.Middle {
- width: 390px;
- height: 689px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- background-color: #ffffff;
- gap: 13px;
-}
-
-.Bottom {
- width: 100%;
- height: 70px;
-}
-
-.ListTop {
- height: 40px;
- padding: 14px 14px 14px 18px;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- font-size: 10px;
- font-weight: 400;
-}
-
-.ListTop button {
- background-color: #ffffff;
-}
-
-.ListView {
- display: inline-flex;
- height: 160px;
- padding: 15px 0px 15px 18px;
- flex-direction: row;
- gap: 18px;
-}
-
-.ListView span {
- width: 72px;
- height: 130px;
- border-radius: 10px;
- background: #d9d9d9;
-}
-
-.ListBottom {
- width: 390px;
- height: 70px;
- background: #f7f8f8;
- display: flex;
- flex-direction: row;
- justify-content: space-evenly;
- align-items: center;
- gap: 20px;
-}
-
-.ListBottom button {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- text-align: center;
- font-size: 10px;
- font-weight: 400;
- border: none;
- background-color: #f7f8f8;
-}
-
-.ListBottom button img {
- width: 24px;
- height: 24px;
-}
+.Frame {
+ width: 390px;
+ height: calc(var(--vh, 1vh) * 100);
+ background: #ffffff;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ user-select: none;
+}
+
+.Top {
+ height: 86px;
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ align-items: center;
+}
+
+.Top span {
+ flex: 1;
+ text-align: center;
+ font-size: 18px;
+ font-weight: 600;
+ padding-left: 50px;
+}
+
+.Top button {
+ width: 24px;
+ height: 24px;
+ background-color: #ffffff;
+ border: none;
+ padding-right: 45px;
+}
+
+.mylistBtn {
+ width: 47px;
+ height: 47px;
+ font-size: 10px;
+ background-color: #ffffff;
+ border-radius: 30px;
+ margin-left: auto;
+ border: none;
+}
+
+.Middle {
+ width: 390px;
+ height: 689px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: #ffffff;
+ gap: 13px;
+}
+
+.Bottom {
+ width: 100%;
+ height: 70px;
+}
+
+.ListTop {
+ height: 40px;
+ padding: 14px 14px 14px 18px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 10px;
+ font-weight: 400;
+}
+
+.ListTop button {
+ background-color: #ffffff;
+}
+
+.ListView {
+ display: inline-flex;
+ height: 160px;
+ padding: 15px 0px 15px 18px;
+ flex-direction: row;
+ gap: 18px;
+}
+
+.ListView span {
+ width: 72px;
+ height: 130px;
+ border-radius: 10px;
+ background: #d9d9d9;
+}
+
+.ListBottom {
+ width: 390px;
+ height: 70px;
+ background: #f7f8f8;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+ align-items: center;
+ gap: 20px;
+}
+
+.ListBottom button {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ font-size: 10px;
+ font-weight: 400;
+ border: none;
+ background-color: #f7f8f8;
+}
+
+.ListBottom button img {
+ width: 24px;
+ height: 24px;
+}
diff --git a/src/pages/Frame/MakeFrame.jsx b/src/pages/Frame/MakeFrame.jsx
index 1584f80..af1c45d 100644
--- a/src/pages/Frame/MakeFrame.jsx
+++ b/src/pages/Frame/MakeFrame.jsx
@@ -1,350 +1,484 @@
-import React, { useState, useEffect } from "react";
-import { Link } from "react-router-dom";
-import { useSelector } from "react-redux";
-import home from "../../img/home.png";
-import save from "../../img/save.png";
-import template from "../../img/template.png";
-import sticker from "../../img/sticker.png";
-import background from "../../img/background.png";
-import brush from "../../img/brush.png";
-import addphoto from "../../img/addphoto.png";
-import left from "../../img/left.png";
-import right from "../../img/right.png";
-import framebase from "../../assets/framebase.png";
-import styles from "./MakeFrame.module.css";
-import Template from "../../component/MakeFrameCpn/Template";
-import Sticker from "../../component/MakeFrameCpn/Sticker";
-import Background from "../../component/MakeFrameCpn/Background";
-import Brush from "../../component/MakeFrameCpn/Brush";
-import AddPhoto from "../../component/MakeFrameCpn/AddPhoto";
-import html2canvas from "html2canvas";
-import axios from "axios";
-import { useAtom } from "jotai";
-import { accessTokenAtom } from "../../store/jotaiAtoms";
-
-const BrushSizeSelector = ({ selectedBrushSize, setSelectedBrushSize }) => {
- return (
-
- setSelectedBrushSize("large")}
- >
- setSelectedBrushSize("medium")}
- >
- setSelectedBrushSize("small")}
- >
-
- );
-};
-
-const MakeFrame = () => {
- const [selectedButton, setSelectedButton] = useState("템플릿");
- const [isListHover, setIsListHover] = useState(false);
- const [selectedBrushSize, setSelectedBrushSize] = useState("medium");
- const [selectedColor, setSelectedColor] = useState("#000000");
- const [uploadedImage, setUploadedImage] = useState(null);
- const [stickerPositionsitions, setStickerPositionsitions] = useState([]);
- const [frameImage, setFrameImage] = useState(framebase);
- const [actions, setActions] = useState([]);
- const [uploadedSticker, setUploadedSticker] = useState(null);
- const [stickerSize, setStickerSize] = useState(100);
- const [selectedSticker, setSelectedSticker] = useState(null);
- const [accessToken] = useAtom(accessTokenAtom);
-
- const handleButtonClick = (button) => {
- setSelectedButton(button);
- if (button !== "브러쉬") {
- // '브러쉬' 버튼이 아닌 경우 브러쉬 크기 설정을 초기화
- setSelectedBrushSize("medium");
- }
- };
-
- /*if (button === "브러쉬") {
- setIsBrushSizeVisible(true);
- } else {
- setIsBrushSizeVisible(false);
- }*/
-
- const handleStickerSelect = (selectedSticker) => {
- // Sticker 컴포넌트로부터 받은 스티커 정보로 중앙에 스티커 렌더링
- setUploadedSticker(selectedSticker);
- setSelectedSticker(selectedSticker);
- };
-
- // Sticker에서 전달된 스티커 위치 정보를 받는 함수
- const handleStickerDrag = ({ index, x, y }) => {
- const newStickerPositionsitions = [...stickerPositionsitions];
- newStickerPositionsitions[index] = { x, y };
- setStickerPositionsitions(newStickerPositionsitions);
- };
-
- // UploadedImage와 스티커들을 합성하여 보여주는 함수
- const renderFrame = () => {
- const uploadedImageStyle = uploadedImage
- ? { position: "absolute", top: 0, left: 0 }
- : {};
-
- return (
-
-
- {uploadedImage && (
-
- )}
- {stickerPositions.map((position, index) => (
-
- ))}
-
- );
- };
-
- const changeFrameImage = (newFrameImage) => {
- setFrameImage(newFrameImage);
- };
-
- const handleSaveClick = () => {
- // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
- if (accessToken) {
- // axios 요청 설정
- const config = {
- headers: {
- Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
- },
- };
-
- const frameElement = document.querySelector(`.${styles.Frame}`);
- html2canvas(frameElement).then((canvas) => {
- const imageData = canvas.toDataURL("image/png");
-
- axios
- .post(
- "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame",
- {
- image: imageData,
- },
- config
- )
- .then((res) => {
- console.log("프레임 저장 API 응답:", res.data);
- // 성공적으로 데이터를 받아온 경우
- // 받아온 데이터로 frames 상태 업데이트
- })
- .catch((err) => {
- // 오류 처리
- console.error("API 요청 중 오류 발생:", err);
- });
- });
- }
- };
-
- useEffect(() => {
- // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
- if (accessToken) {
- // axios 요청 설정
- const config = {
- headers: {
- Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
- },
- };
-
- const frameElement = document.querySelector(`.${styles.Frame}`);
- html2canvas(frameElement).then((canvas) => {
- const imageData = canvas.toDataURL("image/png");
-
- axios
- .post(
- "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame/save",
- {
- image: imageData,
- },
- config
- )
- .then((res) => {
- console.log("프레임 저장 API 응답:", res.data);
- // 성공적으로 데이터를 받아온 경우
- // 받아온 데이터로 frames 상태 업데이트
- })
- .catch((err) => {
- // 오류 처리
- console.error("API 요청 중 오류 발생:", err);
- });
- });
- }
- }, [accessToken]);
-
- const handleUploadedImage = (imageData) => {
- setUploadedImage(imageData);
- console.log(uploadedImage);
- };
-
- let bottomContent;
-
- switch (selectedButton) {
- case "템플릿":
- bottomContent = (
-
- );
- break;
- case "스티커":
- bottomContent = ;
- break;
- case "배경":
- bottomContent = (
-
- );
- break;
- case "브러쉬":
- bottomContent = ;
- break;
- case "사진 추가":
- bottomContent = ;
- break;
- default:
- bottomContent = ;
- break;
- }
-
- const middleContent =
- selectedButton === "브러쉬" ? (
-
-
-
- ) : null;
-
- // 'left' 버튼을 누를 때 이전 동작으로 되돌리는 함수
- const handleUndo = () => {
- if (actions.length > 0) {
- const lastAction = actions[actions.length - 1];
- // 이전 동작을 실행하기 전에 해당 동작을 배열에서 제거
- setActions((prevActions) => prevActions.slice(0, -1));
-
- // TODO: 해당 동작에 따른 처리 구현
- // 예를 들어, 특정 동작에 대한 역으로 처리하는 코드 작성
- // 예: if (lastAction === '이미지 추가') { /* 이미지 삭제 로직 */ }
- }
- };
-
- // 'right' 버튼을 누를 때 다시 실행하는 함수
- const handleRedo = () => {
- // TODO: 재실행 로직 구현
- // actions 배열에 저장된 동작을 순회하며 재실행하는 로직 작성
- // 예: actions.forEach((action) => { /* 각 동작에 대한 처리 */ });
- };
-
- return (
-
-
-
-
-
-
-
-
-
가져오기
-
프레임 보기
-
-
-
-
-
-
- {middleContent}
-
-
- {uploadedImage && (
-
- )}
- {uploadedSticker && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
- {bottomContent}
-
-
handleButtonClick("템플릿")}
- >
-
- 템플릿
-
-
handleButtonClick("스티커")}>
-
- 스티커
-
-
handleButtonClick("배경")}>
-
- 배경
-
-
handleButtonClick("브러쉬")}>
-
- 브러쉬
-
-
handleButtonClick("사진 추가")}>
-
- 사진 추가
-
-
-
-
-
- );
-};
-
-export default MakeFrame;
+import React, { useState, useEffect, useRef } from "react";
+import { Link } from "react-router-dom";
+import { useSelector } from "react-redux";
+import { saveAs } from "file-saver";
+import home from "../../img/home.png";
+import save from "../../img/save.png";
+import template from "../../img/template.png";
+import sticker from "../../img/sticker.png";
+import background from "../../img/background.png";
+import brush from "../../img/brush.png";
+import addphoto from "../../img/addphoto.png";
+import left from "../../img/left.png";
+import right from "../../img/right.png";
+import framebase from "../../assets/framebase.png";
+import styles from "./MakeFrame.module.css";
+import Template from "../../component/MakeFrameCpn/Template";
+import Sticker from "../../component/MakeFrameCpn/Sticker";
+import Background from "../../component/MakeFrameCpn/Background";
+import Brush from "../../component/MakeFrameCpn/Brush";
+import AddPhoto from "../../component/MakeFrameCpn/AddPhoto";
+import html2canvas from "html2canvas";
+import axios from "axios";
+import { useAtom } from "jotai";
+import { accessTokenAtom } from "../../store/jotaiAtoms";
+
+const stickerImg = new Image();
+const INITIAL_POSITION = { x: 0, y: 0 };
+const MIN_SCALE = 0.1;
+const MAX_SCALE = 10;
+
+const MakeFrame = () => {
+ const [selectedButton, setSelectedButton] = useState("템플릿");
+ const [isListHover, setIsListHover] = useState(false);
+ const [selectedBrushSize, setSelectedBrushSize] = useState("medium");
+ const [selectedColor, setSelectedColor] = useState("#000000");
+ const [uploadedImage, setUploadedImage] = useState(null);
+ const [stickerPositions, setStickerPositions] = useState([]);
+ const [frameImage, setFrameImage] = useState(framebase);
+ const [actions, setActions] = useState([]);
+ const [stickerSize, setStickerSize] = useState(50);
+ const [selectedSticker, setSelectedSticker] = useState(null);
+ const [selectedStickers, setSelectedStickers] = useState([]);
+ const [accessToken] = useAtom(accessTokenAtom);
+ const [isDragging, setIsDragging] = useState(false);
+ const [draggingStickerIndex, setDraggingStickerIndex] = useState(null);
+ const [stickerPos, setStickerPos] = useState({ x: 100, y: 100 });
+ const [frames, setFrames] = useState([]);
+ const [bottomContentHeight, setBottomContentHeight] = useState(270);
+
+ const canvasRef = useRef(null);
+ const contextRef = useRef(null);
+
+ const [ctx, setCtx] = useState();
+ const [isDrawing, setIsDrawing] = useState(false);
+ const [lineWidth, setLineWidth] = useState(5);
+ const [lineColor, setLineColor] = useState("black");
+ const [lineOpacity, setLineOpacity] = useState(0.1);
+
+ const zoomCanvasRef = useRef(null);
+ const scaleRef = useRef(1);
+ const panningRef = useRef(false);
+ const viewPosRef = useRef(INITIAL_POSITION);
+ const startPosRef = useRef(INITIAL_POSITION);
+
+ const frameRef = useRef(null);
+
+ useEffect(() => {
+ const frameElement = frameRef.current;
+ const frameWidth = frameElement.offsetWidth;
+ const frameHeight = frameElement.offsetHeight;
+
+ // Canvas 요소에 Frame 영역과 동일한 크기를 설정
+ const canvas = canvasRef.current;
+ canvas.width = frameWidth;
+ canvas.height = frameHeight;
+
+ const context = canvas.getContext("2d");
+ context.lineCap = "round";
+ context.lineJoin = "round";
+ contextRef.current = context;
+
+ setCtx(context);
+ }, []);
+
+ // 브러쉬 로직
+ const startDrawing = ({ nativeEvent }) => {
+ if (selectedButton === "브러쉬") {
+ console.log(nativeEvent);
+ setIsDrawing(true);
+
+ const { offsetX, offsetY } = nativeEvent;
+ if (ctx) {
+ ctx.beginPath();
+ ctx.moveTo(offsetX, offsetY);
+ }
+ }
+ };
+
+ const finishDrawing = () => {
+ if (selectedButton === "브러쉬") {
+ setIsDrawing(false);
+ }
+ };
+
+ const drawing = ({ nativeEvent }) => {
+ if (selectedButton === "브러쉬" && isDrawing) {
+ const { offsetX, offsetY } = nativeEvent;
+ if (ctx) {
+ // Frame 영역 내에서만 브러쉬 그리기
+ ctx.lineTo(offsetX, offsetY);
+ ctx.stroke();
+ }
+ }
+ };
+
+ const handleButtonClick = (button) => {
+ setSelectedButton(button);
+ };
+
+ const setTransform = () => {
+ const zoomCanvas = zoomCanvasRef.current;
+ const context = zoomCanvas.getContext("2d");
+ context.setTransform(
+ scaleRef.current,
+ 0,
+ 0,
+ scaleRef.current,
+ viewPosRef.current.x,
+ viewPosRef.current.y
+ );
+ };
+
+ const draw = () => {
+ const zoomCanvas = zoomCanvasRef.current;
+ const context = zoomCanvas.getContext("2d");
+ zoomCanvas.width = zoomCanvas.width;
+ setTransform();
+ context.drawImage(stickerImg, 0, 0, zoomCanvas.width, zoomCanvas.height);
+ };
+
+ useEffect(() => {
+ stickerImg.src = selectedSticker; // MakeFrame 컴포넌트에서 사용하는 이미지 URL로 변경
+ // Load image
+ stickerImg.onload = function () {
+ draw();
+ };
+ }, []);
+
+ const handleStickerMouseDown = (index, e) => {
+ if (!zoomCanvasRef.current) return;
+
+ const zoomCanvas = zoomCanvasRef.current;
+ const rect = zoomCanvas.getBoundingClientRect();
+
+ const offsetX = e.clientX - rect.left;
+ const offsetY = e.clientY - rect.top;
+
+ startPosRef.current = {
+ x: offsetX - viewPosRef.current.x,
+ y: offsetY - viewPosRef.current.y,
+ };
+ panningRef.current = true;
+
+ setDraggingStickerIndex(index);
+ };
+
+ const handleStickerMouseMove = (e) => {
+ if (!panningRef.current || draggingStickerIndex === null) {
+ return;
+ }
+
+ if (!zoomCanvasRef.current) return;
+ const frameElement = frameRef.current;
+ const frameRect = frameElement.getBoundingClientRect();
+
+ const zoomCanvas = zoomCanvasRef.current;
+ zoomCanvas.width = frameRect.width;
+ zoomCanvas.height = frameRect.height;
+
+ const offsetX = e.clientX - frameRect.left;
+ const offsetY = e.clientY - frameRect.top;
+
+ let newX = offsetX - startPosRef.current.x;
+ let newY = offsetY - startPosRef.current.y;
+
+ // 프레임 영역 내에서만 스티커 위치 변경 가능하도록 설정
+ newX = Math.max(newX, 0 + stickerSize / 2); // X 좌표가 음수가 되지 않도록
+ newY = Math.max(newY, 0 + stickerSize / 2); // Y 좌표가 음수가 되지 않도록
+ newX = Math.min(newX, frameRect.width - stickerSize / 2); // X 좌표가 프레임 영역을 벗어나지 않도록
+ newY = Math.min(newY, frameRect.height - stickerSize / 2); // Y 좌표가 프레임 영역을 벗어나지 않도록
+
+ viewPosRef.current = { x: newX, y: newY };
+
+ // 드래그 중인 스티커의 새 위치 설정
+ const updatedStickerPositions = [...stickerPositions];
+ updatedStickerPositions[draggingStickerIndex] = {
+ x: viewPosRef.current.x,
+ y: viewPosRef.current.y,
+ };
+ setStickerPositions(updatedStickerPositions);
+ draw();
+ };
+
+ const handleStickerMouseUp = () => {
+ panningRef.current = false;
+ };
+
+ const handleWheel = (e) => {
+ const { offsetX, offsetY } = e.nativeEvent;
+ e.preventDefault();
+ const xs = (offsetX - viewPosRef.current.x) / scaleRef.current;
+ const ys = (offsetY - viewPosRef.current.y) / scaleRef.current;
+ const delta = -e.deltaY;
+ const newScale =
+ delta > 0 ? scaleRef.current * 1.2 : scaleRef.current / 1.2;
+
+ if (newScale >= MIN_SCALE && newScale <= MAX_SCALE) {
+ scaleRef.current = newScale;
+ viewPosRef.current = {
+ x: offsetX - xs * scaleRef.current,
+ y: offsetY - ys * scaleRef.current,
+ };
+ }
+ draw();
+ };
+
+ const handleStickerSelect = (selectedSticker) => {
+ setSelectedStickers((prevSelectedStickers) => [
+ ...prevSelectedStickers,
+ selectedSticker,
+ ]);
+ // Sticker 컴포넌트로부터 받은 스티커 정보로 중앙에 스티커 렌더링
+ setSelectedSticker(selectedSticker);
+
+ // Frame 영역 내부의 좌표로 스티커 위치 지정
+ const frameElement = document.querySelector(`.${styles.Frame}`);
+ const frameRect = frameElement.getBoundingClientRect();
+ const initialStickerPosition = {
+ x: frameRect.width / 2, // Frame 가로 중앙
+ y: frameRect.height / 2, // Frame 세로 중앙
+ };
+
+ // 이전 위치 정보와 함께 새로운 스티커 위치 정보를 추가하여 업데이트
+ setStickerPositions([...stickerPositions, initialStickerPosition]);
+
+ console.log("선택된 스티커 정보:", selectedSticker);
+ };
+
+ // 컴포넌트가 마운트될 때 로컬 스토리지에서 스티커 위치와 크기를 불러옵니다.
+ useEffect(() => {
+ const storedStickerPositions = JSON.parse(
+ localStorage.getItem("stickerPositions")
+ );
+ const storedStickerSize = localStorage.getItem("stickerSize");
+
+ if (storedStickerPositions) {
+ setStickerPositions(storedStickerPositions);
+ }
+
+ if (storedStickerSize) {
+ setStickerSize(Number(storedStickerSize));
+ }
+ }, []);
+
+ const changeFrameImage = (newFrameImage) => {
+ setFrameImage(newFrameImage);
+ };
+
+ const handleSaveClick = () => {
+ setUploadedImage(null);
+
+ // 액세스 토큰이 있을 때만 API 요청을 보내도록 조건 처리
+ if (accessToken) {
+ const frameElement = document.querySelector(`.${styles.Frame}`);
+
+ html2canvas(frameElement, { backgroundColor: null }).then((canvas) => {
+ canvas.toBlob((blob) => {
+ const formData = new FormData();
+ formData.append("frame", blob, "frame.png");
+
+ // axios 요청 설정
+ const config = {
+ headers: {
+ "Content-Type": "multipart/form-data",
+ Authorization: `Bearer ${accessToken}`, // 헤더에 accessToken을 추가
+ },
+ };
+
+ axios
+ .post(
+ "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/frame",
+ formData,
+ config
+ )
+ .then((res) => {
+ console.log("프레임 저장 API 응답:", res.data);
+ const updatedFrames = [...frames, res.data]; // 새로운 데이터 추가
+ setFrames(updatedFrames);
+ })
+ .catch((err) => {
+ // 오류 처리
+ console.error("API 요청 중 오류 발생:", err);
+ });
+ }, "image/png");
+ });
+ }
+ };
+
+ const handleUploadedImage = (imageData) => {
+ setUploadedImage(imageData);
+ console.log(uploadedImage);
+ };
+
+ let bottomContent;
+
+ switch (selectedButton) {
+ case "템플릿":
+ bottomContent = (
+
+ );
+ break;
+ case "스티커":
+ bottomContent = (
+
+ handleStickerSelect(selectedSticker)
+ }
+ />
+ );
+ break;
+ case "배경":
+ bottomContent = (
+
+ );
+ break;
+ case "브러쉬":
+ bottomContent = (
+
+
+
+ );
+ break;
+ case "사진 추가":
+ bottomContent = ;
+ break;
+ default:
+ bottomContent = ;
+ break;
+ }
+
+ // 실행 취소 기능 구현
+ const undoAction = () => {
+ // actions 배열에 저장된 액션이 있을 때만 실행 취소 수행
+ if (actions.length > 0) {
+ const lastAction = actions[actions.length - 1];
+
+ // 이전 액션을 실행하여 상태를 되돌립니다.
+ if (lastAction.type === "frame_added") {
+ // 프레임이 추가된 액션이면 추가된 프레임을 제거합니다.
+ const updatedFrames = frames.slice(0, -1);
+ setFrames(updatedFrames);
+ }
+
+ // actions 배열에서 취소된 액션을 제거합니다.
+ setActions(actions.slice(0, -1));
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {uploadedImage && (
+
+ )}
+
+
+ {selectedStickers.map((selectedSticker, index) => (
+
handleStickerMouseDown(index, e)}
+ onMouseMove={handleStickerMouseMove}
+ onMouseUp={handleStickerMouseUp}
+ onMouseLeave={handleStickerMouseUp}
+ />
+ ))}
+
+
+ 실행 취소
+
+
+
+ {bottomContent}
+
+
handleButtonClick("템플릿")}
+ >
+
+ 템플릿
+
+
handleButtonClick("스티커")}>
+
+ 스티커
+
+
handleButtonClick("배경")}>
+
+ 배경
+
+
handleButtonClick("브러쉬")}>
+
+ 브러쉬
+
+
handleButtonClick("사진 추가")}>
+
+ 네컷 추가
+
+
+
+
+
+ );
+};
+
+export default MakeFrame;
diff --git a/src/pages/Frame/MakeFrame.module.css b/src/pages/Frame/MakeFrame.module.css
index 602b47a..bd3c753 100644
--- a/src/pages/Frame/MakeFrame.module.css
+++ b/src/pages/Frame/MakeFrame.module.css
@@ -1,229 +1,194 @@
-body {
- margin: 0px;
- padding: 0px;
-}
-
-.MakeFrame {
- width: 390px;
- height: calc(var(--vh, 1vh) * 100);
- background: #ffffff;
- display: flex;
- flex-direction: column;
- justify-content: center;
- margin: 0;
-}
-
-.MakeFramebox {
- display: flex;
- flex-direction: column;
-}
-
-.Top {
- height: 86px;
- display: flex;
- flex-direction: row;
- justify-content: space-around;
- align-items: center;
-}
-
-.Top button {
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
-}
-
-.TextBtn {
- width: 95px;
- height: 24px;
- background-color: #f7f8f8;
- text-align: center;
- font-size: 12px;
- font-weight: 400;
- border-radius: 19px;
- border: none;
-}
-
-.ImgBtn {
- width: 24px;
- height: 24px;
- background-color: #ffffff;
- border: none;
-}
-
-.Middle {
- width: 390px;
- height: 480px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- background-color: #f7f8f8;
- position: relative;
-}
-
-.FrameImg {
- position: relative;
- width: 100vw;
- height: 100%;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
-}
-
-.UploadedImage {
- position: absolute;
- top: 0;
- left: 50%;
- transform: translateX(-50%);
- z-index: 1;
-}
-
-.UploadedSticker {
- width: 5px;
- height: 5px;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- z-index: 2;
-}
-
-.BrushSize {
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- margin-left: 20px;
- gap: 10px;
-}
-
-.SizeSelector {
- display: inline-flex;
-}
-
-.BrushSize button {
- border-radius: 100%;
- border: none;
- background-color: #bebcbc;
-}
-
-.BrushSizeButton1 {
- width: 18px;
- height: 18px;
-}
-
-.BrushSizeButton2 {
- width: 16px;
- height: 16px;
-}
-
-.BrushSizeButton3 {
- width: 13px;
- height: 13px;
-}
-
-.Frame {
- position: relative;
- width: 390px;
- height: 430px;
-}
-
-.Frame img {
- width: 100%;
- height: 100%;
- object-fit: contain;
- display: flex;
- flex-direction: column;
- justify-content: start;
- align-items: center;
- margin: 0 auto;
- position: absolute;
- top: 0;
- left: 50%;
- transform: translateX(-50%);
-}
-
-.BaseFrame {
- z-index: 1;
-}
-
-.UploadedImage {
- z-index: 0;
-}
-
-/* .Frame span {
- width: 125px;
- height: 78px;
- background-color: #f5f5f5;
-}
-
-.Frame span:nth-child(1) {
- margin-top: 10px;
-} */
-
-.BrushFrame {
- height: 594px;
-}
-
-.Return {
- width: 390px;
- height: 50px;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- background-color: #ffffff;
- gap: 15px;
-}
-
-.Return button {
- background-color: #ffffff;
- border: none;
-}
-
-.Bottom {
- width: 100%;
- height: 270px;
-}
-
-.ListTop {
- height: 40px;
- padding: 14px 14px 14px 18px;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- font-size: 10px;
- font-weight: 400;
-}
-
-.ListBottom {
- width: 390px;
- height: 70px;
- background: #f7f8f8;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- gap: 20px;
-}
-
-.ListBottom button {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- text-align: center;
- font-size: 10px;
- font-weight: 400;
- border: none;
- background-color: #f7f8f8;
-}
-
-.ListBottom button img {
- width: 24px;
- height: 24px;
-}
+body {
+ margin: 0px;
+ padding: 0px;
+}
+
+.MakeFrame {
+ width: 390px;
+ height: calc(var(--vh, 1vh) * 100);
+ background: #ffffff;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin: 0;
+}
+
+.MakeFramebox {
+ display: flex;
+ flex-direction: column;
+}
+
+.Top {
+ height: 86px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+}
+
+.Top button {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.TextBtn {
+ width: 95px;
+ height: 24px;
+ background-color: #f7f8f8;
+ text-align: center;
+ font-size: 12px;
+ font-weight: 400;
+ border-radius: 19px;
+ border: none;
+}
+
+.ImgBtn {
+ width: 24px;
+ height: 24px;
+ background-color: #ffffff;
+ border: none;
+}
+
+.Middle {
+ width: 390px;
+ height: 480px;
+ display: inline-block;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: #f7f8f8;
+ position: relative;
+}
+
+.Frame {
+ position: relative;
+ width: 300px;
+ height: 430px;
+ object-fit: contain;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.Frame img {
+ width: 100%;
+ height: 100%;
+ display: block;
+ flex-direction: column;
+ justify-content: start;
+ align-items: center;
+ margin: 0 auto;
+ position: absolute;
+}
+
+.UploadedImage {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 1;
+}
+
+.BaseFrame {
+ position: absolute;
+ z-index: 2;
+}
+
+.UploadedSticker {
+ width: 5px;
+ height: 5px;
+ position: absolute;
+ transform: translate(-50%, -50%);
+ z-index: 3;
+}
+
+.BrushClass {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 4;
+}
+
+/* .Frame span {
+ width: 125px;
+ height: 78px;
+ background-color: #f5f5f5;
+}
+
+.Frame span:nth-child(1) {
+ margin-top: 10px;
+} */
+
+.Return {
+ width: 390px;
+ height: 50px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ background-color: #ffffff;
+ gap: 15px;
+}
+
+.Return button {
+ background-color: #ffffff;
+ border: none;
+}
+
+.Bottom {
+ width: 100%;
+ height: 270px;
+}
+
+.ListTop {
+ height: 40px;
+ padding: 14px 14px 14px 18px;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 10px;
+ font-weight: 400;
+}
+
+.ListBottom {
+ width: 390px;
+ height: 70px;
+ background: #f7f8f8;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ gap: 20px;
+}
+
+.ListBottom button {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ font-size: 10px;
+ font-weight: 400;
+ border: none;
+ background-color: #f7f8f8;
+}
+
+.ListBottom button img {
+ width: 24px;
+ height: 24px;
+}
+
+.Brush {
+ width: 650px;
+ height: 50px;
+ display: flex;
+ justify-content: space-evenly;
+ border-radius: 5px;
+ align-items: center;
+ background-color: #a3a3a32d;
+ margin: auto;
+ margin-top: 10px;
+}
diff --git a/src/pages/Join/Join.jsx b/src/pages/Join/Join.jsx
index be831ea..9dcd5d4 100644
--- a/src/pages/Join/Join.jsx
+++ b/src/pages/Join/Join.jsx
@@ -1,279 +1,279 @@
-import { useNavigate, Link } from "react-router-dom";
-import { useState } from "react";
-import styles from "./Join.module.scss";
-// import api from "../../apis/api";
-import { onSignUp } from "../../apis/onSignUp";
-import { emailCheck } from "../../apis/emailCheck";
-
-export default function SignUp() {
- const initialState = {
- email: "",
- nickname: "",
- password: "",
- passwordConfirm: "",
- };
-
- // 가입 작성 폼 초기화
- const [formData, setFormData] = useState(initialState);
-
- // 에러 메시지 통합
- const [errorMessages, setErrorMessages] = useState({
- email: "",
- nickname: "",
- password: "",
- passwordConfirm: "",
- });
-
- // 유효성 검사 통합
- const [isValid, setIsValid] = useState({
- email: false,
- nickname: false,
- password: false,
- passwordConfirm: false,
- });
-
- const [allCheck, setAllCheck] = useState(false);
-
- // 체크박스 로직 통합
- const [checkStates, setCheckStates] = useState({
- checkState1: false,
- checkState2: false,
- checkState3: false,
- checkState4: false,
- });
-
- const checkDescriptions = {
- checkState1: "만 14세 이상입니다 (필수)",
- checkState2: "원아워 이용약관에 동의합니다 (필수)",
- checkState3: "원아워 개인정보 수집 및 이용에 동의합니다 (필수)",
- checkState4: "광고성 SNS, 이메일 뉴스레터 수신에 동의합니다 (선택)",
- };
-
- // 라우터 설정
- const router = useNavigate();
-
- const handleChange = (e) => {
- const { name, value } = e.target;
- setFormData({ ...formData, [name]: value });
- validateInput(name, value);
- };
-
- // 각각의 유효성 검사를 하나의 함수로 묶음
- const validateInput = (name, value) => {
- let emailRegex,
- isEmailValid,
- isNicknameValid,
- passwordRegex,
- isPasswordValid,
- isPasswordConfirmValid;
- switch (name) {
- case "email":
- emailRegex =
- /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
- isEmailValid = emailRegex.test(value) || value.length == 0;
- setErrorMessages({
- ...errorMessages,
- email: isEmailValid ? "" : "올바른 이메일을 입력해주세요",
- });
- setIsValid({ ...isValid, email: isEmailValid });
- break;
- case "nickname":
- isNicknameValid =
- (value.length >= 2 && value.length <= 16) || value.length == 0;
- setErrorMessages({
- ...errorMessages,
- nickname: isNicknameValid
- ? ""
- : "2글자 이상 16글자 이하로 입력해주세요",
- });
- setIsValid({ ...isValid, nickname: isNicknameValid });
- break;
- case "password":
- passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,25}$/;
- isPasswordValid = passwordRegex.test(value) || value.length == 0;
- setErrorMessages({
- ...errorMessages,
- password: isPasswordValid
- ? ""
- : "숫자+영문자 조합으로 8자리 이상 입력해주세요!",
- });
- setIsValid({ ...isValid, password: isPasswordValid });
- break;
- case "passwordConfirm":
- isPasswordConfirmValid =
- value === formData.password || value.length == 0;
- setErrorMessages({
- ...errorMessages,
- passwordConfirm: isPasswordConfirmValid
- ? ""
- : "비밀번호가 일치하지 않습니다.",
- });
- setIsValid({ ...isValid, passwordConfirm: isPasswordConfirmValid });
- break;
- default:
- break;
- }
- };
-
- // 체크박스 로직 중 전체선택
- const toggleAllCheck = () => {
- const newAllCheck = !allCheck;
- setAllCheck(newAllCheck);
- setCheckStates({
- checkState1: newAllCheck,
- checkState2: newAllCheck,
- checkState3: newAllCheck,
- checkState4: newAllCheck,
- });
- };
-
- const toggleCheck = (name) => {
- const newCheckStates = {
- ...checkStates,
- [name]: !checkStates[name],
- };
- setCheckStates(newCheckStates);
-
- // 전체 동의 체크 여부 확인
- const allChecked = Object.values(newCheckStates).every((state) => state);
- setAllCheck(allChecked);
- };
-
- // 회원가입 버튼 비활성화 조건
- const isSubmitDisabled = !(
- isValid.email &&
- isValid.nickname &&
- isValid.password &&
- isValid.passwordConfirm &&
- checkStates.checkState1 &&
- checkStates.checkState2 &&
- checkStates.checkState3
- );
-
- return (
-
-
-
-
-
-
이메일
-
-
- {
- emailCheck(formData,setFormData);
- }}
- >
- 중복확인
-
-
- {errorMessages.email && (
-
{errorMessages.email}
- )}
-
-
-
닉네임
-
- {errorMessages.nickname && (
- {errorMessages.nickname}
- )}
-
-
-
비밀번호
-
- {errorMessages.password && {errorMessages.password} }
-
- {errorMessages.passwordConfirm && (
-
- {errorMessages.passwordConfirm}
-
- )}
-
-
-
-
- {/* 체크박스 로직 map함수로 뿌려줌 */}
- {Object.keys(checkStates).map((checkStateName) => (
-
- toggleCheck(checkStateName)}
- >
-
{checkDescriptions[checkStateName]}
-
- ))}
-
-
{
- onSignUp(formData, router);
- }}
- className={isSubmitDisabled ? styles.signUpBtn0 : styles.signUpBtn1}
- disabled={isSubmitDisabled}
- >
- 가입하기
-
-
-
-
- );
-}
+import { useNavigate, Link } from "react-router-dom";
+import { useState } from "react";
+import styles from "./Join.module.scss";
+// import api from "../../apis/api";
+import { onSignUp } from "../../apis/onSignUp";
+import { emailCheck } from "../../apis/emailCheck";
+
+export default function SignUp() {
+ const initialState = {
+ email: "",
+ nickname: "",
+ password: "",
+ passwordConfirm: "",
+ };
+
+ // 가입 작성 폼 초기화
+ const [formData, setFormData] = useState(initialState);
+
+ // 에러 메시지 통합
+ const [errorMessages, setErrorMessages] = useState({
+ email: "",
+ nickname: "",
+ password: "",
+ passwordConfirm: "",
+ });
+
+ // 유효성 검사 통합
+ const [isValid, setIsValid] = useState({
+ email: false,
+ nickname: false,
+ password: false,
+ passwordConfirm: false,
+ });
+
+ const [allCheck, setAllCheck] = useState(false);
+
+ // 체크박스 로직 통합
+ const [checkStates, setCheckStates] = useState({
+ checkState1: false,
+ checkState2: false,
+ checkState3: false,
+ checkState4: false,
+ });
+
+ const checkDescriptions = {
+ checkState1: "만 14세 이상입니다 (필수)",
+ checkState2: "원아워 이용약관에 동의합니다 (필수)",
+ checkState3: "원아워 개인정보 수집 및 이용에 동의합니다 (필수)",
+ checkState4: "광고성 SNS, 이메일 뉴스레터 수신에 동의합니다 (선택)",
+ };
+
+ // 라우터 설정
+ const router = useNavigate();
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({ ...formData, [name]: value });
+ validateInput(name, value);
+ };
+
+ // 각각의 유효성 검사를 하나의 함수로 묶음
+ const validateInput = (name, value) => {
+ let emailRegex,
+ isEmailValid,
+ isNicknameValid,
+ passwordRegex,
+ isPasswordValid,
+ isPasswordConfirmValid;
+ switch (name) {
+ case "email":
+ emailRegex =
+ /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
+ isEmailValid = emailRegex.test(value) || value.length == 0;
+ setErrorMessages({
+ ...errorMessages,
+ email: isEmailValid ? "" : "올바른 이메일을 입력해주세요",
+ });
+ setIsValid({ ...isValid, email: isEmailValid });
+ break;
+ case "nickname":
+ isNicknameValid =
+ (value.length >= 2 && value.length <= 16) || value.length == 0;
+ setErrorMessages({
+ ...errorMessages,
+ nickname: isNicknameValid
+ ? ""
+ : "2글자 이상 16글자 이하로 입력해주세요",
+ });
+ setIsValid({ ...isValid, nickname: isNicknameValid });
+ break;
+ case "password":
+ passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,25}$/;
+ isPasswordValid = passwordRegex.test(value) || value.length == 0;
+ setErrorMessages({
+ ...errorMessages,
+ password: isPasswordValid
+ ? ""
+ : "숫자+영문자 조합으로 8자리 이상 입력해주세요!",
+ });
+ setIsValid({ ...isValid, password: isPasswordValid });
+ break;
+ case "passwordConfirm":
+ isPasswordConfirmValid =
+ value === formData.password || value.length == 0;
+ setErrorMessages({
+ ...errorMessages,
+ passwordConfirm: isPasswordConfirmValid
+ ? ""
+ : "비밀번호가 일치하지 않습니다.",
+ });
+ setIsValid({ ...isValid, passwordConfirm: isPasswordConfirmValid });
+ break;
+ default:
+ break;
+ }
+ };
+
+ // 체크박스 로직 중 전체선택
+ const toggleAllCheck = () => {
+ const newAllCheck = !allCheck;
+ setAllCheck(newAllCheck);
+ setCheckStates({
+ checkState1: newAllCheck,
+ checkState2: newAllCheck,
+ checkState3: newAllCheck,
+ checkState4: newAllCheck,
+ });
+ };
+
+ const toggleCheck = (name) => {
+ const newCheckStates = {
+ ...checkStates,
+ [name]: !checkStates[name],
+ };
+ setCheckStates(newCheckStates);
+
+ // 전체 동의 체크 여부 확인
+ const allChecked = Object.values(newCheckStates).every((state) => state);
+ setAllCheck(allChecked);
+ };
+
+ // 회원가입 버튼 비활성화 조건
+ const isSubmitDisabled = !(
+ isValid.email &&
+ isValid.nickname &&
+ isValid.password &&
+ isValid.passwordConfirm &&
+ checkStates.checkState1 &&
+ checkStates.checkState2 &&
+ checkStates.checkState3
+ );
+
+ return (
+
+
+
+
+
+
이메일
+
+
+ {
+ emailCheck(formData,setFormData);
+ }}
+ >
+ 중복확인
+
+
+ {errorMessages.email && (
+
{errorMessages.email}
+ )}
+
+
+
닉네임
+
+ {errorMessages.nickname && (
+ {errorMessages.nickname}
+ )}
+
+
+
비밀번호
+
+ {errorMessages.password && {errorMessages.password} }
+
+ {errorMessages.passwordConfirm && (
+
+ {errorMessages.passwordConfirm}
+
+ )}
+
+
+
+
+ {/* 체크박스 로직 map함수로 뿌려줌 */}
+ {Object.keys(checkStates).map((checkStateName) => (
+
+ toggleCheck(checkStateName)}
+ >
+
{checkDescriptions[checkStateName]}
+
+ ))}
+
+
{
+ onSignUp(formData, router);
+ }}
+ className={isSubmitDisabled ? styles.signUpBtn0 : styles.signUpBtn1}
+ disabled={isSubmitDisabled}
+ >
+ 가입하기
+
+
+
+
+ );
+}
diff --git a/src/pages/Login/Login.jsx b/src/pages/Login/Login.jsx
index 98c546f..f82b0c2 100644
--- a/src/pages/Login/Login.jsx
+++ b/src/pages/Login/Login.jsx
@@ -62,11 +62,14 @@ function LoginForm() {
};
try {
- const response = await axios.post("http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/user/login", data);
+ const response = await axios.post(
+ "http://ec2-3-35-208-177.ap-northeast-2.compute.amazonaws.com:8080/user/login",
+ data
+ );
const accessToken = response.data;
setAct(accessToken);
localStorage.setItem("accessToken", accessToken);
- router('/frame');
+ router("/frame");
} catch (error) {
console.error(error);
}
diff --git a/src/pages/Main/Main.jsx b/src/pages/Main/Main.jsx
index 022ab7d..ba9540a 100644
--- a/src/pages/Main/Main.jsx
+++ b/src/pages/Main/Main.jsx
@@ -1,39 +1,34 @@
-import styles from "./Main.module.scss";
-import { Link, useNavigate } from "react-router-dom";
-import main_logo from "../../assets/main-logo.png";
-import { useAtom } from "jotai";
-import { accessTokenAtom } from "../../store/jotaiAtoms";
-
-export default function Login() {
- const [ , setAccessToken] = useAtom(accessTokenAtom);
- const router = useNavigate();
-
- const logout = () => {
- if (confirm("정말 로그아웃 하시겠습니까?")) {
- localStorage.removeItem("accessToken");
- setAccessToken("");
- router("/");
- }
- }
-
- return (
-
-
-
-
-
-
-
-
- 로그인
-
-
-
- 로그아웃
-
-
- );
-}
+import styles from "./Main.module.scss";
+import { Link, useNavigate } from "react-router-dom";
+import main_logo from "../../assets/main-logo.png";
+import { useAtom } from "jotai";
+import { accessTokenAtom } from "../../store/jotaiAtoms";
+
+export default function Main() {
+ const [accessToken, setAccessToken] = useAtom(accessTokenAtom);
+ const router = useNavigate();
+
+ const logout = () => {
+ if (confirm("정말 로그아웃 하시겠습니까?")) {
+ localStorage.removeItem("accessToken");
+ setAccessToken("");
+ router("/");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
로그인
+
+
+ 로그아웃
+
+
+ );
+}
diff --git a/src/pages/Main/Main.module.scss b/src/pages/Main/Main.module.scss
index 6bb3bf1..828a578 100644
--- a/src/pages/Main/Main.module.scss
+++ b/src/pages/Main/Main.module.scss
@@ -1,30 +1,31 @@
-.root {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-items: center;
-}
-.logoBtn {
- width: 100%;
- height: 100%;
- background-color: #ffffff;
- border: none;
- margin-bottom: 100px;
-
- img {
- width: 56%;
- height: 50%;
- display: inline-flex;
- }
-}
-.logoutBtn, .loginBtn {
- width: 50vw;
- height: 45px;
- background-color: #009eff;
- border: none;
- border-radius: 50px;
- color: white;
- font-size: 14px;
- font-weight: 600;
- margin-top: 10px;
-}
\ No newline at end of file
+.root {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-items: center;
+}
+.logoBtn {
+ width: 100%;
+ height: 100%;
+ background-color: #ffffff;
+ border: none;
+ margin-bottom: 100px;
+
+ img {
+ width: 56%;
+ height: 50%;
+ display: inline-flex;
+ }
+}
+.logoutBtn,
+.loginBtn {
+ width: 50vw;
+ height: 45px;
+ background-color: #009eff;
+ border: none;
+ border-radius: 50px;
+ color: white;
+ font-size: 14px;
+ font-weight: 600;
+ margin-top: 10px;
+}
diff --git a/src/pages/Photobook/PhotoSelect.jsx b/src/pages/Photobook/PhotoSelect.jsx
index dbbdfbe..d98ef65 100644
--- a/src/pages/Photobook/PhotoSelect.jsx
+++ b/src/pages/Photobook/PhotoSelect.jsx
@@ -14,7 +14,9 @@ export default function PhotoSelect() {
setSelectedPhotos((prevSelectedPhotos) => {
// 이미 선택된 사진이면 제거, 아니면 추가
if (prevSelectedPhotos.includes(index)) {
- const newSelectedPhotos = prevSelectedPhotos.filter((selectedIndex) => selectedIndex !== index);
+ const newSelectedPhotos = prevSelectedPhotos.filter(
+ (selectedIndex) => selectedIndex !== index
+ );
console.log("삭제 후:", newSelectedPhotos);
return newSelectedPhotos;
} else {
@@ -27,24 +29,27 @@ export default function PhotoSelect() {
useEffect(() => {
console.log("세팅 후:", selectedPhotos);
- console.log("api 보내기전: ", selectedPhotos.map(index => getPhotos[index]))
+ console.log(
+ "api 보내기전: ",
+ selectedPhotos.map((index) => getPhotos[index])
+ );
axios
- .get('/photo')
+ .get("/photo")
.then((response) => {
setGetPhotos(response.data);
// console.log(getPhotos);
})
.catch((error) => console.error(error));
}, [selectedPhotos]);
-
+
const handlePostData = async () => {
const data = {
- "name" : "토리",
- "addPhotoList" : selectedPhotos.map(index => getPhotos[index])
+ name: "토리",
+ addPhotoList: selectedPhotos.map((index) => getPhotos[index]),
};
-
+
try {
- const response = await axios.post('/photoBook', data);
+ const response = await axios.post("/photoBook", data);
console.log("API 응답:", response.data);
router("/photobook");
} catch (error) {
@@ -53,15 +58,11 @@ export default function PhotoSelect() {
};
return (
-
-
-
- 나만의 포토북 제작하기
-
-
-
+
+
+ 나만의 포토북 제작하기
+
+
{/* 이미지 배열을 map 함수를 사용하여 렌더링 */}
{getPhotos.map((photo, index) => (
@@ -86,7 +87,7 @@ export default function PhotoSelect() {
))}
-
-
+
+
);
-}
\ No newline at end of file
+}
diff --git a/src/pages/Photobook/PhotoSelect.module.scss b/src/pages/Photobook/PhotoSelect.module.scss
index 4cc13f7..0409611 100644
--- a/src/pages/Photobook/PhotoSelect.module.scss
+++ b/src/pages/Photobook/PhotoSelect.module.scss
@@ -2,6 +2,8 @@
width: 100vw;
height: 100vh;
overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
}
.header {
diff --git a/src/pages/Photobook/Photobook.jsx b/src/pages/Photobook/Photobook.jsx
index 2b67d02..be085c8 100644
--- a/src/pages/Photobook/Photobook.jsx
+++ b/src/pages/Photobook/Photobook.jsx
@@ -8,6 +8,7 @@ import styles from "./Photobook.module.scss";
import shareIcon from "../../img/icon-share.png";
import navBottom from "../../img/nav-bottom.png";
import memoModal from "../../img/memo-modal.png";
+import home from "../../img/home.png";
import emoji1 from "../../img/emoji1.png";
import emoji2 from "../../img/emoji2.png";
import emoji3 from "../../img/emoji3.png";
@@ -26,10 +27,12 @@ import memopic5 from "../../img/memopic5.png";
import useAxios from "../../apis/axiosWithToken";
export default function Photobook() {
- {/* 포토북 UI 시작 */}
+ {
+ /* 포토북 UI 시작 */
+ }
const index = useRef(0);
const [ref, { width }] = useMeasure();
- const [photos, setPhotos] = useState([]);
+ const [photos, setPhotos] = useState([]);
const [activeDot, setActiveDot] = useState(0);
const [props, api] = useSprings(
photos.length,
@@ -43,7 +46,7 @@ export default function Photobook() {
const bind = useDrag(
({ active, movement: [mx], direction: [xDir], distance, cancel }) => {
if (active && distance > width / 2) {
- setActiveDot(null);
+ setActiveDot(null);
index.current = clamp(
index.current + (xDir > 0 ? -1 : 1),
0,
@@ -58,18 +61,18 @@ export default function Photobook() {
const scale = active ? 1 - distance / width / 2 : 1;
return { x, scale, display: "block" };
});
-
- // 스크롤에 따른 활성화를 유지
- // setActiveDot(clamp(Math.round(index.current), 0, photos.length - 1));
- // 클릭한 동그라미가 있다면 스크롤로 인한 활성화를 무시하고 클릭한 동그라미를 유지
- const activeDotIndex = clamp(
- Math.round(index.current),
- 0,
- photos.length - 1
- );
- setActiveDot(activeDotIndex);
- // console.log("스크롤 점 인덱스: ", activeDotIndex);
- }
+
+ // 스크롤에 따른 활성화를 유지
+ // setActiveDot(clamp(Math.round(index.current), 0, photos.length - 1));
+ // 클릭한 동그라미가 있다면 스크롤로 인한 활성화를 무시하고 클릭한 동그라미를 유지
+ const activeDotIndex = clamp(
+ Math.round(index.current),
+ 0,
+ photos.length - 1
+ );
+ setActiveDot(activeDotIndex);
+ // console.log("스크롤 점 인덱스: ", activeDotIndex);
+ }
);
// 동그라미를 클릭했을 때 해당 사진으로 이동하는 함수
const handleDotClick = (dotIndex) => {
@@ -80,9 +83,11 @@ export default function Photobook() {
const x = (i - index.current) * width;
return { x, scale: 1, display: "block" };
});
- setActiveDot(null); // 클릭한 동그라미를 활성화 상태로 설정
+ setActiveDot(null); // 클릭한 동그라미를 활성화 상태로 설정
};
- {/* 포토북 UI 마침 */}
+ {
+ /* 포토북 UI 마침 */
+ }
const axios = useAxios();
const [modal, setModal] = useState(0);
@@ -99,7 +104,7 @@ export default function Photobook() {
.get("/photoBook") // 인스턴스로 axios 요청 보내기
.then((response) => {
console.log(response.data);
- setPhotos(response.data.photoList);
+ setPhotos(response.data.photoList);
setUuid(response.data.uuid);
})
.catch((error) => console.error(error));
@@ -141,7 +146,7 @@ export default function Photobook() {
.catch((err) => {
console.error("링크 복사 중 오류 발생:", err);
});
- console.log("링크:", `${window.location.href}/${uuid}`);
+ console.log("링크:", `${window.location.href}/${uuid}`);
// // 현재 주소에 uuid 파라미터 추가
// const currentUrl = new URL(window.location.href);
// currentUrl.searchParams.set('uuid', 'YOUR_UUID'); // 여기에 받아온 uuid 변수를 넣어주세요
diff --git a/src/pages/Photobook/Photobook.module.scss b/src/pages/Photobook/Photobook.module.scss
index 72b1cda..d9de091 100644
--- a/src/pages/Photobook/Photobook.module.scss
+++ b/src/pages/Photobook/Photobook.module.scss
@@ -5,7 +5,7 @@
}
.wrapper {
- width: 100vw;
+ width: 390px;
height: 100vh;
display: flex;
flex-direction: column;
@@ -38,8 +38,7 @@
height: 430px;
margin-top: -12px;
will-change: transform;
- box-shadow:
- 0 62.5px 125px -25px rgba(50, 50, 73, 0.5),
+ box-shadow: 0 62.5px 125px -25px rgba(50, 50, 73, 0.5),
0 37.5px 75px -37.5px rgba(0, 0, 0, 0.6);
}
.memopic {
@@ -86,6 +85,10 @@
justify-content: flex-start;
background-color: white;
border-bottom: 1.5px solid rgba(0, 0, 0, 0.06);
+ button {
+ background-color: #ffffff;
+ border: none;
+ }
p {
font-family: Pretendard;
margin-left: 18px;
@@ -101,7 +104,7 @@
width: 100%;
height: 58vh;
overflow-x: auto;
- background-color: #DCF2FF;
+ background-color: #dcf2ff;
display: flex;
align-items: center;
justify-content: center;
@@ -126,7 +129,7 @@
width: 72px;
height: 72px;
border-radius: 15px;
- background: #F7F7F7;
+ background: #f7f7f7;
display: flex;
align-items: center;
justify-content: center;
@@ -138,7 +141,7 @@
}
}
p {
- color: #009EFF;
+ color: #009eff;
text-align: center;
font-family: Pretendard;
font-size: 10px;
@@ -149,9 +152,9 @@
}
.navBottom {
- width: 100vw;
+ width: 390px;
img {
- width: 100vw;
+ width: 390px;
}
}
@@ -167,7 +170,7 @@
height: 234px;
top: 33vh;
left: 6vw;
- background-color: url('../../img/memo-modal.png');
+ background-color: url("../../img/memo-modal.png");
background-size: cover; /* 이미지를 화면에 꽉 채우도록 조정 */
background-position: center; /* 이미지를 가운데 정렬 */
background-repeat: no-repeat; /* 이미지 반복 방지 */
@@ -195,8 +198,9 @@
margin-top: 0px;
color: #000;
text-align: center;
- font-family: "SUIT Variable", -apple-system, BlinkMacSystemFont, Pretendard, "Segoe UI", Roboto, Oxygen,
- Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
+ font-family: "SUIT Variable", -apple-system, BlinkMacSystemFont,
+ Pretendard, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
+ "Helvetica Neue", sans-serif;
font-size: 16px;
font-weight: 400;
line-height: normal;
@@ -207,7 +211,7 @@
background-color: #f8f8f8;
resize: none;
&:focus {
- border: 1px solid #009EFF;
+ border: 1px solid #009eff;
}
}
.saveBtn {
@@ -219,7 +223,7 @@
align-items: center;
justify-content: center;
color: white;
- background-color: #009EFF;
+ background-color: #009eff;
border: none;
border-radius: 19px;
cursor: pointer;
@@ -237,7 +241,7 @@
width: 100vw;
height: 1100vh;
position: absolute;
- background-color: rgba(0, 40, 64, 0.60);
+ background-color: rgba(0, 40, 64, 0.6);
// opacity: 40%;
top: 0px;
left: 0px;
@@ -262,4 +266,4 @@
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
-}
\ No newline at end of file
+}
diff --git a/src/pages/Photobook/PhotobookUuid.jsx b/src/pages/Photobook/PhotobookUuid.jsx
index 537c58f..85a58c8 100644
--- a/src/pages/Photobook/PhotobookUuid.jsx
+++ b/src/pages/Photobook/PhotobookUuid.jsx
@@ -27,10 +27,12 @@ import memopic5 from "../../img/memopic5.png";
import useAxios from "../../apis/axiosWithToken";
export default function Photobook() {
- {/* 포토북 UI 시작 */}
+ {
+ /* 포토북 UI 시작 */
+ }
const index = useRef(0);
const [ref, { width }] = useMeasure();
- const [photos, setPhotos] = useState([]);
+ const [photos, setPhotos] = useState([]);
const [activeDot, setActiveDot] = useState(0);
const [props, api] = useSprings(
photos.length,
@@ -44,7 +46,7 @@ export default function Photobook() {
const bind = useDrag(
({ active, movement: [mx], direction: [xDir], distance, cancel }) => {
if (active && distance > width / 2) {
- setActiveDot(null);
+ setActiveDot(null);
index.current = clamp(
index.current + (xDir > 0 ? -1 : 1),
0,
@@ -59,18 +61,18 @@ export default function Photobook() {
const scale = active ? 1 - distance / width / 2 : 1;
return { x, scale, display: "block" };
});
-
- // 스크롤에 따른 활성화를 유지
- // setActiveDot(clamp(Math.round(index.current), 0, photos.length - 1));
- // 클릭한 동그라미가 있다면 스크롤로 인한 활성화를 무시하고 클릭한 동그라미를 유지
- const activeDotIndex = clamp(
- Math.round(index.current),
- 0,
- photos.length - 1
- );
- setActiveDot(activeDotIndex);
- // console.log("스크롤 점 인덱스: ", activeDotIndex);
- }
+
+ // 스크롤에 따른 활성화를 유지
+ // setActiveDot(clamp(Math.round(index.current), 0, photos.length - 1));
+ // 클릭한 동그라미가 있다면 스크롤로 인한 활성화를 무시하고 클릭한 동그라미를 유지
+ const activeDotIndex = clamp(
+ Math.round(index.current),
+ 0,
+ photos.length - 1
+ );
+ setActiveDot(activeDotIndex);
+ // console.log("스크롤 점 인덱스: ", activeDotIndex);
+ }
);
// 동그라미를 클릭했을 때 해당 사진으로 이동하는 함수
const handleDotClick = (dotIndex) => {
@@ -81,9 +83,11 @@ export default function Photobook() {
const x = (i - index.current) * width;
return { x, scale: 1, display: "block" };
});
- setActiveDot(null); // 클릭한 동그라미를 활성화 상태로 설정
+ setActiveDot(null); // 클릭한 동그라미를 활성화 상태로 설정
};
- {/* 포토북 UI 마침 */}
+ {
+ /* 포토북 UI 마침 */
+ }
const axios = useAxios();
const [modal, setModal] = useState(0);
@@ -101,7 +105,7 @@ export default function Photobook() {
.get(`/photoBook/${uuid}`) // 인스턴스로 axios 요청 보내기
.then((response) => {
console.log(response.data);
- setPhotos(response.data.photoList);
+ setPhotos(response.data.photoList);
setUuidData(response.data.uuid);
})
.catch((error) => console.error(error));
@@ -143,7 +147,7 @@ export default function Photobook() {
// .catch((err) => {
// console.error("링크 복사 중 오류 발생:", err);
// });
- // console.log("링크:", `${window.location.href}/${uuidData}`);
+ // console.log("링크:", `${window.location.href}/${uuidData}`);
// // // 현재 주소에 uuid 파라미터 추가
// // const currentUrl = new URL(window.location.href);
// // currentUrl.searchParams.set('uuid', 'YOUR_UUID'); // 여기에 받아온 uuid 변수를 넣어주세요
diff --git a/src/store/cookie.js b/src/store/cookie.js
new file mode 100644
index 0000000..71bfbb1
--- /dev/null
+++ b/src/store/cookie.js
@@ -0,0 +1,15 @@
+import { Cookies } from "react-cookie";
+
+const cookies = new Cookies();
+//쿠키에 값을 저장할때
+export const setCookie = (name, value, option) => {
+ return cookies.set(name, value, { ...option });
+};
+//쿠키에 있는 값을 꺼낼때
+export const getCookie = (name) => {
+ return cookies.get(name);
+};
+//쿠키를 지울때
+export const removeCookie = (name) =>{
+ return cookies.remove(name);
+}
\ No newline at end of file
diff --git a/src/store/store.js b/src/store/store.js
new file mode 100644
index 0000000..bacff4e
--- /dev/null
+++ b/src/store/store.js
@@ -0,0 +1,86 @@
+// store 만들기 - configueStore가 reducer를 감싸고 있고 저기서 모든 state를 관리함
+import { configureStore } from "@reduxjs/toolkit";
+import userReducer from "./userSlice";
+
+export const store = configureStore({
+ reducer: {
+ user: userReducer,
+ },
+});
+
+/*
+"use client";
+
+import { configureStore } from "@reduxjs/toolkit";
+import { combineReducers } from "@reduxjs/toolkit";
+import userReducer from "./userSlice";
+import {
+ persistReducer,
+ persistStore,
+ FLUSH,
+ REHYDRATE,
+ PAUSE,
+ PERSIST,
+ PURGE,
+ REGISTER,
+} from "redux-persist";
+import createWebStorage from "redux-persist/lib/storage/createWebStorage";
+import { encryptTransform } from "redux-persist-transform-encrypt";
+
+const privateKey = process.env.REACT_APP_SECRET_KEY;
+
+const createNoopStorage = () => {
+ return {
+ getItem() {
+ return Promise.resolve(null);
+ },
+ setItem(value) {
+ return Promise.resolve(value);
+ },
+ removeItem() {
+ return Promise.resolve();
+ },
+ };
+};
+
+const storage =
+ typeof window === "undefined"
+ ? createNoopStorage()
+ : createWebStorage("local");
+
+const persistConfig = {
+ key: "root",
+ storage,
+ // persist 암호화 설정
+ transforms: [
+ encryptTransform({
+ secretKey: `${privateKey}`,
+ onError: (err) => console.log(err),
+ }),
+ ],
+ whitelist: ["user"],
+};
+
+// 리듀서를 합친 후, 스토어에 등록
+const rootReducer = combineReducers({
+ user: userReducer,
+});
+
+const persistedReducer = persistReducer(persistConfig, rootReducer);
+
+// 스토어 생성
+export const store = configureStore({
+ reducer: persistedReducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ serializableCheck: {
+ ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
+ },
+ }),
+});
+
+export const persistor = persistStore(store);
+
+export const RootState = store.getState;
+export const AppDispatch = store.dispatch;
+export const RootReducerType = rootReducer; */