[React/Typescript] useForm hook으로 로그인 구현

옹잉·2024년 4월 27일
0

✨ 알게된 것

목록 보기
3/6
post-custom-banner

0. useForm hook 사용을 위한 준비

npm install react-router-dom 모듈 설치

1. Signin.tsx

import { useState } from "react";
import LoginForm from "../../components/form/LoginForm";
import "../../styles/pages/account/signin.scss";
import { Link, useNavigate } from "react-router-dom";
import axios from "axios";

export default function Signip() {
    const [role, setRole] = useState<string>("student");
    function showLoginForm(role: string) {
        setRole(role);
    }

    const navigate = useNavigate();

    // axios
    const login = async (role: string, id: string, pw: string): Promise<void> => {
        try {
            const url = `${process.env.REACT_APP_API_SERVER}/api/login${
                role === "student" ? "Student" : "Tutor"
            }`;

            await axios({
                method: "post",
                url: url,
                data: {
                    id: id,
                    password: pw,
                },
            }).then((res) => {
                if (!res.data.isLogin) {
                    alert("로그인 실패 \n" + res.data);
                } else {
                    navigate("/api");
                }
            });
        } catch (error) {
            console.error("로그인 오류", error);
        }
    };

    return (
        <>
            <div className="login_container">
                <h2>{role === "student" ? "학생 " : "강사 "} 로그인</h2>
                <div className="go_to_sign_up">
                    <p>아직 회원이 아니신가요?</p>
                    <Link to="/api/student">학생 회원가입</Link>
                    &nbsp;&nbsp;&nbsp;
                    <Link to="/api/tutor">강사 회원가입</Link>
                </div>
                <div className="role_toggle_btn">
                    <div className="student_btn">
                        <button onClick={() => showLoginForm("student")}>학생으로 로그인</button>
                    </div>
                    <div className="tutor_btn">
                        <button onClick={() => showLoginForm("tutor")}>강사로 로그인</button>
                    </div>
                </div>
                <div className="login_box">
                    {role && <LoginForm role={role} login={login} />}

                    <div className="find_id_pw">
                        <Link to="#">아이디 찾기</Link> | <Link to="#">비밀번호 찾기</Link>
                    </div>
                </div>
            </div>
        </>
    );
}

2. LoginForm.tsx 컴포넌트

import kakaoLogo from "../../assets/Kakao.png";
import { useForm } from "react-hook-form";
import PasswordInput from "../input/PasswordInput";
import { SigninData } from "../../types/interface";

interface Props {
    role: string;
    login: (role: string, id: string, pw: string) => Promise<void>;
}

  interface SigninData {
    id: string;
    pw: string;
}

export default function LoginForm({ role, login }: Props) {
    const {
        register,
        handleSubmit,
        formState: { errors },
    } = useForm<SigninData>({
        mode: "onSubmit",
        defaultValues: {
            id: "",
            pw: "",
        },
    });

    const onSubmit = async (data: SigninData) => {
        try {
            const { id, pw } = data;

            await login(role, id, pw);
        } catch (error) {
            console.error("로그인 오류", error);
        }
    };

    return (
        <>
            <div className="login_wrapper">
                <div className="basic_login">
                    <form name="login_form" onSubmit={handleSubmit(onSubmit)}>
                        <label htmlFor="id">ID</label>
                        <input
                            type="text"
                            {...register("id", { required: true })}
                            id="id"
                            autoComplete="username"
                        />
                        <label htmlFor="pw">비밀번호</label>
                        <PasswordInput
                            type="password"
                            {...register("pw", { required: true })}
                            id="pw"
                        />

                        <button type="submit">로그인</button>
                    </form>
                    {role === "student" && (
                        <div className="social_login">
                            <button type="button">
                                <img src={kakaoLogo} alt="카카오 로그인 버튼" width={25} />
                                카카오톡 로그인
                            </button>
                        </div>
                    )}
                </div>
            </div>
        </>
    );
}

3. PasswordInput.tsx 컴포넌트

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
import React, { forwardRef, useState } from "react";
import "../../styles/components/input/password.scss";

interface PasswordProps extends React.HTMLProps<HTMLInputElement> {
    type: string;
}

  // PasswordInput 컴포넌트에 ref를 전달하려고 시도했으나, 함수 컴포넌트는 ref를 직접 받을 수 없기 때문에 문제 발생!
  // 이 문제를 해결하기 위해 React.forwardRef를 사용
const PasswordInput = forwardRef<HTMLInputElement, PasswordProps>(({ type, ...rest }, ref) => {
    const [isShow, setIsShow] = useState(false);
    const handleClick = () => {
        setIsShow(!isShow);
    };

    return (
        <>
            <div className="pw_input_wrapper">
                <input
                    ref={ref}
                    type={isShow ? "text" : type}
                    autoComplete="current-password"
                    {...rest}
                />
                <div className="eye_icon" onClick={handleClick}>
                    <FontAwesomeIcon icon={isShow ? faEyeSlash : faEye} />
                </div>
            </div>
        </>
    );
});

export default PasswordInput;

드릅게 어려워 죽겄네...

profile
틀리더라도 🌸🌈🌷예쁘게 지적해주세요💕❣️
post-custom-banner

0개의 댓글