diff --git a/package-lock.json b/package-lock.json
index dd80a5f..c63f4a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,13 +12,16 @@
"@emotion/styled": "^11.13.0",
"@reduxjs/toolkit": "^2.2.8",
"@uiw/react-md-editor": "^3.6.0",
+ "axios": "^1.7.7",
"next": "^14.2.14",
"next-remove-imports": "^1.0.12",
"polished": "^4.3.1",
"react": "^18",
"react-dom": "^18",
"react-icons": "^5.3.0",
- "react-redux": "^9.1.2"
+ "react-redux": "^9.1.2",
+ "react-spinners": "^0.14.1",
+ "zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^20",
@@ -1765,6 +1768,11 @@
"integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
"dev": true
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1789,6 +1797,16 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -2073,6 +2091,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@@ -2123,10 +2152,11 @@
}
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -2309,6 +2339,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3162,6 +3200,25 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -3187,6 +3244,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -5408,7 +5478,6 @@
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">= 0.6"
}
@@ -5418,7 +5487,6 @@
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"mime-db": "1.52.0"
},
@@ -5471,9 +5539,9 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",
@@ -6059,6 +6127,11 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -6207,6 +6280,16 @@
}
}
},
+ "node_modules/react-spinners": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.14.1.tgz",
+ "integrity": "sha512-2Izq+qgQ08HTofCVEdcAQCXFEYfqTDdfeDQJeo/HHQiQJD4imOicNLhkfN2eh1NYEWVOX4D9ok2lhuDB0z3Aag==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
@@ -8165,6 +8248,34 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/zustand": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
+ "integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
+ },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/package.json b/package.json
index 1bb2736..949b1f9 100644
--- a/package.json
+++ b/package.json
@@ -14,13 +14,16 @@
"@emotion/styled": "^11.13.0",
"@reduxjs/toolkit": "^2.2.8",
"@uiw/react-md-editor": "^3.6.0",
+ "axios": "^1.7.7",
"next": "^14.2.14",
"next-remove-imports": "^1.0.12",
"polished": "^4.3.1",
"react": "^18",
"react-dom": "^18",
"react-icons": "^5.3.0",
- "react-redux": "^9.1.2"
+ "react-redux": "^9.1.2",
+ "react-spinners": "^0.14.1",
+ "zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^20",
diff --git a/public/icons/user.svg b/public/icons/user.svg
new file mode 100644
index 0000000..af388cb
--- /dev/null
+++ b/public/icons/user.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/Common/Spinners.tsx b/src/components/Common/Spinners.tsx
new file mode 100644
index 0000000..4a75da4
--- /dev/null
+++ b/src/components/Common/Spinners.tsx
@@ -0,0 +1,23 @@
+import { CSSProperties } from "react";
+import { PropagateLoader } from "react-spinners";
+
+const override: CSSProperties = {
+ display: "block",
+ margin: "0 auto",
+ borderColor: "red",
+};
+
+function App() {
+ return (
+
+ );
+}
+
+export default App;
diff --git a/src/components/LoginModal/LoginModel.tsx b/src/components/LoginModal/LoginModel.tsx
index 9649d74..2281544 100644
--- a/src/components/LoginModal/LoginModel.tsx
+++ b/src/components/LoginModal/LoginModel.tsx
@@ -3,6 +3,8 @@ import * as S from "./styles";
import close from "../../../public/icons/close.svg";
import chevron_left from "../../../public/icons/chevron_left.svg";
import Image from "next/image";
+import { useAuthStore } from "@/stores/authStore";
+import { login } from "@/services/auth";
interface ModalProps {
onClose: () => void;
@@ -17,11 +19,25 @@ interface ModalProps {
const Modal: React.FC = ({ onClose, initialStep = "main" }) => {
const [modalStep, setModalStep] = useState(initialStep);
+ const { login: setLoginState } = useAuthStore();
useEffect(() => {
setModalStep(initialStep);
}, [initialStep]);
+ const handleLogin = async (e: React.FormEvent) => {
+ e.preventDefault();
+ try {
+ const user = await login("email", "password");
+ setLoginState(user);
+ console.log(user);
+ alert("로그인 성공!");
+ } catch (error) {
+ console.error(error);
+ alert("로그인 실패!");
+ }
+ };
+
const handleBackdropClick = (event: React.MouseEvent) => {
if (event.target === event.currentTarget) {
onClose();
@@ -78,6 +94,14 @@ const Modal: React.FC = ({ onClose, initialStep = "main" }) => {
>
이메일로 로그인
+
+ 로그인 테스트
+
diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx
index 9e27361..b279430 100644
--- a/src/components/Navigation/Navigation.tsx
+++ b/src/components/Navigation/Navigation.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import {
LoginText,
SignUpButton,
@@ -13,11 +13,32 @@ import {
import LoginModal from "../LoginModal/LoginModel";
import hamburger from "../../../public/icons/hamburger.svg";
import Image from "next/image";
+import { useAuthStore } from "@/stores/authStore";
+import userIcon from "../../../public/icons/user.svg";
+import { logout } from "@/services/auth";
const Navigation: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [initialStep, setInitialStep] = useState<"main" | "signup">("main");
+ const { isLoggedIn, logout: clearAuthState } = useAuthStore();
+
+ useEffect(() => {
+ if (isLoggedIn) {
+ setIsModalOpen(false);
+ }
+ }, [isLoggedIn]);
+
+ const handleLogout = async () => {
+ try {
+ await logout();
+ clearAuthState();
+ alert("로그아웃 성공!");
+ } catch (error) {
+ console.error(error);
+ alert("로그아웃 실패!");
+ }
+ };
const handleOpenLoginModal = () => {
setInitialStep("main");
@@ -67,9 +88,31 @@ const Navigation: React.FC = () => {
-
-
로그인
-
회원가입
+
+ {isLoggedIn ? (
+
+ 로그아웃
+
+
+ ) : (
+ <>
+
로그인
+
+ 회원가입
+
+ >
+ )}
{isMenuOpen && (
<>
diff --git a/src/components/Navigation/styles.ts b/src/components/Navigation/styles.ts
index a617847..dae5d72 100644
--- a/src/components/Navigation/styles.ts
+++ b/src/components/Navigation/styles.ts
@@ -11,6 +11,7 @@ export const NavContainer = styled.nav`
position: sticky;
top: 0;
width: 100%;
+ height: 80px;
z-index: 1000;
justify-content: space-between;
diff --git a/src/components/Newpost/ImageUpload/ImageUpload.tsx b/src/components/Newpost/ImageUpload/ImageUpload.tsx
index 73769e5..2016c99 100644
--- a/src/components/Newpost/ImageUpload/ImageUpload.tsx
+++ b/src/components/Newpost/ImageUpload/ImageUpload.tsx
@@ -11,8 +11,8 @@ export default function UploadBox() {
const [showModal, setShowModal] = useState(false);
const [selectedImage, setSelectedImage] = useState
(null);
- const handleDragStart = () => setActive(true);
- const handleDragEnd = (event: React.DragEvent) => {
+ const handleDragEnter = () => setActive(true);
+ const handleDragLeave = (event: React.DragEvent) => {
// 드래그가 완전히 벗어난 경우에만 setActive(false)를 호출
if (!event.currentTarget.contains(event.relatedTarget as Node)) {
setActive(false);
@@ -113,9 +113,9 @@ export default function UploadBox() {
))}
diff --git a/src/components/Newpost/MDEditor/styles.ts b/src/components/Newpost/MDEditor/styles.ts
new file mode 100644
index 0000000..08409ad
--- /dev/null
+++ b/src/components/Newpost/MDEditor/styles.ts
@@ -0,0 +1,11 @@
+import styled from "@emotion/styled";
+
+interface DragProps {
+ isActive: boolean;
+}
+
+export const ImageDragDiv = styled.div`
+ align-items: center;
+
+ border-radius: 5px;
+`;
diff --git a/src/components/Newpost/Mdeditor.tsx b/src/components/Newpost/Mdeditor.tsx
deleted file mode 100644
index de5167c..0000000
--- a/src/components/Newpost/Mdeditor.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import "@uiw/react-md-editor/markdown-editor.css";
-import "@uiw/react-markdown-preview/markdown.css";
-import dynamic from "next/dynamic";
-import { useState } from "react";
-
-const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { ssr: false });
-
-function MdEditor() {
- const [value, setValue] = useState("**프로젝트 소개를 입력하세요**");
-
- const handleChange = (val?: string) => {
- setValue(val ?? "");
- };
-
- return (
-
-
-
- );
-}
-
-export default MdEditor;
diff --git a/src/components/Newpost/Newpost.tsx b/src/components/Newpost/Newpost.tsx
index 8a35983..cb8fe01 100644
--- a/src/components/Newpost/Newpost.tsx
+++ b/src/components/Newpost/Newpost.tsx
@@ -1,6 +1,6 @@
import * as Style from "./styles";
import SelectStackType from "../SelectStackType/SelectStackType";
-import NewpostEditor from "./Mdeditor";
+import NewpostEditor from "./MDEditor/mdeditor";
import { useEffect, useRef, useState } from "react";
import NewpostImages from "./ImageUpload/ImageUpload";
import { useAppDispatch } from "@/stores";
diff --git a/src/components/Newpost/styles.ts b/src/components/Newpost/styles.ts
index 0396013..e247a55 100644
--- a/src/components/Newpost/styles.ts
+++ b/src/components/Newpost/styles.ts
@@ -5,7 +5,7 @@ const lightgrey = "#d7e2eb";
const grey = "#b2c0cc";
export const NewpostContainer = styled.div`
- max-width: 800px;
+ max-width: 1200px;
min-height: 100svh;
align-items: center;
padding: 20px 20px 20px 20px;
diff --git a/src/components/newpost/MDEditor/mdeditor.tsx b/src/components/newpost/MDEditor/mdeditor.tsx
new file mode 100644
index 0000000..c6e308d
--- /dev/null
+++ b/src/components/newpost/MDEditor/mdeditor.tsx
@@ -0,0 +1,157 @@
+import "@uiw/react-md-editor/markdown-editor.css";
+import "@uiw/react-markdown-preview/markdown.css";
+import Spinners from "@/components/Common/Spinners";
+import { uploadImage } from "@/pages/api/NewPost/mdeditor-image-upload";
+import dynamic from "next/dynamic";
+import { useState } from "react";
+import * as Style from "./styles";
+
+const MDEditor = dynamic(() => import("@uiw/react-md-editor"), { ssr: false });
+
+function MdEditor() {
+ const [value, setValue] = useState("**프로젝트 소개를 입력하세요**");
+ const [loading, setLoading] = useState(false);
+ const [isActive, setIsActive] = useState(false);
+ const [cursorPosition, setCursorPosition] = useState(0);
+
+ // 이미지 드래그 앤 드롭 관련 처리
+ const handleDragEnter = () => setIsActive(true);
+ const handleDragLeave = (event: React.DragEvent) => {
+ // 드래그가 완전히 벗어난 경우에만 setActive(false)를 호출
+ if (!event.currentTarget.contains(event.relatedTarget as Node)) {
+ setIsActive(false);
+ }
+ };
+ const handleDragOver = (event: React.DragEvent) => {
+ event.preventDefault();
+ };
+
+ const handleDrop = (event: React.DragEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ setIsActive(false);
+
+ const file = event.dataTransfer.files[0];
+ if (file) {
+ if (file.type.startsWith("image/")) {
+ const fileReader = new FileReader();
+ fileReader.readAsDataURL(file);
+ fileReader.onload = async (e) => {
+ if (typeof e.target?.result === "string") {
+ setLoading(true);
+ const imageUrl = await uploadImage(file);
+ if (imageUrl) {
+ setValue(
+ (prev) =>
+ prev.substring(0, cursorPosition) +
+ `\n![image](https://www.kookmin.ac.kr/content/05sub/style0005/images/sub/ui_5_col_image_3.jpg)\n` +
+ prev.substring(cursorPosition)
+ );
+ }
+ setLoading(false);
+ }
+ };
+ } else {
+ alert("이미지 파일만 업로드할 수 있습니다.");
+ }
+ }
+ };
+
+ // 이미지 붙여넣기 처리
+ const handlePaste = async (event: React.ClipboardEvent) => {
+ const clipboardData = event.clipboardData;
+ if (!clipboardData) {
+ return;
+ }
+
+ const clipboardItems = clipboardData.items;
+
+ if (clipboardItems[0].type.startsWith("image/")) {
+ const file = clipboardItems[0].getAsFile();
+
+ if (file) {
+ setLoading(true);
+ const imageUrl = await uploadImage(file);
+ if (imageUrl) {
+ setValue(
+ (prev) =>
+ prev.substring(0, cursorPosition) +
+ `\n![image](https://www.kookmin.ac.kr/content/05sub/style0005/images/sub/ui_5_col_image_2.jpg)\n` +
+ prev.substring(cursorPosition)
+ );
+ }
+ setLoading(false);
+ }
+ }
+ };
+
+ // 입력 처리
+ const handleChange = (text?: string) => {
+ const updatedText = text || "";
+ setValue(updatedText);
+ };
+
+ const handleFocus = (event: React.FocusEvent) => {
+ const position = event.target.selectionStart;
+ setCursorPosition(position);
+ };
+
+ const handleSelect = (event: React.SyntheticEvent) => {
+ const position = event.currentTarget.selectionStart;
+ setCursorPosition(position);
+ };
+
+ const handleMouseUp = (event: React.MouseEvent) => {
+ const position = event.currentTarget.selectionStart;
+ setCursorPosition(position);
+ };
+
+ const handleKeyUp = (event: React.KeyboardEvent) => {
+ const position = event.currentTarget.selectionStart;
+ setCursorPosition(position);
+ };
+
+ return (
+
+
+
+ {loading && (
+
+
+
+ )}
+
+
+ );
+}
+
+export default MdEditor;
diff --git a/src/data/cardDummyDatas.ts b/src/data/cardDummyDatas.ts
index 2b29ab3..cdfc14d 100644
--- a/src/data/cardDummyDatas.ts
+++ b/src/data/cardDummyDatas.ts
@@ -4,7 +4,7 @@ const dummyData = Array.from({ length: 100 }, (_, index) => ({
"https://velog.velcdn.com/images/yena1025/post/295eb434-5b73-421f-bbe4-6bc13acd4c33/image.png",
title: `프로젝트 ${index + 1}`,
description: "외국인 유학생을 위한 앱",
- likes: 123 + index,
+ likes: 124 + index,
author: "캡스톤 30조",
}));
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 70fa9b0..1a68e34 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -1,6 +1,7 @@
import Layout from "@/components/Layout/Layout";
import { store } from "@/stores";
import "@/styles/globals.css";
+import "@/styles/mdeditor.css";
import type { AppProps } from "next/app";
import { Provider } from "react-redux";
import localFont from "next/font/local";
diff --git a/src/pages/api/NewPost/mdeditor-image-upload.ts b/src/pages/api/NewPost/mdeditor-image-upload.ts
new file mode 100644
index 0000000..344e8bd
--- /dev/null
+++ b/src/pages/api/NewPost/mdeditor-image-upload.ts
@@ -0,0 +1,38 @@
+const delay = (ms: number) => {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+};
+
+// 사용 예시
+const runWithDelay = async () => {
+ console.log("대기 시작");
+ await delay(2000); // 2초 대기
+ console.log("2초 후 실행");
+};
+
+export const uploadImage = async (file: File) => {
+ try {
+ const formData = new FormData();
+ formData.append("file", file);
+
+ // 예: 서버로 업로드 요청
+ // const response = await fetch("https://your-server.com/upload", {
+ // method: "POST",
+ // body: formData,
+ // });
+
+ // if (response.ok) {
+ // const result = await response.json();
+ // return result.url; // 업로드된 이미지 URL 반환
+ // } else {
+ // console.error("Image upload failed");
+ // return null;
+ // }
+
+ await runWithDelay();
+
+ return "https://www.kookmin.ac.kr/content/05sub/style0005/images/sub/ui_5_col_image_2.jpg";
+ } catch (error) {
+ console.error("Error uploading image:", error);
+ return null;
+ }
+};
diff --git a/src/pages/api/hello.ts b/src/pages/api/hello.ts
deleted file mode 100644
index ea77e8f..0000000
--- a/src/pages/api/hello.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
-import type { NextApiRequest, NextApiResponse } from "next";
-
-type Data = {
- name: string;
-};
-
-export default function handler(
- req: NextApiRequest,
- res: NextApiResponse,
-) {
- res.status(200).json({ name: "John Doe" });
-}
diff --git a/src/pages/api/login.ts b/src/pages/api/login.ts
new file mode 100644
index 0000000..0fc55ed
--- /dev/null
+++ b/src/pages/api/login.ts
@@ -0,0 +1,5 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+ res.status(200).json({ id: "QWE", name: "ASD" });
+}
diff --git a/src/pages/api/logout.ts b/src/pages/api/logout.ts
new file mode 100644
index 0000000..0fc55ed
--- /dev/null
+++ b/src/pages/api/logout.ts
@@ -0,0 +1,5 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+ res.status(200).json({ id: "QWE", name: "ASD" });
+}
diff --git a/src/services/auth.ts b/src/services/auth.ts
new file mode 100644
index 0000000..6264b5b
--- /dev/null
+++ b/src/services/auth.ts
@@ -0,0 +1,13 @@
+import axios from "axios";
+
+const BASE_URL = "/api";
+// const BASE_URL = "백엔드 api";
+
+export const login = async (email: string, password: string) => {
+ const response = await axios.post(`${BASE_URL}/login`, { email, password });
+ return response.data;
+};
+
+export const logout = async () => {
+ await axios.post(`${BASE_URL}/logout`);
+};
diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts
new file mode 100644
index 0000000..7b32f82
--- /dev/null
+++ b/src/stores/authStore.ts
@@ -0,0 +1,15 @@
+import { create } from "zustand";
+
+interface AuthState {
+ isLoggedIn: boolean;
+ user: { id: string; name: string } | null;
+ login: (user: { id: string; name: string }) => void;
+ logout: () => void;
+}
+
+export const useAuthStore = create((set) => ({
+ isLoggedIn: false,
+ user: null,
+ login: (user) => set({ isLoggedIn: true, user }),
+ logout: () => set({ isLoggedIn: false, user: null }),
+}));
diff --git a/src/styles/globals.css b/src/styles/globals.css
index b666b5a..d94d416 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -3,49 +3,3 @@
padding: 0;
margin: 0;
}
-.w-md-editor {
- height: 600px !important;
- @media (max-width: 695px) {
- height: 640px !important;
- }
-}
-
-.w-md-editor-toolbar {
- height: 40px !important;
- @media (max-width: 695px) {
- height: 80px !important;
- }
- button {
- width: 36px;
- height: 36px !important;
- font-size: 16px;
- /* padding: 8px; */
- &svg {
- width: 20px;
- height: 20px;
- }
- }
- button svg {
- width: 20px;
- height: 20px;
- }
-}
-.w-md-editor-toolbar-divider {
- margin: 0px 0px 0px 0px !important;
- height: 25px !important;
-}
-
-.w-md-editor-content {
- height: 560px !important;
-}
-
-/* .wmde-markdown-color {
- font-size: 20px;
-} */
-.w-md-editor .w-md-editor-text {
- font-size: 20px !important;
- line-height: 1.5 !important;
-}
-.wmde-markdown {
- font-size: 20px !important;
-}
diff --git a/src/styles/mdeditor.css b/src/styles/mdeditor.css
new file mode 100644
index 0000000..54c9731
--- /dev/null
+++ b/src/styles/mdeditor.css
@@ -0,0 +1,49 @@
+.w-md-editor {
+ height: 600px !important;
+ @media (max-width: 695px) {
+ height: 640px !important;
+ }
+}
+
+.w-md-editor-toolbar {
+ height: 40px !important;
+ @media (max-width: 695px) {
+ height: 80px !important;
+ }
+ button {
+ width: 36px;
+ height: 36px !important;
+ font-size: 16px;
+ /* padding: 8px; */
+ &svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+ button svg {
+ width: 20px;
+ height: 20px;
+ }
+}
+.w-md-editor-toolbar-divider {
+ margin: 0px 0px 0px 0px !important;
+ height: 25px !important;
+}
+
+.w-md-editor-content {
+ height: 560px !important;
+}
+
+.w-md-editor-text {
+ font-size: 21px !important;
+ line-height: 26px !important;
+}
+.wmde-markdown {
+ font-size: 20px !important;
+}
+.w-md-editor-fullscreen {
+ height: 100vh !important;
+ .w-md-editor-content {
+ height: 100vh !important;
+ }
+}