파이낸스트랙 포트폴리오

보유 종목만 등록하면 매 평일 저녁 자산 현황과 AI 리포트를 메일·텔레그램으로 보내주는 무료 도구. 증권사 앱 화면을 캡처해 올리면 종목이 자동 등록됩니다.

0 팔로워

소개

미국주식 위주로 몇 년째 모으고 있는 개인 투자자입니다. 제가 쓰려고 만든 걸 무료로 열어뒀습니다. ■ 왜 만들었나 계좌가 흩어져 있으니 전체 평가금액을 보려면 앱을 세 번 열어야 했고, 환율이 움직이면 원화 기준으로 또 계산해야 했습니다. 그런데 만들고 나서 정작 제가 계속 쓰게 된 이유는 합산이 아니었습니다. 증권사 앱은 오늘 잔고만 보여주고, "지난달엔 얼마였지"는 아무도 안 알려줍니다. 그건 오늘부터 남기지 않으면 영영 만들 수 없는 데이터라서, 매일 저녁 자동으로 기록이 쌓이게 했습니다. ■ 하는 일 · 평일 장 마감 후 시세·환율을 가져와 그날 총자산을 기록 · 국내/해외를 원화 기준으로 합산, 자산 추이 그래프 · 그날 등락과 종목 이슈를 AI가 정리해 메일·텔레그램으로 발송 · 증권사 앱 캡처를 올리면 종목명·수량 자동 인식 (등록이 제일 귀찮아서) 증권사 계정은 연동하지 않습니다. 종목명과 수량만 직접 넣는 방식입니다. ■ 만들면서 제일 크게 배운 것 지난주에 유입 데이터를 브라우저별로 쪼개봤다가 놀랐습니다. 신규 방문자의 20%가 로그인 전환율 0%였습니다. 한 명도 없었습니다. 원인은 인앱브라우저였습니다. 커뮤니티 앱에서 링크를 누르면 앱 내장 브라우저가 열리는데, 구글이 보안 정책상 그 환경에서 OAuth를 거부합니다(disallowed_useragent). 그래서 그분들 화면에는 로그인 버튼이 아예 렌더링되지 않았습니다. 저는 크롬에서만 테스트해서 전혀 몰랐고요. 세 가지로 고쳤습니다. 1) 앱 이름 목록(KAKAOTALK 등)으로 감지하던 걸 웹뷰 자체를 판별하는 휴리스틱으로 바꿨습니다. iOS WKWebView는 UA에 Safari/ 토큰이 없고, 안드로이드 WebView는 "; wv)"가 붙습니다. 이름 목록은 새 앱이 나올 때마다 뚫리더군요. 2) 오탐이 더 위험해서, 이름으로 확정된 앱만 버튼을 안내로 교체하고 휴리스틱 추정일 때는 버튼을 남긴 채 안내만 덧붙였습니다. 짐작이 틀려도 멀쩡한 사용자의 로그인을 막지 않게요. 3) 웹뷰 안에서도 되는 이메일 매직링크 로그인을 추가했습니다. 그리고 차단이 발생한 것 자체를 이벤트로 남기게 했습니다. 이게 없어서 원인을 사후에 추정해야 했거든요. 고친 다음 주에 보니 차단 노출 13명 중 8명이 탈출 버튼을 눌렀습니다. 이전엔 0명이었습니다. 혹시 로그인 전환이 이상하게 낮은 서비스를 운영 중이시라면 브라우저별로 한번 쪼개보시길 권합니다. 저는 전체 평균만 보고 "랜딩 카피가 약한가" 하며 엉뚱한 데를 파고 있었습니다. ■ 스택 FastAPI + PostgreSQL + React(Vite/Mantine), Railway 배포. 시세는 yfinance, 실패 시 네이버 증권으로 폴백. 리포트 분석은 Claude. 무료이고 광고나 결제 유도는 없습니다. 피드백 주시면 반영하겠습니다.

포스트

론

론리

나만의 재밌는 방식으로 사는 개발자이자 투자자.

지난주에 이메일 매직링크 로그인을 붙이면서 하마터면 크게 당할 뻔한 게 있어서 공유합니다.

메일로 보낸 로그인 링크는 사용자보다 먼저 열립니다.

매직링크를 만들 때 제일 자연스러운 설계는 이겁니다.

GET /auth/verify?token=xxx → 검증하고 토큰 소진 → 세션 발급

그런데 기업 메일 게이트웨이나 보안 스캐너가 메일 안의 링크를 미리 열어봅니다. 피싱인지 확인하려고요. 그러면 1회용 토큰이 그 시점에 타버리고, 정작 사용자가 누르면 "만료된 링크"가 뜹니다.

재현이 잘 안 되는 게 고약합니다. 내 지메일에선 멀쩡한데 특정 사용자만 계속 실패합니다.

해결은 열람과 소진을 분리하는 겁니다.

  • 메일 링크가 API가 아니라 앱 주소를 가리키게 합니다 (/?login_token=xxx)
  • 랜딩은 토큰을 화면에 띄우기만 하고 아무것도 소비하지 않습니다
  • 사용자가 "로그인하기" 버튼을 눌러야 POST /auth/email/verify로 소진합니다

스캐너는 GET만 하기 때문에 토큰이 살아남습니다. POST는 사람만 만듭니다.

그 김에 같이 넣은 것들입니다.

  • 토큰은 원문을 저장하지 않고 sha256 해시만 넣습니다. DB가 털리면 평문 토큰은 그 자체로 남의 계정 열쇠입니다
  • 링크 요청 엔드포인트는 가입 여부·쿨다운·발송 실패와 상관없이 항상 200입니다. 응답이 갈리면 그게 곧 "이 이메일이 가입돼 있다"는 신호가 됩니다
  • 검증 실패는 만료·재사용·위조를 구분하지 않고 401로 통일했습니다. 사유를 알려주면 토큰 추측에 단서를 줍니다
  • 재발급하면 이전 미사용 토큰을 무효화하고 60초 쿨다운을 뒀습니다. 남한테 메일 폭탄 보내는 데 쓰이면 안 되니까요
  • 검증이 끝나면 성공·실패 상관없이 주소창에서 토큰을 지웁니다. 안 그러면 사용자가 URL을 공유할 때 토큰이 같이 새고 브라우저 기록에도 남습니다

무상태 서명 토큰(JWT류)으로 하면 테이블을 안 만들어도 되는데, 1회용을 보장할 수 없어서 접었습니다. 만료 전까지 같은 링크로 몇 번이든 로그인되고 개별 폐기도 안 됩니다. 로그인 토큰은 계정 접근 권한 그 자체라 그 통제를 포기하기가 어렵더군요.

혹시 매직링크 붙이실 계획 있으시면 GET 하나로 끝내지 마세요. 저는 운 좋게 만들면서 알았는데, 배포하고 나서 알았으면 "왜 특정 사용자만 로그인이 안 되지"로 한참 헤맸을 겁니다.

파이낸스트랙 포트폴리오

보유 종목만 등록하면 매 평일 저녁 자산 현황과 AI 리포트를 메일·텔레그램으로 보내주는 무료 도구. 증권사 앱 화면을 캡처해 올리면 종목이 자동 등록됩니다.

1
1

댓글

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.