본문으로 건너뛰기
DEVKBS
작업 원장
020Dpectrum2025.04 – 2025.10

GOLDIE 금 거래 앱 (사장님)

같은 데이터를 정반대 방향에서 보는 화면

기간
2025.04 – 2025.10
분류
플랫폼
역할
프론트엔드 전담
구성
프론트엔드 1(전담) · 백엔드 1 · Flutter 1
Google Play

화면 준비 중

화면 대신 프로젝트의 기능과 개발 기록을 정리했습니다.

01개요

GOLDIE는 사용자용과 사업자용 두 개의 웹뷰 앱으로 이루어져 있습니다. 이 기록은 사업자(사장님) 쪽입니다. 소비자 앱이 '얼마에 팔 수 있나'를 다룬다면, 이쪽은 '얼마에 사갈 것인가'를 다룹니다.

같은 데이터를 반대 방향에서 보기 때문에 화면을 그대로 뒤집어 쓸 수 없었습니다. 소비자에게는 예상 수령액이 가장 중요한 숫자지만, 사업자에게는 현재 시세 대비 매입가의 비율과 남은 판단 시간이 더 중요합니다. 무엇을 크게 놓을지가 완전히 달라집니다.

국제 금 시세는 계속 움직입니다. 사업자는 그 흐름을 보면서 금액을 조정하는데, 화면이 잠깐이라도 지난 숫자를 보여주면 바로 손해로 이어집니다. 그래서 시세, 남은 시간, 본인이 낸 제시 금액 이 세 가지를 항상 최신으로 맞춰 두는 게 이 앱에서 가장 중요했습니다.

사용자 앱과 코드베이스를 공유하면서도 화면은 따로 가야 해서, 어디까지 공통으로 두고 어디부터 나눌지 정하는 것도 큰 일이었습니다.

02기술 결정

문제, 선택, 결과 순서로 적었습니다.

  1. 결정 01

    마감 시각을 클라이언트 시계로 계산하지 않는다

    문제
    남은 시간을 브라우저 로컬 시각으로 계산하면 사용자 기기의 시계가 몇 분씩 틀어져 있을 때 '아직 30초 남았는데 마감됨' 같은 상황이 생깁니다. 돈이 걸린 화면에서는 곧바로 분쟁이 됩니다.
    선택
    서버 응답 시각과 최초 렌더 시점의 로컬 시각 차이(offset)를 한 번 구해두고, 이후 모든 카운트다운을 '로컬 시각 + offset' 기준으로 계산했습니다. 마감 직전 구간에서는 확인 주기를 좁힙니다.
    결과
    기기 시계가 틀어져 있어도 모든 참여자가 동일한 잔여 시간을 봅니다. 최종 판정은 서버에 두어 화면과 결과가 어긋나지 않게 했습니다.
  2. 결정 02

    실시간 시세는 전역 스토어 하나로만 흘린다

    문제
    시세 숫자가 헤더, 입력 폼, 목록 행, 요약 카드 등 여러 곳에 동시에 등장합니다. 각 컴포넌트가 알아서 시세를 가져오면 한 화면 안에서 서로 다른 숫자가 잠깐씩 보입니다.
    선택
    시세 구독은 최상위에서 한 번만 하고 Zustand 스토어에 넣었습니다. 소비하는 쪽은 selector로 필요한 필드만 구독해 관계없는 값이 바뀌어도 리렌더가 번지지 않게 했습니다.
    결과
    화면 전체가 언제나 같은 시세를 가리킵니다. 구독 연결이 하나뿐이라 탭 전환·재접속 시 정리해야 할 지점도 한 곳으로 모였습니다.
  3. 결정 03

    금액 규칙은 조건문이 아니라 스키마로 검증한다

    문제
    최소 단위, 시세 대비 상·하한, 이전 제시 금액과의 관계 같은 규칙이 서로 얽혀 있었습니다. 조건문으로 쌓으면 규칙이 바뀔 때마다 손댈 곳이 늘어납니다.
    선택
    Zod 스키마 하나에 규칙을 모으고 `superRefine`으로 교차 조건을 표현했습니다. 같은 스키마를 제출 전 검증과 입력 중 경고 표시에 함께 썼습니다.
    결과
    규칙이 바뀌면 스키마 한 곳만 고치면 됩니다. 어떤 조건 때문에 막혔는지 사용자에게 정확한 문장으로 알려줄 수 있게 된 것도 부수 효과였습니다.
  4. 결정 04

    두 앱의 공통 부분은 도메인 기준으로 나눈다

    문제
    사용자 앱과 사업자 앱은 같은 도메인을 다루지만 화면은 거의 다릅니다. 컴포넌트를 기준으로 공통화하면 양쪽 사정이 한 컴포넌트 안에 조건문으로 쌓입니다.
    선택
    화면과 컴포넌트는 각자 두고, 시세 계산·상태 정의·API 타입처럼 도메인에 속한 것만 공유했습니다.
    결과
    한쪽 화면을 고쳐도 다른 쪽이 깨지지 않았습니다. 겉모습이 비슷한지보다 의미가 같은지를 기준으로 공통화하게 됐습니다.

03주요 기능

  • 실시간 시세 기반 매입 화면

    시세 변동에 따라 제시 가능 범위가 함께 움직이고, 벗어나면 이유를 먼저 알려줍니다.

  • 잔여 시간과 상태 전이

    진행 중 → 마감 임박 → 마감 → 확정으로 이어지는 상태를 한 화면에서 끊김 없이 표현.

  • 거래 이력 조회

    과거 제시와 성사 여부를 기간·상태로 좁혀 보는 조회 화면.

  • 사용자와의 1:1 채팅

    Firebase Realtime Database 기반 실시간 상담.

  • 권한 분리

    운영 관리자와 입점 사업자가 서로 다른 메뉴와 데이터 범위를 갖도록 라우트 단위로 분리.

04회고

시간을 다룰 때는 화면에 보여주는 시간과 최종 판정하는 시간을 따로 봐야 합니다. 화면은 사용자가 납득할 수준이면 충분하고, 판정은 언제나 서버가 합니다. 이걸 처음에 정해 두니 마감 직전의 애매한 경우를 이야기할 때 다툴 일이 없었습니다.

같은 데이터로 만든 두 앱을 동시에 맡으면서 재사용 기준을 다시 생각하게 됐습니다. 겉모습이 비슷하다고 합치면 나중에 꼭 다시 갈라야 하고, 의미가 같은 건 처음부터 합쳐 두는 편이 낫습니다.

아쉬운 점은 실시간 데이터의 재연결 시나리오를 뒤늦게 다듬었다는 것입니다. 네트워크가 끊겼다 붙는 순간의 처리는 개발 환경에서 잘 드러나지 않아서, 실제 사용 환경에서 보고를 받고서야 손을 댔습니다. 다음부터는 끊김 상황을 개발 초기에 강제로 만들어 두고 시작하려고 합니다.