예약 프로젝트

aydennote·2023년 9월 21일
0

Project

목록 보기
8/8

1. boder, outline, box-shadow

  1. 문제 :
  • 아이템 클릭 시 border가 추가되어 요소들이 움직이는 상황 발생.

해결 :

  • boder와 outline 에는 아래와 같은 차이가 있음.
  • border 같은 경우, 공간을 차지하며 radius 등으로 스타일링이 가능함.
  • outline 의 경우, 공간을 차지하지 않으며 스타일링 추가 불가능함.
  • 아래 블로그를 참고하여 해결.
  • 출처 : 블로그

  1. 문제 :
  • 위 문제와 동일한 문제가 발생
  • outline으로 요소들이 움직이는 현상은 해결을 했지만 outline으로 할 경우, 부모 요소 밖으로 선들이 잘리는 문제 발생.

해결 :

  • box-shadowinset을 활용하여 선들이 잘리는 문제와 요소가 움직이는 문제 해결.
  • inset은 내부 그림자 효과로 요소의 크기 변화 없이 테두리와 유사한 효과가 가능함.
    box-shadow: 'inset 0 0 0 1px #c3bcb7';

2. 예약 프로세스 재설계

예약 페이지 한 페이지에서 인원 메뉴 날짜 담당자 선택을 하도록 처음에 구현되어 있었다.
이렇게 되면 한 페이지에서 발생하는 상호작용(사용자 <-> 프론트 <-> 서버)이 많고 내용이 많아 모바일에서 사용할 때, 스크롤 액션 횟수가 증가되는 ux 저하 요인이라고 판단되어 한 페이지를 스텝별로 분리하여 구현하도록 디자인 설계를 다시 진행했다.

  • 예약하기 한 페이지에 많은 인터랙션이 집중되어 있어 프로젝트 설계에 대한 의문점 제시함.
  • 날짜, 시간, 매니저, 메뉴 등 많은 데이터들이 서로 연관되어 있고 사용자 클릭 시 데이터가 변동되기 때문에 한 페이지에 몰려있는 상황은 사용자 경험을 헤칠 우려가 있음.
  • 스텝별로 예약을 진행하는 것이 좋아보임.
  1. 날짜, 시간
  2. 코스 선택 또는 매니저 선택
  • 나의 의견이 합리적이다라는 판단으로 프로젝트 설계 변경.

3. 아이템 삭제 사용자 경험

  • 웹앱 페이지인 상황에서 위와 같이 삭제 버튼이 상단 구석에 작게 있을 때, 사용자 경험을 고려하여 아래 두 가지 방식을 고려했음.
  1. 사용자가 'X' 버튼을 터치해서 삭제
  2. 삭제할 하나의 아이템 요소를 터치해서 삭제
  • 1로 구현했을 때, 페이지가 웹앱인 점과 기본적인 요소 사이즈를 고려했을 때 'x' 버튼이 작아 사용자가 한 번에 터치하기 힘들 수 있다라는 의견
  • 2로 구현했을 때, 반대로 사용자가 실수로 요소를 터치해 해당 요소가 삭제될 수도 있다라는 의견
  • 내 의견은 2 였고 결국 위와 같은 이유로 1방식으로 개발이 진행되었음.
  • 다시 생각해 보면 2 방식으로 구현했을 때, 사용자 실수로 요소가 사라지면 다시 추가하는 프로세스를 사용자가 진행해야되는 번거로움이 있는데 1번 방식으로 구현하게 되어 사용자가 'x'를 한 번에 터치 못 하더라도 사용자가 저 주변을 몇 번 터치하면 쉽게 될 것 같기도 함.
  • 하지만, 둘 다 사용자 경험으로 봤을 때 괜찮은 방법은 아닌 듯 하여 다음 프로젝트에서 참고할 수 있도록좀 더 고민하는 게 좋겠음.

4. 이미지 최적화

서버에 저장하는 이미지 용량이 커 서버에 정한 제한 용량을 벗어난 이슈가 있었다. 해당 문제로 API 호출이 거부되는 문제가 발생했는데 서버 용량에서 많이 차지는 부분인 이미지를 최적화하는 것으로 서버 용량을 줄이기로 했다. browser-image-compression 라이브러리를 사용해 서버로 올리는 이미지 사이즈를 약 14.4%를 줄였고 서버 부하가 줄었다.

5. 불필요한 API 호출

문제 :

  • 예약 가능 시간을 조회 할 때, 날짜 초기값이 매달 1일로 정해져있어 매장 또는 매니저 휴무가 1일이라면 예약 가능 시간 조회 API 호출시 오류 발생.

해결 :

  • 에러 발생 이후 프론트에서 매장, 매니저 휴무를 계산해 가능한 제일 빠른 날짜로 초기화하게 되는데 처음부터 휴무가 아닐 때만 API 호출하도록 로직을 개선해 불필요한 API 호출 감소.

6. 모노레포

도입 이유 :

  • useradmin으로 도메인을 분리하여 관심사 분리 필요성 향상.

도입 효과 :

  • 빌드, 배포 시간 단축. (12초에서 각 도메인 별 7초)
  • 도메인 별 코드 구조 파악 용이.(컴포넌트 관리 용이)
  • 기존 user <-> admin 간 토큰 관련 에러 해결.(로컬스토리지의 경우, 도메인 별 토큰을 관리할 수 있음.)

7. 컴포넌트 설계

atomic design pattern으로 컴포넌트를 설계하고 있는데, 현재까지 특정 페이지에서만 사용하는 부분을 컴포넌트화 시켜야 되는지 고민이있었다. 컴포넌트화 시키지 않으면 해당 페이지에 코드가 많아져 가독성과 유지보수성이 떨어지고 컴포넌트화 시키면 재사용되지 않을 부분을 컴포넌트화 시키는 의미가 있을까? 생각했다.

결국, 컴포넌트화 시키는 가장 큰 이유는 반복적인 코드를 컴포넌트화 시켜 코드 중복을 줄이는 것에 있지만, 가독성과 유지보수성을 위한 것도 있기 때문에 당장 재사용성이 낮은 코드를 컴포넌트로 분리하는 작업이 의미없는 작업은 아니며 추후 프로젝트 확장성을 고려했을 때 컴포넌트화 시키는 것이 좋을 것 같다. 라는 결론을 내렸다.

8. 예약 시간 버튼 사용자 경험 향상

우리 예약 시스템 중에 날짜와 시간 선택은 한 페이지에 이루어지며,
fullCalendar 달력 날짜를 클릭하면 아래 예약이 가능한 시간 버튼들이 줄지어 나오는 형태이다.

여기서 날짜를 클릭할 때마다 아래 시간들에 대한 데이터를 API로부터 받아오기 전까지 데이터가 없는 것으로 판단해 아무것도 안 보이다가 데이터가 받아 왔을 때, 시간 버튼들이 나오면서 사용자로 하여금 화면이 깜빡이는 듯한 느낌을 받게 되어있었다.

내 해결 방법은 새로운 데이터를 받아오긴 전까지는(로딩 중일 때는) 이전 데이터를 보여주는 방식이다. 즉, 사용자는 데이터가 없을 때를 볼 수 없는 방식으로 해결했다.

 // 데이터가 로드되면 시간 옵션을 업데이트
  useEffect(() => {
    if (!isLoading && timeData) {
      setTimeOptions(timeData.data);
    }
  }, [timeData, isLoading]);

위 코드는 해결에 필요한 핵심 코드이다.
실제 timeOptions 상태 변수에는 가능한 시간의 배열 정보가 들어가며, timeData는 Query 데이터로 API로부터 받은 가능한 시간 배열의 정보이다. 그리고, 실제로는 timeOptions 변수에 있는 데이터를 기반으로 화면에 그려준다.
이전에는 timeData는 Query 데이터로 API로부터 받은 가능한 시간 배열의 정보를 그대로 화면에 그려주었다.
처음에는 리액트 쿼리 캐시, React.memo, useCallback를 이용해서 해결해보려고 했으나, 한 번 해당 날짜에 대한 시간이 받아와지면 깜빡임이 해결되었지만 새로운 날짜에 대해서는 여전히 깜빡이는 문제가 있었다.

개선 전

개선 후

9. 리팩토링 및 개선사항

9-1 컴포넌트 분리

기존에 체계가 없던 컴포넌트 디자인 패턴에서 atomic 디자인 패턴으로 컴포넌트 설계 진행.
변경 전 31,714 코드 라인에서 변경 후 27,683 코드 라인.(약 12.71% 코드 라인 감소)
전체를 아토믹 디자인으로 바꾸는데 어려웠던 원인
1. 처음부터 아토믹 구조를 생각하고 설계를 진행.

  • 컴포넌트 큰틀 먼저 잡고 조금씩 나눠서 컴포넌트 설계하지 않고 처음부터 작은 단위를 생각하며 컴포넌트를 설계하다 보니 어려움 발생.
  • 동료와 컴포넌트에 대해 충분한 대화가 이뤄지지 않음.

9-2 스켈레톤 디자인

스켈레톤 디자인 적용에 대해서는 프로젝트 중반부쯤에 내가 이야기를 했었다. 그러나, 코드량 증가와 이에따른 코드 가독성이 떨어질 수 있다는 부분에서 내가 이야기를 꺼냈지만 다시 주워 담았다. 이후 프로젝트 후반부 컴포넌트 분리 작업과 모노레포를 적용하면서 전체적인 가독성 향상과 코드량 감소로 인해 모노레포를 적용해도 크게 문제되지 않겠다라는 판단이 있었고, 실제 프로젝트 실행 환경은 해외이기 때문에 네트워크 속도를 생각하면 데이터를 받아 화면에 그려주는 동안 스켈레톤 디자인을 적용하는 것은 사용자 경험에 있어 크게 기여할 수 있는 부분이라 생각했기 때문에 번복에 번복에 번복을 하여 적용하게 되었다.

9-3 빌드

기존 빌드 방식은 Webpack 빌드 모드 설정을 하지 않은 방식으로 빌드 속도는 빠르지만 번들링 파일의 크기는 비교적 큰편이었다. 개발 환경에서는 빠른 업데이트와 작업 환경(디버깅 등)을 위해 해당 빌드 모드를 사용했지만, 배포 환경에서는 빌드 속도보다 번들링 파일 크기가 더 중요하다고 생각되어 배포 모드로 빌드하여 배포하게 되었다.

변경 전

변경 후

위 사진과 같이 3.3MB에서 442kb로(약 86%) 번들링 파일 사이즈 감소가 있었고 이에따라 페이지 전체 렌더링 속도도 1.15s에서 290ms로(약 75%) 렌더링 속도 또한 향상되었다.

9-4 보안

  1. CSP 보안 설정
    특정 도메인만 허용하도록 html meta 태그에 입력.
    이번 프로젝트의 경우, 필요한 특정한 도메인만 허용시켰다.
<meta
      http-equiv="Content-Security-Policy"
      content="허용할 도메인과 옵션"
    />
  1. 사용한 다양한 XSS 공격 예방
    input 태그에 들어갈 수 있는 스크립트, 이미지 태그, 링크, MathML 등 다양한 방식의 XSS 공격을 예방.

9-5 상태 관리

우리가 Context API를 사용한 이유는 최초 프로젝트 설계 시 소규모 프로젝트였고 동료 개발자가 다른 상태 관리 라이브러리를 사용해 본 경험이 없기 때문에 빠른 개발을 위해 Context API를 채택했다.

상태 관리 라이브러리로 변경하는 이유는 프로젝트가 규모가 커지면서 전역 상태 관리 데이터가 많아지고 복잡해지면서 Context API의 성능 한계에 직면했으며 Context API는 상태 관리 라이브러리가 아니라 점과 서로 다른 관심사의 데이터가 변경될 때 전체 하위 컴포넌트가 리렌더링되는 성능적 문제가 있다는 것이다. 이로 인해 성능이 저하되고 효율적인 상태 관리가 어려워지고 있음에 따라 Context API를 대체할 상태 관리 솔루션이 필요하다고 생각했다.

Zustand로 변경하는 이유는 상태 관리에 필요한 코드가 간단한 하고 작은 크기이며, 기존에 사용하고 있던 React-Query와 궁합이 좋고 함수 안에서도 호출이 가능하다는 부분에 있어서 장점이라고 생각하기 때문이다.

profile
기록하는 개발자 Ayden 입니다.

0개의 댓글