본문 바로가기
Project/블로그 프로젝트

네이버 블로그 만들기 프로젝트(6) - Naver SmartEditor 2.0으로 게시글 만들기(+DB연결)

by 히앤님 2022. 11. 4.
반응형
SMALL

Naver SmartEditor 2.0으로 게시글 만들기(+DB연결)

네이버 블로그처럼 블로그 내용을 다채롭게 작성하고 싶었다. 찾아보니 Naver SmartEditor가 오픈소스로 나와있길래 사용하기로 했다.

현재 네이버 블로그에 적용된 3.0은 오픈소스로 공개가 되어있지 않고, 2.8까지 사진 업로드가 가능하다고 해서 2.8로 사용하기로 함.

[ 만들 기능 ]
1. 게시글 작성 페이지에 발행 누르면 DB 저장
2. 저장 후 게시글 상세보기 페이지에서 읽을 수 있도록 연결

1. Naver SmartEditor 2.0 설치하기

요 블로그 참고했다.(https://beforb.tistory.com/53)

스마트 에디터 2.8.2 다운로드 : https://github.com/naver/smarteditor2/releases/tag/v2.8.2.3

 

Release v2.8.2.3: archive v2.8.2.4259f59 · naver/smarteditor2

2.8.2.3 보안 패치 file_uploader_html5.php의 null byte injection 취약점 추가 보완 file_uploader.php의 리다이렉트 취약점 보완 sample.php에서 XSS filtering을 위해 HTMLPurifier라이브러리를 적용 sample.php -> sample/viewer/in

github.com

resources > static > smarteditor 에 다운받은 파일들을 넣어주었다.

resources > static > smarteditor 붙여넣기

스마트 에디터를 출력할 페이지에 js를 import 해준다.

<script type="text/javascript" src="../resources/static/smarteditor/js/HuskyEZCreator.js" charset="utf-8"></script>

포스트 추가할 곳에 textArea 설정해주자.

<div class="row">
    <!-- 포스트 추가하는 곳-->
    <!-- Naver SmartEditor 2.8.2를 사용하였습니다. -->
    <h3>Naver Smart Editor 2.0</h3>
    <form action="insertStudentInfoForm" method="post">
        <div id="smarteditor">
            <textarea
                name="editorTxt"
                id="editorTxt"
                rows="20"
                cols="10"
                placeholder="내용을 입력해주세요"
                style="width: 500px"></textarea>
        </div>
        <input type="button" value="내용콘솔에" onclick="submitPost()"/>
    </form>
    <!-- 포스트 추가하는 곳-->
</div>

스크립트를 설정해준다. 아까 resources > static > smarteditor 경로에 있던 SmartEditor2Skin.html 파일이 에디터 화면이다. sSkinURI 경로를 주의해서 스크립트를 작성해준다.

	     /* 에디터 설정 */
	    let oEditors = [];
	
	    smartEditor = function() {
	      console.log("Naver SmartEditor")
	      nhn.husky.EZCreator.createInIFrame({
	        oAppRef: oEditors,
	        elPlaceHolder: "editorTxt",
	        sSkinURI: "../resources/static/smarteditor/SmartEditor2Skin.html",
	        fCreator: "createSEditor2"
	      })
	    }
	
	    $(document).ready(function() {
	      smartEditor()
	    })

 

++추가) html도 못읽길래 sever>web.xml에 추가해줬다.

    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
        <!-- 아래에 js 및 css 확장자 추가 -->
        <url-pattern>*.js</url-pattern>
        <url-pattern>*.css</url-pattern>
        <url-pattern>*.jpg</url-pattern>
        <url-pattern>*.ico</url-pattern>
        <url-pattern>*.gif</url-pattern>
        <url-pattern>*.png</url-pattern>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

 

저장 버튼 누르면 DB로 스마트에디터에 작성한 내용이 저장되야하므로 일단 콘솔에서 띄워본다. 버튼클릭 이벤트를 만들어서 console.log를 찍어보자.

 /* 버튼 클릭 이벤트 */
	    submitPost = function() {
		  oEditors.getById["editorTxt"].exec("UPDATE_CONTENTS_FIELD", [])
		  let content = document.getElementById("editorTxt").value
		
		  //if(content == '') {
            if(content == '<p>&nbsp;</p>') { //비어있어도 기본 P태그가 붙더라.
		    alert("내용을 입력해주세요.")
		    oEditors.getById["editorTxt"].exec("FOCUS")
		    return
		  } else {
		    console.log(content)
		  }
		}

결과창을 확인하면 잘 나온다.

결과

 

2. 스마트 에디터에 작성한 내용 ajax로 DB에 저장해주기

 1) jsp

일단 제목과 내용을 제대로 저장시켜주는 것이 중요하므로, 썸네일과 기타 다른 데이터들은 정적데이터로 넣어주고 연결부터 하기로 함.

   /* 버튼 클릭 이벤트 */
	    function submitPost(){

		  oEditors.getById["editorTxt"].exec("UPDATE_CONTENTS_FIELD", []);
		  //content Text 가져오기
		  let content = document.getElementById("editorTxt").value;
		
		  if(content == '<p>&nbsp;</p>') { //비어있는 경우
		    alert("내용을 입력해주세요.")
		    oEditors.getById["editorTxt"].exec("FOCUS")
		    return;
		  } else {
		    //console.log(content);
			 let writePost = {
	          title: $("#title")[0].value
	          ,content: content
	        }
			 
		    //ajax 통신으로 서버로 보내 데이터 저장함
		    $.ajax({
	          url: "postInsertAjax"
	          , data: writePost
	          , type: 'POST'
	         , success: function(data) {
	            console.log('success')
	            alert('저장되었습니다.')
	            location.href='./myBlogAction.me'
	          }
	          , error: function(jqXHR, textStatus, errorThrown) {
	            console.log(jqXHR)
	            alert('오류가 발생하였습니다.')
	          }
	        })
		  }
		}

간단하게 writePost 라는 변수를 선언해서 여기에 title이랑 content를 key,value 형태로 담아서 보내주었다.

 2) controller

[새글쓰기] 버튼을 눌렀을 때에도 바로 jsp 페이지가 아니라 me로 끝나는 페이지로 이동하도록 설정해주자.

또한 입력한 데이터를 DB에 저장해주는 Action 페이지도 만든다.

//-------------------- 새글작성 버튼 누르면 게시글 작성 페이지로 이동 ---------------------------
else if(command.equals("/member/postInsert.me")) {
    //게시글 작성 페이지로 이동함
    forward = new ActionForward();
    //객체 생성
    forward.setRedirect(false);
    //이동 허락 안함
    forward.setPath( "./postEdit.jsp"))
    //거기 주소는 postEdit.jsp으로 해라.(이동할 주소 저장)
}
//-------------------- 작성한 블로그 포스팅을 저장하는 postInsertAction 페이지 생성 ---------------------------				
else if (command.equals("/member/postInsertAjax")) {
    action = new PostInsertAction();
    try {
        forward = action.execute(req, resp); //받은 action을 뜯어서 SQL로 보내준다.(서버요청)
    }
    catch (Exception e) {
        e.printStackTrace();
        System.out.println( "포스팅 저장 실패"))
    }
}

 3) Action

postInsertAjax 로 접속했을 때 이동할 PostInsertAction 페이지를 만들어준다.

/* 인터페이스를 함께 추가했다. 작성한 포스트 저장 처리를 위한 클래스이다.*/
public class PostInsertAction implements Action { // Action을 implements 해줌

	@Override
	public ActionForward execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {

		PostBean post = new PostBean(); //vo에 선언한 변수들 import한거.
		//작성한 게시글 내용을 저장하고 DB로 전달.

		ActionForward forward = null;

		//session을 써서 서버 생성함.
		HttpSession session = req.getSession();
		// 세션에서 id값 가지고 있기. -> 이걸로 post_info의 mem_no를 넣어줄 것임.
		String sessionId = (String) session.getAttribute("id");
		
		post.setMEM_ID(sessionId);
		//입력 목록 적어주기(vo에서 받아옴.)
		post.setPOST_TITLE(req.getParameter("title"));
		post.setPOST_THUMBNAIL(req.getParameter("thumbnail"));
		post.setPOST_CONTENT(req.getParameter("content"));
		
		PostInsertService postInsertService = new PostInsertService();
		boolean InsertResult = postInsertService.insertPost(post); //vo에서 받은 변수 보내줌.
		//게시글 저장이 잘 되었는지 여부
		       
		//ajax 통신이라 서버에서 안먹히나? 이 아래로 안됨...
		System.out.println(InsertResult);
		if (InsertResult) { //게시글 저장 후 내 블로그로 이동

			resp.setContentType("text/html;charset=UTF-8");
			PrintWriter out = resp.getWriter();
			out.println("<script>");
			out.println("alert('저장되었습니다.')");
			out.println("location.href='./myBlogAction.me");
			out.println("</script>");
		} else { //게시글 저장 실패 시
			resp.setContentType("text/html;charset=UTF-8");
			PrintWriter out = resp.getWriter(); //위에 있음
			out.println("<script>");
			out.println("alert('게시글 저장 실패')");
			out.println("history.back()");
			out.println("</script>");
		}
		return forward;
	}
}

4) Service

DB 연결한다.

/* 게시글을 DB에 저장하는 역할을 담당하는 클래스(Service) */
public class PostInsertService {
	
	public boolean insertPost(PostBean post) {
		
		boolean joinSuccess = false; //저장완료 기본값 false
		PostDAO postDAO = PostDAO.getInstance();
		// 객체에 저장된 고객 정보를 DB로 전달하는 클래스.
		// DAO : Data Access Object
		Connection con = getConnection();
		// DB와 jsp간의 연결을 해주는 것을 담당함.
		postDAO.setConnection(con);
		int insertCount = postDAO.insertPost(post);
		// DB추가 쿼리를 메소드로 구현.

		if (insertCount > 0) {
			joinSuccess = true;
			// 정상 처리 되었음을 호출했던 곳으로 리턴.
			commit(con); //DB 명령 완료 확인
		} else {
			rollback(con);
			//만약 제대로 동작하지 않았다면
			//쿼리문에 대해서 취소(rollback)
		}
		close(con);
		//연결되지 않았으면 연결 해제(DB와 Connection의 연결 해제)

		return joinSuccess;
	}
}

4) DAO

update를 한다. 받아온 데이터는 두개이고 나머지는 나중에 세팅해줄 것이므로 정적으로 넣어준다.

//=========================== 게시글 저장하는 SQL로직 ===============================
		// PostInsertService에서  게시글 저장할 때 DB와 JSP를 연결할 때 인자로 쓰임.
		public int insertPost(PostBean post) {
			//게시글 저장할 때 SQL문(DB 이름 확인하기***)
			String sql = 
				"INSERT INTO post_info("
						+ "post_no,"
						+ "mem_no,"
						+ "post_title,"
						+ "post_thumbnail,"
						+ "POST_VIDEO,"
						+ "POST_CONTENT,"
						+ "VISIT_CNT,"
						+ "POST_UPLOADTIME"
				+ ") "
				+ "VALUES( "
						+ "(SELECT NVL(MAX(post_no), 0) + 1 FROM post_info)," //post_no
						+ "(SELECT mem_no FROM memberinfo WHERE mem_id = ?)," //mem_no 찾기
						+ "?," //post_title
						+ "'섬네일.jpg'," //post_thumbnail
						+ "'비디오.avi',"
						+ "?," //POST_CONTENT
						+ "0," //VISIT_CNT
						+ "TRUNC(SYSDATE)" //POST_UPLOADTIME
				+ ")";
			
			int insertCount=0;

			try {
				pstmt = con.prepareStatement(sql); 
				//prepareStatement : SQL문 실행하는 기능을 갖는 객체(변수는 ?로, setString으로 아래에 지정함.)
				pstmt.setString(1, post.getMEM_ID()); //sessionId로 mem_no 찾기
				pstmt.setString(2, post.getPOST_TITLE());
				pstmt.setString(3, post.getPOST_CONTENT());

				insertCount=pstmt.executeUpdate(); //executeUpdate : 데이터베이스 변경할 때
				//select는 executeQuery()를 사용한다.
				// insert, update, delete는 executeUpdate()를 사용한다.
				//정상적으로 된다면 insertCount가 1이 된다.
				System.out.println("게시글 저장2");
			} catch (Exception ex) {
				System.out.println("게시글 저장 안됨" + ex);
				

			} finally {
				close(pstmt); // import static db.JdbcUtil.*;
			}
			return insertCount;
		}

Update 및 delete 등 데이터베이스를 변경할 때에는 executeUpdate를 사용한다.

SQL문을 뜯어보자.

a) post_no : 최댓값 +1 을 한다.

"(SELECT NVL(MAX(post_no), 0) + 1 FROM post_info)," //post_no

b) mem_no :  세션에 있는 id로 찾아서 넣어줬다.

"(SELECT mem_no FROM memberinfo WHERE mem_id = ?)," //mem_no 찾기

4) VISIT_CNT:  기본값 0이라 그냥 0 넣어줌.

5) POST_UPLOADTIME:  현재 날짜 넣어줌

"TRUNC(SYSDATE)" //POST_UPLOADTIME

DB 설정값 다시 확인


다음은 이 ajax 통신을 멀티파트로 해서 이미지도 함께 업로드 해주겠다.

 ▼ postEdit.jsp 소스 전체보기

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ page import="vo.MemberBean"%>
<%@ page import="java.util.*"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!-- 빨간줄 그어지는데 jstl 설치 안해서 그럼. lib에 넣어준다. jsp 확장태그로 c로 쓴다.-->


<!DOCTYPE html>
<html lang="en">
    <head>
    <!-- 참고 : https://beforb.tistory.com/53 -->
    <!-- 참고 : https://www.wrapuppro.com/programing/view/ZPLdxEBiyJG38IG -->
    <!-- 에디터 : Naver SmartEditor 2.0 -->
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>게시글 작성</title>
        <!-- 제이쿼리 -->
		<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.js"></script>
        <!-- Favicon-->
       <!-- <link rel="icon" type="image/x-icon" href="assets/favicon.ico" /> -->
        <!-- Core theme CSS (includes Bootstrap)-->
         <link href="../resources/css/mainPage.css" rel="stylesheet" />
      <script type="text/javascript" src="../resources/static/smarteditor/js/HuskyEZCreator.js" charset="utf-8"></script>    </head>
    <body>
        <!-- Responsive navbar-->
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
            <div class="container">
                <a class="navbar-brand" href="#!">N Blog - 새 게시글 작성</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
                        <li class="nav-item"><a class="nav-link" href="#">Home</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">About</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">Contact</a></li>
                        <li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Blog</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        <!-- Page content-->
        <div class="container mt-5">
            <div class="row">
                    <!-- 포스트 추가하는 곳-->
                    <!-- Naver SmartEditor 2.8.2를 사용하였습니다. -->
                    
				    <form action="insertStudentInfoForm" method="post">
				    	<!-- 제목 -->
				      <input type="text" placeholder="제목을 입력하세요" id="title" style='width: 600px'>
				      
				      <div id="smarteditor">
				      <!-- 우선 멤버와 썸네일은 정적으로 데이터를 넣어둠. -->
				      <input type="hidden" value="일단 썸네일임" id="thumbnail">
				      
				        <textarea name="editorTxt" id="editorTxt" 
				                  rows="20" cols="10" 
				                  placeholder="&#13;&#10;내용을 입력하세요"
				                  style='width: 600px'></textarea>
				                  
				      </div>
				      <input type="button" value ="저장" onclick="submitPost()"/>
				    </form>
                    
                
              <!-- 포스트 추가하는 곳-->
            </div>
        </div>
        <!-- Footer-->
        <footer class="py-5 bg-dark">
            <div class="container"><p class="m-0 text-center text-white">Copyright &copy; Your Website 2022</p></div>
        </footer>
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
        <!-- Core theme JS-->
     <!--   <script src="js/scripts.js"></script>-->
	     <script>
	     
	     /* 에디터 설정 */
	    let oEditors = [];
	
	    smartEditor = function() {
	      nhn.husky.EZCreator.createInIFrame({
	        oAppRef: oEditors,
	        elPlaceHolder: "editorTxt",
	        sSkinURI: "../resources/static/smarteditor/SmartEditor2Skin.html",
	        fCreator: "createSEditor2"
	      })
	    }
	
	    $(document).ready(function() {
	      smartEditor()
	    })
	    
	    
	    /* 버튼 클릭 이벤트 */
	    function submitPost(){
	 
		  oEditors.getById["editorTxt"].exec("UPDATE_CONTENTS_FIELD", []);
		  //content Text 가져오기
		  let content = document.getElementById("editorTxt").value;
			console.log(content);
		  if(content == '<p>&nbsp;</p>') { //비어있는 경우
		    alert("내용을 입력해주세요.")
		    oEditors.getById["editorTxt"].exec("FOCUS")
		    return;
		  } else {
		    //console.log(content);
			 let writePost = {
	          title: $("#title")[0].value
	          ,content: content
	        }
			 console.log(writePost);
		    //ajax 통신으로 서버로 보내 데이터 저장함
		    $.ajax({
	          url: "postInsertAjax"
	          , data: writePost
	          , success: function(data) {
	            console.log('success')
	            alert('저장되었습니다.')
	            location.href='./myBlogAction.me'
	          }
	          , error: function(jqXHR, textStatus, errorThrown) {
	            console.log(jqXHR)
	            alert('오류가 발생하였습니다.')
	          }
	        })
	        
		  }
		}
	    
	  </script>
    </body>
</html>

 

▼ 진행중인 gitHub 링크를 남깁니다.

 

GitHub - k-haein/BlogPrj: 회원가입/로그인/게시글 등록 등의 블로그를 처음부터 만듭니다.

회원가입/로그인/게시글 등록 등의 블로그를 처음부터 만듭니다. Contribute to k-haein/BlogPrj development by creating an account on GitHub.

github.com

 

반응형
LIST

댓글