본문으로 건너뛰기
DEVKBS
작업 원장
018Dpectrum2025.01 – 2025.04운영 중

BREAK 카드 거래·경매 플랫폼

웹뷰 앱과 관리자 백오피스를 한 사람이

기간
2025.01 – 2025.04
분류
플랫폼
역할
프론트엔드 전담
구성
프론트엔드 1(전담) · 백엔드 1 · Flutter 1
사이트 방문
데스크톱 화면1600 × 1000
모바일 화면1080 × 2340

01개요

카드 한 장 한 장이 상품이고, 같은 카드라도 등급과 상태에 따라 값이 달라집니다. 목록에서 수백 장의 카드 이미지를 빠르게 훑을 수 있어야 하면서, 상세에서는 카드 표면의 미세한 상태까지 확인할 수 있어야 하는 상반된 요구가 있었습니다.

일반 사용자와 파트너 사업자가 같은 서비스를 쓰되 할 수 있는 일이 다르고, 거기에 운영자용 백오피스까지 필요했습니다. 프론트엔드는 내가 전담했기 때문에 웹뷰 앱과 백오피스를 모두 만들어야 했는데, 두 화면은 성격이 너무 달라서 같은 스택으로 묶지 않는 편이 나았습니다.

거래에는 구매·판매·교환·경매가 섞여 있었습니다. 각각 진행 단계가 다르고 되돌릴 수 있는 지점도 다릅니다. 이 부분을 화면 조건문으로 다루면 금방 손을 댈 수 없게 된다는 것이 초반의 가장 큰 고민이었습니다.

02기술 결정

문제가 무엇이었고, 무엇을 골랐고, 그래서 어떻게 됐는지.

  1. 결정 01

    웹뷰 앱과 백오피스는 같은 스택으로 묶지 않는다

    문제
    둘 다 프론트엔드지만 요구가 정반대였습니다. 사용자 앱은 첫 화면이 빨리 떠야 하고 검색 노출이 필요합니다. 백오피스는 로그인 뒤에만 열리고, 대신 복잡한 테이블과 화면 간 상태 유지가 중요합니다.
    선택
    사용자 앱은 Next.js로 서버 렌더링을 쓰고, 백오피스는 클라이언트 라우팅 중심으로 따로 구성했습니다. 공유하는 것은 API 타입과 도메인 규칙뿐입니다.
    결과
    각 화면이 자기 목적에 맞는 방식을 쓸 수 있었습니다. 하나의 구조에 두 요구를 밀어 넣었을 때 생겼을 타협을 피했습니다.
  2. 결정 02

    카드 목록은 offset이 아니라 커서로 넘긴다

    문제
    거래가 실시간으로 일어나 목록 순서가 계속 바뀝니다. offset 페이지네이션은 2페이지를 볼 때 1페이지에서 이미 본 카드가 다시 나오거나 아예 빠지는 문제가 생깁니다.
    선택
    정렬 기준값과 id를 조합한 커서로 다음 페이지를 요청하도록 API 규약을 백엔드와 함께 정했습니다.
    결과
    스크롤 중 중복·누락이 사라졌습니다. 새로 등록된 카드는 목록 상단 배지로 따로 알리는 방식으로 처리했습니다.
  3. 결정 03

    이미지는 올리는 순간 브라우저에서 줄인다

    문제
    카드 사진은 휴대폰으로 찍은 원본이라 한 장에 수 MB씩 됐습니다. 그대로 올리면 업로드가 오래 걸리고, 저장 비용과 목록 로딩까지 연쇄적으로 무거워집니다.
    선택
    업로드 전에 브라우저에서 크기를 규격에 맞춰 줄여 보내고, 목록용 썸네일과 상세용 원본을 나눠 요청하도록 했습니다.
    결과
    업로드 시간과 저장 리소스가 함께 줄었습니다. 목록 스크롤이 눈에 띄게 부드러워졌고, 상세 진입 시 빈 화면이 보이는 순간도 사라졌습니다.
  4. 결정 04

    거래 종류마다 상태 기계를 따로 둔다

    문제
    구매·판매·교환·경매는 진행 단계가 다릅니다. 한 컴포넌트에서 조건문으로 다루면 어떤 조합이 가능한지 아무도 설명할 수 없게 됩니다.
    선택
    거래 종류별로 가능한 상태와 전이를 따로 정의하고, 화면은 현재 상태에서 가능한 동작만 그리게 했습니다.
    결과
    '이 상황에서 이 버튼이 보여야 하나'라는 질문에 정의를 보고 답할 수 있게 됐습니다. 새 거래 유형을 추가할 때 기존 흐름을 건드릴 필요도 없었습니다.

03주요 기능

  • 권한 기반 화면 분기

    구매자·판매자·파트너의 화면과 기능을 권한 정의에서 파생시켜 접근을 제어.

  • 카드 검색과 필터

    시리즈·등급·가격대·상태를 조합해 좁히고, 조건은 URL에 남아 공유 가능.

  • 무한 스크롤 목록

    커서 기반 페이징으로 실시간 등록에도 중복 없이 이어붙입니다.

  • 거래와 경매 흐름

    구매·판매·교환·경매를 각각 다른 상태 정의로 다루는 거래 화면.

  • 결제와 본인인증

    토스페이먼츠 결제와 포트원 본인인증을 연동.

  • 1:1 실시간 채팅

    Firebase Realtime Database로 거래 당사자 간 채팅을 구현.

  • 배송 조회

    Sweet Tracker API로 거래 후 배송 상태를 확인.

  • 관리자 백오피스

    사용자·상품·거래 관리와 SheetJS 기반 엑셀 다운로드.

04회고

한 사람이 사용자 화면과 운영 화면을 모두 만들면 좋은 점이 하나 있습니다. 운영자가 무엇을 힘들어하는지 알고 사용자 화면을 만들게 된다는 것입니다. 반대로 나쁜 점은 두 화면의 기준이 나 한 사람에게 있어서, 내가 놓친 것은 아무도 못 잡는다는 점이었습니다.

실시간으로 바뀌는 목록은 UI 문제로 보이지만 사실 API 계약 문제입니다. 프론트에서 아무리 잘 감싸도 offset 페이징 위에서는 답이 안 나옵니다. 초반에 백엔드와 커서 규약을 맞춘 게 이 프로젝트의 분기점이었습니다.

상태를 정의로 먼저 적어 두는 방식은 처음에 느리게 느껴졌지만, 후반에 거래 유형이 추가됐을 때 값을 다 회수했습니다. 화면을 만들기 전에 가능한 상태를 적는 습관은 이후로도 계속 쓰고 있습니다.