프리온보딩 회고📝 - 패스트파이브

Sulhwa Choi·2022년 12월 30일
2
post-thumbnail

🥕 프리온보딩 - 패스트파이브

  • 패스트파이브의 기획문서를 기반으로 프로젝트를 진행하였습니다.

⏰ 개발 기간

﹣ 2022.12.12~2022.12.22 (11일)

🙌🏻 협업방식 - (트렐로)

트렐로를 이용해서 진행상황을 공유하였고, 디스코드를 이용하여 오전에 회의 하였습니다!

⚒️ 기술 스택

[프론트] - React, Javascript, Router, Sass
[백엔드] - Javascript, Express(Node.js), Mysql, dbmate(DB scheme 버전관리), jsonwebtoken(토큰 발행), bcryptjs(비밀번호 암호화),Axios

🙋🏻‍♀️ 내가 담당한 부분

  1. 회원가입
  2. 로그인, 로그아웃
  3. 대표/멤버 요청
  4. 게시글 리스트페이지 (전체보기 페이지, 카테고리페이지)

먼저 페이지는 모두 반응형으로 구현하였습니다. 회원가입과 로그인, 대표/멤버요청 페이지는 새롭게 모달을 구현할 수 있는 portal을 알게되어 공부하였고, 이를 이용해 구현하였습니다.

또한 미디어 쿼리로 넓이가 줄어 들 시 배경 이미지 사라지도록 하였습니다!

게시글 리스트 페이지는 grid를 minmax를 사용해 반응형으로 만들었습니다.


(1)회원가입


  //이메일 중복확인
  const [duplicateEmail, setDuplicateEmail] = useState(true);
  const duplicateCheck = e => {
    fetch(`${process.env.REACT_APP_API_URI}/email`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: email,
      }),
    })
      .then(res => res.json())
      .then(res => {
        if (res.duplicateEmail === false) {
          alert('사용 가능한 이메일 입니다.');
          setDuplicateEmail(false);
        } else {
          alert('이미 존재하는 이메일 입니다.');
        }
      });
  };

  const emailCheck = e => {
    const emailRegex =
      /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
    if (emailRegex.test(e.target.value)) {
      setEmailError(false);
      setEmail(e.target.value);
    } else {
      setEmailError(true);
    }
  };

  const passwordCheck = e => {
    const passwordRegex =
      /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,16}$/;
    if (passwordRegex.test(e.target.value)) {
      setPasswordError(false);
      setPassword(e.target.value);
    } else {
      setPasswordError(true);
    }
  };

회원가입은 이메일 유효성 검사 및 비밀번호 유효성 검사와 모든 항목을 다 작성해야만 회원가입을 진행할 수 있도록 구현하였다. 이메일과 비밀번호는 실시간으로 인풋창 아래에서 알림이 뜨도록 만들었으며, 모든 조건을 충족했을 시 알림이 사라지도록 구현하였다. 이메일 중복확인은 버튼 클릭 시 fetch를 이용해 백엔드에서 중복되는 이메일 여부를 확인하였다. 중복이면 다시 이메일을 작성해야하고, 사용가능하면 버튼이 비활성화 되도록 구현하였다.

(2)로그인, 로그아웃


 const onLogin = () => {
    fetch(`${process.env.REACT_APP_API_URI}/login`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: email,
        password: password,
      }),
    })
      .then(res => {
        if (res.status === 200) {
          return res.json();
        } else {
          return null;
        }
      })
      .then(res => {
        if (res !== null) {
          localStorage.setItem('token', res.token);
          alert('로그인에 성공하였습니다!');
          window.location.href = '/';
        } else {
          alert('로그인에 실패하였습니다.');
        }
      });
  };

  const onLogout = () => {
    window.localStorage.setItem('token', '');
    window.location.href = '/';
    setUserName('');
  };

이메일 양식 검사를 실시간으로 하였고, 비밀번호 입력 후, fetch를 이용해 로그인 성공 시 성공하였다고 알림창이 뜨고, 아닐 시 실패하였다고 알림창이 뜨게 구현하였다. 로그아웃은 토큰 값을 없애주고, 메인화면으로 돌아가도록 하였다.

(3)대표/멤버 요청

🥕 대표요청

🥕 멤버요청


//대표요청
  useEffect(() => {
    if (companyCheck) {
      setValidation(true);
    }
  }, [companyCheck]);

  const requestCheck = e => {
    alert('회사 이름을 작성해주세요');
  };

//캘린더
 const [startDate, setStartDate] = useState(new Date());
  const [endDate, setEndDate] = useState(new Date());

  const startDateFormat = moment(startDate, 'YYYY.MM.DD').format('YYYY-MM-DD');
  const endDateFormat = moment(endDate, 'YYYY.MM.DD').format('YYYY-MM-DD');

  useEffect(() => {
    setStartDateData(startDateFormat);
    setEndDateData(endDateFormat);
  }, [startDate, endDate]);

//멤버요청
 useEffect(() => {
    fetch(`${process.env.REACT_APP_API_URI}/company`)
      .then(res => res.json())
      .then(data => {
        setCompanyData(data);
      });
  }, []);

  const onCompanyData = e => {
    setUserCompany(e.target.value);
  };

대표요청에서는 캘린더(DatePicker)를 이용하여 이용기간을 설정할 수 있도록 하였으며 만료날짜는 시작 기한 이전은 선택할 수 없도록 만들었고, moment를 이용하여 원하는 포맷으로 변환시켜주었다. 대표는 자신의 회사를 요청하면 admin(관리자)가 승인 할 수 있다.

멤버요청에서는 등록된 회사들 중 자신의 회사를 선택할 수 있고, 선택 후 요청을 보내면 해당하는 회사의 대표가 승인할 수 있다.

(4)게시글 리스트페이지 (전체보기 페이지, 카테고리페이지)

  1. 로그인 여부 확인 후 게시글 페이지 이동가능

로그인을 해야만 게시글 페이지로 이동이 가능하며, 일반 멤버는 우리회사 소개하기 버튼이 보이지 않으며, 대표나 관리자만 우리회사 소개하기 버튼이 보인다.

// 로그인 여부 확인
   const toCompanyList = () => {
    const token = localStorage.getItem('token');
    if (token === '' || token === null) {
      alert(`로그인 후 이용해주세요!`);
      navigate(`/`);
    } else {
      navigate(`/companyList`);
    }
  };
  1. 게시글 전체보기 페이지

게시글 전체보기 페이지에서는 지역필터, 카테고리필터, 상세 카테고리 필터 이렇게 3가지 필터가 들어가야했고, 선택할때마다 쿼리스트링을 사용해 실시간 필터링 되도록 하였다. 그리고 카테고리 필터 변경 시 이전의 상세 카테고리 필터데이터는 리셋 시켜주었다.

useEffect(() => {
    const queryString = `${area ? `locationsId=${area}&` : ''}${
      category ? `categoriesLv1Id=${category}` : ''
    }${subCategory ? `&categoriesLv2Id=${subCategory}` : ''}`;
    setQueryString(queryString);
  }, [area, currentPage, category, subCategory, navigate, setQueryString]);
  1. 게시글 카테고리 페이지

게시글 전체보기 페이지에서는 1차로 해당 카테고리를 클릭해서 들어가기 때문에 지역필터, 상세분야필터 두가지만 뜨며, 해당하는 카테고리 데이터에서 실시간 필터링 됩니다.


//카테고리 해당 데이터 가져오기
  useEffect(() => {
    const token = localStorage.getItem('token');
    if (queryString !== '') {
      fetch(
        `${process.env.REACT_APP_API_URI}/post?categoriesLv1Id=${params.id}`,
        {
          headers: {
            'Content-Type': 'application/json',
            authorization: token,
          },
        }
      )
        .then(res => res.json())
        .then(data => {
          setAllData(data);
        });
    } else {
      fetch(`${process.env.REACT_APP_API_URI}/post?${queryString}`, {
        headers: {
          'Content-Type': 'application/json',
          authorization: token,
        },
      })
        .then(res => res.json())
        .then(data => {
          setAllData(data);
        });
    }
  }, [params.id, queryString]);
  1. 페이지네이션

한페이지에 10개의 게시글을 불러오고 페이지네이션 기능으로 페이지를 넘기면 다음 10개의 게시글을 불러오도록 하였다.


//전체 게시글을 10개로 나누어 페이지 버튼 생성
const pagination = [];
  for (let i = 1; i <= Math.ceil(allData.length / 10); i++) {
    pagination.push(i);
  }

{pagination.map(page => (
                <button
                  className={
                    Number(currentPage) === page ? css.currentPage : css.page
                  }
                  key={page}
                  value={page}
                  onClick={onPages}
                >
                  {page}
                </button>
              ))}

              
// fetch로 현재 페이지의 10개의 데이터를 가져왔다.
fetch(  `${process.env.REACT_APP_API_URI}/post?categoriesLv1Id=${params.id}&offset=10&page=${currentPage}`,
        {
          headers: {
            'Content-Type': 'application/json',
            authorization: token,
          },
        }
      )
        .then(res => res.json())
        .then(data => {
          setCompanyListData(data);
        });              

👏 프로젝트 마치는 소감

﹣ 기업의 기획문서를 토대로 개발하는것은 처음이라 지금까지 해왔던 프로젝트보다 훨씬 어려웠던 것 같다. 먼저 기획문서에서의 각 페이지마다 주어진 요구사항들을 구현하기 위해 많은 토론을 했으며, 문서 이해를 위해 하루종일 회의했었다. 전 프로젝트에서 구현해 본적이 없는 파트를 담당해보고 싶어 새로운 파트를 담당해보았는데 구현 하니 한층 성장한 느낌이 들어 뿌듯하고 재밌었다. 기한내에 구현하고자 후반부에 가서는 다들 새벽까지 열심히 코딩했는데 같이 한 팀원들 모두 수고하셨고 감사하다고 말하고 싶다!🥺 최고의 팀워크였고, 결과물 또한 너무 마음에 들어 힘들었지만 행복하고 뿌듯했던 2주였다 ❤️

profile
개발 공부 중 〰️ ٩(๑•̀o•́๑)و ✨

0개의 댓글