[Pre Onboarding] 2주차 회고록

Jung taeWoong·2021년 8월 8일
2

개발일기

목록 보기
3/3

2주차 과제

2주 차 과제는 jaranda 기업의 회원가입, 로그인/로그아웃, 관리자 페이지를 구현하는 과제였다.
이번 과제는 분량이 많아서인지 팀원 전체 협업과제에다가 +2일의 추가시간이 주어졌다.
처음에는 7명이서 협업을 하는 것에 어려움이 많을 것이라 생각했지만 매일 팀원들과 각자의 작업 현황을 공유하고 커뮤니케이션을 통해 개발 진행 상황을 정리하여 수월하게 프로젝트를 진행할 수 있었던 것 같다.

로그인 구현

프로젝트 내에서 로그인 기능을 맡았는데 로그인 관련 로직을 한곳에서 파악하고 관리할 수 있도록 class로 설계했다.
실제 로그인은 서버의 데이터베이스에서 데이터를 조회하고 api를 통해 클라이언트에게 결과 값을 넘겨주는 형식이다.
특히 login() 메서드는 서버와 클라이언트가 통신하는 과정을 생각하며 비슷하게 로직을 구현을 해보았다.

// Auth.js
  async login(loginData, isAdminRestrict = false) {
    this.userList = this.userListStorage.load()
    
    const database = isAdminRestrict
      ? this.userList.filter((account) => account.auth === authType.ADMIN.name)
      : this.userList
    const account = database.find((account) => account.email === loginData.id)
    const isRegisteredAccount = database.some(
      (account) => account.email === loginData.id
    )
    const isPasswordMatch =
      isRegisteredAccount && account.password === loginData.pw
    if (!isRegisteredAccount) {
      throw new CustomError(errorState.NO_ACCOUNT_REGISTERED)
    } else if (!isPasswordMatch) {
      throw new CustomError(errorState.PASSWORD_MISMATCH)
    } else {
      const protectedAccountInfo = {
        loginTime: new Date().getTime(),
        name: account.name,
        access: account.access,
        auth: account.auth,
        id: account.id,
        email: account.email,
      }
      this.currentAccountStorage.save(protectedAccountInfo)
      this.auth = protectedAccountInfo
      return protectedAccountInfo
    }
  }
   

toast popup 구현

지금까지 토스트는 팝업 메시지는 toastify 라이브러리 를 사용하였는데
기업 과제는 웬만하면 라이브러리를 사용하지 않는 쪽으로 진행하기로 하여서 직접 구현을 해보기로 했다.
생각보다 어렵지 않아서 놀랐고 무작정 라이브러리만 찾던 나를 반성하게 되었다

// Toast.js
import React, { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import styled from 'styled-components/macro'

export default function Toast({ message, isShow, className }) {
  const [show, setShow] = useState(null)

  useEffect(() => {
    if (!isShow) {
      setTimeout(() => {
        setShow(false)
      }, [200])
    } else {
      setShow(isShow)
    }
  }, [isShow])

  return (
    <>
      {show &&
        createPortal(
          <Wrapper
            role="alertdialog"
            aria-live="assertive"
            isShow={isShow ? 1 : 0}
            className={className}
          >
            <Message>{message}</Message>
          </Wrapper>,
          document.body
        )}
    </>
  )
}

const Wrapper = styled.div.attrs(({ isShow }) => ({
  opacity: isShow,
}))`
  position: fixed;
  top: 15rem;
  left: 50%;
  border-radius: 1.5rem;
  padding: 0.5rem 1rem;
  background: rgba(87, 87, 87, 0.9);
  transform: translateX(-50%);
  opacity: ${({ opacity }) => opacity};
  transition: opacity linear 0.2s;
  z-index: 10000;
`
const Message = styled.span`
  font-size: 1.4rem;
  color: rgba(255, 255, 255, 0.9);
`

토스트 컴포넌트에서 isShow props로 virtual DOM에 탈부착 되는 형식으로 구현을 하고서 토스트 컴포넌트가 사라질 때 스르륵 사라지기 위해 transition을 추가하였는데 스르륵 사라지기 전에 이미 DOM에 떨어져 나간 뒤였다.

그래서 토스트 컴포넌트에서 show라는 상태를 만들고 렌더링 조건문을 show 상태로 지정하고서 isShow props가 false가 되면 setTimeout에 임의의 시간을 주고 show 값을 false로 바꿔주는 사이드이펙트를 추가하였다.

지금 와서 생각해 보면 isShow, show 상태 값과 프룹스값 이름이 겹치는 것도 그렇고 setTimeout에 임의의 시간을 지정한 것도 그렇고 문제가 많은 코드인 것 같다.
1. 상태 값을 [show, setShow] => [render, setRender] 이름 변경
2. setTimeout => transitionend event
이렇게 코드를 작성했으면 더 좋았을 것 같다.

protected Routing

ProtectedRoute 컴포넌트는 이름 그대로 component 속성으로 전달 받은 컴포넌트를 래핑하여 보호합니다. render 함수를 활용해 감싼 컴포넌트를 렌더링 합니다.

import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import auth from './auth';

const ProtectedRoute = ({component: Component, ...rest}) => (
  // 인증 없이 접근 못하도록 구현
  <Route {...rest} render={
    (props) => auth.getAuth ? 
                <Component {...props} /> : 
                <Redirect to={{
                  pathname: '/', 
                  state: { from: props.location }
                }}/>
  }>
);

export default ProtectedRoute;

이번에 protected Routing을 구현해보면서 Route 컴포넌트 렌더링 방법으로 component 속성을 사용하는 방법 외에 다양한 방법이 더 있다는 걸 배웠다.

렌더링 방법설명
<Route component>URL과 매칭되는 컴포너트를 렌더링 합니다.
<Route render> 함수인라인 렌더링 또는 래핑 렌더링 시에 사용합니다.
<Route children> 함수매칭될 경우, 자식 컴포넌트를 렌더링 할 때 사용합니다.
// 인라인 렌더링
<Route 
  path="/" 
  exact 
  render={() => <div className="Home"></div>} 
/>

// 래핑 렌더링
const FadeInRoute = (({component: Component, ...rest}) => {
  return (
    <Route 
      {...rest}
      render={(routeProps) => (
        <FadeIn>
          <Component {...routeProps} />
        </FadeIn>
      )}
    />
  )
}

<FadeInRoute path="/lab" component={Lab} />

📌 읽어볼것
리액트 라우터 component vs render 차이

아쉬웠던점 or 느낀점

컨벤션

  1. 디렉토리, 파일 명
    이번 프로젝트는 모든 디렉토리 명과 파일 명을 PascalCase로 작성하기로 했다.
    개인적으로는 보기에도 별로고 사용하기에도 불편했던 것 같다.
    폴더명은 소문자로, 파일명은 컴포넌트 파일은 PascalCase,그 외 js 파일은 소문자로 구분 지어서 했으면 더 좋았을 것 같다.
  2. jsx 와 js 분리
    컴포넌트와 유틸 js 파일 모두 확장자가. js라 구분되지가 않았다.
    컴포넌트에는 jsx로 주어 구분을 해줬으면 더 좋았을 것 같다. (React icon은 덤++)
profile
Front-End 😲

0개의 댓글