[React] CKEditor 기본 개념 & initData 속성

moongyu·2021년 8월 6일
0

React

목록 보기
1/4
post-thumbnail

CKEditor란?

  • 콘텐츠를 웹 페이지나 온라인 애플리케이션에 직접 작성할 수 있게 하는 위지위그 리치 텍스트 에디터이다. CKEditor는 오픈 소스와 상용 라이선스로 이용할 수 있다.

    * 위지위그(WYSIWYG) : What You See Is What You Get, “보는 대로 얻는다”

    → 말 그대로 웹사이트 게시판의 글쓰기 기능에서 흔히 볼 수 있는 텍스트 편집기이다. 텍스트 에디터 중에서는 상당히 유명한 편인 것 같고 공식 문서도 잘 정리되어있다.


1. 설치 방법

  • React & node.js 환경에서 npm install을 통해 설치했다. 참조
	npm i ckeditor4-react

2. 예시

  • React 컴포넌트에 import 하여 사용 가능하다.
	import { CKEditor } from "ckeditor4-react";

		...

	return( <CKEditor /> )

  • 아래 이미지처럼 텍스트 에디터를 얻어올 수 있다😀

3. initData 속성

  • 개발 과정에서 텍스트 에디터의 content 부분을 다루는 데 어려움을 겪었다. 게시물에서 수정 버튼을 누르면 기존의 내용이 그대로 텍스트 에디터의 content 부분에 반영된 채로 추가 수정이 가능하기를 원했다. 그러나 CKEditor를 불러오는 컴포넌트의 path는 새로운 게시물을 쓰든 기존의 게시물을 수정하든 모두 /board/write로 동일하기 때문에 컴퓨터는 이게 새 게시물인지 원래 있던 게시물인지 알 수가 없는 상황이었다..

  • 💡 initData 속성을 활용하여 해결!

    PropTypeDefaultDescription
    initDatanodeundefinedEditor’s content will be populated with initData once it’s initialized.

    initData 속성을 설정해주면 텍스트 에디터를 불러올 때 해당 content가 포함된 채로 나타난다😀

    1) 현재 게시글에서 수정 버튼을 누르면 react-router-dom의 NavLink 기능 통해 /board/write로 이동시켜주는데, 이때 query에 게시글의 title, content, _id 정보를 넘겨준다.

    즉, 기존의 게시글을 수정하는 경우 props.location.query가 존재한다. 반대로 새로운 게시글을 작성하는 경우 query는 존재하지 않는다.

    2) 위 사실을 바탕으로 먼저 query의 존재 여부를 체크한다. 그런 다음 존재한다면 initData 속성으로 설정해주었다. 존재하지 않는 경우 자연스럽게 undefined로 설정되며 텍스트 에디터 화면에 아무것도 출력되지 않는다.
//BoardWriteForm.js
<CKEditor initData={props.location.hasOwnProperty('query') && props.location.query.content} 
	data={Content} onChange={contentChangeHandler}></CKEditor>

4. 전체 코드

import React, {Component} from 'react';
import { Table, Button } from "react-bootstrap";
import {NavLink} from 'react-router-dom';
import axios from 'axios';
import './css/BoardDetail.css'
axios.defaults.withCredentials = true;
const headers = {withCredentials: true};

class BoardDetail extends Component {
    constructor(props) {
        super(props);
        this.state = {board: []};
      }
    
    componentDidMount() {
        if (this.props.location.query !== undefined) {
            this.getDetail();
        } else {
            window.location.href="/board";
        }
    }

    deleteBoard = _id => {
        const send_param = {
            headers,
            _id
        };
        if (window.confirm("정말 삭제하시겠습니까?")) {
            axios
                .post("/api/board/delete", send_param)
                //정상!
                .then(returnData => {
                    alert("삭제되었습니다.");
                    window.location.href="/";
                })
                //에러
                .catch(err => {
                    console.log(err);
                    alert("삭제에 실패했습니다.")
                });
        }
    };

    getDetail = () => {
        const send_param = {
            headers,
            _id: this.props.location.query._id //게시물 고유번호
        };
        const marginBottom = {
            marginBottom: 5
        };
        axios
            .post("/api/board/detail", send_param)
            //정상!
            .then(returnData => {
                console.log(returnData.data.board[0])
                if (returnData.data.board[0]) { //해당 게시물이 있으면
                    const board = (
                        <div style={{width:'60%', margin:'auto'}}>
                            <Table striped bordered hover>
                                <thead>
                                    <tr>
                                        <th id='title' className='content'>{returnData.data.board[0].title}</th>
                                    </tr>
                                    <tr>
                                        <td id='writer' className='content'>
                                            <span>{`Writer  `}</span>
                                            {returnData.data.board[0].name}
                                        </td>
                                    </tr>
                                    <tr>
                                        <td id='date' className='content'>
                                            <span>{`Date  `}</span>
                                            {returnData.data.board[0].createdAt.substring(0, 10)}
                                        </td>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr>
                                        <td className='content' colSpan="2" dangerouslySetInnerHTML={{
                                            __html: returnData.data.board[0].content
                                        }}/>
                                    </tr>
                                </tbody>
                            </Table>
                            <div>
                            <br/>
                        <NavLink
                                to={{
                                    pathname: "/board/write",
                                    query: {
                                        title: returnData.data.board[0].title,
                                        content: returnData.data.board[0].content,
                                        _id: this.props.location.query._id
                                    }
                                }}
                        >
                            <Button block style={marginBottom}>
                                글 수정
                            </Button>
                        </NavLink>
                        <Button
                            block
                            onClick={this.deleteBoard.bind(
                                null, this.props.location.query._id
                            )}>
                                글 삭제
                            </Button>
                            </div>
                        </div>
                    );
                    this.setState({board: board});
                }
            })
            //에러
            .catch(err => {
                console.log(err);
            });
    };

    render() {
        const divStyle = {
            margin: 50
        };
        return <div style={divStyle}>{this.state.board}</div>;
    }
}

export default BoardDetail;




References

https://ko.wikipedia.org/wiki/CKEditor
https://ckeditor.com/ckeditor-4
https://www.npmjs.com/package/ckeditor4-react

profile
개발 블로그 (●'◡'●)

0개의 댓글