본문으로 건너뛰기

개인 프로젝트: 쓰기는 잠가 두는 로컬 운영 도구

· 약 2분
Seung hyo, Lee
Front End Engineer @ Assistfit

이번 달에는 퇴근 후 개인 프로젝트로 운영 도구를 하나 만들었다. 여러 나라의 오픈마켓에 상품을 파는 판매자가 원가와 할인을 관리하는 운영 도구다. 판매자 PC에서만 돌고, 마켓의 Open API를 로컬 Node 서버가 대신 호출한다.

구성​

화면은 Vite와 React 19, 언어는 TypeScript 없이 JavaScript로 갔다. 테스트는 node --test로 돌린다. 판매자가 입력하는 원가와 손익 시나리오는 IndexedDB에 저장한다. 저장은 읽었던 값과 현재 값이 같을 때만 쓰는 CAS 방식 헬퍼를 거치게 해서, 사이에 다른 저장이 끼어들면 덮어쓰지 않는다.

이번 달에 만든 기능은 상품별 원가와 시장별 예상 손익, 판매 그룹 목표 마진에서 할인율을 역산하는 대량 할인 화면, 예약 할인 만들기, 예상 적자 상품 경보, 할인 만료 관제, CSV 내보내기 정도다. CSV는 엑셀에서 열리므로 =, +, -, @로 시작하는 문자열에 작은따옴표를 붙여 수식으로 실행되지 않게 했다.

export function csvCell(v) {
if (v === null || v === undefined) return ''
if (typeof v === 'number') return Number.isFinite(v) ? String(v) : ''
let s = String(v)
if (/^[=+\-@\t\r]/.test(s)) s = `'${s}`
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s
}

쓰기는 기본 잠금​

실제 판매 중인 상품의 가격과 할인을 건드리는 도구라 안전장치에 가장 많이 신경 썼다. 로컬 서버는 localhost 호스트로 온 요청만 받는다. 마켓 API는 읽기 경로부터 열었고, 상품 상세처럼 호출이 많은 조회는 사용자가 누를 때만 한 건씩 부른다.

쓰기 경로는 전부 기본으로 잠겨 있다. 할인 만들기, 할인가 수정, 상품 삭제 같은 경로마다 환경 변수 스위치가 따로 있고, 켜져 있지 않으면 입력 검증이나 토큰 조회 전에 바로 423을 돌려준다. 파일 맨 위 주석에는 "에이전트는 이 값을 켜지 않는다"고 적었다. 코드 대부분을 AI 에이전트와 같이 쓰기 때문에, 에이전트가 테스트하다가 실서비스에 쓰는 일이 구조적으로 없어야 했다. 쓰기는 국가별로 하나씩 열었다. 먼저 두 나라에서 열고, 세 번째 나라는 실제로 할인을 만들고 바로 지우는 테스트를 통과한 뒤에 허용했다.

구현과 검증을 다른 모델에게​

작업 방식도 이번에 처음 시도한 게 있다. 구현을 맡는 쪽(Red)과 독립 검증을 맡는 쪽(Blue)을 서로 다른 AI 모델로 두고, 또 다른 에이전트가 감독과 최종 판정을 맡았다. Blue는 Red의 커밋을 직접 체크아웃해 테스트와 빌드를 돌리고, 실제 브라우저에서 저장 실패 복구나 CAS 충돌 같은 시나리오를 확인한 뒤 수용 여부를 문서로 남긴다. 나는 기능 범위와 잠금 해제 같은 판단만 하고, 검증 보고서를 읽고 결정했다.

외부로 나가는 쓰기, 유료 크레딧 사용, 배포, push는 처음부터 범위에서 뺐다. 에이전트에게 맡길수록 무엇을 못 하게 할지를 먼저 정해 두는 편이 편했다.