코딩항해기

[과제/Spring] 게시글 기능 구현하기 본문

problem solving/과제&실습 코딩

[과제/Spring] 게시글 기능 구현하기

miniBcake 2024. 10. 9. 01:48

 

 

로그인 기능 없이 게시글 기능을 구현한다.

Controller 위주로 작성할 예정이다.

  • 게시글 작성하기
  • 게시글 목록보기
  • 게시글 상세보기

 

기초 세팅

더보기

DB구성 (테이블)

CREATE TABLE BOARD(
 NUM INT PRIMARY KEY AUTO_INCREMENT, -- 자동 증가
 TITLE VARCHAR(30) NOT null, -- 제목
 CONTENT VARCHAR(30) NOT NULL -- 내용
);

 

JDBC (DAO)

@Repository
public class BoardDAO {
    private final String SELECTALL = "select NUM, TITLE, CONTENT from board";
    private final String SELECTONE = "SELECT NUM, TITLE, CONTENT FROM BOARD where NUM=?";
    private final String INSERT = "insert into board(TITLE, CONTENT) values(?,?)";

    public List<BoardDTO> selectAll(BoardDTO boardDTO) {
        System.out.println("boardDAO selectAll start");
        List<BoardDTO> datas = new ArrayList<>();
        Connection conn= JDBCUtil.connect();
        PreparedStatement pstmt = null;
        try {
            pstmt = conn.prepareStatement(SELECTALL);
            ResultSet rs=pstmt.executeQuery();
            while(rs.next()) {
                BoardDTO data = new BoardDTO();
                data.setBoardNum(rs.getInt("NUM"));
                data.setContent(rs.getString("CONTENT"));
                data.setTitle(rs.getString("TITLE"));
                datas.add(data);
            }
        } catch (SQLException e) {
            System.out.println("SQLException: " + e.getMessage());
        } finally {
            JDBCUtil.disconnect(conn, pstmt);
        }
        System.out.println("boardDAO selectAll end");
        return datas;
    }
    public BoardDTO selectOne(BoardDTO boardDTO) {
        System.out.println("boardDAO selectOne start");
        BoardDTO data=null;
        Connection conn=JDBCUtil.connect();
        PreparedStatement pstmt = null;
        try {
            pstmt=conn.prepareStatement(SELECTONE);
            pstmt.setInt(1, boardDTO.getBoardNum());
            ResultSet rs=pstmt.executeQuery();
            if(rs.next()) {
                data=new BoardDTO();
                data.setBoardNum(rs.getInt("NUM"));
                data.setContent(rs.getString("CONTENT"));
                data.setTitle(rs.getString("TITLE"));
            }
        } catch (SQLException e) {
            System.out.println("SQLException: " + e.getMessage());
            return null;
        } finally {
            JDBCUtil.disconnect(conn, pstmt);
        }
        System.out.println("boardDAO selectOne end");
        return data;
    }
    public boolean insert(BoardDTO boardDTO) {
        System.out.println("board insert start");
        Connection conn=JDBCUtil.connect();
        PreparedStatement pstmt = null;
        try {
            pstmt=conn.prepareStatement(INSERT);
            pstmt.setString(1, boardDTO.getTitle());
            pstmt.setString(2, boardDTO.getContent());
            if(pstmt.executeUpdate() <= 0){
                System.out.println("board insert result failed");
                return false;
            }
        } catch (SQLException e) {
            System.out.println("SQLException: " + e.getMessage());
            return false;
        } finally {
            JDBCUtil.disconnect(conn, pstmt);
        }
        System.out.println("board insert end");
        return true;
    }
    private boolean update(BoardDTO boardDTO) {
        return false;
    }
    private boolean delete(BoardDTO boardDTO) {
        return false;
    }
}

 

JDBCUtil

public class JDBCUtil {
	private static final String driverName="com.mysql.cj.jdbc.Driver";
	private static final String url="jdbc:mysql://localhost:3306/minibcake";
	private static final String userName="root";
	private static final String password="1234";

	public static Connection connect() {
		Connection conn=null;

		try {
			Class.forName(driverName);
		} catch (ClassNotFoundException e) {
			System.err.println("Class.forName(driverName) fail");
		} finally {
			System.out.println("드라이버를 메모리에 로드(load,적재)");
		}

		try {
			conn=DriverManager.getConnection(url, userName, password);
		} catch (SQLException e) {
			System.err.println("Connection fail");
		} finally {
			System.out.println("연결 객체 확보");
		}

		return conn;
	}

	public static boolean disconnect(Connection conn, PreparedStatement pstmt) {
		try {
			if(pstmt == null || conn == null) {
				System.err.println("pstmt, conn null");
				return false;
			}
			pstmt.close();
			conn.close();
		} catch (SQLException e) {
			System.err.println("pstmt, conn close fail");
			return false;
		} finally {
			System.out.println("연결 해제");
		}
		return true;
	}

}

 

BoardDTO

public class BoardDTO {
    private int boardNum;
    private String content;
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getBoardNum() {
        return boardNum;
    }

    public void setBoardNum(int boardNum) {
        this.boardNum = boardNum;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "BoardDTO{" +
                "boardNum=" + boardNum +
                ", content='" + content + '\'' +
                ", title='" + title + '\'' +
                '}';
    }
}

 

WEB-INF.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="https://jakarta.ee/xml/ns/jakartaee" 
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0">
    <!-- DispatcherServlet 생성-->
    <servlet>
        <servlet-name>ds</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ds</servlet-name>
        <!-- .do로 끝나는 요청 처리-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

 

WEB-INF. ds-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--어노테이션 스캔-->
    <!--컨트롤러 Autowired 스캔 이후 Repository도 필요하므로 같이 스캔-->
    <context:component-scan base-package="com.koreait.app.view"/>
    <context:component-scan base-package="com.koreait.app.biz"/>

    <!--ViewResolver는 여러 개가 될 수 있으므로 id를 반드시 기입-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
        <!--SI 주입-->
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

 

페이지(View) 세팅

더보기

webapp 하위 페이지

boardWrite.jsp (게시글 작성페이지)
: 페이지를 로드하는데 별도의 데이터가 필요없으므로 webapp 하위에 위치한다.

(main 페이지는 생략)

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>게시글 작성</title>
</head>
<body>
<main>
    <h1>게시글 작성</h1>
    <%--데이터를 입력받을 부분--%>
    <form action="boardWrite.do" method="POST">
        <input type="text" name="title" placeholder="제목을 입력하세요." required>
        <textarea name="content" placeholder="내용을 입력하세요." required></textarea>
        <input type="submit" value="게시글 작성">
    </form>
    <%--메인으로 돌아갈 버튼--%>
    <a href="main.do">메인으로 돌아가기</a>
</main>
</body>
</html>

 

webapp.WEB-INF.views 하위 페이지

 boardInfo.jsp (게시글 상세보기 페이지)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${data.title}</title>
    <%--페이지이름은 게시글 제목으로--%>
</head>
<body>
<main>
    <h3>제목</h3>
    <%--제목 데이터를 띄울 부분--%>
    <div>${data.title}</div>
    <h3>내용</h3>
    <%--내용 데이터를 띄울 부분--%>
    <p>${data.content}</p>
    <%--돌아갈 버튼--%>
    <a href="boardList.do">게시글 목록으로 돌아가기</a>
</main>
</body>
</html>

 

boardList.jsp (게시글 목록보기 페이지)

<%@ 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 lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>일반 게시판</title>
</head>
<body>
<main>
    <h1>일반 게시판</h1>
    <section>
        <button onclick="location.href='boardWritePage.do'">글쓰기</button>
        <article class="title">
            <span>번호</span>
            <span>제목</span>
        </article>
        <%--Controller에서 넘겨줄때 K값이 datas이므로 datas로 받는다. (사전에 약속된 값)--%>
        <%--varstatue은 인덱스번호이다. --%>
        <c:forEach var="data" items="${datas}"  varStatus="status">
            <article>
                <span>${status.index + 1}</span>
                <a href="boardInfo.do?boardNum=${data.boardNum}">${data.title}</a>
            </article>
        </c:forEach>
    </section>
    <a href="main.do">메인으로 돌아가기</a>
</main>
</body>
</html>

 

 

Controller

현재 사용되는 Controller는 두 가지이다. main 기능을 담당하는 Controller와 board에 관련된 기능을 담당하는 Controller이다. 먼저 main 담당은 main.do요청이 왔을 때 main페이지로 이동할 수 있게 하는 역할만을 수행한다.

@Controller("main")
public class MainController {
    @RequestMapping("/main.do")
    public String mainPage() throws Exception {
        return "redirect:main.jsp"; //단순 페이지 이동
    }
}

 

 

board에 관련된 기능을 수행하는 Controller는 총 4가지의 기능을 수행해야한다.

  1. 게시글 작성 기능 (boardWrite.do)
  2. 게시글 작성 페이지로 이동하는 기능 (boardWritePage.do)
  3. 게시글 리스트를 보여주는 기능 (boardList.do)
  4. 게시글 상세 정보를 보여주는 기능 (boardInfo.do)

이 때 게시글 작성(1번)과, 게시글 작성 페이지로 이동하는 기능(2번)은 별도의 데이터가 필요 없으므로 redirect로 진행된다.

반면 게시글 리스트(3번)와 상세 정보(4번)의 경우에는 페이지에 DB에서 받아온 정보를 보여줘야하기 때문에 포워드 방식으로 진행되며 ViewResolver를 통해 path가 완성된다. 또한 사용자의 접근을 방지하기 위해 WEB-INF 하위에 있는 views 폴더에 위치하게 된다.

 

@Controller("board")
public class BoardController {
    //게시글 작성
    @RequestMapping("/boardWrite.do")
    public String boardWrite(BoardDAO boardDAO, BoardDTO boardDTO) throws Exception {
        System.out.println("boardWrite.do");
        if(!boardDAO.insert(boardDTO)){ //DB에 추가
            //insert 실패 시
            System.out.println("Insert failed");
            throw new RuntimeException("Insert failed");
        }
        //완료 후 이동할 페이지, 이동할 Controller(요청)
        return "redirect:main.jsp";
    }
    //게시글 작성페이지 이동
    @RequestMapping("/boardWritePage.do")
    public String boardWritePage() throws Exception {
        System.out.println("boardWritePage.do");
        return "redirect:boardWrite.jsp"; //단순 페이지 이동
    }

    //게시글 리스트
    @RequestMapping("/boardList.do")
    public String boardList(BoardDAO boardDAO, BoardDTO boardDTO, Model model) throws Exception {
        System.out.println("boardList.do");
        model.addAttribute("datas", boardDAO.selectAll(boardDTO)); //DB 데이터 전달
        return "boardList";
    }

    //게시글 상세보기
    @RequestMapping("/boardInfo.do")
    public String boardInfo(BoardDAO boardDAO, BoardDTO boardDTO, Model model) throws Exception {
        System.out.println("boardInfo.do");
        model.addAttribute("data", boardDAO.selectOne(boardDTO)); //DB 데이터 전달
        return "boardInfo";
    }
}