레몬베이스 (Lemonbase)
소속: 레몬베이스 팀 Lemonbase
레몬베이스는 개인과 조직의 성과와 몰입 관리를 지원하는 B2B HR SaaS입니다. 목표 관리, 리뷰, 원온원 미팅, 서베이, 피드백 등의 다양한 기능을 통해 구성원들이 성장하고 몰입할 수 있는 환경을 제공합니다.
주로 방치된 기술적 문제 해결과 모범 사례 제시를 통해 사용자 경험, 서비스 안정성, 엔지니어링 생산성 향상에 집중했습니다.
Tech Stack:
TypeScript, ts-belt, React, SWR, Tailwind CSS, Ant Design v4, Vitest, MSW, pnpm, Vite, ESLint, Prettier, husky, GitHub Actions
서비스 링크
기여
목표 도메인의 기능 구현·개선
- Wizard Form 형태의 목표 가중치 승인 기능 구현
- 충돌하거나 비어 있던 목표 가중치 승인 요구사항을 인수 기준(Acceptance Criteria)으로 구체화
- 목표 관리와 목표 공통 설정 디자인 개선, ‘목표 수립 기간’ 등의 이벤트별 예약 알림 설정 기능 구현
목표 테이블 렌더링 성능 최적화
- 목표 목록 구성 시 의도치 않은 분산·중복 연산과 부수 효과로 반복되던 구성원 조회를 배열 선형 탐색에서 ID 기반 객체 조회로 전환
- 구성원 정보 조회의 원천인 ‘구성원 목록’ 스토어의 데이터를 적절한 컴포넌트에서 소비해 전달하는 대신 모든 컴포넌트가 직접 참조하는 탓에, 목표 담당자 정보 표시, ‘내 팀원 목표’ 필터 등, 각 영역에서 구성원 정보의 중복 조회·탐색 발생
- 담당자·승인권자·담당자의 평가권자·담당 조직 리더 확인 등, 이미 서버에서 처리하는 권한별 목표 조회 및 마스킹을 클라이언트에서 중복 처리하느라 구성원 정보를 다시 조회
- 결국 목표와 구성원이 많아질수록 탐색 비용이 누적되어 렌더 사이클에 지속적으로 영향을 미침
- 프로파일링으로 Long Task의 병목을 특정하고, 일정 제약을 고려해 설계 교체 없이 시간 복잡도만 개선하는 작은 변경으로 문제를 빠르게 해결 시도
- 목표 테이블 렌더링 시간을 약 25초에서 1초로 단축 (API 응답 시간 포함 시 약 30초 → 5초)
- 테스트 환경: 30,000개의 목표, 15,000명의 구성원, 목표당 평균 담당자 1.5명
- 목표 필터 로직의 도메인 규칙 테스트 작성
- 대규모 목표 조회에 관한 고객사의 계약 갱신 조건에 대응
제품 레이아웃의 반응형 UI 전환
- 좁은 너비의 화면에서 깨지던 UI를 전수 조사, 브레이크포인트 정의
- 페이지 유형별 레이아웃 디자인을 컴포넌트로 제작하여 템플릿화, 반응형 디자인으로 전면 개선
디자인 시스템 기여 및 디자인 불일치 해소
- 색상, 여백, 브레이크포인트 등의 디자인 토큰 정의, 19개의 범용 컴포넌트 제작
- 사용 중인 컴포넌트 패키지(Ant Design v4)의 커스터마이징 기반 마련
- Tailwind CSS와 함께 사용할 수 있도록 Ant Design v4 스타일시트를 개별 import하여 레이어 내에 선언
- Ant Design v4 내부의 미흡한 디자인 토큰 설계 및 사용처를 patch (주로 폼 입력 요소의 간격·여백)
- Emotion으로 작성된 컴포넌트를 Tailwind CSS로 일부 마이그레이션
- 사용 방식의 한계로 인해 디자인 요구사항을 충족하지 못하는 Ant Design v4 컴포넌트를 shadcn/ui로 일부 마이그레이션
- 매번 사용처에서 덮어쓰느라 컴포넌트·페이지마다 달랐던 디자인과 동작을, 단일 진입점에서 커스터마이징해 불일치 해소 및 구현 비용 감소
개발 환경 개선
- 페이지별 Lazy Loading 적용: 10MB가 넘는 단일 JS 파일을 분할
- 프로덕션 서버: 모든 JS를 로드하지 않음을 전제로 경미한 데이터 사용량 감소 (gzip이 적용되어 있어 큰 차이는 없음)
- 개발 서버: 새로고침·HMR 시간을 약 7초 → 2초로 단축
- Frontend 통합·인수 테스트 기반 구축: 코드 변경으로 인한 제품 동작의 회귀 오류를 즉시 확인할 수 있는 검증 체계 마련
- 라우터 선언 구조를 포함한 일련의 리팩터링으로, 단위 테스트 환경에서 여러 페이지 간의 동작을 테스트할 수 있도록 개선
- 목표 권한·알림 설정 등 기존 기능의 테스트 작성 및 작성 방법 공유
- 방치되고 있던 E2E 테스트를 대체
- Dependency cruiser를 활용한 의존성 그래프 생성 스크립트 작성, 리팩터링 과정에 활용
목표 도메인의 Frontend에 Layered Architecture 도입
- 목표 도메인의 Frontend 아키텍처 재설계 방향 제안, 같은 저장소 내에 새 목표 도메인 디렉터리를 만들어 적용
- 스프린트의 변경 범위와 일정을 고려해 레거시 코드를 점진적 리팩터링
- FSD(Feature-Sliced Design)와 같은 외부 규칙을 그대로 가져오지 않고, 팀이 자주 반복하는 Anti-Pattern을 방지하는 최소한의 구조로 설계
- eslint-plugin-boundaries 규칙을 도입하여 경계를 넘나드는 잘못된 의존 코드를 금지
아키텍처 구조와 해설
src/
목표_도메인_레거시_코드베이스/
pages/
components/
hooks/
utils/
api/
목표_도메인_모던_코드베이스/
pages/
features/
ui/
domain/
api/
react-toolkit/
js-toolkit/문제 현상과 설계 의도
팀은 다음 문제들로 코드 변경에 의한 동작을 예측할 수 없고 이를 가속하는 악순환에 빠짐
pages,features,ui- 우발적 중복 여부를 판단하지 않고 DRY 원칙만 적용해, 무리한 구현체 확장을 시도
- 이러한 코드 변경이 기존 사용처에 의도치 않은 동작을 유발, 예를 들어 반환값을 사용하지 않는 API 호출 등
- 결국 재사용하던 컴포넌트를 어느 시점부터 재사용할 수 없게 됨, 실제로 재사용 가능한 컴포넌트가 희소해 동일한 UI를 매번 새로 작성하고, 이를 다시 무리하게 확장하면서 악순환 고리 형성
- → 컴포넌트를 성격에 따라 분류해 나쁜 의존성의 추가를 방지
domain- 비즈니스 규칙이 UI에 인라인 되어 동일한 기능이 페이지마다 동작이 다름
- 이러한 동작 차이를 사용자·실무자가 인지·이해하지 못해 혼란 가중
- 기한을 맞추기 위해 임시방편이 반복되면서 악순환 고리 형성
- → UI로부터 비즈니스 규칙을 추출하여 관리
react-toolkit,js-toolkit- 로직을 추상화할 때 근거 없이 커스텀 Hook으로 만드는 습관이 형성되어 있음
- 그리고 커스텀 Hook에 아무 의존성이나 넣는 습관이 형성되어 있음
- 이는 다시 컴포넌트에 나쁜 의존성을 추가함으로써 악순환 고리 형성
- → React 의존성을 계층으로 격리해 React가 필요한 추상화인지 스스로 사전 검토
- 자세한 설계 의도는 이 아키텍처와 유사한 choegyumin/agent-skills:frontend-layered-architecture 스킬에서 일부 확인할 수 있음
엔지니어링 지식 체계화
- README와 온보딩 문서의 최신화