[WEB] JSP와 서블릿을 이용한 게시판-1-

개나뇽·2024년 8월 6일
0

목적

해당 프로젝트는 서블릿 학습과 MVC 패턴을 공부 목적의 프로젝트입니다.

프로젝트 설정

  • java 17
  • intelliJ
  • gradle
  • apache tomcat 10.1.26

프로젝트 구조

  • DBManager
  • ConnectionConst
  • BoardDAO
  • BoardVO
  • BoardServlet
  • Action + impl 클래스들
  • ActionFactory

구현 코드

  • DB관련 설정 클래스
public abstract class ConnectionConst {
    public static final String URL = "";
    public static final String USER = "";
    public static final String PASSWORD = "";
}

// DB Connection 객체를 얻고 해제를 담당하는 클래스
public class DBManager {

    public static Connection getConnection() {
        try {
            Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); // Todo :  실제로 db와 연결되어 작업을 수행할 수 있는 통로로 작용하는 객체 변수로 해당 객체가 생성되면 db 접근이 가능해진다.
            System.out.println("DB connection success");
            return conn;
        } catch (SQLException e) {
            System.out.println("DB connection failed");
            throw new IllegalStateException(e);
        }
    }

    // select를 수행한후 리소스 해제
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        try {
            rs.close();
            stmt.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // DML(insert, update, delete) 수행후 리소스 해제
    public static void close(Connection conn, Statement stmt) {
        try {
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ConnectionConst 추상 클래스의 경우 객체 생성을 막기위해 abstract 키워드를 사용했으며 DB 연결 정보의 경우 노출되면 위험하기에 별도의 상수로 관리후 .gitignore를 통해 github에 올라가는것을 막으려고 합니다.

DBManager 클래스의 경우 DB 커넥션을 얻는 메서드인 getConnection() 메서드와 sql 실행후 리소스를 정리해주는 메서드인 close()를 정의한 클래스입니다.

  • BoardVO
public class BoardVO {
    private int num;
    private String title;
    private String content;
    private int readCount;
    private Timestamp createDate;
    --- getter, settetr 생략 ----

VO클래스는 자바 빈 값만을 저장한다 하여 Value Object라 부르며 데이터를 전달하는 목적으로 사용되어 Data Transfer Object 즉 DTO라고 부르기도 한다.

  • BoardDAO
public class BoardDAO {
    --- 기본 생성자 생략 ----
    private static BoardDAO instance = new BoardDAO(); 

    public static BoardDAO getInstance() {
        return instance;
    }

    // 전체 게시글 조회
    public List<BoardVO> selectAllBoards() {
        String sql = "select * from board order by num desc";

        List<BoardVO> list = new ArrayList<BoardVO>();
        Connection conn = null; 
        Statement stmt = null;
        ResultSet rs = null; 

        try {
            conn = DBManager.getConnection();
            stmt = conn.createStatement(); /
            rs = stmt.executeQuery(sql);

            while (rs.next()) {
                BoardVO board = new BoardVO();
                board.setNum(rs.getInt("num"));
                board.setTitle(rs.getString("title"));
                board.setReadCount(rs.getInt("readCount"));
                board.setCreateDate(rs.getTimestamp("createDate"));

                list.add(board);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBManager.close(conn, stmt, rs);
        }
        return list;
    }

    // 게시글 작성
    public void insertBoard(BoardVO boardVO) {
        String sql = "insert into board("
                + "title, content,createDate,readCount)"
                + "values(?,?,now(),0)";
        Connection conn = null;  // Todo
        PreparedStatement pstmt = null; // Todo

        try {
            conn = DBManager.getConnection();
            pstmt = conn.prepareStatement(sql);

            pstmt.setString(1, boardVO.getTitle());
            pstmt.setString(2, boardVO.getContent());

            pstmt.executeUpdate();
            System.out.println("Insert Board Success");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBManager.close(conn, pstmt);
        }
    }

    // 게시글 조회수 증가
    public void updateReadCount(String num) {
        String sql = "update board set readcount = readcount +1 where num =?";
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            conn = DBManager.getConnection();
            pstmt = conn.prepareStatement(sql);

            pstmt.setString(1, num);

            pstmt.executeUpdate();
            System.out.println("Insert Board Success");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBManager.close(conn, pstmt);
        }
    }

    // 게시글 단건 조회
    public BoardVO selectDetailBoardByNum(String num) {
        String sql = "select * from board where num =?";

        BoardVO board = null;
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DBManager.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, num);

            rs = pstmt.executeQuery();

            if (rs.next()) {
                board = new BoardVO();
                board.setNum(rs.getInt("num"));
                board.setTitle(rs.getString("title"));
                board.setContent(rs.getString("content"));
                board.setReadCount(rs.getInt("readCount"));
                board.setCreateDate(rs.getTimestamp("createDate"));

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBManager.close(conn, pstmt, rs);
        }
        return board;
    }

    // 게시글 수정
    public void updateBoard(BoardVO boardVO) {
        String sql = "update board set title =?, content =? where num =?";

        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            conn = DBManager.getConnection();
            pstmt = conn.prepareStatement(sql);

            pstmt.setString(1, boardVO.getTitle());
            pstmt.setString(2, boardVO.getContent());
            pstmt.setInt(3, boardVO.getNum());
            pstmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBManager.close(conn, pstmt);
        }
    }

DAO는 데이터베이스의 데이터에 접근하기 위한 객체로 레코드의 조회, 수정, 삭제, 저장 역할을 한다.
-> 즉 데이터베이스에 접근하는 작업을 위해 객체를 생성하기 보다는 싱글톤 패턴으로 클래스를 설계한다.

*싱글톤은 객체를 메모리에 단 한 번만 적재하여 시스템 전반에 걸쳐 특정 자원을 공유할 때 사용 -> 메모리 낭비를 막기 위해 생성

 private static BoardDAO instance = new BoardDAO(); 

  public static BoardDAO getInstance() {
      return instance;
  }

(싱글톤 코드 예제) private 접근 제한자와 static 키워드를 설정해 new 연산자로 객체를 생성후 public로 생성된 static 메서드인 getInstance() 으로만 접근이 가능하도록 했습니다.

  • BoardServlet
@WebServlet(urlPatterns = "/BoardServlet")
public class BoardServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // 요청 메서드
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String command = req.getParameter("command");
        System.out.println("BoardServlet 요청 확인" + command);
        ActionFactory af = ActionFactory.getInstance();
        Action action = af.getAction(command);
        if (action != null) {
            action.execute(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        doGet(req, resp);
    }
}

JSP로 제작된 화면을 통해 클라이언트에게서 요청이 왔을때 요청에 맞는 Action을 호출하는 Servlet 클래스로 모든 요청을 doGet()메서드로 처리하며 JSP의 action 태그로 post 요청이 오면 한글 깨짐 방지를 위해 인코딩 방식 지정후 doGet()메서드로 실행합니다.

  • 커맨트 패턴을 이용한 Action, ActionFactory 클래스
public interface Action {
    public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}

public class ActionFactory {
    private static ActionFactory instance = new ActionFactory();

    private ActionFactory() {
        super();
    }

    public static ActionFactory getInstance() {
        return instance;
    }

    public Action getAction(String command) {
        Action action = null;
        System.out.println("ActionFactory :" + command);
        if (command.equals("board_list")) {
            action = new BoardListAction();
        } else if (command.equals("board_post_form")) {
            action = new BoardPostFormAction();
        } ---- 생략 ------
        return action;
    }
}

// 게시글 리스트를 위한 액션 클래스
public class BoardListAction implements Action {

    @Override
    public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = "/board/boardList.jsp";

        BoardDAO dao = BoardDAO.getInstance();

        List<BoardVO> list = dao.selectAllBoards();

        req.setAttribute("boardList", list);

        RequestDispatcher dispatcher = req.getRequestDispatcher(url);
        dispatcher.forward(req, resp);
    }
}

인터페이스 Action은 요청 파리미터를 동일한 메서드로 처리하기 위한 추상 메서드 execute()를 가지며 요청이 들어오면 요청에 맞는 액션 객체를 생성해주는 ActionFactory 클래스는 싱글톤으로 한 개의 객체만을 생성하며 이를 getInstance()로 호출합니다.

  1. BoardServlet를 요청해 전달된 command 요청 파라미터를 ActionFactory 객체의 getAction() 메서드에 전달합니다.
  2. getAction()메서드에 정의된 조건문을 수행합니다.
  3. getAction()메서드가 리턴하는 Action 객체를 통해 execute()메서드를 호출합니다.

정리

  • 톰캣 설정의 경우 사용하는 툴에 방법이 모두 상의하여 따로 적지 않았습니다.
  • JSP코드의 경우 매우 따로 기술하지 않았습니다.
  • 서블릿의 대한 내용은 아래 링크를 참고해 주세요
    서블릿 정리

추가 예정

  • 서비스 이용자 관리를 위한 회원 가입과 로그인 기능
  • 커넥션 풀 적용하기
  • 프론트 컨트롤러 패턴 적용해보기
  • ActionFactory 클래스의 getAction()메서드 리팩토링

참고

백견불여일타 JSP & Servlet : Oracle & Eclipse

profile
정신차려 이 각박한 세상속에서!!!

0개의 댓글