가짜 데이터를 만들지 않습니다
시세·뉴스·환율을 못 받으면 그 사실을 그대로 보여 줍니다. 틀린 환율로 계산한 총액은 비어 있는 것보다 나쁩니다. 그래서 환율이 없으면 그 계좌를 “환산 불가”로 두고 합계에서 뺍니다.
Asset Pocket 은 여러 증권 계좌의 주식과 예수금을 한 화면에 모으는 포트폴리오 앱입니다. 웹과 iOS·안드로이드 앱이 같은 API, 같은 계정을 씁니다. 계좌마다 보유를 직접 적거나 거래 원장으로 쌓을 수 있고, CSV 는 저장하기 전에 미리보기로 검사합니다. 시세·환율·뉴스를 못 받으면 빈칸을 추측으로 채우지 않고, 무엇을 몰라서 합계에서 뺐는지 화면에 적습니다.
지금은 로컬에서 도는 빌드입니다. 시세(KIS)와 뉴스(네이버) 키를 넣지 않은 상태라, 아래 화면은 “공급자 미설정”일 때 제품이 어떻게 말하는지를 그대로 보여 줍니다.

세 화면은 같은 데모 계정입니다. 국내 계좌 하나와 해외(USD) 계좌 하나, 보유 종목 5개.
계좌마다 두 방식 중 하나를 고릅니다. 보유 수량을 직접 적는 방식과, 거래 원장을 쌓으면 보유가 계산되는 방식. 원장 계좌의 보유는 고쳐 쓰지 않고 매번 원장 전체를 재생해서 다시 만듭니다.
합계는 시세를 받은 종목으로만 냅니다. 하나도 받지 못하면 숫자 대신 “평가할 수 없음”을 띄우고, 매입금액과 예수금은 따로 보여 줍니다. 합계에서 뺀 종목은 코드까지 적습니다.
구성 비중은 원래 평가금액으로 나눕니다. 시세가 비어 있으면 매입금액 기준으로 대신 나누고, 제목 옆에 그 사실을 붙입니다. 계좌별 · 자산 유형별 · 통화별 · 국가별 네 갈래입니다.



README 첫머리의 규칙은 “가짜 데이터를 만들지 않는다”입니다. 랜덤값도, 직전 값 재사용도, 0 이나 매입가로 메우기도 없습니다.
평가할 수 없음합계를 낼 근거가 없다는 뜻입니다. 0원과 같은 모양이면 버그로 봅니다. 시세 없음 · 환산 불가 · 공급자 미설정과 섞지 않습니다.
공급자 미설정키가 없어 아예 가져오지 않는 상태입니다. 장애가 아니므로 기다리라고 하지 않고, 키를 넣으라고 합니다.
뺀 종목은 이름까지시세를 못 받은 종목은 합계에서 빼고, 무엇을 뺐는지 종목 코드로 적습니다. 어느 공급자에서 비었는지도 함께 씁니다.
비중의 기준평가금액으로 나눌 수 없을 때는 매입금액으로 나누고, 그렇다고 적습니다. 차트 색은 실제 배경색 위에서 색각 검사를 통과시킨 값입니다.
공급자는 인터페이스 뒤에 있어, registry.ts 한 곳에 등록하면 나머지 코드를 건드리지 않고 바뀝니다. 지금 무엇이 오고 있는지는 GET /providers/status 와 화면 맨 위 배너가 알려 줍니다.
시세·뉴스·환율을 못 받으면 그 사실을 그대로 보여 줍니다. 틀린 환율로 계산한 총액은 비어 있는 것보다 나쁩니다. 그래서 환율이 없으면 그 계좌를 “환산 불가”로 두고 합계에서 뺍니다.
사용자 소유 테이블 9개에 Postgres 행 단위 보안이 걸려 있습니다. 앱은 테이블 소유자가 아닌 별도 롤로 접속하고, 사용자 맥락이 없으면 0건이 나옵니다. 코드가 where userId 를 빠뜨려도 새지 않습니다.
DB 안에서는 Decimal, JSON 경계에서는 문자열입니다. 평단에는 매수 수수료가 들어가고, 실현손익도 같은 이동평균으로 계산해 평가손익과 합이 맞습니다.
원장 계좌의 보유 종목은 저장된 값이 아니라 원장을 재생한 결과입니다. 증분 갱신은 거래를 지우거나 순서를 바꿀 때 어긋나기 때문입니다. 매도가 기대고 있는 매수를 지우려 하면 거절합니다.
같은 엔드포인트, 같은 계정입니다. 다른 건 refresh 토큰을 두는 곳뿐입니다. 웹은 httpOnly 쿠키, 앱은 키체인. 이미 회전된 토큰이 다시 오면 유출로 보고 그 세션 계보를 전부 끊습니다.
보유와 거래를 CSV 로 올리면 저장하기 전에 미리보기(dryRun)로 검사합니다. 실패한 줄은 파일의 실제 줄 번호와 사유로 돌려줍니다.
시세를 하나도 받지 못한 포트폴리오의 합계를 0원으로 적으면, 보는 사람은 자산이 사라졌다고 읽습니다. 그래서 화면이 구분하는 상태를 넷으로 나눴습니다. 시세 없음, 환산 불가, 평가할 수 없음, 공급자 미설정. 넷은 뜻도, 사용자가 할 일도 다릅니다. 가장 여러 번 다시 고친 부분이라, 어떤 리팩터링에서도 합치지 말라고 인계 문서에 적어 두었습니다.
통화가 다른 계좌는 “환산 불가”로 표시하고 전체 합계에서 뺍니다. 추측한 환율로 메우지 않습니다. 기본 환율 공급자인 open.er-api 는 키 없이 166개 통화를 하루 한 번 받아 옵니다. 환율은 워커가 채우므로, 워커가 꺼져 있으면 들어오지 않는다는 것도 문서에 적어 두었습니다.
보유와 관심 종목이 41개를 넘는 건 흔합니다. 넘친 종목을 버리지 않고 REST 폴링으로 내립니다. 시세는 계속 나오고 갱신 주기만 달라집니다. 화면에 보이는 종목이 슬롯을 먼저 가져가고, 무엇이 실시간이고 무엇이 폴링인지 화면이 말할 수 있어야 한다는 것이 구독 슬롯 매니저의 규칙입니다.
처음에는 simhash 로 제목을 묶으려 했습니다. 한국어 뉴스 제목 표본으로 재 보니 같은 사건과 다른 사건의 해밍거리가 겹쳐 선을 그을 수 없었습니다. 같은 표본에서 문자 2-gram 포함도는 깨끗하게 갈렸습니다. 같은 사건 0.46 이상, 다른 사건 0.07 이하. 그래서 simhash 는 저장과 완전 중복 판정에, 제목 유사도는 묶기에 씁니다.
KIS 시세 파서는 실제 키로 검증하지 않았습니다. 공식 문서 기준으로 짰고, 필드가 없으면 값을 지어내지 않고 실패로 떨어집니다. 앱은 실시간 스트림 대신 30초 폴링이고, 비밀번호 재설정 메일은 발송 어댑터가 없어 나가지 않습니다 — 화면이 그렇게 말합니다. EAS 빌드는 아직 한 번도 돌리지 않았습니다.