본문 바로가기
WebDev/항해99

실전프로젝트 1주차 회고

by S.AHA_dev 2023. 8. 6.
728x90
반응형

7/30일 부터 8/2일까지 설계 기간이었다.

 

백과 프론트 각각 코드 컨벤션을 정하고 백의 ERD작성 후

프론트팀은 팀장님, 디자이너 님과 컨셉을 정한 후 와이어 프레임 작업에 도달 했다.

이 부분에서 조금 아쉬운 점이 있더라면 와이어 프레임을 온전히 맡기고 프론트 팀과 함께 

기술공부에 온전히 집중하지 못한 것이 아쉬움으로 남는다. 

 

우리팀은 이미지를 많이 받아와서 출력해야 했기 때문에

서버사이드렌더링과 넥스트의 강력한 기능인 넥스트 이미지를 도입해야 했어서

프론트 팀장님을 제외한 모든 프론트 인원은 공부를 하면서 작업을 해야 했다.

이때 빨리 들어갔더라면 우리팀장님을 안 귀찮게 했을 수도 ㅠㅠ

 

또한 배우지 않은 타입스크립트도 같이 적용하며 작업에 들어갔다. 

타입지정은 생각보다 어렵지 않았으므로 금방 적응하며 이어갔지만

넥스트의 주요 개념을 적용하는게 생각보다 쉽지는 않았다.

(넥스트이미지를 명시적으로 적어야하는 것 스타일드컴포넌트의 호환성 문제 등등)

 

일단 내가 맡은 부분은 로그인/회원가입/유저페이지를 1차 MVP기능으로 가져와 작업 중이다.

import React, { useState } from 'react';
// import WriteInput from './WriteInput';
// import CheckBox from './CheckBox';
import SocialLogin from './SocialLogin';
import {
  SignUpSection,
  MainHeadText,
  SubHeadText,
  TextParagraphSns,
  SignUpBtn,
  StyledInputBox,
  StyledInput,
  TextParagraph,
  Checkbox,
  SignUpCheckBox,
  SignUpCheckBoxLayout,
  StyledLabel,
  StyledLabelAll,
  Line,
} from '@/styles/signUp';
import { useMutation } from 'react-query';
import { addUser } from '@/api/user';
import { PreviewContainer } from '../../styles/write';
import { useRouter } from 'next/router';

export interface Signup {
  email: string;
  password: string;
  passwordConfirm: string;
  nickname: string;
}
export interface CheckBoxInterface {
  checkAll: boolean;
  checkTerms: boolean;
  checkPersonalInfo: boolean;
  checkNewsletter: boolean;
}
type TextInputType = 'email' | 'password' | 'passwordConfirm' | 'nickname';

const SignUpUi = () => {
  const router = useRouter();
  const [signUpState, setSignUpState] = useState<Signup>({
    email: '',
    password: '',
    passwordConfirm: '',
    nickname: '',
  });
  const [error, setError] = useState<Signup>({
    email: '',
    password: '',
    passwordConfirm: '',
    nickname: '',
  });
  const [checkboxes, setCheckboxes] = useState<CheckBoxInterface>({
    checkAll: false,
    checkTerms: false,
    checkPersonalInfo: false,
    checkNewsletter: false,
  });

  const addUserMutation = useMutation(addUser, {
    onSuccess: () => {
      console.log('회원가입 성공');
      router.push('/auth/SignIn');
    },
    onError: (error) => {
      console.error('회원가입 실패:', error);
    },
  });

  //인풋창들 함수
  const handleInputChange = (
    e: React.ChangeEvent<HTMLInputElement & { name: TextInputType }>
  ) => {
    const { name, value } = e.target;

    setSignUpState((prevSignUpState) => ({
      ...prevSignUpState,
      [name]: value,
    }));
  };

  const validateEmail = (email: string) => {
    const emailRegex = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
    return emailRegex.test(email);
  };

  const validatePassword = (password: string) => {
    const passwordPattern = /^(?=.*\d)(?=.*[a-zA-Z]).{8,}$/;
    return passwordPattern.test(password);
  };

  const validateNickname = (nickname: string) => {
    const nicknamePattern = /^.{2,15}$/;
    return nicknamePattern.test(nickname);
  };
  const validateForm = () => {
    return checkboxes.checkAll;
  };
  // const emailCheckSubmit = (e: React.MouseEvent) => {
  //   e.preventDefault();
  //   if (validateEmail(signUpState.email)) {
  //     alert('중복확인 되었습니다.');
  //   } else {
  //     alert('이메일이 유효하지 않습니다. 다시 입력해 주세요.');
  //   }
  // };
  const handleCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, checked } = e.target;

    if (name === 'checkAll') {
      setCheckboxes({
        checkAll: checked,
        checkTerms: checked,
        checkPersonalInfo: checked,
        checkNewsletter: checked,
      });
    } else {
      setCheckboxes({
        ...checkboxes,
        [name]: checked,
        checkAll:
          checkboxes.checkTerms && checkboxes.checkPersonalInfo && checked,
      });
    }
  };

  const submitUser = (event: any) => {
    event.preventDefault();

    let errors: any = {};

    if (!signUpState.email) {
      errors.email = '이메일 주소를 입력해주세요.';
    } else if (!validateEmail(signUpState.email)) {
      errors.email = '이메일 형식이 올바르지 않습니다.';
    }

    if (!signUpState.password) {
      errors.password = '비밀번호를 입력해주세요.';
    } else if (!validatePassword(signUpState.password)) {
      errors.password =
        '비밀번호는 영문, 숫자를 포함하여 8자 이상이어야합니다.';
    }

    if (signUpState.password !== signUpState.passwordConfirm) {
      errors.password = '비밀번호가 일치하지 않습니다.';
      errors.passwordConfirm = '비밀번호가 일치하지 않습니다.';
    }

    if (!signUpState.nickname) {
      errors.nickname = '닉네임을 입력해주세요.';
    } else if (!validateNickname(signUpState.nickname)) {
      errors.nickname = '2~15자를 입력해주세요.';
    }

    if (Object.keys(errors).length > 0) {
      setError(errors);
      return;
    } else {
      setError({ email: '', password: '', passwordConfirm: '', nickname: '' });
    }
    if (Object.keys(errors).length === 0) {
      const sendData = {
        email: signUpState.email,
        password: signUpState.password,
        nickname: signUpState.nickname,
      };
      addUserMutation.mutate(sendData);
    }
  };

  return (
    <SignUpSection>
      <MainHeadText>HAPOOM</MainHeadText>
      <SubHeadText>회원가입</SubHeadText>
      <TextParagraphSns>sns계정으로 간편 로그인/회원가입</TextParagraphSns>

      <SocialLogin />

      <form name="register" onSubmit={submitUser}>
        <StyledInputBox>
          <TextParagraph>이메일</TextParagraph>
          <StyledInput
            type="email"
            name="email"
            value={signUpState.email}
            placeholder="example@gmail.com"
            onChange={handleInputChange}
          />
          {error.email && <p style={{ color: 'red' }}>{error.email}</p>}
          <SignUpBtn
            onClick={(event: any) => {
              event.preventDefault();
              alert('준비중입니다.');
            }}
          >
            이메일 인증하기
          </SignUpBtn>
        </StyledInputBox>

        <StyledInputBox>
          <TextParagraph>비밀번호</TextParagraph>
          {error.password ? null : (
            <p>영문, 숫자를 포함한 8자이상의 비밀번호를 입력해주세요</p>
          )}
          <StyledInput
            type="password"
            name="password"
            value={signUpState.password}
            placeholder="비밀번호를 입력해 주세요"
            onChange={handleInputChange}
          />
          {error.password && <p style={{ color: 'red' }}>{error.password}</p>}
          <TextParagraph>비밀번호 확인</TextParagraph>
          <StyledInput
            type="password"
            name="passwordConfirm"
            value={signUpState.passwordConfirm}
            placeholder="비밀번호 확인"
            onChange={handleInputChange}
          />
          {error.passwordConfirm && (
            <p style={{ color: 'red' }}>{error.passwordConfirm}</p>
          )}
        </StyledInputBox>

        <StyledInputBox>
          <TextParagraph>닉네임</TextParagraph>
          {error.nickname ? null : (
            <p>다른 유저와 겹치지 않도록 입력해 주세요(2~15자)</p>
          )}
          <StyledInput
            type="text"
            name="nickname"
            value={signUpState.nickname}
            placeholder="닉네임을 입력해 주세요"
            onChange={handleInputChange}
          />
          {error.nickname && <p style={{ color: 'red' }}>{error.nickname}</p>}
        </StyledInputBox>

        <TextParagraph>약관동의</TextParagraph>
        <SignUpCheckBoxLayout>
          <SignUpCheckBox>
            <Checkbox
              type="checkbox"
              name="checkAll"
              checked={checkboxes.checkAll}
              onChange={handleCheckboxChange}
            />
            <label htmlFor="check-all"></label>
            <StyledLabelAll>전체동의</StyledLabelAll>
            <StyledLabel>선택항목에 대한 동의 포함</StyledLabel>
          </SignUpCheckBox>
          <Line></Line>

          <SignUpCheckBox>
            <Checkbox
              type="checkbox"
              name="checkTerms"
              checked={checkboxes.checkTerms}
              onChange={handleCheckboxChange}
            />
            <label htmlFor="check-terms"></label>
            <StyledLabel>이용약관 (필수)</StyledLabel>
          </SignUpCheckBox>

          <SignUpCheckBox>
            <Checkbox
              type="checkbox"
              name="checkPersonalInfo"
              checked={checkboxes.checkPersonalInfo}
              onChange={handleCheckboxChange}
            />
            <label htmlFor="check-personalInfo"></label>
            <StyledLabel>개인정보 수집/이용 동의 (필수)</StyledLabel>
          </SignUpCheckBox>

          <SignUpCheckBox>
            <Checkbox
              type="checkbox"
              name="checkNewsletter"
              checked={checkboxes.checkNewsletter}
              onChange={handleCheckboxChange}
            />
            <label htmlFor="check-newsletter"></label>
            <StyledLabel>개인정보 마케팅 활용 동의 (선택)</StyledLabel>
          </SignUpCheckBox>
        </SignUpCheckBoxLayout>

        <SignUpBtn type="submit" disabled={!validateForm()}>
          회원가입하기
        </SignUpBtn>
      </form>
    </SignUpSection>
  );
};

export default SignUpUi;

회원가입 로직인데 최대한 유효성 검사에 신경을 쓰려고 노력했으며 에러가 나온다면 객체에 담아서 틀렸을 시에 출력하는 방식으로 작성했다. 아직 컴포넌트 분리를 하지 않아 가독성이 좋지는 않지만 로그인까지 작성 후 컴포넌트 분리와 최적화 작업에 들어 갈 것이다.

지금의 성능 ㅎㅎㅎ..... 56점........

모두 90점대로 끌어올려야 하는 것이 나의 목표!

반응형

'WebDev > 항해99' 카테고리의 다른 글

실전프로젝트 분담 변경  (0) 2023.08.11
실전프로젝트 로그인 로직 정리  (0) 2023.08.07
Async/Await와 Promise의 차이점  (0) 2023.08.04
var, let, const의 차이점  (0) 2023.08.03
useEffect는?  (0) 2023.08.02