본문 바로가기
개발/React

리액트 - 로그인 폼 만들기 Login Form

by 피로물든딸기 2022. 3. 9.
반응형

리액트 전체 링크

프로젝트 전체 링크

 

로그인 폼을 React를 이용하여 만들어보자.

아래와 같은 기능이 필요하다.

 

- 아이디 한글 입력 불가 처리

- 아이디 저장

- 비밀번호 표시

- Caps Lock 감지

useReducer로 Input 관리


useReducer 적용 전 최종 코드

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";

import * as mdef from "../library/myDefine";
import "../css/Login.css";

const LS_KEY_ID = "LS_KEY_ID";
const LS_KEY_SAVE_ID_FLAG = "LS_KEY_SAVE_ID_FLAG";

const Login = () => {
  const [loginID, setLoginID] = useState("");
  const [loginPassword, setLoginPassword] = useState("");
  const [saveIDFlag, setSaveIDFlag] = useState(false);
  const [passwordOption, setPasswordOption] = useState(false);
  const [passwordInputType, setPasswordInputType] = useState({
    type: "password",
    autoComplete: "current-password",
  });
  const [capsLockFlag, setCapsLockFlag] = useState(false);

  const checkCapsLock = (e) => {
    let capsLock = e.getModifierState("CapsLock");
    setCapsLockFlag(capsLock);
  };

  const dataRuleCheckForID = (ch) => {
    let ascii = ch.charCodeAt(0);
    if (48 /* 0 */ <= ascii && ascii <= 57 /* 9 */) return true;
    if (65 /* A */ <= ascii && ascii <= 90 /* Z */) return true;
    if (97 /* a */ <= ascii && ascii <= 122 /* z */) return true;
    if (ch === ".") return true;

    return false;
  };

  const getLoginID = (event) => {
    let value = event.target.value;

    if (value === "") {
      setLoginID(value);
      return;
    }

    let length = value.length;
    if (dataRuleCheckForID(value[length - 1]) === false) return;
   
    setLoginID(value);

    return;
  };

  const handleSaveIDFlag = () => {
    localStorage.setItem(LS_KEY_SAVE_ID_FLAG, !saveIDFlag);
    setSaveIDFlag(!saveIDFlag);
  };

  const login = () => {
    console.log({ loginID, loginPassword });

    if (loginID === "") {
      alert("아이디를 입력해주세요.");
    }
    if (loginPassword === "") {
      alert("비밀번호를 입력해주세요.");
    }

    if (true /* login success */) {
      if (saveIDFlag) localStorage.setItem(LS_KEY_ID, loginID);
    }
  };

  useEffect(() => {
    document.title = mdef.DOCUMENT_TITLE_LOGIN;

    let idFlag = JSON.parse(localStorage.getItem(LS_KEY_SAVE_ID_FLAG));
    if (idFlag !== null) setSaveIDFlag(idFlag);
    if (idFlag === false) localStorage.setItem(LS_KEY_ID, "");

    let data = localStorage.getItem(LS_KEY_ID);
    if (data !== null) setLoginID(data);
  }, []);

  useEffect(() => {
    if (passwordOption === false)
      setPasswordInputType({
        type: "password",
        autoComplete: "current-password",
      });
    else 
      setPasswordInputType({ 
        type: "text", 
        autoComplete: "off" 
      });
  }, [passwordOption]);

  return (
    <div className="login-form">
      <div className="login-wrapper">
        <div className="login-container">
          <div className="login-logo">
            <span className="logo-image">LOGO</span>
          </div>
          <form id="loginForm">
            <div className="input-group">
              <input
                type="text"
                id="email"
                name="email"
                placeholder="NAVER 아이디"
                className="input-id"
                onKeyDown={(e) => checkCapsLock(e)}
                value={loginID}
                onChange={(e) => getLoginID(e)}
              />
              <input
                type={passwordInputType.type}
                id="password"
                name="password"
                placeholder="비밀번호"
                className="input-pw"
                autoComplete={passwordInputType.autoComplete}
                onKeyDown={(e) => checkCapsLock(e)}
                value={loginPassword}
                onChange={(e) => setLoginPassword(e.target.value)}
              />
            </div>
            <div className="checkbox-wrapper">
              <span className="checkbox-item">
              <input
                type="checkbox"
                name="saveEmail"
                id="saveEmail"
                checked={saveIDFlag}
                onChange={handleSaveIDFlag}
              />
              <label>
                <span>아이디 저장</span>
              </label>
              </span>
              <span className="checkbox-item">
                <input
                  type="checkbox"
                  checked={passwordOption}
                  onChange={() => setPasswordOption(!passwordOption)}
                />
                <label>
                  <span>비밀번호 표시</span>
                </label>
              </span>
            <span
              className={
                capsLockFlag ? "caps-lock caps-lock-on" : "caps-lock"
              }
            >
              {capsLockFlag ? "Caps Lock On" : "Caps Lock Off"}
            </span>
            </div>

          <span className="login-button" onClick={login}>
            로그인
          </span>
          </form>
          <ul className="login-li-group">
            <li>
              <span onClick={() => alert("잘 기억해보세요.")}>아이디 찾기</span>
            </li>
            <li>
              <span onClick={() => alert("잘 기억해보세요.")}>
                비밀번호 찾기
              </span>
            </li>
            <li>
              <Link to="/signUp">
                <span className="bold">회원가입</span>
              </Link>
            </li>
          </ul>
        </div>
      </div>
    </div>
  );
};

export default Login;

useReducer 적용 후 최종 코드

import React, { useEffect, useReducer, useState } from "react";
import { Link } from "react-router-dom";

import * as mdef from "../library/myDefine";
import "../css/Login.css";

const LS_KEY_ID = "LS_KEY_ID";
const LS_KEY_SAVE_ID_FLAG = "LS_KEY_SAVE_ID_FLAG";

function inputReducer(state, action) {
  return {
    ...state,
    [action.id]: action.value,
  };
}

const Login = () => {
  const [state, dispatch] = useReducer(inputReducer, {
    loginID: "",
    loginPassword: "",
  });
  const { loginID, loginPassword } = state;

  const [saveIDFlag, setSaveIDFlag] = useState(false);
  const [passwordOption, setPasswordOption] = useState(false);
  const [passwordInputType, setPasswordInputType] = useState({
    type: "password",
    autoComplete: "current-password",
  });
  const [capsLockFlag, setCapsLockFlag] = useState(false);

  const checkCapsLock = (e) => {
    let capsLock = e.getModifierState("CapsLock");
    setCapsLockFlag(capsLock);
  };

  const dataRuleCheckForID = (ch) => {
    let ascii = ch.charCodeAt(0);
    if (48 /* 0 */ <= ascii && ascii <= 57 /* 9 */) return true;
    if (65 /* A */ <= ascii && ascii <= 90 /* Z */) return true;
    if (97 /* a */ <= ascii && ascii <= 122 /* z */) return true;
    if (ch === ".") return true;

    return false;
  };

  const getLoginID = (event) => {
    let value = event.target.value;

    if (value === "") {
      dispatch(event.target);
      return;
    }

    let length = value.length;
    if (dataRuleCheckForID(value[length - 1]) === false) return;

    dispatch(event.target);

    return;
  };

  const handleSaveIDFlag = () => {
    localStorage.setItem(LS_KEY_SAVE_ID_FLAG, !saveIDFlag);
    setSaveIDFlag(!saveIDFlag);
  };

  const login = () => {
    console.log({ loginID, loginPassword });

    if (loginID === "") {
      alert("아이디를 입력해주세요."); return;
    }

    if (loginPassword === "") {
      alert("비밀번호를 입력해주세요."); return;
    }

    if (true /* login fail */) {
      alert("아이디 혹은 패스워드가 틀립니다.");
      dispatch({ id: "loginID", value: "" });
      dispatch({ id: "loginPassword", value: "" });
      localStorage.setItem(LS_KEY_ID, "");

      return;
    }

    if (true /* login success */) {
      if (saveIDFlag) localStorage.setItem(LS_KEY_ID, loginID);
    }
  };

  useEffect(() => {
    document.title = mdef.CURRENT_CORP.DOCUMENT_TITLE_LOGIN;

    let idFlag = JSON.parse(localStorage.getItem(LS_KEY_SAVE_ID_FLAG));
    if (idFlag !== null) setSaveIDFlag(idFlag);
    if (idFlag === false) localStorage.setItem(LS_KEY_ID, "");

    let data = localStorage.getItem(LS_KEY_ID);
    if (data !== null) dispatch({ id: "loginID", value: data });
  }, []);

  useEffect(() => {
    if (passwordOption === false)
      setPasswordInputType({
        type: "password",
        autoComplete: "current-password",
      });
    else
      setPasswordInputType({
        type: "text",
        autoComplete: "off",
      });
  }, [passwordOption]);

  return (
    <div className="login-form">
      <div className="login-wrapper">
        <div className="login-container">
          <div className="login-logo">
            <span className="logo-image">LOGO</span>
          </div>
          <form id="loginForm">
            <div className="input-group">
              <input
                type="text"
                id="loginID"
                name="email"
                placeholder="NAVER 아이디"
                className="input-id"
                onKeyDown={(e) => checkCapsLock(e)}
                value={loginID}
                onChange={(e) => getLoginID(e)}
              />
              <input
                type={passwordInputType.type}
                id="loginPassword"
                name="password"
                placeholder="비밀번호"
                className="input-pw"
                autoComplete={passwordInputType.autoComplete}
                onKeyDown={(e) => checkCapsLock(e)}
                value={loginPassword}
                onChange={(e) => dispatch(e.target)}
              />
            </div>
            <div className="checkbox-wrapper">
              <span className="checkbox-item">
                <input
                  type="checkbox"
                  name="saveEmail"
                  id="saveEmail"
                  checked={saveIDFlag}
                  onChange={handleSaveIDFlag}
                />
                <label>
                  <span>아이디 저장</span>
                </label>
              </span>
              <span className="checkbox-item">
                <input
                  type="checkbox"
                  checked={passwordOption}
                  onChange={() => setPasswordOption(!passwordOption)}
                />
                <label>
                  <span>비밀번호 표시</span>
                </label>
              </span>
              <span
                className={
                  capsLockFlag ? "caps-lock caps-lock-on" : "caps-lock"
                }
              >
                {capsLockFlag ? "Caps Lock On" : "Caps Lock Off"}
              </span>
            </div>

            <span className="login-button" onClick={login}>
              로그인
            </span>
          </form>
          <ul className="login-li-group">
            <li>
              <span onClick={() => alert("잘 기억해보세요.")}>아이디 찾기</span>
            </li>
            <li>
              <span onClick={() => alert("잘 기억해보세요.")}>
                비밀번호 찾기
              </span>
            </li>
            <li>
              <Link to="/signUp">
                <span className="bold">회원가입</span>
              </Link>
            </li>
          </ul>
        </div>
      </div>
    </div>
  );
};

export default Login;
반응형

댓글