Coding History

국비 지원 IT(웹앱개발) 취업반 강의 44일차 1 (HTML, CSS, JS)

BlackBirdIT 2024. 8. 6. 17:37

JSP를 활용해서 웹 페이지로 무언가를 보는 것 까지 넘어갔다.

@WebServlet("/Article/list")
public class ArticleListServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        // DB 연결
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("클래스 x");
            e.printStackTrace();
        }

        response.getWriter().append("123");

        String url = "jdbc:mysql://127.0.0.1:3306/AM_JDBC_2024_07?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Seoul";

        String user = "root";
        String password = "";

        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url, user, password);
            response.getWriter().append("연결 성공!");

            DBUtil dbUtil = new DBUtil(request, response);

            String sql = "SELECT * FROM article";

            List<Map<String, Object>> articleRows = dbUtil.selectRows(conn, sql);

//            response.getWriter().append(articleRows.toString());

            request.setAttribute("articleRows", articleRows);
            request.getRequestDispatcher("/jsp/article/list.jsp").forward(request, response);

        } catch (SQLException e) {
            System.out.println("에러 1 : " + e);
        } finally {
            try {
                if (conn != null && !conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

이렇게 서버를 연결하고 데이터를 가져오게 하고,

JSP파일 list를 만들어서 게시물을 불러올 수 있게 코드를 짰다.

<%@page import="java.util.List"%>
<%@page import="java.util.Map"%>

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

<%
List<Map<String, Object>> articleRows = (List<Map<String, Object>>) request.getAttribute("articleRows");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시물 리스트</title>
</head>
<body>

    <h2>게시물 목록 v1</h2>

    <ul>
        <li><%=articleRows.get(0).get("id")%>번, <%=articleRows.get(0).get("regDate")%>,<%=articleRows.get(0).get("title")%>,<%=articleRows.get(0).get("body")%></li>
        <li><%=articleRows.get(1).get("id")%>번, <%=articleRows.get(1).get("regDate")%>,<%=articleRows.get(1).get("title")%>,<%=articleRows.get(1).get("body")%></li>
        <li><%=articleRows.get(2).get("id")%>번, <%=articleRows.get(2).get("regDate")%>,<%=articleRows.get(2).get("title")%>,<%=articleRows.get(2).get("body")%></li>
    </ul>
</body>
</html>

이렇게 하면 게시물을 세개 가져올 수 있는데 모든 게시물을 다 가져오고 싶어서 반복문을 활용하면 코드는 이렇다.

<%@page import="java.util.List"%>
<%@page import="java.util.Map"%>

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

<%
List<Map<String, Object>> articleRows = (List<Map<String, Object>>) request.getAttribute("articleRows");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시물 리스트</title>
</head>
<body>

    <h2>게시물 목록 v1</h2>

    <ul>
    <% 
        if (articleRows != null && !articleRows.isEmpty()) {
            for (int i = 0; i < articleRows.size(); i++) {
                Map<String, Object> article = articleRows.get(i);
    %>
        <li><%=article.get("id")%>번, <%=article.get("regDate")%>, <%=article.get("title")%>, <%=article.get("body")%></li>
    <% 
            }
        } else {
    %>
        <li>게시물이 없습니다.</li>
    <% 
        }
    %>
    </ul>
</body>
</html>

사실 처음에 만들어 놓고 계속 list.JSP에서 실행을 시켜서 게시물이 불러와지질 않았다. 뭐가 문제인지 몰라서 게시물이 없는 경우를 만들어서 if문 까지 넣어봤다.(결국 문제는 내가 코드를 실행하는 위치가 문제였다.)

암튼 화면에 보이는 모습은 이렇다.

예전에 공부하면서 db에 저장되었던 글도 보이고 내가 이걸 하려고 글을 늘린 데이터도 보인다.

강사님은 버전을 네가지로 보여주셨다. 역시 향상된 for문이 보기 편하다.

<h2>게시물 목록 v4</h2>

    <ul>
        <%
        for (Map<String, Object> articleRow : articleRows) {
        %>
        <li><%=articleRow.get("id")%>번, <%=articleRow.get("regDate")%>,<%=articleRow.get("title")%>,<%=articleRow.get("body")%></li>
        <%
        }
        %>
    </ul>

    <h2>게시물 목록 v3</h2>

    <ul>
        <%
        for (int i = 0; i < articleRows.size(); i++) {
        %>
        <li><%=articleRows.get(i).get("id")%>번, <%=articleRows.get(i).get("regDate")%>,<%=articleRows.get(i).get("title")%>,<%=articleRows.get(i).get("body")%></li>
        <%
        }
        %>
    </ul>

    <h2>게시물 목록 v2</h2>

    <ul>
        <%
        for (int i = 0; i <= 2; i++) {
        %>
        <li><%=articleRows.get(i).get("id")%>번, <%=articleRows.get(i).get("regDate")%>,<%=articleRows.get(i).get("title")%>,<%=articleRows.get(i).get("body")%></li>
        <%
        }
        %>
    </ul>



    <h2>게시물 목록 v1</h2>

    <ul>
        <li><%=articleRows.get(0).get("id")%>번, <%=articleRows.get(0).get("regDate")%>,<%=articleRows.get(0).get("title")%>,<%=articleRows.get(0).get("body")%></li>
        <li><%=articleRows.get(1).get("id")%>번, <%=articleRows.get(1).get("regDate")%>,<%=articleRows.get(1).get("title")%>,<%=articleRows.get(1).get("body")%></li>
        <li><%=articleRows.get(2).get("id")%>번, <%=articleRows.get(2).get("regDate")%>,<%=articleRows.get(2).get("title")%>,<%=articleRows.get(2).get("body")%></li>
    </ul>

다음으로 detail 기능을 담당할 클래스와 페이지.

package com.KoreaIT.java.jsp_AM;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/article/detail")
public class ArticleDetailServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        // DB 연결
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("클래스 로드 실패");
            e.printStackTrace();
        }

        String url = "jdbc:mysql://127.0.0.1:3306/AM_JDBC_2024_07?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Seoul";
        String user = "root";
        String password = "1234";

        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url, user, password);
            System.out.println("DB 연결 성공!");

            DBUtil dbUtil = new DBUtil(request, response);

            String idParam = request.getParameter("id");
            if (idParam == null || idParam.isEmpty()) {
                response.getWriter().append("ID 파라미터가 없습니다.");
                return;
            }

            int id = Integer.parseInt(idParam);

            String sql = String.format("SELECT * FROM article WHERE id = %d", id);
            System.out.println("실행할 SQL: " + sql);

            Map<String, Object> articleRow = dbUtil.selectRow(conn, sql);
            if (articleRow == null) {
                response.getWriter().append("해당 ID의 게시물이 없습니다.");
                return;
            }

            request.setAttribute("articleRow", articleRow);
            request.getRequestDispatcher("/jsp/article/detail.jsp").forward(request, response);

        } catch (SQLException e) {
            System.out.println("SQL 에러: " + e.getMessage());
            e.printStackTrace(response.getWriter());
        } finally {
            try {
                if (conn != null && !conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
<%@page import="java.util.Map"%>

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

<%
Map<String, Object> articleRow = (Map<String, Object>) request.getAttribute("articleRow");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시물 상세페이지</title>
</head>
<body>

    <h2>게시물 상세페이지</h2>

    <%
    if (articleRow == null) {
        out.println("<div>게시물이 없습니다.</div>");
    } else {
    %>
        <div>
            번호 : <%= articleRow.get("id") %>
        </div>
        <div>
            날짜 : <%= articleRow.get("regDate") %>
        </div>
        <div>
            제목 : <%= articleRow.get("title") %>
        </div>
        <div>
            내용 : <%= articleRow.get("body") %>
        </div>
    <% } %>
    <div>
        <a style="color: green" href="list">리스트로 돌아가기</a>
    </div>

</body>
</html>

이렇게 상세보기를 만들었고 delete 클래스를 만들고 해당 기능 구현 뒤 여기에 삭제버튼을 만들어서 삭제할 수 있는 기능까지 만들면 된다.

그전에 sql인젝션이라고 db공격하는 개념을 배웠다. 위의 코드를 보면 String으로 sql을 날리고 있는데, secSql로 하면 타입 이슈 때문에 작동이 안되어서 DbUtil의 코드를 싹 다 String으로 고쳐서 사용했다. 근데 이렇게 하면 인젝션 공격에 매우 취약해져서 우리의 소중한 데이터가 타인의 공격에 의해서 모두 날라가거나, 관리자 권한으로 로그인을 해서 사이트를 엉망으로 만들 가능성이 높아진다. 그래서 다시 SecSql 타입으로 고쳐서 db의 보안을 더 강화할 수 있게 코드를 다시 고치고 sql 들도 String 타입이 아닌, SecSql 타입으로 바꾸어주는 작업을 했다.

아무튼 detail에 삭제를 만드는게 아니고 list에 만들었다.


화면에 보이는 모양은 이런 모양이고, 코드를 보면.

package com.KoreaIT.java.jsp_AM.servlet;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import com.KoreaIT.java.jsp_AM.util.DBUtil;
import com.KoreaIT.java.jsp_AM.util.SecSql;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/article/doDelete")
public class ArticleDeleteServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        // DB 연결
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("클래스 x");
            e.printStackTrace();
        }

        String url = "jdbc:mysql://127.0.0.1:3306/AM_JDBC_2024_07?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Seoul";

        String user = "root";
        String password = "1234";

        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url, user, password);
            response.getWriter().append("연결 성공!");

            int id = Integer.parseInt(request.getParameter("id"));

//            String sql = String.format("SELECT * FROM article WHERE id = %d", id);

            SecSql sql = SecSql.from("DELETE");
            sql.append("FROM article");
            sql.append("WHERE id = ?", id);

            DBUtil.delete(conn, sql);

            response.getWriter().append(String.format("<script>alert('%d번글이 삭제됨'), location.replace('list');</script>", id));
//            request.setAttribute("articleRow", articleRow);
//            request.getRequestDispatcher("/jsp/article/detail.jsp").forward(request, response);

        } catch (SQLException e) {
            System.out.println("에러 1 : " + e);
        } finally {
            try {
                if (conn != null && !conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

delete servlet 클래스고,

<%@page import="java.util.List"%>
<%@page import="java.util.Map"%>

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

<%
List<Map<String, Object>> articleRows = (List<Map<String, Object>>) request.getAttribute("articleRows");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시물 목록</title>
</head>
<body>

    <h2>게시물 목록</h2>

    <a href="../home/main">메인 페이지로 </a>

    <table style="border-collapse: collapse; border-color: green;"
        border="1px">
        <thead>
            <tr>
                <th>번호</th>
                <th>날짜</th>
                <th>제목</th>
                <th>내용</th>
                <th>삭제</th>
            </tr>
        </thead>
        <tbody>
            <%
            for (Map<String, Object> articleRow : articleRows) {
            %>
            <tr style="text-align: center;">
                <td><%=articleRow.get("id")%></td>
                <td><%=articleRow.get("regDate")%></td>
                <td><%=articleRow.get("title")%></td>
                <td><%=articleRow.get("body")%></td>
                <td><a href="doDelete?id=<%=articleRow.get("id")%>">del</a></td>
            </tr>
            <%
            }
            %>
        </tbody>
    </table>

    <!--     <ul> -->
    <%--         <% --%>
    <%--//         for (Map<String, Object> articleRow : articleRows) {--%>
    <%--         %> --%>
    <%--         <li><a href="detail?id=<%=articleRow.get("id")%>"><%=articleRow.get("id")%>번, --%>
    <%--                 <%=articleRow.get("regDate")%>,<%=articleRow.get("title")%>,<%=articleRow.get("body")%></a></li> --%>
    <%--         <% --%>
    <%--//         }--%>
    <%--         %> --%>
    <!--     </ul> -->


</body>
</html>

여기가 list 화면 개선한것.

del을 클릭하면

response.getWriter().append(String.format("<script>alert('%d번글이 삭제됨'), location.replace('list');</script>", id));

이렇게 js alert를 활용해서 창이 뜨게 하고

location.replace()를 사용해서 바로 다시 list로 갈 수 있게 구현하였다!