이전 포스트에서는 Articles 리소스 테이블의 데이터를 Excel 파일로 내보내는 기능을 구현했었다.
이번에는 반대로 엑셀 파일의 데이터를 읽어와 데이터베이스에 일괄 업로드하는 기능을 구현해 보겠다.
이전 포스트
라라벨 프로젝트 엑셀 기능 구현, LARAVEL EXCEL 패키지 설치 및 데이터 내보내기
Excel (CSV) 파일의 데이터를 DB로 옮기는 기능 구현 · 4th-avenue/laravel13-livewire@439c638 · GitHub
전체적인 코드 수정은 이 깃허브 링크를 통해 확인하자.
Excel 파일의 데이터를 DB로 업로드하기 위한 Import 클래스 생성
make bash #컨테이너 안으로 들어간다.
php artisan make:import ArticlesImport --model=Article
이번에는 터미널에서 Excel 파일의 데이터를 DB로 업로드하기 위한 Export 클래스를 생성했다.
이번에도 반드시 --model=Article 옵션을 붙여서 ArticlesExport가 Article 모델과 자동으로 연결되도록 하자.
파일 업로드 기능을 위해 해야 할 사전 작업
php artisan storage:link
터미널에서 이 명령어를 실행한다.
이 명령어는 Laravel 애플리케이션에서 storage 디렉터리의 app/public 하위에 있는 파일들을 웹에서 접근할 수 있도록 심볼릭 링크를 생성하는 역할을 한다.
- 목적: 애플리케이션의 public 디렉터리와 storage/app/public 디렉터리 간의 심볼릭 링크를 만든다. 이를 통해 storage/app/public 내의 파일에 직접 접근할 수 있게 된다.
- 사용 예: 파일 업로드 후 웹에서 해당 파일에 접근하려면 이 명령어를 실행해야 한다. 예를 들어, 사용자가 업로드한 이미지 파일을 웹에서 보여주기 위해 필요하다.
- 동작 방식: public/storage라는 디렉터리가 생성되며, 이 디렉터리에는 storage/app/public의 모든 파일이 링크된다.
간단하게 설명하자면, 파일 업로드 기능을 구현하기 위해서는 한 번은 실행해야 하는 명령어다.
딱 한 번만 실행하면 그 후로는 실행할 일이 없다.
Excel (CSV) 파일의 데이터를 DB로 옮기는 기능 구현
지금부터 보여주는 코드들은 app/Imports/ArticlesImport.php에 들어가는 코드들이다.
코드가 들어갈 위치는 포스트 상단에 걸어둔 깃허브 링크를 확인하자.
class ArticlesImport implements ToModel, WithHeadingRow
WithHeadingRow가 추가됐다.
이것은 LARAVEL EXCEL 패키지에서 제공하는 기능으로, 엑셀 파일의 첫 번째 행을 헤더(열 제목)로 인식하여 각 데이터를 이름 기준으로 자동 매핑해 준다.
이 인터페이스를 구현하면 숫자 인덱스 대신 열 제목을 이용해 데이터를 직관적이고 안전하게 처리할 수 있다.
use Importable;
이 코드도 LARAVEL EXCEL 패키지에서 제공하는 Importable trait을 사용하기 위한 코드다.
이 trait은 Excel 파일을 가져오는 과정에서 필요한 여러 기능을 추가해준다.
public function model(array $row): Model|null
model 메서드는 엑셀 파일에서 읽은 각 행($row)을 데이터베이스에 저장할 Article 모델 인스턴스로 변환하는 역할을 한다.
Article::unguard();
대량 할당(Mass Assignment) 보호 기능을 임시로 해제하는 코드다.
이 코드를 실행하지 않으면 Article 모델의 #[Fillable] 어트리뷰트에 명시된 컬럼에만 값이 입력된다.
목록에 없는 컬럼 데이터는 라라벨이 자동으로 무시하므로, DB 기본값이 들어가거나 NOT NULL 제약 조건에 의해 에러가 발생할 수 있다.
$article = new Article([
'id' => $row['id'],
'category_id' => $row['category_id'],
'title' => $row['title'],
'user_id' => $row['user_id'],
'body' => $row['body'],
'images' => $row['images'],
'view_count' => $row['view_count'] ?? 0,
'ip_address' => $row['ip_address'],
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
'deleted_at' => $row['deleted_at'],
]);
$row는 엑셀의 각 행 데이터를 담고 있는 배열이며, 배열의 키는 엑셀 파일의 열 이름(헤더)에 해당한다.
Article 모델 인스턴스를 생성할 때, $row 배열의 키는 모델의 필드(컬럼)명에 일대일로 매핑되며 해당 키의 값이 각 필드에 저장된다.
view_count 컬럼의 경우에는 ?? 연산자를 이용해서 엑셀 파일에 해당 컬럼 값이 비어 있으면 '0'을 넣어서 에러 없이 정상적으로 업로드되도록 했다.
Article::reguard();
위에서 Article::unguard();로 인해 해제되었던 대량 할당(Mass Assignment) 보호 기능을 다시 활성화하여 원상복구하는 코드다.
대량 데이터 임포트 작업이 끝난 후 이 코드를 실행해야, 이후 시스템에서 발생할 수 있는 의도치 않은 데이터 변경 및 대량 할당 보안 취약점을 방지할 수 있다.
return $article;
엑셀의 한 행(Row) 데이터를 기반으로 생성한 Article 객체를 패키지에 전달하는 코드다.
이렇게 반환(return)된 모델 객체는 LARAVEL EXCEL 패키지가 받아서 데이터베이스에 최종 저장(Insert)을 완료한다.
즉, 엑셀의 각 행이 하나의 게시글(Article) 데이터로 DB에 차곡차곡 추가된다.
ArticlesImport.php에 들어가는 코드들은 여기까지다.
이제 마지막으로 ListArticles.php에 Excel (CSV) 파일의 데이터를 DB로 옮기는 기능을 추가할 차례다.
app/Filament/Resources/Articles/Pages/ListArticles.php
Action::make('importArticles')
->label('Import Articles')
->icon('heroicon-o-document-arrow-up')
->schema([
FileUpload::make('attachment')
->disk('public')
->directory('imports')
->required(),
])
->action(function (array $data){
$filePath = Storage::disk('public')->path($data['attachment']);
Excel::import(new ArticlesImport, $filePath);
Notification::make()
->title('Articles Imported')
->success()
->send();
}),
이전 포스트에서 이미 설명했던 코드들은 제외하고 설명하겠다.
->schema([ ... ]): 버튼을 클릭했을 때 화면에 뜰 팝업 창(모달) 내부의 입력 폼 요소들을 정의한다.FileUpload::make('attachment'): 파일 선택 및 업로드를 담당하는 필드를 생성하고, 폼 데이터의 키 이름을 'attachment'로 지정한다.->disk('public'): 라라벨 스토리지 설정(config/filesystems.php)에 정의된 디스크 중 'public' 디스크에 업로드 파일을 저장하도록 지정한다.->directory('imports'): 해당 디스크 내부의 'imports' 폴더(storage/app/public/imports/) 안에 업로드된 파일들을 보관하도록 지정한다.->required(): 파일 첨부를 필수 항목으로 지정하여, 파일을 업로드하지 않으면 양식을 제출할 수 없게 만든다.->action(function (array $data){ ... }): 관리자가 파일 업로드 후 모달의 제출 버튼을 눌렀을 때 실행될 동작(클로저 함수)을 지정한다. 이때$data변수에는 업로드된 파일 정보($data['attachment'])가 전달된다.$filePath = Storage::disk('public')->path($data['attachment']);: 업로드된 파일의 상대 경로(imports/articles.csv)를 서버 OS 환경의 실제 전체 물리 경로(/var/www/.../storage/app/public/imports/articles.csv)로 변환하여$filePath변수에 할당한다.Excel::import(new ArticlesImport, $filePath);: LARAVEL EXCEL 패키지의 파사드를 호출하여, 추출한 서버 절대 경로의 파일을 ArticlesImport 클래스 규칙에 따라 읽어와 DB에 저장한다.Notification::make(): filament의 팝업 알림(Toast Notification) 객체를 새로 생성한다.->title('Articles Imported'): 알림 팝업에 표시될 제목 텍스트를 'Articles Imported'로 지정한다.->success(): 성공 상태를 나타내는 초록색 토스트 알림 스타일을 적용한다.->send(): 설정된 성공 알림 팝업을 사용자의 화면(브라우저)으로 최종 출력한다.
여기까지 하면 Excel (CSV) 파일의 데이터를 DB로 옮길 수 있게 된다.
이제 제대로 작동하는지 확인해 보자.
먼저 articles 테이블의 모든 데이터를 삭제하기 전에 엑셀 파일을 저장하자.
이전 포스트에서 언급했던 것처럼, id 순으로 정렬을 시킨 채로 모든 항목을 선택하고 Export to Excel을 클릭해서 다운로드한다.
다운로드가 끝나면 이제 모든 데이터를 삭제해야 한다.
현재 Article 모델에는 Soft Delete 기능이 활성화되어 있기 때문에 아직 완전히 삭제된 것이 아니다.
필터로 삭제된 항목이 표시되도록 하자.
라라벨의 Soft Delete 기능에 대해 알아보자. (categories와 articles 테이블 추가)
다시 전부 체크하고 영구 삭제하도록 하자.
Import Articles 버튼을 클릭하면 이렇게 모달창이 뜨게 된다.
그리고 다운로드했던 articles.csv 파일을 업로드하고 제출하면 끝이다.
삭제했던 모든 글들이 다시 원래대로 돌아오게 된다.
하지만 업로드를 할 때마다 storage/app/public/ 디렉터리에 업로드했던 csv 파일이 쌓이게 된다.
엑셀 파일의 데이터를 DB에 Import 한 후에 이걸 바로 자동으로 삭제되도록 하는 기능이 필요하다.
그 기능은 다음 포스트에서 다루도록 하겠다.
다음 포스트





0 Comments
댓글 쓰기
🔸 댓글은 블로그 운영자의 승인 후에 블로그에 표시됩니다.
🔸 비로그인 방문자 분께서는 '익명'보다 이름/URL로 댓글을 남겨주시면 감사하겠습니다. (URL은 생략 가능합니다.)
🔸 구글 로그인 방문자는 '알림 사용'에 체크를 하시면, 남겨주신 댓글에 대한 답글 알림을 메일로 받아볼 수 있습니다. 📩