모립 (morib)
Saas 기반 생산성 웹 애플리케이션
사용자가 할일을 생성·관리하고, 할일을 선택한 뒤 타이머를 실행해 서로의 작업 집중 시간을 공유하는 생산성 도구입니다.

사용 기술 React, TypeScript, Framer Motion, MSW Tailwind CSS, Electron
1년 가까이 이어진 프로젝트로, UX 개선·유지보수를 진행했습니다. 사용성 테스트와 Hotjar 분석으로 사용자 진입 플로우를 재설계하는 데이터 기반 UX 개선을 진행했고, 번들 분석으로 초기 로딩 병목을 찾아 코드 스플리팅과 Preloading으로 풀어냈습니다. 서버 작업이 지연되는 동안에는 MSW 기반 API 모킹 환경을 구축해 클라이언트 작업을 멈추지 않고 진행했고, 랜딩페이지를 전담 제작해 Framer Motion 기반 스크롤 위치 추적 인터랙션도 구현했습니다. 타이머 provider 영역을 축소해 타이머 페이지 리렌더링 범위를 줄이는 등, 사용자 흐름에 직접 영향을 주는 레거시 코드도 우선적으로 정리했습니다.
사용성 테스트 기반 진입 플로우·UX 라이팅 재정비
문제 상황 "할 일을 추가하고 타이머를 시작한다"는 핵심 미션의 사용성 테스트 결과, 미션 성공률 자체는 높았고 타이머 진입 이후 사용자 흐름은 명확했지만, 타이머에 도달하기까지 5단계의 흐름을 거치며 필요 이상의 시간이 소모되고 있었습니다. 특히 홈 화면에서 타이머 진입 CTA를 찾지 못해 헤매는 사용자가 많아, 진입 전 단계의 오 클릭률도 함께 높게 측정됐습니다. Hotjar로 사용자 행동을 분석해보니, 온보딩과 홈에 흩어져 있는 "서비스 그룹 생성"·"할 일 생성" 단계가 핵심 진입을 가로막는 병목이었고, 내부 브랜드 용어였던 "모립세트"가 처음 사용하는 사용자에게 기능을 직관적으로 전달하지 못하는 문제도 함께 드러났습니다. 해결 진입 플로우 재설계 온보딩·홈에 분리돼 있던 서비스 등록과 할 일 등록 액션을 타이머 내부 Tab으로 편입해, 사용자가 타이머 바깥을 거치지 않고도 같은 컨텍스트 안에서 등록·선택까지 모두 처리할 수 있도록 플로우를 재설계했습니다. 진입 단계를 5단계 → 2단계로 단축했습니다. - AS-IS (5단계): 온보딩 → 홈에서 할 일 생성 → 오늘의 할 일 선택 → 서비스 그룹 선택 → 타이머 시작 - TO-BE (2단계): 온보딩에서 서비스 그룹 생성 → 타이머 Tab에서 할 일·서비스 선택 UX 라이팅 개선 의미가 잘 전달되지 않던 "모립세트"라는 표현을 단어만 보고도 기능을 짐작할 수 있는 "허용서비스"로 바꿨습니다. 브랜드 톤보다 첫 사용자의 이해 속도를 우선해, 별도 설명 없이도 흐름을 따라갈 수 있도록 다듬었습니다. 성과 다시 진행한 사용성 테스트에서 진입 평균 시간이 47.3s → 28.4s로 약 40% 단축됐고, 사용성 점수도 72점 → 89점으로 17점 상승했습니다.

코드 스플리팅과 onMouseEnter Preloading으로 초기 번들 경량화
문제 상황
React SPA 특성상 사용하지 않는 컴포넌트까지 초기에 한 번에 불러와 첫 화면 렌더링이 지연되고 있었습니다. 번들을 분석해보니 두 곳에서 초기 진입에 불필요한 용량이 함께 묶이고 있었습니다.
- LoginPage의 Lottie 애니메이션: Login 페이지에서만 쓰이는 Lottie가 Home 번들에도 포함돼, 첫 화면 진입까지 무거운 의존성을 끌고 들어왔습니다.
- HomePage의 Calendar(DatePicker) 컴포넌트: BoxCategory와 ModalAddCategory에서 카테고리를 추가할 때만 쓰이는데도, HomePage 진입 시점에 함께 로드되고 있었습니다.
해결
두 경우 모두 React.lazy로 분리하고 Suspense로 감싸 사용 시점에만 청크가 로드되도록 코드 스플리팅했습니다. 다만 lazy만 적용하면 사용자가 액션을 취한 순간에 로딩이 시작돼, 클릭하고 나서 화면이 뜨기까지 짧은 빈 시간이 생기는 문제가 있었습니다.
이를 막기 위해 onMouseEnter 시점에 다음에 쓸 청크를 미리 prefetch하는 Preloading을 함께 적용했습니다.
- LoginPage: 로그인 버튼에 마우스가 올라오면 HomePage 청크를 prefetch해, 로그인 직후 라우트 전환이 지연 없이 이어지도록 처리했습니다.
- Calendar: + 버튼과 날짜 토글에 마우스가 올라오면 Calendar 청크를 선반입하도록 하고, 두 컴포넌트가 공통으로 쓰는 로직은 usePreloadCalendar 훅으로 분리해 재사용성을 확보했습니다.
export const usePreloadCalendar = () => {
const [isCalendarLoaded, setIsCalendarLoaded] = useState(false);
const preloadCalendarComponent = useCallback(() => {
if (!isCalendarLoaded) {
import('@/shared/components/Calendar').then(() => {
setIsCalendarLoaded(true);
});
}
}, [isCalendarLoaded]);
return { isCalendarLoaded, preloadCalendarComponent };
};성과 Login·Home 페이지 초기 번들에서 무거운 의존성이 빠져 첫 화면 렌더링이 가벼워졌고, 사용자가 실제로 액션을 취하는 시점에는 prefetch가 이미 끝나 있어 클릭 → 표시 사이 지연 없이 자연스럽게 이어집니다.
MSW 기반 API 모킹 환경 구축으로 서버·클라이언트 작업 병렬화
문제 상황
서버 파트 작업이 지연되면서 클라이언트 측 페이지·UI 작업이 함께 멈춰 있었습니다. 응답 스펙은 정의돼 있었지만 실제 API 응답 없이는 데이터 흐름이 끊겨, 페이지 연동·로딩 처리·에러 케이스를 미리 검증할 수 없었습니다.
해결
MSW(Mock Service Worker)를 도입해 실제 네트워크 요청을 가로채는 모킹 환경을 구축했습니다. 페이지에서 사용되는 API 엔드포인트마다 핸들러와 응답 타입을 정의해 실제 API와 동일한 인터페이스로 동작하도록 만들었고, 페이지와 직접 연동해 서버 작업이 끝나기 전부터 데이터 흐름을 검증할 수 있게 했습니다.
트러블 슈팅
핸들러에서 request.body를 그대로 읽었더니 응답 값을 확인할 수 없는 이슈가 있었습니다. fetch 스타일의 Request 객체에서 body가 ReadableStream 형태로 제공되어 RAW 상태에서는 JSON으로 바로 접근할 수 없었고, request.json() 메서드로 스트림을 파싱해 해결했습니다.
성과
서버 응답을 기다리지 않고 클라이언트 단독으로 페이지·로딩·에러 흐름을 작업할 수 있게 됐고, 실제 API 연결 시점에는 핸들러 인터페이스를 그대로 따라가면 돼서 통합 비용도 줄었습니다.