[Assignment 3 회고록] 자란다 - 권한 관리와 대시보드

이다은·2021년 8월 7일
0

프리온보딩-회고록

목록 보기
3/11
post-thumbnail

🔗 Github

🔗 배포 링크

🔗 기능별 영상 링크

🔗 피그마 링크


👊 메인 / 로그인 / 회원가입 페이지 개발하기

직접 구현한 부분

✅ 마크업 및 스타일링

  • Input(Text)
  • Input(Radio)
  • Modal(Portal)
  • MessageBox

✅ 로그인, 회원가입 페이지 기능 구현

  • form 입력 데이터 유효성 검사
  • 패스워드 암호화 처리(bcrypt)
  • 모달 팝업 기능 구현
  • 주소 입력 모달 창 다음 지도 API 연동(react-daum-postcode)
  • 신용 카드 입력 모달 창

🔰 공통 컴포넌트 어떻게 해야 잘 만들 수 있을까

  • 컴포넌트를 재사용 할 수 있게끔 만드려고 노력했다.
  • Input-Text
 <Input
            name="email"
            value={formData.email}
            onChange={handleSignUpChange}
            placeholder="이메일을 입력하세요"
            icon={<Mail />}
            error={errors.email}
            errorMessage={getEmailStatusMessage(emailDuplicateStatus)}
            successMessage={emailDuplicateChecked && "사용 가능한 이메일 입니다"}
            width="75%"
 />
  • Input-Radio
 <Radio
          name="authority"
          value={formData.authority}
          onChange={handleSetAuthority}
          data={[
            { value: AUTH_LEVEL.teacher, label: "선생님" },
            { value: AUTH_LEVEL.parent, label: "부모님" },
          ]}
          error={errors.authority}
          errorMessage="원하시는 계정 유형을 선택해 주세요."
/>

🔰 사용해본 기술에 대한 정리

1. Portals : 모달을 띄우는 새로운 방식(처음 사용해봤다)

  • public/index.htmlmodalDom Element를 만들었다.
    (만들 때 public/index.html을 고치지 않고 모달을 사용할 컴포넌트 안에서 엘리먼트를 생성했는데, 성상님이 전역에 생성해야 오염을 받지 않을거라 말씀해주셨다 짱👍)
    <body>
        <div id="root"></div>
        <div id="modalDom"></div>
    </body>
  • createPortal로 모달을 생성하고, chilren으로 보여질 컴포넌트만 넣어주면 된다.
    const elRef = document.getElementById("modalDom");
      return ReactDOM.createPortal(
        <Wrapper ref={ref} onClick={handleClick}>
          {children}
        </Wrapper>,
        elRef
      );

2. bcryptjs :패스워드 암호화

  • LocalStorage에 사용자 정보를 저장하여 사용하기 때문에 별도로 token을 발행하거나 하진 않지만, 비밀번호는 암호화해서 저장하기로 했다.

  • bcryptjs를 설치하고 아래의 함수를 사용해주었다.

  • hashSync의 두번째 매개변수는 salt 값이다. 숫자가 클수록 암호화가 잘되지만 성능이 떨어질 수 있다는 단점이 있다.

export const hashSync = (pw) => bcrypt.hashSync(pw, 8);
export const compareSync = (pw, hash) => bcrypt.compareSync(pw, hash);

3. react-daum-postcode : 주소 API 라이브러리를 다운받아서 진행

  • npm react-daum-postcode 에서 설치해서 사용하기만 하면 된다.
  • 설명이 아주 자세하고 설치만 하면 되는 수준이다.

4. forwardRef : 신용카드 4글자 입력 후 포커스 자동으로 옆으로 이동되게 하기

  • 신용카드 4자리씩 입력받을 수 있는 Input 컴포넌트 4개를 생성했다
  • inputRef 의 초기값을 배열로 생성해서 Input의 ref에 연결해줬다.
const inputRef = useRef([]);
//생략...

<div className="card-wrapper">
        {Array.from({ length: 4 }, (_, i) => i).map((idx) => (
          <div key={idx}>
            <Input
              ref={(r) => (inputRef.current[idx] = r)} // ✅ 이 부분!
              name={`num${idx}`}
              value={creditCardNumber[`num${idx}`]}
              onChange={handleChangeCreditCardNumber}
              maxLength={4}
              numberOnly
            />
            <span>-</span>
          </div>
        ))}
</div>
  • (에러발생) ref를 연결해준 Input은 내가 공통 컴포넌트로 만든 Input 컴포넌트이므로 에러가 발생한다! (> 함수 컴포넌트의 ref는 애초에 존재하지 않기 때문)
  • (해결방법) https://merrily-code.tistory.com/121
    React.forwardRef 활용하기
    forwardRef를 사용하면 부모 컴포넌트로부터 하위 컴포넌트로 ref를 전달할 수 있다.
    이렇게 전달받은 ref를 HTML 요소의 속성으로 넘겨줌으로써 함수 컴포넌트 역시 ref를 통한 제어가 가능하다.
import React, { forwardRef } from "react";
//생략...
export default forwardRef(Input);

🔰 로그인/비밀번호 유효성 검사

  1. 요소별 정규식 패턴을 만든다.
export const isEmail = (checkString) => {
  const emailRegex =
    /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
  return emailRegex.test(checkString);
};

export const isPassword = (checkString) => {
  const passwordRegex = /(?=.*\d{1,50})(?=.*[~`!@#$%\^&*()-+=]{1,50})(?=.*[a-zA-Z]{1,50}).{8,50}$/;
  return passwordRegex.test(checkString);
};

//이후 다른 정규식은 생략...
  1. validator 함수에 유효성 검사 판별식 넣어준다
    반복문을 수행하면서 각 항목마다 value값이 유효성 검사에 맞는지 확인하고,
    에러일 경우 setErrors 함수를 통해 에러나는 부분을 true로 저장한다
const validator = {
    authority: (authority) => !(authority === AUTH_LEVEL.unknown),
    email: (email) => isEmail(email),
    pw: (pw) => isPassword(pw),
    pwCheck: (pwCheck) => pwCheck === formData.pw,
    name: (name) => isName(name),
    address: (address) => !(address === ""),
    detailAddress: (detailAddress) => !(detailAddress === ""),
    dateOfBirth: (dateOfBirth) => isDateOfBirth(dateOfBirth),
    creditCardNum: (creditCardNum) => isCreditNum(creditCardNum),
  };

const isAllValid = (data) => {
    for (const name in data) {
      const value = data[name];
      const validateFunction = validator[name];

      if (!validateFunction(value)) {
        setErrors((prev) => ({
          ...prev,
          [name]: true,
        }));
        return false;
      } else {
        setErrors((prev) => ({
          ...prev,
          [name]: false,
        }));
      }
    }
    return true;
  };
  1. 로그인/회원가입 버튼을 눌렀을 경우(Submit) 유효성 검사를 진행한다.
const handleSignupSubmit = (e) => {
    e.preventDefault();

    const allValid = isAllValid(formData);
    if (allValid) { // ✅ 유효성 검사 통과!
      // ...
    }
  };

🔰 useState의 Setter 함수는 비동기다

  • 유효성 검사를 하면서, 하나의 Func에서 setter 함수를 여러번 호출했는데 업데이트가 반영되지 않고 마지막 setter 함수만 실행되는 결과를 얻었다.

  • 왜 그렇지...왜 이렇게 나오지..계속 생각했는데 setter가 비동기 함수라는걸 잊고 있었다🤦‍♀️

  • 잊지말자. 리액트에서 setState는 비동기로 작동하므로 상태를 업데이트할 경우, 업데이트 된 상태는 즉시 반영되지 않는다!!

  • > 잘 정리된 블로그를 추가


🔰 Git Project/칸반보드 활용

  • Git에 이러한 기능이 있는지 몰랐다. 이슈와 연결해서 프로젝트 진행이 어떻게 되고 있는지, 나뿐만 아니라 팀원 전체의 흐름을 잘 파악할 수 있었다.
    > Git Project 칸반보드 활용

👀 아쉬웠던 점과 해결방안

  • 같이 작업했던 현정님이 처음에 모달 공통 컴포넌트를 개발하시면서 public/index.html에 엘리먼트를 생성하시고 작업했는데, 내가 수정하면서 이걸 컴포넌트 안으로 가져왔다. 근데 다른 팀원분들과 머지를 다 하고보니 public/index.html에 있는게 맞는거 였다.
    Portal을 처음 사용해봐서 모르기도 했지만, 내가 잘 찾아서 했다면 번거롭지 않았을 텐데 하는 생각에 아쉬움이 들었다. 다음에는 설명글을 자세하게 읽고 시작해야겠다.
profile
단단_프로트엔드개발자!

0개의 댓글