멋진 개발자가 되고 싶어

[ck editor] Angular에서의 image upload adapter 사용 본문

CODE/라이브러리

[ck editor] Angular에서의 image upload adapter 사용

nutonny 2023. 2. 13. 13:57

[ck editor] Angular에서의 image upload adapter 사용

ck editor의 adapter

파일 업로드를 위해 ck editor에서는 여러가지 어댑터를 제공한다. 이와 관련하여 자세하게 확인하고 싶다면 아래의 링크를 통해 확인하면 된다.

들어가기 전에

이제부터 Angular 프로젝트에서 이미지 어댑터를 사용하는 방법에 관하여 설명을 할 것이다. 내가 직접 프로젝트를 제작하면서 생긴 문제를 해결해 나가는 과정의 설명정도로 봐주면 좋을 것 같다. ck editor를 사용하여 제작한 프로젝트는 github에 업로드 해놨다. 아래의 링크를 통해 확인할 수 있다.

Custom image upload adapter를 사용한 이유

처음에는 Simple upload adapter를 사용하려 하였으나 자꾸 error가 발생하였다. 이를 해결하기 위해 서치를 하던 중 비슷한 사례의 글을 발견했다. 여기서 문제의 해답으로 Custom image upload adapter를 사용할 것을 추천하였다.

문제 해결 과정

아래는 글에서 제공하는 솔루션이다. 제작중이던 angular 프로젝트와 동일한 방식을 활용한 답변이었다. HttpClient를 활용하고 있다.

class MyUploadAdapter {
  constructor(private loader: any, private http: HttpClient) {}
  upload() {
    return this.loader.file.then((file) => {
      return this.http
        .post(
          'https://your.url',
          new FormData().append('upload', file)
        )
        .subscribe();
    });
  }
  abort() {
    console.error('aborted');
  }
}

위의 코드를 참고한 결과

해결된 부분

  • 더이상 콘솔에 어탭터를 사용하라는 경고가 뜨지 않음
  • 에디터에 사진이 표시됨
  • 원하는 파일 저장소에 바르게 사진이 저장됨

새로 발견한 문제

  • 글 목록에서 다시 글을 확인할 때 본문에서 이미지가 보이지 않음

원인 분석

  • 사진 추가 시 본문 내용을 보면 이미지 태그<img>만 생성됨을 확인 가능
  • 이미지의 주소가 제대로 전달되고 있지 않은 것이 원인임을 알게 됨

그래서 다시 원본 문서를 확인해보았다.


해답

  • The complete implementation 부분을 확인해보면 아래의 부분이 누락되어있다.
  • 이를 해결하니 원하는 결과물을 얻을 수 있었다.

누락된 부분

  • upload()에서 then()에 promise를 반환하는 부분
  • resolve()에서 {default: response.url}를 반환하는 부분

원문에서 참고한 부분

  • 문제해결에 필요한 부분을 해석해봤다.
// 업로드 프로세스의 시작
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();
    }
}
// XMLHttpRequest listeners 초기화
_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}를 반환할 것이다!
        // 이 URL이 본문의 이미지로 보일 것이다. 더 자세한 내용은 UploadAdapter#upload 문서를 참고하라.

        resolve( {
            default: response.url
        } );
    } );

최종 결과

내가 작성한 my-upload-adapter.ts의 전문이다.
파일 전문

import {HttpClient} from "@angular/common/http";
import {environment} from "../../../environments/environment";

export class MyUploadAdapter {
  constructor(private loader: any, private http: HttpClient) {}

  url : string = environment.serverAddress + '/upload';
  fileName : string = '';
  imageSrc : string = '';

  upload() {
    return this.loader.file
      .then((file:any) => new Promise( ( resolve, reject ) => {
        const formData = new FormData();
        formData.append('upload', file);

        return this.http.post(this.url, formData).subscribe({
            next: (data: any) => {
              this.fileName = data.sendFiles[0].filename;
              this.imageSrc = this.url + `/${this.fileName}`;
              resolve({default : this.imageSrc})
            },
            error: (e) => {
              console.error('error : ', e);
            }
          });
    } ));

  }
  abort() {
    console.error('aborted');
  }
}

html의 ck editor 태그
파일 전문

<ckeditor class="content-input"
        id="editor"
        [editor]="Editor"
        [(ngModel)]="content"
        (ready)="onReady($event)"
></ckeditor>

ck editor가 적용된 컴포넌트의 ts파일
파일 전문

/**
* @description MyUploadAdapter에서 받은 데이터 반영
* */

onReady(editor: ClassicEditor): void {
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader :any ) => {
    return new MyUploadAdapter( loader, this.httpClient);
};
}

✅참고자료

Comments