HUNIL PARK

DY Microfinance CMS

고객 관리 시스템

Next.jsNestJSPostgreSQLTypeScript
DY Microfinance CMS

프로젝트 개요

DY Microfinance CMS는 미얀마 소재 소액금융기관의 고객 관리 및 회계 업무를 자동화하기 위한 웹 기반 관리 시스템입니다. 기존에는 Excel 스프레드시트와 수작업으로 고객 정보, 대출 내역, 회계 장부를 관리하다 보니 데이터 정합성 문제가 빈번했고, 월말 결산 시 수십 시간의 수작업이 필요했습니다. 이를 해결하기 위해 Next.js 기반 프론트엔드와 NestJS 기반 백엔드를 PostgreSQL 데이터베이스로 연결한 분리형 아키텍처를 채택했습니다. 1인 개발자로서 프론트엔드와 백엔드를 모두 담당했으며, 프론트엔드에서는 관리자용 대시보드 UI/UX 설계와 구현, 복잡한 회계 데이터 테이블 관리, 폼 검증 로직 등을 중점적으로 다뤘습니다.

전체 시스템 아키텍처 설계부터 배포까지 모든 과정을 담당했으며, 프론트엔드 관점에서는 Next.js App Router 기반의 관리자 대시보드, Server Actions를 활용한 폼 처리, 회계 데이터 테이블 렌더링 최적화, 다국어(미얀마어/영어) 지원 등을 구현했습니다. 대시보드에서는 고객 수, 대출 잔액, 월별 수익 등의 핵심 지표를 실시간으로 시각화하여 경영진이 한눈에 현황을 파악할 수 있도록 했습니다. 회계 프로세스 자동화를 통해 BS(대차대조표)와 PL(손익계산서)을 자동 생성하는 인터페이스를 구축했고, 이를 통해 약 90%의 수작업을 제거하여 월말 결산 시간을 수십 시간에서 수 시간으로 단축했습니다. 또한 사용자(대출 상담사, 회계 담당자, 관리자)별로 권한을 구분하여 각자 필요한 화면과 기능만 접근할 수 있도록 Role-Based Access Control을 프론트엔드 라우팅 레벨에서 구현했습니다.

DY Microfinance CMS architecture

기술 구현

Next.js 기반 관리자 대시보드 설계 및 구현

회계 담당자와 관리자가 매일 확인해야 하는 정보가 많았습니다. 고객 수, 대출 잔액, 연체율, 월별 수익, 최근 대출 신청 내역 등 다양한 데이터를 한 화면에서 직관적으로 보여줘야 했고, 데이터가 실시간으로 업데이트되면서도 페이지 로딩 속도가 느려지지 않아야 했습니다. 특히 PostgreSQL에서 집계 쿼리를 수행할 때 시간이 오래 걸리는 경우가 있어, 프론트엔드에서 어떻게 데이터를 가져와 표시할지 전략이 필요했습니다. 또한 미얀마 현지 사용자들의 인터넷 환경이 불안정한 점을 고려하여 오프라인 대응과 데이터 캐싱도 중요한 요구사항이었습니다.

Next.js App Router의 Server Components를 활용해 대시보드 초기 렌더링 시 서버에서 데이터를 미리 가져와 HTML에 포함시켜 전송하는 방식으로 초기 로딩 속도를 최적화했습니다. 핵심 지표(고객 수, 대출 잔액 등)는 백엔드 API에서 미리 계산된 값을 제공받아 프론트엔드에서는 단순히 표시만 하도록 했고, 복잡한 집계는 PostgreSQL의 Materialized View를 활용해 백엔드에서 주기적으로 갱신하도록 구성했습니다. 차트 렌더링에는 Recharts 라이브러리를 사용해 월별 수익 추이, 대출 상환율 등을 시각화했고, 테이블 컴포넌트는 TanStack Table을 활용해 정렬, 필터링, 페이지네이션 기능을 클라이언트 사이드에서 빠르게 처리할 수 있도록 했습니다. 또한 React Query(TanStack Query)를 도입하여 API 응답을 캐싱하고, stale-while-revalidate 전략으로 사용자가 대시보드를 재방문할 때 캐시된 데이터를 먼저 보여주고 백그라운드에서 최신 데이터를 가져오도록 구성했습니다.

관리자 대시보드 초기 로딩 시간이 3초 이하로 단축되었고, 사용자는 페이지를 열자마자 핵심 지표를 즉시 확인할 수 있게 되었습니다. React Query의 캐싱 덕분에 사용자가 대시보드를 자주 방문해도 불필요한 API 호출이 줄어들어 서버 부하도 감소했습니다. 차트와 테이블이 부드럽게 렌더링되어 사용자 만족도가 높아졌고, 인터넷이 불안정한 환경에서도 캐시된 데이터로 기본적인 정보 확인이 가능해졌습니다.

프론트엔드-백엔드 분리형 아키텍처와 API 연동

초기에는 Next.js의 API Routes를 사용해 풀스택 구성으로 개발할지, 아니면 프론트엔드와 백엔드를 완전히 분리할지 고민이었습니다. 향후 모바일 앱이나 다른 클라이언트가 추가될 가능성을 고려하면 백엔드를 독립적인 API 서버로 분리하는 것이 유리했지만, 1인 개발 환경에서 두 개의 프로젝트를 관리하는 부담도 컸습니다. 또한 프론트엔드에서 백엔드 API를 호출할 때 타입 안정성을 유지하고, 인증 토큰 관리, 에러 핸들링 등을 일관되게 처리하는 아키텍처가 필요했습니다.

Next.js 프론트엔드와 NestJS 백엔드를 분리하되, 개발 환경에서는 Next.js의 rewrites 기능을 활용해 /api/* 경로를 NestJS 서버로 프록시하여 CORS 이슈를 회피했습니다. 프로덕션 환경에서는 Nginx를 리버스 프록시로 사용해 프론트엔드와 백엔드를 동일 도메인에서 서비스하도록 구성했습니다. API 클라이언트는 axios 기반 커스텀 인스턴스를 만들어, 모든 요청에 JWT 토큰을 자동으로 포함시키고, 401 응답 시 자동으로 토큰 갱신을 시도하는 인터셉터를 구현했습니다. 타입 안정성을 위해 백엔드 NestJS에서 정의한 DTO(Data Transfer Object)를 프론트엔드에서 공유하여, TypeScript 타입 추론을 통해 API 요청/응답 구조를 컴파일 타임에 검증할 수 있도록 했습니다. Zod를 사용해 런타임 타입 검증도 추가하여 백엔드 응답이 예상과 다를 경우 즉시 에러를 발생시켜 디버깅을 용이하게 했습니다.

프론트엔드와 백엔드가 명확히 분리되어, 각각의 개발과 배포가 독립적으로 가능해졌습니다. NestJS 백엔드는 RESTful API 서버로 기능하여 향후 모바일 앱이나 외부 시스템과의 연동이 용이한 구조가 되었고, Next.js 프론트엔드는 순수하게 UI/UX에 집중할 수 있었습니다. 타입 공유 구조 덕분에 백엔드 API 변경 시 프론트엔드에서 타입 에러가 즉시 발생하여, 런타임 에러를 사전에 방지할 수 있었습니다. axios 인터셉터를 통한 자동 토큰 갱신 로직으로 사용자가 장시간 대시보드를 사용해도 세션이 끊기지 않아 사용자 경험이 개선되었습니다.

회계 프로세스 자동화 인터페이스 구현

기존 회계 프로세스는 대출 내역, 상환 내역, 비용 지출 등의 데이터를 수작업으로 Excel에 입력하고, 월말에 BS와 PL을 수동으로 작성하는 방식이었습니다. 이를 자동화하려면 프론트엔드에서 대출/상환/지출 데이터를 입력받는 폼을 제공하고, 백엔드에서 회계 로직을 수행한 뒤, 결과를 프론트엔드에서 보고서 형태로 출력해야 했습니다. 특히 회계 데이터의 정합성이 중요했기 때문에, 입력 단계에서 엄격한 검증이 필요했고, 사용자가 실수로 잘못된 데이터를 입력하지 않도록 UX 차원에서 가이드를 제공해야 했습니다. 또한 월말 보고서는 PDF로 출력할 수 있어야 했습니다.

Next.js의 Server Actions를 활용해 폼 제출 시 서버 사이드에서 데이터 검증과 DB 저장을 처리하도록 구성했습니다. 클라이언트 사이드에서는 React Hook Form과 Zod를 조합하여 실시간 폼 검증을 수행하고, 사용자가 필드를 입력할 때마다 즉각적인 피드백(에러 메시지, 성공 인디케이터)을 제공했습니다. 예를 들어 대출 금액 입력 시 숫자만 허용하고, 상환일은 대출일보다 미래여야 한다는 조건을 클라이언트와 서버 양쪽에서 검증했습니다. 월말 보고서 생성 기능은 사용자가 '보고서 생성' 버튼을 누르면 백엔드에서 회계 데이터를 집계하여 BS/PL을 생성하고, 프론트엔드에서는 이를 테이블 형태로 렌더링한 뒤, React-PDF 라이브러리를 사용해 클라이언트 사이드에서 PDF로 변환하여 다운로드할 수 있도록 구현했습니다. 이를 통해 월말 결산 프로세스를 '데이터 입력 → 버튼 클릭 → 보고서 다운로드'의 3단계로 단순화했습니다.

회계 프로세스 자동화로 월말 결산 시간이 기존 수십 시간에서 수 시간으로 단축되었고, 수작업으로 인한 오류(오타, 계산 실수 등)가 거의 사라졌습니다. 사용자는 폼 입력 시 실시간으로 에러 피드백을 받아 잘못된 데이터를 미리 수정할 수 있었고, 보고서를 PDF로 즉시 출력하여 경영진에게 제출할 수 있어 업무 효율이 크게 향상되었습니다. 자동화율 약 90%를 달성하여 회계 담당자의 업무 부담이 대폭 줄어들었고, 데이터 정합성도 크게 개선되었습니다.

트러블슈팅

대시보드 테이블 렌더링 성능 저하

고객 수가 수천 명으로 늘어나면서 대시보드의 고객 목록 테이블을 렌더링할 때 브라우저가 버벅이는 현상이 발생했습니다. 초기에는 백엔드에서 전체 고객 데이터를 한 번에 가져와 프론트엔드에서 렌더링했는데, 수천 개의 행을 DOM에 그리다 보니 렌더링 시간이 수 초 걸렸고, 스크롤 시에도 버벅임이 심했습니다. 사용자는 테이블을 빠르게 스크롤하며 특정 고객을 찾고 싶어 했지만, 렌더링 성능 문제로 사용성이 크게 저하되었습니다.

가상 스크롤링(Virtual Scrolling) 기법을 도입하여 현재 뷰포트에 보이는 행만 DOM에 렌더링하도록 최적화했습니다. TanStack Table의 가상화 기능을 활용해, 수천 개의 데이터가 있어도 실제로는 화면에 보이는 20-30개의 행만 렌더링하고, 스크롤 시 동적으로 DOM을 교체하는 방식으로 변경했습니다. 또한 서버 사이드 페이지네이션을 추가 옵션으로 제공하여, 사용자가 페이지당 50개씩 데이터를 가져오도록 선택할 수 있게 했습니다. 검색 필터 기능도 강화하여 사용자가 이름이나 대출 ID로 필터링하면 해당하는 데이터만 백엔드에서 가져와 렌더링하도록 구성했습니다.

테이블 렌더링 성능이 극적으로 개선되어, 수천 개의 고객 데이터가 있어도 초기 로딩이 1초 이내로 완료되고, 스크롤이 부드럽게 동작하게 되었습니다. 사용자는 빠르게 데이터를 탐색할 수 있었고, 가상 스크롤링 덕분에 메모리 사용량도 크게 줄어들어 저사양 환경에서도 안정적으로 동작했습니다.

폼 제출 시 중복 요청 방지

사용자가 대출 신청 폼을 제출할 때 버튼을 여러 번 클릭하거나, 네트워크가 느려서 응답이 늦어지면 같은 데이터가 중복으로 DB에 저장되는 문제가 발생했습니다. 특히 미얀마 현지의 불안정한 인터넷 환경에서는 사용자가 '제출' 버튼을 누른 후 아무 반응이 없어 다시 누르는 경우가 빈번했습니다. 이로 인해 동일한 대출 신청이 두 번 생성되는 등의 데이터 정합성 문제가 발생했습니다.

프론트엔드에서 폼 제출 시 버튼을 즉시 비활성화(disabled)하고, 로딩 인디케이터(스피너)를 표시하여 사용자에게 '처리 중'임을 명확히 알렸습니다. React Hook Form의 isSubmitting 상태를 활용해 제출 중에는 버튼을 클릭할 수 없도록 막았고, Server Action이 완료되면 성공 메시지를 표시하거나 에러 메시지를 보여주는 피드백을 추가했습니다. 백엔드에서도 멱등성(Idempotency) 처리를 추가하여, 동일한 요청이 짧은 시간 내에 여러 번 들어오면 첫 번째 요청만 처리하고 나머지는 무시하도록 Redis 기반 중복 요청 체크 로직을 구현했습니다. 또한 Optimistic UI 패턴을 적용하여 폼 제출 즉시 UI를 업데이트하고, 백엔드 응답을 기다린 뒤 확정하는 방식으로 사용자 경험을 개선했습니다.

중복 요청으로 인한 데이터 정합성 문제가 완전히 해결되었고, 사용자는 폼 제출 후 즉시 피드백을 받아 '처리 중'임을 명확히 인지할 수 있었습니다. 네트워크가 느린 환경에서도 사용자가 여러 번 버튼을 누르지 않게 되어, 불필요한 요청이 줄어들었고 서버 부하도 감소했습니다.

회고

1인 풀스택 개발자로서 프론트엔드와 백엔드를 모두 설계하고 구현하면서 웹 애플리케이션의 전체 아키텍처에 대한 이해가 깊어졌습니다. 특히 Next.js의 App Router, Server Components, Server Actions 등 최신 기능을 실전에서 활용하며 SSR과 CSR의 장단점을 체감했고, 어떤 상황에서 어떤 렌더링 전략을 선택해야 하는지 판단할 수 있게 되었습니다. 프론트엔드-백엔드 분리형 아키텍처를 구성하며 API 설계, 타입 안정성 확보, 인증/인가 처리 등 실무에서 필수적인 역량을 쌓았습니다. 또한 대시보드와 데이터 시각화 UI를 구현하며 복잡한 비즈니스 로직을 사용자 친화적인 인터페이스로 풀어내는 UX 설계 능력을 향상시켰습니다. 특히 회계 프로세스 자동화를 통해 단순히 '기능 구현'을 넘어 '비즈니스 임팩트'를 만들어내는 개발의 가치를 체감했고, 사용자 피드백을 반영하여 지속적으로 개선하는 과정에서 큰 보람을 느꼈습니다.

프로젝트 초기에 테스트 코드 작성을 미루다 보니, 기능 추가 시 기존 기능이 깨지는 리그레션 이슈가 종종 발생했습니다. 특히 회계 로직처럼 정확성이 중요한 부분에서는 단위 테스트와 통합 테스트가 필수적인데, 일정에 쫓겨 생략한 것이 아쉬웠습니다. 향후에는 TDD(Test-Driven Development) 또는 최소한 핵심 비즈니스 로직에 대한 테스트 커버리지를 확보하는 습관을 들이고 싶습니다. 또한 CI/CD 파이프라인 구축도 미흡했습니다. 수동으로 서버에 SSH 접속하여 배포하는 방식이었는데, GitHub Actions 등을 활용한 자동화된 배포 파이프라인을 구축했다면 배포 속도와 안정성을 높일 수 있었을 것입니다. 성능 모니터링 측면에서도 Sentry나 LogRocket 같은 툴을 도입해 프로덕션 환경에서 발생하는 에러와 성능 병목을 실시간으로 추적했다면 더 빠르게 문제를 해결할 수 있었을 것입니다. 다음 프로젝트에서는 테스트, CI/CD, 모니터링을 초기부터 계획하여 보다 안정적이고 유지보수 가능한 시스템을 구축할 계획입니다.