본문 바로가기
JSP

JSP(웹소켓으로 채팅 프로그램 만들기)_2024-09-10

by 앵보몬 2024. 9. 11.
728x90
반응형

웹 소켓을 이용해 간단한 채팅 프로그램을 구현해봤다. "이것이 자바다" 책에서 소켓 클래스를 이용해 채팅 프로그램을 만드는 방식은 생각보다 꽤 복잡했던 것으로 기억하는데, 어노테이션을 사용해 코드를 작성하니 길이도 짧아지고 가독성도 좋아진 것 같다.

소켓(Socket): 서버와 클라이언트가 양방향 통신을 할 수 있게 해주는 소프트웨어

① 서버에서 서버용 소켓(ServerSocket)을 생성한 후, 클라이언트의 접속을 기다린다.

② 클라이언트가 소켓을 생성하여 서버로 연결을 요청한다.

③ 서버가 접속을 허가(accept)한다.

④ 서버와 클라이언트가 각각 통신을 위한 I/O 스트림을 생성한다.

⑤ 스트림을 통해 서버와 클라이언트가 통신한다.

⑥ 클라이언트가 모든 작업을 마친 후 소켓을 종료한다

⑦ 서버는 새로운 클라이언트의 접속을 위해 대기하거나 종료할 수 있다.

 

웹소켓으로 채팅 프로그램 만들기.zip
0.02MB

 

BoardDTO.java

package model;

public class BoardDTO {
    private String num;
    private String title;
    private String content;
    private String id;
    private java.sql.Date postdate;
    private String visitcount;
    private String name;
   
    public String getNum() {
        return num;
    }
    public void setNum(String num) {
        this.num = num;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public java.sql.Date getPostdate() {
        return postdate;
    }
    public void setPostdate(java.sql.Date postdate) {
        this.postdate = postdate;
    }
    public String getVisitcount() {
        return visitcount;
    }
    public void setVisitcount(String visitcount) {
        this.visitcount = visitcount;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

BoardDAO.java

package model;

import java.util.List;
import java.util.Map;
import java.util.Vector;

import common.JDBConnect;
import jakarta.servlet.ServletContext;

public class BoardDAO extends JDBConnect {
     public BoardDAO(ServletContext application) {
            super(application);
        }

        // 검색 조건에 맞는 게시물의 개수를 반환합니다.
        public int selectCount(Map<String, Object> map) {
            int totalCount = 0; // 결과(게시물 수)를 담을 변수

            // 게시물 수를 얻어오는 쿼리문 작성
            String query = "SELECT COUNT(*) FROM board";
            if (map.get("searchWord") != null) {
                query += " WHERE " + map.get("searchField") + " "
                       + " LIKE '%" + map.get("searchWord") + "%'";
            }

            try {
                stmt = con.createStatement();   // 쿼리문 생성
                rs = stmt.executeQuery(query);  // 쿼리 실행
                rs.next();  // 커서를 첫 번째 행으로 이동
                totalCount = rs.getInt(1);  // 첫 번째 칼럼 값을 가져옴
            }
            catch (Exception e) {
                System.out.println("게시물 수를 구하는 중 예외 발생");
                e.printStackTrace();
            }

            return totalCount;
        }
       
        // 검색 조건에 맞는 게시물 목록을 반환합니다.
        public List<BoardDTO> selectList(Map<String, Object> map) {
            List<BoardDTO> bbs = new Vector<BoardDTO>();  // 결과(게시물 목록)를 담을 변수

            String query = "SELECT * FROM board ";
            if (map.get("searchWord") != null) {
                query += " WHERE " + map.get("searchField") + " "
                       + " LIKE '%" + map.get("searchWord") + "%' ";
            }
            query += " ORDER BY num DESC ";

            try {
                stmt = con.createStatement();   // 쿼리문 생성
                rs = stmt.executeQuery(query);  // 쿼리 실행

                while (rs.next()) {  // 결과를 순화하며...
                    // 한 행(게시물 하나)의 내용을 DTO에 저장
                    BoardDTO dto = new BoardDTO();

                    dto.setNum(rs.getString("num"));          // 일련번호
                    dto.setTitle(rs.getString("title"));      // 제목
                    dto.setContent(rs.getString("content"));  // 내용
                    dto.setPostdate(rs.getDate("postdate"));  // 작성일
                    dto.setId(rs.getString("id"));            // 작성자 아이디
                    dto.setVisitcount(rs.getString("visitcount"));  // 조회수

                    bbs.add(dto);  // 결과 목록에 저장
                }
            }
            catch (Exception e) {
                System.out.println("게시물 조회 중 예외 발생");
                e.printStackTrace();
            }

            return bbs;
        }
       
        // 검색 조건에 맞는 게시물 목록을 반환합니다(페이징 기능 지원).
        public List<BoardDTO> selectListPage(Map<String, Object> map) {
            List<BoardDTO> bbs = new Vector<BoardDTO>();  // 결과(게시물 목록)를 담을 변수
           
            // 쿼리문 템플릿  
            String query = " SELECT * FROM ( "
                         + "    SELECT Tb.*, ROWNUM rNum FROM ( "
                         + "        SELECT * FROM board ";

            // 검색 조건 추가
            if (map.get("searchWord") != null) {
                query += " WHERE " + map.get("searchField")
                       + " LIKE '%" + map.get("searchWord") + "%' ";
            }
           
            query += "      ORDER BY num DESC "
                   + "     ) Tb "
                   + " ) "
                   + " WHERE rNum BETWEEN ? AND ?";

            try {
                // 쿼리문 완성
                psmt = con.prepareStatement(query);
                psmt.setString(1, map.get("start").toString());
                psmt.setString(2, map.get("end").toString());
               
                // 쿼리문 실행
                rs = psmt.executeQuery();
               
                while (rs.next()) {
                    // 한 행(게시물 하나)의 데이터를 DTO에 저장
                    BoardDTO dto = new BoardDTO();
                    dto.setNum(rs.getString("num"));
                    dto.setTitle(rs.getString("title"));
                    dto.setContent(rs.getString("content"));
                    dto.setPostdate(rs.getDate("postdate"));
                    dto.setId(rs.getString("id"));
                    dto.setVisitcount(rs.getString("visitcount"));

                    // 반환할 결과 목록에 게시물 추가
                    bbs.add(dto);
                }
            }
            catch (Exception e) {
                System.out.println("게시물 조회 중 예외 발생");
                e.printStackTrace();
            }
           
            // 목록 반환
            return bbs;
        }

        // 게시글 데이터를 받아 DB에 추가합니다.
        public int insertWrite(BoardDTO dto) {
            int result = 0;
           
            try {
                // INSERT 쿼리문 작성
                String query = "INSERT INTO board ( "
                             + " num,title,content,id,visitcount) "
                             + " VALUES ( "
                             + " seq_board_num.NEXTVAL, ?, ?, ?, 0)";  

                psmt = con.prepareStatement(query);  // 동적 쿼리
                psmt.setString(1, dto.getTitle());  
                psmt.setString(2, dto.getContent());
                psmt.setString(3, dto.getId());  
               
                result = psmt.executeUpdate();
            }
            catch (Exception e) {
                System.out.println("게시물 입력 중 예외 발생");
                e.printStackTrace();
            }
           
            return result;
        }


        // 지정한 게시물을 찾아 내용을 반환합니다.
        public BoardDTO selectView(String num) {
            BoardDTO dto = new BoardDTO();
           
            // 쿼리문 준비
            String query = "SELECT B.*, M.name "
                         + " FROM member M INNER JOIN board B "
                         + " ON M.id=B.id "
                         + " WHERE num=?";

            try {
                psmt = con.prepareStatement(query);
                psmt.setString(1, num);    // 인파라미터를 일련번호로 설정
                rs = psmt.executeQuery();  // 쿼리 실행

                // 결과 처리
                if (rs.next()) {
                    dto.setNum(rs.getString(1));
                    dto.setTitle(rs.getString(2));
                    dto.setContent(rs.getString("content"));
                    dto.setPostdate(rs.getDate("postdate"));
                    dto.setId(rs.getString("id"));
                    dto.setVisitcount(rs.getString(6));
                    dto.setName(rs.getString("name"));
                }
            }
            catch (Exception e) {
                System.out.println("게시물 상세보기 중 예외 발생");
                e.printStackTrace();
            }
           
            return dto;
        }

        // 지정한 게시물의 조회수를 1 증가시킵니다.
        public void updateVisitCount(String num) {
            // 쿼리문 준비
            String query = "UPDATE board SET "
                         + " visitcount=visitcount+1 "
                         + " WHERE num=?";
           
            try {
                psmt = con.prepareStatement(query);
                psmt.setString(1, num);  // 인파라미터를 일련번호로 설정
                psmt.executeQuery();     // 쿼리 실행
            }
            catch (Exception e) {
                System.out.println("게시물 조회수 증가 중 예외 발생");
                e.printStackTrace();
            }
        }
       
        // 지정한 게시물을 수정합니다.
        public int updateEdit(BoardDTO dto) {
            int result = 0;
           
            try {
                // 쿼리문 템플릿
                String query = "UPDATE board SET "
                             + " title=?, content=? "
                             + " WHERE num=?";
               
                // 쿼리문 완성
                psmt = con.prepareStatement(query);
                psmt.setString(1, dto.getTitle());
                psmt.setString(2, dto.getContent());
                psmt.setString(3, dto.getNum());
               
                // 쿼리문 실행
                result = psmt.executeUpdate();
            }
            catch (Exception e) {
                System.out.println("게시물 수정 중 예외 발생");
                e.printStackTrace();
            }
           
            return result; // 결과 반환
        }

        // 지정한 게시물을 삭제합니다.
        public int deletePost(BoardDTO dto) {
            int result = 0;

            try {
                // 쿼리문 템플릿
                String query = "DELETE FROM board WHERE num=?";

                // 쿼리문 완성
                psmt = con.prepareStatement(query);
                psmt.setString(1, dto.getNum());

                // 쿼리문 실행
                result = psmt.executeUpdate();
            }
            catch (Exception e) {
                System.out.println("게시물 삭제 중 예외 발생");
                e.printStackTrace();
            }
           
            return result; // 결과 반환
        }
}

 

List.jsp

<%@page import="model.BoardDAO"%>
<%@page import="model.BoardDTO"%>
<%@ page import="java.util.List"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ page import="model.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
// DAO를 생성해 DB에 연결
BoardDAO dao = new BoardDAO(application);

// 사용자가 입력한 검색 조건을 Map에 저장
Map<String, Object> param = new HashMap<String, Object>();
String searchField = request.getParameter("searchField");
String searchWord = request.getParameter("searchWord");
if (searchWord != null) {
    param.put("searchField", searchField);
    param.put("searchWord", searchWord);
}

int totalCount = dao.selectCount(param);  // 게시물 수 확인
List<BoardDTO> boardLists = dao.selectList(param);  // 게시물 목록 받기
dao.close();  // DB 연결 닫기
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원제 게시판</title>
</head>
<body>
    <jsp:include page="Link.jsp" />  <!-- 공통 링크 -->

    <h2>목록 보기(List)</h2>
    <!-- 검색폼 -->
    <form method="get">  
    <table border="1" width="90%">
    <tr>
        <td align="center">
            <select name="searchField">
                <option value="title">제목</option>
                <option value="content">내용</option>
            </select>
            <input type="text" name="searchWord" />
            <input type="submit" value="검색하기" />
        </td>
    </tr>  
    </table>
    </form>
    <!-- 게시물 목록 테이블(표) -->
    <table border="1" width="90%">
        <!-- 각 칼럼의 이름 -->
        <tr>
            <th width="10%">번호</th>
            <th width="50%">제목</th>
            <th width="15%">작성자</th>
            <th width="10%">조회수</th>
            <th width="15%">작성일</th>
        </tr>
        <!-- 목록의 내용 -->
<%
if (boardLists.isEmpty()) {
    // 게시물이 하나도 없을 때
%>
        <tr>
            <td colspan="5" align="center">
                등록된 게시물이 없습니다^^*
            </td>
        </tr>
<%
}
else {
    // 게시물이 있을 때
    int virtualNum = 0;  // 화면상에서의 게시물 번호
    for (BoardDTO dto : boardLists)
    {
        virtualNum = totalCount--;  // 전체 게시물 수에서 시작해 1씩 감소
%>
        <tr align="center">
            <td><%= virtualNum %></td>  <!--게시물 번호-->
            <td align="left">  <!--제목(+ 하이퍼링크)-->
                <a href="View.jsp?num=<%= dto.getNum() %>"><%= dto.getTitle() %></a>
            </td>
            <td align="center"><%= dto.getId() %></td>          <!--작성자 아이디-->
            <td align="center"><%= dto.getVisitcount() %></td>  <!--조회수-->
            <td align="center"><%= dto.getPostdate() %></td>    <!--작성일-->
        </tr>
<%
    }
}
%>
    </table>
    <!--목록 하단의 [글쓰기] 버튼-->
    <table border="1" width="90%">
        <tr align="right">
            <td><button type="button" onclick="location.href='Write.jsp';">글쓰기
                </button></td>
        </tr>
    </table>
</body>
</html>

 

IsLoggedin.jsp

<%@ page import="utils.JSFunction"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
if (session.getAttribute("UserId") == null) {
    JSFunction.alertLocation("로그인 후 이용해주십시오.",
                             "../06Session/LoginForm.jsp", out);
    return;
}
%>

 

Write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="./IsLoggedIn.jsp"%> <!--로그인 확인-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원제 게시판</title>
<script type="text/javascript">
function validateForm(form) {  // 폼 내용 검증
    if (form.title.value == "") {
        alert("제목을 입력하세요.");
        form.title.focus();
        return false;
    }
    if (form.content.value == "") {
        alert("내용을 입력하세요.");
        form.content.focus();
        return false;
    }
}
</script>
</head>
<body>
<jsp:include page="Link.jsp" />
<h2>회원제 게시판 - 글쓰기(Write)</h2>
<form name="writeFrm" method="post" action="WriteProcess.jsp"
      onsubmit="return validateForm(this);">
    <table border="1" width="90%">
        <tr>
            <td>제목</td>
            <td>
                <input type="text" name="title" style="width: 90%;" />
            </td>
        </tr>
        <tr>
            <td>내용</td>
            <td>
                <textarea name="content" style="width: 90%; height: 100px;"></textarea>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <button type="submit">작성 완료</button>
                <button type="reset">다시 입력</button>
                <button type="button" onclick="location.href='List.jsp';">
                    목록 보기</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

 

WriteProcess.jsp

<%@ page import="model.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="./IsLoggedIn.jsp"%>
<%
// 폼값 받기
String title = request.getParameter("title");
String content = request.getParameter("content");

// 폼값을 DTO 객체에 저장
BoardDTO dto = new BoardDTO();
dto.setTitle(title);
dto.setContent(content);
dto.setId(session.getAttribute("UserId").toString());

// DAO 객체를 통해 DB에 DTO 저장
BoardDAO dao = new BoardDAO(application);
int iResult = dao.insertWrite(dto);
dao.close();

// 성공 or 실패?
if (iResult == 1) {
    response.sendRedirect("List.jsp");
} else {
    JSFunction.alertBack("글쓰기에 실패하였습니다.", out);
}
%>

 

Veiw.jsp

<%@page import="model.BoardDTO"%>
<%@page import="model.BoardDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String num = request.getParameter("num");  // 일련번호 받기

BoardDAO dao = new BoardDAO(application);  // DAO 생성
dao.updateVisitCount(num);                 // 조회수 증가
BoardDTO dto = dao.selectView(num);        // 게시물 가져오기
dao.close();                               // DB 연결 해제
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원제 게시판</title>
<script>
function deletePost() {
    var confirmed = confirm("정말로 삭제하겠습니까?");
    if (confirmed) {
        var form = document.writeFrm;       // 이름(name)이 "writeFrm"인 폼 선택
        form.method = "post";               // 전송 방식
        form.action = "DeleteProcess.jsp";  // 전송 경로
        form.submit();                      // 폼값 전송
    }
}
</script>
</head>
<body>
<jsp:include page="../Common/Link.jsp" />
<h2>회원제 게시판 - 상세 보기(View)</h2>
<form name="writeFrm">
    <input type="hidden" name="num" value="<%= num %>" />  <!-- 공통 링크 -->

    <table border="1" width="90%">
        <tr>
            <td>번호</td>
            <td><%= dto.getNum() %></td>
            <td>작성자</td>
            <td><%= dto.getName() %></td>
        </tr>
        <tr>
            <td>작성일</td>
            <td><%= dto.getPostdate() %></td>
            <td>조회수</td>
            <td><%= dto.getVisitcount() %></td>
        </tr>
        <tr>
            <td>제목</td>
            <td colspan="3"><%= dto.getTitle() %></td>
        </tr>
        <tr>
            <td>내용</td>
            <td colspan="3" height="100">
                <%= dto.getContent().replace("\r\n", "<br/>") %></td>
        </tr>
        <tr>
            <td colspan="4" align="center">
            <%
            if (session.getAttribute("UserId") != null
                && session.getAttribute("UserId").toString().equals(dto.getId())) {
            %>
                <button type="button"
                        onclick="location.href='Edit.jsp?num=<%= dto.getNum() %>';">
                    수정하기</button>
                <button type="button" onclick="deletePost();">삭제하기</button>
            <%
            }
            %>
                <button type="button" onclick="location.href='List.jsp';">
                    목록 보기
                </button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

 

Edit.jsp

<%@ page import="model1.board.BoardDAO"%>
<%@ page import="model1.board.BoardDTO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="./IsLoggedIn.jsp"%>
<%
String num = request.getParameter("num");  // 일련번호 받기
BoardDAO dao = new BoardDAO(application);  // DAO 생성
BoardDTO dto = dao.selectView(num);        // 게시물 가져오기
String sessionId = session.getAttribute("UserId").toString(); // 로그인 ID 얻기
if (!sessionId.equals(dto.getId())) {      // 본인인지 확인
    JSFunction.alertBack("작성자 본인만 수정할 수 있습니다.", out);
    return;
}
dao.close();  // DB 연결 해제
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원제 게시판</title>
<script type="text/javascript">
function validateForm(form) {  // 폼 내용 검증
    if (form.title.value == "") {
        alert("제목을 입력하세요.");
        form.title.focus();
        return false;
    }
    if (form.content.value == "") {
        alert("내용을 입력하세요.");
        form.content.focus();
        return false;
    }
}
</script>
</head>
<body>
<jsp:include page="../Common/Link.jsp" />
<h2>회원제 게시판 - 수정하기(Edit)</h2>
<form name="writeFrm" method="post" action="EditProcess.jsp"
      onsubmit="return validateForm(this);">
    <input type="hidden" name="num" value="<%= dto.getNum() %>" />
    <table border="1" width="90%">
        <tr>
            <td>제목</td>
            <td>
                <input type="text" name="title" style="width: 90%;"
                       value="<%= dto.getTitle() %>"/>
            </td>
        </tr>
        <tr>
            <td>내용</td>
            <td>
                <textarea name="content" style="width: 90%; height: 100px;"><%= dto.getContent() %></textarea>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <button type="submit">작성 완료</button>
                <button type="reset">다시 입력</button>
                <button type="button" onclick="location.href='List.jsp';">
                    목록 보기</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

 

EditProcessjsp

<%@page import="model.BoardDAO"%>
<%@page import="model.BoardDTO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ include file="./IsLoggedIn.jsp"%>
<%
// 수정 내용 얻기
String num = request.getParameter("num");
String title = request.getParameter("title");
String content = request.getParameter("content");

// DTO에 저장
BoardDTO dto = new BoardDTO();
dto.setNum(num);
dto.setTitle(title);
dto.setContent(content);

// DB에 반영
BoardDAO dao = new BoardDAO(application);
int affected = dao.updateEdit(dto);
dao.close();

// 성공/실패 처리
if (affected == 1) {
    // 성공 시 상세 보기 페이지로 이동
    response.sendRedirect("View.jsp?num=" + dto.getNum());
}
else {
    // 실패 시 이전 페이지로 이동
    JSFunction.alertBack("수정하기에 실패하였습니다.", out);
}
%>

 

 

 

 

 

 

728x90
반응형