PiKi
여러 쇼핑몰에 흩어진 위시를 한곳에 모아 1:1 토너먼트로 '진짜 사고 싶은 것'을 고르게 돕는 쇼핑 어시스턴트. 위시 수집부터 토너먼트 결정, 결과 영수증까지 하나의 흐름으로 연결한 서비스로, 양대 스토어에 출시했습니다.
PiKi는 여러 쇼핑몰(29CM·무신사·지그재그 등)에 흩어진 위시를 한곳에 모아, 비슷한 가격대끼리 자동 매칭한 1:1 토너먼트로 '진짜 사고 싶은 것'을 가려내는 쇼핑 어시스턴트입니다.
App Store·Google Play에 출시해 운영 중이며, 최종 발표에서 대상과 모의투자 최대 유치를 기록했습니다.
쇼핑몰로 나가는 외부 요청이 많아, 그 부하가 메인 서버에 번지지 않도록 렌더·추출을 별도 모듈로 떼어낸 구조로 설계했습니다.
저는 이 서비스의 백엔드 전반을 맡았습니다. 버그가 나면 증상에서 멈추지 않고 원인을 데이터로 확인할 때까지 파고들고, 고친 뒤에도 그 수정이 진짜 문제를 막는지 테스트로 확인합니다.
- ▸인증·세션·OAuth 카카오·구글·애플로 몇 초 만에 로그인하게 만들고, 탈취된 토큰을 무력화하려 refresh 토큰을 회전시키며 재사용을 탐지했습니다. 이 회전이 동시 요청과 부딪혀 로그아웃되던 문제까지 풀어, 사용자는 끊김 없이 쓰면서도 토큰 탈취는 막히게 했습니다.
- ▸토너먼트 로직 PiKi의 핵심 유저 플로우인 토너먼트를 담당합니다. 사용자가 담은 위시들을 비슷한 가격대끼리 자동으로 짝지어 공정한 1:1 대진을 만들고, 라운드가 올라갈수록 취향이 선명해지게 결과를 집계합니다. 데이터가 커져도 초대코드·결과 조회가 느려지지 않게 풀스캔 쿼리를 인덱스로 걷어냈습니다.
- ▸실시간 알림 친구가 대신 골라주거나 토너먼트가 끝난 순간을 놓치지 않도록 FCM 푸시와 SSE로 실시간 알림을 보냈습니다. 앱 아이콘 badge로 안읽음을 표시하고, 여러 기기에서 동시에 접속해도 토큰 충돌로 알림이 깨지지 않게 멱등 처리했습니다.
- ▸공지 시스템 개발자가 코드로 공지를 보내는 것이 아니라, 팀 누구나 마크다운으로 공지를 작성하고 미리보기로 확인해 예약 발송까지 하게 만들었습니다. 푸시 문구를 본문과 따로 다듬을 수 있고, 누가 언제 보냈는지도 기록됩니다.
- ▸배포·인프라 빠른 무중단 배포를 위해 blue-green을 택하고, dev·staging·prod를 버튼 한 번에 승격하도록 만들었습니다. 브랜치가 어긋나 반복되던 배포 충돌은 승격 방식을 바꿔 구조적으로 없앴습니다.
- ▸운영 백오피스·접근통제 디자이너 등 비개발자 팀원도 코드 수정 없이 운영할 수 있도록, 공지·알림 발송처럼 원래 개발자 손이 필요하던 일을 GUI 백오피스에서 직접 하게 만들었습니다. 운영 도구를 여는 만큼 아무나 못 들어오게 비밀번호 없이 Discord 인증으로 접근을 통제하고, 누가 언제 무엇을 했는지 감사 로그로 남깁니다.
- ▸운영 자동화·지표 PR과 배포 상황을 Discord로 자동 알리고, /stats 한 줄이면 유저 지표를 그 자리에서 볼 수 있게 봇을 만들었습니다. 운영 통계 대시보드와 주간 리포트를 자동으로 보내, 팀이 서비스가 어떻게 돌아가는지 매주 확인하게 했습니다.
- ▸성능·에러 품질 클라이언트가 에러의 원인을 코드로 바로 알 수 있게 전 도메인에 일관된 에러 응답 체계를 깔고, 느려지던 조회를 인덱스로 손봐 데이터가 늘어도 빠르게 유지했습니다.
외부 쇼핑몰로 나가는 요청이 많은 서비스라, 그 부하와 불안정이 메인 API에 번지지 않도록 렌더·추출을 별도 모듈로 분리했습니다. 링크가 들어오면 봇 탐지를 피하는 헤드리스 브라우저 렌더가 페이지를 열고 추출 모듈이 상품 정보를 구조화해 메인 API에 넘기는 식으로, 무거운 외부 작업과 핵심 로직을 격리했습니다.
동시 요청이 몰려도 토큰 회전이 정확히 한 번만 일어나도록 판정을 Redis Lua로 원자화하고, 회전 직후 grace 창으로 정상 동시 갱신과 도난을 구분했습니다. 수정이 진짜 경쟁 상태를 막는지 8스레드 동시 호출 테스트로 검증했습니다.
백오피스가 외부에 열려 있는 만큼, '누가 들어올 수 있나'가 기능만큼 중요했습니다. Discord 슬래시커맨드로 접근을 열되 인터랙션을 Ed25519 서명으로 직접 검증하고, JDK 기본 구현의 raw 공개키 처리 문제를 피해 BouncyCastle로 검증하며 오래된 요청은 재생 공격으로 막아, 계정 없이도 운영자만 접근하게 만들었습니다.
외부 방문객을 초청한 데모데이에 사용자가 몰리며 앱이 멈칫하던 걸, Grafana로 GC를 추적해 원인을 찾았습니다. 컨테이너가 cgroup 메모리를 잘못 인식해 JVM이 Serial GC로 돌고 있었고, 힙만 늘리면 STW가 더 길어지는 구조라 G1으로 바꿔 멈춤을 없앴습니다.
Depromeet 18기에서 대상(1위)을 수상하고, 모의투자에서 최대 투자금을 유치했습니다.
기획부터 양대 스토어 심사 통과와 정식 출시까지 완주했고, 800명 이상이 가입해 쓰고 있습니다.
가장 크게 남는 건 개발 흐름 자체를 커맨드와 스킬로 자동화한 경험입니다. 이슈 생성과 보드 정리부터 작업·테스트, PR 생성과 리뷰어 지정, 리뷰 봇, 배포 승격, 운영 지표 조회까지 하나의 파이프라인으로 엮었습니다. PR을 만드는 스킬 하나만 서른 번 넘게 다듬었고, 반복되던 일을 흐름에서 걷어내면서 팀의 속도와 일관성이 눈에 띄게 올라갔습니다.
버그는 증상에서 멈추지 않고 근본 원인까지 파고들었습니다. 로그아웃은 동시성과 시간, DB 500은 트랜잭션 전파, 힙 경고는 GC 알고리즘처럼, 추측 대신 지표로 추적하고 문제를 재현해 원인을 좁혔습니다. 고친 뒤에는 수정을 일부러 되돌려 테스트가 실패하는지 확인해, 그 테스트가 진짜 문제를 잡는 장치인지까지 검증했습니다.
비개발 팀원이 개발자를 거치지 않고 공지·알림을 관리하고 지표를 확인할 수 있게 만든 백오피스는, 팀이 서비스를 더 주도적으로 다루는 도구가 됐습니다.
기능별 부하 테스트는 했지만, 서버 자체가 실제 동시 트래픽을 어디까지 감당하는지는 미리 밀어보지 못했습니다. 그 탓에 GC 설정 같은 서버 레벨의 한계가 데모데이 트래픽에서야 드러났습니다. 다음엔 개별 로직뿐 아니라 서버 전체를 부하로 먼저 검증하려 합니다.
보안과 사용성처럼 상충하는 요구는 'grace 창', 'TTL allowlist'처럼 시간·범위를 설계 변수로 두면 양립시킬 수 있다는 걸 배웠습니다. 또한 한계와 범위를 정직하게 명시하는 것이 팀과 사용자의 신뢰를 만든다는 것을 실감했습니다.











