SPRING January 14, 2021

spring-Webmaker-게시판

Words count 17k Reading time 16 mins.

lombok과 Spring을 이용해 게시판을 만들어보자

lombok 이란

일반적으로 자바개발을 하다보면 Model 을 만들고 각 멤버변수를 접근할수 있는 (각 요소들이 private 접근권한을 가지고 있을때) method 를 만들게 된다. 아래처럼 말이다. (윈도우/이클립스 기준)

public class Student {
    private int id;
    private String name;
    private int grade;
    private String department;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", grade=" + grade + ", department=" + department + "]";
    }    
}

이렇게 하는 방법도 있지만 어노테이션 설정으로 적용할수 있는 간단한 라이브러리를 소개하고자 한다. 바로 lombok, 공식 홈페이지 : https://projectlombok.org 설치 및 사용방법은 아주 간단하다. 공식 홈페이지에서 jar를 다운받고 실행, 아래처럼 이클립스 실행파일 경로를 설정해준다음에 인스톨을 누르면 된다. lombok.png maven 환경에서 dependency를 가져오기 위해서는 당연히 추가설정을 해줘야 한다.

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version> <!--버전은 그때 맞춰서-->
</dependency>

실제로 코드상에서 사용방법은 다음과 같다. 정말 간단히, 어노테이션만 적용해주면 끝!

lombok 설치 방법중 중요한 부분만 캡쳐해 보았다 자세한 사항은 따로 검색해 보길 바란다.

Db설정

DROP SEQUENCE SIMPLE_SEQ;
CREATE SEQUENCE SIMPLE_SEQ
START WITH 1
INCREMENT BY 1
MAXVALUE 999999
NOCYCLE
NOCACHE;

DROP TABLE SIMPLE;
CREATE TABLE SIMPLE
(
    NO NUMBER PRIMARY KEY,
    WRITER VARCHAR2(100),
    TITLE VARCHAR2(1000),
    CONTENT VARCHAR2(4000),
    REGDATE DATE

);

DTO 생성

package com.koreait.simple1.dto;

import java.sql.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor  // 디폴트 생성자를 자동으로 생성해 줍니다.
@AllArgsConstructor  // 필드를 이용한 생성자를 자동으로 생성해 줍니다.
@Data  // lombok의 애너테이션, getter/setter등을 자동으로 생성해 줍니다.
       // Window - Show View - Outline 에서 확인이 가능합니다.
public class SimpleDto {

    // field
    private int no;
    private String writer;
    private String title;
    private String content;
    private Date regDate;

}

lombok과 애너테이션들을 이용해 생성자를 자동으로 생성!!


DAO

package com.koreait.simple1.dao;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.koreait.simple1.dto.SimpleDto;

public class SimpleDao {

    // Connection Pool 처리는 DataSource 클래스가 합니다.
    // 필요한 설정은 톰캣 내부의 context.xml에 있습니다.
    // <Resource name="jdbc/oracle">을 처리하는 Context 클래스가 필요합니다.
    // name에 의한 접근: JNDI

    private DataSource dataSource;

    // singleton
    private SimpleDao() {
        // context.xml에 있는 설정을 읽어와서 dataSource를 만듭니다.
        try {
            Context context = new InitialContext();
            dataSource = (DataSource)context.lookup("java:comp/env/jdbc/oracle");  // 톰캣이다: java:comp/env/ 나머지는 <Resource>의 name 속성
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
    private static SimpleDao simpleDao = new SimpleDao();
    public static SimpleDao getInstance() {
        return simpleDao;
    }


    private Connection con;
    private PreparedStatement ps;
    private ResultSet rs;
    private String sql;


    private void close(Connection con, PreparedStatement ps, ResultSet rs) {
        try {
            if (rs != null) { rs.close(); }
            if (ps != null) { ps.close(); }
            if (con != null) { con.close(); }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /***** 1. list *****/
    public List<SimpleDto> simpleList() {

        List<SimpleDto> list = new ArrayList<SimpleDto>();

        try {
            con = dataSource.getConnection();
            sql = "SELECT NO, WRITER, TITLE, CONTENT, REGDATE FROM SIMPLE";
            ps = con.prepareStatement(sql);
            rs = ps.executeQuery();  // select문 실행 메소드
            while (rs.next()) {
                int no = rs.getInt("NO");
                String writer = rs.getString("WRITER");
                String title = rs.getString("TITLE");
                String content = rs.getString("CONTENT");
                Date regDate = rs.getDate("REGDATE");
                SimpleDto simpleDto = new SimpleDto(no, writer, title, content, regDate);
                list.add(simpleDto);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, ps, rs);
        }

        return list;

    }


    /***** 2. insert *****/
    public void simpleInsert(SimpleDto simpleDto) {

        try {
            con = dataSource.getConnection();
            sql = "INSERT INTO SIMPLE VALUES (SIMPLE_SEQ.NEXTVAL, ?, ?, ?, SYSDATE)";
            ps = con.prepareStatement(sql);
            ps.setString(1, simpleDto.getWriter());
            ps.setString(2, simpleDto.getTitle());
            ps.setString(3, simpleDto.getContent());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, ps, null);
        }

    }


    /***** 3. view *****/
    public SimpleDto simpleView(int no) {

        SimpleDto simpleDto = null;

        try {
            con = dataSource.getConnection();
            sql = "SELECT * FROM SIMPLE WHERE NO = ?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, no);
            rs = ps.executeQuery();
            if (rs.next()) {
                String writer = rs.getString("WRITER");
                String title = rs.getString("TITLE");
                String content = rs.getString("CONTENT");
                Date regDate = rs.getDate("REGDATE");
                simpleDto = new SimpleDto(no, writer, title, content, regDate);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, ps, rs);
        }

        return simpleDto;

    }

    /***** 4. delete *****/
    public void simpleDelete(int no) {

        try {
            con = dataSource.getConnection();
            sql = "DELETE FROM SIMPLE WHERE NO = ?";
            ps = con.prepareStatement(sql);
            ps.setInt(1, no);
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, ps, null);
        }

    }


    /***** 5. update *****/
    public void simpleUpdate(SimpleDto simpleDto) {

        try {
            con = dataSource.getConnection();
            sql = "UPDATE SIMPLE SET TITLE = ?, CONTENT = ? WHERE NO = ?";
            ps = con.prepareStatement(sql);
            ps.setString(1, simpleDto.getTitle());
            ps.setString(2, simpleDto.getContent());
            ps.setInt(3, simpleDto.getNo());
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, ps, null);
        }

    }

}

기능 추가될 때마다 추가시켜 나간다

Command

첫번째로 Java interface 를 이용해 모든 커멘드에 기본 인터페이스로 참조될 SimpleCommand.java 생성

SimpleCommand

package com.koreait.simple1.command;

import org.springframework.ui.Model;

public interface SimpleCommand {

    public void execute(Model model);
}

SimpleListCommand

package com.koreait.simple1.command;

import org.springframework.ui.Model;

import com.koreait.simple1.dao.SimpleDao;

public class SimpleListCommand implements SimpleCommand {

    @Override
    public void execute(Model model) {

        // JSP와 달리 command는 데이터만 처리하면 됩니다.
        // VIEW는 컨트롤러가 처리합니다.
        model.addAttribute("list", SimpleDao.getInstance().simpleList());

    }

}

SimpleInsertCommand

package com.koreait.simple1.command;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.ui.Model;

import com.koreait.simple1.dao.SimpleDao;
import com.koreait.simple1.dto.SimpleDto;

public class SimpleInsertCommand implements SimpleCommand {

    @Override
    public void execute(Model model) {

        // 전달 받은 model에는 request가 들어 있어요.
        // 그걸 꺼내야 합니다.

        // model에 저장된 속성을 직접 꺼내는 방법은 없어요.
        // 대신 model은 Map으로 바꾸는 것이 가능합니다. (asMap() 메소드)

        // Map으로 바꾸고 나면 Map에서 꺼내는 메소드인 get() 메소드를 이용해서 꺼내면 됩니다.

        Map<String, Object> map = model.asMap();

        HttpServletRequest request = (HttpServletRequest) map.get("request");  // model에 저장된 속성(attribute)을 키 값으로 사용하면 됩니다.

        String writer = request.getParameter("writer");
        String title = request.getParameter("title");
        String content = request.getParameter("content");

        SimpleDto simpleDto = new SimpleDto();
        simpleDto.setWriter(writer);
        simpleDto.setTitle(title);
        simpleDto.setContent(content);

        SimpleDao.getInstance().simpleInsert(simpleDto);

    }

}

Controller

@Controller
public class SimpleController {


    // 21_simple 프로젝트는 command들을 new로 생성하는 연습입니다.


    // field
    private SimpleCommand command;

    @RequestMapping(value="/", method=RequestMethod.GET)
    public String index() {
        return "index";
    }

    @RequestMapping(value="simpleList.do")
    public String simpleList(Model model) {
        // DB에서 list를 가져와서 simpleListPage.jsp로 이동시킵니다.
        // simpleListPage.jsp로 보낼 데이터인 list는 Model에 저장합니다.
        // Model에 저장된 데이터를 simpleListPage.jsp으로 보내려면 forward합니다.
        /*
            SimpleListCommand가 없다면 아래와 같이 작업할 수 있겠습니다.
            하지만, 이렇게 하시면 안 됩니다.
            SimpleDao simpleDao = SimpleDao.getInstance();
            model.addAttribute("list", simpleDao.simpleList());
            return "simple/simpleListPage";
        */

        command = new SimpleListCommand();  // 개발자 직접 생성
        command.execute(model);
        return "simple/simpleListPage";  // simple 폴더 아래 simpleListPage.jsp로 forward합니다.

    }

    @RequestMapping(value="simpleInsertPage.do")
    public String simpleInsertPage() {
        return "simple/simpleInsertPage";  // simple 폴더 아래 simpleInsertPage.jsp로 forward합니다.
    }

    @RequestMapping(value="simpleInsert.do")
    public String simpleInsert(HttpServletRequest request, Model model) {

        // SimpleInsertCommand 가 삽입을 담당합니다.
        // 모든 command들은 model이 필요합니다.

        // SimpleInsertCommand에게 request를 전달하려면?
        // 모든 command들은 실행할 때 model만 전달 받습니다.
        // 따라서, model에 request를 저장해 둡니다. 그리고 model을 전달합니다.
        model.addAttribute("request", request);

        command = new SimpleInsertCommand();  // 개발자가 직접 생성
        command.execute(model);

        // return "simple/simpleListPage";  // insert 후에는 forward하면 안 됩니다.
        return "redirect:simpleList.do";  // insert 후에는 redirect 합니다.

    }    
}

View

Index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8”%>

Insert title here
    <!-- 이동합니다 -->
    <a href="simpleList.do">간단게시판으로 이동</a>

첫 화면을 먼저 만들어 준다

SimpleListPage

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

    <table border="1">
        <thead>
            <tr>
                <td>번호</td>
                <td>작성자</td>
                <td>제목</td>
                <td>작성일</td>
            </tr>
        </thead>
        <tbody>
            <c:if test="${empty list}">
                <tr>
                    <td colspan="4">없음</td>
                </tr>
            </c:if>
            <c:if test="${not empty list}">
                <c:forEach var="simpleDto" items="${list}">
                    <tr>
                        <td>${simpleDto.no}</td>
                        <td>${simpleDto.writer}</td>
                        <td>${simpleDto.title}</td>
                        <td>${simpleDto.regDate}</td>
                    </tr>
                </c:forEach>
            </c:if>
        </tbody>
        <tfoot>
            <tr>
                <td colspan="4">
                    <input type="button" value="새글작성하러가기" onclick="location.href='simpleInsertPage.do'" />
                </td>
            </tr>
        </tfoot>
    </table>

</body>
</html>

간단게시판으로 이동하면 보여줄화면이다

SimpleInsertPage

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

    <form action="simpleInsert.do">
        작성자
        <input type="text" name="writer" /><br/>
        제목
        <input type="text" name="title" /> <br/>
        내용
        <input type="text" name="content" /><br/>
        <button>작성완료</button>
    </form>

</body>
</html>

새글 작성하러 가기로 가면 보여줄 화면이다

0%