예약 프로젝트

aydennote·2023년 9월 21일

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. 예약 프로세스 재설계

문제 :

  • 예약 페이지에서 인원, 메뉴, 날짜, 담당자 선택을 한 화면에서 처리하는 구조로 설계되어 있었음.
  • 이로 인해 한 페이지에서 발생하는 사용자 상호작용이 많아지고 특히, 모바일 환경에서는 긴 스크롤과 복잡한 입력 과정으로 인해 사용자 경험이 저하될 가능성이 있음.

원인 :

  • 예약 과정에서 선택해야 하는 날짜, 시간, 메뉴, 담당자 등의 데이터가 서로 연관되어 동적으로 변경되는 구조였기 때문에 한 화면에 모든 입력을 배치할 경우 사용자 인지 부담이 커지고 인터랙션이 과도하게 집중되는 문제가 있음.

해결 :

  • 이 문제를 해결하기 위해 기존 설계에 대한 개선 의견을 제시하고, 예약 과정을 단일 페이지 구조에서 단계별 진행 방식(퍼널)으로 재설계.
  • 사용자가 한 번에 처리해야 하는 정보량을 줄이기 위해 예약 과정을 다음과 같이 분리
    1. 날짜 및 시간 선택 2. 코스 또는 매니저 선택
  • 이를 통해 사용자가 한 단계씩 선택하며 자연스럽게 다음 행동으로 이어질 수 있는 구조로 개선

결과 :

  • 예약 과정에서 사용자가 처리해야 하는 정보량을 줄이고 화면 내 인터랙션을 분산시켜 모바일 환경에서도 보다 직관적인 예약 흐름을 제공

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


문제 :

  • 웹앱 환경에서 리스트 아이템을 삭제하는 기능을 구현할 때, 삭제 인터랙션 방식에 대한 사용자 경험 문제가 발생
  • 삭제 버튼이 아이템 우측 상단의 작은 X 버튼으로 배치되어 있어 모바일 환경에서 터치 정확도가 떨어질 가능성 발생

원인 :

  • 웹앱 특성상 화면 요소가 작고, X 버튼의 터치 영역이 제한적이기 때문에 사용자가 한 번에 정확히 터치하기 어려울 가능성
  • 반면, 아이템 자체를 터치하여 삭제하는 방식은 터치 정확도 문제는 줄일 수 있지만 사용자가 실수로 요소를 눌러 삭제되는 위험이 존재

해결 :

  • 두 가지 삭제 인터랙션 방식을 비교 검토
    1. 삭제 버튼(X)을 터치하여 삭제 2.아이템 자체를 터치하여 삭제
  • 아이템 터치 방식은 사용자의 터치 정확도 문제를 해결할 수 있지만 실수로 삭제될 경우 복구 과정이 필요해 사용자 부담이 커질 수 있다고 판단
  • 따라서 명시적인 의도를 기반으로 삭제가 이루어지는 방식(X 버튼)을 선택해 구현

결과

  • 어떤 방식으로 해도 이상하지 않고 틀리다고 생각하지 않음
  • 두 방식 모두 장단점이 존재해 사용자 경험측면을 더 고려하여 채택

4. 이미지 최적화

문제 :

  • 사용자가 업로드한 이미지의 용량이 커 서버에서 설정한 업로드 제한 용량을 초과하는 문제가 발생
  • 이로 인해 일부 이미지 업로드 요청이 API에서 거부되는 상황이 발생

원인 :

  • 이미지 파일이 원본 그대로 서버로 전송되면서 서버 저장 용량을 빠르게 소모했고, 서버에서 설정한 업로드 제한 용량을 초과하는 경우가 발생

해결 :

  • 서버 저장 용량과 업로드 실패 문제를 줄이기 위해 클라이언트에서 이미지 압축을 수행하는 방식을 적용
  • browser-image-compression 라이브러리를 사용하여 이미지 업로드 전에 파일을 압축하도록 구현

결과 :

  • 이미지 업로드 시 평균 약 14.4%의 용량 감소 효과
  • 서버 저장 용량 사용량을 줄이고 API 업로드 실패 문제를 완화

5. 불필요한 API 호출

문제 :

  • 예약 가능 시간을 조회하는 과정에서 초기 날짜가 매달 1일로 고정되어 있어 API 호출 오류 발생
  • 매장 또는 매니저의 휴무일이 1일인 경우, 예약 가능 시간 조회 API가 불필요하게 호출되고 오류가 발생하는 상황이 반복

원인 :

  • 초기 날짜를 단순히 매달 1일로 설정한 상태에서 휴무 여부를 확인하기 전에 예약 가능 시간 조회 API를 호출하는 구조였기 때문에 실제 예약이 불가능한 날짜에도 API 요청 발생

해결 :

  • 예약 가능 시간을 조회하기 전에 매장 및 매니저의 휴무일을 먼저 계산하도록 로직 수정
  • 휴무일이 아닌 가장 빠른 예약 가능 날짜를 계산한 뒤 API를 호출하도록 흐름을 변경해 불필요한 요청 방지

결과 :

  • 휴무일에 발생하던 불필요한 API 호출과 오류 요청을 제거하고 예약 가능 시간 조회 과정의 네트워크 요청 효율을 개선

6. 모노레포

도입 이유 :

  • 일반 사용자 서비스(user)와 관리자 서비스(admin)가 분리된 프로젝트로 운영되며 관리 복잡도 증가
  • 두 애플리케이션을 하나의 저장소에서 관리하기 위해 Monorepo 구조 도입

구현 방식 :

  • Lerna를 활용해 두 애플리케이션의 의존성과 스크립트를 통합 관리
  • 도메인(user / admin) 단위로 프로젝트 구조를 분리해 서비스 역할 명확화

도입 효과 :

  • 하나의 저장소에서 두 애플리케이션을 관리해 개발 및 배포 프로세스 단순화
  • 도메인 단위 구조로 코드 탐색성과 유지보수성 향상
  • user / admin 인증 로직을 분리해 토큰 충돌 문제 해결

7. 컴포넌트 설계

고민 :

  • 컴포넌트화를 하지 않으면 페이지 내부에 코드가 집중되어 가독성과 유지보수성이 떨어질 수 있었고, 반대로 컴포넌트로 분리할 경우 재사용 가능성이 낮은 코드까지 분리하는 것이 의미 있는지에 대한 의문

판단 기준 :

  • 컴포넌트화의 목적은 단순히 재사용성을 높이는 것뿐만 아니라
    코드 구조를 명확하게 만들고 가독성과 유지보수성을 개선하는 데에도 있다는 점을 기준으로 판단

결론 :

  • 따라서 현재 재사용성이 낮더라도 UI 역할 단위로 컴포넌트를 분리하는 방식이 구조적으로 더 적합하다고 판단
  • 이를 통해 코드의 책임을 명확히 하고 향후 기능 확장이나 UI 변경 시에도 유지보수와 확장이 용이한 구조를 유지할 수 있도록 설계

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


문제 :

  • 날짜를 클릭할 때마다 API 응답이 오기 전까지 데이터가 없는 상태가 되어 시간 버튼 영역이 잠시 비어 있다가 다시 나타나는 화면 깜빡임 현상이 발생
  • 이로 인해 사용자에게 화면이 불안정하게 보이는 UX 문제가 발생

원인 :

  • 시간 버튼을 렌더링할 때 React Query로 받아온 데이터를 직접 화면에 사용하고 있었기 때문에 새로운 날짜를 선택하면 데이터가 다시 로딩 상태가 되면서 기존 데이터가 사라졌다가 새로운 데이터가 나타나는 구조

해결 :

  • 새로운 데이터를 받아오기 전까지 이전 데이터를 유지하도록 상태 관리 방식을 변경
  • API 응답 데이터를 바로 렌더링하지 않고 timeOptions 상태에 저장해 관리하고, 로딩이 완료된 이후에만 상태를 업데이트하도록 구현
 // 데이터가 로드되면 시간 옵션을 업데이트
  useEffect(() => {
    if (!isLoading && timeData) {
      setTimeOptions(timeData.data);
    }
  }, [timeData, isLoading]);

결과 :

  • 날짜 선택 시 발생하던 시간 버튼 영역의 깜빡임 현상을 제거
  • 사용자가 데이터가 없는 상태를 보지 않도록 개선해 보다 안정적인 사용자 경험 제공

개선 전

개선 후

9. 리팩토링 및 개선사항

9-1 컴포넌트 분리

문제 :

  • 기존 프로젝트에서는 컴포넌트 설계에 대한 명확한 기준이 없음.
  • UI 컴포넌트 구조가 일관되지 않고 코드 복잡성 증가

해결 :

  • 컴포넌트 구조를 체계적으로 관리하기 위해 Atomic Design Pattern을 도입하여 컴포넌트 설계를 재구성
  • UI 요소를 Atom, Molecule, Template, Page 단위로 나누어 역할과 책임을 명확히 하고, 반복적으로 사용되는 UI 요소를 컴포넌트로 분리해 구조 정리

결과 :

  • 컴포넌트 구조를 정리하면서 중복 코드가 감소
  • 31,714줄 → 27,683줄로 약 12.71%의 코드 라인이 감소
  • 이를 통해 코드 가독성과 유지보수성 개선

9-2 스켈레톤 디자인

문제 :

  • 페이지 데이터가 API로부터 로딩되는 동안 화면에 아무 콘텐츠도 표시되지 않아 사용자가 로딩 상태를 인지하기 어려운 UX

원인 :

  • 프로젝트 중반부에 Skeleton UI 도입을 제안했지만, 스켈레톤 컴포넌트를 추가할 경우 코드량 증가와 가독성 저하 우려가 있어 적용을 보류
  • 또한 당시에는 컴포넌트 구조가 정리되지 않아 추가적인 UI 상태 관리가 코드 복잡도를 높일 수 있는 상황

해결 :

  • Skeleton UI를 다시 검토했고 실제 서비스 환경이 해외 사용자 대상이기 때문에 네트워크 지연 상황에서 로딩 상태를 명확히 보여주는 것이 사용자 경험에 중요하다고 판단
  • 결과적으로 데이터 로딩 동안 Skeleton UI를 표시하는 방식으로 구현해 로딩 상태를 사용자에게 명확하게 전달하도록 개선

결과 :

  • 데이터 로딩 시 화면이 비어 보이는 문제를 해결하고 사용자가 서비스가 정상적으로 동작하고 있다는 피드백을 받을 수 있도록 UX 개선

9-3 상태 관리

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

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

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

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

0개의 댓글