[프로젝트] 6. 로그인(일반) - 프론트화면(react)

저번에 spring boot에서 로그인후 JWT토큰 생성하고 응답 Header에 토큰 전송하는것까지 완료하였다.

이번에는 react(처음써봄) 이용해서 비동기 통신 이용하여 로그인 기능 작성해볼려고한다.

 

우선 react에서 router-dom 설치해두었고,

페이지이동을위한 router설정을 진행하였다.

vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
    // "/api" 요청시 -> "http://localhost:9000/api~~" 으로 치환됨
      "/api": "http://localhost:9000",
    },
  },
});

 

main.jsx

import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import { BrowserRouter } from "react-router-dom";

createRoot(document.getElementById("root")).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

 

App.jsx

"/" = 메인페이지

"/login" = 로그인 페이지

"/*" = 그외 페이지(오류페이지)

import Home from "./pages/Home/Home";
import Login from "./pages/Login/Login";
import NotFound from "./pages/Error/NotFound";
import { Routes, Route } from "react-router-dom";
import "./styles/reset.min.css";

function App() {
  return (
    <>
      <Routes>
        <Route path="/" element={<Home />}></Route>
        <Route path="/login" element={<Login />}></Route>
        <Route path="/*" element={<NotFound />}></Route>
      </Routes>
    </>
  );
}

export default App;

 

Home.jsx

const Home = () => {
  return <div>Home Page</div>;
};

export default Home;

 

Login.jsx

아직 완성은 아니고, 계속해서 수정해나갈예정.

우선 localStorage에 저장은 해두었지만,

이런방식보다는 엑세스토큰, 리프레쉬토큰 사용해서 많이 하는것같다

아직 학습한적이없기 때문에 우선진행할지.. 엑세스,리프레쉬 토큰 학습하고 진행할지.. 고민중이다..

import "./Login.css";
import { useState, useRef } from "react";
import axios from "axios";

const Login = () => {
  const idRef = useRef();
  const pwRef = useRef();

  const [state, setState] = useState({
    userid: "",
    password: "",
  });

  const { userid, password } = state;

  const stateHandler = (e) => {
    setState({
      ...state,
      [e.target.name]: e.target.value,
    });
  };

  /**
   * 로그인 기능
   * @returns
   */
  const loginHandler = () => {
    try {
      if (!validationForm()) {
        return;
      }

      const formData = new FormData();
      formData.append("userid", state.userid);
      formData.append("password", state.password);

      axios
        .post("/api/login", formData)
        .then((res) => {
          // 만약 api 통신 실패했을시
          if (res.status !== 200) {
            alert("API 통신에 실패 하였습니다.");
            return;
          }

          // JWT 토큰 파싱
          const token = res.headers.authorization.replace("Bearer ", "");

          // localStorage에 JWT 토큰 저장
          localStorage.setItem("token", token);
        })
        .catch((error) => {
          alert("로그인 정보가 일치하지 않습니다. 다시 시도해 주세요.");
        });
    } catch (error) {
      alert("통신중 오류가 발생하였습니다. 잠시후 시도해주세요.");
    }
  };

  /**
   * form 입력값 유효성 체크
   * @returns
   */
  const validationForm = () => {
    if (!userid.trim()) {
      alert("아이디를 입력해주세요.");
      idRef.current.focus();
      return false;
    }

    if (!password.trim()) {
      alert("비밀번호를 입력해주세요.");
      pwRef.current.focus();
      return false;
    }

    return true;
  };

  return (
    <>
      <div className="login_wrap">
        <div className="login">
          <h3 className="login_logo"></h3>
          <div className="login_input_box">
            <input
              type="text"
              name="userid"
              id="userid"
              className="input"
              placeholder="아이디"
              value={state.userid}
              onChange={stateHandler}
              ref={idRef}
            />
            <input
              type="password"
              name="password"
              id="password"
              className="input"
              placeholder="비밀번호"
              value={state.password}
              onChange={stateHandler}
              ref={pwRef}
            />
          </div>
          <div className="login_btn_wrap">
            <button className="active" onClick={loginHandler}>
              로그인
            </button>
            <button>회원가입</button>
          </div>
        </div>
      </div>
    </>
  );
};

export default Login;

 

Login.css

.login_wrap {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
.login_wrap .login {
  width: 500px;
}
.login_wrap .login .login_logo {
  width: 100%;
  height: 50px;
  text-align: center;
  line-height: 50px;
  background: #999;
  font-weight: bold;
  background: url(/src/assets//images/logo.png) no-repeat 50% 50%;
  background-size: 400px;
}

.login_wrap .login .login_input_box {
  padding: 3cqmin 0;
}

.login_wrap .login .login_input_box .input {
  display: block;
  width: 100%;
  height: 40px;
  box-sizing: border-box;
  padding: 0 10px;
  border: 1px solid #999;
  border-radius: 5px;
  margin-top: 20px;
}

.login_wrap .login .login_input_box .input:first-child {
  margin-top: 0;
}

.login_wrap .login .login_btn_wrap {
}

.login_wrap .login .login_btn_wrap button {
  display: block;
  width: 100%;
  height: 40px;
  line-height: 20px;
  text-align: center;
  margin-bottom: 10px;
  border-radius: 5px;
  border-width: 0;
  font-size: 14px;
  background: rgb(232, 240, 254);
  color: rgb(75, 90, 214);
  font-weight: bold;
}

.login_wrap .login .login_btn_wrap .active {
  background: rgb(82, 106, 240);
  color: #fff;
}

 

로그인 테스트

1. 로그인 페이지

  - 아이디, 패스워드 입력후 로그인 버튼 클릭하면

 

2. 응답헤더로 토큰 저장

 - 아래 처럼 localStorage에 token 값을 저장해둔다.

 - 만약 필요할떄 | localStorage.getItem("token") | 으로 토큰을 꺼내쓰면된다.

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유