본 내용은 (주)다모넷 김종호 차장이 인터넷 자료를 토대로 작성한 사내 문서에서 발췌한 것이다.




- Checkstyle는 Java의 소스코드(.java파일)의 기술형식 코딩규약에 준하고 있는지를 체크하는 오픈 소스의 정적 해석 툴입니다.

- 정적 해석(Static program analysis):  실제 실행 없이 컴퓨터 소프트웨어를 분석하는 것을 말하는 것으로, 자동화된 툴을 사용한 분석, 또는 코드 검토 등의 활동을 의미합니다.


ㄱ. 소프트웨어 설치

- 이클립스에서 Software 설치 창으로 이동한다.

- 이클립스 실행 > Help > Install New Software


ㄴ. 업데이트 사이트 주소추가

- 주소 명: http://eclipse-cs.sf.net/update/


ㄷ. 설치 플러그인 선택 - Checkstyle


ㄹ. 설치과정 확인

Eclipse가 재시작되면 설치가 완료된 것이다


Eclipse 메뉴에서 Window > Show View > Other를 선택한다.

위 그림과 같이 Checkstyle > Checkstyle violations를 선택하고 OK한다.

Eclipse 하단에 Checkstyle violations 탭이 추가된 것을 확인할 수 있다.





'Java > 게시판 기타: 품질 등' 카테고리의 다른 글

5. PMD와 예외처리(Exception)  (2) 2016.05.07
1. 프로그램 작성 외의 것들  (1) 2016.04.24
3. CheckStyle II  (0) 2016.04.23
4. FindBugs  (0) 2016.04.23

본 게시판 예제의 프로젝트를 선택하고 마우스 오른쪽 버튼을 눌러 Properties를 실행한다.

Checkstyle에서 "Checkstyle active for this project"를 체크하고 OK를 선택한다.


다음 그림과 같이 규칙에 어긋난 내용들이 나타나게 된다.

먼저 하단의 "Checkstyle violations" 뷰를 보면 프로젝트의 오류 리스트들이 종류별 정리되어 나타난다.

이 규칙은 위 그림에서 "Google Checks"을 선택했기 때문에 나오는 것으로 상세한 규칙은 구글 사이트번역 자료를 확인해 보길 바란다.


위 오류를 해결하는 방법은 2가지가 있다.

- 첫 번째 방법은 오류대로 수정하는 것이고,

- 두 번째 방법은 규칙을 수정하는 것이다.


규칙을 수정하는 이유는 현재 사용한 규칙이 구글의 방식이고

다소 타이트하게 구성되었기 때문에 각자에 맞게끔 수정해서 사용하는 것이 좋다.

개인적으로 예제로 사용된 구글 규칙은 Java 어플리케이션 규칙으로

웹일 경우는 다소 다르게 적용하는 게 좋을 것 같다.

Java 웹도 클래스 등을 사용하고 상속의 개념을 이용하지만 약하게 사용되고

특히 Spring은 클래스 개념이 조금 무시되기도 한다

(Spring은 다른 패키지 경로에 동일한 클래스가 있으면 오류 발생).


오류 수정을 위해
- 현재까지 진행된 보드(board1~4)를 수정하면 제공된 예제가 모두 정상이 되기에

  board4를 복사해서 board41로 만든다.

  SQL, JSP는 제외하고 Java 파일만 복사해서 수정해 본다.


- 규칙도 구글 원본을 수정하기 보다는 복사해서 별도의 규칙 파일을 만들어서 사용한다.


먼저, board4를 복사해서 board41로 만들고 클래스도 동일하게 바꾸어 준다.

다음으로 Eclipse의 windows > Preferences 메뉴를 실행한다.

Google Chceks를 선택하고 Copy 버튼을 눌러서 복사한다.


새로운 규칙 이름을 My Check로 입력하고 OK한다.


다음으로 프로젝트(board)를 선택하고 마우스 오른쪽 메뉴를 눌러서 컨텍스트 메뉴를 실행한다.

메뉴에서 Properties를 실행한후 왼쪽 트리에서 Checkstyle을 선택한다.

화면 중앙의 콤보 박스(Simple)에서 My Checks를 선택하고 OK 한다.

위 그림에서 오른쪽에 있는 Configure 버튼을 기억해야 한다.


Checkstyle violations 뷰에서 Marker count 컬럼명을 클릭하여 오류가 많은 순으로 정렬한다.


오류의 수는 진행 상태에 따라 개수가 바뀔 수 있다.


1. Line contains a tab character가 557회 발생했다.

탭(Tab) 키 사용을 금지하는 것으로 리스트에서 해당 항목을 더블 클릭한다.

831개의 오류가 난 파일들이 나오게 된다. 

확인을 위해 앞서 만든 board41Ctr.java를 찾아서 다시 더블 클릭하면 다음 그림과 같은 화면이 나온다 (아무 파일을 해도 상관없다).

소스 코드 앞에 돋보기에 마우스를 대면 오류를 확인할 수 있다.

Eclipse의 바꾸기(Repalce)를 이용하여 탭을 공백(Space) 4개로 바꾸어 주면 돋보기들이 사라진다.


board41Svc 도 같은 방식으로 수정해 준다.

남는 돋보기는 다른 오류이니 넘어가도 된다.

앞서 오류가 난 39라인의 돋보기를 보면 오류명이 바뀌어 있다.

Checkstyle violations 뷰에서 board41Ctr.java 파일이 사라지고 없다.

탭키는 자주 사용되는 것으로 탭키를 누르면 공백으로 바꾸어 주는 것이 좋다.

구글에서 "eclipse tab to space"로 검색해서 방법을 익혀두면 좋을 것이다.


2. ‘X’ have incorrect indentation level X, expected level should be X가 467회 발생했다.

더블 클릭해서 상세 정보를 보면 들여쓰기 오류인 것을 알수 있다.

2번째(Level 2)에 있어야 하는데 4번째(Level 4)에 있다는 의미로 게시판 예제는 4개씩 들여썻고,

구글은 2칸씩 들여쓰기하고 있다.

프로젝트(board)를 선택하고 마우스 오른쪽 메뉴를 눌러서 컨텍스트 메뉴를 실행한다.

Properties > Checkstyle를 선택하고 Configure 버튼을 클릭하여 다음 화면을 실행한다.

왼쪽 트리에서 Miscellaneous를 선택하고 Indentation의 체크를 해제한뒤 OK 한다.

‘X’ have incorrect indentation level X, expected level should be X뿐 아니라 

‘X’ child have incorrect indentation level X, expected level should be X (353회)도 사라지고 없다.

둘다 들여쓰기 규칙을 의미하는 것으로 위 설정으로 해당 규칙 적용이 되지 않아서 사라졌다.


3. Import statement for 'X' is in the wrong order. Should be in the 'X' group, expecting not assigned imports on this line. (65회)

이것은 Java의 import에 대한 규칙으로 구글 명명 규칙 번역 자료에 상세하게 나와 있다.

이 규칙도 다음 그림과 같이 Properties > Checkstyle | Configure > Import에서 "Custom Import Order"의 체그를 해제한다.


4. Missing a Javadoc comment가 29회 발생했다.

주석에 대한 것으로 현재는 //로 되어 있는 것을 그림과 같이 /* */로 작성해야 한다.

다만 지켜야 할 것이 시작시 /** 이고 문장은 반드시 점(.)으로 끝나야 한다.

이 주석들은 모아져 Javadoc(API 문서)으로 생성되는데 생성 방식은 구글링 해보길 바란다.

개인적으로 웹 프로그래밍에 Javadoc(API 문서)이 필요한 지와 사용여부에 의문이 있지만

주석은 꼭 필요한 것이라 함수의 파라메터에 대한 설명등 보다 많은 주석을 작성하는 것이 좋다.


5. Name 'X' must match pattern 'X'가 19회 발생했다.

상세 화면에서는 Type name 'board41Ctr' must match pattern '^[A-Z][a-zA-Z0-9]*$'.로 나온다.

이것은 정규식을 사용한 것으로 클래스명의 첫 글자를 대문자로 하라는 것이다.

board41Ctr, board41Src, boardVO를 Board41Ctr, Board41Src, BoardVO로 바꾸어 준다.

이것은 Java의 기본 명명 규칙으로 클래스명은 대문자로 시작하고

인스턴스는 소문자로 시작해서 둘을 구분짓는 것이다.

상 세 정보에서 이외에도Local variable name 'p' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$', Parameter name 'Filename' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$'. 가 있다.

해당 코드에서 돋보기를 보면 더 많은 오류가 동시에 발생하는데

이 오류는 변수명이 너무 짧고 파리메타의 변수명은 소문자로 시작해야 한다는 것이다.

 board41Ctr, board41Src, boardVO만 수정하고 넘어간다.

다른 파일 코드를 수정하면 오류가 발생하기 때문에 전체적으로 수정해야 한다.


6. Abbreviation in name 'X' must contain no more than '1' capital letters가 16회 발생했다.

이것은 변수 이름에 대문자가 1개 이상이 사용되었다는 것이다.

Properties > Checkstyle | Configure 에서 "Naming Conventions"에 있는 "Abbreviation As Word In Name"의 체크를 해제해서

명명 규칙 적용을 해제한다.


7. 'X' should be separated from previous statement.가 13회 발생했다.

상세 정보를 보면 'METHOD_DEF' should be separated from previous statement와

',' should be separated from previous statement. 가 있다.

'METHOD_DEF' should be separated from previous statement은 앞 문장과 한 라인을 띄우라는 의미이다.

다음 그림의 27라인에서 엔터키를 치고 저장하면 돋보기가 사라진다.

',' should be separated from previous statement.는 콤마(,) 다음은 다음 라인에서 시작하라는 의미이다.

돋보기에 확인하면 Each variable declaration must be in its own statement라는 문장도 같이 나온다.

Checkstyle violations뷰에 Each variable declaration must be in its own statement는 5회 발생했다.

이것은 다음 코드처럼 각 변수를 콤마가 아닌 개별 선언하라는 의미이다.

public class boardVO {

    private String brdno, brdtitle, brdwriter, brdmemo, brddate, brdhit, brddeleteflag
                 , filecnt;

-----------------------------

public class boardVO {
    private String brdno;
    private String brdtitle;
    private String brdwriter;
    private String brdmemo;
    private String brddate;
    private String brdhit;
    private String brddeleteflag;
    private String filecnt;

개인적으로 오류가 난 코드를 선호하여 명명 규칙을 제거할 수도 있는데

위 코드로 수정하였다.

개인적인 코드 작성 원칙이 가급적 많은 코드를 한 화면에서 본다는 것이다.

많은 코드가 한 눈에 보여야 앞뒤 흐름를 파악하여 실제 버그를 빠르고 쉽게 해결한 경험때문이다.

더우기 같은 코드인데 앞뒤 순서에 따라 성능 향상등의 이점을 얻기도 하기 때문이다.

하지만 수정된 코드가 원칙처럼 사용되고 변수 선언 뒤에 //로 해당 변수의 설명을 넣는 것이 좋은 코드 방법이다.

개인적으로는 위 VO의 변수명, 테이블 필드명등의 이름을 동일하게 부여한다.

직관적으로 이해하게 하여 이러한 설명을 빼고 작성한다.


8. 'X' is not preceded with whitespace가 13회 발생했다.

더블 클릭해서 상세화면을 실행한다.

다음 그림과 같이 board41Ctr.java를 찾아서 다시 더블 클릭하면 오류가 발생한 코드를 볼 수 있다.

상세 메세지와 코드로 != 앞뒤로 공백이 없는 것을 확인할 수 있다.

공백을 넣어 주면 오류가 사라진다.

10회 발생한 'X' is not followed by whitespace도 같은 오류라 위 오류 수정에 의해 해제 되기도 한다.

안 될 경우 상세화면에서 해당 코드를 찾아서 공백 처리하면 된다.


9. 'X' construct must use '{}'s가 8회 발생했다.

아래 코드와 같이 블럭으로 {}를 넣어 주면 된다.

            if (param.getBrdno() == null || "".equals(param.getBrdno()))
                 sqlSession.insert("insertBoard4", param);
            else sqlSession.update("updateBoard4", param);


            if (param.getBrdno() == null || "".equals(param.getBrdno())) {
                 sqlSession.insert("insertBoard4", param);
            } else {
                sqlSession.update("updateBoard4", param);
            }


10. Line is longer than X characters (found X) 가 3회 발견되었다.

한 행의 코드 수가 100자를 넘겼다는 의미이다.

다음 그림과 같이 Maximum Line Length를 해제한다.


11. First sentence of Javadoc is incomplete (period is missing) or not present이 3회 발생했다.

이것은 Javadoc에서 언급한 것으로 문장을 마침표(.)로 끝내라는 것이다.


2가지가 더 있지만 모두 앞서 언급한 것과 같이 처리하면 된다.

board41Ctr.java, board41Src.java, boardVO.java의 코드를 보면 돋보기가 모두 사라진 것을 확인 할 수 있다.

board41Src.java에 노랗게 경고 표시가 되어 있는 것이 있다.

이 것은 HashMap의 세부 타일을 표시하면 좋다는 것으로 다음과 같이 수정하면

코드 편집창에 나타난 경고들이 모두 사라진다.

            HashMap fparam = new HashMap();
            fparam.put("fileno", fileno);

            HashMap<String, String[]> fparam = new HashMap<String, String[]>();


이상으로 명명 규칙등과 관련된 Checkstyle 사용을 살펴 봤다.

이 외에도 더 많은 규칙이 있고 추가도 할 수 있다.

각자에 맞추어 선택하면 될 것이다.





'Java > 게시판 기타: 품질 등' 카테고리의 다른 글

5. PMD와 예외처리(Exception)  (2) 2016.05.07
1. 프로그램 작성 외의 것들  (1) 2016.04.24
2. CheckStyle I - 설치  (0) 2016.04.23
4. FindBugs  (0) 2016.04.23

FindBugs는 작성한 프로그램의 잠재적인 결함을 찾아주는 도구이다.


Eclipse 메뉴의 Help > EclipseMarketplace를 실행한 뒤 "findbug"를 검색한다.

다음 그림과 같이 검색된 결과를 설치(install)하면 된다.

설치가 완료되면 Eclipse를 재시작한다.


메뉴에서 Window > Show View > Other를 선택한다.

FindBugs의 Bug Explorer를 선택하고 OK를 선택한다.

Eclipse 하단에 Bug Explorer 뷰가 추가 되어 있는 것을 확인할 수 있다.


프로젝트(board)를 선택하고 마우스 오른쪽 버튼을 눌러 컨텍스트 메뉴를 실행한다.

Find Bugs > Find Bugs를 눌러서 실행한다.


실행 결과 Bug Explorer 뷰에 버그가 없는 것으로 나타난다.


따라서, 다음과 같이 버그를 만들어 실행해 본다.

먼저, board4Ctr의 리스트(board4List) 컨트롤에 다음 색칠된 문장을 추가해 준다.

    @RequestMapping(value = "/board4List")
       public String boardList(SearchVO searchVO, ModelMap modelMap) throws Exception {

        String a = null;
        if (a.equals("a")) a = "error";
       
        searchVO.PageCalculate( boardSvc.selectBoardCount(searchVO) ); // startRow, endRow

        List<?> listview   = boardSvc.selectBoardList(searchVO);
       
        modelMap.addAttribute("listview", listview);
        modelMap.addAttribute("searchVO", searchVO);
        return "board4/boardList";
    }

board4Ctr.java

Find Bugs를 실행하면 다음과 같은 결과가 나오게 된다.

이것은 변수의 값이 Null이라 문제가 생긴다는 의미이다. 

다음과 같이 작성하면 문제를 해결할 수 있다.

if ("a".equals(a)) a = "error";

앞뒤 순서만 바뀐 것 같지만 의미가 다르다.

변수 a 가 Null인데 equals를 사용하면 Null 인데 사용했다고 Null pointer 오류가 발생한다.

Null은 아무 것도 없는 상태로 어떠한 메소드도 사용할 수 없다.

하지만 문자열 "a"에 equals을 사용하면

문자열 "a"는 값이 있는 상태이므로 오류가 발생하지 않는다.

(예제는 변수 값을 Null로 고정한 상태라 Hign confidence에서 Normal confidence로 오류 등급이 떨어진다.)

변수 a는 Null이어도 상관이 없다.


이러한 코드는 컴파일에서는 문제가 없지만

변수의 값에 따라 오류가 발생하기도 하고, 안하기도 하는 귀찮은 문제를 발생시킬 수 있다.



'Java > 게시판 기타: 품질 등' 카테고리의 다른 글

5. PMD와 예외처리(Exception)  (2) 2016.05.07
1. 프로그램 작성 외의 것들  (1) 2016.04.24
2. CheckStyle I - 설치  (0) 2016.04.23
3. CheckStyle II  (0) 2016.04.23

기존 게시판에 파일 관련 기능을 넣어서 자료실 형태로 만들어 보자.

자료실을 만들기 위해서는 기존 기능에 다음과 같이 파일 관련 기능을 넣어주면 된다.

  • 사용자가 글을 쓰면서 파일을 첨부하기 때문에, 글쓰기에 파일 태그(input type=file)를 추가해 주어야 한다.
  • 글 저장 컨트롤에서는 전송된 파일을 받아서 지정된 디렉터리에 저장하고 그 파일 이름을 게시물과 같이 데이터 베이스에 저장한다.
  • 글 수정은 글 쓰기와 같지만 첨부한 파일을 삭제할 수 있는 방법이 있어야 한다.
  • 게시판 리스트에서는 첨부 파일이 있는 경우 이미지 등의 방법으로 파일이 있다는 것을 보여 준다.
  • 글 읽기에서는 첨부된 파일 리스트를 보여주고 파일명을 클릭하면 다운로드 받을 수 있게 한다.
  • 글 삭제는 게시 글이 삭제 될 때 첨부 파일도 같이 삭제한다.

파일을 서버에 저장할 때 고려해야 할 사항이 몇 가지 있다.

먼저, 첨부한 파일을 서버에 어떻게 저장할 것인가 하는 문제이다. 

사용자가 첨부한 파일은 “xxx.jpg” 등과 같은 이름을 가지게 되는데

간혹 서버에서 실행 가능한 “xxx.jsp”, “xxx.js” 등으로 지정하여 많은 문제(해킹 등)를 발생시킬 수 있다.

따라서 저장할 때는 별도의 이름으로 저장해야 된다.

사용자가 명명한 파일명과 서버에 저장될 때의 파일명이 필요하다는 의미이다.

두 번째는 지정된 게시물과 첨부한 파일명이 같이 저장해야 된다는 것이다. 

당연한 것이지만 그렇게 해야 다운로드, 삭제 등이 가능하기 때문이다.

같이 저장하는 방식은 게시물이 저장된 테이블(TBL_BOARD)에 필드를 추가해서 처리할 것인지,

별도의 테이블에 처리할 것인지를 고려해야 한다.

첨부파일이 하나일 경우 같은 테이블에 저장하기도 하지만 대부분 하나 이상의 파일을 첨부하기 때문에 별도 테이블로 처리하는 것이 좋다.

이상의 두 가지를 고려해서 다음과 같이 첨부 파일을 저장하는 테이블(TBL_BOARDFILE)을 생성해야 한다.

CREATE TABLE TBL_BOARDFILE (
    FILENO INT(11)  NOT NULL AUTO_INCREMENT,   -- 파일 번호
    BRDNO INT(11),                                                -- 글번호
    FILENAME VARCHAR(100),                                -- 파일명
    REALNAME VARCHAR(30),                                -- 실제파일명
    FILESIZE INT,                                                   -- 파일 크기
    PRIMARY KEY (FILENO)
);

파일 번호(FILENO)는 첨부파일에 대한 기본 키 값으로 저장된 순서를 의미한다.

BRDNO는 게시판(TBL_BOARD)의 글 번호를 의미하는 것으로 게시물과 파일의 관련성을 나타낸다.

즉, 해당 게시물(BRDNO)의 모든 파일 파일을 가지고 오는데 사용한다.

FILENAME은 사용자가 첨부할 때의 원래 파일 이름을 의미하고, 

REALNAME은 서버에 저장하면서 새롭게 부여된 실제 파일명을 의미한다.

실제 파일명(REALNAME)은 날짜와 시간(millisecond)을 이용하여 중복되지 않게 부여하는데 30 자 이내로 저장한다.



'Java > 게시판 4:자료실' 카테고리의 다른 글

2. 자료실 - 글쓰기  (0) 2016.03.28
3. 자료실 - 글 수정  (3) 2016.03.28
4. 자료실 - 리스트  (0) 2016.03.28
5. 자료실 - 글 읽기 / 삭제  (0) 2016.03.26

데이터 베이스를 기반으로 하는 프로그램들은

게시판만 잘 만들 줄 알아도 대부분의 개발은 되는 것으로 알고 있다.

모든 프로그램은 입력(INSERT), 수정(UPDATE), 삭제(DELETE), 조회(SELECT) 기능이 다양한 형태로 표현되는 것으로,

이러한 기능을 가장 간단하게 구현할 수 있는 것이 게시판 이라고 생각하기 때문이다.

따라서, 대단한 기술이 없이 단순 게시판만 잘 만들 줄 알아도 대부분의 프로그램을 문제 없이 만들 수 있다.

오히려 어설프게 아는 기술들을 적용해서 프로젝트를 망치는 경우를 매우 많이 봤다.

여기서는 게시판 만드는 과정을 단계별로 구성하여

보다 쉽고 깊이 있게 배울 수 있도록 시도하였다.

(게시판을 만들줄 알면 나머지는 프로세스에 따른 구현과 SQL 사용 능력인데 여기서는 다루지 않는다.)


본 게시판 예제는 Spring 4.1과 MyBatis 3.2를 기반으로 구성하였다.

Spring 4를 기반으로 제작했지만 

Spring 4의 특징들을 반영하지 않았기 때문에 Spring 3(전자정부 프레임)에서도 사용 가능하다.

다음 그림은 본 예제에서 사용한 Spring MVC 구조로 기본 구조보다 단순하게 구성하였다.   

Spring MVC에서는 DAO(mapper)와 Service interface(추상화)등의 구성이 더 구현되어야 한다.

하지만, 경험상 필요성을 느끼지 못하고 이러한 구조는 복잡성을 가중시킨다고 생각하여

본 예제에서는 다음 그림과 같이 단순화 하여 구성하였다.

즉, 추상화를 사용하지 않았고 DAO와 Servic를 합쳐서 Service로 사용하였다.


본 예제는 게시판을 개발하며 프로그래밍 하는 과정을 익히는 것으로

Spring과 MyBatis, MVC에 대하여 어는 정도 알고 있다는 전제로 진행되기 때문에

상세한 내용을 적지는 않을 것이지만 개발 구성에 대해서 간단하게 서술하였다.



사용자가 웹 브라우저를 통하여 특정 페이지에 접속하면 웹 서버(실제로는 WAS)는 해당 페이지의 컨트롤을 호출하고,

사용자가 입력하거나 처리에 필요한 정보를 구성하여 작성한 HTML을 웹 서버에 반환하게 되고

웹 서버는 해당 내용을 클라이언트(웹브라우저)에게 전송하면 사용자가 원하는 내용을 보게 된다.

다만 컨트롤이 처리할 때, 데이터 베이스 관련 작업이 필요한 경우 서비스를 호출하여 해당 데이터 처리와 관련된 실행(SQL)을 처리하여 반환하게 된다.




게시판의 리스트를 예로 들면,

사용자가 게시판 리스트(board1List)를 선택한 경우 웹 서버는 컨트롤인 board1List(Java)를 호출한다.

컨트롤인 board1List는 데이터 베이스에서 게시판 데이터를 가져오기 위해 서비스인 boardSvc를 호출하고

boardSvc는 Mybtis를 통해 데이터를 가져올 수 있는 SQL문을 board1.xml 파일에서 찾아서 실행시키게 된다. 

실행 결과로 게시판 데이터들을 집합(RecordSet)으로 받게 되고 이 데이터를 컨트롤에게 반환한다.


컨트롤은 이 데이터와 필요한 데이터를 묶어서 HTML로 구성된 board1List.jsp로 넘겨주고,

JSP 파일에서는 Taglib와 HTML로 사용자가 보는 화면을 구성하게 된다.

즉, MVC ((Model–View–Controller)로 운영되는 것이다.

데이터 베이스는 MariaDB를 대상으로 작성하였으나 MySQL에서도 사용가능하며,

Limit나 DATE_FORMAT등 함수 몇 가지를 수정하면 표준 SQL이라 다른 데이터 베이스에서도 사용 가능하다.



'Java > 게시판기본' 카테고리의 다른 글

2. 게시판의 구성  (4) 2016.04.02
3. 설치  (18) 2016.04.02
4. 게시판 구성 단계  (14) 2016.04.02

프로그래밍 언어에 관계없이 온라인 게시판은 일반적으로 다음 그림과 같이 구성된다.


  • 리스트: 게시판의 모든 내용을 웹 페이지에 출력 하는 것으로 게시판 테이블(TBL_BOARD)의 내용을 가져와서(Select) 하여 지정된 형태로 출력하게 된다.
  • 글 읽기: 리스트의 게시물 하나(한 행)를 선택하면 선택한 게시물에 대한 상세한 내용을 볼 수 있는 글 읽기 페이지로 이동한다. 게시물을 확인하고 리스트로 돌아가거나 글 수정이나 삭제를 할 수 있다.
  • 글 쓰기: 리스트 페이지에서 [글쓰기] 링크를 선택하면 나타나는 페이지로 사용자가 입력할 화면이 나오고, 입력 후 [저장] 버튼을 클릭하면 테이블에 저장(Insert) 한 후 리스트로 돌아가서 저장된 내용을 볼 수 있게 한다.
  • 글 수정: 글쓰기와 같은 화면으로 구성되고, 글 읽기에서 [글수정]을 선택하면 이동한다. 수정할 내용을 입력 후 저장] 버튼을 클릭하면 테이블에 저장(Update)한 후 리스트로 돌아가서 저장된 내용을 볼 수 있게 한다.
  • 글 삭제: 글 읽기에서 [글삭제]를 선택하면 화면 없이 삭제를 진행하고 리스트로 돌아가서 삭제된 내용을 볼 수 있게 한다.

글쓰기를 제외하고는 기본키(Primary Key, PK) 값이 있어야 해당 글을 읽거나 수정/ 삭제 할 수 있다.

게시판에서는 기본키가 글 번호가 된다.

개발하는 방식에 따라 글 수정과 삭제 링크를 글읽기가 아닌 리스트에 둘 수 있지만 본 예제에서는 위 그림처럼 진행한다.





이 그림은 게시판의 구성에 컨트롤명(웹페이지명)을 매핑한 그림이다.

사용자는 게시판 리스트 페이지에 접속하고 웹서버는 리스트 페이지를 제공하기 위해 boardList 컨트롤을 호출한다.

하나의 Java 파일(board1Ctr, board2Ctr 등)에 모아져 있으니 먼저 확인하면 좋을 것이다.

또, 이 컨트롤에서 모든 처리가 이루어지기 때문에 먼저 설명한다.


이하의 게시판 예제는 GitHub에서 다운 받을 수 있다.

'Java > 게시판기본' 카테고리의 다른 글

Spring 4 + MyBatis 3 + MariaDB (Maven) 기반 게시판 예제  (3) 2016.04.02
3. 설치  (18) 2016.04.02
4. 게시판 구성 단계  (14) 2016.04.02

여기서는 다루지 않지만 기본적으로 다음 사항은 설치가 되어 있어야 한다.

설치 되지 않았다면 참고할 사이트를 작성하였으니 읽고 설치 해야 한다.


이클립스(Eclipse)에서 가져오기(import)나 새로만들기(New)를 실행하여

다음 그림과 같이 Git(Projects from Git)을 선택한다.

다음 화면에서 Clone URI를 선택한다.

Github의 게시판 예제 사이트 (https://github.com/gujc71/board_sample)에서

본 샘플 Git URL(https://github.com/gujc71/board_sample.git)을 복사한다.

이클립스에서 URL에 붙여넣기를 한다.

가져온 샘플을 저장할 디렉토리를 지정한다.

디렉토리는 이클립스에서 지정된 workspace 하위에 있어야 한다.

샘플로 생성할 프로젝트 종류를 지정하는 화면으로

다음과 같이 "Import existing Eclipse projects"를 선택한다.

다음 화면에서 완료(finish)를 선택하면

Github에서 데이터를 가져와 컴파일하는 화면이 나타난다.

Maven을 사용했기 때문에

다음 그림과 같은 프로젝트가 생성되고,

Maven이 필요한 라이브러리(jar)를 설치해 준다.

위 트리에 빨간색 X 표시 아이콘이 나타날 경우가 있다.

해당 파일을 열어 보면 자바 기본 클래스들이 모두 오류 표시가 된 경우는 자바 버전이 맞지 않은 것이다.

제공되는 소스는 자바 1.7로 개발되었는데 설치한 사람의 PC에는 다른 버전의 자바가 설치 된 경우 이다.


위 트리의 상위(프로젝트)를 선택하고 마우스 오른 쪽을 눌러 "Properties"를 실행하고

Java Build Path > Libaraies 탭을 선택한다.

JRE System Library에 jre 버전을 확인하고 Edit 버튼으로 설치된 자바 버전으로 변경한다.

board\src\main\webapp\WEB-INF하위에 있는

applicationContext.xml파일을 열어서 MariaDB의 적절한 url와 아이디/비밀번호를 넣고

톰캣을 실행한다.

MariaDB에는 다음 테이블이 생성되어 있어야 한다.

CREATE TABLE TBL_BOARD (
  BRDNO int(11) NOT NULL AUTO_INCREMENT,
  BRDTITLE varchar(255),
  BRDWRITER varchar(20),
  BRDMEMO   varchar(4000),
  BRDDATE   datetime,
  BRDHIT INT,
  BRDDELETEFLAG CHAR(1),
  PRIMARY KEY (BRDNO)
) ;

CREATE TABLE TBL_BOARDFILE (
    FILENO INT(11)  NOT NULL AUTO_INCREMENT,
    BRDNO INT(11),
    FILENAME VARCHAR(100),
    REALNAME VARCHAR(30),
    FILESIZE INT,
    PRIMARY KEY (FILENO)
);


웹브라우저에서 http://localhost:8080/board/board1List를 입력하여

실행해 보면 다음 그림과 같이 게시판이 실행된다.


게시판 예제는 다음 그림과 같은 구조로 제작되었다.

Java > gu 폴더에 서비스, 컨트롤 등의 Java 파일이 있고

Resources > sql 폴더에 SQL 문이 Mybatis의 XML 파일로 있다.

Webapp > WEB-INF > jsp 폴더에 JSP(HTML)로 작성된 파일이 있다.

gu 폴더 하위의 board1 폴더,

sql 폴더에 있는 board1.xml, 

jsp 폴더 하위의 board1 폴더가 각 단계별 하나의 묶음으로 구성되었다.


gu 폴더 하위의 board1 폴더 하위에 있는

board1Ctr.java에 기본 게시판에 사용된 모든 컨트롤

board1Src.java에 기본 게시판에 사용된 모든 서비스가 있다.

gu 폴더 하위의 board2, 3 등의  폴더 하위에도 동일한 규칙이 적용되었다.

board1Ctr, board2Ctr등과 board1Src, board2Src 등이 아닌

boardCtr 과 boardSrc로 각 폴더별로 동일한 명칭을 사용하면 좀더 편한데

이러한 명명 규칙은 Java에서는 허용하지만 Spring에서 허용되지 않아서 앞서의 명명 규칙처럼 다른 이름을 사용하였다.





'Java > 게시판기본' 카테고리의 다른 글

Spring 4 + MyBatis 3 + MariaDB (Maven) 기반 게시판 예제  (3) 2016.04.02
2. 게시판의 구성  (4) 2016.04.02
4. 게시판 구성 단계  (14) 2016.04.02

본 샘플은 Spring 4 + MyBatis 3 + MariaDB (Maven) 기반으로 게시판을 만드는 과정을 단계별로 구현한 샘플이다.

각 내용은 다음과 같이 구성되었다.


1. board Step 1 (게시판1)
    List: 모든 게시물 출력
    Form: 사용자 입력 내용 저장
    Update: 사용자 입력 내용 수정
    Read: 사용자 입력 내용 보기
    Delete: 지정된 게시물 삭제

    URL: http://localhost:8080/board/board1List


2. board Step 2 (게시판2)

    List: 페이징, 새로운 번호 부여
    Form: 입력/수정을 하나로
    Read: 조회수
    Delete: 삭제에서 숨기기로

    URL: http://localhost:8080/board/board2List


3. board Step 3 (게시판3)

    List: 검색, 제목을 한 줄로 표시 ==> 페이징을 공통으로
    Form: 필수입력, 수정/저장 서비스 하나로
    Read: 스크립트 실행 방지

4. board Step 4 (게시판4)

    자료실

5. board Step 5 (게시판5)

    댓글

6. board Step 6 (게시판6)

    무한 댓글 (계층형)

7. board Step 7 (게시판7)

    JQuery / Ajax

8. board Step 8
(게시판8)

    멀티 게시판

9. board Step 9 - (게시판9)

    멀티 게시판 관리자


'Java > 게시판기본' 카테고리의 다른 글

Spring 4 + MyBatis 3 + MariaDB (Maven) 기반 게시판 예제  (3) 2016.04.02
2. 게시판의 구성  (4) 2016.04.02
3. 설치  (18) 2016.04.02

파일을 첨부하기 위해서는 글 쓰기 폼(boardForm.jsp)에 file 태그만 추가해 주면 된다.

다음 코드에서 빨갛게 표시된 것처럼 uploadfile파일이라고 이름을 주고 생성하였다. 

Multiple 속성은 한 번에 여러 개의 파일을 지정할 수 있다.

<input type="file" name="uploadfile" multiple="" />

이러한 첨부 파일을 전송하기 위해 폼 태그도 인코딩 타입(enctype)을 multipart/form-data로 지정해 줘야 한다. 

이것은 파일이나 용량이 큰 데이터를 전송할 때 지정하는 방식으로 기억해두면 될 것 같다.

<form name="form1" action="board4Save" method="post" enctype="multipart/form-data">
    <table border="1" style="width:600px">
        ~~ 생략 ~~
            <tr>
                <td>내용</td>

                <td><textarea name="brdmemo" rows="5" cols="60"><c:out value="${boardInfo.brdmemo}"/></textarea></td>
            </tr>
            <tr>
                <td>첨부</td>
                <td>
                    <input type="file" name="uploadfile" multiple="" />

boardForm.jsp

사용자가 파일을 첨부하여 전송한 내용을 서버에서 받기 위해 스프링에서 제공하는 MultipartFile를 다음 코드와 같이 boardVO에 추가해 줘야 한다.

다수의 파일을 전송하기 때문에 List 형태로 지정해야 하고 변수 이름(uploadfile)은 파일 태그에서 지정한 이름과 같아야 한다.

public class boardVO {

    private String brdno, brdtitle, brdwriter, brdmemo, brddate, brdhit, brddeleteflag;
    private List<MultipartFile> uploadfile;

    ~~ 생략 ~~
    public List<MultipartFile> getUploadfile() {
        return uploadfile;
    }


    public void setUploadfile(List<MultipartFile> uploadfile) {
        this.uploadfile = uploadfile;
    }

boardVO.java

파일을 저장하기 위해서 관련 내용을 boardVO로 받아온 뒤에 처리를 하게 된다.

먼저, MultipartFile로 받아온 파일들을 서버의 지정된 디렉터리에 저장해야 한다.

본 예제에서는 FileUtil라는 클래스로 이 기능을 미리 개발해 두었다. 

복잡하지 않기 때문에 원리를 이해하면 좋겠지만 여기에서는 saveAllFiles만 호출하면 된다는 것을 기억해 두자. 

다만 FileUtil 안에 저장 디렉터리로 "d:\\workspace\\fileupload\\"가 지정되어 있으니 각자의 디렉터리 구조에 맞추어 수정해줘야 하다.

@RequestMapping(value = "/board4Save")
public String boardSave(HttpServletRequest request, boardVO boardInfo) throws Exception {
    FileUtil fs = new FileUtil();

    List<FileVO> filelist = fs.saveAllFiles(boardInfo.getUploadfile());

    boardSvc.insertBoard(boardInfo, filelist);

    return "redirect:/board4List";
}

board4Ctr.java

주의: 파일명은 날짜와 시간(millisecond)을 이용하여 중복되지 않게 부여하고, 각 파일은 해당 년도 디렉터리에 저장하게 된다.

디렉터리는 파일명의 앞 4자리(년도)를 잘라서 자동 생성되고 그 디렉터리에 파일이 저장된다.

하나의 디렉터리에 저장될 수 있는 파일의 개수는 한정적(약 만개로 기억)이라 년도 별로 저장하게 개발하였다.

만약, 1년간 파일의 개수가 제한된 개수를 넘는다면 년월(앞6자리)로 수정해 주면 된다.


첨부된 파일이 실제 디렉터리에 저장되면 FileVO 형의 List가 반환된다.

FileVO는 파일 첨부 기능이 있는 모든 페이지에서 비슷하게 사용하기 때문에 공통(common)으로 제작하였다.

클래스 구성은 테이블(TBL_BOARDFILE) 구조와 유사하지만

테이블의 글번호(brdno) 대신에 부모글 번호(parentPK)로 명명하여 공통으로 사용할 수 있게 구성하였다.

size2String 함수는 비트로 저장된 파일 크기(filesize)를 byte, K-byte, M-Byte 등으로

적절하게 변환하여 반환하는 함수로 공통 함수로 구성하여 사용해도 되지만

FileVO 외에서는 사용할 일이 없어서 FileVO에 넣어서 제작하였다.

사용된 공식은 몇 년 전 인터넷에서 찾은 것으로 간단해서 익혀두면 조금이나마 알고리즘 연습이 될 수 있을 것이다.

package gu.common;

public class FileVO {
    private Integer fileno;         // 글번호
    private String parentPK;     // 부모 글번호
    private String filename;     // 파일명
    private String realname;    // 실제파일명
    private long filesize;        // 파일 크기
   
    public String size2String() {
        Integer unit = 1024;
        if (filesize < unit){
            return String.format("(%d B)", filesize);
        }
        int exp = (int) (Math.log(filesize) / Math.log(unit));

        return String.format("(%.0f %s)", filesize / Math.pow(unit, exp), "KMGTPE".charAt(exp-1));
    }

   
    public Integer getFileno() {
        return fileno;
    }

    public void setFileno(Integer fileno) {
        this.fileno = fileno;
    }
    ~~ 생략 ~~

FileVO.java

원리는 로그(log)를 이용한 것으로 밑수가 10인 로그(상용로그)를 실행하면 10의 배수가 계산된다.

byte, K-byte, M-Byte 등은 1024배씩 커지는 것으로 약 10의 3배수씩 커진다고 보면 된다.

즉, 1 K byte = 1024 byte 이고 1024 byte 는 Log를 취하면 약 3(3.010)의 값이 나온다.

이 3은 10의 자릿수로 보면 된다 (1000으로 보면 0의 개수).

예로, 파일 크기(filesize)가 2248 byte일 경우 로그를 취하면 약 3(3.351)이 나오고

단위 (1024의 로그값 3)로 나누면 1(1.11)이 계산 된다.

즉, 단위를 나타내는 문자열("KMGTPE")에서 첫 번째 K가 선택되게 된다.

단위를 구했으면 단위에 맞게끔 파일 크기를 계산해 줘야 한다.

단위값(1024)을 거듭제곱(power)으로 1을 계산하면 1024가 나오고 이 값을 실제 파일 크기 2248에 대하여 나누면 2.19가 계산된다.

따라서 최종적으로 2.2 K byte로 표기되는 것이다.

이렇게 반환된 파일 리스트(filelist)는 게시물 내용과 같이 서비스(insertBoard)로 전달하여 데이터 베이스에 저장한다.

public void insertBoard(boardVO param, List<FileVO> filelist) throws Exception {
        if (param.getBrdno()==null || "".equals(param.getBrdno()))
               sqlSession.insert("insertBoard4", param);
        else sqlSession.update("updateBoard4", param);

        for (FileVO f : filelist) {
            f.setParentPK(param.getBrdno());
           sqlSession.insert("insertBoard4File", f);
        }
}

board4Svc.java

서비스에서는 파일 리스트 개수만큼 반복해서 다음과 같이 Insert문으로 첨부

테이블(TBL_BOARDFILE)에 저장하면 된다.

게시물의 글번호(brdno)를 parentPK에 넣고(setParentPK), mybatis에서는 INSERT문을 #{parentPK}로 만들어 실행하게 된다. 

<insert id="insertBoard4File" parameterType="gu.common.FileVO" >
        INSERT INTO TBL_BOARDFILE (BRDNO, FILENAME, REALNAME, FILESIZE)
        VALUES (#{parentPK}, #{filename}, #{realname}, #{filesize})
</insert>

board4.xml

게시판 테이블(TBL_BOARD)에 저장하는 insertBoard4의 SQL문도 수정이 필요하다.
위 SQL의 insertBoard4File에 게시판의 글번호(brdno) 값이 필요하다.
글 수정일 경우 글번호가 같이 넘어 오지만
신규 글 쓰기일 경우 글번호가 부여 되지 않았다.
특히, 글 쓰기는 글번호가 Insert문이 실행되면 DBMS가 자동으로 부여하기 때문에 그 번호를 알 수 없다.
따라서, 자동으로 부여하지 않고 계산해서 가져와야 한다.
자동으로 부여 하는 방법은 현재 테이블에 저장된 번호 중 최대값(MAX)을 구하고, 그 값에 1을 더하면 된다.


            SELECT IFNULL(MAX(BRDNO),0)+1 FROM TBL_BOARD


IFNULL은 null일 때 0값을 주는 것으로 MAX 함수가 null 일 경우는 데이터가 하나도 없는 경우를 의미한다.
즉, 첫 데이터를 입력하기 전이다.
이렇게 반환 된 값으로 게시판 글번호로 저장하고 첨부 파일에도 저장하면 된다.
따라서 insertBoard4 서비스에 위 SELECT문을 실행할 수 있도록 하면 되지만,
Mybatis의 selectKey를 이용하면 쉽게 해결 할 수 있다.
다음 코드와 같이 selectKey의 keyProperty에 글번호(brdno)를 지정하면 신규 글번호 값이 boardVO 클래스의 brdno에 넣어져 반환 된다.
이 값이 INSERT문에서 #{brdno}로 사용해주면 새로운 게시물이 잘 저장된다.
그리고, 파라메타로 넘어온 boardVO 클래스에 저장되어 반환되기 때문에,
f.setParentPK(param.getBrdno())로 글번호를 받아와서 사용할 수 있게 된다.

<insert id="insertBoard4" parameterType="gu.board4.boardVO" >
        <selectKey resultType="String" keyProperty="brdno" order="BEFORE">
            SELECT IFNULL(MAX(BRDNO),0)+1 FROM TBL_BOARD
       </selectKey>
  
       INSERT INTO TBL_BOARD(BRDNO, BRDTITLE, BRDWRITER, BRDMEMO, BRDDATE, BRDHIT, BRDDELETEFLAG)
                              VALUES (#{brdno}, #{brdtitle}, #{brdwriter}, #{brdmemo}, NOW(), 0, 'N' )
</insert>

board4.xml

마지막으로 다음과 같이 트랜잭션(Transaction) 처리를 해주어야 한다.

앞서 insertBoard 서비스(board4Svc)에서 기존에 게시물 저장 후에

첨부한 파일 리스트들을 저장하는 SQL문을 실행하도록 추가했다.

즉 2개의 트랜잭션이 발생하는 경우에,

앞의 게시물을 저장하고 여러 가지 원인으로 오류가 발생 할 경우 첨부 파일 정보는 저장되지 않는다.

데이터 무결성에 문제가 발생한다.

이러한 경우 모든 데이터가 저장 안되게 처리하게 되는데 

다음과 같이 try 문을 사용하여 오류가 생기면 앞서 작업한 내용을 모두 취소(rollback)하고

오류가 없으면 모두 저장(commit)하도록 작성한다.

txManager 클래스는 이미 applicationContext.xml에 선언되어 있는 것으로 그냥 트랜잭션에 사용되는 것으로 기억하고 넘어간다.

DefaultTransactionDefinition ~~클래스 등은 트랜잭션 사용시 나오는 상용 구문 정도로 기억하면 될 듯하다.

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);

try{
    if (param.getBrdno()==null || "".equals(param.getBrdno()))
           sqlSession.insert("insertBoard4", param);
    else sqlSession.update("updateBoard4", param);

    for (FileVO f : filelist) {
        f.setParentPK(param.getBrdno());
        sqlSession.insert("insertBoard4File", f);
    }
    txManager.commit(status);
} catch (Exception ex) {

    txManager.rollback(status);
    throw ex;
}     

board4Svc.java

본 예제는 첨부한 파일을 "d:\workspace\fileupload\"에 저장한다.

지정된 디렉터리에 대한 운영체제의 권한에 따라 권한오류(특히 리눅스)가 발생하면서 파일이 저장 되지 않으니 주의해야 한다.




'Java > 게시판 4:자료실' 카테고리의 다른 글

1. 자료실 - 준비 & 시작  (1) 2016.04.02
3. 자료실 - 글 수정  (3) 2016.03.28
4. 자료실 - 리스트  (0) 2016.03.28
5. 자료실 - 글 읽기 / 삭제  (0) 2016.03.26

자료실에서 글 수정의 핵심은 다음 그림과 같이 기존에 첨부한 파일을 삭제할 수 있게 하는 것이다.



새로 첨부하는 것은 글쓰기와 수정이 하나로 처리되어 있기 때문에 별도의 처리가 필요치 않다.

먼저, 게시물과 같이 첨부 파일 리스트를 가져오도록 구성해 줘야 한다.

@RequestMapping(value = "/board4Form")
public String boardForm(HttpServletRequest request, ModelMap modelMap) throws Exception {
    String brdno = request.getParameter("brdno");
    if (brdno!=null) {
        boardVO boardInfo = boardSvc.selectBoardOne(brdno);
        List<?> listview = boardSvc.selectBoard4FileList(brdno);
   
        modelMap.addAttribute("boardInfo", boardInfo);
        modelMap.addAttribute("listview", listview);
    }
   
    return "board4/boardForm";
}

board4Ctr.java

다음 SELECT문은 첨부 테이블(TBL_BOARDFILE)에서 게시글에 해당하는 데이터를 가져오는 것으로,

서비스에 selectBoard4FileList를 추가해 주고,

컨트롤에서는 이 서비스를 호출하여 넘겨받은 List를 게시물 정보(boardInfo)와 같이 modelMap에 넣어(listview) jsp로 넘겨준다.

<select id="selectBoard4FileList" resultType="gu.common.FileVO" parameterType="String">
        SELECT FILENO, FILENAME, REALNAME, FILESIZE
          FROM TBL_BOARDFILE
         WHERE BRDNO=#{brdno}
         ORDER BY FILENO DESC
    </select>

baord4.xml

글 수정(쓰기) 폼에서는 listview의 개수만큼 반복(foreach)하여 파일 명을 보여주고 사용자가 선택하면 삭제 할 수 있도록 한다.

즉, 파일명을 보여 줄 때 사용자가 삭제하고자 선택 할 수 있는 checkbox를 주고

이름은 fileno로 동일하게 부여 하고 값(value)은 각 첨부 파일의 파일 번호(fileno)를 주도록 한다.

<form name="form1" action="board4Save" method="post" enctype="multipart/form-data">
    <table border="1" style="width:600px">
        ~~ 생략 ~~
            <tr>
                <td>내용</td>
                <td><textarea name="brdmemo" rows="5" cols="60"><c:out value="${boardInfo.brdmemo}"/></textarea></td>
            </tr>
            <tr>
                <td>첨부</td>
                <td>
                    <c:forEach var="listview" items="${listview}" varStatus="status">
                        <input type="checkbox" name="fileno" value="<c:out value="${listview.fileno}"/>">   
                        <a href="fileDownload?filename=<c:out value="${listview.filename}"/>&downname=<c:out value="${listview.realname}"/>">
                        <c:out value="${listview.filename}"/></a> <c:out value="${listview.size2String()}"/><br/>
                    </c:forEach>                   
               
                    <input type="file" name="uploadfile" multiple="" />

boardForm.jsp

예로 파일 이름과 번호(fileno)가 1, 2, 3인 경우 각각의 체크 박스가 동일한 이름으로 선택할 수 있게 생성하는 것이다. 

사용자가 1, 3 번을 선택하면 서버로 이 값이 배열로 fileno에 저장되어 전송된다.


파일명을 선택하면 해당 파일이 다운로드(fileDownload) 되도록 제작하였다.

fileDownload 컨트롤은 해당 파일명을 파라메타로 넘겨주면 지정된 파일을 찾아서 클라이언트로 전송해 준다.

fileDownload도 공통으로 사용하는 것으로 원리는 넘어가니 사용법만 기억해 두자.

파라메터 filename은 사용자가 입력한 파일명(filename)이고, downname은 서버에서 저장한 실제 파일명(realname)을 지정해 준다.

fileDownload에도 파일 경로가 고정되어 있으니 자신에게 맞게끔 수정하면 된다.


사용자가 삭제하기 위해 선택한 파일번호들을

다음과 같이 getParameterValues를 이용하여 배열로 받아와서 처리한다.

@RequestMapping(value = "/board4Save")
public String boardSave(HttpServletRequest request, boardVO boardInfo) throws Exception {
    String[] fileno = request.getParameterValues("fileno");

    FileUtil fs = new FileUtil();
    List<FileVO> filelist = fs.saveAllFiles(boardInfo.getUploadfile());

    boardSvc.insertBoard(boardInfo, filelist, fileno);

    return "redirect:/board4List";
}

board4Ctr.java


컨드롤에서 받은 파일번호들을(fileno) Delete문을 이용하여 삭제하면 개발이 끝나게 된다.

배열 변수 하나을 Mybatis로 넘길 수 없기 때문에,

배열변수를 가지는 클래스를 생성하거나 HashMap 클래스를 이용해야 한다.

본 예제에서는 후자를 이용했다.

public void insertBoard(boardVO param, List<FileVO> filelist, String[] fileno) throws Exception {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = txManager.getTransaction(def);
        
        try{
            if (param.getBrdno()==null || "".equals(param.getBrdno()))
                 sqlSession.insert("insertBoard4", param);
            else sqlSession.update("updateBoard4", param);
   
            if (fileno != null) {
                HashMap p = new HashMap();
                p.put("fileno", fileno) ;
                sqlSession.insert("deleteBoard4File", p);
            }

            for (FileVO f : filelist) {
                f.setParentPK(param.getBrdno());
                    sqlSession.insert("insertBoard4File", f);
            }
            txManager.commit(status);
        } catch (Exception ex) {
            txManager.rollback(status);
            throw ex;
        }            
}

board4Svc.java

Mybatis에서 넘겨 받은 삭제할 파일 번호들을 foreach문을 사용하여 삭제 하게 된다.

넘겨 받은 파일번호(fileno)의 배열 값을 콤마(,)로 생성하고

SQL문의 저장되어 있기 때문에 IN을 사용하여 한번에 지정된 모든 파일 정보를 삭제한다.

<delete id="deleteBoard4File" parameterType="hashmap">
    DELETE
      FROM TBL_BOARDFILE
         WHERE FILENO IN (
              <foreach item="item" index="index" collection="fileno" separator=",">
                     ${item}
              </foreach> 
        )    
</delete>

baord4.xml

삭제할 파일이 1번과 3번 파일이면 deleteBoard4File은 다음과 같은 SQL 문을 실행하게 될 것이다.

DELETE FROM TBL_BOARDFILE WHERE FILENO IN (1, 3)

클래스나 HashMap에 넘겨서 처리하는 방법외에

하나의 DELETE 문을 배열의 개수 만큼 반복 실행하는 방법도 있지만

개인적으로 데이터 입출력 횟수가 적은 것을 선호해서 이러한 방법을 사용했다.


앞서서 게시물 삭제를 실제로 삭제하지 않고 플래그(deleteflag) 처리하였는데 첨부 파일도 그렇게 구현할 수 있다. 

더욱이 실제 파일을 삭제하는 코드는 구현하지 않았는데 컨트롤에서 java delete 함수를 이용해 파일 삭제도 해보길 …

'Java > 게시판 4:자료실' 카테고리의 다른 글

1. 자료실 - 준비 & 시작  (1) 2016.04.02
2. 자료실 - 글쓰기  (0) 2016.03.28
4. 자료실 - 리스트  (0) 2016.03.28
5. 자료실 - 글 읽기 / 삭제  (0) 2016.03.26

+ Recent posts