👨🏻💻 Project Summary
Web Services
Contenfi
한 줄 소개 : 콘텐츠 제작자와 투자자를 연결하고, 이들의 관리 리소스를 덜어주어 콘텐츠 제작에 대한 투자를 활성화하는 중개 플랫폼입니다.
설명 : 증권형 토큰(STO) 가이드라인에 맞춰, 벤처 캐피탈의 새로운 투자처를 창출하기 위해 만든 서비스입니다. 계좌 연결 및 항목 라벨링 기능을 통해, 음원 콘텐츠 제작에 사용된 비용과 발생한 수익 정보를 시각화하여 투명하게 제공합니다.
크릿벤처스 ↔ DOD Entertainment 의 20억 원 규모 음원 제작 투자 계약 건에 서비스를 지원했습니다.
기간 : 2023.01 - 2024.11
인원 : 개발 2명 / 디자인 1명 / 기획 및 운영 2명 (5명)
스택 : Next.js React.js Typescript Firestore Emotion SWR Tanstack-Query
기여한 부분 :
-
서비스 핵심 기능 개발 및 유지 보수를 진행했습니다.
-
계좌 내역 라벨링 : 연결된 투자 전용 계좌의 입출금 내역을 목적 및 출처 기준으로 라벨링하여, 피투자자의 자금 흐름 파악과 관리 리소스를 절감했습니다.
-
차트를 통해 수익률 데이터 시각화 : 프로젝트별, 유형별 수익 데이터를 월 단위로 정리하여, 수익 구조를 직관적으로 파악 가능하게 도왔습니다.
-
서류 아카이빙 : 정산 내역, 영수증 등 투자 관련 문서를 업로드 및 관리할 수 있는 기능을 통해 자료 보관 및 공유 편의성을 제공했습니다.
Firestore 기반 NoSQL 구조를 설계하고, 이를 활용하는 커스텀 훅과 UI 및 비즈니스 로직을 직접 구현하며 엔드 투 엔드 개발을 진행했습니다.
-
디자인 시스템을 도입해 코드의 재사용성과 디자인 일관성을 높였습니다.
- 디자인 시스템을 도입하기 전 프로덕트 개발 과정에서 다음과 같은 문제점을 겪고 있었습니다.
- 중복된 코드 사용 및 유지 보수의 어려움
- 개발팀은 반복적으로 사용되는 컴포넌트나 속성을 사전에 파악하는 데 어려움을 겪었고, 이로 인해 컴포넌트를 재사용 가능한 형태로 만드는 것에 제약이 있었습니다. 이는 코드 중복과 유지 보수의 어려움이라는 문제를 야기했습니다.
- 디자인의 일관성 부족으로 인한 사용자 경험 저하
- 요소의 크기나 여백에서 미세한 차이가 나거나, 육안상 비슷해 보이지만 실제로는 다른 색상이 사용되는 일이 빈번하게 발생했습니다. 이로 인해 디자인 품질이 일관되지 않았고, 전체적인 사용자 경험에 부정적인 영향을 미쳤습니다.
- 이러한 문제들을 해결하기 위해 디자인 시스템 도입을 적극적으로 건의하였고, 팀 내에서 이를 수용하여 도입이 결정되었습니다.
- 디자인 시스템 도입 후 프로덕트 개발 과정이 다음과 같이 개선되었습니다.
- 중복된 코드 제거 및 유지 보수성 개선
- 시스템을 기반으로 반복 사용되는 UI 요소들을 컴포넌트화함으로써 코드 중복을 줄이고 일관성을 유지할 수 있었습니다.
- 디자인 시스템을 확장하거나 수정할 때, 변경 사항이 전체 애플리케이션에 일관되게 반영되어 유지보수성과 확장성이 개선되었습니다.
- 디자인 작업물의 일관성 향상
- 시스템의 요소들을 사용해 작업물을 구현함으로써 디자인 일관성이 향상되었습니다.
- 개발 과정 중 2차 검수가 자연스럽게 이루어져 디자인 작업에서 발생할 수 있는 오류를 미리 발견하고 수정할 수 있게 되었습니다.
- 디자인팀 ↔ 개발팀의 커뮤니케이션 비용 감소
- 시스템 요소를 기준으로 명칭이 통일되어 디자인팀과 개발팀의 소통 과정이 효율적으로 개선되었습니다.
-
서비스 내의 모달과 관련된 상태를 중앙에서 관리하도록 리팩토링을 진행했습니다.
- 모달은 서비스 내에서 다양한 화면 전환 및 사용자 상호작용에 중요한 역할을 합니다. 기존에는 각 컴포넌트에서 필요할 때마다 모달을 띄우고 닫는 로직을 관리했으나, 이로 인해 모달 간 충돌과 모달 상태 관리 로직의 중복이 발생하는 문제가 있었습니다.
- 이를 해결하기 위해 모달 관련 상태를 전역적으로 관리하도록 리팩토링을 진행했습니다. 상태 관리에는 Context API를 사용했고, ModalProvider를 앱 최상단에서 감싸주었습니다. ModalProvider 내부에는 다음과 같은 요소들을 추가했습니다.
- openedModals : 현재 열려 있는 모달 엘리먼트들을 관리하는 배열입니다.
- openModal : 모달을 추가하는 함수이며, 인자로 리액트 엘리먼트를 받습니다.
- closeModal : 특정 모달 혹은 전체 모달을 화면에서 닫는 함수입니다.
- 이를 통해 모달 상태를 중앙 집중식으로 관리함으로써 모달 간 충돌을 방지하고, 코드 유지 보수성과 모달 로직의 재사용성을 높여 개발 효율성을 개선했습니다.
-
Next.js API Routes와 EmailJS를 활용해 클라이언트 실무진의 자율 초대 기능을 구현했습니다.
- 투자 프로젝트를 진행하는 과정에서, 클라이언트 측에서 관련 팀원을 추가해야 하는 상황이 자주 발생했습니다. 기존에는 저희가 팀원 정보를 전달받아 직접 DB에 등록하는 방식이었으나, 이는 팀원 추가 시마다 운영팀의 개입이 필요해 비효율적이었습니다. 따라서 클라이언트가 직접 팀원을 추가하면서도 보안성을 확보할 수 있는 방안이 필요했습니다.
- 이를 해결하기 위해 초대 코드 기반 사용자 등록 시스템을 구현했으며, 전체 흐름은 다음과 같습니다.
- 기존 가입자가 초대할 팀원의 정보를 입력한 후 ‘초대하기’ 버튼을 클릭하면, Firestore에 invitation 문서를 생성하고 EmailJS를 통해 초대 메일을 발송합니다.
- 수신자는 메일에 포함된 사이트 링크를 통해 접속 후 Google 로그인을 진행하며, 이후 signUpWithInvitationCode(Next.js API Routes)를 호출해 초대 코드와 이메일을 검증합니다.
- 검증이 완료되면, 초대된 팀원은 기존 가입자와 동일한 권한으로 서비스를 이용할 수 있습니다.
-
Firebase Admin SDK을 활용해 관리자 인증 절차를 구현했습니다.
- 서비스 특성상 B2B 클라이언트의 투자 계약 데이터를 다루고 있었기 때문에 데이터 접근은 오직 관련 클라이언트에게만 허용되어야 했습니다. 하지만 테스트 및 운영 과정에서는 개발팀과 운영팀이 클라이언트 데이터를 확인해야 했기 때문에 계정별 역할에 따라 접근 권한을 분리할 필요가 생겼습니다.
- 관리자 인증 절차는 다음과 같은 순서로 이루어지도록 구현했습니다.
- isAdmin(Next.js API Routes) 요청 유효성 확인
- HTTP 메서드 종류와 요청(req)에 accessToken이 포함되어 있는지 확인합니다.
- accessToken은 Firebase Authentication에서 구글 로그인을 통해 발급받은 ID Token을 의미합니다.
- verifyIdToken()을 통해 accessToken 검증
- Firebase의 Admin SDK에서 제공하는 verifyToken 메서드를 통해 accessToken의 서명, 만료 시간, 유효성 등을 검증하여 인증된 사용자만 진행할 수 있게 합니다.
- 관리자 이메일 검증
- 인증된 이메일 주소가 Firestore의 admin 컬렉션에 등록되어 있는지 확인하여, 관리자가 아닌 사용자의 요청을 거절합니다.
- 이를 통해 별도의 백엔드 서버 없이 인증 절차를 중앙에서 관리하면서도 보안성을 강화할 수 있었습니다.
-
리액트 메모이제이션을 통해 불필요한 재계산을 방지하고, 렌더링 성능을 최적화했습니다.
- useMemo를 활용하여 복잡한 계산이 필요한 값(예: 투자 수익금)을 캐싱하였습니다.
- React.memo와 useCallback을 활용하여 부모 컴포넌트에서 정의한 함수를 전달하는 경우에 자식 컴포넌트의 불필요한 리렌더링을 방지했습니다.
-
Session Storage를 활용하여 필요한 정보를 클라이언트 측에 저장하고 관리했습니다.
- 차트 및 계좌 내역 보기 옵션과 같은 정보는 화면을 그리기 위해 필요하지만, 서버에 저장할 만큼 중요한 정보는 아니었습니다. 하지만 해당 정보를 저장하지 않으면 사용자는 페이지 새로 고침 후 설정을 다시 해야 하는 불편을 겪어야 했습니다.
- 이를 해결하기 위해 클라이언트 측 저장소를 활용하기로 결정했습니다. Local Storage와 Session Storage가 후보로 고려되었고, 해당 정보는 세션 종료 시 더 이상 필요하지 않다고 판단해 Session Storage를 선택했습니다.
- 이를 통해 같은 세션 내에서 페이지 새로 고침 및 이동 후에도 사용자 설정을 유지할 수 있게 되어 사용자 경험을 개선할 수 있었습니다.
-
SWR → TanStack Query 리팩토링을 통해 사용자 경험 및 유지보수성을 개선했습니다.
- 초기에는 SWR을 활용해 서버 상태를 관리했지만, 서비스가 성장하면서 CRUD(Create, Update, Delete) 작업이 증가하며 상태 관리가 복잡해졌습니다. 또한 데이터 변경 후 캐싱을 수동으로 무효화해야 하는 번거로움과 개발자 간 CRUD 작업의 코드 일관성을 유지하는 데 어려움이 발생했습니다.
- 이를 해결하기 위해 SWR → TanStack Query 리팩토링을 진행했으며, 다음과 같은 효과를 얻을 수 있었습니다.
- 자동 캐싱 무효화(Query Invalidation)를 통한 상태 동기화 최적화
- useMutation을 활용하여 데이터 변경 시 캐싱을 자동으로 무효화하여, 개발자가 캐싱을 직접 관리해야 하는 부담을 줄였습니다.
- CRUD 작업 방식 표준화 및 유지보수성 향상
- useMutation을 기반으로 Create, Update, Delete 작업의 코드 패턴을 통일하여 개발자 간 코드 일관성을 유지할 수 있도록 하였습니다.
- 이를 통해 반복적인 코드 작성을 줄이고 유지보수성을 향상시켰습니다.
- 오프라인 복구 시 자동 패칭 및 데이터 유효 기간 최적화
- refetchOnReconnect 옵션을 통해 온라인 상태로 복귀했을 때 최신 데이터를 자동으로 패칭할 수 있도록 개선했습니다.
- staleTime, gcTime을 통해 데이터의 유효 기간을 조정하고, 캐싱 관리를 세밀하게 제어했습니다.
-
TDD 도입 여부를 판단하기 위해 계좌 내역 불러오기에 대한 단위 테스트 코드를 작성했습니다.
- 프로젝트에서 TDD(테스트 주도 개발) 방식을 도입할지 여부를 판단하기 위해, Jest와 React Testing Library를 활용해 테스트 코드를 작성했습니다.
- 작성한 테스트 코드의 주요 검사항목은 다음과 같았습니다.
- 계좌 내역이 로딩 상태를 거쳐 정상적으로 불러와지는지
- 계좌 내역이 없을 경우 이를 처리하는 인터페이스가 잘 동작하는지
- 선택된 필터 옵션에 따라 계좌 내역이 적절하게 변하는지
- 테스트 코드 작성 후 전사 회의를 통해 최종 도입 여부를 논의했으며, 결과적으로 TDD 도입은 보류되었습니다. 이유는 두 가지로 요약할 수 있었습니다.
- 테스트 코드 작성을 위해 필요한 추가 리소스
- 테스트 시나리오를 고민하고 그에 맞는 코드를 작성하는 것은 저희의 예상보다 더 많은 리소스를 필요로 했습니다.
- 서비스의 불확실성으로 인한 효율 저하
- 서비스가 언제, 어떻게 변할지 예측할 수 없는 불확실한 상황에서, 테스트 코드가 유효하지 않게 될 가능성이 높았고, 그로 인해 TDD 방식의 효율성이 떨어질 것이라 판단했습니다.