미새문지

24.09.09 day63 작업 일지 본문

개발 TIL

24.09.09 day63 작업 일지

문미새 2024. 9. 9. 23:10
728x90

오늘은 에러 수정이나 데이터 값 변경을 위한 코드 수정이 전부였다.

회원가입에 닉네임 중복과 이메일 중복을 깜빡해서 오늘 재희님과 회의를 통해 추가해줬다.

중복체크 버튼을 추가해주려고 했는데 기존의 구조가 좀 틀어질 것 같아 다음 페이지로 가는 버튼에 중복체크를 걸어서 해당 중복체크를 통과해야 넘어갈 수 있게 수정했다. 그러나 기존에 다음으로 넘어가던 버튼이 회원가입의 최상위 컴포넌트에 있기 때문에 코드를 하위 컴포넌트로 끌고와야 했다.

import styled from "styled-components";
import Signup1 from "../components/signup/signup1";
import Signup2 from "../components/signup/signup2";
import Signup3 from "../components/signup/signup3";
import BackHeader from "../components/common/backHeader";
import { useState } from "react";
import { useNavigate } from "react-router-dom";

const Signup: React.FC = () => {
  const navigate = useNavigate();
  const [step, setStep] = useState(1);

  const handleStepChange = (newStep: number) => {
    setStep(newStep);
  };

  const handleBack = () => {
    if (step > 1) {
      setStep(step - 1);
    } else {
      navigate(-1);
    }
  };

  return (
    <>
      <SignupWrapper>
        <BackHeader onBack={handleBack} />
        {step === 1 && <Signup1 onStepChange={handleStepChange} />}
        {step === 2 && <Signup2 onStepChange={handleStepChange} />}
        {step === 3 && <Signup3 />}
      </SignupWrapper>
    </>
  );
};
export default Signup;

const SignupWrapper = styled.div`
  width: 55%;
  margin: 0 auto;
`;

기존에는 회원가입 페이지에서 페이지 이동 step값을 관리했으나 중복체크를 위해 하위에서 수정한 step값을 useState를 이용해 값을 변경해주는걸로 수정했다.

import styled from "styled-components";
import CommonInput from "../common/commonInput";
import NextBtn from "./nextBtn";
import PwVisible from "../common/pwVisible";
import React, { useEffect, useState } from "react";
import PageCount from "../common/pageCount";
import PageGuide from "../common/pageGuide";
import { useDispatch, useSelector } from "react-redux";
import {
  setEmail,
  setPassword,
  setPasswordConfirm,
} from "../../redux/slice/signupSlice";
import { RootState } from "../../redux/store";
import axios from "axios";

interface NextProps {
  onStepChange: (step: number) => void;
}

const Signup1: React.FC<NextProps> = ({ onStepChange }) => {
  const dispatch = useDispatch();

  const email = useSelector((state: RootState) => state.signup.email);
  const password = useSelector((state: RootState) => state.signup.password);
  const passwordConfirm = useSelector(
    (state: RootState) => state.signup.passwordConfirm
  );

  const [isFormValid, setIsFormValid] = useState(false);
  const [emailValid, setEmailValid] = useState(false);
  const [passwordLengthValid, setPasswordLengthValid] = useState(false);
  const [passwordComplexityValid, setPasswordComplexityValid] = useState(false);
  const [showPassword, setShowPassword] = useState(false);
  const [emailCheck, setEmailCheck] = useState<null | boolean>(null);
  const backPort = process.env.REACT_APP_BACKEND_PORT;

  const emailRegex = /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/;
  const passwordLengthRegex = /.{8,}$/;
  const passwordComplexityRegex =
    /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>])[a-zA-Z\d!@#$%^&*(),.?":{}|<>]{8,}$/;

  useEffect(() => {
    const isEmailValid = emailRegex.test(email);
    const isPasswordLengthValid = passwordLengthRegex.test(password);
    const isPasswordComplexityValid = passwordComplexityRegex.test(password);
    const isPasswordConfirmed = password === passwordConfirm;

    setEmailValid(isEmailValid);
    setPasswordLengthValid(isPasswordLengthValid);
    setPasswordComplexityValid(isPasswordComplexityValid);
    setIsFormValid(
      isEmailValid &&
        isPasswordLengthValid &&
        isPasswordComplexityValid &&
        isPasswordConfirmed
    );
  }, [email, password, passwordConfirm]);

  const handleEmailCheck = async () => {
    try {
      const response = await axios.get(`${backPort}/api/check/${email}`);

      const message = response.data.message;

      if (message == "이미 사용중인 이메일입니다.") {
        return true;
      }

      return false;
    } catch (error) {
      console.error("이메일 중복 확인 중 오류 발생", error);
      return true;
    }
  };

  const handleNext = async () => {
    if (!isFormValid) return;

    const emailExists = await handleEmailCheck();

    if (emailExists) {
      setEmailCheck(false);
      alert("이미 사용중인 이메일입니다.");
    } else {
      setEmailCheck(true);
      onStepChange(2);
    }
  };

  return (
    <>
      <Signup1Wrapper>
        <SignupBox>
          <PageCount count="1" />
          <PageGuide text="이메일과 비밀번호를 입력해주세요." />
          <CommonInput
            typeValue="email"
            placeholderValue="이메일"
            value={email}
            onChange={(e) => dispatch(setEmail(e.target.value))}
          />
          <InputCheck valid={emailValid}>√ 이메일 확인</InputCheck>
          <CommonInput
            typeValue={showPassword ? "text" : "password"}
            placeholderValue="비밀번호"
            value={password}
            onChange={(e) => dispatch(setPassword(e.target.value))}
          />
          <InputCheck valid={passwordLengthValid}>√ 8자리 이상</InputCheck>
          <InputCheck valid={passwordComplexityValid}>
            √ 영문, 숫자, 특수문자 포함
          </InputCheck>
          <CommonInput
            typeValue={showPassword ? "text" : "password"}
            placeholderValue="비밀번호 확인"
            value={passwordConfirm}
            onChange={(e) => dispatch(setPasswordConfirm(e.target.value))}
          />
          <InputCheck valid={password === passwordConfirm}>
            √ 비밀번호 동일
          </InputCheck>
          <PwVisible onToggle={setShowPassword} />
          <NextBtn onClick={handleNext} disabled={!isFormValid} />
        </SignupBox>
      </Signup1Wrapper>
    </>
  );
};
export default Signup1;

const Signup1Wrapper = styled.div`
  width: 100%;
  display: flex;
  flex-direction: column;
`;

const SignupBox = styled.div`
  width: 100%;
  margin: 50px auto;
`;

const InputCheck = styled.div<{ valid: boolean }>`
  font-size: 11px;
  margin-left: 10px;
  margin-bottom: 5px;
  color: #c9c9c9;
  color: ${(props) => (props.valid ? "green" : "#c9c9c9")};
`;

signup1 컴포넌트에서는 기존의 onNext 함수를 onStepChange로 변경해주고 props를 전달하기 위해 step을 넣었다.

이메일 중복검사를 위해 get방식의 axios를 이용해 이메일 값이 DB에 있는지 확인하는 handleEmailCheck 함수를 만들었다. api를 통해 백엔드에서 응답한 메세지값을 상수에 담아 해당 값이 사용중인 이메일이면 true를 반환하고 사용할 수 있는 이메일이면 false를 반환한다. boolean값이 바뀐거 같긴한데 결국 참 거짓이라 크게 상관없을 것 같아서 그대로 사용했다.

 

그리고 버튼 onClick함수를 담당하는 handleNext에 api를 요청하는 handleEmailCheck함수를 넣어 반환값을 상수에 담는다.

해당 boolean값에 따라  EmailCheck를 판단하여 사용중일 경우 alert을 띄우며 다음으로 못넘어가게 막았고 사용할 수 있는 경우 Check값에 true를 넣으며 상위 컴포넌트의 props인 onStepChange에 다음 페이지값을 2로 넣을 수 있게 수정했다.

interface NextProps {
  onStepChange: (step: number) => void;
}

const Signup2: React.FC<NextProps> = ({ onStepChange }) => {
  const dispatch = useDispatch();
  const username = useSelector((state: RootState) => state.signup.username);
  const [usernameValid, setUsernameValid] = useState<boolean | null>(null);
  const backPort = process.env.REACT_APP_BACKEND_PORT;

  const handleNicknameCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    const isValid = /^[a-zA-Z0-9가-힣]*$/.test(value);

    dispatch(setUsername(value));

    if (value.length <= 10 && value.length > 0 && isValid) {
      setUsernameValid(true);
    } else {
      setUsernameValid(false);
    }
  };

  const handleRandomNickname = () => {
    const randomColor = colors[Math.floor(Math.random() * colors.length)];
    const randomAnimal = animals[Math.floor(Math.random() * animals.length)];
    const randomNickname = `${randomColor}${randomAnimal}`;

    dispatch(setUsername(randomNickname));
    setUsernameValid(true);
  };

  const handleNicknameDuplicationCheck = async () => {
    try {
      const response = await axios.get(
        `${backPort}/api/user/check/${username}`
      );
      const message = response.data.message;

      if (message === "이미 사용중인 닉네임입니다.") {
        return true;
      }

      return false;
    } catch (error) {
      console.error("닉네임 중복 확인 중 오류", error);
      return true;
    }
  };

  const handleNext = async () => {
    if (!usernameValid) return;

    const isDuplicated = await handleNicknameDuplicationCheck();

    if (isDuplicated) {
      alert("이미 사용중인 닉네임입니다.");
    } else {
      onStepChange(3);
    }
  };

  return (
    <>
      <Signup2Wrapper>
        <SignupBox>
          <PageCount count="2" />
          <PageGuide text="닉네임을 설정해주세요" />
          <NicknameBox>
            <CommonInput
              typeValue="text"
              placeholderValue="닉네임"
              value={username}
              onChange={handleNicknameCheck}
            />
            {usernameValid !== null && (
              <NickCheckWrapper>
                <NicknameCheck valid={usernameValid}>
                  {usernameValid
                    ? "사용할 수 있는 닉네임이에요"
                    : "사용할 수 없는 닉네임이에요"}
                </NicknameCheck>
                <span>{username.length}/10</span>
              </NickCheckWrapper>
            )}
            <RandomCreate onClick={handleRandomNickname}>
              랜덤 생성
            </RandomCreate>
          </NicknameBox>
          <NextBtn onClick={handleNext} disabled={!usernameValid} />
        </SignupBox>
      </Signup2Wrapper>
    </>
  );
};
export default Signup2;

해당 닉네임 중복 체크도 이메일 체크와 비슷하게 get방식의 axios를 사용해 받아온 메세지값을 대조해서 중복값인지 체크하는 방식이다.

 

해당 api들을 구현한 후 회원가입과 로그인이 제대로 작동되는지 테스트 한 결과 사아소한 실수 때문에 시간이 좀 걸렸지만 제대로 구현 완료했다.

원래 api 경로를 입력할 때 `${백엔드 주소}/api/user/blurblur`이런식으로 작성해놓는데 백엔드 주소를 안집어넣고 /api/user/blurblur만 써서 왜 안되는건지 한참 헤맸다. 하필 에러메세지도 거의 본적없는 error code: 431이 떠서 더 헤맸는데 431 에러는 header의 용량이 요청값과 맞지 않아 발생하는 문제라고 한다. 근데 이게 왜 여기서 뜨는지 도저히 이해가 안 가서 삽질을 오래 했다. 후..

어이없어서 웃는 재희님 보고 눈치보여서 오히려 좋아 한번 봐달라 하고 넘어갔다.

그리고 api 명세서를 잘못확인하여 서버에 전달할 데이터 명도 싹 수정했다.

이 후 먼저 해야될 순서는 루틴 페이지의 데이터 받아오기와 정렬 드롭다운 구현 그리고 필터 구현도 들어갈 예정이다. api명세서를 확인한 결과 결제 페이지라던지, 주소 적는 칸이라던지 구현할 페이지가 더 생겨서 페이지 기획이 되는대로 바로 들어가려고 한다.

 

< 재남갓 그의 블로그 >

https://jaenam615.github.io/

 

재로그

JH Nam

jaenam615.github.io

 

728x90

'개발 TIL' 카테고리의 다른 글

24.09.11 day65 작업 일지  (2) 2024.09.11
24.09.10 day64 작업 일지  (0) 2024.09.10
24.09.06 day62 작업 일지  (2) 2024.09.06
24.09.05 day61 작업 일지  (0) 2024.09.05
24.09.04 day60 jwt 오류 해결, 카카오 로그인 구현 중  (3) 2024.09.04