마지막으로

NicEdit를 수정하면서 몇 가지 개념을 정리한다.

nicEditorSelect의 자식들이

실제로 웹에서 사용되는 다음 그림을 보면 문제가 있다.

첫 번째는 설정 판넬의 크기가 작아서 항목들이 잘려서 보인다.

두 번째는 하나의 nicEditorSelect를 클릭하고,

다른 nicEditorSelect을 선택하면

앞서 선택한 nicEditorSelect의 설정 판넬이 그대로 있는 것이다.

버그를 수정하기 위해

NicEdit에서 데모 파일을 다운로드 받는다.

다운로드 페이지 하단의 압축되지 않은 파일 설정을 체크하고

(Uncompressed NicEdit for development) 다운로드 받는다.


demos 폴더에 있는 예제 중 적당한 파일을 선택하여 실행한다.

nicEdit.js만 수정하고

수정한 실행 결과를 보는 것이기 때문에

NicEdit가 실행되는 어떤 파일이든 관계가 없다.


준비가 되었으면

간단하게 이 두 가지 버그를 수정해 본다.

먼저 설정 판넬의 크기(width) 문제를 해결해 본다.

이 설정 판넬을 사용하는 것은 nicEditorSelect의 자식인

폰트명(nicEditorFontFamilySelect),

폰트 크기(nicEditorFontSizeSelect),

폰트 형식(nicEditorFontFormatSelect) 인데

모두 nicEditorSelect에서 설정 판넬(nicEditorPane)을

보이거나(open) 숨기고(close) 있다.

nicEditorSelect의 open함수를 보면

nicEditorPane의 크기(width)가 88px로 고정되어 생성되는 것을 알 수 있다.

    open: function() {
        this.pane = new nicEditorPane(this.items, this.ne, {
            width: "88px",
            padding: "0px",
            borderTop: 0,
            borderLeft: "1px solid #ccc",
            borderRight: "1px solid #ccc",
            borderBottom: "0px",
            backgroundColor: "#fff"
        });
        for (var C = 0; C < this.selOptions.length; C++) {
            var B = this.selOptions[C];
            var A = new bkElement("div").setStyle({
                overflow: "hidden",
                borderBottom: "1px solid #ccc",
                width: "88px",
                textAlign: "left",
                overflow: "hidden",
                cursor: "pointer"
            });
            var D = new bkElement("div").setStyle({
                padding: "0px 4px"
            }).setContent(B[1]).appendTo(A).noSelect();
            D.addEvent("click", this.update.closure(this, B[0])).addEvent("mouseover", this.over.closure(this, D)).addEvent("mouseout", this.out.closure(this, D)).setAttributes("id", B[0]);
            this.pane.append(A);
            if (!window.opera) {
                D.onmousedown = bkLib.cancelEvent
            }
        }
    },
    close: function() {
        if (this.pane) {
            this.pane = this.pane.remove()
        }
    }, 

this.selOptions은 설정 판넬에 출력할 항목들 (폰트명, 크기등)인데

마찬가지로 크기(width)가 88px로 고정되어 생성되는 것을 알 수 있다.

이 값을 대충 150px로 수정하고 실행한다.

그림과 같이 넓이 문제가 해결 되었다.

하지만, 크기가 너무 커서 여백이 남는 클래스도 있다.

따라서, 그림처럼 각각 자기의 크기를 갖는 것이 좋을 것이다.


해결 방법은 this.selOptions이 힌트가 되어 준다.

this.selOptions는 부모인 nicEditorSelect에서는

변수 선언이나 할당된 데이터를 볼 수 없다.

위 코드에서 사용된 것이 전부 이다.

즉, 이 변수는 자식에서 선언되고 부모에서 사용된다.

(실제로는 자식에서 sel로 선언되고 add 되면서 할당된다.)

자식 클래스인 폰트 크기(nicEditorFontSizeSelect)에서

다음 코드와 같이 panelWidth 변수에 "150px" 넣어서 생성한다.

이와 같은 방식으로 폰트명과 폰트 형식도 작성한다.

var nicEditorFontSizeSelect = nicEditorSelect.extend({
    panelWidth: "150px",
    sel: {
        1: "1&nbsp;(8pt)",
        2: "2&nbsp;(10pt)",
        3: "3&nbsp;(12pt)",
        4: "4&nbsp;(14pt)",
        5: "5&nbsp;(18pt)",
        6: "6&nbsp;(24pt)"
    },
    init: function() {
        this.setDisplay("Font&nbsp;Size...");
        for (itm in this.sel) {
            this.add(itm, '<font size="' + itm + '">' + this.sel[itm] + "</font>")
        }
    }
}); 

그리고, nicEditorSelect의 open함수에서

다음과 같이 panelWidth의 크기로 생성하도록 한다.

open: function() {
    this.pane = new nicEditorPane(this.items, this.ne, {
        width: this.panelWidth,
        padding: "0px",
        borderTop: 0,
        생략
    });
    for (var C = 0; C < this.selOptions.length; C++) {
        var B = this.selOptions[C];
        var A = new bkElement("div").setStyle({
            overflow: "hidden",
            borderBottom: "1px solid #ccc",
            width: this.panelWidth,
            생략
        }
    }
}, 

아주 간단하게

각각의 자식에 필요한 크기로 설정 판넬이 생성된다.

여기서 하나 더 생각해 보면

폰트 크기는 너비가(width) 넓지만

나머지 폰트명과 형식은 너비가 비슷해 보인다.

이 두 자식(실제로는 아주 많을 수 있다.)에서 코드를 입력하는 것이 귀찮다.

이 경우, 기본 값(default)을 주어서 해결할 수 있다.

즉, 부모에서 panelWidth 변수를 선언하고 값을 주는 것이다.

자식에서 재할당(선언) 하면 자식 값으로 사용되고(override),

그렇지 않으면 부모의 값이 사용되도록 하면 된다.


아주 간단하다.

다음 코드처럼 nicEditorSelect에 자식에게 넣었던 것을 그대로 작성하면 된다.

var nicEditorSelect = bkClass.extend({
    panelWidth: "100px",
    construct: function(D, A, C, B) {
        this.options = C.buttons[A];
 

즉, 부모(nicEditorSelect)에게서

panelWidth를 선언하고 100px을 할당한다.

자식 중에서 좀 큰 너비가 필요한

폰트 크기(nicEditorFontSizeSelect)는 150px을 할당하고,

비슷한 너비를 가진 나머지 자식은 아무 것도 하지 않는다.

즉, 부모에서 상속 받은 값을 그대로 사용하는 것이다.


두 번째 버그는 하나의 nicEditorSelect를 클릭하고,

다른 nicEditorSelect을 선택하면

앞서 선택한 nicEditorSelect의 설정 판넬이 그대로 있는 것이다.

이 것은 하나의 nicEditorSelect의 설정 판넬을 열 때(open),

이전의 nicEditorSelect의 설정 판넬을 닫아 주면 해결 된다.


var openedSelectBox = null;
var nicEditorSelect = bkClass.extend({
    panelWidth: "100px",
    construct: function(D, A, C, B) {
       ~~ 생략 ~~

    open: function() {
        if (openedSelectBox) openedSelectBox.close()
        openedSelectBox = this;
        this.pane = new nicEditorPane(this.items, this.ne, {

코드와 같이 전역 변수로 openedSelectBox를 선언하고,

nicEditorSelect의 open 함수가 실행될 때 (설정 판넬이 열릴 때)

현재 nicEditorSelect(this)를 openedSelectBox에 보관한다.

보관하기 전에 다음 코드와 같이 이전에 지정된 nicEditorSelect를

닫아주면(close)면 문제가 해결 된다.

nicEdit.js


간단하게 마지막 버그를 수정했지만

좋은 프로그램 방식은 아니다.

JS에서 전역 변수(openedSelectBox) 사용은 추천되지 않으며

객체 지향 프로그래밍으로 보기 어렵다.

JS에는 static변수가 없어서 전역 변수로 처리했지만

좋은 방법은 아니다.

조금 더 나은 방식은 openedSelectBox를 다른 클래스에 두는 것이다.

적절한 클래스는 버튼과 편집 창을 관리하는 niceditor일 것이다.

더욱이 niceditor는 nicEditorSelect가 생성될 때 파라미터로 넘어오고

nicEditorSelect내부에서 ne로 관리되기 때문이다.

따라서 niceditor에 openedSelectBox를 선언하고

addSelectList 메소드를 만들어서 추가하는 방식으로 구현하는 것이 좋다.

이 것은 연습 문제로  남겨둔다.






2019 년에 기능을 보강하면서 문서를 새로 작성하였습니다.




gu-upload는 HTML5 기반으로 파일을 업로드하는 JavaScript 라이브러리로

IE10 이상이나 Firefox, Chrome등 에서 작동한다.

IE9 이하에서는 SWFUpload를 이용하였다.

js 폴더에 있는 guupload 폴더를 복사해서 사용하거나

github에서 다운받아 사용하고자 하는 폴더에 복사한다.


개발하는 자신의 웹 페이지의 Head에

다음과 같이 CSS와 JavaScript를 포함한다.

<link rel="stylesheet" type="text/css" href="js/guupload/css/guupload.css"/>

<script type="text/javascript" src="js/guupload/guUploadManager.js"></script>

guupload를 복사할 경로를 설정해 줘야 한다.

예제에서는 js 폴더 하위에 있기 때문에

위 코드와 같이 각 경로가 js로 시작하고 있다.


웹 페이지 실행이 완료되면(window.onload)

guUploadManager를 생성하도록(new) 한다.

guUploadManager는 화면 구성과

웹 브라우저에 따라 guuplaod, swfupload를 실행하도록 하는 역할 등을 한다.

<script type="text/javascript">
var guManager=null;

window.onload = function() {
    var option = {
            listtype: "thumbnail",
            fileid: "guupload",
            uploadURL: "upload",
            form: document.form1
    }
    guManager = new guUploadManager(option);
}   

function formSubmit(){
    guManager.uploadFiles();
}
</script>

~~ 생략 ~~

        <tr>
            <td>Attach File</td>
            <td>
                <div id="guupload" class="guupload" style="width: 500px; height: 120px;">
                </div>
            </td>
        </tr>

생성시 파라미터는 listtype, fileid, uploadURL, form 4가지가 JSON 형태로 제공되어야 한다.

먼저 listtype은 파일 리스트 방식을 선택하는 것이다.

thumbnail로 지정하면 이미지 파일에 대하여 미리보기를 사용할 수 있다.

값을 지정하지 않으면 파일명과 파일크기가 리스트 형태로 출력된다.

fileid는 DIV id로 파일 업로드 라이브러리를 그리는 div를 의미하며,

지정된 DIV에 사용자가 보는 화면을 구성하게 된다.

uploadURL은 파일을 업로드 하면

서버에서 전송 받아 실제 파일로 저장하는 URL, 즉 컨트롤을 의미한다.

form은 제목, 내용, 파일 등 게시글과 관련된 항목을 가지고 있는 form 태그를 의미한다.

form은 사용자가 커밋을 하게 되면 formSubmit함수를 호출하여

파일을 먼저 서버에 저장하고,

파일 전송이 완료되면 guupload가 대신 submit을 하기 위한 것이다.


guuplaod는 파일명(filename), 실제 파일명(realname), 파일크기(filesize)의 세가지 정보를 제공한다.

실제 파일명(realname)는 파일을 서버에 저장하고 반환 받은 파일명을 의미한다.

이 값들은 다수의 파일을 전송 하기 때문에 콤마(,)로 구분하여 전송된다.

따라서 다음 코드와 같이 split로 구별하여 처리한다.

<%
    String filename = request.getParameter("filename");
    String realname = request.getParameter("realname");
    String filesize = request.getParameter("filesize");
    String[] reallist = realname.split(",");
    String[] filelist = filename.split(",");
    String[] sizelist = filesize.split(",");

    for (int i=0; i<filelist.length; i++) {
        out.println(filelist[i] + " : " + reallist[i] + " : " + sizelist[i] + "<br/>");
    }               
%>

upload_save.jsp


제공되는 디자인이 맘에 들지 않고, CSS를 알고 있다면

guupload.css 파일에서 수정하여 사용하면 된다.

대부분의 디자인을 수정할 수 있는 CSS 클래스를 포함하고 있다.

gu-upload는 HTML5 기반으로 파일을 업로드하는 JavaScript 라이브러리이다.

여기서는 gu-upload를 사용하는 예제 설치 방법을 설명한다.


먼저, Eclipse에서 다음 주소로 github 소스를 다운로드 받는다.

https://github.com/gujc71/guupload_sample.git

예제 소스는 JDK 1.7, Spring 4, Maven 환경에서 작성되었다.

gu-upload는 JavaScript 라이브러리이기 때문에

서버는 Struts, PHP, 닷넷(.NET) 등 무엇이든 관계 없지만 

개인적인 편의로 Java Spring 기반으로 예제를 작성하였다.


프로젝트를 Eclipse Tomcat에 추가 해서 실행한 후

다음과 같이 웹브라우저로 접속하면 데모 화면을 볼 수 있다.

http://localhost:8080/guupload/

예제는 게시판(자료실) 글 등록 화면처럼 구성하였다.

먼저, 예제 1은 IE 9 이하(호환성 모드)의 웹 브라우저에서 실행되는 것으로 예제는 IE8로 지정하였다.

<meta http-equiv="X-UA-Compatible" content="IE=8" />

내부적으로는 SWFUpload가 실행 된다.

예제 2는 IE 10 이상이나 Firefox, Chrome등(HTML 5)에서 실행되는 것으로

Gu-Upload의 List모드가 실행된 예제이다.

탐색기에서 파일을 끌어다 놓을 수 있다(Drag & Drop).

예제 3은 Gu-Upload의 Thumbnail모드가 실행된 예제이다.

이미지를 첨부한 경우 미리보기를 할 수 있다.


Submit 버튼을 눌러 서버로 파일을 전송해서 저장하려면

Upload.java 파일에 설정된 저장 경로를 수정해야 한다.

기본으로 D 드라이브가 지정되어 있으니 자신의 디렉터리에 맞추어 수정해야 한다.

   @RequestMapping(value = "/upload")
    public void upload(HttpServletResponse response, HttpServletRequest request, @RequestParam("Filedata") MultipartFile Filedata) {
           SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
           String newfilename = df.format(new Date()) + Integer.toString((int) (Math.random()*10));
          
           File f = new File("d:\\" + newfilename);
           try {
               Filedata.transferTo(f);
               response.getWriter().write(newfilename);
           } catch (IllegalStateException | IOException e) {
               e.printStackTrace();
           }
    }

guupload_sample\src\main\java\gu\Upload.java

업로드 컨트롤에서는 날자와 시간으로 파일을 저장한다.

파일 확장자가 없이 새로운 이름으로 저장되는데

보안을 위해 실제 파일명(realname), 파일명(filename)을 구분하여 처리하기 때문이다.

자세한 내용은 블로그의 자료실 관련 내용을 참고하면 된다.


Submit 버튼을 누르면 upload_save.jsp 가 실행되어

서버로 전송된 값을 확인할 수 있다.


서버로 전송된 값은

파일명(filename), 실제 서버에 저장한 파일명(realname), 파일 크기(filesize) 이다.


+ Recent posts