1차 공부/WIL

항해 10주차 실전프로젝트 (5)

공대탈출 2023. 2. 5. 23:45

프로젝트가 거의 다 끝나간다.

어짜피 다음주에 정리를 해야하지만 먼저 간단하게 어떤라이브러리를 왜 사용했는지 정리를 해보려고한다.

 

1. @emotion/styled

기존 리액트를 배울 때 styled-components를 사용했었다.

둘의 스타일 기능은 전반적으로 동일하다. props사용, 미디어쿼리, 글로벌 스타일, nested selectors, SSR 등...

둘다 sass문법을 사용하기에 스타일 문법에도 차이가 없다.

 

성능상 둘의 유의미한 차이는 없지만 emotion의 퍼포먼스가 전반적으로 좋게 나온다하여 선택하였다.

 

러닝커브가 높다는 단점이 있었지만, 리액트를 공부하면서 계속 사용해왔어서 어찌보면 지금은 css보다 익숙해져있어 선택하게 되었다.

 

2. redux / redux/toolkit

 

처음에는 사용할 일이 없었다. 컴포넌트 단에서 axios호출을 해서 state에 저장해서 보여주는 방식으로 진행하여

딱히 중앙 상태관리를 해야할 필요가 없었다.

일단 사용할 때가 있겠다 싶어서 설치는 해뒀었고, 서버와 통신을 진행하고, 무한스크롤을 사용할 때나 찜하기를 누를 때 사용하게 되었다.

 

상태관리를 따로 사용하지않고 하다보니 리렌더링이 일어나지않았고, 해당 부분을 중앙 store에 저장하고 계속 가져오면서 리렌더링이 되도록 하였다.

 

처음 프로젝트를 시작할 때 recoil을 사용해 볼까 싶었지만, typescript를 사용하기로 정하면서 익숙해지고 사용하는데 너무 어려움을 겪을 것 같아 redux를 사용하게되었다.

 

또, redux devtools를 사용해서 상태값의 변경사항을 직관적으로 볼 수 있으므로 좋다고 생각했다.

redux는 분명하게 코드의 양이 많아진다. toolkit을 사용하더라도 코드량이 많이 증가하는 단점이 있다.

하지만 프로젝트의 기간이 짧은데, 3명이었던 FE가 2명으로 변하게되면서 새로운걸 더 하기에는 어려움이 있겠다 싶어서 redux를 사용하게 되었다.

 

3. typescript

프로젝트를 진행하기 전까지 typescript는 타입을 미리 지정해 사전에 버그를 방지한다. 라는 장점이 너무 좋게 보였다.

그래서 꼭 사용하고 싶었고, 사용하게 되었다.

 

처음에는 단점을 딱히 생각하지 못했다. 타입지정하는 것도 생각보다 쉬워보였고, 할만할 줄 알았다.

그런데 아니었다. 내 생각대로 타입을 지정하고 코드를 짜다보면 코드에는 빨간줄이 보이곤했다. 도대체 뭐가문젠지 싶어 마우스를 갖다댔을때 해결된 문제도있었지만, 해결하지못하고 구글링을하고, 여기저기 찾아봤는데도 해결되지 않을 때도있었다. 그럴때는 any를 사용하였는데, 이는 리팩토링을 진행할 때 타입을 지정해줘야 할 것같다.

 

또한 이번프로젝트를 진행할 때 Input이라는 공통 컴포넌트를 사용했다.

해당 컴포넌트는 팀원분이 js를 사용하여 만들었다. 이유는 딱히 묻지 않았지만 사용하면서 느꼈던 점은 확실히 편하다는 것이다.

 

//라이브러리
import styled from '@emotion/styled';
import { ReactNode } from 'react';

interface ButtonProps {
  children: ReactNode;
  width?: string;
  height?: string;
  bgColor?: string;
  borderRadius?: string;
  fontSize?: string;
  fontWeight?: string;
  onClick?: any;
  display?: any;
  margin?: string;
  fontFamily?: string;
  color?: string;
  hColor?: string;
  mwidth?: string;
  gap?: string;
  hBgColor?: string;
  onMouseOver?: any;
  onMouseOut?: any;
}

const Button = ({
  children,
  width,
  height,
  bgColor,
  borderRadius,
  fontSize,
  onClick,
  fontWeight,
  display,
  margin,
  fontFamily,
  color,
  hColor,
  mwidth,
  gap,
  hBgColor,
  onMouseOver,
  onMouseOut,
}: ButtonProps) => {
  return (
    <Btn
      width={width}
      height={height}
      bgColor={bgColor}
      borderRadius={borderRadius}
      fontSize={fontSize}
      fontWeight={fontWeight}
      onClick={() => onClick()}
      display={display}
      margin={margin}
      fontFamily={fontFamily}
      color={color}
      hColor={hColor}
      mwidth={mwidth}
      gap={gap}
      hBgColor={hBgColor}
      onMouseOver={() => onMouseOver()}
      onMouseOut={() => onMouseOut()}
    >
      {children}
    </Btn>
  );
};

const Btn = styled.button<ButtonProps>`
  margin: ${({ margin }) => (margin ? margin : '10px')};
  border: none;
  cursor: pointer;
  user-select: none;
  width: ${({ width }) => (width ? width : '100px')};
  height: ${({ height }) => (height ? height : '30px')};
  border-radius: ${({ borderRadius }) =>
    borderRadius ? borderRadius : '10px'};
  background-color: ${({ bgColor }) => (bgColor ? bgColor : 'lightgray')};
  font-size: ${({ fontSize }) => (fontSize ? fontSize : '15px')};
  font-weight: ${({ fontWeight }) => (fontWeight ? fontWeight : 'normal')};
  font-family: ${({ fontFamily }) => (fontFamily ? fontFamily : 'NanumSquare')};
  color: ${({ color }) => (color ? color : 'black')};
  display: ${({ display }) => (display ? display : 'inline-block')};
  align-items: center;
  justify-content: center;
  gap: ${({ gap }) => gap};
  &:hover {
    filter: brightness(95%);
    color: ${({ hColor }) => (hColor ? hColor : 'black')};
    background-color: ${({ hBgColor }) => hBgColor};
  }
  @media (max-width: 1200px) {
    width: ${({ mwidth }) => mwidth};
    padding: 0px;
  }
`;

export default Button;
//라이브러리
import styled from '@emotion/styled';

const Input = ({ width, height = '26px', ...props }) => {
  return <StInput width={width} height={height} {...props} />;
};

const StInput = styled.input`
  width: ${props => props.width};
  height: ${props => props.height};
  border: ${props => (props.border ? props.border : '1px solid #dadada')};
  border-radius: ${props => (props.borderRadius ? props.borderRadius : '4px')};
  padding: 3px 0 3px 20px;
  min-width: 150px;
  font-size: ${props => (props.fontSize ? props.fontSize : '12px')};
  background-color: ${props => props.bgColor};
  ::placeholder {
    font-size: 20px;
  }
  :focus {
    outline: ${props => props.outline};
  }
  @media (max-width: 1200px) {
    width: 80%;
    height: 70%;
    font-size: 16px;
    ::placeholder {
      font-size: 16px;
    }
  }
`;

export default Input;

Input 컴포넌트에서는 ...props를 사용하여 input태그의 readOnly라던지, placeholder 같은 지정해줘야할 것들을 쉽게 지정할 수 있었다.

 

분명하게 typescript가 타입을 미리 지정해서 버그를 사전에 방지할 수는 있지만, 코드의 양이 증가하고, 그만큼 시간을 사용해야 한다는 단점을 느꼈다.

 

만약 취직을 하게되거나, 다음 프로젝트를 진행할 때 typescript를 사용할 것이냐? 라고 물어보면 당연히 yes이다.

버그를 잡아낸다는 장점이 확실하게 느껴졌기 때문이다.

 

4. axios

axios는 미리 정리해둔 글이 있어 그곳을 참고하자.

https://codingpracticenote.tistory.com/160

5. dayjs

서버에서 Date형식으로 보내달라기에 그냥 2000-00-00T00-00:00:00 이런식으로 new Date()한 그대로 보내주었다.

처음에는 에러가 없었지만 나중에 백엔드에서 버그를 잡아내어 특정 포맷으로 변경해서 보내달라는 요청이 있었다.

 

그래서 처음에는 moment를 사용하였다. 일단 처음 접한 라이브러리여서 사용하기 쉽겠다 생각했었다.

단점을 조사하던 중 번들의 크기가 크다는 것을 알게되었고, 이를 멘토링 시간에도 지적받아 dayjs로 바꾸게되었다.

막상 dayjs를 사용해보니 오히려 더 편하다고 생각이들었다.

 

6. react-datepicker

캠핑장 예약 시스템을 만들어야하다보니, 날짜를 선택하는 라이브러리가 필요했다.

react-datepicker은 범용적으로 사용되어 레퍼런스가 많았고, 커스텀 스타일이 가능하다는 점이 좋게 다가왔다.

 

두번째로 react-calendar은 default로 설정된 스타일이 괜찮았지만, 어짜피 프로젝트의 색상에 맞추어 바꿔야하기때문에 범용성이 높은 react-datepicker에 밀렸다.

 

세번째로 react-day-picker은 범용적이기도하고 커스텀기능도 다양하게 제공했다. 단점도 딱히 생각되지않았을 정도로 괜찮았다.

하지만 react-datepicker가 조금 더 다양한 커스텀기능을 제공하여 만약 날짜선택에 관해 뭔가 확장하거나, 다른 컴포넌트에서 사용할떄를 생각하여 첫번째 라이브러리로 선택했다.

 

7. react-ga

이건 선택지가 없었다. 구글 애널리틱스를 사용하여 사용자의 이벤트횟수와 페이지 유지시간, 이탈률을 비교하고 그에따라 문제점을 파악하기위해 사용했다.

8. react-router-dom

다양한 페이지를 보여줘야하고, 그 페이지마다 이동해야하며, 이동할 때 특정 데이터를 넘겨줘야할 때도 있어 사용하였다.

또한 a태그의 기능을 사용해야하는데 a태그는 클릭 시 페이지를 새로 불러오기때문에 사용하지 않는다.

따라서 Link컴포넌트를 사용하는데 생김새는 a태그를 사용하지만 History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어있다고 한다.

 

9. swiper

선택지는 두개였던것으로 기억한다.

AntDesign의 Carousel과 Swiper

그런데 AntDesign은 navigation혹은 swiping기능을 지원하지 않아서 swiper을 사용하게 되었다.

swiper은 최소한의 props로 네비게이션 구현이 가능하고, 사용자가 많아 레퍼런스가 엄청나게 많다.

또한 swiping기능이 있어 반응형도 생각해야하는 우리 프로젝트 특성상 모바일 환경에서도 좋을 것이라고 생각해서 선택하였다.

 

마치며, 실전프로젝트 전까지 항해를 진행하며 '이 라이브러리 깔아서 이렇게 이렇게 쓰세요~'했었지만, 직접 해당 라이브러리의 장단점과 더 많은 기능들을 찾아보진 않았다.

이번 실전프로젝트를 진행하면서 내가 쓰는 라이브러리의 경쟁라이브러리는 어떤게 있는지? 이 라이브러리의 장단점은 무엇인지? 어떻게 사용하는지? 를 직접 찾아보게되면서 기술적 의사결정이 굉장히 중요하다는 것을 알게되었다.

또한 라이브러리를 선택할 때는 npm install 횟수도 중요하겠지만, 라이브러리의 번들 크기도 고려애햐겠다는 것을 깨닫게 되었다.

 

월요일부터 남은 피드백을 거르고 적용할 점은 적용하고, 최종 발표를 위한 준비를 해야겠다.