[개드립 소설] 7. 양팔저울
7-1장: 양팔저울
그 주간 회의 이후, 사무실에는 두 개의 시간이 흐르고 있었다.
오전 10시. 결제 담당 사원이 문 수석의 자리로 왔다.
"수석님, 이 건은 결제 완료 후 부분 환불인데, PG사 정산이 아직 안 끊겨서요. 그 상태에서 쿠폰 재발급이 가능한 건가요?"
"안 됩니다. 정산 확정 전에는 환불 상태가 열려 있어서, 쿠폰을 먼저 처리하면 이중 차감됩니다. 정산 확정 후에 순서대로 가야 합니다."
"그러면 이번 주 금요일 정산 마감 이후에 처리해도 될까요?"
"금요일 오후 가능합니다. 그 전에 업무 흐름을 명확히 할 필요가 있을 것 같습니다. 이따 4시에 회의 잡아도 될까요?"
---
같은 시각, 사무실 반대편에서 마케팅팀 이현주의 메신저가 떴다.
"성 부장님, 오후 1시에 반짝 세일 들어가요! 최근 30일 비구매자만 할인율 15%로 걸어주세요!"
치훈이 SQL 창을 열었다. UPDATE 문 한 줄.
"이 대리, 됐어."
이현주가 치훈의 모니터를 흘끗 보며 말했다.
"부장님 영어 잘하시나 봐요. 전 봐도 하나도 모르겠다."
"영어긴 무슨. 컴퓨터한테 명령 내리는 거야. 이러면 데이터를 바꿀 수 있어."
시계를 보니 10시 8분이었다.
---
오후. 결제 담당 사원이 다시 문 수석을 찾았다.
"수석님, 아까 그 환불 건이랑 엮인 정기 결제가 하나 더 있는데요. 이것도 금요일에 같이 처리하면 되나요?"
문 수석이 화이트보드의 상태 흐름을 확인하며 대답했다.
"이건 별개입니다. 정기 결제는 환불 건과 상태가 독립이라 먼저 처리해도 됩니다."
옆에서 듣고 있던 결제팀 세진이 노트에 흐름도를 옮겨 적으며 물었다.
"그러면 이 건은 내일 오전에 제가 올려도 되나요?"
문 수석이 고개를 끄덕였다.
"단위 테스트 먼저 추가하고, 환불-정기결제 간 상태 전이 커버리지 확인되면 스테이징에 올리세요."
세진이 노트에 적으며 되물었다.
"환불하다가 정기 결제가 끼어드는 경우를 전부 만들어보라는 거죠?"
"네. 상태 전이 커버리지라고 합니다."
"엣지 케이스는 기존 픽스쳐로 커버가 될까요?"
"안 됩니다. 이번 건은 새로 만드세요. 실패하는 경우와 성공하는 경우를 분리해서, 부분 환불 중 정기 결제가 걸리는 시나리오를 반드시 넣어야 합니다."
"시나리오면 유즈케이스죠?"
"네. 테스트 케이스로 만들어서 픽스쳐에 넣으세요."
세진이 노트북 화면을 다시 훑으며 정리했다.
"그러면 이번에 바뀌는 건 결제 규칙 쪽이니까, 테스트도 거기만 확인하면 되고... PG 연동이나 DB 쪽은 안 건드리니까 빼도 되는 거죠?"
"네. 도메인 레이어만 검증하면 됩니다."
세진이 키보드에서 손을 떼며 말했다.
"업무 로직이 한 곳에 모여 있으니까 고칠 때도 거기만 보면 되는 거군요. 이게 DB 프로시저에 들어가 있었으면 어디를 테스트해야 하는지도 몰랐을 것 같아요."
"그게 도메인 격리의 효과입니다."
세진이 중얼거렸다.
"도메인 격리... 그러니까 업무 로직을 DB나 외부 시스템이랑 섞지 않고 따로 모아두는 거니까, 나중에 DB가 바뀌어도 업무 로직은 안 건드려도 되는 거죠? ORM과 도메인을 이렇게 분리하는 거네요... 인프라로 ORM을 구분하는 거구나..."
"네. 결제 이력은 누적 속도가 빠릅니다. 나중에 NoSQL로 확장하더라도 업무 로직은 그대로 쓸 수 있어야 합니다."
"아... 의존성 방향이... 도메인은 전혀 DB에 대해 모르니까, 몽고DB든 SQLite든 바뀌어도 업무 규칙이 위치한 도메인 코드는 전혀 변경이 없는 거네요?"
문 수석이 고개를 끄덕였다.
같은 오후, 이현주가 캔커피를 들고 치훈의 자리로 왔다.
"성 부장님~ 아까 30일이라고 했는데 2주일로 바꿔주실 수 있어요?"
치훈이 커피를 받으며 물었다.
"2주일? 14일? 아님 월요일 기준?"
이현주가 멈칫했다.
"...확인하고 올게요!"
3분 뒤 돌아왔다.
"14일이래요!"
치훈이 SQL 창에서 화살표 키를 눌러 아까 쿼리를 불러왔다.
"잠만, 모수 좀 확인하고..."
숫자를 고치고 옆자리 후배에게 모니터를 돌렸다.
"이거 건수 맞지?"
후배가 고개를 끄덕이더니 덧붙였다.
"하나 더요, 저번 달 12일 12시 이전 데이터가 포함됐는지 확인하면 됩니다."
치훈이 실행 버튼을 누르기 전에 이현주를 돌아봤다.
"이 대리, 근데 14일 맞아? 팀장님한테 다시 확인한 거야?"
이현주가 웃었다.
"아 맞을걸요?"
치훈이 손을 뗐다.
"맞을걸요 말고, 팀장님 확인하고 다시 알려줘."
이현주가 후다닥 자리로 돌아갔다. 잠시 후 메신저가 왔다. "14일 맞습니다!"
치훈이 실행 버튼을 눌렀다.
"이 대리, 결과 나왔으니까 대상자 목록 한번 확인해봐."
오후 4시. 문 수석이 아까 잡아둔 회의가 시작됐다. 화이트보드에는 결제 상태 흐름도가 그려져 있었다. 결제 담당 사원과 세진이 나란히 앉았다.
"현재 부분 환불 처리할 때 PG사에서 정산이 끝났는지를 사람이 직접 확인하고 있습니다. 이 부분을 자동화해야 합니다."
세진이 물었다.
"PG에서 정산 끝났다는 신호가 오면, 그때 환불을 진행하게 만드는 거죠?"
"네. 그 신호를 도메인 이벤트로 발행합니다."
세진이 노트북에 적으며 되물었다.
"도메인 이벤트... 그러니까 '정산 끝났다'는 알림을 시스템 안에서 자동으로 보내는 거죠?"
"네. 그리고 그 알림이 오기 전에는 환불 완료로 넘어갈 수 없도록 막아야 합니다."
"막는다는 건... 순서를 잠가두는 거죠? 아까 말씀하신 가드?"
"네."
결제 담당 사원이 노트북을 열며 말했다.
"그러면 기존에 만들어둔 환불 테스트도 전부 다시 돌려봐야겠네요. 오래 걸릴 것 같은데..."
"도메인 레이어 전체 테스트 수행해 보세요."
결제 담당 사원이 노트북에서 테스트를 실행했다. 화면에 초록색 막대가 쭉 채워졌다.
"...1초도 안 걸렸네요?"
"외부 인프라 없이, DB도 없이, 업무 로직만 검증하는 겁니다."
문 수석이 세진을 보며 말했다.
"무슨 말인지 알겠죠?"
세진이 고개를 끄덕였다.
"아까 그 도메인 격리... DB에서 조회하지 않고, 테스트에 필요한 실제 데이터를 픽스쳐에 미리 넣어두니까 이렇게 빠른 거군요."
세진이 초록색 막대를 다시 보며 중얼거렸다.
"아... 전 처음에 왜 엔티티를 그냥 도메인 모델로 안 쓰나 했거든요. 매핑하느라 소스도 늘어나고 지루해 죽는 줄 알았는데... 이래서 분리한 거네요."
"맞습니다. 기존 테스트가 깨지지 않는 것까지 확인되어야 합칠 수 있습니다."
회의는 40분이 걸렸다.
같은 시각, 치훈의 자리에서는 이현주가 대상자 목록을 들고 와 있었다.
"부장님, 확인했는데 3명이 중복이에요."
치훈이 모니터를 돌려봤다.
"어디? 아, 이거 탈퇴 회원이 안 빠진 거네."
WHERE절에 조건 하나를 추가했다. 실행. 3초.
"됐어. 다시 뽑아봐."
이현주가 목록을 확인하고 엄지를 올렸다.
다음 날 오전. 결제팀 세진이 슬랙에 메시지를 올렸다.
"정기결제 건 테스트 올렸습니다. 어제 말씀하신 유즈케이스 테스트용 픽스쳐 3건 추가했고, 기존 테스트 전부 통과 확인했습니다."
문 수석이 답했다.
"리뷰 후 합치겠습니다."
30분 뒤, 세진이 다시 올렸다.
"수석님, 리뷰 코멘트 반영했습니다. 그런데 어제 회의에서 잡은 가드 조건이 하나 빠져 있었습니다. 순서 잠금이 안 걸린 경우가 있어서 테스트에서 실패가 떴습니다."
세진이 한 박자 멈추었다.
"테스트가 잡아준 거예요. 이거 그냥 올렸으면 실서비스에서 터졌습니다."
문 수석이 잠시 멈추었다.
"그 부분은 우리가 놓친 겁니다. 테스트가 없었으면 고객 결제에서 장애가 났을 겁니다."
세진이 노트북 화면을 보며 말했다.
"이런 게 엣지 케이스가 숨어 있는 부분이군요. 어제 회의에서 잡은 흐름도만 믿고 넘어갈 뻔했는데... 마케팅 쪽으로 따지면 천만 원짜리 보상 쿠폰 될 뻔했네요."
"네. 흐름도에 그려진 건 정상 경로입니다. 문제는 항상 정상 경로 사이에 끼어드는 예외에서 터집니다. 그걸 미리 잡는 게 테스트의 역할입니다."
세진이 잠깐 멈추더니 말했다.
"전 책으로 공부할 때는 막연하게 테스트 케이스가 예외를 알아서 검증해주는 줄 알았어요."
"아닙니다. 우리가 정한 규칙을 검증해야 하고, 그것을 테스트 코드로 표현하는 겁니다. 우리가 안 하면 그것은 누락입니다."
세진이 고개를 끄덕이며 적었다.
"그러면 지금 잡은 것 말고도 비슷하게 숨어 있는 게 더 있을 수 있다는 거죠?"
"그렇습니다. 이 예외를 처리하려면 기존 픽스쳐로는 부족할 겁니다."
세진이 노트북 화면을 넘기며 바로 말했다.
"새로 만들겠습니다. 어제 만든 것처럼 유즈케이스 형태로 잡으면 되죠? 금방이면 되겠는데요?"
"네. 이틀 정도 투자해서 확실하게 잡읍시다. 예상보다 하루 지연될 것 같지만, 결제팀에는 제가 지연 협조 요청하겠습니다."
세진이 고개를 끄덕이며 노트북을 열었다.
화면에는 결제 도메인의 코드가 띄워져 있었다.
세진은 그 코드를 한참 들여다보았다.