한 처리 흐름이 눈에 띄게 느려서 어디서 시간을 쓰는지 봤다. 특별히 무거운 계산은 없었고 같은 행을 세 번 SELECT 하고 있었다.
Table of contents
Open Table of contents
세 번 가져온 같은 자료
grep 으로 흐름을 따라가 보니 process 가 orderNo 만 넘기고 각 단계가 다시 find 하고 있었다.
public function process(int $orderNo): void {
$this->validate($orderNo);
$this->calculate($orderNo);
$this->notify($orderNo);
}
private function validate(int $orderNo): void {
$order = $this->repo->find($orderNo); // 1번째
}
private function calculate(int $orderNo): void {
$order = $this->repo->find($orderNo); // 2번째
}
private function notify(int $orderNo): void {
$order = $this->repo->find($orderNo); // 3번째
}
번호를 받은 쪽은 그 번호로 다시 find 하는 것 말고 할 수 있는 것이 없었다.
validate 든 calculate 든 따로 보면 아무 문제가 없다는 것이 이 구조의 성질이었다. 자기가 필요한 것을 find 해서 쓸 뿐이므로 한 함수만 열어서는 안 보인다.
번호 대신 객체를 넘기기
그래서 process 가 한 번 find 하고 그 order 를 넘기게 고쳤다.
public function process(int $orderNo): void {
$order = $this->repo->find($orderNo);
$this->validate($order);
$this->calculate($order);
$this->notify($order);
}
세 번 나가던 SELECT 가 한 번으로 줄었다.
넘기는 것을 바꾸면 각 단계가 자기 것을 스스로 챙기지 않으므로 앞 단계에 의존하게 된다. 그 의존은 흐름을 읽을 때 드러나므로 감춰지는 것보다 낫다고 봤다.
중간에 바꾼 값은 돌려준다
중간 단계에서 값을 UPDATE 하는 자리가 있었는데 이때 넘겨받은 것과 MySQL 의 값이 갈릴 수 있었다. 바꾼 뒤에도 넘겨받은 것을 그대로 다음으로 보내면 예전 값이 흘러간다.
그래서 값을 바꾸는 단계는 바꾼 결과를 돌려주게 했다. 넘기는 방식으로 갈 때는 어디서 값이 바뀌는지를 함께 정해 둬야 했다.
넘길 것이 많아질 때
단계를 지나면서 넘길 것이 늘어나 인자가 다섯 개가 넘어가는 자리가 생겼다. 그러면 함수 서명이 길어지고 순서를 헷갈리기 시작한다.
그런 자리는 한 묶음으로 만들어서 하나만 넘기게 했다. 묶는 기준은 같은 흐름에서 함께 흘러가는 것들로 잡았다.
조회 횟수 세기와 담아 두기
고치고 나서 같은 행을 몇 번 SELECT 하는지 세는 것을 개발 환경에서 켜 뒀다. 다른 흐름에서도 같은 모양이 있는지가 그 수로 보인다.
한 번 가져온 것을 캐시에 담아 두는 방법도 있었지만 그것은 나중으로 미뤘다. 캐시에 담으면 중복 SELECT 가 안 보이게 되므로 흐름을 드러내는 쪽을 먼저 하는 것이 순서였다.
정리
- 번호만 넘기면 각 단계가 다시
SELECT한다 - 각 함수만 보면 문제가 없어 보인다
- 한 번 가져와서 다음 단계로 넘긴다
- 중간에 값을 바꾸면 바꾼 결과를 돌려준다
- 넘길 것이 많아지면 한 묶음으로 만든다
- 같은 행을 몇 번
SELECT하는지 센다 - 캐시는 중복
SELECT를 감춘다 - 흐름을 드러내는 쪽을 먼저 한다