Skip to content
isdnetworks
Go back

이력 문장이 제각각이라 읽기 어려웠다

이력 화면을 만들어 두고 몇 달 지나 열어 봤더니 무엇이 왜 바뀌었는지 알 수 있는 줄이 하나도 없었다.

가격 변경
관리자가 수정함
12000 -> 13200
프로모션 적용
상품 정보 업데이트

Table of contents

Open Table of contents

자리마다 다르게 적고 있었다

grep -rn 으로 이력을 남기는 코드를 찾으니 여덟 군데였고 형식이 전부 달랐다.

$this->history->add($no, '가격 변경');
$this->history->add($no, '관리자가 수정함');
$this->history->add($no, "{$old} -> {$new}");
$this->history->add($no, '프로모션 적용');

값만 적은 것과 행위만 적은 것과 사유만 적은 것이 섞여 있었다. add() 가 문자열 하나만 받으니 부르는 쪽이 각자 편한 대로 채운 결과였다.

형식을 정하고 인자로 강제했다

한 문장에 들어갈 것을 먼저 정했다. 원인과 대상과 행위와 전후 값 네 가지다. 하나라도 빠지면 나중에 읽을 수 없다.

프로모션 적용으로 판매가를 변경했다 (12,000 → 13,200)
관리자 수정으로 재고를 조정했다 (15 → 20)
입점사 요청으로 판매를 중지했다 (판매중 → 판매중지)

형식만 정하고 문자열을 계속 받으면 또 제각각이 된다. entityentity_nofieldoldnewreason 을 인자로 나눠 받게 바꿨다. manager_idreg_date 는 함수 안에서 채운다.

public function add($entity, $entityNo, $field, $old, $new, $reason)
{
    $label = self::label($field);
    $msg = sprintf('%s로 %s을(를) 변경했다 (%s → %s)',
                   $reason, $label, self::fmt($field, $old), self::fmt($field, $new));

    $this->db->insert('history', [
        'entity'    => $entity,
        'entity_no' => $entityNo,
        'field'     => $field,
        'old_value' => $old,
        'new_value' => $new,
        'reason'    => $reason,
        'message'   => $msg,
        'manager_id'=> $this->auth->id(),
        'reg_date'  => date('Y-m-d H:i:s'),
    ]);
}

sprintf 로 만든 messagehistory 에 넣지 않았다. old_valuenew_value 도 컬럼으로 따로 담았다. 문장만 있으면 GROUP BY field 로 셀 수 없다.

reason 은 기본값 없는 필수 인자로 뒀다. 비었으면 insert 전에 오류가 나게 했다. 사유를 못 적을 상황이면 그건 이력에 남길 일이 아니거나 사유를 정해야 하는 상황이다. 자동 처리는 사람이 적을 수 없으니 const 로 고정 사유를 뒀다. REASON_SYNCREASON_BATCHREASON_ADMINREASON_API 넷이다.

const REASON_SYNC   = '자동 동기화';
const REASON_BATCH  = '정기 배치';
const REASON_ADMIN  = '관리자 수정';
const REASON_API    = '외부 연동';

사람이 읽는 말로 바꿨다

값과 컬럼 이름이 저장된 그대로 화면에 나왔다. use_ynY 에서 N 으로 바뀌었다는 줄은 읽을 수 없다.

private static function fmt($field, $v)
{
    static $maps = [
        'status'    => ['sale' => '판매중', 'stop' => '판매중지', 'soldout' => '품절'],
        'use_yn'    => ['Y' => '사용', 'N' => '미사용'],
    ];
    if (isset($maps[$field][$v])) return $maps[$field][$v];
    if (in_array($field, ['price', 'qty'], true)) return number_format($v);
    return $v;
}

금액에는 number_format 으로 쉼표를 넣고 코드는 이름으로 바꾼다. fmt() 안의 maps 가 그 대응표이고 컬럼 이름도 label() 로 같은 방식을 뒀다. 대응표에 없으면 원래 이름을 그대로 쓰게 해서 새 컬럼이 생겨도 안 죽게 했다. 다만 없는 것이 나오면 log_message 로 남겼다. 표에 추가해야 할 것이 그렇게 드러난다.

안 바뀐 것과 이미 쌓인 것

수정 버튼만 눌러도 이력이 여덟 줄씩 쌓이고 있었다. 아무것도 안 바꿔도 필드 수만큼 생긴다.

foreach ($fields as $f) {
    if ((string)$before[$f] === (string)$after[$f]) continue;
    $this->history->add('product', $no, $f, $before[$f], $after[$f], $reason);
}

바뀐 필드만 남기게 했다. (string) 으로 맞춘 뒤 === 로 비교하는 데는 이유가 둘이다.

하나는 타입이다. === 만 쓰면 DB 에서 온 "12000" 과 코드의 12000 이 다르게 잡힌다.

다른 하나는 == 의 대가다. 그 시절 PHP 는 0 == 'abc' 를 참으로 본다. '12000' == '12000.0' 도 참이다. DECIMAL 컬럼이 12000.00 으로 오는 자리에서는 그것이 편하다. 대신 0 과 글자를 같다고 보는 것을 함께 받는다.

(string) 으로 맞추면 12000"12000" 은 같다. 0'abc' 는 다르다. 이걸 넣고 하루치 이력이 3분의 1로 줄었다. 줄어든 만큼이 읽을 필요가 없던 줄이었다.

이미 쌓인 4천여 건은 reason 이 비어 있고 형식도 제각각이다. old_value 가 안 남아 있어 다시 만들 수 없었다. 지우지 않고 그대로 두되 화면에서 구분해 표시했다. 불완전해도 있는 것이 없는 것보다 낫다.

정리


Share this post on:

Previous Post
읽는 코드는 있는데 쓰는 코드가 없었다
Next Post
연말 정산은 한 번만 돌아야 했다