본문 바로가기
Do it Node.js/React BackEnd

6. 로그인 페이지

by 갱생angel 2024. 3. 20.

로그인 기능 구현

  -login.js 추가

  -header.js, mainHeader.js, registerHeader.js 로 헤더 3개 분류

 

-백엔드-

 

controllers - <loginController-2.js> : 로그인 기능 

(...)
require("dotenv").config(); //.env 파일 불러오기
const jwt = require("jsonwebtoken"); //jsonwebtokeen 불러오기
const jwtSecret = process.env.JWT_SECRET; //비밀키 지정

(...)

//Post Login User, / : 로그인 기능
const loginUser = asynchHandler(async (req, res) => {
  const { username, password } = req.body;

  const user = await User.findOne({ username }); 
  if (!user) {
    return res.status(401).json({ message: "일치하는 사용자가 없습니다." });
  }

  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) {
    return res.status(401).json({ message: "비밀번호가 일치하지 않습니다." });
  }

  const token = jwt.sign({ id: user._id }, jwtSecret); //토큰 생성
  res.cookie("token", token, { httpOnly: true }); //쿠키 생성
  res.status(200).json({ message: "로그인 성공", token });
});

(...)

module.exports = { loginUser, registerUser }; //loginUser 내보내기

 

routes - <loginRoutes-2.js> : 로그인 라우터

const express = require("express");
const router = express.Router();
const { loginUser, registerUser } = require("../controllers/loginController-2");

router.route("/").post(loginUser); //로그인 라우터
router.route("/register").post(registerUser);

module.exports = router;

-프론트엔드-

 

index.js

App.js

component

  -header.js

  -mainHeader.js

  -registerHeader.js

page

  -main.js

  -add.ejs

  -update.js

  -register.js

  -login.js

css

  -style.css

 

App.js

import React from "react";
import Main from "./page/main";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Add from "./page/add";
import Update from "./page/update";
import Register from "./page/register";
import Login from "./page/login";

function App() {
  return (
    <div>
      <BrowserRouter>
        <Routes>
          <Route index element={<Login />} />
          <Route path="/contact" element={<Main />} />
          <Route path="/register" element={<Register />} />
          <Route path="/add" element={<Add />} />
          <Route path="/contact/:id" element={<Update />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

 

page - <login.js> : 로그인 페이지

window.location.replace() : 페이지 이동 함수

import React, { useEffect, useState } from "react";
import Header from "../component/header";
import axios from "axios";
import { useNavigate } from "react-router-dom";

function Login() {
  const navigate = useNavigate();

  const [loginName, setLoginName] = useState("");
  const [loginPassword, setLoginPassword] = useState("");

  const changeLoginName = (e) => {
    setLoginName(e.target.value);
  };
  const changeLoginPassword = (e) => {
    setLoginPassword(e.target.value);
  };

  const loginForm = async (e) => {
    e.preventDefault();

    const loginData = { username: loginName, password: loginPassword };

    if (loginName === "" || loginPassword === "") {
      alert("ID, 비밀번호 입력을 해주세요.");
    } else {
      try {
        const response = await axios.post("http://localhost:8080/", loginData); //POST 보내기

        const { token } = response.data; //토큰 생성
        localStorage.setItem("token", token); //토큰을 로컬 스토리지에 저장

        navigate("/contact"); //메인 페이지로 이동
        alert(response.data.message);
      } catch (err) {
        alert(err.response.data.message);
        setLoginName("");
        setLoginPassword("");
      }
    }
  };

  useEffect(() => {
    if (localStorage.getItem("token") !== null) { //로컬 스토리지에 token이 있을 경우
      alert("이미 로그인 중입니다.");
      window.location.replace("http://localhost:3000/contact"); //contact 페이지로 이동
    }
  }, []);

  return (
    <div>
      <Header />
      <div className="site-main">
        <h3>로그인</h3>
        <p>로그인이 필요한 서비스입니다.</p>
        <form onSubmit={loginForm} className="login">
          <div>
            <label>아이디</label>
            <input
              type="text"
              value={loginName || ""}
              onChange={changeLoginName}
            />
          </div>
          <div>
            <label>비밀번호</label>
            <input
              type="password"
              value={loginPassword || ""}
              onChange={changeLoginPassword}
            />
          </div>
          <button type="submit" className="login-btn">
            로그인
          </button>
        </form>
      </div>
    </div>
  );
}

export default Login;

 

page - <main.js> : 메인 페이지

import React, { useEffect, useState } from "react";
import axios from "axios";
import "../css/style.css";
import { useNavigate } from "react-router-dom";
import MainHeader from "../component/mainHeader";

function Main() {
  const navigate = useNavigate();

  const [lists, setLists] = useState([]);

  const deleteData = (deleteId) => {
    alert("삭제되었습니다.");
    axios.delete(`http://localhost:8080/contact/${deleteId}`);
    window.location.replace("/contact");
  };

  useEffect(() => {
    if (localStorage.getItem("token") == null) { //로컬 스토리지에 token이 없을 경우
      alert("로그인 후 이용해주세요.");
      window.location.replace("http://localhost:3000");
    } else { //로컬 스토리지에 token이 있을 경우
      axios
        .get("http://localhost:8080/contact")
        .then((res) => setLists(res.data));
    }
  }, []);

  return (
    <div>
      <MainHeader />
      <div className="site-main">
        <div className="button-box">
          <button onClick={() => navigate("/add")}>+ 연락처 추가</button>
        </div>
        <table className="table">
          <thead>
            <tr>
              <th>이름</th>
              <th>이메일</th>
              <th>전화번호</th>
              <th>&nbsp;</th>
            </tr>
          </thead>
          <tbody>
            {lists.map((list, index) => {
              return (
                <tr key={index}>
                  <td>{list.name}</td>
                  <td>{list.email}</td>
                  <td>{list.phone}</td>
                  <td>
                    <button onClick={() => navigate(`/contact/${list._id}`)}>
                      수정
                    </button>
                    <button onClick={() => deleteData(list._id)}>삭제</button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

export default Main;

 

page - <add.js> : 연락처 추가 페이지

import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import MainHeader from "../component/mainHeader";

function Add() {
  const navigate = useNavigate();

  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [phone, setPhone] = useState("");

  const changeName = (e) => {
    setName(e.target.value);
  };
  const changeEmail = (e) => {
    setEmail(e.target.value);
  };
  const changePhone = (e) => {
    setPhone(e.target.value);
  };

  const addForm = (e) => {
    e.preventDefault();
    const userData = { name: name, email: email, phone: phone };
    if (name === "" || email === "" || phone === "") {
      alert("입력해주세요.");
    } else {
      axios
        .post("http://localhost:8080/contact/add", userData)
        .then(() => navigate("/contact"))
        .then(() => {
          alert("저장되었습니다.");
        });
    }
  };

  useEffect(() => { //
    if (localStorage.getItem("token") == null) { //로컬 스토리지에 token이 없을 경우
      alert("로그인 후 이용해주세요.");
      window.location.replace("http://localhost:3000");
    }
  }, []);

  return (
    <div>
      <MainHeader />
      <div className="site-main">
        <div className="button-box">
          <button onClick={() => navigate("/contact")}>연락처 목록</button>
        </div>
        <h2>연락처 추가</h2>
        <p>연락처 정보를 추가합니다</p>
        <form onSubmit={addForm}>
          <div className="user-info">
            <div>
              <label>이름</label>
              <input type="text" value={name || ""} onChange={changeName} />
            </div>
            <div>
              <label>메일 주소</label>
              <input type="text" value={email || ""} onChange={changeEmail} />
            </div>
            <div>
              <label>전화번호</label>
              <input type="text" value={phone || ""} onChange={changePhone} />
            </div>
            <button type="submit">저장하기</button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default Add;

 

page - <update.js> : 연락처 수정 페이지

import React, { useEffect, useState } from "react";
import axios from "axios";
import { useNavigate, useParams } from "react-router-dom";
import MainHeader from "../component/mainHeader";

function Update() {
  const { id } = useParams();
  const navigate = useNavigate();

  const [updateName, setModifyName] = useState("");
  const [updateEmail, setModifyEmail] = useState("");
  const [updatePhone, setModifyPhone] = useState("");

  const changeModifyName = (e) => {
    setModifyName(e.target.value);
  };
  const changeModifyEmail = (e) => {
    setModifyEmail(e.target.value);
  };
  const changeModifyPhone = (e) => {
    setModifyPhone(e.target.value);
  };

  const updateForm = (e) => {
    e.preventDefault();
    const updateUserData = {
      name: updateName,
      email: updateEmail,
      phone: updatePhone,
    };
    if (updateName === "" || updateEmail === "" || updatePhone === "") {
      alert("입력해주세요.");
    } else {
      axios
        .put(`http://localhost:8080/contact/${id}`, updateUserData)
        .then(() => navigate("/contact"))
        .then(() => {
          alert("수정되었습니다.");
        });
    }
  };

  useEffect(() => {
    if (localStorage.getItem("token") == null) { //로컬 스토리지에 token이 없을 경우
      alert("로그인 후 이용해주세요.");
      window.location.replace("http://localhost:3000");
    } else { //로컬 스토리지에 token이 있을 경우
      axios.get(`http://localhost:8080/contact/${id}`).then((res) => {
        setModifyName(res.data.name);
        setModifyEmail(res.data.email);
        setModifyPhone(res.data.phone);
      });
    }
  }, [id]);

  return (
    <div>
      <MainHeader />
      <div className="site-main">
        <div className="button-box">
          <button onClick={() => navigate("/contact")}>연락처 목록</button>
        </div>
        <h2>연락처 수정</h2>
        <p>연락처 정보를 수정합니다.</p>
        <form onSubmit={updateForm}>
          <div className="user-info">
            <div>
              <label>이름</label>
              <input
                type="text"
                value={updateName || ""}
                onChange={changeModifyName}
              />
            </div>
            <div>
              <label>메일 주소</label>
              <input
                type="text"
                value={updateEmail || ""}
                onChange={changeModifyEmail}
              />
            </div>
            <div>
              <label>전화번호</label>
              <input
                type="text"
                value={updatePhone || ""}
                onChange={changeModifyPhone}
              />
            </div>
            <button type="submit">수정하기</button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default Update;

 

component - <mainHeader.js> : 로그아웃 헤더

import React from "react";
import "../css/style.css";
import { useNavigate } from "react-router-dom";

function MainHeader() {
  const navigate = useNavigate();

  const goToLogin = () => {
    localStorage.removeItem("token"); //로컬 스토리지에 token 제거
    alert("로그아웃 되었습니다.");
    navigate("/");
  };

  return (
    <div>
      <header className="border-shadow">
        <div className="container">
          <nav>
            <p>My Contact</p>
          </nav>
          <div className="login-box">
            <button onClick={goToLogin} className="logoutBth">
              로그아웃
            </button>
          </div>
        </div>
      </header>
    </div>
  );
}

export default MainHeader;

'Do it Node.js > React BackEnd' 카테고리의 다른 글

5. 회원가입 페이지  (0) 2024.03.19
4. 연락처 수정 페이지, 삭제  (0) 2024.03.17
3. 연락처 추가 페이지, react-router-dom  (0) 2024.03.15
2. 연락처 목록 페이지, Axios  (0) 2024.03.14
1. 백엔드 연결  (0) 2024.03.14