로그인 api를 만들었으니 로그인 요청할 화면을 만들어야된다.
react써볼거고 reqct-query랑 zustand, react-bootstrap 써볼 예정
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
npm install zustand
npm install react-bootstrap bootstrap
받고axios도 받고
main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import queryClient from "./queryClient.jsx";
import {QueryClientProvider} from "@tanstack/react-query";
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
createRoot(document.getElementById('root')).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<div style={{fontSize: '16px'}}>
<ReactQueryDevtools initialIsOpen={false} />
</div>
</QueryClientProvider>
</StrictMode>,
)
queryClient.jsx
import {QueryClient} from "@tanstack/react-query";
const queryClient = new QueryClient( {
defaultOptions : {
queries : {
staleTime : 1000 * 60 * 3, // 3분.
retryDelay : 1000 * 2, // 2초
gcTime : 1000 * 60 * 5 // 5분
}
}
})
export default queryClient
App.jsx
import 'bootstrap/dist/css/bootstrap.min.css';
function App() {
return (
<>
<BrowserRouter>
<Routes>
<Route element={<Layout/>}>
<Route index element={<Home/>}/>
</Route>
</Routes>
</BrowserRouter>
</>
)
}
export default App
layout.jsx
export default function Layout() {
return (
<>
<Header/>
<main>
<Outlet />
</main>
</>
)
}
Header.jsx
function Header() {
return(
<>
<Navbar collapseOnSelect expand="lg" className="bg-body-tertiary">
<Container>
<Navbar.Brand href="/">Board</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="#features">Features</Nav.Link>
<Nav.Link href="#pricing">Pricing</Nav.Link>
</Nav>
<Nav>
<Nav.Link href="#deets">sign in</Nav.Link>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</>
)
}
export default Header
userStore.jsx
export const UserStore = create(
persist
// eslint-disable-next-line no-unexpected-multiline
(
(set) => ({
// 로그인 여부
isLogin : false,
loginId : '',
setIsLogin: (state) => set({ isLogin: state }),
setLoginId: (code) => set({ loginId: code }),
reset: () => {
set({
isLogin: false,
loginId: '',
});
},
}),{
name: 'userStorage',
storage: createJSONStorage(() => {
return sessionStorage;
}),
}
)
)
헤더 상단에
const {isLogin} = UserStore((state) => state)
추가하고
<Nav>
{
isLogin ? <Nav.Link href="#deets">sign out</Nav.Link>
:
<Nav.Link href="/login">sign in</Nav.Link>
}
</Nav>
Login.jsx
function Login(){
const nav = useNavigate();
const [formData, setFormData] = useState({userId:'',password:''})
const { isLogin , setIsLogin} = UserStore((state) => state);
useEffect(() => {
if(isLogin){
nav('/')
}
})
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const login = () => {
if(!formData.userId){
alert('아이디를 입력해주세요')
return
}
if(!formData.password){
alert('비밀번호를 입력해주세요')
return;
}
loginMutaion.mutate(formData)
}
const loginMutaion = useMutation({
mutationFn: (account) => {
return loginApi(account)
.then( result => {
console.log(result)
if(result.status === 200)
{
console.log(result.data.token)
const accessToken = result.data.token;
const refreshToken = result.data.refreshToken;
setAccessToken(accessToken)
setRefreshToken(refreshToken)
setIsLogin(true);
nav('/')
}else{
alert(result.data.message)
}
})
.catch(
reason => {
console.log(reason)
}
)
.finally()
}
})
return(
<>
<Form>
<Form.Group as={Row} className="mb-3" controlId="formPlaintextId">
<Form.Label column sm="2">
ID
</Form.Label>
<Col sm="10">
<Form.Control type="text" name="userId" onChange={handleChange}/>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="formPlaintextPassword">
<Form.Label column sm="2">
Password
</Form.Label>
<Col sm="10">
<Form.Control type="password" name="password" onChange={handleChange}/>
</Col>
</Form.Group>
<Row>
<Col sm={{ span: 10, offset: 2 }}>
<Button type="button" variant="primary" onClick={login}>
로그인
</Button>
<Button type="button" variant="dark" onClick={login} className="ms-2">
회원가입
</Button>
</Col>
</Row>
</Form>
</>
)
}
Axios.interceptor.jsx
axios.defaults.baseURL = import.meta.env.VITE_SERVER_URL
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.interceptors.request.use((config) => {
const accessToken = getAccessToken();
if (accessToken) {
config.headers['Authorization'] = `${accessToken}`;
}
config.headers['Channel'] = 'WEB';
config.headers['X-Requested-With'] = 'XMLHttpRequest';
config.headers['Access-Control-Allow-Credentials'] = true;
config.headers['Access-Control-Allow-Origin'] = '*';
config.headers['Access-Control-Allow-Methods'] = 'GET, PUT, POST, DELETE, OPTIONS';
config.headers['Cache-Control'] = 'no-cache';
config.headers['Pragma'] = 'no-cache';
console.log('[ Axios.interceptor ] :: processAwait ' + processAwait);
return config;
}, err => {
console.error(err);
Promise.reject(err);
});
일단 요청만 하고

잘된다.
이제 백이랑 통신시키고 갱신시켜야지