diff --git a/front/public/images/googleLoginBtn.png b/front/public/images/googleLoginBtn.png new file mode 100644 index 00000000..be552342 Binary files /dev/null and b/front/public/images/googleLoginBtn.png differ diff --git a/front/public/images/kakaoLoginBtn.png b/front/public/images/kakaoLoginBtn.png new file mode 100644 index 00000000..8d4d93cb Binary files /dev/null and b/front/public/images/kakaoLoginBtn.png differ diff --git a/front/public/images/naverLoginBtn.png b/front/public/images/naverLoginBtn.png new file mode 100644 index 00000000..b7aa18ba Binary files /dev/null and b/front/public/images/naverLoginBtn.png differ diff --git a/front/src/api/authApi.js b/front/src/api/authApi.js index c366f70b..bfadb202 100644 --- a/front/src/api/authApi.js +++ b/front/src/api/authApi.js @@ -77,6 +77,20 @@ const authApi = { .catch((error) => reject(error)); }); }, + oauth: ({ path, code }) => { + return new Promise((resolve, reject) => { + return axios + .get(`${path}${code}`) + .then((response) => { + signInSuccess(response); + setTimeout(authApi.logout, REFRESH_EXPIRY_TIME); + return resolve(response); + }) + .catch((error) => { + return reject(error); + }); + }); + }, }; export default authApi; diff --git a/front/src/components/RoutesComponent.js b/front/src/components/RoutesComponent.js index 1dead1e3..231deeea 100644 --- a/front/src/components/RoutesComponent.js +++ b/front/src/components/RoutesComponent.js @@ -2,6 +2,7 @@ import { Routes, Route, Navigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { selectFirstLogin, selectIsLogin } from 'store/modules/authSlice'; import LandingPage from 'pages/LandingPage'; +import OauthPage from 'pages/TestPage/OauthPage'; import { MainPage, SignUpPage, @@ -138,6 +139,7 @@ const RoutesComponent = () => { } /> } /> } /> + } /> ); diff --git a/front/src/index.js b/front/src/index.js index 99c8271a..86fa9c5f 100644 --- a/front/src/index.js +++ b/front/src/index.js @@ -1,4 +1,4 @@ -import React from 'react'; +// import React from 'react'; import { createRoot } from 'react-dom/client'; import { Provider } from 'react-redux'; import { store } from './store/store'; @@ -13,13 +13,13 @@ const container = document.getElementById('root'); const root = createRoot(container); root.render( - - - - - - - + // + + + + + + // ); reportWebVitals(); diff --git a/front/src/pages/SignInPage/SignInPage.js b/front/src/pages/SignInPage/SignInPage.js index 9049b920..26b6ad50 100644 --- a/front/src/pages/SignInPage/SignInPage.js +++ b/front/src/pages/SignInPage/SignInPage.js @@ -34,6 +34,36 @@ const AvatarStyled = styled(Avatar)` background-color: ${({ theme }) => theme.colors.purple_2}; `; +const OauthBtns = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + margin-top: -5px; + .google { + border: 1px solid rgba(0, 0, 0, 0.1); + } +`; + +const BtnStyled = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 40px; + margin: 5px; + border-radius: 20px; + overflow: hidden; + &:hover { + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); + } + img { + margin-top: 5px; + height: 45px; + } +`; + const SignInPage = () => { const navigate = useNavigate(); const dispatch = useDispatch(); @@ -110,21 +140,47 @@ const SignInPage = () => { 입력하신 내용을 다시 확인해주세요. ) : null} - - {/* */} - {/* + + + + + google oauth login button + + + + + kakao oauth login button + + + + + naver oauth login button + + + + + {/* */} + {/* Forgot password? */} - - 계정이 없으신가요?{' '} - - 회원가입 - - + + 계정이 없으신가요?{' '} + + 회원가입 + - + ); }; diff --git a/front/src/pages/TestPage/OauthPage.js b/front/src/pages/TestPage/OauthPage.js new file mode 100644 index 00000000..8efa7875 --- /dev/null +++ b/front/src/pages/TestPage/OauthPage.js @@ -0,0 +1,48 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import { oauthAsync } from 'store/modules/authSlice'; +import { setOpenSnackbar } from 'store/modules/snackbarSlice'; +import PageContainer from 'containers/PageContainer'; +import Loading from 'components/Loading'; + +const OauthPage = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + useEffect(() => { + const path = location.pathname; + const code = location.search; + getOauthCode(path, code); + }, []); + + const getOauthCode = (path, code) => { + dispatch(oauthAsync({ path, code })) + .unwrap() + .then(() => { + dispatch( + setOpenSnackbar({ + severity: 'success', + message: '로그인이 완료되었습니다.', + }) + ); + navigate('/', { replace: true }); + }) + .catch((err) => { + console.log(err); + dispatch( + setOpenSnackbar({ + severity: 'error', + message: `${err.message}로 인해 로그인 실패하였습니다`, + }) + ); + navigate('/', { replace: true }); + }); + }; + return ( + + + + ); +}; + +export default OauthPage; diff --git a/front/src/store/modules/authSlice.js b/front/src/store/modules/authSlice.js index 380ff33a..58c3464f 100644 --- a/front/src/store/modules/authSlice.js +++ b/front/src/store/modules/authSlice.js @@ -24,6 +24,21 @@ export const signInAsync = createAsyncThunk( } ); +export const oauthAsync = createAsyncThunk( + 'auth/oauth', + async ({ path, code }, thunkAPI) => { + console.log('path', path); + console.log('code', code); + try { + const response = await authApi.oauth({ path, code }); + console.log('성공하면 오는 res', response.data); + return response.data.data; + } catch (error) { + return thunkAPI.rejectWithValue(error); + } + } +); + export const firstLoginAsync = createAsyncThunk( 'auth/firstLogin', async (params, thunkAPI) => { @@ -81,6 +96,33 @@ export const authSlice = createSlice({ state.roles = []; state.profileImage = ''; }) + .addCase(oauthAsync.pending, (state) => { + state.loading = true; + state.isLogin = false; + state.firstLogin = false; + state.nickName = ''; + state.email = ''; + state.roles = []; + state.profileImage = ''; + }) + .addCase(oauthAsync.fulfilled, (state, { payload }) => { + state.loading = false; + state.isLogin = true; + state.firstLogin = payload.firstLogin; + state.nickName = payload.nickName; + state.email = payload.email; + state.roles = payload.roles; + state.profileImage = payload.profileImage; + }) + .addCase(oauthAsync.rejected, (state) => { + state.loading = false; + state.isLogin = false; + state.firstLogin = false; + state.nickName = ''; + state.email = ''; + state.roles = []; + state.profileImage = ''; + }) .addCase(firstLoginAsync.pending, (state) => { state.loading = true; state.isLogin = false;