From 87820afef4a32c5570d48b949eb3ecc9da5f95c7 Mon Sep 17 00:00:00 2001 From: Janghan Noh Date: Tue, 24 Sep 2024 02:12:51 +0900 Subject: [PATCH] =?UTF-8?q?Feature:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 토큰을 받아서 쿠키에 저장함 - 사실 토큰은 없어도 기능에 문제 없음 - But, 학번만으로 예약 가능하면 악용 가능성있으므로 차단 - libraryRequest.js: 핸들러 추가하여 로깅 - TODOS - 자동 로그인 구현 - 토큰 확인만 하면 됨 - 로깅한 것 DEV에서만 하도록 해야함 --- package-lock.json | 107 ++++++++++++++++++++++++++++++++++ package.json | 1 + src/pages/index.js | 38 ++++++++++-- src/scripts/libraryRequest.js | 80 +++++++++++++++++-------- 4 files changed, 197 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index d221b09..fb84968 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "gatsby": "^5.13.7", "gatsby-source-filesystem": "^5.13.1", "react": "^18.2.0", + "react-cookie": "^7.2.0", "react-day-picker": "^9.0.9", "react-dom": "^18.2.0", "swiper": "^11.1.14" @@ -3762,6 +3763,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -9659,6 +9670,15 @@ "tslib": "^2.0.3" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hosted-git-info": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", @@ -13170,6 +13190,20 @@ "node": ">=0.10.0" } }, + "node_modules/react-cookie": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.0.tgz", + "integrity": "sha512-mqhPERUyfOljq5yJ4woDFI33bjEtigsl8JDJdPPeNhr0eSVZmBc/2Vdf8mFxOUktQxhxTR1T+uF0/FRTZyBEgw==", + "license": "MIT", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.5", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^7.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, "node_modules/react-day-picker": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.0.9.tgz", @@ -15180,6 +15214,31 @@ "node": ">=8" } }, + "node_modules/universal-cookie": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.0.tgz", + "integrity": "sha512-PvcyflJAYACJKr28HABxkGemML5vafHmiL4ICe3e+BEKXRMt0GaFLZhAwgv637kFFnnfiSJ8e6jknrKkMrU+PQ==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^0.6.0" + } + }, + "node_modules/universal-cookie/node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/universal-cookie/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -18523,6 +18582,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -22817,6 +22885,14 @@ "tslib": "^2.0.3" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", @@ -25271,6 +25347,16 @@ "loose-envify": "^1.1.0" } }, + "react-cookie": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.0.tgz", + "integrity": "sha512-mqhPERUyfOljq5yJ4woDFI33bjEtigsl8JDJdPPeNhr0eSVZmBc/2Vdf8mFxOUktQxhxTR1T+uF0/FRTZyBEgw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.5", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^7.0.0" + } + }, "react-day-picker": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.0.9.tgz", @@ -26749,6 +26835,27 @@ "crypto-random-string": "^2.0.0" } }, + "universal-cookie": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.0.tgz", + "integrity": "sha512-PvcyflJAYACJKr28HABxkGemML5vafHmiL4ICe3e+BEKXRMt0GaFLZhAwgv637kFFnnfiSJ8e6jknrKkMrU+PQ==", + "requires": { + "@types/cookie": "^0.6.0", + "cookie": "^0.6.0" + }, + "dependencies": { + "@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + } + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index be20773..f571472 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "gatsby": "^5.13.7", "gatsby-source-filesystem": "^5.13.1", "react": "^18.2.0", + "react-cookie": "^7.2.0", "react-day-picker": "^9.0.9", "react-dom": "^18.2.0", "swiper": "^11.1.14" diff --git a/src/pages/index.js b/src/pages/index.js index d37c747..1d88748 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -8,6 +8,9 @@ import Header from "../components/header" import "swiper/css"; import "swiper/css/pagination"; import { logInFormContainer } from "../styles/logInForm.module.css" +import { login } from "../scripts/libraryRequest" +import { navigate } from "gatsby" +import { useCookies } from "react-cookie" const IndexPage = () => { @@ -22,13 +25,36 @@ const IndexPage = () => { [e.target.name]: e.target.value }) } + + const [cookies, setCookie, removeCookie] = useCookies(['accessToken', 'refreshToken', 'tokenType', 'userId']) + async function onClickLogin(e) { + console.log('[Login] try to login') + if(e.target.disabled) { + alert("로그인중입니다.") + return false + } - function login(e) { - console.log('try to login') e.target.disabled = true - // await login(userData) - // TODO: 로그인 함수 구현 필요 - e.target.disabled = false + // 로그인 처리 + const userId = userData.id + await login(userData.id, userData.pw).then((res) => { + if(res.status === 400 || res.status === 401) { + alert("입력한 정보를 다시 확인해주세요.") + e.target.disabled = false + } else if(res.status === 200) { + setCookie('accessToken', res.data['access_token'], {path: '/', maxAge: res.data['expires_in']}) + setCookie('refreshToken', res.data['refresh_token']) + setCookie('tokenType', res.data['token_type']) + setCookie('userId', userId) + navigate('/reservation') + } else { + throw new Error(res) + } + }).catch((err) => { + alert('로그인에 실패했습니다. 다시 시도해주세요.') + e.target.disabled = false + console.error(err) + }) } return ( {
- + diff --git a/src/scripts/libraryRequest.js b/src/scripts/libraryRequest.js index cd6ba48..af1de66 100644 --- a/src/scripts/libraryRequest.js +++ b/src/scripts/libraryRequest.js @@ -1,7 +1,36 @@ -import axios from 'axios' +import axios, { AxiosError } from 'axios' const requestUrl = 'https://n1ba6bpzj7.execute-api.ap-northeast-2.amazonaws.com/default/' +function httpResponseHandler(res) { + console.log(`[HttpRequest Done] ${res.status} | ${JSON.stringify(res.data)}`) + // 예외처리는 실제로 데이터 처리하는 곳에서 하기로 함 + // if(res.status !== 200) { + // return { + // status: res.status, + // body: res.data + // } + // } else { + // return res.data + // } + return res +} + +function axiosErrorHandler(err) { + if(err instanceof AxiosError) { + //handle + if(err.response) { + return { + status: err.response.status, + data: err.response.data + } + } + } else { + console.error('❌ AxiosError: ' + err) + throw err + } +} + // Access Token 요청 // 방 예약에 필요 없음 export async function login(id, pw) { @@ -13,13 +42,12 @@ export async function login(id, pw) { password: pw } // 포스트 요청에 param을 넘기기 위해서 data에 null을 전달해야함 - await axios.post(requestUrl + "oauth/token", null, {params: params}, { + await axios.post(requestUrl + "oauth/token", params, {headers: {"Content-Type": 'application/x-www-form-urlencoded'}}, { // withCredentials: true // HTTPonly Cookie 설정을 위함. but 도서관 서버에서 쿠키를 안씀. - }).then((res) => { - console.log(res) - data = res - }).catch(console.error) - return data.data + }).then(httpResponseHandler).then((body) => data = body).catch((err) => { + data = axiosErrorHandler(err) + }) + return data } export async function refresh(refresh_token) { @@ -30,13 +58,12 @@ export async function refresh(refresh_token) { refresh_token: refresh_token } // 포스트 요청에 param을 넘기기 위해서 data에 null을 전달해야함 - await axios.post(requestUrl + "oauth/token", null, {params: params}, { + await axios.post(requestUrl + "oauth/token", params, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}}, { // withCredentials: true // HTTPonly Cookie 설정을 위함. but 도서관 서버에서 쿠키를 안씀. - }).then((res) => { - console.log(res) - data = res - }).catch(console.error) - return data.data + }).then(httpResponseHandler).then((body) => data = body).catch((err) => { + data = axiosErrorHandler(err) + }) + return data } export async function getReservedDates(userId, startDate, endDate, roomId) { @@ -47,23 +74,26 @@ export async function getReservedDates(userId, startDate, endDate, roomId) { ROOM_ID: roomId } await axios.get(requestUrl + "api/v1/mylibrary/facilityreservation/" + userId,{params: params} - ).then((res) => { - data = res - }).catch(console.error) - return data.data.result + ).then(httpResponseHandler).then((body) => data = body).catch((err) => { + data = axiosErrorHandler(err) + }) + return data.result } -export async function getUserInfo(userId, targetDate) { +export async function getUserInfo(userId, targetDate = undefined) { let data = {} const params = targetDate ? {RES_YYYYMMDD: targetDate} : {} - data = await axios.get(requestUrl + "api/v1/mylibrary/facilityreservation/info/" + userId, {params: params}) - - return data.data + await axios.get(requestUrl + "api/v1/mylibrary/facilityreservation/info/" + userId, {params: params}) + .then(httpResponseHandler).then((body) => data = body).catch((err) => { + data = axiosErrorHandler(err) + }) + return data } // 방 정보 요청 export async function getRoomInfo(userId, roomId, date) { + let data = {} const params = { ROOM_ID: roomId, RES_YYYYMMDD: date @@ -72,7 +102,11 @@ export async function getRoomInfo(userId, roomId, date) { // END_DT_YYYYMMDD } - return await axios.get(requestUrl + "api/v1/mylibrary/facilityreservation/room/" + userId, {params: params}).then((res) => res.data) + await axios.get(requestUrl + "api/v1/mylibrary/facilityreservation/room/" + userId, {params: params}).then((res) => res.data) + .then(httpResponseHandler).then((body) => data = body).catch((err) => { + data = axiosErrorHandler(err) + }) + return data } // 예약 요청 @@ -87,7 +121,7 @@ export async function reserveRoom(userId, roomId, date, time, remark = '', isAdm } // post 요청시에는 data에 null을 전달해야함. - await axios.post(requestUrl + "api/v1/mylibrary/facilityreservation/room/" + userId, null, {params: params}) + await axios.post(requestUrl + "api/v1/mylibrary/facilityreservation/room/" + userId, null, {params: params}).then(httpResponseHandler) // no return return null }