다 됐는데 도메인 하나 없어서 못 닫은 하루
목차
오늘은 시작부터 세 가지 굵직한 흐름이 동시에 열려 있다는 걸 알고 있었다. kp-pay 통합인증 POPUP 전환, 파트너 삭제 연계 일괄탈퇴 구현, earnPoint 잠금 수술 2차분. 그 사이에 버그들이 끼어들었고, psy는 자동으로 오늘 치 콘텐츠를 만들어놨다.
커밋이 10개 쌓였는데 다들 서로 다른 맥락에서 나왔다. 하나를 깊이 파는 날이 있고, 여러 트랙이 동시에 돌아가는 날이 있는데 오늘은 확실히 후자였다. 집중력이 분산되는 날 특유의 피로가 저녁에 와서 느껴졌다.
도메인이 오면 테스트할 수 있다
jeju 프로젝트의 kp-pay 가상계좌 결제를 통합인증 POPUP 방식으로 전환하는 작업이 오늘 기술적으로 가장 신경이 많이 쓰인 부분이었다.
결제 PG 연동은 건드릴 때마다 손이 많이 간다. 특히 가상계좌 플로우는 카드 결제와 다르게 콜백 시점이 분리된다. 결제 요청 시점과 실제 입금 확인 시점이 따로 들어오기 때문에, POPUP 전환을 하면서 이 타이밍 처리를 새로 맞춰야 했다. 건드린 레이어가 네 개다 - charge.jsp, payment web 컨트롤러, wallet web, sqlmap 쿼리 매퍼.
charge.jsp를 열었을 때 기존 결제 방식별 분기가 여러 군데 흩어져 있었다. POPUP 전환이 어느 분기에 영향을 주는지 파악하는 게 구현 자체보다 오래 걸렸다. 이런 코드에서 "뭘 건드려야 하는지" 확인하는 데 쓰는 시간은 사람마다, 코드 구조마다 다른데, 오늘 charge.jsp는 좀 복잡하게 얽혀 있었다. 잘못 건드리면 입금 확인이 누락되는 류의 버그가 생기고, 그건 배포 후에야 드러나기 때문에 더 조심스럽게 된다.
sqlmap 쿼리 매퍼도 수정했다. 통합인증 방식에서 가맹점 인증 정보를 다루는 쿼리가 바뀌어야 해서, 조회와 업데이트 쿼리를 같이 손봤다.
개발 DB 세팅 SQL은 따로 기록으로 남겼다. UPDATE_20260813_kppay_dev_test_merchant.sql. 테스트 가맹점 정보를 개발 DB에 세팅하는 내용이다. 이런 데이터 세팅 쿼리를 파일로 남기는 이유는 명확하다 - 나중에 스테이징이나 운영 반영 때 "개발 환경에 어떤 데이터로 세팅했는지"를 기준으로 삼아야 할 순간이 반드시 온다. 기억에 의존하다가 틀리는 것보다 파일을 열면 되는 게 낫다.
Flutter bridge SKILL.md도 오늘 수정됐다. 앱 쪽 연동을 위한 인터페이스 계약을 정리하는 문서 작업이라, 코드 변경보다는 경계를 명확하게 만드는 작업에 가까웠다. 이런 문서가 있어야 나중에 앱 쪽이 어떻게 연동해야 하는지 물어볼 때 답을 빠르게 줄 수 있다.
그리고 구현이 다 됐는데 막혀 있는 게 하나 있다. -96 도메인 등록 대기. 통합인증 POPUP의 콜백 URL에 이 도메인이 물려야 하는데 아직 등록이 완료되지 않았다. 개발계 세팅은 완료됐고 코드도 나왔는데, 이 도메인 하나 때문에 실제 동작 테스트를 할 수 없는 상황이다.
기술적으로 할 수 있는 건 다 했고 외부 요인을 기다리는 구간이다. 이런 상태가 항상 묘하게 불편하다. 완료에 가깝지만 완료라고 쓸 수 없는 느낌. jeju 진행현황 문서도 오늘 기준으로 업데이트했다 - 개발계 세팅 완료, 도메인 등록 대기. 상태를 명확하게 기록해두는 게, 나중에 "이거 됐어요?" 질문에 답하는 데 쓰는 시간을 줄이는 가장 빠른 방법이라 생각하고 있다.
파트너 삭제가 이렇게 많은 레이어를 건드리는 기능이었나
파트너 삭제에 일괄탈퇴와 잔액 처리 모드를 붙이는 기능 구현이 오늘 코드 레이어가 가장 많이 열린 작업이었다.
비즈니스 요구 자체는 이해하기 어렵지 않다. 파트너가 삭제될 때, 그 파트너 소속 회원들을 어떻게 처리할 것인지 운영자가 선택할 수 있어야 한다. 세 가지 모드로 정의했다.
- 운영자귀속: 소속 회원들의 잔액을 플랫폼 운영자에게 귀속시킨다
- 소멸: 잔액을 소멸 처리한다
- 차단: 회원 계정을 차단 상태로 전환한다. 잔액은 건드리지 않는다
memberBalanceMode라는 파라미터로 세 케이스를 선택받는 구조다. 단순해 보이는데, 각 모드마다 처리 로직이 다르다. 운영자귀속은 settlement 쪽 처리가 엮이고, 소멸은 잔액 처리 로그를 남겨야 하고, 차단은 회원 상태값 변경이 수반된다. 세 케이스가 각자의 트랜잭션 경계를 가진다.
건드린 파일이 많다. partner web 컨트롤러, settlement web, batch/withdrawal, 유틸 클래스 세 개. 단일 기능 치고는 넓게 열린 편이다. batch/withdrawal을 건드린 건 기존 탈퇴 배치 로직을 파트너 삭제 시나리오에서 재사용하기 위해서였다. 새로 만드는 것보다 이미 검증된 로직을 이어쓰는 게 안정적이지만, 기존 배치 코드가 이 시나리오를 고려하고 만들어진 게 아니라서 연결점을 맞추는 작업이 필요했다.
유틸 클래스가 세 개 열린 건 공통 처리 로직이 분산돼 있다는 신호이기도 하다. 기능 완료 후 한번 정리 대상으로 보는 게 맞을 것 같은데, 오늘은 일단 기능 완료에 집중했다.
API 문서도 구현과 같은 날 업데이트했다. 파트너 삭제 API에 일괄탈퇴와 memberBalanceMode 파라미터를 반영하는 내용. 구현 후 문서 업데이트를 미루면 연동 쪽에서 혼선이 생기거나 나중에 따로 시간을 내야 한다. 당일에 같이 묶는 게 습관이 됐다.
모달을 다시 짜다가 한글이 깨졌다
파트너 삭제 기능과 연계해서 어드민 쪽 UI도 재설계했다. _member-balance-modal.jsp - 회원 잔액 처리 모달이다. 세 가지 모드를 선택할 수 있는 UI여야 하니까, 기존 모달 구조를 뜯어서 다시 짰다.
CSS가 여러 개 열렸다. common.css, notice.css, SUNRISE.css, 이커머스 PG 플랫폼 사이트 CSS. 모달 스타일이 사이트별 테마와 연결돼 있는 구조라, 공통 CSS만 바꾸면 끝나지 않는다. 사이트별 오버라이드 파일까지 다 확인하고 맞춰야 한다. 사이트가 늘어날수록 이 체크 목록도 늘어난다는 게 유지보수 비용을 키우는 지점인데, 매번 기능 구현에 쫓기다 보면 정리 타이밍이 안 나온다.
그리고 한글 깨짐이 있었다. 동적 include로 모달 JSP를 불러올 때 한글이 깨지는 문제였다. 렌더된 결과물에서 깨진 글자만 보이고, 어느 파일에서 문제가 나는지 바로 보이지 않아서 역추적해야 했다.
원인은 pageEncoding 선언 누락이었다. JSP 동적 include에서 포함되는 파일에 pageEncoding이 없으면, 컨테이너가 서버 기본 인코딩으로 처리한다. 그게 UTF-8이 아닌 환경이면 한글이 깨진다. 선언 하나 추가하면 끝나는 문제인데, 찾기까지 시간이 걸렸다.
알고 나면 허탈하다. JSP 파일 새로 만들 때 pageEncoding을 명시하는 게 습관처럼 박혀 있지 않으면 계속 같은 실수가 반복된다. 팀 공유 템플릿에 기본으로 넣어두거나, 파일 생성 시 체크하는 구조가 있으면 이런 류는 줄어든다. 오늘은 일단 고쳤고, 구조적인 정리는 나중으로 미뤘다. 이런 미룸이 쌓이면 결국 나중에 같은 삽질을 또 하게 된다는 걸 알면서도.
수익 요약 JSP도 같이 손봤다. total-summary.jsp의 KP열 버그와 FORFEIT 라벨 정정. KP열에서 수치가 잘못 표시되고 있었고, FORFEIT 라벨이 틀린 문자열로 나오고 있었다. 수치 오류는 운영팀이 어드민에서 데이터를 보고 판단할 때 영향을 줄 수 있어서 빠르게 잡아야 했다. FORFEIT 라벨은 표시만 잘못된 거라 급하진 않았지만, 고치는 김에 같이 정정했다.
교차검증이 4건을 잡았다
일괄탈퇴 구현을 마치고 Codex 교차검증을 돌렸더니 4건이 걸렸다. 요즘 작업 흐름이 구현하고, 검증 돌리고, 걸린 것 보정하는 사이클로 자연스럽게 자리를 잡았다.
4건은 유틸 클래스와 쿼리 매퍼에서 나왔다. 쿼리 매퍼는 엣지케이스 조건 처리 관련이었고, 유틸 쪽은 잔액 처리 시나리오에서 빠진 케이스가 있었다. 구현할 때 생각하지 못한 부분들이다. 수동 코드 리뷰만으로 이걸 다 잡으려면 시간이 많이 걸렸을 것이고, 놓쳤다면 운영에서 데이터 문제로 터질 가능성이 있는 류였다.
잔액 처리는 돈이 관련된 로직이라 틀리면 데이터 정합성 문제가 된다. 잘못 쌓인 데이터를 복구하는 게, 구현 버그 하나 고치는 것보다 몇 배 더 힘들다. 그런 의미에서 4건 보정이 사소해 보이지 않는다.
스톨 수술 C-1 2차
earnPoint 배치에서 lot 조회를 잠금 밖으로 끌어올리는 작업이다. 커밋 제목에 "스톨 수술 C-1 2차"라고 붙인 건, 이 작업이 연속 수술의 일부라는 걸 나중에도 추적할 수 있게 하기 위해서다.
문제는 지갑과 돈통에 대한 잠금 보유시간이었다. lot 조회가 잠금 내부에서 이루어지고 있었는데, 이 조회는 잠금 전에 해도 되는 작업이다. 잠금을 잡기 전에 조회를 먼저 해두면, 잠금을 실제로 필요한 구간에만 잡게 되고 보유시간이 줄어든다. 호이스팅이라는 말이 딱 맞는다.
개념은 단순한데, 안전하게 하려면 조회와 잠금 사이에 의존관계가 없는지, 잠금 전에 읽어온 데이터가 잠금 내부에서도 유효한지 따져야 한다. 잠금 전후로 상태가 바뀌는 케이스가 있으면 호이스팅이 오히려 정합성 문제를 만들 수 있어서, 이 부분을 꼼꼼하게 검토했다. 이게 구현보다 오래 걸리는 부분이다.
잠금 보유시간이 길면 다른 트랜잭션이 그 잠금을 기다리다가 스톨이 생긴다. 포인트 처리처럼 빈도가 높은 배치에서 이게 누적되면 성능 저하로 이어진다. 바뀐 파일이 두 개인데, 이런 수술은 파일 수로 작업량을 가늠하면 안 된다. 수술 시리즈가 몇 차까지 갈지는 프로파일링 결과를 보고 결정할 것 같다.
psy는 조용히 제 몫을 했다
chore(psy): daily auto-generated tests (2026-08-13). 심리테스트 콘텐츠 세 개가 자동 생성됐다. midnight-elevator-b4, rejection-style, starseed-origin. 커버 이미지 webp 파일과 콘텐츠 JSON이 각각 세 개씩.
psy 쪽 자동 파이프라인이 에러 없이 오늘 치 분량을 만들어놨다. 다른 작업이 많은 날에 이쪽이 조용히 돌아가고 있다는 게 안심이 된다. 신경 쓰지 않아도 커밋이 쌓인다는 게, 파이프라인이 제대로 살아있다는 신호이기도 하다.
돌아보면 오늘은 돈이 오가는 코드를 두 군데 건드린 날이었다. kp-pay 결제 전환과 잔액 처리 모드. 이런 날은 구현 후에도 머릿속이 좀 돌아간다. 빠진 케이스가 없나, 콜백 타이밍이 맞나. kp-pay 전환은 도메인이 들어와야 테스트를 돌릴 수 있고, 잔액 처리는 교차검증 4건을 보정했으니 그나마 찝찝함이 덜하다.
pageEncoding 한글 깨짐 같은 건 찾고 나면 허탈한 류인데, 찾기 전까지 쓴 시간이 아깝다는 생각이 든다. 이런 게 반복되지 않으려면 구조적으로 막아야 하는데, 그 타이밍이 언제일지는 모르겠다.
댓글 0
첫 댓글 달아줘.