팀프로젝트: Wingle(1.7) API 연결 - Auth: 회원가입 데이터 전송

윤뿔소·2023년 5월 8일
0

팀프로젝트: Wingle

목록 보기
11/16

이제 회원가입 막바지이다. 그동안 다른 컴포넌트에서 다 회원가입 데이터를 주고 받는 컴포넌트를 작성했다면 이번에는 Atom을 검사하고, 필수적인 데이터들이 다 들어왔다면 API 요청을 보낼 수 있게 할 것이다!

디자인 / 기능 기획

이렇게 피그마를 보면 순서대로 배치가 되고, 필수 데이터들이 다 들어오면 바로 버튼이 활성화가 된다.

즉, 필요한 건 아래와 같다.

  1. 나머지 TSX 부분 디자인 및 그동안 만든 컴포넌트 5개 넣기
  2. 버튼 disabled하게 만드는 상태 추가
  3. 리코일 아톰을 불러오고, 아톰이 변경되는 순간 감지할 수 있는 useEffect가 필요
  4. API 호출 코드 작성하기
  5. useMutation을 사용해 API 호출 실행하기

할 게 되게 많다.. 1번부터 차근차근히 해보자.

기능 개발 스타트

디자인은 이미 만들어진 상태라 모양만 추가하는 식으로 할 것이다. 나중에 SRP 관련이나 변수 리팩토링은 추가적으로 진행할 것이다!

일단 기본적인 디자인은 아래와 같다.

import React, { useState, useEffect } from "react";
import { Text, Margin } from "@/src/components/ui";
import styled from "styled-components";
import InputBox from "../../components/authpage/signup/signUpInput";
import DropDown from "../../components/authpage/signup/dropDownSignUpCountry";
import StudentCard from "../../components/authpage/signup/studentCard";
import GenderSelectBox from "../../components/authpage/signup/genderSelect";
import AgreeBox from "@/src/components/authpage/signup/agreeBox";
import router from "next/router";
import Image from "next/image";

export default function SignUp() {
  return (
    <S.Wrapper>
      <S.HeaderWrapper>
        <S.BackButton
          src="/auth/arrow_back.svg"
          alt="arrow"
          width={24}
          height={24}
          onClick={() => router.push("/auth/login")}
        />
        <Margin direction="row" size={14} />
        <Text.Title2 color="gray900">회원가입</Text.Title2>
      </S.HeaderWrapper>

      <StudentCard />
      <InputBox />
      <DropDown />
      <GenderSelectBox />
      <AgreeBox />

      <S.CompleteButton
        onClick={}
      >
        작성완료
      </S.CompleteButton>
    </S.Wrapper>
  );
}

const S = {
  Wrapper: styled.div`
    padding-left: 24px;
    padding-right: 24px;
  `,
  HeaderWrapper: styled.div`
    padding: 16px 0;
    display: flex;
  `,
  BackButton: styled(Image)`
    cursor: pointer;
  `,
  CompleteButton: styled.button`
    background-color: #FF812E;
    color: #fff;
    border-radius: 8px;
    width: 452px;
    height: 50px;
    margin-bottom: 144px;
    cursor: pointer;
    border-radius: 8px;
    margin: 0 auto;
    font-weight: 700;
    font-size: 16px;
    line-height: 22.4px;
    margin-bottom: 144px;
  `,
};

기본적인 디자인과 만든 컴포넌트는 저렇게 넣었다. 이제 추가적으로 만들 것이다!

버튼 disabled하게 만드는 상태 추가

일단 상태를 추가해야된다.

const [isButtonDisabled, setButtonDisabled] = useState(true);

직관적인 변수로 isButtonDisabled로 네이밍을 했다.

이제 버튼에 넣어보자. Props로 받아야하고, 타입도 지정하고, 조건별로 적용되는 CSS 코드를 집어넣어보자!

Props로 받기

disabled라는 PropisButtonDisabled를 할당하자.

  return (
    ...
      <S.CompleteButton
        disabled={isButtonDisabled}
        onClick={}
      >

Styled-Components 코드 수정하기

  1. Props의 타입 지정
  2. disabled라는 조건을 나눠 CSS 적용하기
  CompleteButton: styled.button<{ disabled: boolean }>`
    background-color: ${({ disabled }) => (disabled ? "#EEEEF2" : "#FF812E")};
    color: ${({ disabled }) => (disabled ? "#959599" : "#fff")};
    border-radius: 8px;
    width: 452px;
    height: 50px;
    margin-bottom: 144px;
    cursor: ${({ disabled }) => (disabled ? "not-allowed" : "pointer")};
    ...
  `,

이렇게 말이다! disabled의 타입 지정 및 조건에 맞게 클릭을 하지 못하고, 배경, 폰트색도 변하게 만들어 UI 적으로 잘 보이게 해뒀다.

isButtonDisableduseEffect

이제 useEffect를 사용하여 리코일 아톰이 변경되면서 조건에 충족한다면 setter 함수를 불러와 상태를 바꿔보겠다!

export default function SignUp() {
  const [isButtonDisabled, setButtonDisabled] = useState(true);

  const signUpFormData = useRecoilValue(signUpFormDataAtom);

  useEffect(() => {
    if (
      signUpFormData.idCardImage &&
      signUpFormData.email &&
      signUpFormData.password &&
      signUpFormData.name &&
      signUpFormData.nation &&
      signUpFormData.termsOfUse &&
      signUpFormData.termsOfPersonalInformation
    ) {
      setButtonDisabled(false);
    }
  }, [signUpFormData]);
  ...

보이는가? 고봉밥이?
무시무시한 길이긴 하지만 그냥 리코일 아톰에 있는 데이터 중 필수로 들어가야할 데이터만 조건으로 둔 것이다.

signUpFormData가 변할 때마다, 조건을 충족하는지 확인하고, 충족된다면 setButtonDisabled를 호출해 false로 만든다!

signUpFormData 변경에 대한 고찰

근데 위의 단점이 분명 있을 거 같다. signUpFormData가 변할 때마다 불필요하게 useEffect이 실행되니까 말이다. 뭔가 아톰의 데이터를 계속 감시하는 게 아니라 한번에 다 차면 변경되게 할 수 있는 방법이나 로직을 생각할 수 있을 거 같은데 잘 모르겠다.

아래같은 방법도 생각해본 적이 있다. useMemo를 넣어서 해봤는데 비슷한 거 같다.. if문이 실행되지 않기 때문에 상관없으려나?

const isFormValid = useMemo(
  () =>
    !!(
      signUpFormData.idCardImage &&
      signUpFormData.email &&
      signUpFormData.password &&
      signUpFormData.name &&
      signUpFormData.nation &&
      signUpFormData.termsOfUse &&
      signUpFormData.termsOfPersonalInformation
    ),
  [signUpFormData]
);

useEffect(() => {
  setButtonDisabled(!isFormValid);
}, [isFormValid]);

나중에 더 배워서 이 부분을 보고 어떻게 고쳤는지, 아니면 아예 다른 방법이 있는지 고찰해보자.


쨌든 버튼 관련 상태와 그 setter 함수를 호출하는 것, 호출하는 로직까지 작성해봤다.
다음 편에서는 API 작성과 리액트쿼리 구현을 해보겠다. 다음편이 좀 빡셀 것이다.. 힘들었기 때문에 제대로 기록해보겠다.

profile
코뿔소처럼 저돌적으로

4개의 댓글

comment-user-thumbnail
2023년 5월 8일

마지막 제가 잘이해한지는 모르겠는데 전부다 바뀌고나서 업데이트하기를 원한다면 위처럼은 안됩니다 하나만 바뀌어도 새로운 객체로 생성되니깐요 모든게 바뀌었을때를 하시려면
마지막 로직이 작성된 이후에 활성화를 하시거나 모두다 체크하는 방식으로 하는 수밖에 없지않을까 싶네요 화이팅

답글 달기
comment-user-thumbnail
2023년 5월 14일

ㅋㅋ 고봉밥 든든~하네요

답글 달기
comment-user-thumbnail
2023년 5월 14일

고생하셨네욤!

답글 달기
comment-user-thumbnail
2023년 5월 14일

고봉밥ㅋㅋㅋㅋㅋ 흠.. 저렇게 조건이 많을 때는 더 깔끔하게 작성할 방법이 없는지 저도 궁금하네요..!!

답글 달기