개발 환경#

<div class="note"> Java : JDK 5.0
DB : MySql 5.0, Sqlyog 5.16
IDE : Eclipse 3.2, Jetty Launcher plug-in
Server : Jetty 5.1.12
Build : Maven 2.0.4 </div>

사용 프레임워크#

<div class="note"> Web : Wicket 1.2.3, Databinder 1.0
ORM : Hibernate 3.2, Hibernate Annotation 3.2 </div>

Wicket을 이용하여 게시판을 개발할 것인데 그 중에서도 Wicket의 하위 프레임워크인 Databinder를 사용할 것이다. Databinder는 Wicket에 Hiberate를 바로 연동하여 사용할 수 있도록 만들어진 프레임워크이다.

Maven 프로젝트 생성#

Maven 을 이용하여 프로젝트를 생성하고 이를 Maven의 이클립스 플러그인을 사용해서 이클립스 프로젝트로 변환할 것이다. Maven을 사용하면 모든 필요 라이브러리를 자동으로 받을 수 있지만 Sun의 JTA는 라이센스 문제로 불가능한 모양이다. 따라서, 직접 받아서 Maven 저장소에 설치해야한다. http://java.sun.com/products/jta/로 가서 다운받도록 한다. 그리고, 커맨드 창에서 아래와 같이 입력한다.

mvn install:install-file -DgroupId=javax.transaction
  -DartifactId=jta -Dversion=1.0.1B -Dpackaging=jar
  -Dfile=jta-1_0_1B-classes.zip

이클립스 프로젝트가 생성되는 workspace 디렉토리로 디렉토리를 변경하고 커맨드 창에서 아래와 같이 입력하면 프로젝트가 생성된다.

mvn archetype:create -DarchetypeGroupId=net.databinder
  -DarchetypeArtifactId=data-app -DarchetypeVersion=1.0
  -DgroupId=example -DartifactId=JBoard

맨 마지막 줄의 groupId=example이 기본 package 설정이고, artifactId=JBoard는 프로젝트 이름이다.

Database 설정#

{eclipse-workspace}/JBoard/src/main/java 디렉토리에 hibernate.properties 파일이 있다. 자기 환경에 맞게 프로퍼티 파일을 수정한다.

hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost:3306/mydb?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=euckr
hibernate.connection.username=root
hibernate.connection.password=

Eclipse 프로젝트 생성#

{eclipse-workspace}/JBoard로 디렉토리를 변경하고 커맨드 창에서 아래와 같이 입력한다.

mvn -Declipse.workspace=/home/me/workspace eclipse:add-maven-repo

이것으로 기본적인 프로젝트는 생성됐지만 게시판을 개발하면서 이 외에 추가로 필요한 라이브러리가 있기 때문에 pom.xml의 dependency 부분에 아래의 내용을 추가한다.

<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.14</version>
</dependency>
<dependency>
     <groupId>wicket</groupId>
     <artifactId>wicket-extensions</artifactId>
     <version>1.2.3</version>
</dependency>

Maven 저장소에 Jar 파일을 다운받기 위해서 아래의 명령을 입력한다.

mvn -Declipse.downloadSources=true eclipse:eclipse

Eclipse를 실행한다. Jetty Launcher 플러그인을 설치한다. File 메뉴의 Import 클릭. General의 Existing Projects into Workspace를 선택. Select root directory에서 workspace의 JBoard 디렉토리를 선택하고 Finish 버튼 클릭. 아래와 같이 프로젝트가 생성될 것이다.

1-1.jpg

모든 프로젝트 설정은 이것으로 완료. 이제 코딩만 하면 된다.

Model 객체#

게시물을을 나타내는 Board 클래스를 작성한다. 답변형 게시판과 파일업로드가 가능한 게시판을 위한 필드도 포함할 것이다.

Board.java

package example.model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Transient;
import example.board.util.StringUtil;

@Entity
@Table(name = "tbl_jboard")
public class Board implements Serializable {
    private static final long serialVersionUID = 6754324425667306493L;
    private long id; // 게시물 id
    private long thread; // 답변형 게시판을 위한 필드
    private int depth; // 답변형 게시판을 위한 필드
    private String writer; // 작성자
    private String password; // 암호
    private String email; // E-mail
    private String title; // 제목
    private String content; // 내용
    private Date writeDate; // 작성일
    private int level; // 게시물 레벨(추후 구현)
    private int isDelete; // 게시물 삭제 여부(
    private String fileId; // 업로드 파일 id
    private String fileName; // 업로드 파일명
    private int readCount; // 조회수
    private int replyCount; // 리플수
    public Board() {
    };
    /**
     * Copy Constructor
     *
     @param board
     *            a <code>Board</code> object
     */
    public Board(Board board) {
        this.id = board.id;
        this.thread = board.thread;
        this.depth = board.depth;
        this.writer = board.writer;
        this.password = board.password;
        this.email = board.email;
        this.title = board.title;
        this.content = board.content;
        this.writeDate = board.writeDate;
        this.level = board.level;
        this.isDelete = board.isDelete;
        this.fileId = board.fileId;
        this.fileName = board.fileName;
    }
    @Column(length = 1048576)
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public int getDepth() {
        return depth;
    }
    public void setDepth(int depth) {
        this.depth = depth;
    }
    @Column(length = 50)
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Column(length = 30)
    public String getFileId() {
        return fileId;
    }
    public void setFileId(String fileId) {
        this.fileId = fileId;
    }
    @Column(length = 100)
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public int getLevel() {
        return level;
    }
    public void setLevel(int level) {
        this.level = level;
    }
    @Column(length = 20)
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getReadCount() {
        return readCount;
    }
    public void setReadCount(int readCount) {
        this.readCount = readCount;
    }
    @Column(length = 1)
    public int getIsDelete() {
        return isDelete;
    }
    public void setIsDelete(int isDelete) {
        this.isDelete = isDelete;
    }
    public long getThread() {
        return thread;
    }
    public void setThread(long thread) {
        this.thread = thread;
    }
    @Column(length = 100)
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Date getWriteDate() {
        return writeDate;
    }
    public void setWriteDate(Date writeDate) {
        this.writeDate = writeDate;
    }
    @Column(length = 20)
    public String getWriter() {
        return writer;
    }
    public void setWriter(String writer) {
        this.writer = writer;
    }
    public int getReplyCount() {
        return replyCount;
    }
    public void setReplyCount(int replyCount) {
        this.replyCount = replyCount;
    }
    @Transient
    public String getDisplayContent() {
        return StringUtil.toHTML(content);
    }
    /**
     * Constructs a <code>String</code> with all attributes in name = value
     * format.
     *
     @return <code>String</code> representation of this object.
     */
    public String toString() {
        final String TAB = "n";
        StringBuilder retValue = new StringBuilder();
        retValue.append("Board ( ").append(super.toString()).append(TAB)
                .append("id = ").append(this.id).append(TAB)
                .append("thread = ").append(this.thread).append(TAB).append(
                        "depth = ").append(this.depth).append(TAB).append(
                        "writer = ").append(this.writer).append(TAB).append(
                        "password = ").append(this.password).append(TAB)
                .append("email = ").append(this.email).append(TAB).append(
                        "title = ").append(this.title).append(TAB).append(
                        "content = ").append(this.content).append(TAB).append(
                        "writeDate = ").append(this.writeDate).append(TAB)
                .append("level = ").append(this.level).append(TAB).append(
                        "isDelete = ").append(this.isDelete).append(TAB)
                .append("fileId = ").append(this.fileId).append(TAB).append(
                        "fileName = ").append(this.fileName).append(TAB)
                .append("readCount = ").append(this.readCount).append(TAB)
                .append("replyCount = ").append(this.replyCount).append(TAB)
                .append(" )");
        return retValue.toString();
    }
}

Hibernate Annotation을 이용하여 DB 테이블과 매핑을 한다.

WebApplication 클래스와 web.xml#

모 든 Wicket 웹어플리케이션은 WebApplication을 상속한 클래스를 작성해야 한다. 이 클래스에서 시작페이지 설정 등의 기본 설정을 할 수 있다. Databinder 프레임워크를 사용하기 위해서는 DataApplication 클래스를 상속해서 써야한다. 이 클래스에서는 추가적으로 Hibernate Entity에 대한 설정을 할 수 있다.

BoardApplication.java

package example.board;
import net.databinder.DataApplication;
import org.hibernate.cfg.AnnotationConfiguration;
import wicket.util.lang.PackageName;
import example.board.model.Board;
import example.board.page.WriteBoard;
public class BoardApplication extends DataApplication {
    @Override
    public void init() {
        super.init();
        getMarkupSettings().setStripWicketTags(true);
        mount("/pages", PackageName.forPackage(WriteBoard.class.getPackage()));
    }
    @Override
    public Class getHomePage() {
        return WriteBoard.class;
    }
    @Override
    protected void configureHibernate(AnnotationConfiguration config) {
        super.configureHibernate(config);
        config.addAnnotatedClass(Board.class);
    }
}

시작 페이지를 WriteBoard, 즉 앞으로 만들 게시물 작성 페이지로 설정한다. 그리고 Board 클래스를 Hibernate에서 Entity로 인식할 수 있도록 설정한다.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <filter>
        <filter-name>RedirectFilter</filter-name>
        <filter-class>net.databinder.util.RedirectFilter</filter-class>
        <init-param>
            <param-name>redirectUrl</param-name>
            <param-value>app</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>RedirectFilter</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>DataServlet</servlet-name>
        <servlet-class>
            wicket.protocol.http.WicketServlet
        </servlet-class>
        <init-param>
            <param-name>applicationClassName</param-name>
            <param-value>example.board.BoardApplication</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DataServlet</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

web.xml에 BoardApplication 클래스에 대한 설정을 한다.

게시물 작성 페이지#

Wicket 은 매 웹페이지에 대해 웹페이지에 해당하는 Java 클래스와 마크업 HTML 템플릿으로 구성된다. 게시물 작성 페이지에 해당하는 Java 클래스는 WriteBoard.java, HTML 템플릿은 WriteBoard.html 이다. HTML 템플릿은 디폴트로 Java 클래스와 같은 디렉토리에서 찾기 때문에 Java 클래스와 같은 디렉토리에 생성하면 된다. Wicket은 컴포넌트 기반의 웹프레임워크로써 Swing과 비슷한 방식의 프로그래밍으로 웹프로그래밍을 하게 된다.

WriteBoard.java

package example.board.page;
import java.util.Date;
import java.util.List;
import net.databinder.DataRequestCycle;
import net.databinder.components.DataForm;
import net.databinder.components.DataPage;
import net.databinder.components.FocusableTextField;
import wicket.PageParameters;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.PasswordTextField;
import wicket.markup.html.form.RequiredTextField;
import wicket.markup.html.form.TextArea;
import wicket.markup.html.form.TextField;
import example.board.model.Board;
public class WriteBoard extends DataPage {
    private static final long serialVersionUID = 5968452177224968650L;
    public WriteBoard(final PageParameters params) {
        Form form = null;
        form = new BoardForm("boardForm");
        add(form);
    }
    @SuppressWarnings("serial")
    class BoardForm extends DataForm {
        public BoardForm(String id) {
            super(id, Board.class);
            init();
        }
        protected void init() {
            FocusableTextField writerComp = new FocusableTextField("writer",
                    WriteBoard.this);
            writerComp.requestFocus();
            PasswordTextField passwordComp = new PasswordTextField("password");
            TextField emailComp = new TextField("email");
            RequiredTextField titleComp = new RequiredTextField("title");
            TextArea contentComp = new TextArea("content");
            contentComp.setRequired(true);
            add(writerComp);
            add(passwordComp);
            add(emailComp);
            add(titleComp);
            add(contentComp);
        }
        @Override
        public void onSubmit() {
            Board board = (BoardgetModelObject();
            List list = DataRequestCycle.getHibernateSession().createQuery(
                    "select max(board.thread) from Board board").list();
            long maxThread = 0;
            if (list.get(0!= null) {
                maxThread = ((Longlist.get(0)).longValue();
            }
            if (maxThread == 0) {
                maxThread = 1000;
            else {
                maxThread = maxThread + 1000;
            }
            board.setThread(maxThread);
            board.setDepth(0);
            board.setWriteDate(new Date());
            super.onSubmit();
            clearPersistentObject();
        }
    }
    @Override
    protected String getName() {
        return "게시물 작성";
    }
}

생 성자에서 HTML의 form에 해당하는 Form 클래스를 생성하고 Form 클래스에서 TextField 등의 컴포넌트를 추가한다. 웹페이지가 submit될 때 Form 클래스의 onSubmit() 메소드가 호출된다. 따라서 이 메소드에 게시물을 저장하는 로직을 구현하면 된다. Databinder 프레임워크에서는 super.onSubmit() 메소드만 호출하면 Hibernate를 이용하여 Entity 클래스를 DB에 저장한다.

WriteBoard.html

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:wicket="http://wicket.sourceforge.net/" lang="euc-kr">
<head>
<link
    href="http://localhost:8080/app/resources/net.databinder.components.DataPage/DataPage.css"
    type="text/css" rel="stylesheet" />
<title>게시물 작성</title>
</head>
<body>
<wicket:extend>
    <form wicket:id="boardForm" method="post">
    <table width="600">
        <tr>
            <td>이름</td>
            <td><input type="text" length="20" wicket:id="writer" /></td>
            <td>비밀번호</td>
            <td><input type="password" length="20" wicket:id="password" /></td>
        </tr>
        <tr>
            <td>E-mail</td>
            <td colspan="3"><input type="text" length="50" wicket:id="email" /></td>
        </tr>
        <tr>
            <td>제목</td>
            <td colspan="3"><input type="text" length="100"
                wicket:id="title" /></td>
        </tr>
        <tr>
            <td>내용</td>
            <td colspan="3"><textarea cols="60" rows="20"
                wicket:id="content"></textarea></td>
        </tr>
        <tr>
            <td colspan="4"><input type="submit" value="저장" /></td>
        </tr>
    </table>
    </form>
</wicket:extend>
</body>
</html>

HTML 템플릿에 wicket:id="" 부분이 Java 클래스에서 생성할 컴포넌트에 해당한다.

여기까지 해서 게시물 작성 페이지가 완료됐다. Jetty 서버를 실행시키고 테스트를 해보자. 잘 동작한다. 정말 쉽지 않은가?

1-2.jpg

1-3.jpg


Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
jpg
1-1.jpg 122.5 kB 1 13-Dec-2006 23:28 59.187.230.143
jpg
1-2.jpg 155.7 kB 1 13-Dec-2006 23:28 59.187.230.143
jpg
1-3.jpg 98.8 kB 1 13-Dec-2006 23:28 59.187.230.143
jpg
2-1.jpg 175.9 kB 1 13-Dec-2006 23:26 59.187.230.143
« This page (revision-12) was last changed on 28-Nov-2007 09:56 by DongGukLee