<게시판>
🤦🏻♀️ 게시판 작성
더보기
1. BoardEnrollFormController (URL mapping: /enrollForm.bo)
자유게시판 작성 페이지를 띄우기만 할 용도
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 게임 태그를 작성하기 위해(선택사항) 게임 테이블로부터 전체 게임을 조회해서 request에 담기
ArrayList<Game> list = new BoardService().selectGameList();
request.setAttribute("list", list);
// 자유게시판 작성 페이지를 띄우기만 할 용도
request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);
}
2. boardEnrollForm.jsp 글 작성 폼
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.insertcoin.member.model.vo.Member" %>
<%
String contextPath = request.getContextPath();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<!-- CSS 스타일시트 -->
<link href="resources/css/MemberEnrollForm.css" rel="stylesheet">
<link href="resources/css/MainCss.css" rel="stylesheet">
<link href="resources/css/MainContentCss.css" rel="stylesheet">
<!-- jQuery 라이브러리 연결 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<!-- Bootstrap 프레임워크 연결 -->
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<!-- Popper JS -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<!-------------------------------------- 컨텐츠 영역 -------------------------------------->
<!-- 컨텐츠 영역 -->
<div class="content_container">
<!-- 회원가입 창 -->
<form id="enroll_form" action="<%= contextPath %>/enroll.me" method="post">
<!-- 로고, 이메일, 인증번호, 비밀번호/확인, 닉네임 입력 영역 -->
<div class="enroll_content">
<!-- 회원가입 창 로고 -->
<div style="text-align:center;">
<img src="resources/image/logo/insertcoin_logo.png" alt="insert_coin_logo" id="enroll_logo">
</div>
<!-- 회원가입 글자 -->
<div id="enroll_text"><h3>회원가입</h3><hr></div>
<!-- 로그인 정보 입력 폼 -->
<table align="center" id="enroll_table">
<tr>
<th>이메일</th>
</tr>
<tr>
<th colspan="6">
<input type="email" name="memEmail" maxlength="30" onchange="emailCheck();" required> <br>
</th>
</tr>
<tr>
<td colspan="6">
<div><label id="emailCheckOutput"></label></div>
</td>
</tr>
<tr>
<th colspan="4">
<input type="text" name="certification_num" placeholder="인증번호를 입력하세요." required><br>
<div class="superscript">인증번호는 최대 10분간만 유효합니다.</div>
<td><button class="table_button_cert">요청</button></td>
<td><button class="table_button_cert">확인</button></td>
</th>
</tr>
<tr>
<th colspan="6">비밀번호</th>
</tr>
<tr>
<th colspan="6">
<input type="password" class="pwd" id="pwd1" minlength="8" maxlength="16" required>
</th>
</tr>
<tr>
<th colspan="6">비밀번호 확인</th>
</tr>
<tr>
<th colspan="6">
<input type="password" class="pwd" id="pwd2" minlength="8" maxlength="16" required>
</th>
</tr>
<tr>
<th colspan="6">
<div><label id="pwdCheckOutput"></label></div>
</th>
</tr>
<tr>
<th colspan="6">닉네임</th>
</tr>
<tr>
<th colspan="6">
<input type="text" name="memNickname" id="memNickname" minlength="2" maxlength="10" onchange="nicknameCheck();" required>
</th>
</tr>
<tr>
<td colspan="6">
<div><label id="nicknameCheckOutput"></label></div>
</td>
</tr>
<tr>
<!-- 가입하기 버튼 버튼 -->
<th colspan="6"><button type="submit" id="enroll_button">가입하기</button></th>
</tr>
</table>
</div>
</form>
</div>
<%@ include file = "../../views/common/footer.jsp" %>
<script>
// 아이디 중복 체크용 제이쿼리
function emailCheck() {
var $memEmail = $("#enroll_table input[name=memEmail]");
$.ajax({
url : "emailCheck.me",
data : {checkEmail : $memEmail.val()},
success : function(result) {
// result 값은 "possible" 또는 "impossible"
if(result == "impossible") {
$("#emailCheckOutput").text("이미 존재하는 아이디이므로 사용할 수 없습니다.").css("color","red").css("font-size","12px");
$memEmail.val("");
$memEmail.focus();
} else {
$("#emailCheckOutput").text("사용 가능한 아이디입니다!").css("color","rgb(232, 183, 34)").css("font-size","12px");
}
},
error : function () {
console.log("이메일 중복 체크용 ajax 통신 실패!");
}
});
}
// 닉네임 중복 체크용 제이쿼리
function nicknameCheck() {
var $memNickname = $("#enroll_table input[name=memNickname]");
let nickname = $("#memNickname").val();
let regNickname = /^[a-zA-Z0-9ㄱ-ㅎ가-힣]{2,10}$/;
$.ajax({
url : "nicknameCheck.me",
data : {checkNickname : $memNickname.val()},
success : function(result) {
// result 값은 "possible" 또는 "impossible"
if(result == "impossible") {
$("#nicknameCheckOutput").text("이미 존재하는 닉네임이므로 사용할 수 없습니다.").css("color","red").css("font-size","12px");
$memNickname.val("");
$memNickname.focus();
} else {
if(regNickname.test(nickname) == false) {
$("#nicknameCheckOutput").text("닉네임은 2~10자로 한글/영문자/숫자/특수문자()가 포함될 수 있습니다.").css("color","red").css("font-size","12px");
} else {
$("#nicknameCheckOutput").text("사용 가능한 닉네임입니다!").css("color","rgb(232, 183, 34)").css("font-size","12px");
}
}
},
error : function () {
console.log("닉네임 중복 체크용 ajax 통신 실패!");
}
});
}
// 비밀번호 확인용 제이쿼리
$('.pwd').focusout(function() {
let pwd1 = $("#pwd1").val();
let pwd2 = $("#pwd2").val();
let regPwd = /^[a-zA-Z0-9]{8,20}$/;
if(pwd1 != "" && pwd2 != "") { // 사용자가 비밀번호, 비밀번호 확인을 모두 입력했다면
if(pwd1 != pwd2) {
$("#pwdCheckOutput").text("비밀번호가 일치하지 않습니다. 다시 확인해 주세요.").css("color","red").css("font-size","12px");
} else {
if(regPwd.test(pwd2) == false) {
$("#pwdCheckOutput").text("비밀번호는 8~20자로 숫자/영어 대,소문자/특수문자()가 포함될 수 있습니다.").css("color","red").css("font-size","12px");
} else {
$("#pwdCheckOutput").text("비밀번호가 일치합니다!").css("color","rgb(232, 183, 34)").css("font-size","12px");
}
}
}
});
</script>
</body>
</html>
3. GAME, ATTACHMENT 클래스 생성
package com.insertcoin.game.model.vo;
import java.sql.Date;
public class Game {
// 필드부
private int gameNo; // GAME_NO NUMBER
private String gameName; // GAME_NAME VARCHAR2(100 BYTE)
private int memNo; // MEM_NO NUMBER
private String gameGenre; // GAME_GENRE VARCHAR2(20 BYTE)
private String gameOs; // GAME_OS VARCHAR2(10 BYTE)
private String gameContent; // GAME_CONTENT VARCHAR2(4000 BYTE)
private int gameDonateDefault; // GAME_DONATE_DEFAULT NUMBER
private String gameFreeDownload; // GAME_FREE_DOWNLOAD CHAR(1 BYTE)
private Date gameRegisterDate; // GAME_REGISTER_DATE DATE
private String gameShow; // GAME_SHOW CHAR(1 BYTE)
private String gamePageColor; // GAME_PAGE_COLOR VARCHAR2(10 BYTE)
// 생성자부
public Game() {
super();
}
public Game(int gameNo, String gameName, int memNo, String gameGenre, String gameOs, String gameContent,
int gameDonateDefault, String gameFreeDownload, Date gameRegisterDate, String gameShow,
String gamePageColor) {
super();
this.gameNo = gameNo;
this.gameName = gameName;
this.memNo = memNo;
this.gameGenre = gameGenre;
this.gameOs = gameOs;
this.gameContent = gameContent;
this.gameDonateDefault = gameDonateDefault;
this.gameFreeDownload = gameFreeDownload;
this.gameRegisterDate = gameRegisterDate;
this.gameShow = gameShow;
this.gamePageColor = gamePageColor;
}
// 게시판 게임 태그용 생성자
public Game(int gameNo, String gameName) {
super();
this.gameNo = gameNo;
this.gameName = gameName;
}
// 메소드부
public int getGameNo() {
return gameNo;
}
public void setGameNo(int gameNo) {
this.gameNo = gameNo;
}
public String getGameName() {
return gameName;
}
public void setGameName(String gameName) {
this.gameName = gameName;
}
public int getMemNo() {
return memNo;
}
public void setMemNo(int memNo) {
this.memNo = memNo;
}
public String getGameGenre() {
return gameGenre;
}
public void setGameGenre(String gameGenre) {
this.gameGenre = gameGenre;
}
public String getGameOs() {
return gameOs;
}
public void setGameOs(String gameOs) {
this.gameOs = gameOs;
}
public String getGameContent() {
return gameContent;
}
public void setGameContent(String gameContent) {
this.gameContent = gameContent;
}
public int getGameDonateDefault() {
return gameDonateDefault;
}
public void setGameDonateDefault(int gameDonateDefault) {
this.gameDonateDefault = gameDonateDefault;
}
public String getGameFreeDownload() {
return gameFreeDownload;
}
public void setGameFreeDownload(String gameFreeDownload) {
this.gameFreeDownload = gameFreeDownload;
}
public Date getGameRegisterDate() {
return gameRegisterDate;
}
public void setGameRegisterDate(Date gameRegisterDate) {
this.gameRegisterDate = gameRegisterDate;
}
public String getGameShow() {
return gameShow;
}
public void setGameShow(String gameShow) {
this.gameShow = gameShow;
}
public String getGamePageColor() {
return gamePageColor;
}
public void setGamePageColor(String gamePageColor) {
this.gamePageColor = gamePageColor;
}
@Override
public String toString() {
return "Game [gameNo=" + gameNo + ", gameName=" + gameName + ", memNo=" + memNo + ", gameGenre=" + gameGenre
+ ", gameOs=" + gameOs + ", gameContent=" + gameContent + ", gameDonateDefault=" + gameDonateDefault
+ ", gameFreeDownload=" + gameFreeDownload + ", gameRegisterDate=" + gameRegisterDate + ", gameShow="
+ gameShow + ", gamePageColor=" + gamePageColor + "]";
}
}
package com.insertcoin.common.model.vo;
import java.sql.Date;
public class Attachment {
// 필드부
private int attachmentNo; // 파일번호
private String attachmentPath; // 파일경로
private String attachmentName; // 원본 파일명
private String attachmentRename; // 수정 파일명
private Date attachmentUploadDate; // 업로드일
private String attachmentStatus; // 삭제유무
private int memNo; // 참조 회원번호
private int gameNo; // 참조 게임등록번호
private int reviewNo; // 참조 리뷰등록번호
private int genNo; // 참조 일반게시판 게시글번호
private int genCommentNo; // 참조 일반게시판 댓글번호
private int devNo; // 참조 개발자게시판 게시글번호
private int devCommentNo; // 참조 개발자게시판 댓글번호
private int noticeNo; // 참조 공지사항번호
// 생성자부
public Attachment() {
super();
}
public Attachment(int attachmentNo, String attachmentPath, String attachmentName, String attachmentRename,
Date attachmentUploadDate, String attachmentStatus, int memNo, int gameNo, int reviewNo, int genNo,
int genCommentNo, int devNo, int devCommentNo, int noticeNo) {
super();
this.attachmentNo = attachmentNo;
this.attachmentPath = attachmentPath;
this.attachmentName = attachmentName;
this.attachmentRename = attachmentRename;
this.attachmentUploadDate = attachmentUploadDate;
this.attachmentStatus = attachmentStatus;
this.memNo = memNo;
this.gameNo = gameNo;
this.reviewNo = reviewNo;
this.genNo = genNo;
this.genCommentNo = genCommentNo;
this.devNo = devNo;
this.devCommentNo = devCommentNo;
this.noticeNo = noticeNo;
}
// 메소드부
public int getAttachmentNo() {
return attachmentNo;
}
public void setAttachmentNo(int attachmentNo) {
this.attachmentNo = attachmentNo;
}
public String getAttachmentPath() {
return attachmentPath;
}
public void setAttachmentPath(String attachmentPath) {
this.attachmentPath = attachmentPath;
}
public String getAttachmentName() {
return attachmentName;
}
public void setAttachmentName(String attachmentName) {
this.attachmentName = attachmentName;
}
public String getAttachmentRename() {
return attachmentRename;
}
public void setAttachmentRename(String attachmentRename) {
this.attachmentRename = attachmentRename;
}
public Date getAttachmentUploadDate() {
return attachmentUploadDate;
}
public void setAttachmentUploadDate(Date attachmentUploadDate) {
this.attachmentUploadDate = attachmentUploadDate;
}
public String getAttachmentStatus() {
return attachmentStatus;
}
public void setAttachmentStatus(String attachmentStatus) {
this.attachmentStatus = attachmentStatus;
}
public int getMemNo() {
return memNo;
}
public void setMemNo(int memNo) {
this.memNo = memNo;
}
public int getGameNo() {
return gameNo;
}
public void setGameNo(int gameNo) {
this.gameNo = gameNo;
}
public int getReviewNo() {
return reviewNo;
}
public void setReviewNo(int reviewNo) {
this.reviewNo = reviewNo;
}
public int getGenNo() {
return genNo;
}
public void setGenNo(int genNo) {
this.genNo = genNo;
}
public int getGenCommentNo() {
return genCommentNo;
}
public void setGenCommentNo(int genCommentNo) {
this.genCommentNo = genCommentNo;
}
public int getDevNo() {
return devNo;
}
public void setDevNo(int devNo) {
this.devNo = devNo;
}
public int getDevCommentNo() {
return devCommentNo;
}
public void setDevCommentNo(int devCommentNo) {
this.devCommentNo = devCommentNo;
}
public int getNoticeNo() {
return noticeNo;
}
public void setNoticeNo(int noticeNo) {
this.noticeNo = noticeNo;
}
@Override
public String toString() {
return "Attachment [attachmentNo=" + attachmentNo + ", attachmentPath=" + attachmentPath + ", attachmentName="
+ attachmentName + ", attachmentRename=" + attachmentRename + ", attachmentUploadDate="
+ attachmentUploadDate + ", attachmentStatus=" + attachmentStatus + ", memNo=" + memNo + ", gameNo="
+ gameNo + ", reviewNo=" + reviewNo + ", genNo=" + genNo + ", genCommentNo=" + genCommentNo + ", devNo="
+ devNo + ", devCommentNo=" + devCommentNo + ", noticeNo=" + noticeNo + "]";
}
}
4. BoardService
public ArrayList<Game> selectGameList() {
Connection conn = getConnection();
ArrayList<Game> list = new BoardDao().selectGameList(conn);
close(conn);
return list;
}
public int insertBoard(Board b, Attachment at) {
Connection conn = getConnection();
// Dao의 메소드 1개 = 쿼리문 1개
// 첨부파일이 있든 없든 간에 무조건 일어나야 하는 BOARD 테이블 INSERT 요청 먼저
int result1 = new BoardDao().insertBoard(conn, b); // 성공 1, 실패 0
// 한 개의 트랜잭션에 테이블의 변동이 있는 DML이 두 번 실행
// 두 번의 DML 중 하나라도 실패한다면 전부 rollback / 두 번 모두 성공해야 commit
// 만약 첨부파일이 있다면 Attachment 테이블에 insert 요청 보내기
// 두 번째 요청에 대한 결과값을 담을 수 있는 변수 세팅
// 기본값 0으로 설정하면 첨부파일 없을 때는 무조건 실패이니 기본값 1
int result2 = 1;
if(at != null) { // 첨부파일이 있다면
result2 = new BoardDao().insertAttachment(conn, at);
}
// 트랜잭션 처리는 result1 > 0 %% result2 > 0 일 때만 커밋
if(result1 > 0 && result2 > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
// 하나라도 실패해서 0이 나오면 실패여야 하기 때문에 곱셈 결과로 보냄
return result1 * result2;
}
5. BoardDao
public ArrayList<Game> selectGameList(Connection conn) {
// SELECT문 => ResultSet 객체 (여러 행 조회)
ArrayList<Game> list = new ArrayList<>();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectGameList");
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
while(rset.next()) {
list.add(new Game(rset.getInt("GAME_NO"),
rset.getString("GAME_NAME")));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
public int insertBoard(Connection conn, Board b) {
// insert문 => int (처리된 행 개수)
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertBoard");
try {
pstmt = conn.prepareStatement(sql);
// GEN_CATEGORY / GAME_NO / MEM_NO / GEN_TITLE / GEN_CONTENT
// genCategory / gemeNo / memNo / genTitle / genContent
pstmt.setString(1, b.getGenCategory());
// pstmt.setInt(2, b.getGameNo());
pstmt.setInt(2, Integer.parseInt(b.getMemNo()));
pstmt.setString(3, b.getGenTitle());
pstmt.setString(4, b.getGenContent());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
public int insertAttachment(Connection conn, Attachment at) {
// INSERT문 => int 처리된 행의 개수
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertAttachment");
try {
pstmt = conn.prepareStatement(sql);
// private String attachmentName; // 원본 파일명
// private String attachmentRename; // 수정 파일명
// private String attachmentPath; // 파일경로
pstmt.setString(1, at.getAttachmentName());
pstmt.setString(2, at.getAttachmentRename());
pstmt.setString(3, at.getAttachmentPath());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
System.out.println(result);
return result;
}
6. board-mapper.xml
<entry key="selectGameList">
SELECT *
FROM GAME
</entry>
<entry key="insertBoard">
INSERT INTO GEN_BOARD (GEN_NO
, GEN_CATEGORY
, MEM_NO
, GEN_TITLE
, GEN_CONTENT
, GEN_REGISTER_DATE)
VALUES (SEQ_GEN_NO.NEXTVAL
, ?
, ?
, ?
, ?
, SYSDATE)
</entry>
<entry key="insertAttachment">
INSERT INTO ATTACHMENT (ATTACHMENT_NO
, GEN_NO
, ATTACHMENT_NAME
, ATTACHMENT_RENAME
, ATTACHMENT_PATH)
VALUES (SEQ_ATTACHMENT_NO.NEXTVAL
, SEQ_GEN_NO.CURRVAL
, ?
, ?
, ?)
</entry>
7. BoardInsertController (URL mapping: /insert.bo)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 일반 게시글 추가 기능
// 1. Board 테이블에 insert
// 2. 만약 첨부파일이 있다면 Attachment 테이블도 insert
// 인코딩 설정
request.setCharacterEncoding("UTF-8");
// memNo: 작성자 번호
// genCategory: 카테고리 번호(현재 String으로 받아 왔음)
// gameNo: 게임 번호
// genTitle: 게시글 제목
// genContent: 게시글 내용
// upfile: 첨부파일
// multipart/form-data 형식인지 검사
if(ServletFileUpload.isMultipartContent(request)) {
// 전송된 파일을 처리할 작업 내용
// 용량 제한
int maxSize = 10 * 1024 * 1024;
// 전달된 파일을 저장할 서버의 실 경로
String savePath = request.getSession().getServletContext().getRealPath("/resources/image/board/gen/post/");
// 전달된 파일명 수정 및 서버에 업로드
MultipartRequest multiRequest = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
// DB에 기록할 데이터를 뽑아서 VO 객체에 담기
String memNo = multiRequest.getParameter("memNo");
String genCategory = multiRequest.getParameter("genCategory");
// String gameNo = multiRequest.getParameter("gameNo");
String genTitle = multiRequest.getParameter("genTitle");
String genContent = multiRequest.getParameter("genContent");
Board b = new Board();
b.setMemNo(memNo);
b.setGenCategory(genCategory);
// b.setGameNo(null);
b.setGenTitle(genTitle);
b.setGenContent(genContent);
// 만약 첨부파일이 있다면 첨부파일에 대한 정보도 뽑아서 VO 객체에 담기
Attachment at = null;
if(multiRequest.getOriginalFileName("upfile") != null) {
at = new Attachment();
at.setAttachmentName(multiRequest.getOriginalFileName("upfile")); // 원본명
at.setAttachmentRename(multiRequest.getFilesystemName("upfile")); // 수정명, 실제 서버에 업로드된 파일명
at.setAttachmentPath("resources/image/board/gen/post/");
}
// 이 시점 기준으로 첨부파일이 없다면 at == null
// 서비스 요청
int result = new BoardService().insertBoard(b, at);
// 결과에 따른 응답 페이지 지정
if(result > 0) { // 성공 /jsp/list.bo?currentPage=1 요청 (가장 최신글)
request.getSession().setAttribute("alertMsg", "게시글이 성공적으로 작성되었습니다.");
response.sendRedirect(request.getContextPath() + "/list.bo?currentPage=1");
} else { // 실패
// 첨부파일이 있었을 경우 이미 업로드된 첨부파일을 굳이 서버에 보관할 필요가 없음
if(at != null) {
new File(savePath + at.getAttachmentRename()).delete();
}
request.setAttribute("errorMsg", "게시글 작성 실패");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
}
}
8. MyFileRenamePolicy
package com.insertcoin.common;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.oreilly.servlet.multipart.FileRenamePolicy;
public class MyFileRenamePolicy implements FileRenamePolicy {
// 기존 파일 전달받아 파일명 수정 작업 후 수정된 파일 자체를 return
@Override
public File rename(File originFile) {
// 원본 파일명
String originName = originFile.getName();
// 수정 파일명
// gen_post__yyyymmdd_nnnnn
// 파일 업로드 시간
String text = "gen_post_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
// 5자리 랜덤값
int ranNum = (int)(Math.random() * 90000) + 10000;
// 원본파일 확장자
String ext = originName.substring(originName.lastIndexOf("."));
// 결합
String changeName = text + ranNum + ext;
// 수정된 파일명으로 원본파일명 적용 후 파일 객체로 변환
return new File(originFile.getParent(), changeName);
}
}
🤦🏻♀️ 게시판 상세조회
더보기
1. boardDetailView.jsp (하드코딩)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>자유게시판 상세 게시글</title>
<!-- CSS 스타일시트 -->
<link href="resources/css/boardDetailView.css" rel="stylesheet">
</head>
<body>
<!-- 전체 영역 -->
<div class="wrap">
<%@ include file = "../../views/common/header.jsp" %>
<!-- 컨텐츠 영역 -->
<div class="content_container">
<div class="outer">
<div id="title">자유게시판</div>
<!-- 조회할 게시물이 보여질 자리 -->
<table align="center" class="list-area">
<thead>
<tr id="board_title" class="line">
<th width="1000">
<div class="board_left">[카테고리명] 글제목</div>
</th>
<td>
<div class="board_right">2022.11.01</div>
</td>
</tr>
</thead>
<tbody>
<tr class="line">
<td>
<div class="board_left">user01</div>
</td>
<td>
<div class="board_right">조회수 26 | 신고 0</div>
</td>
</tr>
<tr class="line">
<td colspan="3">
<div id="board_text">
안녕하세요 제가 이걸 만들 수 있을까요? 글의 길이에 따라 보여지는 창이 달라진다라...
테스트 중입니다 얼마나 길어져야 이게 자기 마음대로 엥 쓰다보니까 알아서 길이가 달라지는 것 같은데 착각일까요?
아아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ방금 또 한 줄이 자기 마음대로 넘어갔네요<br><br>
br을 먹여 봐도 넘어갑니다 신기방기
</div>
</td>
</tr>
<tr>
<td colspan="3">
<div>
<button class="table_button">수정</button>
<button class="table_button">삭제</button>
<button class="table_button">목록</button>
<button type="button" id="red_board" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
<!--------------------------------------------- 신고 모달창 ------------------------------------------------>
<!-- The Modal -->
<div class="modal fade" id="myReport">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h5 class="modal-title">게시글 신고</h5>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<b>신고사유</b><br>
<input type="checkbox" id="1" value="1"><label for="1"> 스팸</label><br>
<input type="checkbox" id="2" value="2"><label for="2"> 음란성</label><br>
<input type="checkbox" id="3" value="3"><label for="3"> 증오심 표현</label><br>
<input type="checkbox" id="4" value="4"><label for="4"> 잘못된 정보</label><br>
<input type="checkbox" id="5" value="5"><label for="5"> 기타</label><br>
<input type="text" placeholder="내용을 입력해 주세요" style="width:300px;">
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button class="modal_button button1" type="reset">취소</button>
<button class="modal_button button2" type="submit">신청</button>
</div>
</div>
</div>
</div>
<!--------------------------------------------- 신고 모달창 ------------------------------------------------>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div id="comment_count">댓글 2개</div>
</td>
</tr>
</tbody>
</table>
<table class="comment_table">
<tr>
<td rowspan="2" width="5%">
<img src="resources/image/profile/profile_default.png" id="profile_photo">
</td>
<td>
<div class="comment_info1">
<div>내닉넴짱짱</div>
</div>
</td>
<td>
<div class="comment_info2">
<div>2022.10.23 12:02</div>
</div>
</td>
<td>
<button id="comment_info3" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
</td>
</tr>
<tr class="line">
<td>어디보자... 점점 구색을 갖추는듯하나 아직 고칠 게 많아 보입니다 ^^</td>
</tr>
<tr>
<td rowspan="2" width="5%">
<img src="resources/image/profile/profile_default.png" id="profile_photo">
</td>
<td>
<div class="comment_info1">
<div>하지만만약내닉네임이길어진다면</div>
</div>
</td>
<td>
<div class="comment_info2">
<div>2022.10.23 12:28</div>
</div>
</td>
<td>
<button id="comment_info3" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
</td>
</tr>
<tr class="line">
<td colspan="3">제가 볼때는 별로같은데요... 일단 이 작업을 하고 나서 글씨 크기부터 좀 줄여야겠어요 전혀 분간이 안되네요 가독성 구려요 그리고 댓글이 더 길어지면 어떻게 되는지도 봐야겠어요</td>
</tr>
<tr>
<td colspan="4">
<div class=comment_container> 댓글쓰기 <br>
<textarea class="comment_textarea" placeholder="명예훼손, 개인정보 유출, 분쟁 유발, 허위사실 유포 등의 글은 이용약관에 의해 제재는 물론 법률에 의해 처벌받을 수 있습니다. 건전한 커뮤니티를 위해 자제를 당부드립니다."></textarea>
<button id="comment_submit">등록</button>
</div>
</td>
</tr>
</table>
<!-- 조회할 게시물이 보여질 자리 -->
<table align="center" class="list-area">
<thead>
<tr id="board_title">
<!-- th[width=]*6 + Enter -->
<th width="100">글번호</th>
<th width="100">카테고리</th>
<th width="700">제목</th>
<th width="150">작성자</th>
<th width="70">조회수</th>
<th width="150">작성일</th>
</tr>
</thead>
<tbody>
<tr class="line">
<td>15</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>14</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>13</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>12</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>11</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>10</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>9</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>8</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>7</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>6</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>5</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>4</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>3</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>2</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>1</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
</tbody>
</table>
<br>
<div class="board_search board_left">
<input type="button" value="글쓰기" class="board_button">
</div>
<div class="board_search board-right">
<select class="board_search">
<option selected>제목+내용</option>
<option value="10">제목</option>
<option value="20">내용</option>
<option value="30">작성자</option>
<option value="40">댓글</option>
<option value="50">게임명</option>
</select>
<input type="text">
<button class="board_button">검색</button>
</div>
<br><br>
<!-- 페이징바 -->
<div align="center" class="paging-area">
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>7</button>
<button>8</button>
<button>9</button>
<button>10</button>
</div>
</div>
</div>
</div>
</body>
</body>
</html>
2. boardListView.jsp
<script>
// 게시글에 클릭 버튼 걸기
$(function() {
$(".list-area>tbody>tr").click(function() {
location.href = "<%= contextPath %>/detail.bo?genNo=" + $(this).children().eq(0).text();
});
});
</script>
3. BoardDetailController (URL mapping: /detail.bo)
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 해당 게시글 먼저 뽑기
int genNo = Integer.parseInt(request.getParameter("genNo"));
// 조회수 증가 / 게시글 조회(Board) / 첨부파일 조회(Attachment)
// => 서비스로 요청 3번을 보내야 함
BoardService bService = new BoardService();
// 조회 수 증가 요청
int result = bService.increaseCount(genNo);
if(result > 0) { // 조회수 증가에 성공
// 게시글 조회, 첨부파일 조회
Board b = bService.selectBoard(genNo);
Attachment at = bService.selectAttachment(genNo);
// 게시글 정보 보내기
request.setAttribute("b", b);
request.setAttribute("at", at);
// 게시글 상세 조회 페이지로 포워딩
request.getRequestDispatcher("views/board/boardDetailView.jsp").forward(request, response);
} else { // 조회수 증가에 실패했다면
// 에러 문구 담아서 에러 페이지로 포워딩
// 키값 오타 시 null
request.setAttribute("errorMsg", "게시글 상세 조회 실패");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
}
4. boardDetailView.jsp (동적 코딩)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.insertcoin.board.model.vo.Board, com.insertcoin.common.model.vo.Attachment" %>
<%
// 필요한 데이터 먼저 뽑기
Board b = (Board)request.getAttribute("b");
// 게시글 번호, 카테고리명, 글 제목, 글 내용, 작성자 아이디, 작성일, 신고 수
Attachment at = (Attachment)request.getAttribute("at");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>자유게시판 상세 게시글</title>
<!-- CSS 스타일시트 -->
<link href="resources/css/boardDetailView.css" rel="stylesheet">
</head>
<body>
<!-- 전체 영역 -->
<div class="wrap">
<%@ include file = "../../views/common/header.jsp" %>
<!-- 컨텐츠 영역 -->
<div class="content_container">
<div class="outer">
<div id="title">자유게시판</div>
<!-- 조회할 게시물이 보여질 자리 -->
<table align="center" class="list-area">
<thead>
<tr id="board_title" class="line">
<th width="1000">
<div class="board_left">[<%= b.getGenCategory() %>] <%= b.getGenTitle() %></div>
</th>
<td>
<div class="board_right"><%= b.getGenRegister() %></div>
</td>
</tr>
</thead>
<tbody>
<tr class="line">
<td>
<div class="board_left">작성자: <b><%= b.getMemNo() %></b></div>
</td>
<td>
<div class="board_right">조회수 <%= b.getGenViews() %> | 신고 0</div>
</td>
</tr>
<tr class="line">
<td colspan="3">
<div id="board_text">
<%= b.getGenContent() %>
</div>
</td>
</tr>
<tr class="line">
<th>첨부파일</th>
<td colspan="3">
<% if(at == null) { %>
첨부파일이 없습니다.
<% } else { %>
<a download="<%= at.getAttachmentName() %>" href="<%= contextPath %>/<%= at.getAttachmentPath() + at.getAttachmentRename() %>">
<%= at.getAttachmentName() %>
</a>
<% } %>
</td>
</tr>
<tr>
<td colspan="3">
<div>
<% if(loginUser != null && loginUser.getMemNickname().equals(b.getMemNo())) { %>
<button class="table_button">수정</button>
<button class="table_button">삭제</button>
<button type="button" id="red_board" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
<% } %>
<button class="table_button" onclick="goToList();">목록</button>
<!--------------------------------------------- 신고 모달창 ------------------------------------------------>
<!-- The Modal -->
<div class="modal fade" id="myReport">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h5 class="modal-title">게시글 신고</h5>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<b>신고사유</b><br>
<input type="checkbox" id="1" value="1"><label for="1"> 스팸</label><br>
<input type="checkbox" id="2" value="2"><label for="2"> 음란성</label><br>
<input type="checkbox" id="3" value="3"><label for="3"> 증오심 표현</label><br>
<input type="checkbox" id="4" value="4"><label for="4"> 잘못된 정보</label><br>
<input type="checkbox" id="5" value="5"><label for="5"> 기타</label><br>
<input type="text" placeholder="내용을 입력해 주세요" style="width:300px;">
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button class="modal_button button2" type="submit" data-dismiss="modal" onclick="report();">신청</button>
</div>
</div>
</div>
</div>
<!--------------------------------------------- 신고 모달창 ------------------------------------------------>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div id="comment_count">댓글 2개</div>
</td>
</tr>
</tbody>
</table>
<table class="comment_table">
<tr>
<td rowspan="2" width="5%">
<img src="resources/image/profile/profile_default.png" id="profile_photo">
</td>
<td>
<div class="comment_info1">
<div>내닉넴짱짱</div>
</div>
</td>
<td>
<div class="comment_info2">
<div>2022.10.23 12:02</div>
</div>
</td>
<td>
<button id="comment_info3" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
</td>
</tr>
<tr class="line">
<td>어디보자... 점점 구색을 갖추는듯하나 아직 고칠 게 많아 보입니다 ^^</td>
</tr>
<tr>
<td rowspan="2" width="5%">
<img src="resources/image/profile/profile_default.png" id="profile_photo">
</td>
<td>
<div class="comment_info1">
<div>하지만만약내닉네임이길어진다면</div>
</div>
</td>
<td>
<div class="comment_info2">
<div>2022.10.23 12:28</div>
</div>
</td>
<td>
<button id="comment_info3" class="btn btn-primary" data-toggle="modal" data-target="#myReport">신고</button>
</td>
</tr>
<tr class="line">
<td colspan="3">제가 볼때는 별로같은데요... 일단 이 작업을 하고 나서 글씨 크기부터 좀 줄여야겠어요 전혀 분간이 안되네요 가독성 구려요 그리고 댓글이 더 길어지면 어떻게 되는지도 봐야겠어요</td>
</tr>
<tr>
<td colspan="4">
<div class=comment_container> 댓글쓰기 <br>
<textarea class="comment_textarea" placeholder="명예훼손, 개인정보 유출, 분쟁 유발, 허위사실 유포 등의 글은 이용약관에 의해 제재는 물론 법률에 의해 처벌받을 수 있습니다. 건전한 커뮤니티를 위해 자제를 당부드립니다."></textarea>
<button id="comment_submit">등록</button>
</div>
</td>
</tr>
</table>
<!-- 조회할 게시물이 보여질 자리 -->
<table align="center" class="list-area">
<thead>
<tr id="board_title">
<!-- th[width=]*6 + Enter -->
<th width="100">글번호</th>
<th width="100">카테고리</th>
<th width="700">제목</th>
<th width="150">작성자</th>
<th width="70">조회수</th>
<th width="150">작성일</th>
</tr>
</thead>
<tbody>
<tr class="line">
<td>15</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>14</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>13</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>12</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>11</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>10</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>9</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>8</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>7</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>6</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>5</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>4</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>3</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>2</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
<tr class="line">
<td>1</td>
<td>게임</td>
<td>게시글 제목이다</td>
<td>user02</td>
<td>10</td>
<td>2022-05-01</td>
</tr>
</tbody>
</table>
<br>
<div class="board_search board_left">
<input type="button" value="글쓰기" class="board_button">
</div>
<div class="board_search board-right">
<select class="board_search">
<option selected>제목+내용</option>
<option value="10">제목</option>
<option value="20">내용</option>
<option value="30">작성자</option>
<option value="40">댓글</option>
<option value="50">게임명</option>
</select>
<input type="text">
<button class="board_button">검색</button>
</div>
<br><br>
<!-- 페이징바 -->
<div align="center" class="paging-area">
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>7</button>
<button>8</button>
<button>9</button>
<button>10</button>
</div>
</div>
</div>
</div>
<script>
function goToList() {
location.href = "<%=contextPath %>/list.bo?currentPage=1";
}
function report() {
alert("신고가 성공적으로 접수되었습니다.");
}
</script>
</body>
</body>
</html>
5. BoardService
// 조회수 증가용 서비스
public int increaseCount(int genNo) {
Connection conn = getConnection();
int result = new BoardDao().increaseCount(conn, genNo);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
return result;
}
// Board 정보를 가지고 올 서비스
public Board selectBoard(int genNo) {
Connection conn = getConnection();
Board b = new BoardDao().selectBoard(conn, genNo);
close(conn);
return b;
}
// Attachment 정보를 가지고 올 서비스
public Attachment selectAttachment(int genNo) {
Connection conn = getConnection();
Attachment at = new BoardDao().selectAttachment(conn, genNo);
close(conn);
return at;
}
6. BoardDao
// 조회수 증가용 Dao
public int increaseCount(Connection conn, int genNo) {
// UPDATE문 => int (처리된 행의 개수)
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("increaseCount");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, genNo);
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
// Board 정보용 DAO
public Board selectBoard(Connection conn, int genNo) {
// SELECT문 => ResultSet 객체 (많아도 1행)
Board b = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectBoard");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, genNo);
rset = pstmt.executeQuery();
if(rset.next()) {
b = new Board(rset.getInt("GEN_NO")
, rset.getString("GEN_CATEGORY")
, rset.getString("GAME_NAME") // 일단 게임 태그 제외
, rset.getString("MEM_NICKNAME")
, rset.getString("GEN_TITLE")
, rset.getString("GEN_CONTENT")
, rset.getDate("GEN_REGISTER_DATE")
, rset.getInt("GEN_VIEWS"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return b;
}
public Attachment selectAttachment(Connection conn, int genNo) {
// SELECT문 => ResultSet 객체 (1행 조회)
Attachment at = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectAttachment");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, genNo);
rset = pstmt.executeQuery();
if(rset.next()) {
at = new Attachment();
at.setAttachmentNo(rset.getInt("ATTACHMENT_NO"));
at.setAttachmentName(rset.getString("ATTACHMENT_NAME"));
at.setAttachmentRename(rset.getString("ATTACHMENT_RENAME"));
at.setAttachmentPath(rset.getString("ATTACHMENT_PATH"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return at;
}
7. Board
// 일반게시글 상세 조회용 생성자
public Board(int genNo, String genCategory, String gameNo, String memNo, String genTitle, String genContent,
Date genRegister, int genViews) {
super();
this.genNo = genNo;
this.genCategory = genCategory;
this.gameNo = gameNo;
this.memNo = memNo;
this.genTitle = genTitle;
this.genContent = genContent;
this.genRegister = genRegister;
this.genViews = genViews;
}
7. board-mapper.xml
<entry key="increaseCount">
UPDATE GEN_BOARD
SET GEN_VIEWS = GEN_VIEWS + 1
WHERE GEN_NO = ?
AND GEN_SHOW = 'Y'
</entry>
<entry key="selectBoard">
SELECT GEN_NO
, GEN_CATEGORY
, GAME_NAME
, M.MEM_NICKNAME
, GEN_TITLE
, GEN_CONTENT
, GEN_REGISTER_DATE
, GEN_VIEWS
FROM GEN_BOARD B
LEFT JOIN MEMBER M ON (B.MEM_NO = M.MEM_NO)
LEFT JOIN GAME USING (GAME_NO)
WHERE GEN_NO = ?
AND GEN_SHOW = 'Y'
</entry>
<entry key="selectAttachment">
SELECT ATTACHMENT_NO
, ATTACHMENT_NAME
, ATTACHMENT_RENAME
, ATTACHMENT_PATH
FROM ATTACHMENT
WHERE GEN_NO = ?
AND ATTACHMENT_STATUS = 'N'
</entry>
🤦🏻♀️ 아이디 저장 쿠키
더보기
1. memberLoginForm.jsp
<%
String contextPath = request.getContextPath();
// 쿠키 불러오기
// => request.getCookies() 메소드 => Cookie 타입의 배열로 리턴
Cookie[] cookies = request.getCookies();
// 배열에 담긴 여러 개의 쿠키 세트들 중에 내가 원하는 쿠키만 골라내는 작업 진행
String saveEmail = "";
if(cookies != null) {
for(int i = 0; i < cookies.length; i++) {
// System.out.println(i + " : " + cookies[i].getName() + " / " + cookies[i].getValue());
// 서버는 기본적으로 JSSESIONID라는 쿠키를 만들어 줌
// 쿠키로부터 name(키값)을 뽑아내려면 getName(), value(밸류값)을 뽑아내려면 getValue() 메소드 이용
if(cookies[i].getName().equals("saveEmail")) {
saveEmail = cookies[i].getValue();
break; // 안 걸어도 상관없지만 쿠키가 많은 경우 해당 쿠키를 찾은 이후로는 굳이 계속 반복 돌면서 낭비할 필요가 없음!
}
}
}
// 이 시점 기준으로 "saveId"라는 키값을 가진 쿠키가 있었다면 String 타입으로 saveId라는 변수에 해당 아이디값 자체가 담겨 있을 것!
%>
<tr>
<th id="emailSave">
<input type="checkbox" id="saveEmail" name="saveEmail" value="y">
<label for="saveEmail">이메일 저장</label>
</th>
</tr>
<script>
// 모든 요소들이 화면에 다 로딩된 후 saveEmail이라는 자바 변수에 저장된 값을 불러옴
// 이메일 입력창에 value 속성으로 설정해 둘 것 & 아이디 저장하기 체크박스에 체크 수행
$(function () {
var saveEmail = "<%= saveEmail %>";
if(saveEmail != "") { // 쿠키가 있다면
$("#login_form input[name=memEmail]").val(saveEmail);
$("#saveEmail").attr("checked", true);
}
});
function enrollPage() {
location.href = "<%=contextPath %>/enrollForm.me";
}
</script>
2. LoginController
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 로그인 정보는 POST 방식으로 처리해야 함 => 인코딩 처리
request.setCharacterEncoding("UTF-8");
// 요청 시 전달값을 꺼내기
// memEmail: 아이디 값(이메일)
String memEmail = request.getParameter("memEmail");
// memPwd: 비밀번호 값
String memPwd = request.getParameter("memPwd");
// memNickname: 닉네임값 (성공 메시지 표출 때 보여 주고자 함)
String memNickname = request.getParameter("memNickname");
// 이메일 저장
String saveEmail = request.getParameter("saveEmail"); // 체크를 했다면 "y", 하지 않았다면 null 값이 넘어옴
if(saveEmail != null && saveEmail.equals("y")) {
// 아이디를 저장하겠다
// => saveEmail라는 키값으로 넘겨받았던 아이디값을 쿠키로 저장
Cookie cookie = new Cookie("saveEmail", memEmail); // "saveId" - "user01"
// 1분 == 60초
// 1시간 == 60분 == 60 * 60초
// 하루 == 24시간 == 1 * 24 * 60분 == 1 * 24 * 60 * 60초
// 이틀 == 48시간 == 2 * 24 * 60분 == 2 * 24 * 60 * 60초
cookie.setMaxAge(1 * 24 * 60 * 60); // 만료기간 1일 (초단위 작성)
// 쿠키를 브라우저로 넘기기 => 응답 정보에 첨부함 (response 객체 사용)
response.addCookie(cookie);
} else {
// 아이디를 저장하지 않겠다
// => 아이디를 저장하고 있었던 쿠키 자체를 삭제
// (쿠키를 삭제시키지 않으면 만료일까지 계속 살아 있기 때문)
Cookie cookie = new Cookie("saveEmail", memEmail);
cookie.setMaxAge(0); // 0초
response.addCookie(cookie);
}
// 요청 시 전달값들을 VO 객체로 가공
Member m = new Member();
m.setMemEmail(memEmail);
m.setMemPwd(memPwd);
// 가공한 VO 객체를 해당 요청을 처리하는 서비스 클래스의 메소드로 넘기기
Member loginUser = new MemberService().loginMember(m);
// 처리된 결과 응답 뷰 지정
if(loginUser == null) { // 로그인 실패 => 에러 문구를 담아서 에러 페이지로 응답하기
request.setAttribute("errorMsg", "로그인에 실패했습니다.");
RequestDispatcher view = request.getRequestDispatcher("views/common/errorPage.jsp");
view.forward(request, response);
} else { // 로그인 성공 => 응답 페이지에 loginUser 데이터 전달, 메인 페이지로 응답
HttpSession session = request.getSession();
session.setAttribute("loginUser", loginUser);
session.setAttribute("alertMsg", loginUser.getMemNickname() + "님! 오늘도 즐거운 게임 하세요!");
response.sendRedirect(request.getContextPath());
}
}
<회원가입>
🤦🏻♀️ 개인정보 수집 및 이용 동의
더보기
1. header.jsp
<% if (loginUser == null) { %>
<li>
<a id="login_menu" onclick="loginPage();">로그인</a>
<ul>
<li><a onclick="loginPage();"> 로그인</a></li>
<li><a onclick="enrollPolicyPage();"> 회원가입</a></li>
</ul>
</li>
<% } else { %>
<li>
<a href="" class="main_menu">
<img src="resources/image/profile/profile_default.png" id="header_profile">
InsertCoin
</a>
<ul>
<li><a href=""> 마이페이지</a></li>
<li><a href="<%= contextPath %>/logout.me"> 로그아웃</a></li>
</ul>
</li>
<% } %>
<script>
function enrollPolicyPage() {
location.href = "<%=contextPath %>/enrollPolicy.me";
}
</script>
2. memberEnrollPolicy.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입_동의</title>
<!-- CSS 스타일시트 -->
<link href="resources/css/memberEnrollPolicy.css" rel="stylesheet">
</head>
<body>
<!-- 전체 영역 -->
<div class="wrap">
<%@ include file = "../../views/common/header.jsp" %>
<!-- 컨텐츠 영역 -->
<div class="content_container">
<!-- 회원가입 창 -->
<form id="enroll_form" action="<%= contextPath %>/enrollForm.me" method="post">
<!-- 로고, 개인정보 수집 및 이용 동의 체크, 버튼 영역 -->
<div class="enroll_content">
<!-- 회원가입 창 로고 -->
<div style="text-align:center;">
<img src="resources/image/logo/insertcoin_logo.png" alt="insert_coin_logo" id="enroll_logo">
</div>
<!-- 회원가입 글자 -->
<div id="enroll_text"><h3>회원가입</h3><hr></div>
<!-- 회원가입 개인정보 동의 폼 -->
<table align="center" id="enroll_table">
<tr>
<th>개인정보 수집 및 이용 동의</th>
<td>(필수)</td>
</tr>
<tr>
<th colspan="2">
<textarea cols="40" rows="5" style="resize:none;" readonly>
INSERT COIN은 이용자들의 개인정보보호를 매우 중요시하며, 이용자가 회사의 서비스를 이용함과 동시에 온라인상에서 회사에 제공한 개인정보가 보호 받을 수 있도록 최선을 다하고 있습니다. 이에 INSERT COIN은 통신비밀보호법, 전기통신사업법, 정보통신망 이용촉진 및 정보보호 등에 관한 법률 등 정보통신서비스제공자가 준수하여야 할 관련 법규상의 개인정보보호 규정 및 정보통신부가 제정한 개인정보보호지침을 준수하고 있습니다. INSERT COIN은 개인정보 보호정책을 통하여 이용자들이 제공하는 개인정보가 어떠한 용도와 방식으로 이용되고 있으며 개인정보보호를 위해 어떠한 조치가 취해지고 있는지 알려 드립니다</textarea>
</th>
</tr>
<tr>
<th colspan="2">
<input type="checkbox" id="agreeCheck" name="agreeCheck" checked><label for="agree">개인정보 수집 및 이용에 동의합니다.</label>
</th>
</tr>
</table>
<br>
<div>
<input type="button" id="reset_button" class="buttons" value="취소" onclick="goToHome();">
<input type="button" id="submit_button" class="buttons" name="checkButton" onclick="enrollPage();" value="확인">
</div>
</div>
</form>
</div>
<%@ include file = "../../views/common/footer.jsp" %>
</div>
<script>
function goToHome() {
location.href = "<%= contextPath %>";
}
// 약관 동의 시 확인 버튼 클릭할 수 있게
$(function() {
$("#agreeCheck").change (function () {
var st = this.checked;
if(st) {
$("input[name=checkButton]").prop("disabled", false);
} else {
$("input[name=checkButton]").prop("disabled", true);
}
});
});
function enrollPage() {
location.href = "<%= contextPath %>/enrollForm.me";
}
</script>
</body>
</html>