-
[삭제대기] 텍스트 에디터(CKEDITOR5 + VUE + LARAVEL) 이미지 업로드Laravel 2021. 12. 27. 16:09반응형SMALL
# What?
- 텍스트에디터로 이미지 업로드하는법
# How?
1. 기본 세팅
1) ckeditor5 온라인 빌더로 원하는 기능 추가해서 생성하기
https://ckeditor.com/ckeditor-5/online-builder/
CKEditor 5 Online Builder | Create your own editor in 5 steps
Create your own CKEditor 5 build with customized plugins, toolbar and language in 5 simple steps.
ckeditor.com
2) build폴더 public/js/ckeditor 폴더에 옮기기
4) ckeditor js 연결 및 csrf 토큰 메타태그 추가
@ app.blade.php
<!DOCTYPE html> <html> <head> ... <script src="{{ asset('/js/ckeditor/build/ckeditor.js') }}" defer></script> <meta name="csrf-token" content="{{ csrf_token() }}"> <script> <body> @inertia </body> </html>
2. 커스텀 어댑터 세팅
@ Utils/MyUploadAdapter.js
class MyUploadAdapter { constructor(loader) { this.loader = loader; } upload() { return this.loader.file .then(file => new Promise((resolve, reject) => { this._initRequest(); this._initListeners(resolve, reject, file); this._sendRequest(file); })); } abort() { if (this.xhr) { this.xhr.abort(); } } _sendRequest(file) { const data = new FormData(); data.append('upload', file); this.xhr.send(data); } _initListeners(resolve, reject, file) { const xhr = this.xhr; const loader = this.loader; const genericErrorText = `Couldn't upload file: ${file.name}.`; xhr.addEventListener('error', () => reject(genericErrorText)); xhr.addEventListener('abort', () => reject()); xhr.addEventListener('load', () => { const response = xhr.response; if (!response || response.error) { return reject(response && response.error ? response.error.message : genericErrorText); } resolve({ default: response.url }); }); if (xhr.upload) { xhr.upload.addEventListener('progress', evt => { if (evt.lengthComputable) { loader.uploadTotal = evt.total; loader.uploaded = evt.loaded; } }); } } _initRequest() { const xhr = this.xhr = new XMLHttpRequest(); // 핵심 xhr.open('POST', '/ckeditor/upload', true); xhr.setRequestHeader('x-csrf-token', document.querySelector('meta[name="csrf-token"]').getAttribute('content')); xhr.responseType = 'json'; } } export default MyUploadAdapter
* 그대로 복붙하고 주석 "// 핵심" 부분만 자기가 이미지 업로드 시 서버요청할 url로 변경하면됨
@ CustomCkeditor.js
import MyUploadAdapter from "./MyUploadAdapter"; function SimpleUploadAdapterPlugin(editor) { editor.plugins.get("FileRepository").createUploadAdapter = (loader) => { return new MyUploadAdapter(loader); }; } class CustomCkeditor { constructor(target = "#editor", onCreate = () => {}) { this.target = target; this.onCreate = onCreate; } create(){ return ClassicEditor.create( document.querySelector( '#editor' ), { licenseKey: '', extraPlugins: [SimpleUploadAdapterPlugin], } ).then( editor => { window.editor = editor; // 세팅 완료 후 할일이 있다면 this.onCreate(); }).catch( error => { console.error( error ); }); } } export default CustomCkeditor
@ Edit.vue
<template> <form class="form type02" @submit.prevent="update"> <div class="m-input-wrap type01"> <div class="m-input-text type01"> <input type="text" placeholder="주소" v-model="form.address"> <p class="m-input-error" v-if="form.errors.address">{{ form.errors.address }}</p> </div> </div> <textarea v-model="form.description" id="editor"></textarea> <button class="m-btn type01 bg-primary">저장하기</button> </form> </template> <script> import CustomCkeditor from "../../Utils/CustomCkeditor"; export default { data() { return { form: this.$inertia.form({ address: this.$page.props.user.data.address, description: this.$page.props.user.data.description, }), } }, methods: { update() { this.form.description = window.editor.getData(); this.form.post("/users/update", { forceFormData: true, preserveScroll: true, onSuccess: (response) => { if (response.props.flash.error) alert(response.props.flash.error); if (response.props.flash.success) alert(response.props.flash.success); } }); }, }, mounted() { new CustomCkeditor("#editor").create(); } } </script>
* 주석 "//핵심" 부분 보면 form 요청을 보낼 때 editor로부터 입력된 데이터를 받아와서 저장시켜줌.
@ style.css
.ck-editor__editable { min-height: 600px; } .ck-editor h1 {font-size:30px; font-weight:600;} .ck-editor h2 {font-size:24px; font-weight:600;} .ck-editor h3 {font-size:20px; font-weight:600;} .ck-editor ol li {list-style-type: decimal;} .ck-editor ol li {list-style-type: decimal;}
3. 백엔드 세팅
1) 라우팅 세팅
@ web.php
Route::post("/ckeditor/upload", [\App\Http\Controllers\CkeEditorController::class, "upload"])->name('ckeditor.upload');
2) 모델 세팅
@ Ckeditor.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; class Ckeditor extends Model implements HasMedia { use HasFactory, InteractsWithMedia; protected $appends = ["file"]; public function registerMediaCollections():void { $this->addMediaCollection('file'); } }
* migration은 별다른 컬럼 추가할 필요 없음
3) 컨트롤러 세팅
@ CkeditorController.php
<?php namespace App\Http\Controllers; use App\Models\Ckeditor; use Illuminate\Http\Request; class CkeEditorController extends Controller { public function upload(Request $request) { $ckeEditor = Ckeditor::first(); if(!$ckeEditor) $ckeEditor = Ckeditor::create(); if($request->hasFile("upload")){ $media = $ckeEditor->addMedia($request->upload)->toMediaCollection("img", "s3"); return response()->json([ "url" => $media->getFullUrl() ]); } } }
LIST'Laravel' 카테고리의 다른 글
Carbon 이번주 특정 요일 얻기(월화수목금토일) (0) 2022.03.22 그룹별로 카운팅하고 싶을 때 + 그룹핑 대상에 where처럼 조건 걸고싶을 때 (GroupBy + Having) (0) 2022.01.21 Has(hasMany의 개수와 where) (0) 2021.12.21 whereDoesntHave (0) 2021.11.25 한 컨트롤러에서 여러 모델에 대한 pagination 사용하고 싶을 때(페이지네이션) (0) 2021.11.16