Skip to content
isdnetworks
Go back

엑셀 업로드에 검사가 없었다

관리자가 PHPExcel 로 읽는 표 파일로 상품을 한 번에 올리는 기능이 있었다. 수백 건을 올렸는데 중간에 죽었고 확인해 보니 절반쯤 들어가 있었다.

Table of contents

Open Table of contents

증상 — 앞의 것은 남고 멈췄다

로그를 보니 한 행에서 오류가 났다.

가격 컬럼에 문자가 들어 있음

코드는 이랬다.

foreach ($rows as $row) {
    $model->insert($row);   // 여기서 오류
}

$model->insert() 가 오류를 내면 거기서 끝인데 앞의 200건은 이미 들어간 뒤다. 되돌아가지 않는다.

고쳐서 다시 올리니 앞의 200건이 또 들어갔다. 그래서 관리자가 앞의 것을 손으로 지우고 다시 올리고 있었고 올리다 실패할 때마다 그 작업이 반복됐다.

트랜잭션으로 묶었다

먼저 중간에 죽으면 되돌아가게 했다.

$this->db->trans_start();
foreach ($rows as $row) { $model->insert($row); }
$this->db->trans_complete();

trans_starttrans_complete 사이에서 하나라도 실패하면 전부 안 들어간다. 그 전에 대상 테이블이 InnoDB 인지부터 봤는데 MyISAM 이면 묶어도 안 되돌아간다.

전부 들어가거나 아무것도 안 들어가는 두 상태만 남고 중간 상태가 없어졌다. 앞의 것을 손으로 지우는 일도 그것으로 없어졌다.

그런데 불편이 남았다. 한 번 올리면 한 오류만 알려 주고 그것을 고쳐 다시 올리면 다음 오류가 나온다. 파일에 오류가 열 개면 열 번을 왕복한다.

넣기 전에 전부 검사했다

저장하기 전에 전체를 먼저 검사하게 했다.

$errors = [];
foreach ($rows as $i => $row) {
    $e = validate($row);
    if ($e) $errors[$i + 2] = $e;   // 엑셀 행 번호
}
if ($errors) { 화면에 표시; return; }

validate 가 돌려준 것을 $errors 에 모아 한 번에 보여 준다.

3행: 가격이 숫자가 아닙니다
17행: 카테고리를 찾을 수 없습니다
42행: 상품코드가 중복입니다

$errors 의 키가 곧 엑셀 행 번호라 바로 그 행을 열어 고친다.

행 번호를 맞추는 데 걸린 것이 있었다. 배열은 0부터 시작하고 엑셀은 1부터인데 첫 줄이 머리글이라 +2 를 해야 맞았다. 이 보정을 안 하면 관리자가 엉뚱한 줄을 고친다.

고치고 다시 올리는 왕복이 한 번으로 줄었다.

중복과 큰 파일과 결과 건수

검사에 중복도 넣었다. 파일 안에서 같은 상품코드가 두 번 나오는 것이 있었다.

$seen = [];
foreach ($rows as $i => $row) {
    if (isset($seen[$row['code']])) { 중복 오류; }
    $seen[$row['code']] = true;
}

이미 MySQL 에 있는 코드인지도 봤다.

$existing = $model->get_codes_in($codes);

get_codes_in 으로 한 번에 조회해서 대조했다. 한 건씩 조회하면 수백 번이 된다. 파일 안에서만 보면 UNIQUE 에 걸릴 것을 못 잡으니 두 가지를 다 봐야 저장 단계에서 안 걸린다.

파일이 크면 전부 읽어 두는 방식이 memory_limit 에 걸렸다. 한 행씩 읽는 방식으로 바꾸니 검사와 저장에서 파일을 두 번 읽게 됐다. 두 번 읽는 비용보다 왕복이 사라진 이득이 크다고 봤다.

끝나면 결과를 보여줬다.

성공 342건
실패 0건
소요 12초

특히 건수였다. PHPExcel 이 읽은 행 수와 실제 저장 건수를 나란히 두면 올린 파일과 대조할 수 있다.

실제로 안 맞은 적이 있었다. 엑셀은 450행인데 처리는 342건이었고 빈 행이 섞여 있어 건너뛴 것이었다. 건수를 안 보여줬으면 몰랐다.

정리


Share this post on:

Previous Post
소스에 박힌 절대 주소
Next Post
코드와 데이터가 다른 길로 갔다