[2차 프로젝트 회고] 기억하고싶은 코드들

김현재·2021년 10월 4일
2

Projects

목록 보기
6/6
post-thumbnail

잊고싶지 않은 코드들을 기록해보는 시간✨

custom hook 사용

로그인 페이지와 회원가입 페이지에서 공통적으로 사용하는 유효성 검증 로직을 custom hook으로 빼내어 별도로 관리해주었다.
이렇게 함으로써, 각 페이지 component의 용량을 줄일 수 있고, 유효성 관련된 조작을 한 페이지에서 할 수 있어 관리가 용이해졌다.

아래의 useForm은 form에 입력되는 모든 값을 controll하는 hook이다.
이 hook을 통해서 입력된 값들을 error validation함수로 조건부 전달이 가능하다.

import { useEffect, useState } from 'react';

// 모든 입력란의 초기값, 제출된 입력값을 처리하는 로직, 입력값을 검증하는 로직을 인자로 받습니다.
function useForm({ initialValues, onSubmit, validate }) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({}); //틀린 내용 알려주는 것
  const [submitting, setSubmitting] = useState(false);

  const handleChange = e => {
    const { name, value } = e.target;
    setValues({ ...values, [name]: value });
  };

  // 제출 이벤트 처리
  const handleSubmit = e => {
    setSubmitting(true);
    e.preventDefault();
    setErrors(validate(values));
  };

  // 에러가 없을 때만 인자로 넘어온 입력값을 처리
  useEffect(() => {
    if (submitting) {
      if (Object.keys(errors).length === 0) {
        onSubmit(values);
      }
      setSubmitting(false);
    }
  }, [errors]);

  // return을 해야 이 hook을 사용하는 함수에서 활용할 수 있다!
  return { values, errors, submitting, handleChange, handleSubmit };
}

export default useForm;

입력된 값들의 유효성 검증만 진행하는 validate 함수를 만들어서, 이곳에서 유효성 검증 처리 후 결과를 반환하도록 하였다.
여기선 error가 발생한 key와 error 내용을 객체화하여 form component가 있는 곳으로 전달해준다.
해당 객체에 내용이 들어있으면 form component에서 error를 대응하도록 해준다.

export default function validate({
  email,
  password,
}) {
  const errors = {};

  const num = /[0-9]/;
  const char = /[a-zA-Z]/;
  const pattern = /[~!@#$%^&*()-=_+]/;

  if (!email) {
    errors.email = '이메일을 입력해주세요';
  } else if (!email.includes('@') && !email.includes('.')) {
    errors.email = '올바른 양식으로 입력해주세요';
  }

  if (!password) {
    errors.password = '비밀번호를 입력해주세요';
  } else if (
    !num.test(password) ||
    !char.test(password) ||
    !pattern.test(password) ||
    password.length < 8
  ) {
    errors.password = '8자리 이상, 문자, 숫자, 특수문자를 포함시켜주세요';
  }

  return errors;
}

(아래는 연결된 component에서 error 객체를 처리하는 방법이다)

  return (
    <Wrapper>
      <Section onSubmit={handleSubmit}>
        <Form onSubmit={handleSubmit}>
          <InputWrapper>
            <InputSet>
              <input
                value={values.email}
                onChange={handleChange}
              />
              {errors.email && <Warning>{errors.email}</Warning>}
            </InputSet>
            <InputSet>
              <input
                value={values.password}
                onChange={handleChange}
              />
              {errors.password && <Warning>{errors.password</Warning>}
        </Section>      
    </Wrapper>
  );


axios 사용

실무에서 axios를 주로 활용하여 네트워크 통신을 한다는 것을 감안하여, 이번 프로젝트에서는 fetch 함수 대신 axios를 적용해보았다.
네트워크 통신과 관련된 로직은 data-fetch.js라는 함수를 별도로 만들어 한곳에서 관리되도록 하였다.

import axios from 'axios';
import URL from '../config';

class fetchData {
  constructor() {
  }
  
  async logIn(email, password) {
    const response = await axios.post(`${URL}/users/signin`, {
      email: email,
      password: password,
    });
    const result = response.data;
    return result;
  }

  async kakaoLogin(authObj) {
    const HEADERS = {
      'Content-Type': 'application/json',
      Authorization: authObj.access_token,
    };

    const response = await axios.post(`${URL}/users/signin/kakao`, null, {
      headers: HEADERS,
    });
    const result = response.data;
    return result;
  }
}

export default fetchData;

axios는 fetch와 다르게 response, result 부분을 모두 변수화 해서 사용할 수 있기에 보다 직관적으로 코드가 눈에 들어온다는 장점이 있었다.
또한 함수로 이루어져있기에, 네트워크 통신에 필요한 매개변수들도 보다 간편하게 전달이 가능한 것 같다는 인상을 받았다.

 headers, body 전달 방법

  async kakaoLogin(authObj) {
    const HEADERS = {
      'Content-Type': 'application/json',
      Authorization: authObj.access_token,
    };

    const response = await axios.post(`${URL}/users/signin/kakao`, null, {
      headers: HEADERS,
    });
    const result = response.data;
    return result;
  }

axios의 경우 .post와 같은 메서드 뒤에 아래와 같은 parameter를 기재하여야 한다.
1. URI 주소
2. body 객체 (필수 입력. 전달할 body가 없을 시 null로 표기)
3. 그 외 전달하고픈 객체(config, headers 등)

headers 작성 시 유의할 것은 body와 다르게 key name으로 headers를 명시해주어야 하며, value로 필요 객체를 넣어줘야 한다. (headers와 다르게 body의 경우 key name을 body로 명시할 필요가 없으며, 바로 전달하고픈 객체를 넣어주면 된다)



kakao login 이후 회원가입 페이지로 redirection (with react-router-dom)

우리가 클론코딩한 사이트는 P2P투자 사이트기에, 유저 정보에 "계좌정보"가 필수적으로 요구되었다.
허나 카카오 로그인 시, 해당 정보를 자동적으로 불러올 수 없기에 카카오 로그인의 경우 로그인 시도 시 바로 회원가입 페이지로 이동시켜, "계좌정보"를 입력시키도록 구현하였다.

보다 회원가입 방법을 편리하게 만들기 위해서, 카카오 로그인 시 승인한 정보들은 (이메일, 이름) 회원가입 페이지로 redirection 시 자동으로 입력되도록 하였다.
이를 구현하기 위해서,

  1. BE에서 카카오 로그인 시 토큰과 함께 유저 정보를 객체로 전달해주었다.
  2. 전달받은 객체를 history.push()에 함께 넘겨서 회원가입 페이지에서 자동으로 입력되도록 구현하였다.

카카오 로그인 시 회원 정보 담은 객체를 받아 회원가입 페이지로 전달하기

const handleKakaoLogin = e => {
    const { Kakao } = window;
    Kakao.Auth.login({
      success: response => {
        data
          .kakaoLogin(response)
          .then(res => {
            setKakaoTOKEN(res.access_token);
            alert('회원가입 페이지로 이동합니다.');
            history.push({
              pathname: '/signUp',
              state: { res },
            });
         });
       };
    });
  };

res라는 객체에는 BE에서 카카오 인증 이후 전달해주는 고객 정보가 들어가있다

{
  "res" : {
          "user_name": "김코드",
          "user_email": "kimcode@gmail.com"
         }
}

이 객체를 페이지 이동 시 전달하고자 하는 경우, history.push()내에 state를 활용하면 된다.

history.push({
              pathname: '/signUp',
              state: { res },
            });

history.push 로 받아낸 데이터를 화면에 출력하기

import { useHistory, useLocation } from 'react-router';

function SignUp() {
  const history = useHistory();
  const location = useLocation();

  //  카카오 로그인 시 기본적으로 받아오는 요소들
  const kakaoMail = location.state.res.user_email ?? null;
  const kakaoName = location.state.res.user_name ?? null;
  
  return (
    <Form onSubmit={handleSubmit}>
      <input
        value={kakaoMail}
       />
       {errors.name && <Warning>{errors.name}</Warning>}
    </Form>
  );

history.push로 전달받은 데이터는 useLocation()을 사용하여 출력할 수 있다.
위와 같이 useLocation()을 선언한 후, location.state.객체명을 변수에 할당하면, 받아온 객체를 자유롭게 사용할 수 있다.



localeString으로 입력된 값을 number로 변환하기


프로젝트 중 상당히 까다로웠던 작업 중 하나가, input에 숫자가 입력될 때에는 ,콤마가 자동적으로 입력되게 하면서, 동시에 state에 저장되는 값은 숫자로 저장되게 해야했던 것이다.

이는 아래와 같은 방법으로 해결되었다.

  1. onChange이벤트를 활용하여 입력되는 값을 state에 우선 저장한다.
  2. state에 저장된 값을 value를 사용하여 출력 시 toLocaleString()이 적용되도록 한다.
  3. toLocaleString()을 통해 문자열로 값이 변경된 것을 onChange함수가 다시 잡아내여, 이를 숫자로 바꿔주는 작업을 한다.

-> 즉 onChangevalue가 숫자인지 문자인지 확인한 후(if사용) 각각에 대응하여 기대되는 값으로 변환해주어야 한다.

구현 결과

const handleAmount = e => {
  const value = e.target.value;
  if (typeof value === 'string') {
    if (value.includes(',')) {
      setAmount(+value.replaceAll(',', '')); // 문자로 들어온 경우 숫자로 바꿔준 후 state에 저장
  } else {
      setAmount(+value); // 숫자는 숫자인 채로 state에 저장
  }
}

return(
  <Input
    value={amount.toLocaleString()} 
    //보여지는 값은 항상 콤마가 입력되어 있어야 하기에 state값에 toLocaleString()이 적용되도록 함
    onChange={handleAmount} //값이 변화함을 인지하여 state에 알맞게 처리되도록 함
    maxLength="7"
  />
);
profile
쉽게만 살아가면 재미없어 빙고!

0개의 댓글