본문으로 건너뛰기

고급 Props

FroalaEditor의 고급 기능을 제어하는 Props들입니다.

이벤트 핸들러 예제

로딩 중...
이벤트 로그:

editorRef 사용 예제

로딩 중...
editorRef 로그:

커스텀 이벤트 예제

로딩 중...
커스텀 이벤트 로그:

editorRef

  • 타입: React.MutableRefObject<FroalaEditorType | null>
  • 기본값: undefined
  • 설명: 에디터 인스턴스에 직접 접근할 수 있는 참조를 제공합니다.
import { useRef } from 'react';

function MyEditor() {
const editorRef = useRef(null);

const handleButtonClick = () => {
if (editorRef.current) {
// 에디터 인스턴스에 직접 접근
const content = editorRef.current.html.get();
console.log('현재 내용:', content);

// 에디터 내용 설정
editorRef.current.html.set('<p>새로운 내용</p>');

// 에디터에 포커스 주기
editorRef.current.focus();
}
};

return (
<div>
<FroalaEditor editorRef={editorRef} />
<button onClick={handleButtonClick}>에디터 제어</button>
</div>
);
}

주요 사용 사례

1. 내용 조작

// HTML 내용 가져오기
const htmlContent = editorRef.current.html.get();

// HTML 내용 설정하기
editorRef.current.html.set('<p>새로운 내용</p>');

// 플레인 텍스트 가져오기
const plainText = editorRef.current.html.get(true);

2. 에디터 상태 제어

// 포커스 주기
editorRef.current.focus();

// 에디터 비활성화/활성화
editorRef.current.edit.off();
editorRef.current.edit.on();

// 전체 선택
editorRef.current.selection.selectAll();

3. 이미지 삽입

// 이미지 URL로 삽입
editorRef.current.image.insert('https://example.com/image.jpg');

// Base64 이미지 삽입
editorRef.current.image.insert('data:image/png;base64,...');

onInit

  • 타입: (editor: FroalaEditorType) => void
  • 기본값: undefined
  • 설명: 에디터 초기화가 완료된 후 호출되는 콜백 함수입니다.
const handleInit = (editor) => {
console.log('에디터 초기화 완료');

// 초기화 후 설정 적용
editor.opts.placeholderText = '동적으로 설정된 placeholder';

// 커스텀 툴바 버튼 추가
editor.button.addAfter('bold', 'customButton', {
title: '커스텀 버튼',
icon: 'star',
callback: () => {
editor.html.insert('<span style="color: gold;">⭐</span>');
},
});
};

<FroalaEditor onInit={handleInit} />;

초기화 시 설정 예제

const initializeEditor = (editor) => {
// 툴바 커스터마이징
editor.opts.toolbarButtons = ['bold', 'italic', 'underline'];

// 이벤트 리스너 추가
editor.events.on('keydown', (e) => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
saveContent();
}
});

// 드래그 앤 드롭 설정
editor.events.on('drop', handleFileDrop);
};

onBlur

  • 타입: (editor: FroalaEditorType) => void
  • 기본값: undefined
  • 설명: 에디터에서 포커스가 해제될 때 호출되는 콜백 함수입니다.
const handleBlur = (editor) => {
console.log('에디터 포커스 해제');

// 자동 저장
const content = editor.html.get();
saveToLocalStorage(content);

// 유효성 검사
if (content.length < 10) {
showWarning('내용이 너무 짧습니다.');
}
};

<FroalaEditor onBlur={handleBlur} />;

실용적인 onBlur 사용 예제

const [errors, setErrors] = useState([]);

const validateContent = (editor) => {
const content = editor.html.get();
const newErrors = [];

if (content.length < 50) {
newErrors.push('최소 50자 이상 작성해주세요.');
}

if (!content.includes('<img')) {
newErrors.push('이미지를 최소 1개 포함해주세요.');
}

setErrors(newErrors);
};

<FroalaEditor onBlur={validateContent} />;
{
errors.map((error) => (
<div key={error} className="error-message">
{error}
</div>
));
}

licenseKey

  • 타입: string
  • 기본값: undefined
  • 설명: Froala Editor의 라이선스 키를 설정합니다.
<FroalaEditor licenseKey="your-license-key-here" value={content} onChange={setContent} />

환경별 라이선스 관리

// 환경 변수로 관리
const licenseKey = process.env.NEXT_PUBLIC_FROALA_LICENSE_KEY;

// 개발/프로덕션 분기
const getLicenseKey = () => {
if (process.env.NODE_ENV === 'development') {
return 'development-license-key';
}
return process.env.FROALA_PRODUCTION_LICENSE_KEY;
};

<FroalaEditor licenseKey={getLicenseKey()} />;

events

  • 타입: Record<string, (...args: any[]) => void>
  • 기본값: undefined
  • 설명: 에디터의 다양한 이벤트에 대한 커스텀 핸들러를 설정합니다.
const customEvents = {
// 에디터 초기화 후 이벤트
initialized: function () {
console.log('에디터가 초기화되었습니다');
// this는 FroalaEditor 인스턴스를 가리킵니다
},

// 내용 변경 이벤트 (가장 많이 사용)
contentChanged: function () {
console.log('내용이 변경됨');
const content = this.html.get();
console.log('현재 내용:', content);
},

// 포커스 이벤트
focus: function () {
console.log('에디터에 포커스됨');
},

// 블러 이벤트
blur: function () {
console.log('에디터에서 포커스 해제됨');
},

// 키보드 이벤트
keydown: function (e) {
console.log('키가 눌러짐:', e.key);
// Ctrl+S로 저장 기능
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
console.log('저장 단축키 실행');
// saveContent(); // 실제 저장 함수 호출
}
},

// 클릭 이벤트
click: function (e) {
console.log('에디터 클릭됨', e.target);
},
};

<FroalaEditor events={customEvents} />;

실용적인 이벤트 활용 예제

const useEditorEvents = () => {
const [wordCount, setWordCount] = useState(0);
const [unsavedChanges, setUnsavedChanges] = useState(false);
const saveTimeoutRef = useRef(null);

const events = {
// 내용 변경 시 실행
contentChanged: function () {
// 단어 수 계산 (this는 에디터 인스턴스)
const text = this.html.get(true); // 플레인 텍스트로 가져오기
const words = text
.trim()
.split(/\s+/)
.filter((word) => word.length > 0);
setWordCount(words.length);

// 자동 저장 (디바운스 적용)
setUnsavedChanges(true);

// 이전 타이머 취소
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current);
}

// 3초 후 자동 저장
saveTimeoutRef.current = setTimeout(() => {
const content = this.html.get();
console.log('자동 저장:', content);
// saveContent(content); // 실제 저장 함수
setUnsavedChanges(false);
}, 3000);
},

// 붙여넣기 전 HTML 정리
'paste.beforeCleanup': function (original_html) {
console.log('붙여넣기 전 HTML 정리');
// 스크립트 태그 제거
let cleaned = original_html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
// 스타일 태그 제거 (선택적)
cleaned = cleaned.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');
return cleaned;
},

// 이미지 삽입 전 검증
'image.beforeUpload': function (files) {
console.log('이미지 업로드 시작:', files);

// 파일 크기 검증 (5MB 제한)
for (let i = 0; i < files.length; i++) {
if (files[i].size > 5 * 1024 * 1024) {
alert('이미지 크기는 5MB 이하여야 합니다.');
return false; // 업로드 중단
}
}

// 파일 형식 검증
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
for (let i = 0; i < files.length; i++) {
if (!allowedTypes.includes(files[i].type)) {
alert('JPG, PNG, GIF 형식만 업로드 가능합니다.');
return false;
}
}

return true; // 업로드 허용
},
};

return { events, wordCount, unsavedChanges };
};

function EditorWithEvents() {
const { events, wordCount, unsavedChanges } = useEditorEvents();
const [content, setContent] = useState('');

return (
<div>
<div style={{ marginBottom: '10px', fontSize: '14px', color: '#666' }}>
단어 수: {wordCount}
{unsavedChanges && <span style={{ color: 'orange' }}> • 저장되지 않은 변경사항</span>}
</div>
<FroalaEditor value={content} onChange={setContent} events={events} />
</div>
);
}

주요 이벤트 목록

이벤트 이름설명매개변수
initialized에디터 초기화 완료-
contentChanged내용 변경됨-
focus에디터에 포커스-
blur에디터에서 포커스 해제-
keydown키 누름event
keyup키 뗌event
click클릭event
paste.before붙여넣기 전event
paste.beforeCleanup붙여넣기 HTML 정리 전html
image.beforeUpload이미지 업로드 전files
image.uploaded이미지 업로드 완료response
image.error이미지 업로드 오류error, response
이벤트 함수에서 this 사용

모든 이벤트 함수에서 this는 Froala Editor 인스턴스를 가리킵니다. 화살표 함수를 사용하면 this 바인딩이 되지 않으므로 일반 함수를 사용해야 합니다.

기타 Props

viewContentSize

  • 설명: 내부적으로 사용되는 설정으로, 콘텐츠 크기 표시 여부를 제어합니다.
  • 기본값: true

tag와 다른 Props들

에디터는 [key: string]: any 타입으로 추가적인 Froala 설정을 받을 수 있습니다:

<FroalaEditor
// Froala 공식 옵션들
language="ko"
theme="royal"
toolbarInline={true}
charCounterCount={false}
// 커스텀 Props
value={content}
onChange={setContent}
/>

💡 고급 사용 팁

1. 에디터 인스턴스 상태 관리

const useEditorState = () => {
const editorRef = useRef(null);
const [isReady, setIsReady] = useState(false);

const handleInit = (editor) => {
setIsReady(true);
};

return { editorRef, isReady, handleInit };
};

2. 동적 툴바 설정

const [toolbarMode, setToolbarMode] = useState('full');

const getToolbarButtons = () => {
switch (toolbarMode) {
case 'simple':
return ['bold', 'italic', 'underline'];
case 'full':
return ['bold', 'italic', 'underline', 'insertImage', 'insertTable'];
default:
return [];
}
};

<FroalaEditor
toolbarButtons={getToolbarButtons()}
onInit={(editor) => {
editor.toolbar.refresh();
}}
/>;

3. 에러 핸들링

const handleInit = (editor) => {
try {
// 에디터 설정
editor.opts.imageUploadURL = '/api/upload';
} catch (error) {
console.error('에디터 초기화 오류:', error);
showErrorNotification('에디터 초기화에 실패했습니다.');
}
};

툴바 옵션

에디터의 툴바를 다양한 화면 크기에 맞게 개별적으로 설정할 수 있습니다.

툴바 Props

  • toolbarButtons: 데스크톱 화면 (≥ 1200px)의 툴바 버튼
  • toolbarButtonsMD: 중간 크기 화면 (≥ 992px)의 툴바 버튼
  • toolbarButtonsSM: 작은 화면 (≥ 768px)의 툴바 버튼
  • toolbarButtonsXS: 매우 작은 화면 (< 768px)의 툴바 버튼

모든 툴바 옵션은 기본적으로 TOOLBAR_CONFIG_OPTIONS의 설정을 가지고 시작합니다.

TOOLBAR_CONFIG_OPTIONS 구성

{
moreText: {
buttons: [
'paragraphFormat',
'fontFamily',
'fontSize',
'textColor',
'backgroundColor',
'bold',
'italic',
'underline',
'strikeThrough',
'subscript',
'superscript',
'clearFormatting',
],
buttonsVisible: 4,
},
moreParagraph: {
buttons: [
'formatOL',
'formatUL',
'alignLeft',
'alignCenter',
'alignRight',
'alignJustify',
'lineHeight',
'outdent',
'indent',
'quote',
],
buttonsVisible: 2,
},
moreRich: {
buttons: ['insertImage', 'insertLink', 'insertTable', 'emoticons', 'specialCharacters', 'insertHR'],
buttonsVisible: 2,
},
moreMisc: {
buttons: ['undo', 'redo', 'fullscreen', 'selectAll', 'print', 'html', 'pastePlain'],
align: 'right',
buttonsVisible: 0,
},
}
import { FroalaEditor } from '@ncds/editor';

// 반응형 툴바 설정 예제
<FroalaEditor
value={content}
onChange={setContent}
// 데스크톱 전체 툴바
toolbarButtons={[
'bold',
'italic',
'underline',
'strikeThrough',
'fontFamily',
'fontSize',
'color',
'backgroundColor',
'insertTable',
'insertImage',
'insertVideo',
'insertLink',
]}
// 태블릿용 축소 툴바
toolbarButtonsMD={['bold', 'italic', 'underline', 'fontSize', 'color', 'insertImage', 'insertLink']}
// 모바일용 최소 툴바
toolbarButtonsSM={['bold', 'italic', 'underline', 'insertImage', 'insertLink']}
// 매우 작은 화면용 기본 툴바
toolbarButtonsXS={['bold', 'italic']}
/>;

툴바 커스터마이징 예제

const ToolbarCustomExample = () => {
const [content, setContent] = useState('');

// 기본 TOOLBAR_CONFIG_OPTIONS를 기반으로 한 커스텀 설정
const customToolbarButtons = [
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'fontFamily',
'fontSize',
'|',
'color',
'backgroundColor',
'|',
'formatOL',
'formatUL',
'outdent',
'indent',
'|',
'insertImage',
'insertTable',
'insertLink',
'|',
'undo',
'redo',
];

const mobileToolbarButtons = [
'bold',
'italic',
'underline',
'|',
'fontSize',
'color',
'|',
'insertImage',
'insertLink',
];

return (
<FroalaEditor
value={content}
onChange={setContent}
toolbarButtons={customToolbarButtons}
toolbarButtonsMD={customToolbarButtons}
toolbarButtonsSM={mobileToolbarButtons}
toolbarButtonsXS={['bold', 'italic', 'insertImage']}
/>
);
};
툴바 구분자

툴바 버튼 배열에서 '|'를 사용하여 버튼 그룹을 구분할 수 있습니다.