Laravel
[삭제대기] 텍스트 에디터(CKEDITOR5 + VUE + 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