nv-suneditor
Vue 3 기반의
nv-suneditor
는 SunEditor를 커스터마이징한 텍스트 에디터입니다. 이미지 업로드와 삽입, 폰트 지정, 외부 모델 연동 등의 기능을 확장하여 제공합니다.
또한, SunEditor의 기본 속성과 메서드를 그대로 사용할 수 있습니다. 자세한 속성 목록은 공식 문서를 참고하세요.
props
이름 | 타입 | 기본값 | 필수 여부 | 설명 |
---|---|---|---|---|
v-model | string | null | ✅ | 에디터의 내용과 바인딩되는 값입니다. |
v-model:host | object | null | ✅ | 업로드 대상이 되는 객체입니다. (예: 게시물, 템플릿 등) |
ref-domain | string | null | ✅ | 업로드 시 파일이 소속되는 도메인 값입니다. |
ref-category | string | null | ✅ | 업로드 시 파일이 소속되는 카테고리 값입니다. |
ref-key | string number | null | ✅ | 업로드 파일이 연결될 대상 객체의 고유 식별자입니다. (예: 게시글 ID 등) |
font-list | string[] | [] | ❌ | 사용자 정의 폰트 목록을 설정할 수 있습니다. |
img-upload-header | boolean | false | ❌ | 이미지 업로드 영역 상단에 헤더(접기/펼치기 버튼 포함)를 표시할지 여부를 설정합니다. 기본값은 false이며, 이 값을 true로 설정하면 이미지 리스트 영역을 토글할 수 있는 버튼이 리스트 헤더에 표시됩니다. |
기본
기본
html
<nv-suneditor
v-model="model.config"
v-model:host="model"
ref-domain="board"
ref-category="image"
:ref-key="model.id"
:created-dt="model.createdDt"
:font-list="['Do Hyeon', 'Hi Melody']"
:img-upload-header="false"
/>
typescript
<script setup lang="ts">
import {ref} from 'vue';
const model = ref<{
id?: string;
config?: string;
[key: string]: any;
}>({ id: 1, config: '초기 내용입니다.', createdDt: new Date(), refCategory: 'image', refDomain: 'board' });
</script>
INFO
에디터에서 이미지를 삽입하는 방식은 다음의 세 가지로 나뉩니다:
드래그 앤 드롭
이미지를 에디터로 직접 드래그 → 자동으로 업로드 및 삽입 → 즉시 이미지 편집 다이얼로그가 열림툴바의 이미지 버튼 사용
툴바에서 이미지 버튼 클릭 → 로컬 파일 선택 → 업로드 및 자동 삽입 → 삽입 직후 편집 다이얼로그가 자동 실행하단 이미지 업로드 목록에서 삽입
nv-editor-image-upload
를 통해 이미지 업로드 → 목록에 이미지가 표시됨 → 사용자가 원하는 시점에 삽입 가능 → 삽입 시 자동으로 편집 다이얼로그 실행
img-upload-header
img-upload-header
prop은 에디터 하단에 표시되는 이미지 업로드 영역의 상단 헤더를 표시할지 여부를 결정합니다.- 이 헤더에는 토글 버튼(펼치기/접기) 과 제목 표시 영역이 포함되어 있어, 이미지 리스트를 깔끔하게 숨기거나 펼칠 수 있습니다.
- 기본값은 false이며, true로 설정할 경우 사용자는 이미지 업로드 목록을 접거나 펼칠 수 있는 UI를 사용할 수 있게 됩니다.
기본
이미지 리스트 (0)
html
<nv-suneditor
v-model="model.config"
v-model:host="model"
ref-domain="board"
ref-category="image"
:ref-key="model.id"
:created-dt="model.createdDt"
:font-list="['Do Hyeon', 'Hi Melody']"
:img-upload-header="true"
/>
typescript
<script setup lang="ts">
import {ref} from 'vue';
const model = ref<{
id?: string;
config?: string;
[key: string]: any;
}>({ id: 1, config: '초기 내용입니다.', createdDt: new Date(), refCategory: 'image', refDomain: 'board' });
</script>
이미지 업로드
onImageUploadBefore
훅을 활용하여 커스텀 이미지 업로드 처리를 수행합니다.- 업로드한 이미지는 서버에서 URL을 응답받아 에디터 내에 삽입됩니다.
- 업로드 실패 시 URL.createObjectURL()를 이용해 에디터에 이미지는 미리보기 형태로 삽입되지만, 이 이미지는 서버(또는 DB)에 저장되지 않으므로 실제로는 임시 표시용일 뿐입니다. 따라서 페이지를 새로고침하거나 다시 불러오면 해당 이미지는 사라집니다.
typescript
editorInstance.value.onImageUploadBefore = (files, info, core, uploadHandler) => {
handleImageUpload(files, uploadHandler);
return false;
};
const handleImageUpload = async (files, uploadHandler) => {
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]);
}
formData.append('refDomain', props.refDomain);
formData.append('refCategory', props.refCategory);
formData.append('useFileExtension', true);
formData.append('resultKey', 'result');
try {
const { data } = await api.post('/api/firo/attach/direct', formData);
if (data?.result?.length > 0) {
uploadHandler(data);
data.result.forEach((it) => {
uploadComp.value.addFileToBag(it);
});
} else {
alert('이미지 업로드에 실패했습니다. 서버 응답에 URL이 없습니다.');
}
} catch (e) {
alert('이미지 업로드 중 오류가 발생했습니다: ' + e.message);
const fallbackImages = files.map(file => ({
url: URL.createObjectURL(file),
name: file.name,
size: file.size,
}));
uploadHandler({ result: fallbackImages });
}
};
이미지 삽입 후 자동 편집 다이얼로그 열기
- 이미지 업로드가 완료되고 에디터에 삽입할 경우, 자동으로 편집 다이얼로그가 열립니다.
- 편집 다이얼로그를 통해 Alternative text와 이미지 크기 등의 속성을 수정할 수 있습니다.
typescript
editorInstance.value.onImageUpload = (targetElement, index, state, info, remainingFilesCount, core) => {
if (state === 'create' && remainingFilesCount === 0 && targetElement) {
nextTick(() => {
core.plugins.image.select.call(core, targetElement);
core.plugins.image.openModify.call(core, false);
})
}
}
WARNING
- 삽입된 이미지의 편집 다이얼로그를 통해 파일을 변경할 경우, 속성 내용은 변경되지 않습니다.
Font 추가
nv-suneditor
에서는 @import
, @font-face
를 이용해 웹 폰트 또는 로컬 폰트를 추가할 수 있으며, 에디터 단위 또는 전체 에디터 공통으로 폰트를 적용할 수 있습니다.
TIP
font-list
prop을 사용하면 기본 폰트에 추가로 원하는 폰트를 개별 에디터에만 적용할 수 있습니다.- 중복된 폰트 이름이 있을 경우 자동으로 병합되므로 충돌은 발생하지 않습니다.
- 폰트는 CSS로 반드시 불러와야 에디터 내에서 적용이 가능합니다.
개별 에디터에 폰트 추가
- 에디터를 사용하는 페이지에서
<style scoped>
를 활용하여 폰트를 불러오고,:font-list
속성으로 추가할 수 있습니다.
vue
<nv-suneditor
v-model="model.content"
v-model:host="model"
ref="inputArea"
ref-target="msg-template"
ref-target-type="content"
:ref-target-key="model.id"
:created-dt="model.createdDt"
:font-list="['Do Hyeon', 'Hi Melody']"
/>
<style scoped>
@import url('https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap');
@font-face {
font-family: 'Hi Melody';
src: url('../../../assets/font/HiMelody-Regular.ttf') format('truetype');
}
</style>
Do Hyeon
: 구글 웹폰트로 불러오기Hi Melody
: 로컬 폰트로 직접 추가
전체 에디터 공통 폰트 추가
nv-suneditor.vue
컴포넌트 내부에서 전체 공통으로 사용할 폰트를 정의할 수 있습니다.defaultFonts
배열을 수정하여 전체 에디터에서 사용할 기본 폰트 목록을 지정할 수 있습니다.
typescript
const defaultFonts = ['Arial', 'Comic Sans MS', 'Courier New', 'Impact', 'Georagia', 'tahoma', 'Trebuchet MS', 'Verdana', 'Noto Sans KR', 'Nanum Pen Script']
css
<style scoped>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Nanum+Gothic&display=swap');
@font-face {
font-family: 'Nanum Pen Script';
src: url('../../../assets/font/Nanum-Pen-Script.ttf') format('truetype');
}
</style>
INFO
로컬 폰트를 사용할 경우 @font-face
를 통해 해당 폰트를 등록하고, font-family
이름을 defaultFonts
에 추가해야 합니다.