관리자 화면에 메뉴를 하나 추가해 달라는 요청을 받고 grep -rn 으로 코드를 뒤졌는데 안 나왔다.
Table of contents
Open Table of contents
자료에 있었다
views/ 의 화면 코드는 목록을 받아 그리기만 하고 있었다. 그 목록이 어디서 오는지 따라가니 admin_menu 표였다. parent_no 로 계층을 만들고 sort_no 로 순서를 정하는 구조다.
CREATE TABLE admin_menu (
menu_no INT NOT NULL AUTO_INCREMENT,
parent_no INT NULL,
name VARCHAR(50) NOT NULL,
url VARCHAR(200) NULL,
sort_no INT NOT NULL DEFAULT 0,
use_yn CHAR(1) NOT NULL DEFAULT 'Y',
PRIMARY KEY (menu_no)
);
메뉴가 자료였으므로 추가하려면 INSERT 한 줄이면 됐다. 코드에 없는 것을 코드에서 찾으면 안 나온다. 무엇이 코드에 있고 무엇이 자료에 있는지를 먼저 확인해야 했다.
자료로 둔 이유
왜 이렇게 했는지 물어보니 둘이었다. 메뉴마다 필요한 권한이 있어 사람에 따라 다르게 보여야 하고, 새 기능을 열고 닫는 것을 배포 없이 해야 한다. 둘 다 코드로 하면 배포가 필요한데 자료면 화면에서 한다.
편한 대신 따라오는 것도 있었다. 코드만 봐서는 어디에 무엇이 있는지 모른다. 없는 url 을 넣어도 저장된다. sort_no 와 parent_no 가 자료라 꼬일 수 있다. 부모가 없는 자식이나 순환 참조 같은 것이다.
검사를 넣었다
메뉴를 저장할 때 ucfirst 로 컨트롤러 파일명을 만들어 그 화면이 실제로 있는지 확인하게 했다.
$file = APPPATH . 'controllers/' . ucfirst($controller) . '.php';
if (!file_exists($file)) {
return '해당 주소의 화면이 없습니다: ' . $url;
}
file_exists 로 보는 것이라 완벽하지는 않지만 오타는 잡힌다. 저장은 되고 눌러야 404 가 나는 것보다 낫다.
계층도 확인했다. 자기 자신이나 자기 자손을 parent_no 로 지정하는 것을 막았다.
순환이 생기면 메뉴를 그리는 재귀 함수가 안 끝난다. 안 끝나면 어떻게 되는지 직접 돌려 보니 PHP 는 재귀 깊이 자체에 제한을 두지 않고 스택이 쌓이다 memory_limit 에 걸려 Allowed memory size exhausted 로 죽는다. 화면은 빈 채로 끝나고 Allowed memory size exhausted 는 error_log 에만 남는다.
구조를 보는 화면도 만들었다. 코드를 읽어도 안 나오니 어딘가에서는 보여야 했다.
상품관리
├ 상품목록 /product/list
├ 상품등록 /product/write
└ 분류관리 /category/list
주문관리
├ 주문목록 /order/list
└ 정산 /settle/list [사용안함]
url 을 같이 보여 주니 어느 코드가 어느 메뉴인지 대응이 된다. use_yn 이 N 인 메뉴도 표시만 다르게 해서 남겼다. 없는 것과 꺼진 것은 다르다.
양방향으로 대조했다
admin_menu 에 없는 화면이 있는지도 봤다. controllers/ 의 파일 이름을 basename 과 strtolower 로 뽑아 url 목록과 맞댔다. 자료에서 코드로만 확인하면 자료에 적힌 것이 맞는지만 알 수 있고 반대쪽은 안 나온다.
foreach ($files as $f) {
$name = strtolower(basename($f, '.php'));
$found = false;
foreach ($urls as $u) {
if (strpos($u, '/' . $name . '/') === 0) { $found = true; break; }
}
if (!$found) echo "메뉴 없음: {$name}\n";
}
7개가 나왔고 셋은 메뉴를 안 만든 것이었으며 넷은 안 쓰는 화면이었다. 어느 쪽 목록에만 있는지를 양방향으로 봐야 빠진 것과 남은 것이 다 드러난다.
정리
- 화면 구성이
admin_menu에 있으면grep으로 뒤져도 안 나온다 - 무엇이 코드에 있고 무엇이 자료에 있는지 먼저 확인한다
- 자료로 두는 이유는 권한별 노출과 배포 없는 변경이다
- 대신 코드만 봐서는 구조를 모르고
url오타가 안 걸린다 - 저장할 때
file_exists로 그 화면이 실제로 있는지 확인한다 parent_no순환을 막는다 — 재귀가memory_limit에 걸려 죽는다- 구조를 보여 주는 화면을 만들고
url을 같이 보여 준다 admin_menu와controllers/를 양방향으로 대조한다