Skip to content
isdnetworks
Go back

들어오는 자리와 실제로 일하는 자리가 달랐다

특정 화면이 느려서 고치려 했는데 grep 으로 어느 코드가 도는지 찾는 데만 반나절이 걸렸다.

Table of contents

Open Table of contents

주소로는 못 찾았다

주소는 이것이었다.

/order/list

라우팅 파일을 봤다.

$route['order/list'] = 'order/index';

OrderController::index 로 간다고 돼 있어 열어 봤다.

public function index() {
    $this->load->library('legacy_bridge');
    $this->legacy_bridge->dispatch();
}

화면을 그리는 코드가 없고 다른 데로 넘기기만 한다. dispatch 를 따라갔다.

public function dispatch() {
    $path = $this->uri->segment_array();
    require_once APPPATH . '../legacy/' . implode('/', $path) . '.php';
}

주소 조각으로 파일 경로를 만들어 그것을 읽고 있었다. 실제 코드는 legacy/order/list.php 에 있었다.

경로가 문자열로 조립되니 검색으로는 안 나온다. 주소와 실제로 일하는 자리가 두 단계 떨어져 있었다.

grep 으로 order/list 를 찾으면 $route 한 줄만 나온다. 그 뒤로는 implode 가 만든 문자열이라 검색어가 코드에 없다.

찾는 방법이 없는 것이 아니라 grep 이라는 방법이 안 통하는 자리였고 그것을 아는 데 반나절을 썼다.

원인 — 옮기는 중인 구조

svn log 로 왜 이렇게 돼 있는지를 보니 옛 시스템을 새 프레임워크로 옮기는 중이었다. 옮긴 것은 Controller 로 가고 안 옮긴 것은 legacy 로 넘긴다.

중간 상태라서 그런 것이고 그 자체는 이해할 수 있었다. 한 번에 다 옮기는 것이 더 위험하다.

legacy 284개를 한꺼번에 옮기면 무엇이 깨졌는지 못 가리고 하나씩 옮기며 공존하는 것이 맞는 선택이다.

다만 공존하는 동안 읽는 사람이 헷갈리는 비용이 붙는데 그것을 아무도 계산해 두지 않았다.

문제는 어느 것이 옮겨졌는지 알 방법이 없다는 것이다. 그것을 알려면 매번 dispatch 를 읽어야 했다.

목록을 만들었다

먼저 규모부터 셌다.

$ ls legacy/**/*.php | wc -l
284

옛 파일이 284개이고 컨트롤러는 62개였다. 어느 주소가 어디로 가는지를 표로 만들었다.

/order/list      legacy/order/list.php
/order/detail    OrderController::detail   (2017-05 이관)
/product/list    legacy/product/list.php
...

만드는 데 하루가 걸렸지만 그 뒤로 찾는 시간이 없어졌다. 다만 새로 옮길 때마다 이 표가 낡는다.

표를 만들어 두면 다음 사람이 그 하루를 안 쓰는데 표가 틀리기 시작하면 없는 것보다 나쁘다.

틀린 표를 믿고 엉뚱한 파일을 열면 처음부터 다시 찾게 되고 낡는 속도가 빠르면 만들 이유가 없다.

조치 — 어디로 갔는지 응답에 남기기

표가 낡는 문제를 코드가 스스로 말하게 해서 풀었다.

if (ENVIRONMENT !== 'production') {
    header('X-Handler: legacy/' . $path);
}

X-Handler 헤더에 실제 처리 자리를 담는다.

$ curl -sI http://dev/order/list | grep X-Handler
X-Handler: legacy/order/list.php

주소를 하나 열어 보면 어디서 처리되는지가 나오므로 표를 안 봐도 된다. 운영에서는 안 나오게 했는데 안쪽 구조를 밖에 알릴 이유가 없었다.

표는 사람이 갱신해야 하고 헤더는 코드가 바뀌면 저절로 따라온다. 낡을 수 있는 것과 없는 것의 차이였다.

로그에도 남겼다

느린 요청을 찾을 때도 같은 값이 필요했다.

log_message('info', sprintf('%s handler=%s time=%.3f', $uri, $handler, $elapsed));

handlertime 을 한 줄에 남긴다.

/order/list handler=legacy/order/list.php time=4.221
/order/detail handler=OrderController::detail time=0.182

느린 것이 옛 파일 쪽에 몰려 있는지 옮긴 쪽에도 있는지가 보였다. 며칠 보니 옛 파일 쪽이 대부분이었다.

이것이 옮기는 순서를 정할 근거가 됐다. 느린 것부터 옮기면 옮기는 작업의 이득이 빨리 나타난다.

옮기는 작업은 기능이 안 늘어 값을 보이기 어려운데 느린 화면이 빨라지면 그 값이 눈에 보인다.

어느 것을 먼저 옮길지가 그 값을 언제 보일지를 정했고 handler 한 줄이 그 근거를 줬다.

주의 — 옛 파일 안의 또 다른 분기

옛 파일을 열었더니 거기서 또 나뉘었다.

include '../common/order_query.php';
if ($_GET['type'] === 'excel') { include '../export/order_excel.php'; }

읽어 들이는 파일이 또 있고 조건에 따라 다른 파일을 읽는다. 이런 것은 표로 못 만들고 실행해 봐야 안다.

register_shutdown_function(function () {
    if (ENVIRONMENT !== 'production') {
        log_message('debug', '읽은 파일: ' . implode(', ', get_included_files()));
    }
});

get_included_files 로 요청이 끝날 때 무엇을 읽었는지를 남겼다. 짐작이 아니라 실제로 실행된 경로가 나온다.

조건 분기가 있는 곳에서는 정적인 목록이 통하지 않는다. 무엇이 실행됐는지를 남기는 쪽이 정확했다.

판단 기준 — 옮기는 순서

전부 옮기는 데 오래 걸리므로 순서가 필요했다.

1. 느린 것
2. 자주 고치는 것
3. 새 기능이 붙을 것
4. 나머지

자주 고치는 것을 옮기면 그 뒤로 고치기가 쉬워진다. 어느 파일을 자주 고치는지는 이력에서 셌다.

$ svn log -v -r {2017-03-01}:HEAD legacy/ | awk '/^   M/ {print $2}' | sort | uniq -c | sort -rn | head

순서를 안 정하면 손이 가는 것부터 옮기게 되는데 그것이 반드시 중요한 것은 아니다.

정리


Share this post on:

Previous Post
도우미 함수인 줄 알았는데 외부를 부르고 있었다
Next Post
세지 않아도 되는 것을 세고 있었다