From ec4bdfa088e8869149e13731bd1eab741741103a Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 25 Oct 2023 14:46:27 +0900 Subject: [PATCH 01/22] =?UTF-8?q?chore:=20tabSize=202=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/.prettierrc.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/.prettierrc.json b/client/.prettierrc.json index 1b9fe48..531faae 100644 --- a/client/.prettierrc.json +++ b/client/.prettierrc.json @@ -1,13 +1,13 @@ { - "printWidth": 120, - "tabWidth": 2, - "useTabs": false, - "semi": true, - "singleQuote": true, - "trailingComma": "all", - "bracketSpacing": true, - "jsxBracketSameLine": true, - "arrowParens": "avoid", - "vueIndentScriptAndStyle": false, - "endOfLine": "auto" - } \ No newline at end of file + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "jsxBracketSameLine": true, + "arrowParens": "avoid", + "vueIndentScriptAndStyle": false, + "endOfLine": "auto" +} \ No newline at end of file From bbd252b4a84146a5bf325d19fff7e15e84c50b9a Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 25 Oct 2023 14:51:33 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20=ED=83=80=EC=9E=85=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 46 +++++++++++++++++++++++++--------------- client/package.json | 6 +++++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 0638e12..f4afb64 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15,6 +15,10 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.6", + "@types/node": "^20.8.8", + "@types/react": "^18.2.31", + "@types/react-dom": "^18.2.14", "axios": "^1.5.0", "chartjs-react": "^4.1.0", "eslint-plugin-jest-dom": "^5.1.0", @@ -28,6 +32,7 @@ "react-router-dom": "^6.15.0", "react-scripts": "5.0.1", "redux": "^4.2.1", + "typescript": "^5.2.2", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -4172,9 +4177,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "version": "29.5.6", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.6.tgz", + "integrity": "sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -4436,9 +4441,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" + "version": "20.8.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.8.tgz", + "integrity": "sha512-YRsdVxq6OaLfmR9Hy816IMp33xOBjfyOgUd77ehqg96CFywxAPbDbXvAsuN2KVg2HOT8Eh6uAfU+l4WffwPVrQ==", + "dependencies": { + "undici-types": "~5.25.1" + } }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4471,9 +4479,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.2.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.31.tgz", + "integrity": "sha512-c2UnPv548q+5DFh03y8lEDeMfDwBn9G3dRwfkrxQMo/dOtRHUUO57k6pHvBIfH/VF4Nh+98mZ5aaSe+2echD5g==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4481,9 +4489,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", + "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", "dependencies": { "@types/react": "*" } @@ -16939,16 +16947,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -16965,6 +16972,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/client/package.json b/client/package.json index 3cf70a8..7bda281 100644 --- a/client/package.json +++ b/client/package.json @@ -1,4 +1,3 @@ - { "name": "client", "version": "0.1.0", @@ -11,6 +10,10 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.6", + "@types/node": "^20.8.8", + "@types/react": "^18.2.31", + "@types/react-dom": "^18.2.14", "axios": "^1.5.0", "chartjs-react": "^4.1.0", "eslint-plugin-jest-dom": "^5.1.0", @@ -24,6 +27,7 @@ "react-router-dom": "^6.15.0", "react-scripts": "5.0.1", "redux": "^4.2.1", + "typescript": "^5.2.2", "web-vitals": "^2.1.4" }, "scripts": { From e2873385fabad8d379ad04341789e8a3046f4206 Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 25 Oct 2023 14:53:47 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20tsconfig=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tsconfig.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 client/tsconfig.json diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..6acd9bf --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "allowJs": true, + "target": "ES5", + "outDir": "./dist", + "moduleResolution": "Node", + "lib": ["ES2015", "DOM", "DOM.Iterable"] + }, + "include": ["./src/**/*"], + "exclude": ["node_modules", "dist"] +} From 3483287ad41b3241d69be875fcaab92be2f6f006 Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 25 Oct 2023 15:40:37 +0900 Subject: [PATCH 04/22] =?UTF-8?q?chore=20:=20"jsx":=20"react"=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/tsconfig.json b/client/tsconfig.json index 6acd9bf..08c0c16 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,10 +1,13 @@ { "compilerOptions": { + "jsx": "react", + "baseUrl": "./src", "allowJs": true, "target": "ES5", "outDir": "./dist", "moduleResolution": "Node", - "lib": ["ES2015", "DOM", "DOM.Iterable"] + "lib": ["ES2015", "DOM", "DOM.Iterable"], + "esModuleInterop": true }, "include": ["./src/**/*"], "exclude": ["node_modules", "dist"] From 3c18361349c2590ebd4986468021fca73217e40d Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 25 Oct 2023 15:41:16 +0900 Subject: [PATCH 05/22] =?UTF-8?q?refactor=20:=20NavBar=20TS=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/jsconfig.json | 10 - .../src/components/{NavBar.jsx => NavBar.tsx} | 49 +++-- client/src/pages/AllBoardPage.jsx | 27 +-- client/src/pages/AuthBoardPage.jsx | 27 +-- client/src/pages/AuthDetailPage.jsx | 175 +++++++++--------- client/src/pages/EditerPage.jsx | 29 ++- client/src/pages/EnvBoardPage.jsx | 27 +-- client/src/pages/FreeBoardPage.jsx | 27 +-- client/src/pages/FreeDetailPage.jsx | 171 +++++++++-------- client/src/pages/MyPageInfo.jsx | 145 ++++++--------- client/src/pages/MyPageMain.jsx | 72 ++++--- client/src/pages/MyPost.jsx | 71 +++---- 12 files changed, 379 insertions(+), 451 deletions(-) delete mode 100644 client/jsconfig.json rename client/src/components/{NavBar.jsx => NavBar.tsx} (64%) diff --git a/client/jsconfig.json b/client/jsconfig.json deleted file mode 100644 index d16f428..0000000 --- a/client/jsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "./src", - "paths": { - "*": ["*"], - "react": ["node_modules/react"], - "react-dom": ["node_modules/react-dom"] - } - } -} \ No newline at end of file diff --git a/client/src/components/NavBar.jsx b/client/src/components/NavBar.tsx similarity index 64% rename from client/src/components/NavBar.jsx rename to client/src/components/NavBar.tsx index 02c6f88..fbdf03a 100644 --- a/client/src/components/NavBar.jsx +++ b/client/src/components/NavBar.tsx @@ -4,24 +4,22 @@ import { useNavigate } from 'react-router-dom'; import { selectActiveMenu, setActiveMenu } from '../store/menuSlice.js'; import '../styles/NavBar.css'; -const NavBar = ( ) => { +const NavBar = () => { const activeMenu = useSelector(selectActiveMenu); const dispatch = useDispatch(); const navigate = useNavigate(); const accessToken = localStorage.getItem('accessToken'); - const [ isLoggedIn, setIsLoggedIn] = useState(!accessToken); - + const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); useEffect(() => { if (accessToken) { - setIsLoggedIn(true) + setIsLoggedIn(true); return; } - setIsLoggedIn(false) - } - , [accessToken]); + setIsLoggedIn(false); + }, [accessToken]); const BOARD_MENU = { ALL: '전체 글 보기', @@ -29,16 +27,16 @@ const NavBar = ( ) => { AUTH: '인증 게시판', ENV: '환경 정보 게시판', }; - + const MY_MENU = { MY_PAGE: '내가 쓴 글', MY_INFO: '내 정보', }; - const handleMenuClick = (menuName) => { + const handleMenuClick = (menuName: string) => { dispatch(setActiveMenu(menuName)); - const menuRoutes = { + const menuRoutes: { [key: string]: string } = { [BOARD_MENU.ALL]: '/', [BOARD_MENU.FREE]: '/free', [BOARD_MENU.AUTH]: '/auth', @@ -55,30 +53,27 @@ const NavBar = ( ) => { return (
게시판
- {Object.values(BOARD_MENU).map((menuName) => ( + {Object.values(BOARD_MENU).map(menuName => ( ))} {/* isLoggedIn이 true일 때만 MY_MENU 렌더링합니다. */} - {isLoggedIn && ( -
마이 페이지
- )} - {isLoggedIn && Object.values(MY_MENU).map((menuName) => ( - - ))} + {isLoggedIn &&
마이 페이지
} + {isLoggedIn && + Object.values(MY_MENU).map(menuName => ( + + ))}
); -} +}; -export default NavBar; \ No newline at end of file +export default NavBar; diff --git a/client/src/pages/AllBoardPage.jsx b/client/src/pages/AllBoardPage.jsx index 8659bca..896ee61 100644 --- a/client/src/pages/AllBoardPage.jsx +++ b/client/src/pages/AllBoardPage.jsx @@ -1,21 +1,22 @@ import React from 'react'; import '../styles/Button.css'; import PostList from '../components/PostList.jsx'; -import NavBar from '../components/NavBar.jsx'; +import NavBar from '../components/NavBar.tsx'; const AllBoardPage = () => { return ( - <> -
-
- - -
- -
+ <> +
+
- - ) -} +
+ +
+ +
+
+ + ); +}; -export default AllBoardPage; \ No newline at end of file +export default AllBoardPage; diff --git a/client/src/pages/AuthBoardPage.jsx b/client/src/pages/AuthBoardPage.jsx index adfdbd7..5ff277b 100644 --- a/client/src/pages/AuthBoardPage.jsx +++ b/client/src/pages/AuthBoardPage.jsx @@ -2,23 +2,24 @@ import React from 'react'; import '../styles/Button.css'; import '../components/PostList.jsx'; import AuthPostList from '../components/AuthPostList.jsx'; -import NavBar from '../components/NavBar.jsx'; +import NavBar from '../components/NavBar.tsx'; const AuthBoardPage = () => { - return ( <> -
-
- -
- + +
+
+
+ +
+ +
-
-
- - ) -} +
+ + ); +}; -export default AuthBoardPage; \ No newline at end of file +export default AuthBoardPage; diff --git a/client/src/pages/AuthDetailPage.jsx b/client/src/pages/AuthDetailPage.jsx index c34f5fb..2a7d021 100644 --- a/client/src/pages/AuthDetailPage.jsx +++ b/client/src/pages/AuthDetailPage.jsx @@ -3,15 +3,8 @@ import { useParams } from 'react-router-dom'; import jwtDecode from 'jwt-decode'; import '../styles/Button.css'; import '../styles/BoardDetailPage.css'; -import NavBar from '../components/NavBar.jsx'; -import { - getPost, - getUser, - getComment, - postComment, - getVote, - patchVote, -} from '../api/api.js'; +import NavBar from '../components/NavBar.tsx'; +import { getPost, getUser, getComment, postComment, getVote, patchVote } from '../api/api.js'; const AuthDetailPage = () => { const { postId, userId, voteId } = useParams(); @@ -20,7 +13,7 @@ const AuthDetailPage = () => { const [user, setUser] = useState({}); const [loggedInUserId, setLoggedInUserId] = useState({}); - + const accessToken = localStorage.getItem('accessToken'); useEffect(() => { if (accessToken) { @@ -33,7 +26,7 @@ const AuthDetailPage = () => { }, [accessToken]); const [vote, setVote] = useState({}); - const [alreadyLiked, setAlreadyLiked] = useState(false); + const [alreadyLiked, setAlreadyLiked] = useState(false); const [liked, setLiked] = useState(alreadyLiked); const [commentText, setCommentText] = useState(''); @@ -43,17 +36,16 @@ const AuthDetailPage = () => { const intersectionRef = useRef(null); - const [ isLoggedIn, setIsLoggedIn] = useState(!accessToken); - + const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); + useEffect(() => { if (accessToken) { - setIsLoggedIn(true) + setIsLoggedIn(true); return; } - setIsLoggedIn(false) - } - , [accessToken]); + setIsLoggedIn(false); + }, [accessToken]); useEffect(() => { // 로컬 스토리지에서 사용자의 좋아요 여부를 가져옴 @@ -65,7 +57,7 @@ const AuthDetailPage = () => { // 페이지 로드 시 서버에서 투표 정보를 가져옴 getVote(postId, userId) - .then((response) => { + .then(response => { if (response.status === 200) { const voteData = response.data; console.log(voteData); @@ -74,7 +66,7 @@ const AuthDetailPage = () => { setVote(voteData); } }) - .catch((error) => { + .catch(error => { console.error('투표 정보 가져오기 오류:', error); }); }, [postId, userId]); @@ -83,7 +75,7 @@ const AuthDetailPage = () => { try { // API 요청 보내기 (patchVote 함수를 사용하여 요청 보냄) const response = await patchVote(postId, userId, voteId); - console.log("patch한후: ", response.data); + console.log('patch한후: ', response.data); // API 요청이 성공적으로 완료된 경우에만 UI를 업데이트합니다. if (response.status === 200) { @@ -114,8 +106,7 @@ const AuthDetailPage = () => { } }; - - const handleCommentTextChange = (event) => { + const handleCommentTextChange = event => { setCommentText(event.target.value); }; @@ -133,7 +124,7 @@ const AuthDetailPage = () => { const response = await postComment(postId, loggedInUserId, commentText); console.log('댓글 작성 완료:', response.data); setCommentText(''); - + // 페이지를 새로고침 window.location.reload(); } catch (error) { @@ -144,82 +135,81 @@ const AuthDetailPage = () => { useEffect(() => { // 포스트 데이터 가져오기 getPost(postId) - .then((response) => { + .then(response => { setPost(response.data); }) - .catch((error) => { + .catch(error => { console.error('포스트 데이터 가져오기 오류:', error); }); // 유저 데이터 가져오기 getUser(userId) - .then((response) => { + .then(response => { console.log(response.data); setUser(response.data); }) - .catch((error) => { + .catch(error => { console.error('유저 데이터 가져오기 오류:', error); }); - // 댓글 데이터 가져오기 getComment(postId) - .then((response) => { - const sortedComments = response.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); - }); - const commentsWithUser = sortedComments.map((comment) => ({ - ...comment, - user: null, - })); - - // 사용자 정보를 가져온 후에 댓글 객체를 갱신하고 상태에 설정 - const updateUserComments = async () => { - for (let i = 0; i < commentsWithUser.length; i++) { - const comment = commentsWithUser[i]; - try { - const userResponse = await getUser(comment.userId); - const user = userResponse.data; - const updatedComment = { - ...comment, - user, - }; - commentsWithUser[i] = updatedComment; - } catch (error) { - console.error('댓글 사용자 데이터 가져오기 오류:', error); + .then(response => { + const sortedComments = response.data.sort((a, b) => { + return new Date(b.createdAt) - new Date(a.createdAt); + }); + const commentsWithUser = sortedComments.map(comment => ({ + ...comment, + user: null, + })); + + // 사용자 정보를 가져온 후에 댓글 객체를 갱신하고 상태에 설정 + const updateUserComments = async () => { + for (let i = 0; i < commentsWithUser.length; i++) { + const comment = commentsWithUser[i]; + try { + const userResponse = await getUser(comment.userId); + const user = userResponse.data; + const updatedComment = { + ...comment, + user, + }; + commentsWithUser[i] = updatedComment; + } catch (error) { + console.error('댓글 사용자 데이터 가져오기 오류:', error); + } } - } - setVisibleComments(commentsWithUser.slice(0, 10)); - }; + setVisibleComments(commentsWithUser.slice(0, 10)); + }; - updateUserComments(); - }) - .catch((error) => { - console.error('댓글 데이터 가져오기 오류:', error); - }); -}, [postId, userId]); + updateUserComments(); + }) + .catch(error => { + console.error('댓글 데이터 가져오기 오류:', error); + }); + }, [postId, userId]); useEffect(() => { - const handleIntersect = (entries) => { + const handleIntersect = entries => { if (entries[0].isIntersecting) { setTimeout(() => { const endVisibleIndex = visibleComments.length; const newVisibleComments = [...visibleComments, ...allComments.slice(endVisibleIndex, endVisibleIndex + 10)]; setVisibleComments(newVisibleComments); - },); + }); } }; - + const observer = new IntersectionObserver(handleIntersect, { root: null, rootMargin: '0px', threshold: 0.1, }); - + if (intersectionRef.current) { observer.observe(intersectionRef.current); } - + return () => { observer.disconnect(); }; @@ -228,45 +218,46 @@ const AuthDetailPage = () => { return ( <> -
+
-
-
- {`${post.postId}`} +
+
+ {`${post.postId}`}
-
+

{post.title}

-

{user.grade} {user.userName}

+

+ {user.grade} {user.userName} +

{new Date(post.createdAt).toLocaleDateString()}

-

{post.body}

+

{post.body}

{/* */}
- -
- {isLoggedIn && ( -
- - +
+ {isLoggedIn && ( +
+ +
- )} - {visibleComments.map((comment) => ( -
+ )} + {visibleComments.map(comment => ( +

{comment.user.grade} {comment.user.userName} @@ -275,10 +266,10 @@ const AuthDetailPage = () => {

))} -
+
); -} +}; -export default AuthDetailPage; \ No newline at end of file +export default AuthDetailPage; diff --git a/client/src/pages/EditerPage.jsx b/client/src/pages/EditerPage.jsx index f31552f..f62f880 100644 --- a/client/src/pages/EditerPage.jsx +++ b/client/src/pages/EditerPage.jsx @@ -1,41 +1,40 @@ -import React, { useState } from "react"; -import { PostEditerWithImage, PostEditer } from "../components/PostEditer.jsx"; +import React, { useState } from 'react'; +import { PostEditerWithImage, PostEditer } from '../components/PostEditer.jsx'; import '../styles/EditerPage.css'; -import NavBar from "components/NavBar.jsx"; +import NavBar from 'components/NavBar.tsx'; function EditerPage() { - const [selectFreeBoard, setSelectFreeBoard] = useState(true); const [selectPhotoBoard, setSelectPhotoBoard] = useState(false); const handleSelectFreeBoard = () => { setSelectFreeBoard(selectFreeBoard => true); setSelectPhotoBoard(selectPhotoBoard => false); - } + }; const handleSelectPhotoBoard = () => { setSelectFreeBoard(selectFreeBoard => false); setSelectPhotoBoard(selectPhotoBoard => true); - } + }; return ( <> - +
- - + +
- {selectFreeBoard ? : } - + {selectFreeBoard ? : }
- - - ); } -export default EditerPage; \ No newline at end of file +export default EditerPage; diff --git a/client/src/pages/EnvBoardPage.jsx b/client/src/pages/EnvBoardPage.jsx index 537cd61..1237945 100644 --- a/client/src/pages/EnvBoardPage.jsx +++ b/client/src/pages/EnvBoardPage.jsx @@ -2,23 +2,24 @@ import React from 'react'; import '../styles/Button.css'; import '../components/PostList.jsx'; import EnvPostList from '../components/EnvPostList.jsx'; -import NavBar from '../components/NavBar.jsx'; +import NavBar from '../components/NavBar.tsx'; const EnvBoardPage = () => { - return ( <> -
-
- -
- + +
+
+
+ +
+ +
-
-
- - ) -} +
+ + ); +}; -export default EnvBoardPage; \ No newline at end of file +export default EnvBoardPage; diff --git a/client/src/pages/FreeBoardPage.jsx b/client/src/pages/FreeBoardPage.jsx index 6728bfe..fa7b2b8 100644 --- a/client/src/pages/FreeBoardPage.jsx +++ b/client/src/pages/FreeBoardPage.jsx @@ -1,21 +1,22 @@ import React from 'react'; import '../styles/Button.css'; import PostList from '../components/PostList.jsx'; -import NavBar from '../components/NavBar.jsx'; +import NavBar from '../components/NavBar.tsx'; const FreeBoardPage = () => { return ( - <> -
-
- - -
- -
+ <> +
+
- - ) -} +
+ +
+ +
+
+ + ); +}; -export default FreeBoardPage; \ No newline at end of file +export default FreeBoardPage; diff --git a/client/src/pages/FreeDetailPage.jsx b/client/src/pages/FreeDetailPage.jsx index 990c70a..4770517 100644 --- a/client/src/pages/FreeDetailPage.jsx +++ b/client/src/pages/FreeDetailPage.jsx @@ -3,15 +3,8 @@ import { useParams } from 'react-router-dom'; import jwtDecode from 'jwt-decode'; import '../styles/Button.css'; import '../styles/BoardDetailPage.css'; -import NavBar from '../components/NavBar.jsx'; -import { - getPost, - getUser, - getComment, - postComment, - getVote, - patchVote, -} from 'api/api.js'; +import NavBar from '../components/NavBar.tsx'; +import { getPost, getUser, getComment, postComment, getVote, patchVote } from 'api/api.js'; const FreeDetailPage = () => { const { postId, userId, voteId } = useParams(); @@ -35,7 +28,7 @@ const FreeDetailPage = () => { const [vote, setVote] = useState({}); // 해당 게시글에 이미 좋아요를 했었는지 여부를 저장하는 상태 // 새로고침했을때 이전 기록을 로컬스토리지에서 가져오는 변수 - const [alreadyLiked, setAlreadyLiked] = useState(false); + const [alreadyLiked, setAlreadyLiked] = useState(false); // 좋아요 버튼 클릭 여부를 저장하는 상태 const [liked, setLiked] = useState(alreadyLiked); @@ -47,17 +40,16 @@ const FreeDetailPage = () => { const intersectionRef = useRef(null); - const [ isLoggedIn, setIsLoggedIn] = useState(!accessToken); - + const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); + useEffect(() => { if (accessToken) { - setIsLoggedIn(true) + setIsLoggedIn(true); return; } - setIsLoggedIn(false) - } - , [accessToken]); + setIsLoggedIn(false); + }, [accessToken]); useEffect(() => { // 로컬 스토리지에서 사용자의 좋아요 여부를 가져옴 @@ -69,7 +61,7 @@ const FreeDetailPage = () => { // 페이지 로드 시 서버에서 투표 정보를 가져옴 getVote(postId, userId) - .then((response) => { + .then(response => { if (response.status === 200) { const voteData = response.data; console.log(voteData); @@ -78,7 +70,7 @@ const FreeDetailPage = () => { setVote(voteData); } }) - .catch((error) => { + .catch(error => { console.error('투표 정보 가져오기 오류:', error); }); }, [postId, userId]); @@ -87,7 +79,7 @@ const FreeDetailPage = () => { try { // API 요청 보내기 (patchVote 함수를 사용하여 요청 보냄) const response = await patchVote(postId, userId, voteId); - console.log("patch한후: ", response.data); + console.log('patch한후: ', response.data); // API 요청이 성공적으로 완료된 경우에만 UI를 업데이트합니다. if (response.status === 200) { @@ -110,7 +102,7 @@ const FreeDetailPage = () => { setLiked(!liked); // 로컬 스토리지에 좋아요 상태 저장 - localStorage.setItem(`alreadyLikeState_${postId}_${userId}`, alreadyLiked);//JSON.stringify(alreadyLiked)); + localStorage.setItem(`alreadyLikeState_${postId}_${userId}`, alreadyLiked); //JSON.stringify(alreadyLiked)); } else { console.error('좋아요 버튼 기능 오류'); } @@ -119,7 +111,7 @@ const FreeDetailPage = () => { } }; - const handleCommentTextChange = (event) => { + const handleCommentTextChange = event => { setCommentText(event.target.value); }; @@ -136,7 +128,7 @@ const FreeDetailPage = () => { const response = await postComment(postId, loggedInUserId, commentText); console.log('댓글 작성 완료:', response.data); setCommentText(''); - + // 페이지를 새로고침 window.location.reload(); } catch (error) { @@ -148,15 +140,15 @@ const FreeDetailPage = () => { // const response = await postComment(postId, loggedInUserId, commentText); // console.log('댓글 작성 완료:', response.data); // setCommentText(''); - + // // 새로 작성된 댓글과 사용자 정보를 가져오기 // const newComment = response.data; // const userResponse = await getUser(newComment.userId); // const user = userResponse.data; - + // // 새로 작성된 댓글을 기존 댓글 상단에 추가 // setAllComments((prevComments) => [newComment, ...prevComments]); - + // } catch (error) { // console.error('댓글 작성 오류:', error); // } @@ -165,80 +157,80 @@ const FreeDetailPage = () => { useEffect(() => { // 포스트 데이터 가져오기 getPost(postId) - .then((response) => { + .then(response => { setPost(response.data); }) - .catch((error) => { + .catch(error => { console.error('포스트 데이터 가져오기 오류:', error); }); // 유저 데이터 가져오기 getUser(userId) - .then((response) => { + .then(response => { setUser(response.data); }) - .catch((error) => { + .catch(error => { console.error('유저 데이터 가져오기 오류:', error); }); // 댓글 데이터 가져오기 getComment(postId) - .then((response) => { - const sortedComments = response.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); - }); - const commentsWithUser = sortedComments.map((comment) => ({ - ...comment, - user: null, - })); - - // 사용자 정보를 가져온 후에 댓글 객체를 갱신하고 상태에 설정 - const updateUserComments = async () => { - for (let i = 0; i < commentsWithUser.length; i++) { - const comment = commentsWithUser[i]; - try { - const userResponse = await getUser(comment.userId); - const user = userResponse.data; - const updatedComment = { - ...comment, - user, - }; - commentsWithUser[i] = updatedComment; - } catch (error) { - console.error('댓글 사용자 데이터 가져오기 오류:', error); + .then(response => { + const sortedComments = response.data.sort((a, b) => { + return new Date(b.createdAt) - new Date(a.createdAt); + }); + const commentsWithUser = sortedComments.map(comment => ({ + ...comment, + user: null, + })); + + // 사용자 정보를 가져온 후에 댓글 객체를 갱신하고 상태에 설정 + const updateUserComments = async () => { + for (let i = 0; i < commentsWithUser.length; i++) { + const comment = commentsWithUser[i]; + try { + const userResponse = await getUser(comment.userId); + const user = userResponse.data; + const updatedComment = { + ...comment, + user, + }; + commentsWithUser[i] = updatedComment; + } catch (error) { + console.error('댓글 사용자 데이터 가져오기 오류:', error); + } } - } - setVisibleComments(commentsWithUser.slice(0, 10)); - }; + setVisibleComments(commentsWithUser.slice(0, 10)); + }; - updateUserComments(); - }) - .catch((error) => { - console.error('댓글 데이터 가져오기 오류:', error); - }); -}, [postId, userId]); + updateUserComments(); + }) + .catch(error => { + console.error('댓글 데이터 가져오기 오류:', error); + }); + }, [postId, userId]); useEffect(() => { - const handleIntersect = (entries) => { + const handleIntersect = entries => { if (entries[0].isIntersecting) { setTimeout(() => { const endVisibleIndex = visibleComments.length; const newVisibleComments = [...visibleComments, ...allComments.slice(endVisibleIndex, endVisibleIndex + 10)]; setVisibleComments(newVisibleComments); - },); + }); } }; - + const observer = new IntersectionObserver(handleIntersect, { root: null, rootMargin: '0px', threshold: 0.1, }); - + if (intersectionRef.current) { observer.observe(intersectionRef.current); } - + return () => { observer.disconnect(); }; @@ -246,42 +238,45 @@ const FreeDetailPage = () => { return ( <> -
- -
+
+ +
+
-
+

{post.title}

-

{user.grade} {user.userName}

+

+ {user.grade} {user.userName} +

{new Date(post.createdAt).toLocaleDateString()}

-

{post.body}

+

{post.body}

{/* */}
-
- {isLoggedIn && ( -
- - +
+ {isLoggedIn && ( +
+ +
- )} - {visibleComments.map((comment) => ( -
+ )} + {visibleComments.map(comment => ( +

{comment.user.grade} {comment.user.userName} diff --git a/client/src/pages/MyPageInfo.jsx b/client/src/pages/MyPageInfo.jsx index 7b2495a..09e1912 100644 --- a/client/src/pages/MyPageInfo.jsx +++ b/client/src/pages/MyPageInfo.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { setActiveMenu } from '../store/menuSlice.js'; -import NavBar from '../components/NavBar.jsx'; +import NavBar from '../components/NavBar.tsx'; import Modal from '../components/Modal.jsx'; import '../styles/Button.css'; import '../styles/MyPageInfo.css'; @@ -39,7 +39,7 @@ const MyPageInfo = () => { if (agreed) { deleteUser(userId) .then(() => { - console.log("회원탈퇴가 완료되었습니다."); + console.log('회원탈퇴가 완료되었습니다.'); // 로그아웃 상태로 변경 localStorage.removeItem('accessToken'); @@ -48,7 +48,7 @@ const MyPageInfo = () => { dispatch(setActiveMenu('전체 글 보기')); navigate('/'); }) - .catch((error) => { + .catch(error => { console.error('회원탈퇴 오류:', error); // 오류 처리 }); @@ -59,14 +59,13 @@ const MyPageInfo = () => { } }; - - const handleImageChange = (e) => { + const handleImageChange = e => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); - reader.onload = (event) => { + reader.onload = event => { const imageUrl = event.target.result; setPreviewImageUrl(imageUrl); setSelectedImage(file); @@ -77,111 +76,90 @@ const MyPageInfo = () => { }; const handleButtonClick = () => { alert('서비스 준비중입니다.'); - } + }; useEffect(() => { if (accessToken) { - setIsLoggedIn(true) + setIsLoggedIn(true); return; } - setIsLoggedIn(false) - } - , [accessToken]); + setIsLoggedIn(false); + }, [accessToken]); return ( <> -

-
-

내 정보

-
- +
+ +
+
+

내 정보

+
- +
- +
- +
-
-
- -
- -
- - +
+ +
+
- -
- - +
+ +
+ + -
- + {showModal && (

탈퇴 안내

-

회원탈퇴를 신청하기 전에 안내 사항을 꼭 확인해주세요.


-

✔ 사용하고 계신 아이디는 탈퇴 시 복구가 불가능합니다.


-

- ✔ 탈퇴 후 회원정보 및 개인형 서비스 이용기록은 모두 삭제됩니다. -
-  삭제된 데이터는 복구되지 않습니다. 삭제되는 내용을 확인하시고 -
-  필요한 데이터는 미리 백업을 해주세요. -


-

- ✔ 탈퇴 후에도 게시판형 서비스에 등록한 게시물은 그대로 남아 있습니다. -
-  게시판형 서비스에 남아 있는 게시글은 탈퇴 후 삭제할 수 없습니다. -


-

- - 안내 사항을 모두 확인하였으며, 이에 동의합니다. -

+

회원탈퇴를 신청하기 전에 안내 사항을 꼭 확인해주세요.

+
+

✔ 사용하고 계신 아이디는 탈퇴 시 복구가 불가능합니다.

+
+

+ ✔ 탈퇴 후 회원정보 및 개인형 서비스 이용기록은 모두 삭제됩니다. +
+  삭제된 데이터는 복구되지 않습니다. 삭제되는 내용을 확인하시고 +
+  필요한 데이터는 미리 백업을 해주세요. +

+
+

+ ✔ 탈퇴 후에도 게시판형 서비스에 등록한 게시물은 그대로 남아 있습니다. +
+  게시판형 서비스에 남아 있는 게시글은 탈퇴 후 삭제할 수 없습니다. +

+
+

+ + 안내 사항을 모두 확인하였으며, 이에 동의합니다. +

} onCancel={handleCloseModal} @@ -192,7 +170,6 @@ const MyPageInfo = () => {
); - }; -export default MyPageInfo; \ No newline at end of file +export default MyPageInfo; diff --git a/client/src/pages/MyPageMain.jsx b/client/src/pages/MyPageMain.jsx index a792a87..7538e09 100644 --- a/client/src/pages/MyPageMain.jsx +++ b/client/src/pages/MyPageMain.jsx @@ -4,16 +4,15 @@ import '../styles/MyPageMain.css'; import Pagination from 'components/Pagination.jsx'; import jwtDecode from 'jwt-decode'; import { instance } from 'api/api'; -import NavBar from 'components/NavBar.jsx'; +import NavBar from 'components/NavBar.tsx'; import { Link } from 'react-router-dom'; const MyPageMain = () => { - const accessToken = localStorage.getItem('accessToken'); const decodedToken = jwtDecode(accessToken); const userId = decodedToken.userId; - const [ userData, setUserData ] = useState([]); + const [userData, setUserData] = useState([]); useEffect(() => { async function getUserData() { @@ -31,39 +30,39 @@ const MyPageMain = () => { getUserData(); }, []); - const [ filter, setFilter ] = useState('all'); - const [ currentPage, setCurrentPage ] = useState(1); + const [filter, setFilter] = useState('all'); + const [currentPage, setCurrentPage] = useState(1); - const filteredPosts = userData.filter((post) => { - if ( filter === 'all') return true; - if (filter === 'public') return post.open === "true"; - return post.open === "false"; + const filteredPosts = userData.filter(post => { + if (filter === 'all') return true; + if (filter === 'public') return post.open === 'true'; + return post.open === 'false'; }); - const handleFilterChange = ( newFilter ) => { + const handleFilterChange = newFilter => { setFilter(newFilter); setCurrentPage(1); - } + }; const viewType = { ALL: '전체', PUBLIC: '공개', - PRIVATE: '비공개' - } + PRIVATE: '비공개', + }; + + const [selectedButton, setSelectedButton] = useState(viewType.ALL); + const handleButtonClick = buttonName => { + setSelectedButton(buttonName); + }; - const [ selectedButton, setSelectedButton ] = useState(viewType.ALL); - const handleButtonClick = (buttonName) => { - setSelectedButton(buttonName) - } - const postsPerPage = 5; - const startPostIndex = ( currentPage - 1 ) * postsPerPage; + const startPostIndex = (currentPage - 1) * postsPerPage; const endPostIndex = Math.min(startPostIndex + postsPerPage); const currentPosts = filteredPosts.slice(startPostIndex, endPostIndex); return ( <> - +

내가 쓴 글

    @@ -73,8 +72,7 @@ const MyPageMain = () => { onClick={() => { handleButtonClick(viewType.ALL); handleFilterChange('all'); - }} - > + }}> {viewType.ALL} @@ -84,8 +82,7 @@ const MyPageMain = () => { onClick={() => { handleButtonClick(viewType.PUBLIC); handleFilterChange('public'); - }} - > + }}> {viewType.PUBLIC} @@ -93,28 +90,23 @@ const MyPageMain = () => {
-
- {currentPosts.map((post) => ( +
+ {currentPosts.map(post => (
-
+
- - {post.title} - + {post.title}
-
{ - new Date(post.createdAt).toLocaleDateString() - }
+
{new Date(post.createdAt).toLocaleDateString()}
-

{ post.body }

+

{post.body}

))} {
- ) -} + ); +}; -export default MyPageMain; \ No newline at end of file +export default MyPageMain; diff --git a/client/src/pages/MyPost.jsx b/client/src/pages/MyPost.jsx index 132dba9..bf394bb 100644 --- a/client/src/pages/MyPost.jsx +++ b/client/src/pages/MyPost.jsx @@ -2,15 +2,15 @@ import React, { useState, useEffect } from 'react'; import '../styles/MyPost.css'; import { instance, deletePost } from 'api/api.js'; import jwtDecode from 'jwt-decode'; -import NavBar from 'components/NavBar.jsx'; -import { useNavigate , useParams , Link } from 'react-router-dom'; +import NavBar from 'components/NavBar.tsx'; +import { useNavigate, useParams, Link } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { setActiveMenu } from '../store/menuSlice.js'; -const MyPost = ( ) => { +const MyPost = () => { const { postId } = useParams(); - const [ post, setPost ] = useState({}); - const [ userData, setUserData ] = useState({}); + const [post, setPost] = useState({}); + const [userData, setUserData] = useState({}); const accessToken = localStorage.getItem('accessToken'); const decodedToken = jwtDecode(accessToken); @@ -22,28 +22,26 @@ const MyPost = ( ) => { const handleDeleteButton = () => { deletePost(userId, postId) - .then(()=>{ + .then(() => { dispatch(setActiveMenu('내가 쓴 글')); navigate('/mypage/main'); }) - .catch((error) => { + .catch(error => { console.error('게시글 삭제 오류:', error); }); - } + }; useEffect(() => { async function getPost() { try { const res = await instance.get('/post/' + postId); setPost(res.data); - } - catch (err) { + } catch (err) { console.error('err: ', err); } } getPost(); - }, [postId]); useEffect(() => { @@ -57,14 +55,13 @@ const MyPost = ( ) => { } getUserData(); - }, [userId]); - const userPicture = userData.imageUrl || require("../assets/user_shadow.png"); + const userPicture = userData.imageUrl || require('../assets/user_shadow.png'); const date = new Date(post.createdAt); const daysOfWeek = ['일', '월', '화', '수', '목', '금', '토']; - const dayOfWeek = daysOfWeek[date.getDay()] + "요일"; + const dayOfWeek = daysOfWeek[date.getDay()] + '요일'; const displayDate = date.toLocaleDateString(); return ( @@ -74,50 +71,38 @@ const MyPost = ( ) => {

내가 쓴 글

  • -
    - 공개 -
    +
    공개
  • -
    - 비공개 -
    +
    비공개
- + | - +
-
+

{post.title}

-
{ - `${displayDate} ${dayOfWeek}` - } -
+
{`${displayDate} ${dayOfWeek}`}
-
- profile -
{userName}
+
+ profile +
{userName}
-
- {post.imageUrls && ( - uploded_image - )} -

- {post.body} -

+
+ {post.imageUrls && uploded_image} +

{post.body}

- ) -} + ); +}; -export default MyPost; \ No newline at end of file +export default MyPost; From 0d916346973e22eee86291bd06b88adea17014ba Mon Sep 17 00:00:00 2001 From: matchaing Date: Thu, 26 Oct 2023 15:28:46 +0900 Subject: [PATCH 06/22] =?UTF-8?q?feat=20:=20ts=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/types/types.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 client/src/types/types.ts diff --git a/client/src/types/types.ts b/client/src/types/types.ts new file mode 100644 index 0000000..9b03d13 --- /dev/null +++ b/client/src/types/types.ts @@ -0,0 +1,13 @@ +export interface Post { + createdAt: string; + postId: number; + type: string; + title: string; + userId: number; + body: string; + open: boolean; +} + +export interface PostListProps { + type: string; +} From 13d987bf394024a9330f2711ea02d65393ecdc73 Mon Sep 17 00:00:00 2001 From: matchaing Date: Thu, 26 Oct 2023 15:29:13 +0900 Subject: [PATCH 07/22] =?UTF-8?q?chore=20:=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/client/src/App.js b/client/src/App.js index fcf8488..2b8bcf4 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -20,26 +20,24 @@ import MyPageInfo from './pages/MyPageInfo.jsx'; import MyPost from 'pages/MyPost.jsx'; function App() { - return (
-
- } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> } /> - } /> + } /> } /> From 4356816ef0655dd47925c4793109536d2f647d16 Mon Sep 17 00:00:00 2001 From: matchaing Date: Thu, 26 Oct 2023 15:29:37 +0900 Subject: [PATCH 08/22] =?UTF-8?q?chore=20:=20ESLint=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/.eslintrc.json | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/client/.eslintrc.json b/client/.eslintrc.json index dc58e08..c429593 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -14,20 +14,23 @@ "plugin:import/recommended", "prettier" ], - "plugins": [ - "react", - "react-hooks", - "jsx-a11y", - "import" - ], + "plugins": ["react", "react-hooks", "jsx-a11y", "import"], "rules": { - "no-unused-vars": "warn" + "no-unused-vars": "warn", + "import/no-unresolved": [2, { "commonjs": true, "amd": true }], + "import/extensions": [2, "ignorePackages"], + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } }, "settings": { "import/resolver": { "node": { - "paths": ["src"] // src 폴더를 모듈 경로로 추가 + "paths": ["src"], // src 폴더를 모듈 경로로 추가 + "extensions": [".js", ".jsx", ".ts", ".tsx"] } } } -} \ No newline at end of file +} From 933408135bbe3dc5efab8e8858d0689be2a26250 Mon Sep 17 00:00:00 2001 From: matchaing Date: Thu, 26 Oct 2023 15:30:07 +0900 Subject: [PATCH 09/22] =?UTF-8?q?refactor=20:=20PostList=20TS=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/{PostList.jsx => PostList.tsx} | 61 +++++++++---------- client/src/pages/AllBoardPage.jsx | 2 +- client/src/pages/AuthBoardPage.jsx | 2 +- client/src/pages/EnvBoardPage.jsx | 2 +- client/src/pages/FreeBoardPage.jsx | 2 +- 5 files changed, 32 insertions(+), 37 deletions(-) rename client/src/components/{PostList.jsx => PostList.tsx} (54%) diff --git a/client/src/components/PostList.jsx b/client/src/components/PostList.tsx similarity index 54% rename from client/src/components/PostList.jsx rename to client/src/components/PostList.tsx index 78ab54d..5f6b940 100644 --- a/client/src/components/PostList.jsx +++ b/client/src/components/PostList.tsx @@ -3,41 +3,41 @@ import '../styles/PostList.css'; import { Link } from 'react-router-dom'; import { getAllPosts, getAlltypePosts } from 'api/api.js'; import PropTypes from 'prop-types'; +import { Post, PostListProps } from '../types/types'; -const PostList = (props) => { - const [allPosts, setAllPosts] = useState([]); - const [visiblePosts, setVisiblePosts] = useState([]); - const [loading, setLoading] = useState(false); +const PostList: React.FC = props => { + const [allPosts, setAllPosts] = useState([]); + const [visiblePosts, setVisiblePosts] = useState([]); + const [loading, setLoading] = useState(false); - const intersectionRef = useRef(null); + const intersectionRef = useRef(null); useEffect(() => { if (props.type === 'all') { getAllPosts() //모든 게시글 가져오기 - .then((res) => { - const sortedData = res.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + .then(res => { + const sortedData = res.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); setAllPosts(sortedData); setVisiblePosts(sortedData.slice(0, 10)); }) - .catch((error) => console.error('Error:', error)); + .catch(error => console.error('Error:', error)); } else { getAlltypePosts(props.type) //게시판 별 게시글 가져오기 - .then((res) => { - const sortedData = res.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + .then(res => { + const sortedData = res.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); setAllPosts(sortedData); setVisiblePosts(sortedData.slice(0, 10)); }) - .catch((error) => console.error('Error:', error)); + .catch(error => console.error('Error:', error)); } }, [props.type]); - useEffect(() => { - const handleIntersect = (entries) => { + const handleIntersect = (entries: IntersectionObserverEntry[]) => { if (entries[0].isIntersecting) { setLoading(true); setTimeout(() => { @@ -45,43 +45,38 @@ const PostList = (props) => { const newVisiblePosts = [...visiblePosts, ...allPosts.slice(endVisibleIndex, endVisibleIndex + 10)]; setVisiblePosts(newVisiblePosts); setLoading(false); - }, ); + }, 0); } }; - + const observer = new IntersectionObserver(handleIntersect, { root: null, rootMargin: '0px', threshold: 0.1, }); - + if (intersectionRef.current) { observer.observe(intersectionRef.current); } - + return () => { observer.disconnect(); }; }, [allPosts, visiblePosts]); - + return (
- {visiblePosts.map((post) => ( + {visiblePosts.map((post: Post) => (
- - {post.title} - -
- {new Date(post.createdAt).toLocaleDateString()} -
-
-
- {post.body.length > 90 ? `${post.body.slice(0, 90)}...` : post.body} + + {post.title} + +
{new Date(post.createdAt).toLocaleDateString()}
+
{post.body.length > 90 ? `${post.body.slice(0, 90)}...` : post.body}
))} {loading &&
Loading...
} diff --git a/client/src/pages/AllBoardPage.jsx b/client/src/pages/AllBoardPage.jsx index 896ee61..16e1c9a 100644 --- a/client/src/pages/AllBoardPage.jsx +++ b/client/src/pages/AllBoardPage.jsx @@ -1,6 +1,6 @@ import React from 'react'; import '../styles/Button.css'; -import PostList from '../components/PostList.jsx'; +import PostList from '../components/PostList.tsx'; import NavBar from '../components/NavBar.tsx'; const AllBoardPage = () => { diff --git a/client/src/pages/AuthBoardPage.jsx b/client/src/pages/AuthBoardPage.jsx index 5ff277b..ce28ac9 100644 --- a/client/src/pages/AuthBoardPage.jsx +++ b/client/src/pages/AuthBoardPage.jsx @@ -1,6 +1,6 @@ import React from 'react'; import '../styles/Button.css'; -import '../components/PostList.jsx'; +import '../components/PostList.tsx'; import AuthPostList from '../components/AuthPostList.jsx'; import NavBar from '../components/NavBar.tsx'; diff --git a/client/src/pages/EnvBoardPage.jsx b/client/src/pages/EnvBoardPage.jsx index 1237945..ec90413 100644 --- a/client/src/pages/EnvBoardPage.jsx +++ b/client/src/pages/EnvBoardPage.jsx @@ -1,6 +1,6 @@ import React from 'react'; import '../styles/Button.css'; -import '../components/PostList.jsx'; +import '../components/PostList.tsx'; import EnvPostList from '../components/EnvPostList.jsx'; import NavBar from '../components/NavBar.tsx'; diff --git a/client/src/pages/FreeBoardPage.jsx b/client/src/pages/FreeBoardPage.jsx index fa7b2b8..706b200 100644 --- a/client/src/pages/FreeBoardPage.jsx +++ b/client/src/pages/FreeBoardPage.jsx @@ -1,6 +1,6 @@ import React from 'react'; import '../styles/Button.css'; -import PostList from '../components/PostList.jsx'; +import PostList from '../components/PostList.tsx'; import NavBar from '../components/NavBar.tsx'; const FreeBoardPage = () => { From d76b014292e9ce322dd4fe1e7f1d4dd0e94e9bc9 Mon Sep 17 00:00:00 2001 From: matchaing Date: Thu, 26 Oct 2023 16:24:50 +0900 Subject: [PATCH 10/22] =?UTF-8?q?chore=20:=20tsx=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/AuthPostList.jsx | 45 +++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/client/src/components/AuthPostList.jsx b/client/src/components/AuthPostList.jsx index 57bec2e..827f9c5 100644 --- a/client/src/components/AuthPostList.jsx +++ b/client/src/components/AuthPostList.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import '../styles/AuthPostList.css'; import { getAuthPosts } from 'api/api.js'; +import { Interface } from 'readline'; const AuthPostList = () => { const [allAuthPosts, setAllAuthPosts] = useState([]); @@ -10,16 +11,26 @@ const AuthPostList = () => { const itemsPerPage = 6; const maxPagesToShow = 5; // 한 번에 보여줄 최대 페이지 수 + // interface AuthPost { + // createdAt: string; + // postId: number; + // title: string; + // userId: number; + // body: string; + // open: boolean; + // imageUrls: string; + // } + useEffect(() => { getAuthPosts() - .then((res)=> { + .then(res => { const sortedData = res.data.sort((a, b) => { return new Date(b.createdAt) - new Date(a.createdAt); }); setAllAuthPosts(sortedData); setVisibleAuthPosts(sortedData.slice(0, 6)); }) - .catch((error) => console.error('Error:', error)); + .catch(error => console.error('Error:', error)); }, []); // 페이지를 변경할 때 visibleAuthPosts 업데이트 @@ -36,7 +47,7 @@ const AuthPostList = () => { }; // 페이지 번호를 클릭했을 때 호출되는 함수 - const handlePageClick = (pageNumber) => { + const handlePageClick = pageNumber => { setCurrentPage(pageNumber); }; @@ -76,31 +87,33 @@ const AuthPostList = () => {
{visibleAuthPosts.map((post, index) => ( + to={`/auth/${post.postId}/${post.userId}`} // 경로 설정 + key={post.postId} + className="auth_post_item_container"> {`${post.postId}`} ))}
- {!isPrevButtonDisabled && visibleAuthPosts.length > 0 && } - {!isPrevButtonDisabled && visibleAuthPosts.length > 0 && } - {getPageNumbers().map((pageNumber) => ( + {!isPrevButtonDisabled && visibleAuthPosts.length > 0 && ( + + )} + {!isPrevButtonDisabled && visibleAuthPosts.length > 0 && ( + + )} + {getPageNumbers().map(pageNumber => ( ))} - {!isNextButtonDisabled && visibleAuthPosts.length > 0 && } {!isNextButtonDisabled && visibleAuthPosts.length > 0 && ( - + + )} + {!isNextButtonDisabled && visibleAuthPosts.length > 0 && ( + )}
From d083f3172dcc15b11a6b065868fb07b3b0427d8a Mon Sep 17 00:00:00 2001 From: matchaing Date: Mon, 30 Oct 2023 14:59:17 +0900 Subject: [PATCH 11/22] =?UTF-8?q?refactor=20:=20PostList=20tsx=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/.eslintrc.json | 7 +-- .../{AuthPostList.jsx => AuthPostList.tsx} | 54 +++++++---------- .../{EnvPostList.jsx => EnvPostList.tsx} | 58 ++++++++++--------- client/src/pages/AuthBoardPage.jsx | 2 +- client/src/pages/EnvBoardPage.jsx | 2 +- client/src/types/types.ts | 10 ++++ 6 files changed, 66 insertions(+), 67 deletions(-) rename client/src/components/{AuthPostList.jsx => AuthPostList.tsx} (67%) rename client/src/components/{EnvPostList.jsx => EnvPostList.tsx} (57%) diff --git a/client/.eslintrc.json b/client/.eslintrc.json index c429593..83082d7 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -18,12 +18,7 @@ "rules": { "no-unused-vars": "warn", "import/no-unresolved": [2, { "commonjs": true, "amd": true }], - "import/extensions": [2, "ignorePackages"], - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - } + "import/extensions": [2, "ignorePackages"] }, "settings": { "import/resolver": { diff --git a/client/src/components/AuthPostList.jsx b/client/src/components/AuthPostList.tsx similarity index 67% rename from client/src/components/AuthPostList.jsx rename to client/src/components/AuthPostList.tsx index 827f9c5..50b031d 100644 --- a/client/src/components/AuthPostList.jsx +++ b/client/src/components/AuthPostList.tsx @@ -2,35 +2,25 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import '../styles/AuthPostList.css'; import { getAuthPosts } from 'api/api.js'; -import { Interface } from 'readline'; +import { AuthPost } from '../types/types'; -const AuthPostList = () => { - const [allAuthPosts, setAllAuthPosts] = useState([]); - const [visibleAuthPosts, setVisibleAuthPosts] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 6; - const maxPagesToShow = 5; // 한 번에 보여줄 최대 페이지 수 - - // interface AuthPost { - // createdAt: string; - // postId: number; - // title: string; - // userId: number; - // body: string; - // open: boolean; - // imageUrls: string; - // } +const AuthPostList: React.FC = () => { + const [allAuthPosts, setAllAuthPosts] = useState([]); + const [visibleAuthPosts, setVisibleAuthPosts] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage: number = 6; + const maxPagesToShow: number = 5; // 한 번에 보여줄 최대 페이지 수 useEffect(() => { getAuthPosts() - .then(res => { - const sortedData = res.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + .then((res: any) => { + const sortedData = res.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); setAllAuthPosts(sortedData); setVisibleAuthPosts(sortedData.slice(0, 6)); }) - .catch(error => console.error('Error:', error)); + .catch((error: any) => console.error('Error:', error)); }, []); // 페이지를 변경할 때 visibleAuthPosts 업데이트 @@ -39,23 +29,23 @@ const AuthPostList = () => { }, [currentPage, allAuthPosts]); // 페이지를 변경하고 visibleAuthPosts 업데이트하는 함수 - const updateVisiblePosts = (page, data) => { - const startIndex = (page - 1) * itemsPerPage; - const endIndex = startIndex + itemsPerPage; - const pageData = data.slice(startIndex, endIndex); + const updateVisiblePosts = (page: number, data: AuthPost[]) => { + const startIndex: number = (page - 1) * itemsPerPage; + const endIndex: number = startIndex + itemsPerPage; + const pageData: AuthPost[] = data.slice(startIndex, endIndex); setVisibleAuthPosts(pageData); }; // 페이지 번호를 클릭했을 때 호출되는 함수 - const handlePageClick = pageNumber => { + const handlePageClick = (pageNumber: number) => { setCurrentPage(pageNumber); }; // 페이지 번호 배열 생성 const getPageNumbers = () => { - const totalPages = Math.ceil(allAuthPosts.length / itemsPerPage); - const halfMaxPages = Math.floor(maxPagesToShow / 2); - let startPage, endPage; + const totalPages: number = Math.ceil(allAuthPosts.length / itemsPerPage); + const halfMaxPages: number = Math.floor(maxPagesToShow / 2); + let startPage: number, endPage: number; if (totalPages <= maxPagesToShow) { startPage = 1; @@ -77,15 +67,15 @@ const AuthPostList = () => { }; // 이전 페이지 버튼이 비활성화 상태인지 여부 - const isPrevButtonDisabled = currentPage === 1; + const isPrevButtonDisabled: boolean = currentPage === 1; // 다음 페이지 버튼이 비활성화 상태인지 여부 - const isNextButtonDisabled = currentPage === Math.ceil(allAuthPosts.length / itemsPerPage); + const isNextButtonDisabled: boolean = currentPage === Math.ceil(allAuthPosts.length / itemsPerPage); return ( <>
- {visibleAuthPosts.map((post, index) => ( + {visibleAuthPosts.map((post: AuthPost, index: number) => ( { - const [allEnvPosts, setAllEnvPosts] = useState([]); - const [visibleEnvPosts, setVisibleEnvPosts] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 6; - const maxPagesToShow = 5; + const [allEnvPosts, setAllEnvPosts] = useState([]); + const [visibleEnvPosts, setVisibleEnvPosts] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage: number = 6; + const maxPagesToShow: number = 5; useEffect(() => { getEnvPosts() - .then((res)=> { - const sortedData = res.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + .then(res => { + const sortedData = res.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); setAllEnvPosts(sortedData); setVisibleEnvPosts(sortedData.slice(0, 6)); }) - .catch((error) => console.error('Error:', error)); + .catch(error => console.error('Error:', error)); }, []); useEffect(() => { updateVisiblePosts(currentPage, allEnvPosts); }, [currentPage, allEnvPosts]); - const updateVisiblePosts = (page, data) => { + const updateVisiblePosts = (page: number, data: AuthPost[]) => { const startIndex = (page - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; - const pageData = data.slice(startIndex, endIndex); + const pageData: AuthPost[] = data.slice(startIndex, endIndex); setVisibleEnvPosts(pageData); }; - const handlePageClick = (pageNumber) => { + const handlePageClick = (pageNumber: number) => { setCurrentPage(pageNumber); }; const getPageNumbers = () => { - const totalPages = Math.ceil(allEnvPosts.length / itemsPerPage); - const halfMaxPages = Math.floor(maxPagesToShow / 2); - let startPage, endPage; + const totalPages: number = Math.ceil(allEnvPosts.length / itemsPerPage); + const halfMaxPages: number = Math.floor(maxPagesToShow / 2); + let startPage: number, endPage: number; if (totalPages <= maxPagesToShow) { startPage = 1; @@ -60,36 +61,39 @@ const EnvPostList = () => { return Array.from({ length: endPage - startPage + 1 }, (_, index) => startPage + index); }; - const isPrevButtonDisabled = currentPage === 1; + const isPrevButtonDisabled: boolean = currentPage === 1; - const isNextButtonDisabled = currentPage === Math.ceil(allEnvPosts.length / itemsPerPage); + const isNextButtonDisabled: boolean = currentPage === Math.ceil(allEnvPosts.length / itemsPerPage); return ( <>
{visibleEnvPosts.map((post, index) => (
- {`${post.postId}`} + {`${post.postId}`}
))}
- {!isPrevButtonDisabled && visibleEnvPosts.length > 0 && } - {!isPrevButtonDisabled && visibleEnvPosts.length > 0 && } - {getPageNumbers().map((pageNumber) => ( + {!isPrevButtonDisabled && visibleEnvPosts.length > 0 && ( + + )} + {!isPrevButtonDisabled && visibleEnvPosts.length > 0 && ( + + )} + {getPageNumbers().map(pageNumber => ( ))} - {!isNextButtonDisabled && visibleEnvPosts.length > 0 && } {!isNextButtonDisabled && visibleEnvPosts.length > 0 && ( - + + )} + {!isNextButtonDisabled && visibleEnvPosts.length > 0 && ( + )}
diff --git a/client/src/pages/AuthBoardPage.jsx b/client/src/pages/AuthBoardPage.jsx index ce28ac9..526f37e 100644 --- a/client/src/pages/AuthBoardPage.jsx +++ b/client/src/pages/AuthBoardPage.jsx @@ -1,7 +1,7 @@ import React from 'react'; import '../styles/Button.css'; import '../components/PostList.tsx'; -import AuthPostList from '../components/AuthPostList.jsx'; +import AuthPostList from '../components/AuthPostList.tsx'; import NavBar from '../components/NavBar.tsx'; const AuthBoardPage = () => { diff --git a/client/src/pages/EnvBoardPage.jsx b/client/src/pages/EnvBoardPage.jsx index ec90413..63ab0ce 100644 --- a/client/src/pages/EnvBoardPage.jsx +++ b/client/src/pages/EnvBoardPage.jsx @@ -1,7 +1,7 @@ import React from 'react'; import '../styles/Button.css'; import '../components/PostList.tsx'; -import EnvPostList from '../components/EnvPostList.jsx'; +import EnvPostList from '../components/EnvPostList.tsx'; import NavBar from '../components/NavBar.tsx'; const EnvBoardPage = () => { diff --git a/client/src/types/types.ts b/client/src/types/types.ts index 9b03d13..9c61582 100644 --- a/client/src/types/types.ts +++ b/client/src/types/types.ts @@ -11,3 +11,13 @@ export interface Post { export interface PostListProps { type: string; } + +export interface AuthPost { + createdAt: string; + postId: number; + title: string; + userId: number; + body: string; + open: boolean; + imageUrl: string; +} From 48c78b09ac4465edd105ac8d515719352838e65e Mon Sep 17 00:00:00 2001 From: matchaing Date: Mon, 30 Oct 2023 15:03:50 +0900 Subject: [PATCH 12/22] =?UTF-8?q?refactor=20:=20modal=20tsx=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Modal.jsx | 32 -------------------------------- client/src/components/Modal.tsx | 25 +++++++++++++++++++++++++ client/src/pages/MyPageInfo.jsx | 2 +- client/src/types/types.ts | 6 ++++++ 4 files changed, 32 insertions(+), 33 deletions(-) delete mode 100644 client/src/components/Modal.jsx create mode 100644 client/src/components/Modal.tsx diff --git a/client/src/components/Modal.jsx b/client/src/components/Modal.jsx deleted file mode 100644 index d3fe6c1..0000000 --- a/client/src/components/Modal.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import '../styles/Button.css'; -import '../styles/Modal.css'; -import PropTypes from 'prop-types'; - -const Modal = ({ content, onCancel, onConfirm }) => { - return ( -
-
-
- {content} -
-
- - -
-
-
- ); - }; - - Modal.propTypes = { - content: PropTypes.node.isRequired, - onCancel: PropTypes.func.isRequired, - onConfirm: PropTypes.func.isRequired, - }; - -export default Modal; \ No newline at end of file diff --git a/client/src/components/Modal.tsx b/client/src/components/Modal.tsx new file mode 100644 index 0000000..d18e71f --- /dev/null +++ b/client/src/components/Modal.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import '../styles/Button.css'; +import '../styles/Modal.css'; +import PropTypes from 'prop-types'; +import { ModalProps } from '../types/types'; + +const Modal: React.FC = ({ content, onCancel, onConfirm }) => { + return ( +
+
+
{content}
+
+ + +
+
+
+ ); +}; + +export default Modal; diff --git a/client/src/pages/MyPageInfo.jsx b/client/src/pages/MyPageInfo.jsx index 09e1912..83f9fb2 100644 --- a/client/src/pages/MyPageInfo.jsx +++ b/client/src/pages/MyPageInfo.jsx @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { setActiveMenu } from '../store/menuSlice.js'; import NavBar from '../components/NavBar.tsx'; -import Modal from '../components/Modal.jsx'; +import Modal from '../components/Modal.tsx'; import '../styles/Button.css'; import '../styles/MyPageInfo.css'; import jwtDecode from 'jwt-decode'; diff --git a/client/src/types/types.ts b/client/src/types/types.ts index 9c61582..5bfef6b 100644 --- a/client/src/types/types.ts +++ b/client/src/types/types.ts @@ -21,3 +21,9 @@ export interface AuthPost { open: boolean; imageUrl: string; } + +export interface ModalProps { + content: React.ReactNode; + onCancel: () => void; + onConfirm: () => void; +} From 6af4c3cf54f2a7ef23c50007e642039c9e73df5e Mon Sep 17 00:00:00 2001 From: jumpm9239 Date: Tue, 31 Oct 2023 17:12:12 +0900 Subject: [PATCH 13/22] =?UTF-8?q?refactor:=20redux=20=EC=A0=81=EC=9A=A9,?= =?UTF-8?q?=20fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.js | 54 ++++++----- client/src/api/api.js | 22 +++-- client/src/components/AppHeader.jsx | 31 ++++++ client/src/components/AuthPostList.jsx | 2 +- client/src/components/GuestHeader.jsx | 36 +++++++ client/src/components/PostList.tsx | 2 +- .../components/{Header.jsx => UserHeader.jsx} | 95 ++++--------------- client/src/store/authSlice.js | 24 +++++ client/src/store/rootReducer.js | 10 ++ client/src/store/store.js | 8 ++ 10 files changed, 171 insertions(+), 113 deletions(-) create mode 100644 client/src/components/AppHeader.jsx create mode 100644 client/src/components/GuestHeader.jsx rename client/src/components/{Header.jsx => UserHeader.jsx} (57%) create mode 100644 client/src/store/authSlice.js create mode 100644 client/src/store/rootReducer.js create mode 100644 client/src/store/store.js diff --git a/client/src/App.js b/client/src/App.js index 2b8bcf4..615e9ec 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,6 +1,8 @@ import React from 'react'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; +import { Provider } from 'react-redux'; import './App.css'; +import store from './store/store.js'; import SignUpPage from './pages/SignUpPage.jsx'; import Login from './pages/LoginPage.jsx'; @@ -13,36 +15,38 @@ import AuthBoardPage from './pages/AuthBoardPage.jsx'; import AuthDetailPage from './pages/AuthDetailPage.jsx'; import EnvBoardPage from './pages/EnvBoardPage.jsx'; -import Header from './components/Header.jsx'; +import AppHeader from './components/AppHeader.jsx'; -import MyPageMain from 'pages/MyPageMain.jsx'; +import MyPageMain from './pages/MyPageMain.jsx'; import MyPageInfo from './pages/MyPageInfo.jsx'; -import MyPost from 'pages/MyPost.jsx'; +import MyPost from './pages/MyPost.jsx'; function App() { return ( -
- -
- - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - - -
+ +
+ + + + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + + +
+
); } -export default App; +export default App; \ No newline at end of file diff --git a/client/src/api/api.js b/client/src/api/api.js index 0d74e63..8ef687c 100644 --- a/client/src/api/api.js +++ b/client/src/api/api.js @@ -60,7 +60,6 @@ export const postComment = (postId, userId, commentText) => { return instance.post(`/comment/${postId}/${userId}`, { body: commentText }); }; - export const postPosts = (type, title, body, open, img, userId) => { const formData = new FormData(); @@ -176,14 +175,17 @@ export const postLogin = async ( id, password ) => { } } -const accessToken = localStorage.getItem('accessToken'); - -if (accessToken) { - instance.interceptors.request.use( - function (config) { +instance.interceptors.request.use( + function (config) { + // decode 토큰 만료 시간 확인하는 함수 isValid(Token) 넣기 + const accessToken = localStorage.getItem('accessToken'); + + if (accessToken /* && 함수 */) { instance.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`; - - return config; + } else { + // } - ) -} \ No newline at end of file + + return config; + } +) \ No newline at end of file diff --git a/client/src/components/AppHeader.jsx b/client/src/components/AppHeader.jsx new file mode 100644 index 0000000..33527ff --- /dev/null +++ b/client/src/components/AppHeader.jsx @@ -0,0 +1,31 @@ +import React, { useState, useEffect } from 'react'; +import "../styles/Header.css"; +import { useDispatch, useSelector } from 'react-redux'; +import UserHeader from './UserHeader.jsx'; +import GuestHeader from './GuestHeader.jsx'; +import { setLoggedIn } from '../store/authSlice.js'; + +const AppHeader = () => { + const dispatch = useDispatch(); + const accessToken = localStorage.getItem('accessToken'); + + useEffect(() => { + if (accessToken) { + dispatch(setLoggedIn(true)); + return; + } + + dispatch(setLoggedIn(false)); + }, [accessToken]); + + const isLoggedIn = useSelector((state) => state.auth.isLoggedIn); + + return ( +
+ + +
+ ) +} + +export default AppHeader; \ No newline at end of file diff --git a/client/src/components/AuthPostList.jsx b/client/src/components/AuthPostList.jsx index 827f9c5..a9c8de1 100644 --- a/client/src/components/AuthPostList.jsx +++ b/client/src/components/AuthPostList.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import '../styles/AuthPostList.css'; import { getAuthPosts } from 'api/api.js'; -import { Interface } from 'readline'; +// import { Interface } from 'readline'; const AuthPostList = () => { const [allAuthPosts, setAllAuthPosts] = useState([]); diff --git a/client/src/components/GuestHeader.jsx b/client/src/components/GuestHeader.jsx new file mode 100644 index 0000000..9045ee0 --- /dev/null +++ b/client/src/components/GuestHeader.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPencil, faRightToBracket } from "@fortawesome/free-solid-svg-icons"; + +const GuestHeader = ( { isLoggedIn } ) => { + + if (isLoggedIn) { + return null; + } + + return ( +
+
+ + logo + +
+ + + + + + +
+
+
+ ) +} + +GuestHeader.propTypes = { + isLoggedIn: PropTypes.bool.isRequired, +} + +export default GuestHeader; \ No newline at end of file diff --git a/client/src/components/PostList.tsx b/client/src/components/PostList.tsx index 5f6b940..c4100f6 100644 --- a/client/src/components/PostList.tsx +++ b/client/src/components/PostList.tsx @@ -3,7 +3,7 @@ import '../styles/PostList.css'; import { Link } from 'react-router-dom'; import { getAllPosts, getAlltypePosts } from 'api/api.js'; import PropTypes from 'prop-types'; -import { Post, PostListProps } from '../types/types'; +import { Post, PostListProps } from '../types/types.ts'; const PostList: React.FC = props => { const [allPosts, setAllPosts] = useState([]); diff --git a/client/src/components/Header.jsx b/client/src/components/UserHeader.jsx similarity index 57% rename from client/src/components/Header.jsx rename to client/src/components/UserHeader.jsx index 0d15106..3322edd 100644 --- a/client/src/components/Header.jsx +++ b/client/src/components/UserHeader.jsx @@ -1,48 +1,19 @@ import React, { useState, useEffect } from 'react'; -import "../styles/Header.css"; import { Link, useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; import jwtDecode from 'jwt-decode'; import PropTypes from 'prop-types'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPencil, faRightToBracket, faRightFromBracket, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; +import { faPencil, faRightFromBracket, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; +import { setActiveMenu } from 'store/menuSlice.js'; +import { logout, setLoggedIn } from 'store/authSlice.js'; -const HeaderLoggedOut = ( { isLoggedIn } ) => { - - if (isLoggedIn) { - return null; - } - - return ( -
-
- - logo - -
- - - - - - -
-
-
- ) -} - -HeaderLoggedOut.propTypes = { - isLoggedIn: PropTypes.bool.isRequired, -} - - -const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { +const UserHeader = ( { isLoggedIn } ) => { const accessToken = localStorage.getItem('accessToken'); const [ userName, setUserName ] = useState(''); const dispatch = useDispatch(); + const navigate = useNavigate(); const handleLogoClick = () => { dispatch(setActiveMenu('전체 글 보기')); @@ -52,9 +23,20 @@ const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { dispatch(setActiveMenu('내가 쓴 글')); } - useEffect(() => { + const handleLogout = () => { + try { + localStorage.removeItem('accessToken'); + dispatch(logout()); + dispatch(setActiveMenu('전체 글 보기')); + navigate('/'); + } catch (error) { + console.error('로그아웃 실패: ', error); + } + }; + useEffect(() => { if (accessToken) { + dispatch(setLoggedIn(true)); const decodedToken = jwtDecode(accessToken); const userName = decodedToken.userName; setUserName(userName); @@ -91,47 +73,8 @@ const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { ) } -HeaderLoggedIn.propTypes = { +UserHeader.propTypes = { isLoggedIn: PropTypes.bool.isRequired, - handleLogout: PropTypes.func.isRequired -} - - -const Header = () => { - - const dispatch = useDispatch(); - const accessToken = localStorage.getItem('accessToken'); - const [ isLoggedIn, setIsLoggedIn] = useState(!accessToken); - - useEffect(() => { - if (accessToken) { - setIsLoggedIn(true) - return; - } - - setIsLoggedIn(false) - } - , [accessToken]); - - const navigate = useNavigate(); - - const handleLogout = () => { - try { - localStorage.removeItem('accessToken'); - setIsLoggedIn(false); - dispatch(setActiveMenu('전체 글 보기')); - navigate('/'); - } catch (error) { - console.error('로그아웃 실패: ', error); - } - }; - - return ( -
- - -
- ) } -export default Header; \ No newline at end of file +export default UserHeader; \ No newline at end of file diff --git a/client/src/store/authSlice.js b/client/src/store/authSlice.js new file mode 100644 index 0000000..1b9f8da --- /dev/null +++ b/client/src/store/authSlice.js @@ -0,0 +1,24 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const authSlice = createSlice({ + name: 'auth', + initialState: { + isLoggedIn: false, + accessToken: null, + }, + reducers: { + setLoggedIn: (state, action) => { + state.isLoggedIn = action.payload; + }, + setAccessToken: (state, action) => { + state.accessToken = action.payload; + }, + logout: (state) => { + state.isLoggedIn = false; + state.accessToken = null; + }, + }, +}); + +export const { setLoggedIn, setAccessToken, logout } = authSlice.actions; +export default authSlice.reducer; \ No newline at end of file diff --git a/client/src/store/rootReducer.js b/client/src/store/rootReducer.js new file mode 100644 index 0000000..78f7fcc --- /dev/null +++ b/client/src/store/rootReducer.js @@ -0,0 +1,10 @@ +import { combineReducers } from '@reduxjs/toolkit'; +import authReducer from './authSlice.js'; +import menuReducer from './menuSlice.js'; + +const rootReducer = combineReducers({ + auth: authReducer, + menu: menuReducer, +}); + +export default rootReducer; \ No newline at end of file diff --git a/client/src/store/store.js b/client/src/store/store.js new file mode 100644 index 0000000..5888103 --- /dev/null +++ b/client/src/store/store.js @@ -0,0 +1,8 @@ +import { configureStore } from "@reduxjs/toolkit"; +import rootReducer from "./rootReducer.js"; + +const store = configureStore({ + reducer: rootReducer, +}); + +export default store; \ No newline at end of file From 68e5af33dc80076b08e63bfb96803e9ab2142258 Mon Sep 17 00:00:00 2001 From: jumpm9239 Date: Wed, 1 Nov 2023 13:26:50 +0900 Subject: [PATCH 14/22] =?UTF-8?q?ts=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/.eslintrc.json | 9 ++------- client/src/index.js | 2 +- client/src/pages/MyPageMain.jsx | 2 +- client/tsconfig.json | 4 +++- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/client/.eslintrc.json b/client/.eslintrc.json index c429593..ec9fca2 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -18,12 +18,7 @@ "rules": { "no-unused-vars": "warn", "import/no-unresolved": [2, { "commonjs": true, "amd": true }], - "import/extensions": [2, "ignorePackages"], - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - } + "import/extensions": [2, "ignorePackages"] }, "settings": { "import/resolver": { @@ -33,4 +28,4 @@ } } } -} +} \ No newline at end of file diff --git a/client/src/index.js b/client/src/index.js index 00f73ef..3292879 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -4,7 +4,7 @@ import './index.css'; import App from 'App.js'; import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; // configureStore를 import합니다. -import menuSliceReducer from './store/menuSlice'; // 리듀서를 import합니다. +import menuSliceReducer from './store/menuSlice.js'; // 리듀서를 import합니다. // 스토어를 구성합니다. diff --git a/client/src/pages/MyPageMain.jsx b/client/src/pages/MyPageMain.jsx index 7538e09..d439347 100644 --- a/client/src/pages/MyPageMain.jsx +++ b/client/src/pages/MyPageMain.jsx @@ -3,7 +3,7 @@ import '../styles/Button.css'; import '../styles/MyPageMain.css'; import Pagination from 'components/Pagination.jsx'; import jwtDecode from 'jwt-decode'; -import { instance } from 'api/api'; +import { instance } from 'api/api.js'; import NavBar from 'components/NavBar.tsx'; import { Link } from 'react-router-dom'; diff --git a/client/tsconfig.json b/client/tsconfig.json index 08c0c16..54a851b 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -7,7 +7,9 @@ "outDir": "./dist", "moduleResolution": "Node", "lib": ["ES2015", "DOM", "DOM.Iterable"], - "esModuleInterop": true + "esModuleInterop": true, + "allowImportingTsExtensions": true, + "noEmit": true, }, "include": ["./src/**/*"], "exclude": ["node_modules", "dist"] From b2db6789df3aa1891b316c64c9a05994a6297d72 Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 1 Nov 2023 13:37:22 +0900 Subject: [PATCH 15/22] =?UTF-8?q?refactor=20:=20tsx=EB=A1=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.js | 14 +-- .../{AllBoardPage.jsx => AllBoardPage.tsx} | 6 +- .../{AuthBoardPage.jsx => AuthBoardPage.tsx} | 8 +- ...{AuthDetailPage.jsx => AuthDetailPage.tsx} | 28 +++--- .../{EnvBoardPage.jsx => EnvBoardPage.tsx} | 8 +- .../{FreeBoardPage.jsx => FreeBoardPage.tsx} | 6 +- ...{FreeDetailPage.jsx => FreeDetailPage.tsx} | 96 +++++++++---------- .../pages/{MyPageInfo.jsx => MyPageInfo.tsx} | 26 ++--- 8 files changed, 96 insertions(+), 96 deletions(-) rename client/src/pages/{AllBoardPage.jsx => AllBoardPage.tsx} (75%) rename client/src/pages/{AuthBoardPage.jsx => AuthBoardPage.tsx} (71%) rename client/src/pages/{AuthDetailPage.jsx => AuthDetailPage.tsx} (90%) rename client/src/pages/{EnvBoardPage.jsx => EnvBoardPage.tsx} (72%) rename client/src/pages/{FreeBoardPage.jsx => FreeBoardPage.tsx} (75%) rename client/src/pages/{FreeDetailPage.jsx => FreeDetailPage.tsx} (76%) rename client/src/pages/{MyPageInfo.jsx => MyPageInfo.tsx} (86%) diff --git a/client/src/App.js b/client/src/App.js index 2b8bcf4..e36b329 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -6,17 +6,17 @@ import SignUpPage from './pages/SignUpPage.jsx'; import Login from './pages/LoginPage.jsx'; import EditerPage from './pages/EditerPage.jsx'; -import AllBoardPage from './pages/AllBoardPage.jsx'; -import FreeBoardPage from './pages/FreeBoardPage.jsx'; -import FreeDetailPage from './pages/FreeDetailPage.jsx'; -import AuthBoardPage from './pages/AuthBoardPage.jsx'; -import AuthDetailPage from './pages/AuthDetailPage.jsx'; -import EnvBoardPage from './pages/EnvBoardPage.jsx'; +import AllBoardPage from './pages/AllBoardPage.tsx'; +import FreeBoardPage from './pages/FreeBoardPage.tsx'; +import FreeDetailPage from './pages/FreeDetailPage.tsx'; +import AuthBoardPage from './pages/AuthBoardPage.tsx'; +import AuthDetailPage from './pages/AuthDetailPage.tsx'; +import EnvBoardPage from './pages/EnvBoardPage.tsx'; import Header from './components/Header.jsx'; import MyPageMain from 'pages/MyPageMain.jsx'; -import MyPageInfo from './pages/MyPageInfo.jsx'; +import MyPageInfo from './pages/MyPageInfo.tsx'; import MyPost from 'pages/MyPost.jsx'; function App() { diff --git a/client/src/pages/AllBoardPage.jsx b/client/src/pages/AllBoardPage.tsx similarity index 75% rename from client/src/pages/AllBoardPage.jsx rename to client/src/pages/AllBoardPage.tsx index 16e1c9a..bae9c45 100644 --- a/client/src/pages/AllBoardPage.jsx +++ b/client/src/pages/AllBoardPage.tsx @@ -1,9 +1,9 @@ import React from 'react'; import '../styles/Button.css'; -import PostList from '../components/PostList.tsx'; -import NavBar from '../components/NavBar.tsx'; +import PostList from '../components/PostList'; +import NavBar from '../components/NavBar'; -const AllBoardPage = () => { +const AllBoardPage: React.FC = () => { return ( <>
diff --git a/client/src/pages/AuthBoardPage.jsx b/client/src/pages/AuthBoardPage.tsx similarity index 71% rename from client/src/pages/AuthBoardPage.jsx rename to client/src/pages/AuthBoardPage.tsx index 526f37e..0d333aa 100644 --- a/client/src/pages/AuthBoardPage.jsx +++ b/client/src/pages/AuthBoardPage.tsx @@ -1,10 +1,10 @@ import React from 'react'; import '../styles/Button.css'; -import '../components/PostList.tsx'; -import AuthPostList from '../components/AuthPostList.tsx'; -import NavBar from '../components/NavBar.tsx'; +import '../components/PostList'; +import AuthPostList from '../components/AuthPostList'; +import NavBar from '../components/NavBar'; -const AuthBoardPage = () => { +const AuthBoardPage: React.FC = () => { return ( <>
diff --git a/client/src/pages/AuthDetailPage.jsx b/client/src/pages/AuthDetailPage.tsx similarity index 90% rename from client/src/pages/AuthDetailPage.jsx rename to client/src/pages/AuthDetailPage.tsx index 2a7d021..de5894a 100644 --- a/client/src/pages/AuthDetailPage.jsx +++ b/client/src/pages/AuthDetailPage.tsx @@ -3,38 +3,38 @@ import { useParams } from 'react-router-dom'; import jwtDecode from 'jwt-decode'; import '../styles/Button.css'; import '../styles/BoardDetailPage.css'; -import NavBar from '../components/NavBar.tsx'; +import NavBar from '../components/NavBar'; import { getPost, getUser, getComment, postComment, getVote, patchVote } from '../api/api.js'; -const AuthDetailPage = () => { - const { postId, userId, voteId } = useParams(); +const AuthDetailPage: React.FC = () => { + const { postId, userId, voteId } = useParams<{ postId: string; userId: string; voteId: string }>(); - const [post, setPost] = useState({}); - const [user, setUser] = useState({}); + const [post, setPost] = useState({}); + const [user, setUser] = useState({}); - const [loggedInUserId, setLoggedInUserId] = useState({}); + const [loggedInUserId, setLoggedInUserId] = useState({}); const accessToken = localStorage.getItem('accessToken'); useEffect(() => { if (accessToken) { setIsLoggedIn(true); - const decodedToken = jwtDecode(accessToken); + const decodedToken: any = jwtDecode(accessToken); setLoggedInUserId(decodedToken.userId); } else { setIsLoggedIn(false); } }, [accessToken]); - const [vote, setVote] = useState({}); - const [alreadyLiked, setAlreadyLiked] = useState(false); + const [vote, setVote] = useState({}); + const [alreadyLiked, setAlreadyLiked] = useState(false); const [liked, setLiked] = useState(alreadyLiked); const [commentText, setCommentText] = useState(''); - const [allComments, setAllComments] = useState([]); - const [visibleComments, setVisibleComments] = useState([]); + const [allComments, setAllComments] = useState([]); + const [visibleComments, setVisibleComments] = useState([]); - const intersectionRef = useRef(null); + const intersectionRef = useRef(null); const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); @@ -155,8 +155,8 @@ const AuthDetailPage = () => { // 댓글 데이터 가져오기 getComment(postId) .then(response => { - const sortedComments = response.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + const sortedComments = response.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); const commentsWithUser = sortedComments.map(comment => ({ ...comment, diff --git a/client/src/pages/EnvBoardPage.jsx b/client/src/pages/EnvBoardPage.tsx similarity index 72% rename from client/src/pages/EnvBoardPage.jsx rename to client/src/pages/EnvBoardPage.tsx index 63ab0ce..ca8555c 100644 --- a/client/src/pages/EnvBoardPage.jsx +++ b/client/src/pages/EnvBoardPage.tsx @@ -1,10 +1,10 @@ import React from 'react'; import '../styles/Button.css'; -import '../components/PostList.tsx'; -import EnvPostList from '../components/EnvPostList.tsx'; -import NavBar from '../components/NavBar.tsx'; +import '../components/PostList'; +import EnvPostList from '../components/EnvPostList'; +import NavBar from '../components/NavBar'; -const EnvBoardPage = () => { +const EnvBoardPage: React.FC = () => { return ( <>
diff --git a/client/src/pages/FreeBoardPage.jsx b/client/src/pages/FreeBoardPage.tsx similarity index 75% rename from client/src/pages/FreeBoardPage.jsx rename to client/src/pages/FreeBoardPage.tsx index 706b200..4b81aaa 100644 --- a/client/src/pages/FreeBoardPage.jsx +++ b/client/src/pages/FreeBoardPage.tsx @@ -1,9 +1,9 @@ import React from 'react'; import '../styles/Button.css'; -import PostList from '../components/PostList.tsx'; -import NavBar from '../components/NavBar.tsx'; +import PostList from '../components/PostList'; +import NavBar from '../components/NavBar'; -const FreeBoardPage = () => { +const FreeBoardPage: React.FC = () => { return ( <>
diff --git a/client/src/pages/FreeDetailPage.jsx b/client/src/pages/FreeDetailPage.tsx similarity index 76% rename from client/src/pages/FreeDetailPage.jsx rename to client/src/pages/FreeDetailPage.tsx index 4770517..235b9dd 100644 --- a/client/src/pages/FreeDetailPage.jsx +++ b/client/src/pages/FreeDetailPage.tsx @@ -3,42 +3,42 @@ import { useParams } from 'react-router-dom'; import jwtDecode from 'jwt-decode'; import '../styles/Button.css'; import '../styles/BoardDetailPage.css'; -import NavBar from '../components/NavBar.tsx'; +import NavBar from '../components/NavBar'; import { getPost, getUser, getComment, postComment, getVote, patchVote } from 'api/api.js'; const FreeDetailPage = () => { - const { postId, userId, voteId } = useParams(); + const { postId, userId, voteId } = useParams<{ postId: string; userId: string; voteId: string }>(); - const [post, setPost] = useState({}); - const [user, setUser] = useState({}); + const [post, setPost] = useState({}); + const [user, setUser] = useState({}); - const [loggedInUserId, setLoggedInUserId] = useState({}); + const [loggedInUserId, setLoggedInUserId] = useState({}); const accessToken = localStorage.getItem('accessToken'); useEffect(() => { if (accessToken) { setIsLoggedIn(true); - const decodedToken = jwtDecode(accessToken); + const decodedToken: any = jwtDecode(accessToken); setLoggedInUserId(decodedToken.userId); } else { setIsLoggedIn(false); } }, [accessToken]); - const [vote, setVote] = useState({}); + const [vote, setVote] = useState({}); // 해당 게시글에 이미 좋아요를 했었는지 여부를 저장하는 상태 // 새로고침했을때 이전 기록을 로컬스토리지에서 가져오는 변수 - const [alreadyLiked, setAlreadyLiked] = useState(false); + const [alreadyLiked, setAlreadyLiked] = useState(false); // 좋아요 버튼 클릭 여부를 저장하는 상태 const [liked, setLiked] = useState(alreadyLiked); const [commentText, setCommentText] = useState(''); - const [allComments, setAllComments] = useState([]); - const [visibleComments, setVisibleComments] = useState([]); + const [allComments, setAllComments] = useState([]); + const [visibleComments, setVisibleComments] = useState([]); - const intersectionRef = useRef(null); + const intersectionRef = useRef(null); const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); @@ -75,41 +75,41 @@ const FreeDetailPage = () => { }); }, [postId, userId]); - const handleVoteClick = async () => { - try { - // API 요청 보내기 (patchVote 함수를 사용하여 요청 보냄) - const response = await patchVote(postId, userId, voteId); - console.log('patch한후: ', response.data); - - // API 요청이 성공적으로 완료된 경우에만 UI를 업데이트합니다. - if (response.status === 200) { - // const updatedVoteCount = response.data.voteCount; - const updatedVoteCount = liked ? vote.voteCount - 1 : vote.voteCount + 1; - setVote({ - ...vote, // 이전 vote 객체 내용을 그대로 유지 - voteCount: updatedVoteCount, // voteCount만 업데이트 - }); - console.log(liked); - console.log(vote.voteCount); - - // 이미 좋아요를 한 상태였다면 좋아요를 해제하고, 그 반대의 경우에는 좋아요를 활성화합니다. - if (alreadyLiked) { - setAlreadyLiked(false); - } else { - setAlreadyLiked(true); - } - - setLiked(!liked); - - // 로컬 스토리지에 좋아요 상태 저장 - localStorage.setItem(`alreadyLikeState_${postId}_${userId}`, alreadyLiked); //JSON.stringify(alreadyLiked)); - } else { - console.error('좋아요 버튼 기능 오류'); - } - } catch (error) { - console.error('좋아요 오류', error); - } - }; + // const handleVoteClick = async () => { + // try { + // // API 요청 보내기 (patchVote 함수를 사용하여 요청 보냄) + // const response = await patchVote(postId, userId, voteId); + // console.log('patch한후: ', response.data); + + // // API 요청이 성공적으로 완료된 경우에만 UI를 업데이트합니다. + // if (response.status === 200) { + // // const updatedVoteCount = response.data.voteCount; + // const updatedVoteCount = liked ? vote.voteCount - 1 : vote.voteCount + 1; + // setVote({ + // ...vote, // 이전 vote 객체 내용을 그대로 유지 + // voteCount: updatedVoteCount, // voteCount만 업데이트 + // }); + // console.log(liked); + // console.log(vote.voteCount); + + // // 이미 좋아요를 한 상태였다면 좋아요를 해제하고, 그 반대의 경우에는 좋아요를 활성화합니다. + // if (alreadyLiked) { + // setAlreadyLiked(false); + // } else { + // setAlreadyLiked(true); + // } + + // setLiked(!liked); + + // // 로컬 스토리지에 좋아요 상태 저장 + // localStorage.setItem(`alreadyLikeState_${postId}_${userId}`, alreadyLiked); //JSON.stringify(alreadyLiked)); + // } else { + // console.error('좋아요 버튼 기능 오류'); + // } + // } catch (error) { + // console.error('좋아요 오류', error); + // } + // }; const handleCommentTextChange = event => { setCommentText(event.target.value); @@ -176,8 +176,8 @@ const FreeDetailPage = () => { // 댓글 데이터 가져오기 getComment(postId) .then(response => { - const sortedComments = response.data.sort((a, b) => { - return new Date(b.createdAt) - new Date(a.createdAt); + const sortedComments = response.data.sort((a: { createdAt: string }, b: { createdAt: string }) => { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); const commentsWithUser = sortedComments.map(comment => ({ ...comment, diff --git a/client/src/pages/MyPageInfo.jsx b/client/src/pages/MyPageInfo.tsx similarity index 86% rename from client/src/pages/MyPageInfo.jsx rename to client/src/pages/MyPageInfo.tsx index 83f9fb2..61128ff 100644 --- a/client/src/pages/MyPageInfo.jsx +++ b/client/src/pages/MyPageInfo.tsx @@ -2,26 +2,26 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { setActiveMenu } from '../store/menuSlice.js'; -import NavBar from '../components/NavBar.tsx'; -import Modal from '../components/Modal.tsx'; +import NavBar from '../components/NavBar'; +import Modal from '../components/Modal'; import '../styles/Button.css'; import '../styles/MyPageInfo.css'; import jwtDecode from 'jwt-decode'; import { deleteUser } from '../api/api.js'; -const MyPageInfo = () => { +const MyPageInfo: React.FC = () => { const dispatch = useDispatch(); const navigate = useNavigate(); - const [previewImageUrl, setPreviewImageUrl] = useState(''); - const [selectedImage, setSelectedImage] = useState(null); + const [previewImageUrl, setPreviewImageUrl] = useState(''); + const [selectedImage, setSelectedImage] = useState(null); - const [showModal, setShowModal] = useState(false); - const [agreed, setAgreed] = useState(false); + const [showModal, setShowModal] = useState(false); + const [agreed, setAgreed] = useState(false); - const accessToken = localStorage.getItem('accessToken'); - const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); - const decodedToken = jwtDecode(accessToken); - const userId = decodedToken.userId; + const accessToken: string | null = localStorage.getItem('accessToken'); + const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); + const decodedToken: { userId: number } = jwtDecode(accessToken); + const userId: number = decodedToken.userId; const handleOpenModal = () => { setShowModal(true); @@ -59,14 +59,14 @@ const MyPageInfo = () => { } }; - const handleImageChange = e => { + const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = event => { - const imageUrl = event.target.result; + const imageUrl = event.target.result as string; setPreviewImageUrl(imageUrl); setSelectedImage(file); }; From 37eee926e9659a4ce02b2216f8fda641d08b177b Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 1 Nov 2023 14:36:06 +0900 Subject: [PATCH 16/22] =?UTF-8?q?refactor=20:=20tsx=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.js | 2 +- client/src/components/Header.jsx | 79 ++++----- client/src/components/NavBar.tsx | 2 +- client/src/components/PostEditer.jsx | 132 +++++++------- .../{SignUpForm.jsx => SignUpForm.tsx} | 163 ++++++++++-------- client/src/pages/LoginPage.jsx | 62 +++---- client/src/pages/MyPageInfo.tsx | 2 +- client/src/pages/MyPost.jsx | 2 +- client/src/pages/SignUpPage.jsx | 17 -- client/src/pages/SignUpPage.tsx | 15 ++ .../src/store/{menuSlice.js => menuSlice.ts} | 9 +- client/src/types/types.ts | 4 + 12 files changed, 255 insertions(+), 234 deletions(-) rename client/src/components/{SignUpForm.jsx => SignUpForm.tsx} (50%) delete mode 100644 client/src/pages/SignUpPage.jsx create mode 100644 client/src/pages/SignUpPage.tsx rename client/src/store/{menuSlice.js => menuSlice.ts} (74%) diff --git a/client/src/App.js b/client/src/App.js index e36b329..79c1175 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -2,7 +2,7 @@ import React from 'react'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import './App.css'; -import SignUpPage from './pages/SignUpPage.jsx'; +import SignUpPage from './pages/SignUpPage.tsx'; import Login from './pages/LoginPage.jsx'; import EditerPage from './pages/EditerPage.jsx'; diff --git a/client/src/components/Header.jsx b/client/src/components/Header.jsx index 0d15106..70e06f8 100644 --- a/client/src/components/Header.jsx +++ b/client/src/components/Header.jsx @@ -1,47 +1,44 @@ import React, { useState, useEffect } from 'react'; -import "../styles/Header.css"; +import '../styles/Header.css'; import { Link, useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; +import { setActiveMenu } from '../store/menuSlice.ts'; import jwtDecode from 'jwt-decode'; import PropTypes from 'prop-types'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPencil, faRightToBracket, faRightFromBracket, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; - -const HeaderLoggedOut = ( { isLoggedIn } ) => { +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPencil, faRightToBracket, faRightFromBracket, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'; +const HeaderLoggedOut = ({ isLoggedIn }) => { if (isLoggedIn) { return null; } - + return (
- - logo + + logo -
- - +
+ + - - + +
- ) -} + ); +}; HeaderLoggedOut.propTypes = { isLoggedIn: PropTypes.bool.isRequired, -} - - -const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { +}; +const HeaderLoggedIn = ({ isLoggedIn, handleLogout }) => { const accessToken = localStorage.getItem('accessToken'); - const [ userName, setUserName ] = useState(''); + const [userName, setUserName] = useState(''); const dispatch = useDispatch(); const handleLogoClick = () => { @@ -50,17 +47,16 @@ const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { const handleProfileClick = () => { dispatch(setActiveMenu('내가 쓴 글')); - } + }; useEffect(() => { - if (accessToken) { const decodedToken = jwtDecode(accessToken); const userName = decodedToken.userName; setUserName(userName); } }, [accessToken]); - + if (!isLoggedIn) { return null; } @@ -68,15 +64,15 @@ const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => { return (
- - logo + + logo
-
- - user profile +
+ + user profile
{userName} 님
@@ -88,30 +84,27 @@ const HeaderLoggedIn = ( { isLoggedIn, handleLogout } ) => {
- ) -} + ); +}; HeaderLoggedIn.propTypes = { isLoggedIn: PropTypes.bool.isRequired, - handleLogout: PropTypes.func.isRequired -} - + handleLogout: PropTypes.func.isRequired, +}; const Header = () => { - const dispatch = useDispatch(); const accessToken = localStorage.getItem('accessToken'); - const [ isLoggedIn, setIsLoggedIn] = useState(!accessToken); + const [isLoggedIn, setIsLoggedIn] = useState(!accessToken); useEffect(() => { if (accessToken) { - setIsLoggedIn(true) + setIsLoggedIn(true); return; } - setIsLoggedIn(false) - } - , [accessToken]); + setIsLoggedIn(false); + }, [accessToken]); const navigate = useNavigate(); @@ -131,7 +124,7 @@ const Header = () => {
- ) -} + ); +}; -export default Header; \ No newline at end of file +export default Header; diff --git a/client/src/components/NavBar.tsx b/client/src/components/NavBar.tsx index fbdf03a..8f3cdfe 100644 --- a/client/src/components/NavBar.tsx +++ b/client/src/components/NavBar.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { useNavigate } from 'react-router-dom'; -import { selectActiveMenu, setActiveMenu } from '../store/menuSlice.js'; +import { selectActiveMenu, setActiveMenu } from '../store/menuSlice'; import '../styles/NavBar.css'; const NavBar = () => { diff --git a/client/src/components/PostEditer.jsx b/client/src/components/PostEditer.jsx index 4c55e23..d4a6d11 100644 --- a/client/src/components/PostEditer.jsx +++ b/client/src/components/PostEditer.jsx @@ -1,15 +1,14 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef } from 'react'; import '../styles/PostEditer.css'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPlus } from "@fortawesome/free-solid-svg-icons"; -import { postPosts, postVote } from "api/api.js"; +import { setActiveMenu } from '../store/menuSlice.ts'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPlus } from '@fortawesome/free-solid-svg-icons'; +import { postPosts, postVote } from 'api/api.js'; import jwtDecode from 'jwt-decode'; import { useNavigate } from 'react-router-dom'; -function PostEditer( ) { - +function PostEditer() { const accessToken = localStorage.getItem('accessToken'); const decodedToken = jwtDecode(accessToken); const userId = decodedToken.userId; @@ -22,52 +21,57 @@ function PostEditer( ) { title: '', body: '', open: 'true', - img: '' + img: '', }); - const handleInputChange = (e) => { + const handleInputChange = e => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; const handleCreatePost = () => { - if (formData.title !== '' && formData.body !== '') { postPosts(formData.type, formData.title, formData.body, formData.open, formData.img, userId) - .then((resp) => { + .then(resp => { postVote(resp.data.postId) - .then((response)=>{ + .then(response => { dispatch(setActiveMenu('전체 글 보기')); navigate('/'); }) - .catch((error)=> { - - }) + .catch(error => {}); }) - .catch((err) => { - - }); - + .catch(err => {}); } else { alert('제목과 내용을 모두 입력해주세요.'); } - - } + }; return ( <>
- - - + + +
); } - function PostEditerWithImage() { - const accessToken = localStorage.getItem('accessToken'); const decodedToken = jwtDecode(accessToken); const userId = decodedToken.userId; @@ -80,18 +84,18 @@ function PostEditerWithImage() { title: '', body: '', open: 'true', - img: '' + img: '', }); const [previewImage, setPreviewImage] = useState(null); const imageInputRef = useRef(null); - const handleFileInputChange = (e) => { - const file = e.target.files[0]; + const handleFileInputChange = e => { + const file = e.target.files[0]; setFormData({ ...formData, file }); if (file) { const reader = new FileReader(); - reader.onload = (e) => { + reader.onload = e => { setPreviewImage(e.target.result); }; reader.readAsDataURL(file); @@ -99,7 +103,7 @@ function PostEditerWithImage() { e.target.value = null; setFormData({ ...formData, img: file }); - } + } }; const handleImageClick = () => { @@ -108,7 +112,7 @@ function PostEditerWithImage() { } }; - const handleInputChange = (e) => { + const handleInputChange = e => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; @@ -116,50 +120,60 @@ function PostEditerWithImage() { const handleCreatePost = () => { if (formData.title !== '' && formData.body !== '' && previewImage !== null) { postPosts(formData.type, formData.title, formData.body, formData.open, formData.img, userId) - .then((resp) => { + .then(resp => { postVote(resp.data.postId) - .then((response)=>{ + .then(response => { dispatch(setActiveMenu('전체 글 보기')); navigate('/'); }) - .catch((error)=> { - - }) + .catch(error => {}); }) - .catch((err) => { - - }); + .catch(err => {}); } else { alert('이미지 업로드와 제목 및 내용을 모두 작성해주세요.'); } - - } - + }; return (
- -
- - {previewImage && } -
- 이미지 +
+ + {previewImage && ( + + )} +
+ + 이미지
- - - + + +
-
); } -export { PostEditerWithImage, PostEditer }; \ No newline at end of file +export { PostEditerWithImage, PostEditer }; diff --git a/client/src/components/SignUpForm.jsx b/client/src/components/SignUpForm.tsx similarity index 50% rename from client/src/components/SignUpForm.jsx rename to client/src/components/SignUpForm.tsx index 9dfad72..e57db95 100644 --- a/client/src/components/SignUpForm.jsx +++ b/client/src/components/SignUpForm.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState } from 'react'; import '../styles/SignUpForm.css'; import { postSignUp } from 'api/api.js'; import { useNavigate } from 'react-router-dom'; @@ -6,12 +6,12 @@ import { useNavigate } from 'react-router-dom'; function SignUpForm() { const navigate = useNavigate(); const [view, setView] = useState(false); - const [selected, setSelected] = useState("기억에 남는 추억의 장소는?"); + const [selected, setSelected] = useState('기억에 남는 추억의 장소는?'); const [formData, setFormData] = useState({ username: '', userid: '', password: '', - passwordConfirm: '' + passwordConfirm: '', }); const [showWarning, setShowWarning] = useState(false); const [isValid, setIsValid] = useState(true); @@ -30,9 +30,9 @@ function SignUpForm() { setIsValid(isValid => true); setValidMessage('특수문자 1개 이상, 영문·숫자 포함 8글자 이상'); } - } + }; - const validMessageStyle = {}; + const validMessageStyle: React.CSSProperties = {}; if (validMessage === '특수문자 1개 이상, 영문·숫자 포함 8글자 이상') { validMessageStyle.color = 'black'; } else if (validMessage === '유효하지 않은 패스워드 입니다') { @@ -41,25 +41,30 @@ function SignUpForm() { validMessageStyle.color = 'green'; } - const handleInputChange = (e) => { + const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; - const handleSubmit = (e) => { + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (validMessage === '사용가능한 패스워드 입니다' && formData.username !== '' && formData.userid !== '' && formData.password !== '' && formData.passwordConfirm !== '') { + if ( + validMessage === '사용가능한 패스워드 입니다' && + formData.username !== '' && + formData.userid !== '' && + formData.password !== '' && + formData.passwordConfirm !== '' + ) { setShowWarning(false); postSignUp(formData.username, formData.userid, formData.password, selected, formData.passwordConfirm) - .then((resp) => { - navigate('/'); - }) - .catch((err) => { - alert('이미 사용중인 회원정보 입니다.'); - }); - + .then(resp => { + navigate('/login'); + }) + .catch(err => { + alert('이미 사용중인 회원정보 입니다.'); + }); } else { setShowWarning(true); } @@ -67,82 +72,93 @@ function SignUpForm() { const handleDropdown = () => { setView(!view); - } + }; const handleSelectedMessagePlace = () => { setSelected(selected => '기억에 남는 추억의 장소는?'); - } + }; const handleSelectedMessagePet = () => { setSelected(selected => '반려동물의 이름은?'); - } + }; const handleSelectedMessageDate = () => { setSelected(selected => '추억하고 싶은 날짜가 있다면?'); - } - - + }; return ( <>
회원가입
- -
 이름
- +
+  이름
+
-
 아이디
- +
+  아이디
+
-
 비밀번호
- -
{validMessage}
+
+  비밀번호
+ +
+ {validMessage} +
-
비밀번호 확인 질문
- +
비밀번호 확인 답변
-
-
- - {showWarning && -
- 모든 입력을 채워주세요 -
} + {showWarning && ( +
+ 모든 입력을 채워주세요 +
+ )}
-
- - + ); } -export default SignUpForm; \ No newline at end of file +export default SignUpForm; diff --git a/client/src/pages/LoginPage.jsx b/client/src/pages/LoginPage.jsx index bc7fb81..4918f45 100644 --- a/client/src/pages/LoginPage.jsx +++ b/client/src/pages/LoginPage.jsx @@ -1,29 +1,29 @@ import React, { useState } from 'react'; import { postLogin } from 'api/api.js'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; +import { setActiveMenu } from '../store/menuSlice.ts'; import { Link, useNavigate } from 'react-router-dom'; import '../styles/Login.css'; const LogIn = () => { const dispatch = useDispatch(); const navigate = useNavigate(); - - const [ id, setId ] = useState(""); - const [ password, setPassword ] = useState(""); - const [ errorMessage, setErrorMessage ] = useState(""); - const emailHandler = (e) => { + const [id, setId] = useState(''); + const [password, setPassword] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + + const emailHandler = e => { setId(e.target.value); - } + }; - const passwordHandler = (e) => { + const passwordHandler = e => { setPassword(e.target.value); - } + }; - const submitHandler = async (e) => { + const submitHandler = async e => { e.preventDefault(); - + try { const result = await postLogin(id, password); if (result === true) { @@ -33,36 +33,36 @@ const LogIn = () => { setErrorMessage(result); } } catch (err) { - console.error() + console.error(); } - } + }; return ( - <> -
- logo -
-
+ <> +
+ logo + +
-
+
-
{errorMessage ? errorMessage : ''}
-
- +
{errorMessage ? errorMessage : ''}
+
+
-
-
-
비밀번호 찾기
-
|
- 회원가입 -
+
+
+
비밀번호 찾기
+
|
+ 회원가입 +
- ) -} + ); +}; -export default LogIn; \ No newline at end of file +export default LogIn; diff --git a/client/src/pages/MyPageInfo.tsx b/client/src/pages/MyPageInfo.tsx index 61128ff..da0ab81 100644 --- a/client/src/pages/MyPageInfo.tsx +++ b/client/src/pages/MyPageInfo.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; +import { setActiveMenu } from '../store/menuSlice'; import NavBar from '../components/NavBar'; import Modal from '../components/Modal'; import '../styles/Button.css'; diff --git a/client/src/pages/MyPost.jsx b/client/src/pages/MyPost.jsx index bf394bb..696a925 100644 --- a/client/src/pages/MyPost.jsx +++ b/client/src/pages/MyPost.jsx @@ -5,7 +5,7 @@ import jwtDecode from 'jwt-decode'; import NavBar from 'components/NavBar.tsx'; import { useNavigate, useParams, Link } from 'react-router-dom'; import { useDispatch } from 'react-redux'; -import { setActiveMenu } from '../store/menuSlice.js'; +import { setActiveMenu } from '../store/menuSlice.ts'; const MyPost = () => { const { postId } = useParams(); diff --git a/client/src/pages/SignUpPage.jsx b/client/src/pages/SignUpPage.jsx deleted file mode 100644 index 2857771..0000000 --- a/client/src/pages/SignUpPage.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import SignUpForm from '../components/SignUpForm.jsx'; -import '../styles/SignUpPage.css'; - -function SignUpPage() { - - return ( - <> -
- -
- - - ); -} - -export default SignUpPage; \ No newline at end of file diff --git a/client/src/pages/SignUpPage.tsx b/client/src/pages/SignUpPage.tsx new file mode 100644 index 0000000..d1c2c5b --- /dev/null +++ b/client/src/pages/SignUpPage.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import SignUpForm from '../components/SignUpForm'; +import '../styles/SignUpPage.css'; + +function SignUpPage() { + return ( + <> +
+ +
+ + ); +} + +export default SignUpPage; diff --git a/client/src/store/menuSlice.js b/client/src/store/menuSlice.ts similarity index 74% rename from client/src/store/menuSlice.js rename to client/src/store/menuSlice.ts index 4b6e59d..5d47585 100644 --- a/client/src/store/menuSlice.js +++ b/client/src/store/menuSlice.ts @@ -1,7 +1,8 @@ -import { createSlice } from '@reduxjs/toolkit'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { MenuState } from '../types/types'; // 초기 상태를 정의합니다. -const initialState = { +const initialState: MenuState = { activeMenu: '전체 글 보기', }; @@ -16,7 +17,7 @@ const menuSlice = createSlice({ name: 'menu', initialState, reducers: { - setActiveMenu: (state, action) => { + setActiveMenu: (state, action: PayloadAction) => { state.activeMenu = action.payload; // 메뉴가 변경될 때 로컬 스토리지에도 저장합니다. localStorage.setItem('activeMenu', action.payload); @@ -28,7 +29,7 @@ const menuSlice = createSlice({ export const { setActiveMenu } = menuSlice.actions; // 선택자 함수를 내보냅니다 (현재 활성 메뉴를 선택하는 역할) -export const selectActiveMenu = (state) => state.menu.activeMenu; +export const selectActiveMenu = (state: { menu: MenuState }) => state.menu.activeMenu; // 리듀서 함수를 내보냅니다 (상태 관리 수행 역할) export default menuSlice.reducer; diff --git a/client/src/types/types.ts b/client/src/types/types.ts index 5bfef6b..f3f4244 100644 --- a/client/src/types/types.ts +++ b/client/src/types/types.ts @@ -27,3 +27,7 @@ export interface ModalProps { onCancel: () => void; onConfirm: () => void; } + +export interface MenuState { + activeMenu: string; +} From cbba85b73cee2318ea141fc4e343c1ea375279c7 Mon Sep 17 00:00:00 2001 From: matchaing Date: Wed, 1 Nov 2023 16:41:11 +0900 Subject: [PATCH 17/22] =?UTF-8?q?chore=20:=20=EC=A0=9C=EB=AA=A9=20?= =?UTF-8?q?=EC=99=B8=20=ED=81=B4=EB=A6=AD=ED=95=B4=EB=8F=84=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/PostList.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/client/src/components/PostList.tsx b/client/src/components/PostList.tsx index 5f6b940..fac7f16 100644 --- a/client/src/components/PostList.tsx +++ b/client/src/components/PostList.tsx @@ -68,15 +68,14 @@ const PostList: React.FC = props => {
{visiblePosts.map((post: Post) => (
-
- - {post.title} - -
{new Date(post.createdAt).toLocaleDateString()}
-
-
{post.body.length > 90 ? `${post.body.slice(0, 90)}...` : post.body}
+ +
+
{post.title}
+
{new Date(post.createdAt).toLocaleDateString()}
+
+
{post.body.length > 90 ? `${post.body.slice(0, 90)}...` : post.body}
+
))} {loading &&
Loading...
} From 5a5e6c981b0eb1ef7f60b89685c7874d01c6bc47 Mon Sep 17 00:00:00 2001 From: jumpm9239 Date: Wed, 1 Nov 2023 17:12:33 +0900 Subject: [PATCH 18/22] =?UTF-8?q?fix:=20accessToken=20=EA=B0=B1=EC=8B=A0?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=ED=95=B8=EB=93=A4=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/api/api.js | 65 ++++++++++++++++++++++------- client/src/components/AppHeader.jsx | 2 +- client/src/store/authSlice.js | 6 ++- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/client/src/api/api.js b/client/src/api/api.js index 8ef687c..e5884da 100644 --- a/client/src/api/api.js +++ b/client/src/api/api.js @@ -1,11 +1,13 @@ import axios from "axios"; +import jwtDecode from "jwt-decode"; +import Cookies from 'js-cookie'; +import { useDispatch } from 'react-redux'; +import { setAccessToken, setRefreshToken } from "store/authSlice.js"; axios.defaults.withCredentials = true; export const instance = axios.create({ - baseURL: 'http://ec2-52-78-145-37.ap-northeast-2.compute.amazonaws.com:8080', - timeout: 5000, }); @@ -165,6 +167,10 @@ export const postLogin = async ( id, password ) => { const auth = res.headers['authorization']; const accessToken = auth.substring(6); localStorage.setItem('accessToken', accessToken); + + const refresh = res.headers['refresh']; + Cookies.set('refreshToken', refresh); + return true; } catch (error) { @@ -176,16 +182,45 @@ export const postLogin = async ( id, password ) => { } instance.interceptors.request.use( - function (config) { - // decode 토큰 만료 시간 확인하는 함수 isValid(Token) 넣기 - const accessToken = localStorage.getItem('accessToken'); - - if (accessToken /* && 함수 */) { - instance.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`; - } else { - // - } - - return config; - } -) \ No newline at end of file + async function (config) { + if (config.method === 'get' ) return config; + else { + const isValid = (token) => { + const decodedToken = jwtDecode(token) + const tokenExpirationTime = decodedToken.exp; + const currentTime = Math.floor(Date.now() / 1000); + return tokenExpirationTime >= currentTime; + } + + const accessToken = localStorage.getItem('accessToken'); + const userId = accessToken.userId; + const refreshToken = Cookies.get('refreshToken'); + + if (accessToken && isValid(accessToken)) { + instance.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`; + instance.defaults.headers.common['Refresh'] = `${refreshToken}`; + return config; + } else if (refreshToken && isValid(refreshToken)) { + + try { + const dispatch = useDispatch(); + const res = await instance.patch(`user/refresh/${userId}`); + + const newAuth = res.headers['authorization']; + const newAccessToken = newAuth.substring(6); + localStorage.setItem('accessToken', newAccessToken); + dispatch(setAccessToken(newAccessToken)); + instance.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; + + const newRefresh = res.headers['refresh']; + Cookies.set('refreshToken', newRefresh); + dispatch(setRefreshToken(newRefresh)); + instance.defaults.headers.common['Refresh'] = `${newRefresh}`; + } catch (error) { + console.error('accessToken을 갱신하지 못했습니다.'); + throw error; + } + } + } + return config; +}); diff --git a/client/src/components/AppHeader.jsx b/client/src/components/AppHeader.jsx index 33527ff..2b7b7c3 100644 --- a/client/src/components/AppHeader.jsx +++ b/client/src/components/AppHeader.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; import "../styles/Header.css"; import { useDispatch, useSelector } from 'react-redux'; import UserHeader from './UserHeader.jsx'; diff --git a/client/src/store/authSlice.js b/client/src/store/authSlice.js index 1b9f8da..64cf9b8 100644 --- a/client/src/store/authSlice.js +++ b/client/src/store/authSlice.js @@ -5,6 +5,7 @@ const authSlice = createSlice({ initialState: { isLoggedIn: false, accessToken: null, + refreshToken: null, }, reducers: { setLoggedIn: (state, action) => { @@ -13,6 +14,9 @@ const authSlice = createSlice({ setAccessToken: (state, action) => { state.accessToken = action.payload; }, + setRefreshToken: (state, action) => { + state.refreshToken = action.payload; + }, logout: (state) => { state.isLoggedIn = false; state.accessToken = null; @@ -20,5 +24,5 @@ const authSlice = createSlice({ }, }); -export const { setLoggedIn, setAccessToken, logout } = authSlice.actions; +export const { setLoggedIn, setAccessToken, setRefreshToken, logout } = authSlice.actions; export default authSlice.reducer; \ No newline at end of file From f8c106b124bd311bbab07944c79a4e651b1adb8b Mon Sep 17 00:00:00 2001 From: jumpm9239 <129942171+jumpm9239@users.noreply.github.com> Date: Thu, 2 Nov 2023 05:11:03 +0000 Subject: [PATCH 19/22] Update UserHeader.jsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 충돌 해결 (예전 컴포넌트 이름 삭제) --- client/src/components/UserHeader.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/components/UserHeader.jsx b/client/src/components/UserHeader.jsx index e6ba4bf..e64ef38 100644 --- a/client/src/components/UserHeader.jsx +++ b/client/src/components/UserHeader.jsx @@ -10,7 +10,6 @@ import { logout, setLoggedIn } from 'store/authSlice.js'; const UserHeader = ( { isLoggedIn } ) => { -const HeaderLoggedIn = ({ isLoggedIn, handleLogout }) => { const accessToken = localStorage.getItem('accessToken'); const [userName, setUserName] = useState(''); const dispatch = useDispatch(); From 92dddb2100ba26f02cc9fe24e035694b784441a8 Mon Sep 17 00:00:00 2001 From: jumpm9239 Date: Thu, 2 Nov 2023 14:18:08 +0900 Subject: [PATCH 20/22] =?UTF-8?q?chore:=20import=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=20.js=20->=20.fs=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/build/asset-manifest.json | 15 --- client/build/index.html | 1 - client/build/static/css/main.f4fdb1bd.css | 2 - client/build/static/css/main.f4fdb1bd.css.map | 1 - client/build/static/js/main.4fcd2700.js | 3 - .../static/js/main.4fcd2700.js.LICENSE.txt | 113 ------------------ client/build/static/js/main.4fcd2700.js.map | 1 - .../media/logo.d61babd8e60c9f74e60d.png | Bin 13489 -> 0 bytes .../user_shadow.697fdcd21f6d157b9073.png | Bin 16943 -> 0 bytes client/src/components/UserHeader.jsx | 2 +- client/src/store/rootReducer.js | 2 +- 11 files changed, 2 insertions(+), 138 deletions(-) delete mode 100644 client/build/asset-manifest.json delete mode 100644 client/build/index.html delete mode 100644 client/build/static/css/main.f4fdb1bd.css delete mode 100644 client/build/static/css/main.f4fdb1bd.css.map delete mode 100644 client/build/static/js/main.4fcd2700.js delete mode 100644 client/build/static/js/main.4fcd2700.js.LICENSE.txt delete mode 100644 client/build/static/js/main.4fcd2700.js.map delete mode 100644 client/build/static/media/logo.d61babd8e60c9f74e60d.png delete mode 100644 client/build/static/media/user_shadow.697fdcd21f6d157b9073.png diff --git a/client/build/asset-manifest.json b/client/build/asset-manifest.json deleted file mode 100644 index ad7a1ec..0000000 --- a/client/build/asset-manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "files": { - "main.css": "/static/css/main.f4fdb1bd.css", - "main.js": "/static/js/main.4fcd2700.js", - "static/media/user_shadow.png": "/static/media/user_shadow.697fdcd21f6d157b9073.png", - "static/media/logo.png": "/static/media/logo.d61babd8e60c9f74e60d.png", - "index.html": "/index.html", - "main.f4fdb1bd.css.map": "/static/css/main.f4fdb1bd.css.map", - "main.4fcd2700.js.map": "/static/js/main.4fcd2700.js.map" - }, - "entrypoints": [ - "static/css/main.f4fdb1bd.css", - "static/js/main.4fcd2700.js" - ] -} \ No newline at end of file diff --git a/client/build/index.html b/client/build/index.html deleted file mode 100644 index c432be4..0000000 --- a/client/build/index.html +++ /dev/null @@ -1 +0,0 @@ -그린어스포어스
\ No newline at end of file diff --git a/client/build/static/css/main.f4fdb1bd.css b/client/build/static/css/main.f4fdb1bd.css deleted file mode 100644 index 4025bd6..0000000 --- a/client/build/static/css/main.f4fdb1bd.css +++ /dev/null @@ -1,2 +0,0 @@ -.page_container{padding-left:280px;padding-top:120px;width:90%}.auth_board_container,.env_board_container{background-color:#fff;border-radius:15px;box-shadow:0 4px 4px rgba(0,0,0,.25);height:400px;margin-bottom:30px;margin-top:10px;padding:20px;width:100%}.auth_detail_container{display:flex;gap:20px;width:102%}.auth_detail_container_image{background-color:#fff;border-radius:15px;box-shadow:0 4px 4px rgba(0,0,0,.25);height:400px;margin:10px 0 30px;padding:20px;width:50%}.auth_detail_container_image img{height:100%;width:100%}.auth_detail_container_post{background-color:#fff;border-radius:15px;box-shadow:0 4px 4px rgba(0,0,0,.25);height:400px;margin:10px 0 30px;padding:20px;width:50%}.free_board_container{width:103%}.free_board_container,.free_detail_container{background-color:#fff;border-radius:15px;box-shadow:0 4px 4px rgba(0,0,0,.25);height:70%;margin-bottom:30px;margin-top:10px;padding:10px}.free_detail_container{width:101%}.detail_comment_container{display:flex;justify-content:space-between;padding:10px}.signUp_wrapper{align-items:center;display:flex;flex-direction:column;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.signUp_title{font-size:22px}.signUp_form{align-items:center;background-color:#fff;border-radius:10px;border-top:none;box-shadow:0 5px 4px #8dc693;display:flex;flex-direction:column;height:575px;margin-top:6px;width:395px}.id_input_area,.name_input_area,.password_input_area{font-size:15px;margin-top:33px}.id_input,.name_input,.password_input{border:1px solid #8dc693;border-radius:10px;height:40px;margin-top:5px;padding-left:7px;width:310px}.password_infomation{color:#40394a;font-size:8px;margin-left:5px;margin-top:5px}.password_confirm{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;margin-top:33px;width:350px}.password_confirm_A,.password_confirm_Q{align-items:center;display:flex;gap:18px;justify-content:center;margin:20px 5px}.password_confirm_A_title,.password_confirm_Q_title{font-size:12.5px}.select_box{border:1px solid #8dc693;border-radius:10px;box-sizing:border-box;cursor:pointer;font-size:12px;height:30px;line-height:30px;padding-left:12px;position:relative;width:220px}.arrow_after{right:22px}.arrow_after,.arrow_before{background-color:#000;height:10px;position:absolute;top:10px;width:1px}.arrow_before{right:15px}.select_box>ul{background-color:#fff;display:flex;flex-direction:column;gap:0;height:99px;left:7px;position:absolute;top:29px}.select_box li,.select_box>ul{box-sizing:border-box;width:180px}.select_box li{background-color:#fafafa;border-bottom:1px solid #d9d9d9;font-size:12px;height:33px;line-height:33px;list-style:none;padding-left:5px;text-align:left}.select_box li:hover{background-color:#d9d9d9}.password_confirm_Q>select{box-sizing:border-box;height:30px;width:190px}.password_confirm_answer_input{border:1px solid #8dc693;border-radius:10px;box-sizing:border-box;font-size:12px;height:30px;padding-left:12px;width:220px}.signup_complete{background-color:#8dc693;border:none;border-radius:15px;color:#fff;cursor:pointer;font-size:15px;height:50px;line-height:50px;margin-top:20px;text-align:center;width:220px}.signup_complete:hover{background-color:#82b888}.signup_complete:active{-webkit-transform:translateY(1px);transform:translateY(1px)}.signUpPage_wrapper{display:flex;justify-content:center;padding-top:180px}*{--box-color:#8dc693;--text-title:#2b2b2b;--text-sub:#888}.login_container{align-items:center;display:flex;flex-direction:column;gap:15px;justify-content:center;padding-top:120px}.login_form_logo{margin-top:30px;max-width:40px}.login_form{align-items:center;border-radius:8px;box-shadow:0 2px 3px 0 var(--box-color);display:flex;flex-direction:column;gap:10px;height:auto;justify-content:center;padding:35px 0;width:380px}.login_id{padding-bottom:10px}.login_id,.login_password{display:flex;flex-direction:column;width:200px}#id,#password{border:.5px solid var(--box-color);border-radius:3px;height:30px}.error_message{align-items:center;color:red;font-size:9pt;justify-content:left;padding:5px 0;width:200px}.login_submit{background-color:var(--box-color);border:none;border-radius:5px;color:#fff;height:30px;width:80px}.links{color:var(--text-sub);display:flex;flex-direction:row;font-size:11pt;gap:5px;justify-content:center;margin:20px 0}.editer_wrapper.wide{height:60vh;width:85%}.post_complete_btn.wide{margin-right:5%}.post_editer_with_image{display:flex;gap:30px;height:90%;width:85%}.image_upload{align-items:center;border:1px solid #9e9e9e;border-radius:10px;color:#2b2b2b;display:flex;font-size:16px;height:60vh;justify-content:center;line-height:16px;position:relative;width:45%}.plus_image_icon{position:absolute}.plus_image_icon.clear{opacity:0}.image_upload>img.uploade_img{border-radius:10px;height:100%;max-width:none;width:100%}.image_upload>input{border-radius:10px;cursor:pointer;height:100%;opacity:0;position:absolute;width:100%}.plus_icon{font-size:12px;margin-bottom:2px;margin-right:2px}.editer_wrapper{align-items:center;border-radius:10px;box-shadow:0 4px 4px rgba(0,0,0,.4);display:flex;flex-direction:column;height:60vh;width:55%}.post_title_input{height:9.5%;margin-bottom:15px;padding:0 10px}.post_text_area,.post_title_input{border:1px solid #8dc693;width:90%}.post_text_area{height:50%;padding:10px;resize:none}.post_complete_btn{background-color:#8dc693;border:none;border-radius:15px;color:#fff;font-size:16px;font-weight:600;height:40px;margin:20px 5% 13px auto;width:70px}.post_complete_btn:hover{background-color:#82b888}.post_complete_btn:active{-webkit-transform:translateY(1px);transform:translateY(1px)}.page_container.editCenter{margin-left:40px;padding-top:150px}.board_btn_container{width:85%}.free_board_btn{background-color:#8dc693;border:1px solid #8dc693;border-radius:15px;color:#fff;font-weight:700;height:50px;margin:0 30px 30px 0;width:200px}.free_board_btn.unselected{background-color:#fff;color:#000}.photo_board_btn{border:1px solid #8dc693;border-radius:15px;font-weight:700;height:50px;width:200px}.photo_board_btn.selected{background-color:#8dc693;color:#fff}.navbar{background-color:#fff;border-right:5px solid #fafafa;box-sizing:border-box;height:100%;margin-top:120px;padding:20px;position:fixed;width:253px}.text_menu_big{color:#000;font-size:19px;font-weight:700;line-height:60px;margin-left:40px}.menu_button{background-color:initial;border:none;color:#2b2b2b;cursor:pointer;font-size:14px;font-weight:700;height:70px;margin-left:60px;padding:10px;text-align:left;transition:background-color .3s,color .3s;width:170px}.menu_button.active{background-color:#8dc693;color:#fff;font-weight:700}.custom_button{height:50px}.custom_board_button,.custom_button{align-items:center;border-radius:15px;display:inline-flex;font-weight:700;gap:10px;justify-content:center;padding:23px 30px}.custom_board_button{height:79px;height:50px;margin-top:30px;width:250px}.custom_mypage_button{align-items:center;border-radius:15px;display:inline-flex;font-weight:700;gap:10px;height:50px;justify-content:center;padding:20px}.confirm_button{background-color:#8dc693;border:none;color:#fff;margin-right:20px}.cancel_button{background-color:#fafafa;border:1px solid #9e9e9e;color:#2b2b2b}.post_list_container{align-items:center;display:flex;flex-direction:column;gap:5px;width:100%}.post_item{background-color:#fff;border-bottom:1px solid #ddd;margin:30px 30px 10px;padding:20px;width:90%}.post_header{align-items:baseline;display:flex;justify-content:space-between}.post_title{color:#2b2b2b;font-size:18px;font-weight:700;text-decoration:none}.post_content{color:#2b2b2b;height:32px;margin:10px 0;width:100%}.post_date{color:#2b2b2b;font-size:14px;margin-top:10px;text-align:right}.post_detail_header{align-items:baseline;border-bottom:1px solid #ddd;display:flex;justify-content:space-between;padding:20px}.post_detail_title{font-size:18px;font-weight:700}.post_detail_content,.post_detail_content_auth{color:#2b2b2b;height:auto;margin:10px 0;padding:20px;width:100%}.post_detail_content_auth{height:210px}.vote_button{padding:15px}.comment_content{border-bottom:1px solid #ddd;padding-bottom:20px}.comment_input{border:1px solid #8dc693;border-radius:10px;justify-content:space-between;margin:5px;padding:15px;width:90%}.comment_button{align-items:center;background-color:#8dc693;border:none;border-radius:15px;color:#fff;display:inline-flex;font-weight:700;gap:10px;height:30px;justify-content:center;margin:5px;padding:23px}.auth_post_grid{grid-gap:30px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(2,1fr);padding:10px 50px}.auth_post_item_container{align-items:center;border:1px solid #9e9e9e;border-radius:10px;display:flex;flex-direction:column;height:160px;justify-content:center;margin-bottom:10px;overflow:hidden;text-align:center;width:100%}.auth_post_item_container img{height:100%;object-fit:cover;width:100%}.env_post_grid{grid-gap:30px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(2,1fr);padding:30px 50px}.env_post_item_container{align-items:center;border:1px solid #9e9e9e;border-radius:10px;display:flex;flex-direction:column;gap:10px;height:100%;margin-bottom:10px;width:100%}.env_post_item_container img{height:150px;max-width:100%}.pagination{margin-top:20px}.pagination button{background-color:initial;border:1px solid #8dc693;border-radius:5px;color:#333;cursor:pointer;font-size:14px;height:30px;margin:0 5px;transition:background-color .3s,color .3s;width:30px}.pagination button.active,.pagination button:hover{background-color:#8dc693;color:#fff}.header_container{background-color:#fafafa;justify-content:flex-start;padding:32px 30px 32px 35px;position:fixed;width:100vw}.header_bar,.header_container{align-items:center;display:flex;flex-direction:row}.header_bar{justify-content:center;width:88vw}.header_logo{display:flex;flex:1 1 auto;flex-direction:row}.header_logo img{max-width:230px}.search{color:#2b2b2b;flex:100 1 auto;flex-direction:row;justify-content:left;opacity:0;pointer-events:none}.header_bar_user,.search{align-items:center;display:flex}.header_bar_user{flex:1 1 auto;flex-direction:row;gap:30px;justify-content:right}.header_profile{align-items:center;display:flex;flex-direction:row;gap:10px;justify-content:center;padding-top:3px}.header_user_picture{max-width:45px}.header_icon{align-items:center;cursor:pointer;display:flex;flex-direction:row;justify-content:center}.mypage_main_container{display:flex;flex-direction:column;justify-content:LEFT;margin-bottom:50px;padding:120px 0 0 280px;width:92%}.my_posts{margin:35px 0 15px}ul{gap:10px;margin-bottom:10px}.custom_button{background-color:#fff;border:1px solid #8dc693;color:#2b2b2b;cursor:pointer}.custom_button.active{background-color:#8dc693;color:#fff}.mypage_posts_container{box-shadow:0 4px 4px rgba(0,0,0,.25);flex:0 1;gap:10px;height:auto;margin-top:15px}.mypage_posts_container,.post{display:flex;flex-direction:column}.post{border-bottom:1px solid #d9d9d9;flex:0 1 auto;gap:5px;margin:0 20px;padding:25px 15px}.post_info{display:flex;flex-direction:row;justify-content:space-between}.post_title{font-size:15pt}.mypage_post_content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.posts_container>article:first-child{margin-top:15px}.posts_container>article:last-child{border-bottom:0;margin-bottom:5px}.disabled{color:#d3d3d3}.pagination{align-items:center;display:flex;flex-direction:row;gap:10px;justify-content:center;margin:15px 0 20px}button{border:none;border-radius:4px;font-size:10pt;padding:2px 4px}.modal_container{background:#fff;background-color:#fff;border:1px solid #2b2b2b;border-radius:15px;border-radius:8px;box-shadow:0 0 10px rgba(0,0,0,.3);flex-direction:column;gap:20px;padding:80px;width:40%}.modal_container,.modal_overlay{align-items:center;display:flex;justify-content:center}.modal_overlay{background-color:rgba(0,0,0,.5);bottom:0;left:0;position:fixed;right:0;top:0}.modal_content{margin-bottom:20px}.my_custom_container{align-items:center;display:flex;flex-direction:row}.id_container{margin-top:110px}.circle_container{align-items:center;background-color:#d3d3d3;border-radius:50%;display:flex;height:70px;justify-content:center;margin:10px;overflow:hidden;position:relative;width:70px}.circle_input{height:100%;opacity:0;position:absolute;width:100%}.circle_image{height:100%;object-fit:cover;width:100%}.custom_label_id{font-size:13px;font-weight:700;margin:20px}.custom_label_pw{font-size:13px;font-weight:700;margin:20px 27px 20px 20px}.input_container{display:flex;flex-direction:column;height:50px;justify-content:center;margin-left:20px;margin-right:40px;width:250px}.nickname_input{border:1px solid #8dc693;border-radius:15px;color:#838383;font-size:15px;height:100%;padding:10px;width:100%}.leave_button{font-size:7px;margin:20px 17px 17px;text-align:left!important}.my_info{margin-top:35px}*{box-sizing:border-box;margin:0;padding:0}.my_post_container{color:#2b2b2b;display:flex;flex-direction:column;height:auto;padding-left:280px;padding-top:120px;width:91%}.my_post{margin:35px 0 15px}.my_post,ul{display:flex;flex-direction:row}ul{margin:0}li,ul{align-items:center}li{display:flex;flex-direction:row;list-style:none}.openOrNot{align-items:center;background-color:#fff;border:1px solid #8dc693;border-radius:15px;color:#2b2b2b;display:inline-flex;font-size:11pt;font-weight:700;gap:10px;height:50px;justify-content:center;padding:15px 29px;white-space:nowrap}.openOrNot.active{background-color:#8dc693;color:#fff}.each_post{box-shadow:0 4px 4px rgba(0,0,0,.25);display:flex;flex:0 1 auto;flex-direction:column;gap:10px;margin:15px 0 50px;max-height:550px;overflow-y:auto;padding:15px 15px 10px;width:auto}.buttons{gap:5px;justify-content:right;padding-right:15px;text-decoration:none;top:5px}.buttons,.delete_button,.edit_button,span{align-items:center;display:flex;flex-direction:row}.delete_button,.edit_button,span{background-color:#fff;font-size:11pt;justify-content:center}.each_post_info{align-items:center;justify-content:space-between;padding:0 15px}.each_post_date,.each_post_info,.user_info{display:flex;flex-direction:row}.user_info{align-items:center;border-bottom:1px solid #d9d9d9;gap:10px;justify-content:left;padding:10px 15px}.each_profile_image{border-radius:50%;height:40px;width:40px}.user_name{font-size:13pt}.each_post_content{align-items:flex-start;display:flex;flex-direction:row;font-size:11pt;gap:20px;height:auto;margin-bottom:30px;padding-top:15px}.uploaded{width:360px}@media screen and (max-width:768px){.uploaded{width:180px}}.post_writing{display:flex;flex-direction:row;margin-right:15px;white-space:pre-wrap}.no_image{width:100%}a{color:inherit;text-decoration:none} -/*# sourceMappingURL=main.f4fdb1bd.css.map*/ \ No newline at end of file diff --git a/client/build/static/css/main.f4fdb1bd.css.map b/client/build/static/css/main.f4fdb1bd.css.map deleted file mode 100644 index a0e5957..0000000 --- a/client/build/static/css/main.f4fdb1bd.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"static/css/main.f4fdb1bd.css","mappings":"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","sources":["App.css","styles/SignUpForm.css","styles/SignUpPage.css","styles/Login.css","styles/PostEditer.css","styles/EditerPage.css","styles/NavBar.css","styles/Button.css","styles/PostList.css","styles/BoardDetailPage.css","styles/AuthPostList.css","styles/EnvPostList.css","styles/Header.css","styles/MyPageMain.css","styles/Pagination.css","styles/Modal.css","styles/MyPageInfo.css","styles/MyPost.css"],"sourcesContent":[".page_container {\n padding-top: 120px;\n padding-left:280px;\n width: 90%;\n}\n\n.auth_board_container,\n.env_board_container{\n width: 100%;\n height: 400px;\n margin-top: 10px;\n margin-bottom: 30px;\n border-radius: 15px;\n background-color: #ffffff;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n padding: 20px;\n}\n\n.auth_detail_container{\n display: flex;\n gap: 20px;\n width: 102%;\n}\n\n.auth_detail_container_image{\n width: 50%;\n height: 400px;\n margin: 10px 0 30px;\n border-radius: 15px;\n background-color: #ffffff;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n padding: 20px;\n}\n.auth_detail_container_image img {\n width: 100%;\n height: 100%;\n /* object-fit: cover; */\n}\n\n.auth_detail_container_post {\n width: 50%;\n height: 400px;\n margin: 10px 0 30px;\n border-radius: 15px;\n background-color: #ffffff;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n padding: 20px;\n}\n\n.free_board_container{\n width: 103%;\n height: 70%;\n margin-top: 10px;\n margin-bottom: 30px;\n padding: 10px;\n border-radius: 15px;\n background-color: #ffffff;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n}\n\n.free_detail_container{\n width: 101%;\n height: 70%;\n margin-top: 10px;\n margin-bottom: 30px;\n padding: 10px;\n border-radius: 15px;\n background-color: #ffffff;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n}\n\n.detail_comment_container{\n display: flex;\n justify-content: space-between;\n padding: 10px;\n}\n",".signUp_wrapper {\n display: flex;\n flex-direction: column;\n width: fit-content;\n height: fit-content;\n align-items: center;\n}\n\n.signUp_title {\n font-size: 22px;\n}\n\n.signUp_form {\n width: 395px;\n height: 575px;\n /* border: 1px solid #8DC693; */\n border-top: none;\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-top: 6px;\n box-shadow: 0 5px 4px #8DC693;\n background-color: #fff;\n}\n\n.name_input_area, \n.id_input_area, \n.password_input_area {\n margin-top: 33px;\n font-size: 15px;\n} \n\n.name_input, \n.id_input, \n.password_input {\n width: 310px;\n height: 40px;\n margin-top: 5px;\n border-radius: 10px;\n border: 1px solid #8DC693;\n padding-left: 7px;\n}\n\n.password_infomation {\n font-size: 8px;\n margin-top: 5px;\n margin-left: 5px;\n color: #40394A;\n}\n\n.password_confirm {\n width: 350px;\n height: fit-content;\n margin-top: 33px;\n}\n\n.password_confirm_Q,\n.password_confirm_A {\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 20px 5px;\n gap: 18px;\n}\n\n.password_confirm_Q_title,\n.password_confirm_A_title {\n font-size: 12.5px;\n}\n\n.select_box {\n width: 220px;\n height: 30px;\n border: 1px solid #8DC693;\n position: relative;\n border-radius: 10px;\n font-size: 12px;\n line-height: 30px;\n padding-left: 12px;\n box-sizing: border-box;\n cursor: pointer;\n}\n\n.arrow_after {\n width: 1px;\n height: 10px;\n background-color: black;\n position: absolute;\n right: 22px;\n top: 10px;\n}\n\n.arrow_before {\n width: 1px;\n height: 10px;\n background-color: black;\n position: absolute;\n right: 15px;\n top: 10px;\n}\n\n.select_box > ul {\n width: 180px;\n height: 99px;\n box-sizing: border-box;\n position: absolute;\n top: 29px;\n left: 7px;\n background-color: #fff;\n gap: 0;\n display: flex;\n flex-direction: column;\n}\n\n.select_box li {\n list-style: none;\n width: 180px;\n height: 33px;\n font-size: 12px;\n line-height: 33px;\n text-align: left;\n box-sizing: border-box;\n padding-left: 5px;\n background-color: #fafafa;\n border-bottom: 1px solid #d9d9d9;\n}\n\n.select_box li:hover {\n background-color: #d9d9d9;\n}\n\n.password_confirm_Q > select {\n width: 190px;\n height: 30px;\n box-sizing: border-box;\n}\n\n.password_confirm_answer_input {\n width: 220px;\n height: 30px;\n box-sizing: border-box;\n border-radius: 10px;\n border: 1px solid #8DC693;\n font-size: 12px;\n padding-left: 12px;\n}\n\n.signup_complete {\n width: 220px;\n height: 50px;\n background-color: #8DC693;\n margin-top: 20px;\n border-radius: 15px;\n line-height: 50px;\n text-align: center;\n font-size: 15px;\n color: #fff;\n cursor: pointer;\n border: none;\n}\n\n.signup_complete:hover {\n background-color: #82b888;\n}\n\n.signup_complete:active {\n transform: translateY(1px);\n}",".signUpPage_wrapper {\n display: flex;\n justify-content: center;\n padding-top: 180px;\n} ","* {\n box-sizing: border-box;\n --box-color: rgb(141,198,147);\n --text-title: rgb(43, 43, 43);\n --text-sub: rgba(136, 136, 136, 1);\n}\n\n.login_container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding-top: 120px;\n gap: 15px;\n}\n\n.login_form_logo {\n max-width: 40px;\n margin-top: 30px;\n}\n\n.login_form {\n padding: 35px 0;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n width: 380px;\n height: auto;\n border-radius: 8px;\n box-shadow: 0px 2px 3px 0px var(--box-color);\n}\n\n.login_form {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 10px;\n}\n\n.login_id {\n display: flex;\n flex-direction: column;\n width: 200px;\n padding-bottom: 10px;\n}\n\n.login_password {\n display: flex;\n flex-direction: column;\n width: 200px;\n}\n\n#id, #password {\n height: 30px;\n border: 0.5px solid var(--box-color) ;\n border-radius: 3px;\n}\n\n.error_message {\n color: red;\n font-size: 9pt;\n width: 200px;\n justify-content: left;\n align-items: center;\n padding: 5px 0;\n}\n\n.login_submit {\n background-color: var(--box-color);\n border: none;\n border-radius: 5px;\n color: white;\n width: 80px;\n height: 30px;\n}\n\n.links {\n display: flex;\n flex-direction: row;\n color: var(--text-sub);\n gap: 5px;\n font-size: 11pt;\n margin: 20px 0;\n justify-content: center;\n}","/* PostEditer */\n.editer_wrapper.wide {\n width: 85%;\n height: 60vh;\n}\n\n.post_complete_btn.wide {\n margin-right: calc(10% / 2);\n}\n\n\n\n/* PostEditerWithImage */\n.post_editer_with_image {\n width: 85%;\n height: 90%;\n display: flex;\n gap: 30px;\n}\n\n.image_upload {\n width: 45%;\n height: 60vh;\n border: 1px solid #9e9e9e;\n border-radius: 10px;\n color: #2b2b2b;\n font-size: 16px;\n line-height: 16px;\n display: flex;\n justify-content: center;\n align-items: center;\n position: relative;\n}\n\n.plus_image_icon {\n position: absolute;\n\n}\n\n.plus_image_icon.clear {\n opacity: 0;\n\n}\n\n.image_upload > img.uploade_img {\n height: 100%;\n border-radius: 10px;\n width: 100%;\n max-width: none;\n}\n\n.image_upload > input {\n width: 100%;\n height: 100%;\n border-radius: 10px;\n cursor: pointer;\n opacity: 0;\n position: absolute;\n}\n\n.plus_icon {\n font-size: 12px;\n margin-right: 2px;\n margin-bottom: 2px;\n}\n\n.editer_wrapper {\n width: 55%;\n height: 60vh;\n border-radius: 10px;\n box-shadow: 0 4px 4px rgba(0, 0, 0, .4);\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.post_title_input {\n width: 90%;\n height: 9.5%;\n padding: 0 10px;\n margin-bottom: 15px;\n border: 1px solid #8DC693;\n}\n\n.post_text_area {\n width: 90%;\n height: 50%;\n resize: none;\n padding: 10px;\n border: 1px solid #8DC693;\n}\n\n.post_complete_btn {\n border-radius: 15px;\n width: 70px;\n height: 40px;\n border: none;\n background-color: #8DC693;\n color: #fff;\n font-weight: 600;\n font-size: 16px;\n margin: 20px calc(10% / 2) 13px auto;\n}\n\n.post_complete_btn:hover {\n background-color: #82b888;\n}\n\n.post_complete_btn:active {\n transform: translateY(1px);\n}\n",".page_container.editCenter {\n padding-top: 150px;\n margin-left: 40px;\n}\n\n.board_btn_container {\n width: 85%;\n}\n.free_board_btn {\n width: 200px;\n height: 50px;\n border-radius: 15px;\n margin: 0 30px 30px 0;\n background-color: #8DC693;\n border: 1px solid #8DC693;\n font-weight: 700;\n color: #fff;\n}\n\n.free_board_btn.unselected {\n background-color: #fff;\n color: #000;\n}\n\n.photo_board_btn {\n width: 200px;\n height: 50px;\n border-radius: 15px;\n border: 1px solid #8DC693;\n font-weight: 700;\n}\n\n.photo_board_btn.selected {\n background-color: #8DC693;\n color: #fff;\n}",".navbar {\n background-color:white;\n margin-top: 120px;\n width: 253px;\n padding: 20px;\n box-sizing: border-box;\n border-right: 5px solid #FAFAFA;\n position: fixed;\n height: 100%;\n }\n\n /* 게시판, 마이페이지 */\n .text_menu_big {\n color: #000;\n font-size: 19px;\n font-weight: bold;\n margin-left: 40px;\n /* margin-top: 40px; */\n line-height: 60px;\n }\n \n /* 나머지 메뉴 버튼 */\n .menu_button {\n width: 170px;\n height: 70px;\n border: none;\n background-color: transparent;\n color: #2B2B2B;\n font-weight: bold;\n text-align: left;\n margin-left: 60px;\n padding: 10px;\n font-size: 14px;\n cursor: pointer;\n transition: background-color 0.3s, color 0.3s;\n }\n \n /* 클릭 했을 때 */\n .menu_button.active {\n background-color: #8dc693;\n font-weight: bold;\n color: white;\n }","/* 버튼 공통 스타일*/\n.custom_button {\n display: inline-flex;\n height: 50px;\n padding: 23px 30px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 15px;\n font-weight: bold;\n}\n/* 게시글 버튼 */\n.custom_board_button {\n width: 250px;\n height: 79px;\n display: inline-flex;\n height: 50px;\n padding: 23px 30px;\n margin-top: 30px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 15px;\n font-weight: bold;\n}\n\n/* 마이페이지 버튼 */\n.custom_mypage_button{\n display: inline-flex;\n height: 50px;\n padding: 20px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 15px;\n font-weight: bold;\n}\n\n/* 확인 버튼 */\n.confirm_button {\n background-color: #8DC693;\n border: none;\n color: white;\n margin-right: 20px;\n}\n/* 취소 버튼 */\n.cancel_button {\n border: 1px solid #9E9E9E;\n background-color: #FAFAFA;\n color: #2B2B2B;\n}\n",".post_list_container {\n display: flex;\n flex-direction: column;\n gap: 5px;\n align-items: center;\n width: 100%;\n}\n\n.post_item {\n width: 90%;\n margin: 30px 30px 10px 30px;\n background-color: #ffffff;\n border-bottom: 1px solid #ddd;\n padding: 20px;\n}\n\n\n.post_header {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n}\n\n.post_title {\n font-weight: bold;\n font-size: 18px;\n text-decoration: none;\n color: #2B2B2B;\n}\n\n.post_content {\n color: #2B2B2B;\n margin: 10px 0;\n width: 100%;\n height: 32px;\n}\n\n.post_date {\n text-align: right;\n margin-top: 10px;\n color: #2B2B2B;\n font-size: 14px;\n}\n",".post_detail_header {\n display: flex;\n justify-content: space-between;\n align-items: baseline;\n border-bottom: 1px solid #ddd;\n padding: 20px;\n}\n\n.post_detail_title {\n font-weight: bold;\n font-size: 18px;\n}\n\n.post_detail_content {\n color: #2B2B2B;\n margin: 10px 0;\n width: 100%;\n height: auto;\n padding: 20px;\n}\n.post_detail_content_auth{\n color: #2B2B2B;\n margin: 10px 0;\n width: 100%;\n height: auto;\n padding: 20px;\n height: 210px;\n}\n.vote_button {\n padding: 15px;\n}\n.comment_content{\n padding-bottom: 20px;\n border-bottom: 1px solid #ddd;\n}\n.comment_input {\n justify-content: space-between;\n border: 1px solid #8DC693;\n border-radius: 10px;\n width: 90%;\n padding: 15px;\n margin: 5px;\n}\n.comment_button {\n display: inline-flex;\n height: 30px;\n padding: 23px;\n margin: 5px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 15px;\n font-weight: bold;\n background-color: #8DC693;\n border: none;\n color: white;\n}\n",".auth_post_grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n grid-template-rows: repeat(2, 1fr);\n grid-gap: 30px;\n padding: 10px 50px 10px 50px;\n}\n.auth_post_item_container {\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 100%;\n height: 160px;\n border-radius: 10px;\n border: 1px solid #9E9E9E;\n margin-bottom: 10px;\n overflow: hidden;\n justify-content: center;\n text-align: center;\n}\n.auth_post_item_container img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.pagination {\n display: flex;\n justify-content: center;\n margin-top: 20px;\n}\n\n.pagination button {\n width: 30px;\n height: 30px;\n border: 1px solid #8DC693;\n background-color: transparent;\n color: #333;\n font-size: 14px;\n margin: 0 5px;\n border-radius: 5px;\n cursor: pointer;\n transition: background-color 0.3s, color 0.3s;\n}\n\n.pagination button:hover {\n background-color: #8DC693;\n color: #fff;\n}\n\n.pagination button.active {\n background-color: #8DC693;\n color: #fff;\n}",".env_post_grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n grid-template-rows: repeat(2, 1fr);\n grid-gap: 30px;\n padding: 30px 50px 30px 50px;\n}\n.env_post_item_container {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: center;\n width: 100%;\n height: 100%;\n border-radius: 10px;\n border: 1px solid #9E9E9E;\n margin-bottom: 10px;\n}\n.env_post_item_container img {\n max-width: 100%;\n height: 150px;\n}\n\n.pagination {\n display: flex;\n justify-content: center;\n margin-top: 20px;\n}\n\n.pagination button {\n width: 30px;\n height: 30px;\n border: 1px solid #8DC693;\n background-color: transparent;\n color: #333;\n font-size: 14px;\n margin: 0 5px;\n border-radius: 5px;\n cursor: pointer;\n transition: background-color 0.3s, color 0.3s;\n}\n\n.pagination button:hover {\n background-color: #8DC693;\n color: #fff;\n}\n\n.pagination button.active {\n background-color: #8DC693;\n color: #fff;\n}","* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.header_container {\n position: fixed;\n display: flex;\n flex-direction: row;\n align-items: center;\n width: 100vw;\n justify-content: flex-start;\n padding: 32px 30px 32px 35px;\n background-color: #FAFAFA;\n}\n\n.header_bar {\n display: flex;\n flex-direction: row;\n width: 88vw;\n justify-content: center;\n align-items: center;\n}\n\n.header_logo {\n display: flex;\n flex-direction: row;\n flex: 1 1 auto;\n}\n.header_logo img {\n max-width: 230px;\n}\n\n.search {\n display: flex;\n flex-direction: row;\n flex: 100 1 auto;\n justify-content: left;\n align-items: center;\n color: rgb(43 43 43);\n pointer-events: none;\n opacity: 0;\n}\n\n.header_bar_user {\n display: flex;\n flex-direction: row;\n flex: 1 1 auto;\n justify-content: right;\n align-items: center;\n gap: 30px;\n}\n\n.header_profile {\n display: flex;\n flex-direction: row;\n gap: 10px;\n justify-content: center;\n align-items: center;\n padding-top: 3px;\n}\n\n.header_user_picture {\n max-width: 45px;\n}\n\n.header_icon {\n cursor: pointer;\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n}\n\na {\n text-decoration: none;\n}","* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.mypage_main_container {\n display: flex;\n flex-direction: column;\n justify-content: LEFT;\n width: 92%;\n padding: 120px 0 0 280px;\n margin-bottom: 50px;\n}\n\n.my_posts {\n margin: 35px 0 15px;\n}\n\nul {\n display: flex;\n gap: 10px;\n margin-bottom: 10px;\n}\n\nli {\n list-style: none;\n}\n\n.custom_button {\n background-color: white;\n border: 1px solid #8DC693;\n color: #2B2B2B;\n cursor: pointer;\n}\n\n.custom_button.active {\n background-color: #8DC693;\n color: white;\n}\n\n.mypage_posts_container {\n height: auto;\n display: flex;\n flex: 0 1 0;\n flex-direction: column;\n gap: 10px;\n margin-top: 15px;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n}\n\n.post {\n display: flex;\n flex-direction: column;\n gap: 5px;\n flex: 0 1 auto;\n padding: 25px 15px 25px;\n margin: 0 20px;\n border-bottom: 1px solid #D9D9D9;\n}\n\n.post_info {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.post_title {\n font-size: 15pt;\n}\n\n.mypage_post_content {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.posts_container > article:first-child {\n margin-top: 15px;\n}\n\n.posts_container > article:last-child {\n margin-bottom: 5px;\n border-bottom: 0;\n}","* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.disabled {\n color: lightgray;\n}\n\n.pagination {\n display: flex;\n flex-direction: row;\n gap: 10px;\n justify-content: center;\n align-items: center;\n margin: 15px 0px 20px;\n}\n\nbutton {\n border: none;\n font-size: 10pt;\n border-radius: 4px;\n padding: 2px 4px;\n}","/* 모달 컨테이너 */\n.modal_container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n width: 40%;\n gap: 20px;\n border-radius: 15px;\n border: 1px solid #2B2B2B;\n background: #FFF;\n}\n\n.modal_container {\n background-color: #fff;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n padding: 80px;\n}\n\n.modal_overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.modal_content {\n margin-bottom: 20px;\n}",".my_custom_container{\n display: flex;\n flex-direction: row;\n align-items: center;\n}\n\n.id_container{\n margin-top: 110px;\n}\n\n.circle_container {\n width: 70px;\n height: 70px;\n border-radius: 50%; \n background-color: lightgray; \n display: flex;\n justify-content: center;\n align-items: center;\n overflow: hidden; \n position: relative;\n margin: 10px;\n}\n\n.circle_input {\n width: 100%;\n height: 100%;\n opacity: 0; \n position: absolute;\n}\n\n.circle_image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.custom_label_id{\n font-weight: bold;\n font-size: 13px;\n margin: 20px;\n}\n.custom_label_pw{\n font-weight: bold;\n font-size: 13px;\n margin: 20px 27px 20px 20px;\n}\n\n.input_container {\n width: 250px;\n height: 50px;\n margin-left: 20px; \n display: flex;\n flex-direction: column;\n justify-content: center;\n margin-right: 40px;\n}\n\n.nickname_input {\n width: 100%;\n height: 100%;\n border-radius: 15px;\n border: 1px solid #8DC693;\n font-size: 15px;\n padding: 10px;\n color: #838383;\n}\n\n.leave_button{\n margin: 20px 17px 17px 17px;\n font-size: 7px;\n text-align: left !important;\n}\n.my_info{\n margin-top: 35px;\n}","* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.my_post_container {\n display: flex;\n flex-direction: column;\n padding-left: 280px;\n padding-top: 120px;\n color: #2B2B2B;\n width: 91%;\n height: auto;\n}\n\n.my_post {\n display: flex;\n flex-direction: row;\n margin: 35px 0px 15px;\n}\n\nul {\n display: flex;\n flex-direction: row;\n align-items: center;\n margin: 0;\n}\n\nli {\n display: flex;\n flex-direction: row;\n align-items: center;\n list-style: none;\n}\n\n.openOrNot {\n display: inline-flex;\n height: 50px;\n padding: 15px 29px;\n justify-content: center;\n align-items: center;\n gap: 10px;\n border-radius: 15px;\n font-weight: bold;\n background-color: white;\n border: 1px solid #8DC693;\n color: #2B2B2B;\n font-size: 11pt;\n white-space: nowrap;\n}\n\n.openOrNot.active {\n background-color: #8DC693;\n color: white;\n}\n \n.each_post {\n display: flex;\n flex: 0 1 auto;\n flex-direction: column;\n width: auto;\n max-height: 550px;\n overflow-y: auto;\n gap: 10px;\n margin: 15px 0 50px 0;\n padding: 15px 15px 10px 15px;\n box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25) ;\n}\n\n.buttons {\n display: flex;\n flex-direction: row;\n justify-content: right;\n align-items: center;\n gap: 5px;\n text-decoration: none;\n top: 5px;\n padding-right: 15px;\n}\n\n.edit_button, .delete_button, span {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n font-size: 11pt;\n background-color: white;\n}\n\n.each_post_info {\n padding: 0 15px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n}\n\n.each_post_date {\n display: flex;\n flex-direction: row;\n}\n\n.user_info {\n display: flex;\n flex-direction: row;\n justify-content: left;\n align-items: center;\n gap: 10px;\n padding: 10px 15px 10px;\n border-bottom: 1px solid #D9D9D9;\n}\n\n.each_profile_image {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n\n.user_name {\n font-size: 13pt;\n}\n\n.each_post_content {\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n gap: 20px;\n font-size: 11pt;\n height: auto;\n margin-bottom: 30px;\n padding-top: 15px;\n}\n\n.uploaded {\n width: 360px;\n}\n\n@media screen and (max-width: 768px) {\n .uploaded {\n width: 180px;\n }\n}\n\n.post_writing {\n display: flex;\n flex-direction: row;\n white-space: pre-wrap;\n margin-right: 15px;\n}\n\n.no_image {\n width: 100%;\n}\n\na {\n text-decoration: none;\n color: inherit;\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/client/build/static/js/main.4fcd2700.js b/client/build/static/js/main.4fcd2700.js deleted file mode 100644 index 2eae183..0000000 --- a/client/build/static/js/main.4fcd2700.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see main.4fcd2700.js.LICENSE.txt */ -!function(){var e={110:function(e,t,n){"use strict";var r=n(309),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function u(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var s=Object.defineProperty,c=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(m){var a=p(n);a&&a!==m&&e(t,a,r)}var i=c(n);f&&(i=i.concat(f(n)));for(var l=u(t),h=u(n),v=0;v