시험 서버에서 결제를 시험 모드로 돌리려고 config.php 를 바꿨는데 실제 결제로 넘어갔다.
Table of contents
Open Table of contents
읽는 코드가 없었다
grep -rn 으로 그 값을 읽는 곳을 찾으니 정의한 한 줄뿐이었다. 결제 모드를 가르는 코드는 ENVIRONMENT 를 보고 있었다.
if (ENVIRONMENT === 'production') {
$url = PAY_REAL_URL;
} else {
$url = PAY_TEST_URL;
}
전에는 설정 항목으로 갈랐다가 나중에 환경 값으로 바꾸면서 옛 항목을 안 지운 것으로 보였다. 설정 항목이 있는데 아무도 안 읽으면 그 값은 장식이다.
지워지지 않은 설정은 지워진 것보다 나쁘다. 없으면 찾아보고 없다는 것을 알지만 있으면 그게 동작한다고 믿는다.
안 읽히는 설정을 찾았다
설정 파일의 키를 전부 뽑아서 코드에서 읽는 자리가 있는지 대조했다.
foreach (array_unique($keys) as $k) {
$hit = shell_exec("grep -rl \"item('{$k}')\" " . APPPATH . " | wc -l");
if ((int)$hit === 0) {
echo "안 읽힘: {$k}\n";
}
}
14개가 나왔고 세 부류였다. 프레임워크가 내부에서 읽는 것과 config_item 처럼 다른 방식으로 읽는 것은 정상이고 아무도 안 읽는 것이 7개였다. 지우면서 각 값이 원래 무엇을 하려던 것인지 확인해 기능 자체가 빠진 것이 있는지도 같이 봤다.
반대 방향도 봤다
코드가 읽는데 설정에 없는 키도 찾았다. 두 개가 나왔는데 이쪽이 더 나빴다.
if ($this->config->item('use_coupon')) { // 항상 거짓
...
}
없는 키를 읽으면 FALSE 가 오고 그것은 조건문에서 거짓이다. 쿠폰 기능이 통째로 안 돌고 있었는데 오류가 없으니 아무도 몰랐다. 안 읽히는 설정은 아무 일도 안 일어나지만 없는 설정을 읽는 코드는 기능을 조용히 끈다.
없는 키가 드러나게
설정을 읽는 함수를 감싸서 없는 키를 만나면 로그에 남기게 했다. 처음에는 이렇게 썼다.
$v = $ci->config->item($key);
if ($v === null) { log_message('error', "설정 없음: {$key}"); }
이 조건이 한 번도 참이 안 됐다. Config::item() 소스를 열어 보니 없는 키에 return FALSE; 를 하고 있었다. null 이 아니다.
그래서 키가 실제로 있는지를 따로 보게 고쳤다.
function cfg($key)
{
$ci = &get_instance();
$all = $ci->config->config;
if (!array_key_exists($key, $all)) {
log_message('error', "설정 없음: {$key}");
return null;
}
return $all[$key];
}
FALSE 로 설정한 값과 아예 없는 키가 이렇게 갈린다. 앞의 코드로는 둘이 똑같이 보였다.
원래 문제로 돌아가서 시험 서버의 ENVIRONMENT 가 왜 운영으로 돼 있었는지도 확인했다. 운영 설정을 복사해 만들면서 index.php 의 그 한 줄만 바꾸는 것을 빼먹은 것이었다.
서버를 만들 때 확인할 항목을 적어 두고 운영이 아닌 환경에서는 첫 화면에 ENVIRONMENT 를 표시하게 했다. 어느 서버인지가 눈에 보이면 같은 실수를 그 자리에서 알아챈다.
정리
- 설정 항목이 있어도 읽는 코드가 없으면 아무 효과가 없다
- 지워지지 않은 설정은 지워진 것보다 나쁘다
grep -rl로 키 목록과 코드를 대조해 안 읽히는 항목을 찾는다- 반대로 코드가 읽는데 설정에 없는 키도 찾는다
config->item()은 없는 키에FALSE를 준다 — 조건에서 거짓이다- 그래서
=== null로 검사하면 한 번도 안 걸린다 array_key_exists로 봐야FALSE설정과 없는 키가 갈린다ENVIRONMENT를 화면에 표시하면 어느 서버인지가 눈에 보인다