Skip to content

Commit

Permalink
Login (#9)
Browse files Browse the repository at this point in the history
* <feat> : router.jsx -> app.jsx로 이름 변경 및 로그인 기능추가

* <chore> : module 오류로 인한 npm 재설치

* <chore> : browserRouter app.jsx에서 index.js로 이동

* <feat> : delete api추가 및 get, post 요청 수정

* <feat> : 토큰방식 로그인기능 구현

* #8 <fix> : 빌드 오류로 인한 절대경로 상대경로로 번경
  • Loading branch information
benidene authored Aug 9, 2023
1 parent c72a254 commit 6bc7e61
Show file tree
Hide file tree
Showing 13 changed files with 3,662 additions and 2,929 deletions.
16 changes: 15 additions & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
{
"compilerOptions": {
"jsx": "react",
"lib": ["es6", "dom"],
"rootDir": "src",
"module": "commonjs",
"esModuleInterop": true,
"target": "es5",
"sourceMap": true,
"moduleResolution": "node",
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"baseUrl": "src"
}
},
"include": ["./src"],
"exclude": ["node_modules", "build"]
}
6,168 changes: 3,343 additions & 2,825 deletions package-lock.json

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Routes, Route, useNavigate } from 'react-router-dom';
import Home from './pages/Home/Home.jsx';
import Login from './pages/Login/Login.jsx';
import { useEffect, createContext, useMemo } from 'react';
import useLocalStorage from './util/useLocalStorage.js';

export const userContext = createContext({
setIsLoggedIn: () => {},
isLoggedIn: false,
setTokens: () => {},
tokens: {
accessToken: '',
refreshToken: ''
}
});

function App() {
const navigate = useNavigate();
const [isLoggedIn, setIsLoggedIn] = useLocalStorage('login', false);
const [tokens, setTokens] = useLocalStorage('jwt', {
accessToken: '',
refreshToken: ''
});

const value = useMemo(
() => ({ isLoggedIn, setIsLoggedIn, tokens, setTokens }),
[isLoggedIn, setIsLoggedIn, tokens, setTokens]
);

useEffect(() => {
const url = new URL(window.location.href);
const accessToken = url.searchParams.get('access_token');
const refreshToken = url.searchParams.get('refresh_token');

if (accessToken && refreshToken) {
setTokens({ accessToken, refreshToken });
setIsLoggedIn(true);
navigate('/');
}
}, [isLoggedIn]);

return (
<userContext.Provider value={value}>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/login' element={<Login />} />
</Routes>
</userContext.Provider>
);
}

export default App;
16 changes: 0 additions & 16 deletions src/Router.jsx

This file was deleted.

9 changes: 7 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import ReactDOM from 'react-dom/client';
import Router from './Router.jsx';
import { BrowserRouter } from 'react-router-dom';
import App from './App.jsx';
import './reset.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Router />);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
137 changes: 105 additions & 32 deletions src/pages/Login/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,117 @@
import { useState } from 'react';
import { useLogin } from '../../util/useLogin';
import { styled } from 'styled-components';
// import { useState } from 'react';
import useLoginLogic from '../../util/useLoginLogic';
import { faGoogle } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import MainImg from 'asset/logo192.png';

function Login() {
const [userIdentifier, setUserIdentifier] = useState('');
const [password, setPassword] = useState('');
const { data, loading, error, login } = useLogin('http://localhost:8080/member1/login');
const initialInputs = {
userIdentifier: '',
password: ''
};
const LOGIN_POST_URL = `${process.env.REACT_APP_URL}/login`;
const msg = 'User name and password cannot be empty.';

function handleSubmit(event) {
event.preventDefault();
login({ userIdentifier, password });
}
const [inputs, onChange, onClick] = useLoginLogic(
initialInputs,
LOGIN_POST_URL,
msg,
'userIdentifier',
'password'
);

const { userIdentifier, password } = inputs;

return (
<div>
<form onSubmit={handleSubmit}>
<label>
id:
<input
type='text'
value={userIdentifier}
onChange={(event) => setUserIdentifier(event.target.value)}
/>
</label>
<br />
<label>
Password:
<input
<Wrapper>
<Container>
<Wrapper>
<Logo src={MainImg} alt='MainImg' />
</Wrapper>
<Wrapper>
<Input type='text' placeholder='아이디 입력' value={userIdentifier} onChange={onChange} />
<Input
type='password'
placeholder='비밀번호 8자~20자'
value={password}
onChange={(event) => setPassword(event.target.value)}
onChange={onChange}
/>
</label>
<br />
<button type='submit'>Log in</button>
</form>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{data && <div>Logged in as: {data.userIdentifier}</div>}
</div>
<LoginButton type='submit' onClick={onClick}>
로그인
</LoginButton>
</Wrapper>
<Wrapper>
<Google icon={faGoogle} />
</Wrapper>
</Container>
<div>아이디 비밀번호찾기 회원가입</div>
<div>Copyright © 2019 MANGO Co.,Ltd. All Rights Reserved.</div>
</Wrapper>
);
}

const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
/* height: 100%; */
`;

const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
height: 500px;
width: 481px;
margin-top: 100px;
padding: 0px 50px 0px 50px;
border: 1px solid lightgray;
border-radius: 10px;
`;

const Logo = styled.img`
height: 100px;
width: 100px;
`;

const Input = styled.input`
height: 47px;
width: 364px;
font-size: 18px;
padding: 0px 16px 0px 16px;
border-radius: 4px;
border: 1px solid lightgray;
margin-bottom: 1px;
`;

const LoginButton = styled.button`
height: 51px;
width: 396px;
background-color: yellow;
border-radius: 4px;
font-size: 18px;
margin-top: 14px;
`;

const Google = styled(FontAwesomeIcon)`
font-size: 1.5rem;
`;
export default Login;

// const [userIdentifier, setUserIdentifier] = useState('');
// const [password, setPassword] = useState('');
// const LOGIN_POST_URL = `${process.env.REACT_APP_URL}/login`;

// function handleClick(e) {
// e.preventDefault();
// }
// const [inputs, onChange, onSubmit] = useLoginLogic(
// initialInputs,
// LOGIN_POST_URL,
// msg,
// 'username',
// 'password'
// );
81 changes: 41 additions & 40 deletions src/util/api.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
import { useState, useEffect } from 'react';
const useGet = async (url) => {
try {
const res = await fetch(url, {
method: 'GET'
});

function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
async function fetchData() {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
}
fetchData();
}, [url]);

return { data, loading };
}
return await res.json();
} catch (error) {
console.error(error);
}
};

function usePost(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const usePost = async (url, newData, jwt) => {
try {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: jwt,
withCredentials: true
},
body: JSON.stringify(newData)
});

async function postData(body) {
setLoading(true);
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setError(err);
setLoading(false);
if (res.ok) {
return res;
}
} catch (err) {
console.log(err);
}
};

return { data, loading, error, postData };
}
const useDelete = async (url, jwt) => {
try {
const res = await fetch(url, {
method: 'DELETE',
headers: {
Authorization: jwt,
withCredentials: true
}
});
return res;
} catch (err) {
console.log(err);
}
};

export { useFetch, usePost };
export { useGet, usePost, useDelete };
11 changes: 11 additions & 0 deletions src/util/checkPassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const checkPassword = (str) => {
const regexp = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9^\W_]{8,20}$/;
const result = regexp.test(str);

if (!result) {
alert(
'Passwords must contain at least eight characters, including at least 1 letter and 1 number.'
);
}
return result;
};
10 changes: 10 additions & 0 deletions src/util/useAccessToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from 'react';
import { userContext } from '../App.js';

function useAccessToken() {
const { tokens } = useContext(userContext);
const accessToken = tokens && tokens.accessToken;
return accessToken;
}

export default useAccessToken;
18 changes: 18 additions & 0 deletions src/util/useCheckLogin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { userContext } from '../App';

function useCheckLogin() {
const navigate = useNavigate();
const { isLoggedIn } = useContext(userContext);

useEffect(() => {
if (!isLoggedIn) {
navigate('/login');
}
}, []);

return isLoggedIn;
}

export default useCheckLogin;
Loading

0 comments on commit 6bc7e61

Please sign in to comment.