본문 바로가기

2022-코딩 수업 정리

게시판 만들기-2

목표:게시판을 만들고 파일을 원하는 경로에 저장하여 첨부한 파일과 동일한 파일명이 있을 경우 파일명을 변경하여 저장한다. 또한 mysql에 폼으로 입력한 데이터가 올라가도록 한다.

 

 

게시판 만들기-1에 이어서 제작하기에 게시판 만들기-1의 코드에서 좀 더 추가, 수정을 하거나 그대로인 경우도 있다.

 

mysql

-- 게시판 생성 수업시간에 진도 나간 부분


USE STUDY;
-- #####################################################################################
DROP TABLE IF EXISTS TBOARD_PART;
CREATE TABLE IF NOT EXISTS TBOARD_PART (
	FIDX		TINYINT			NOT NULL					COMMENT 'P/K, 머릿글 번호',
    FPART		NVARCHAR(4)		NOT NULL					COMMENT '머릿글',
    PRIMARY KEY (FIDX)
) COMMENT='게시판의 글머릿글에 사용';
INSERT INTO TBOARD_PART VALUES 
('1', '학습'), ('2', '생활'), ('3', '취업'), ('4', '고민'), ('5', '과정');
-- #####################################################################################
DROP TABLE IF EXISTS TBOARD;
CREATE TABLE IF NOT EXISTS TBOARD (
	FIDX		INT					NOT NULL				COMMENT '일련번호, P/K, ',
    FNUM		INT					NOT NULL	DEFAULT 0	COMMENT '목록번호(질문), 답글의 경우 0',
    FGROUP		INT					NOT NULL				COMMENT '그룹번호, 1차 정렬 필드, 질문은 자신의 FIDX, 답글은 선택글의 FGROUP번호',
    FLEVEL		INT					NOT NULL	DEFAULT 0	COMMENT '질문은 0, 답글은 선택한 글의 FLEVEL+1',
    FSTEP		INT					NOT NULL				COMMENT '2차 정렬 필드, 질문은 0, 답글은 그룹내 선택한 목록의 FSTEP 이상인 글은 모두 1증가 이후 선택 목록의 FSTEP값을 갖는다, ',
    FTOP		ENUM('Yes', 'No')	NOT NULL				COMMENT '상단노출, 1페이지 상단에 노출하고자 하는 목록은 Yes(1)',
    FPARTIDX	TINYINT				NOT NULL				COMMENT '머릿글, 별도의 테이블을 만들어 관리, fk_tboard_fpartidx_tboardPart_fidx',
    FSUBJECT	NVARCHAR(50)		NOT NULL				COMMENT '게시판의 제목 필드',
    FCONTENT	TEXT				NOT NULL				COMMENT '게시판의 내용 필드',
    FID			VARCHAR(20)									COMMENT '회원의 경우 아이디 저장, 이름은 회원명 저장, 비밀번호는 저장하지 않음',
    FNAME		NVARCHAR(20)		NOT NULL				COMMENT '작성자 이름, 회원의 경우 회원명 저장(Join으로 인한 검색속도 저하문제 보완)',
    FPASSW		VARCHAR(20)			NULL					COMMENT '삭제 수정을 위한 비밀번호, 회원의 경우 Null',
    FHIT		SMALLINT			NOT NULL	DEFAULT 0	COMMENT '글 조횟수',
    FDELETED	ENUM('Yes', 'No')	NOT NULL				COMMENT '작성자에 의해 삭제된 경우(1, Yes)', -- DEFAULT 값은 NO로 줬어야했다.(강사님이 까먹으셨고 servelt 쪽에서 no 넣음)
    FDATE		DATETIME			NOT NULL				COMMENT '24시간 이내의 글은 new표시',
	PRIMARY KEY (FIDX)
    -- FOREIGN KEY는 관계를 맺어야하므로 해당 테이블만 가지고 테스트를 할 때는 막아두기
   --  ,FOREIGN KEY	(FPARTIDX)		REFERENCES	TBOARD_PART (FIDX)
    -- ON UPDATE CASCADE
) COMMENT='JSP&SERVLET을 활용한 Q&A 게시판 수업을 위한 게시글 정보 테이블';
-- #####################################################################################
DROP TABLE IF EXISTS TBOARD_FILE;
CREATE TABLE IF NOT EXISTS TBOARD_FILE (
	FIDX		INT					NOT NULL				COMMENT '일련번호, P/K',
    FBOARDIDX	INT					NOT NULL				COMMENT 'F/K, 게시판의 목록 FIDX',
    FFILENAME	VARCHAR(30)			NOT NULL				COMMENT '업로드된 파일의 원래 이름',
    FSAVENAME	VARCHAR(30)			NOT NULL				COMMENT '업로드된 파일의 저장된 이름',
    FFILESIZE	INT					NOT NULL				COMMENT '업로드된 파일의 크기',
	FDOWNLOAD	INT					NOT NULL	DEFAULT 0	COMMENT '다운로드 수' ,
    PRIMARY KEY (FIDX)	
-- ,    FOREIGN KEY (FBOARDIDX)		REFERENCES	TBOARD (FIDX)
-- FOREIGN KEY는 관계를 맺어야하므로 해당 테이블만 가지고 테스트를 할 때는 막아두기
) COMMENT='게시글에 파일을 첨부할 경우 다중 파일 업로드가 가능하도록 별도의 테이블로 관리';

-- =====================================================================================

select MAX(FIDX) from TBOARD_FILE;
-- TBOARD_FILE에서 FIDX가 MAX(최대치)인 값 출력

select ifnull(MAX(FIDX),1) from TBOARD_FILE;
-- null 이면 1을 출력, 아니면 MAX(FIDX)출력


select ifnull(MAX(FIDX)+1,1) aidx from TBOARD_FILE;
-- TBOARD_FILE에서 fid가 null이 아니면 FIDX 최대 값에 1을 더하고 null이면 1을 출력한다
-- aidx는 가상 칼럼의 이름이다


-- ==================================================



desc TBOARD;

desc TBOARD_FILE;

insert into TBOARD_FILE(FIDX, FBOARDIDX, FFILENAME, FSAVENAME,FFILESIZE)
 value('"++"','"++"','"++"','"++"','"++"');
 -- value는 한 칸 띄우고 시작(공백도 신경쓰기)



USE STUDY;
INSERT INTO TBOARD_FILE (FIDX, FBOARDIDX, FFILENAME, FSAVENAME, FFILESIZE)
VALUE('1','1','memo.txt','memo.txt','1024');

INSERT INTO TBOARD_FILE (FIDX, FBOARDIDX, FFILENAME, FSAVENAME, FFILESIZE)
VALUE('2','1','memo.txt','memo.txt','1024');    


SELECT ifnull(MAX(FIDX)+1,1) aidx from TBOARD_FILE;
-- TBOARD_FILE에서 FIDX의 MAX(최대값)이 null이 아니면 최대값+1, null이면 1을 가상칼럼 aidx로 출력한다


select*from TBOARD_FILE;
select*from TBOARD;
select*from TBOARD_PART;


select FGROUP ,FLEVEL, FSTEP from TBOARD;

desc tboard;
-- ===============================

-- TBOARD 요소들
-- FIDX, FNUM, FGROUP, FLEVEL, FSTEP, FTOP, FPARTIDX, FSUBJECT, FCONTENT, FID, FNAME, FPASSW, FHIT, FDELETED, FDATE
insert into TBOARD (FIDX, FNUM, FGROUP, FLEVEL, FSTEP, FTOP, FPARTIDX, FSUBJECT,FCONTENT, FID, FNAME, FPASSW,  FDELETED, FDATE)
value('1','1','1','1','1','No','1','제목','내용','ID','익명','passwd','No','2022-01-01');
-- 데이터가 들어가는지 확인하고 들어가지 않을 경우 수정할 것

select*from TBOARD;

insert into tboard (FIDX, FNUM, FGROUP, FLEVEL, FSTEP, FTOP, FPARTIDX, FSUBJECT, FCONTENT ,FID, FNAME, FPASSW, FDELETED, FDATE) value ('4','3','4','0','0','No','1','2','3','','1','','NO', sysdate());



insert into tboard (FIDX, FNUM, FGROUP, FLEVEL, FSTEP, FTOP, FPARTIDX, FSUBJECT, FCONTENT, FID, FNAME, FPASSW, FDELETED, FDATE) value ('3','2','3','0','0','No','학습','2','3','','1','','NO', sysdate());


-- =====================================2022-09-30 오후 수업==========
delete from tboard where fidx>0; -- tboard에서 fidx>0인 경우 데이터를 삭제한다
commit;
select*from tboard;
-- tboard에 데이터가 들어갔는지 확인

TRUNCATE TABLE tboard; -- 테이블 안에 있는 데이터만 지운다

 

 

 

이전 게시글과 동일

reset.css

@charset "utf-8";

/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html {-webkit-text-size-adjust:100%;line-height:1.15}
body {margin:0;padding:0;}
main {display:block}
h1, h2, h3, h4, h5, h6, h7 {margin:0;padding:0;}
h1 {font-size:2em;margin:0}
hr {box-sizing:content-box;height:0;overflow:visible}
pre {font-family:monospace, monospace;font-size:1em}
/* ---------------- 사용자 설정 -----------------*/
ul, li, ol, dl, dt, dd {margin:0;padding:0;list-style: none;}
/* --------------- e:사용자 설정 ----------------*/
a {background-color:transparent}
abbr[title] {border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
b, strong {font-weight:bolder}
code, kbd, samp {font-family:monospace, monospace;font-size:1em}
small {font-size:80%}
sub, sup {font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sub {bottom:-.25em}
sup {top:-.5em}
img {border-style:none}
button, input, optgroup, select, textarea {font-family:inherit;font-size:100%;line-height:1.15;margin:0}
button, input {overflow:visible}
button, select {text-transform:none}
[type=button], [type=reset], [type=submit], button {-webkit-appearance:button}
[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {border-style:none;padding:0}
[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring {outline:1px dotted ButtonText}
fieldset {padding:.35em .75em .625em}
legend {box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}
progress {vertical-align:baseline}
textarea {overflow:auto}
[type=checkbox], [type=radio] {box-sizing:border-box;padding:0}
[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {height:auto}
[type=search] {-webkit-appearance:textfield;outline-offset:-2px}
[type=search]::-webkit-search-decoration {-webkit-appearance:none}
::-webkit-file-upload-button {-webkit-appearance:button;font:inherit}
details {display:block}
summary {display:list-item}
[hidden], template {display:none}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

 

이전 게시글과 동일

style.css

@charset "UTF-8";
@import url(reset.css);
@import url(https://cdn.jsdelivr.net/gh/moonspam/NanumSquare@1.0/nanumsquare.css);
html * {
    font-family: 'NanumSquare', sans-serif;
    color:#222;
}
/*-------------------------------------------------------*/
body {background-color:#fff; }
#container { 
    margin:0 auto;padding:0;
    width:70%;min-width: 720px;
}
h3 {
	width:100%; min-width:720px;margin:0 auto;
	padding:10px 0;}
h3 span {
	display:inline-block; border-bottom:3px solid #fd5a02;
}
.tableBox {
    display:table; width:100%; min-width:720px;margin:0 auto; 
    border-top:3px solid #7b7b3f;border-bottom:3px solid #7b7b3f;
}
.row {display:table-row;}
.cell {
	display:table-cell; padding:10px 5px;
	vertical-align: top; 
	border-bottom:1px solid #7b7b3f;
	}
.cell:nth-child(odd) {
	width:150px;vertical-align: top;
	text-align: center; background-color:#faefb8; 
	border-bottom:1px solid #fff;
	}
.findArea {position:relative; margin-left:310px;margin-top:85px;}
#filelist {padding:2px;border:1px solid #ccc; width:300px;height:100px;overflow-y:auto;}
#filelist li:first-child {background-color:#408080;color:#fff;text-align:center;}
.boardFooter {text-align: right; width:10%; min-width:720px;padding:20px;}

.btn {padding: 3px 10px;border:1px solid #ccc;cursor:pointer;}
.btn:hover {background:#000;border-color:#000;color:#fff;font-weight:bold;}
input[type=text] {width:80%;}
input[type=file] {
					opacity: 0; width: 85px;position:absolute; 
					visibility:visible;left:0px;top:-3px;
					}
textarea {width:80%;height:150px;}

.fl {float:left;}
.fr {float:right;}
.cl {clear:left;}
.cr {clear:right;}
.cb {clear:both;}

 

 

 

이전 게시글과 동일하지만 알람 부분은 코드를 테스트할 때 시간이 오래 걸리게 되기 때문에 주석으로 처리.

board_common.js

$().ready(function(){
    //########################################################################
        //============ 파일을 선택할 경우 ====================
        $("#file").on("change", function() {

            console.log("파일선택");//$("#file").on("change", function(){}) 작동 확인

            //파일명 추출
            var fileInput = document.getElementsByClassName("file");//("file")==>class 이름 주의할 것!!!
            for( var i=0; i<fileInput.length; i++ ){
                if( fileInput[i].files.length > 0 ){
                    for( var j = 0; j < fileInput[i].files.length; j++ ){
                        var fileName=fileInput[i].files[j].name; // 파일명 출력
                    }
                }
            }
            //end:파일명 추출

           
            var filesize=this.files[0].size;//파일 크기 추출

            //작동 확인
            console.log("파일명="+fileName);
            console.log("파일 사이즈="+filesize);
           

        });//e:$("#file").on("change");
        //============ e:파일을 선택할 경우 ====================





        //============ 저장을 클릭 한경우 ====================
        
        $("#btnReg").on("click",function() {
            console.log("저장 클릭");//$("#btnReg")on("click",function(){}) 동작 확인



		/*임시로 막아둔다
            //작성 여부 확인
            if(fn.header.value=="") {
                alert("말머리를 선택해주세요");
                fn.header.focus();
                return;
            }

            if(fn.writer.value=="") {
                alert("작성자를 입력하세요");
                fn.writer.focus();
                return;
            }

             if(fn.subject.value=="") {
                alert("제목을 입력하세요");
                fn.subject.focus();
                return;
            }
            
            if(fn.subject.value=="Title") {
                alert("제목을 바꿔주세요");
                fn.subject.focus();
                return;
            }


            if(fn.content.value=="") {
                alert("내용을 입력하세요");
                fn.content.focus();
                return;
            }

            if(fn.password.value=="") {
                alert("비밀번호를 입력하세요");
                fn.password.focus();
                return;
            }

            if(fn.file.value=="") {
                alert("파일을 첨부하세요");
                fn.file.focus();
                return;
            }
            //end:작성 확인
            */
            



           
            fn.action ="CW";//servelt과 연결
            fn.method="post";// 연결
            fn.submit();
            
 

        });//
        //============ e:저장을 클릭한 경우 ====================




        //============ 취소를 클릭한 경우 ====================
        $("#btnCan").on("click",function() {
            console.log("취소 클릭 /r/n 모든 내용을 초기화합니다");
            
            fn.method="get";//get 방식으로 연결
            fn.action = "write.jsp";//원래 페이지로 돌아간다
            fn.submit();

        });//
        //============ e:취소를 클릭한 경우 ====================
        
        
    //########################################################################
    });

 

 

 

 

 

 

 

 

write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
   <%
   
   //데이터 베이스에 전달할 데이터
   //form에서 input으로 idx, group, level, step, list의 value를 결과값을 출력하는 태그로 줬기 때문에 제어해준다.
   //하지 않으면 그냥 빈값이 들어간다.
   String list="";
   String idx="";
   String group="";
   String step="";
   String level="";
   
   
 	//if문 해석=null이 아니고 빈 문자열이 아니고 숫자이면 list에 값을 넣는다
	if(request.getParameter("idx")!=null && !"".equals(request.getParameter("idx"))&&
	request.getParameter("idx").matches("[0-9.]+"))
	list=request.getParameter("idx");
 
 
	if(request.getParameter("group")!=null && !"".equals(request.getParameter("group"))&&
			request.getParameter("group").matches("[0-9.]+"))
			list=request.getParameter("group");
	
	
	if(request.getParameter("level")!=null && !"".equals(request.getParameter("level"))&&
			request.getParameter("level").matches("[0-9.]+"))
			list=request.getParameter("level");
	
	if(request.getParameter("step")!=null && !"".equals(request.getParameter("step"))&&
			request.getParameter("step").matches("[0-9.]+"))
			list=request.getParameter("step");
	
	
	if(request.getParameter("list")!=null && !"".equals(request.getParameter("list"))&&
			request.getParameter("list").matches("[0-9.]+"))
			list=request.getParameter("list");
   
   %>
    
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>게시판 글쓰기</title>
    
	<!--     CSS 파일 연결 -->
    <link href="style.css" rel="stylesheet" />
    
    <!--  js 파일 연결-->
    <script src="https://code.jquery.com/jquery-3.6.1.js"></script>
    <script src="board_common.js"></script>
    
    
    
    
</head>
<body>
    <div id="container">
    <form name="fn" method="get" enctype="multipart/form-data">
    <!-- form태그로 데이터를 넘긴다 -->
    
    
   
    <input type="hidden" name="idx" id="idx" value="<%=idx %>"/>
    <input type="hidden" name="group" id="group" value="<%=group %>"/> 
    <input type="hidden" name="level" id="level" value="<%=level %>"/> 
    <input type="hidden" name="step" id="step" value="<%=step %>"/>
    <input type="hidden" name="list" id="list" value="<%=list %>"/> 
    
	<h3><span>질문</span>게시판</h3>
	
    <ul class="tableBox">
    
       		<li class="row">
       			<div class="cell"><label for="part">상단고정</label></div>
       			<div class="cell">
       			<input type="checkbox" name="top" id="top" value="Yes"/>
       			<label for="top">게시판 상단에 위치시키실 경우 선택하세요</label>
       			</div>
				<!--상단 처리 js에서 만들기 -->
       			
       		</li>
           <li class="row">    
               <div class="cell"><label for="header">머릿글</label></div>
               
               <div class="cell">
               <!-- select로 드롭 박스 만들기 -->
               	<select name="header" id="header">
               		<option value="">== 선택 ==</option>
               		<option value="1">학습</option>
               		<option value="2">생활</option>
               		<option value="3">취업</option>
               		<option value="4">고민</option>
               		<option value="5">과정</option>
               	</select>
               </div>
          	</li>
          
          
          <li class="row">
          
               <div class="cell">
               	<label for="writer">작성자</label>
               </div>
          
               <div class="cell">
               	<input type="text" name="writer" id="writer">
               </div>
          </li>
          
          
          <li class="row">
          
               <div class="cell">
               	<label for="subject">제목</label>
               </div>
               
               <div class="cell">
               	<input type="text" name="subject" id="subject"
               	value="Title" />
               </div>
          </li>
          
          
          <li class="row">
          
               <div class="cell">
               	<label for="content">내용</label>
               </div>
               
               <div class="cell">
               	<textarea name="content" id="content"></textarea>
               </div>
               
          </li>
          
          
          <li class="row">
          
               <div class="cell">
               	<label for="password">비밀번호</label>
               </div>
               
               
               <div class="cell">
               	<input type="password" name="passw" id="passw" />
               </div>
          </li>
          
          
          <li class="row">
          
               <div class="cell">
               	<label for="file">파일</label>
               </div>
               
               
               <div class="cell">
	               	<ul id="filelist" class="fl">
	               		<li>파일목록</li>
	               		<li class="fl"></li>
	               	</ul>
	               	
	               	<div class="findArea">
	               		<span id="btnFileUpload" class="btn">파일찾기</span>
	               		<input type="file" class="file" name="file" id="file" multiple />
	               	</div>
               	
               </div>
          </li>
       </ul>
       <div class="boardFooter cl">
			<span class="btn" id="btnReg">저장</span>
			<span class="btn" id="btnCan">취소</span>
       </div>
       </form>
    </div>
    
    
    
</body>
</html>

 

 

 

 

 

BbsDAO.java

package pkgStudy;

import java.util.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

//========== 데이터 베이스 관련 =====================
public class BbsDAO {
	private Connection conn;
	private ResultSet rs;
	
	public void bbsConnect() {
		try {
			//데이터 베이스 접근용 주소, 아이디, 비번
			String dbURL="jdbc:mysql://localhost:3306/study?serverTimezone=UTC";
			String dbID="root";
			String dbPassw="1234";
			//==>상황에 따라 mysql 주소, 아이디, 비밀번호는 수정할 수 있다.
			
					
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection(dbURL, dbID, dbPassw);
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	//=================== 파일처리 인덱스 값 생성하여 반환 =============
	public int getGroupIdx() {//이름만 Group이 들어가고 Group과는 상관 없음
		//public  int getGroupIdx(String tableName, String col)이면 String tableName은 테이블 명을 넘기겠다는 뜻이다
		
		String SQL="";//String SQL 변수 선언
		try {
			SQL="SELECT IFNULL(MAX(FIDX)+1,1) aidx  FROM TBOARD_FILE;";//TBOARD_FILE에서 FIDX들이 null이 아니면 최대값에 1을 더하고 null이면 1출력
			//mysql에서 먼저 실행해보고 가져오기
			PreparedStatement pstmt = conn.prepareStatement(SQL);
			rs = pstmt.executeQuery();
			int idx = 1;//초기값 주어지지 않아도 된다. 집합함수를 사용했을 경우 반드시 행이 반환된다
			if (rs.next()) {
				idx = rs.getInt(1);//int 타입 값 가져오기. String은 getString 사용
			}
			rs.close();//result set 객체를 닫아준다. 방대한 양의 데이터를 사용할 경우에는 데이터 베이스에 부담이 되기 때문에 닫아줘야한다.
			return idx;//반환 받은 FIDX값을 반환
		}catch(Exception e) {
			return -1;//에러 발생시
		}
	}
	//===================== 업로드 파일 정보 삽입 =======================
	
	public int addFile(String SQL) {
		try {
			int rst=0;
			PreparedStatement pstmt=conn.prepareStatement(SQL);
			rst=pstmt.executeUpdate(SQL);//영향을 받은 행의 갯수를 반환
			return rst;//rst를 반환 받을 수 있는 건 구조가 동일한 것을 알고 있기 때문이다=>삽입 성공한 행의 갯수 반환
		}catch(Exception e) {
			return -1;//에러 발생시
		}
	}//end:public int addFile(String SQL)
	
	//===============================================================
	//===================== 게시물 등록 =============================
	//질문을 기준으로 생성한다
	//하나의 메서드로 처리(제어권이 모듈 쪽에 있다) 질문과 답변의 메서드를 따로 해서 처리한다(제어권이 서블릿에 있다)
	
	
	public int[] addBoard(List<String> req) {
		//mysql에서 받아오는 매개변수들을 일일히 적어서 받는게 아니라 List로 받는다. int를 복잡하게 받은 대신 String은 쉽게 받는다. 둘 다 쉽게하는 건 욕심
		try {
			
			int rst=0;
			ResultSet rs;
			// --------------idx anmd group and listNum 반환-----------
			int idx, num, grp, level, step;//변수 선언
			int qidx;
			bbsConnect();//mysql 연결==>데이터 베이스 서버 연결, conn은 인스턴스(전역) 변수
			//3개의 값을 추출한다
			String SQL="select ifnull(max(fidx)+1,1) aidx, ifnull(max(fnum)+1,1) anum from tboard";
			//ifnull==>만약 null이면 1을 반환. 아니면 max(fidx)+1이 반환된다
			//max(fidx)+1== 새롭게 삽입할 수
			//aidx, anum은 가상 컬럼명
			PreparedStatement pstmt = conn.prepareStatement(SQL);
			rs=pstmt.executeQuery(SQL);
			rs.next();
			//무조건 반환 받기 때문에 따로 if문을 주지 않아도 된다
			idx=rs.getInt("aidx");//질문의 경우 fidx컬럼과 fgroup의 번호는 동일하다==그룹 번호는 반환받지 않는다
			
			//------아래쪽은 if문으로 제어해야하는 변수들
			num=rs.getInt("anum");//목록 번호 추출(질문은 최대값+1, 답변은 0)
			grp=idx;//질문의 경우 fidx컬럼과 fgroup의 번호는 동일하다
			level=0;//질문인 경우
			step=0;//질문인 경우
			//num, grp, level, step은 나중에 질문이냐 답변이냐에 따라 달라지기 때문에 if문으로 제어한다
			rs.close();
			System.out.print("에러:"+req.get(7));
			if(Integer.parseInt(req.get(7))>0) {//답변
				num=0;//답변은 무조건=0
				grp=Integer.parseInt(req.get(8));//선택한 목록의 그룹번호. CtrlWrite의 List<String> req=new ArrayList<>();에서 8번째
				level=Integer.parseInt(req.get(9))+1;//req.get(9)는 문자열이므로 +1은 바깥에서 처리한다
				//-------------내가 선택한 목록의 level보다 큰 level이 있다면 큰 level을 전부 1 증가
				//idx는 질의응답과 관계 없는 ID값이다=>여기에서는 처리안함
				
				step=Integer.parseInt(req.get(10));
				
				
				//------------같은 그룹 내에서 나보다 크거나 같은 것들을 Update문으로 1증가시킨다==>Update문이 필요하다
				//Update를 먼저 하고 값이 들어가야 들어가고자 하는 값은 빠지고 +1씩 된다
				//-------선택한 목록의 level에 1증가
				SQL="update tboard set fstep=fstep+1 "
						+" where fgroup="+grp+" and fstep >="+step;//where, and 앞에 한 칸씩 띄우기
				System.out.print(SQL);
				pstmt=conn.prepareStatement(SQL);
				rst=pstmt.executeUpdate(SQL);//영향을 받은 행의 갯수를 반환
				
			}
			
			else {//질문
				
				
			}
				
			//====================return value 생성===============
			int[] keyVal={idx,grp,level,step,num};
			
			//-------------------전송 데이터-----------------------
			
			//FHIT는 기본값으로 0을 줘서 뺀다
			
			SQL="insert into tboard (FIDX, FNUM, FGROUP, FLEVEL, FSTEP, FTOP, FPARTIDX, FSUBJECT, FCONTENT"//FCONTENT는 세션 아이디이다
					+ " ,FID, FNAME, FPASSW, FDELETED, FDATE) value "
					//default 값이 있으면 빼도 된다.
					+"('"+idx+"','"+num+"','"+grp+"','"+level+"','"+step+"','"+req.get(0)
					+"','"+req.get(1)+"','"+req.get(2)+"','"+req.get(3)+"','"+req.get(4)
					+"','"+req.get(5)+"','"+req.get(6)+"','NO', sysdate())";
			// --------------end:idx anmd group and listNum 반환-----------
			pstmt=conn.prepareStatement(SQL);
			rst=pstmt.executeUpdate(SQL);
			return keyVal;
			
			//오류 확인 부분. 오류가 없었으므로 주석 처리
//			if(rst>0) return keyVal;
//			else {
//				
//				System.out.print(SQL);
//				return null;
//			}
			
			//return keyVal;
			//삽입 성공한 행의 갯수 반환
			
		}//end:try
		catch(Exception e) {
			return null;//에러 발생시//배열이기 때문에 null 값을 사용. 배열을 만들어서 보낼 수는 없다
		}//end:catch()
	}//end:addBoard(List<String> req)
	//===================== end:게시물 등록 ==================================
	
	
}//e:Class

 

 

 

 

 

CtrlWrite.java

코드를 만들면서 out.print를 사용하여 값이 넘어가는 것과 오류가 있는지를 검사했고 코드가 정상적으로 돌아갈 경우 주석 처리를 했다.

또한 코드가 길어짐에 따라 코드를 구분하기 위한 주석과 수업시간의 필기 또한 주석으로 작성하여 주석이 많다.

package pkgStudy;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
//import java.lang.*;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@WebServlet("/CW")
@MultipartConfig(
        maxFileSize = 1024 * 1024 * 5,
        maxRequestSize = 1024 * 1024 * 50 //첨부 파일의 최대 크기 지정
)

//post로 출력하기 위해 사용

public class CtrlWrite extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public CtrlWrite() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//post 방식 사용
        
		response.setContentType("text/html;charset=UTF-8");
		request.setCharacterEncoding("UTF-8");
		//한글화
		PrintWriter out = response.getWriter(); 
        //out.print를 사용할 수 있게 된다
		
		String header=""; String top = "No"; String subject = "";
		String writer = ""; String content = "";String passw = "";
		
		long fileSize=0; //전송된 파일의 size
		int idx = 0;//새로 등록할 파일정보 P/K 값(FIDX)
		
		
		
		//html에서 값 넘겨 받기. name값으로 한다.
		//request.getParameter("top")는 JSP에서 넘겨받는 name=top인 값.
		//if문 해석= request.getParameter("top")이 null이 아니고 공백이 아닐 때==값이 들어있을 때 실행하는 것
		if(request.getParameter("top")!=null && !"".equals(request.getParameter("top"))) 
			top=request.getParameter("top");
		if(request.getParameter("header")!=null && !"".equals(request.getParameter("header"))) 
			header=request.getParameter("header");
		if(request.getParameter("writer")!=null && !"".equals(request.getParameter("writer"))) 
			writer=request.getParameter("writer");
		if(request.getParameter("subject")!=null && !"".equals(request.getParameter("subject"))) 
			subject=request.getParameter("subject");
		if(request.getParameter("content")!=null && !"".equals(request.getParameter("content"))) 
			content=request.getParameter("content");
		if(request.getParameter("passw")!=null && !"".equals(request.getParameter("passw"))) 
			passw=request.getParameter("passw");
		
		//데이터가 넘어가는지 확인하고 넘어갈 경우 주석처리 혹은 삭제한다
//		out.print(top);
//		out.print(header);
//		out.print(writer);
//		out.print(subject);
//		out.print(content);
//		out.print(password);
		//out.print("연결 성공!!");
		
		
		//------------------- 답변글에 대한 데이터 취득부 ------------------------------------
		String qidx="0"; String group="0"; String level="0"; 
		String step="0"; String list="0";// 해당 변수들로 질문인지 답변인지 구분한다
		
		
		if(request.getParameter("idx")!=null && !"".equals(request.getParameter("idx"))&&
				request.getParameter("idx").matches("[0-9.]+"))
			qidx=request.getParameter("idx");//어차피 밑에서 문자열로 바꿔서 그대로 둔다
		if(request.getParameter("group")!=null && !"".equals(request.getParameter("group"))&&
			request.getParameter("group").matches("[0-9.]+")) 
			group=request.getParameter("group");
		if(request.getParameter("level")!=null && !"".equals(request.getParameter("level"))&&
				request.getParameter("level").matches("[0-9.]+")) 
			level=request.getParameter("level");
		if(request.getParameter("step")!=null && !"".equals(request.getParameter("step"))&&
				request.getParameter("step").matches("[0-9.]+")) 
			step=request.getParameter("step");
		if(request.getParameter("list")!=null && !"".equals(request.getParameter("list"))&&
				request.getParameter("list").matches("[0-9.]+")) 
			list=request.getParameter("list");
		//---------------------------------------------------------------------------------
		//String형=> FTOP, FPARTIDX, FSUBJECT,
		//FID, FNAME, FPASSW, FDELETED, FDATE
		//mysql 칼럼 순서대로 데이터를 받는다
		
		
		List<String> req=new ArrayList<>();
		//다 문자열로 넘어온다
		req.add(top);
		req.add(header);
		req.add(subject);
		req.add(content);
		req.add("");//id부분
		req.add(writer);//name부분
		req.add(passw);
		req.add(qidx);
		req.add(""+group);//문자열+숫자열=문자열
		req.add(level);
		req.add(step);
		req.add(list);
		
		
		//####################### 게시물 등록부 ########################################
		//============== 데이터베이스 연결 ==========================
		BbsDAO bbs = new BbsDAO();//BbsDAO 클래스와 연결. 사용자 객체 생성
		//----------------------------------------------------------
		int[] keyVal=bbs.addBoard(req);//forward를 통해 이동할 수 있는 준비
		if(keyVal==null) out.print("null");//에러 페이지 생성 후 redirect로 에러 페이지로 이동하여 에러를 잡을 수 있도록 한다
		
		//오류확인 부분
//		if(keyVal!=null) out.print(keyVal[0]);
//		//out.print(bbs.addBoard(req));
//		else out.print("null");
		
		

		//###################### 게시물 등록부 끝 #####################
		
		
		//================ 파일 업로드 부분============================
		String firstName = ""; String exName = ""; String orgName = "";
	
		
		
		
		bbs.bbsConnect();//객체 메서드 실행
		//===========================================================
		idx = bbs.getGroupIdx();

		String strSQL="insert into tboard_file (FIDX, FBOARDIDX, FFILENAME, FSAVENAME, FFILESIZE) values";
		Collection<Part> parts = request.getParts();//업로드된 파일 컨트롤을 위해 Part Object 생성
		for( Part part : request.getParts()) { //업로드된 내용(요소)만큼 반복
			if(part.getSubmittedFileName()!=null) {//다른 입력요소는 제외하고 파일 이름만 넣는다
				String uploadFilePath="C:\\project\\save_upload_file\\";//==>상황에 따라 경로는 수정할 수 있다.
				String regFileName="";//폴더를 처음부터 문자열 데이터 저장에 사용한다 
				orgName = part.getSubmittedFileName();//파일명 추출
		        firstName = orgName.substring(0,orgName.lastIndexOf("."));//반드시 제어를 하고 내려와야 오류가 없다(null 때문)
		        exName = orgName.substring(orgName.lastIndexOf("."));
		        //out.println(fristName+exName);//추출한 파일명을 출력하여 확인한다
				
				//=============== 파일 중복 체크 후 새 파일명 생성 ===================
		        //File file = new File("fullPath");
		        //==>파일 객체를 생성해서 사용, 디렉토리 생성, 파일 유무 확인.
		        File file = new File(uploadFilePath + orgName);
		        int cnt=1;
		        regFileName = orgName;//중복되지 않는 파일이 업로드 된경우 대비. while문이 돌지 않는 것은 중복되는 파일이 없다는 경우이다. 이 때 파일명은 그대로 저장되도록 한다
		        while(file.exists()) {
		        	//한 번에 여러개의 파일이 업로드 될 수 있으므로 반복문을 사용하여 확인한다
		        	//반복적으로 파일명을 확인 하여 새로운 파일명을 만들어 준다
		        	//file = new File(uploadFilePath + 새로만들어진 파일명);
                    
		        	//파일명 만들기
		        	regFileName = firstName + (cnt++) + exName;
		        	//regFileName=firstName+변수(숫자)+exName;==저장하려는 파일명
		        	
		        	
		        	//file은 업로드된 파일경로+파일명을 의미한다.
		        	//file=new File(uploadFilePath(파일 경로)+새로 만들어진 파일명);
		        	file = new File(uploadFilePath + regFileName);
		        	
		        	
		        	//어쨌든 중복되는 파일을 저장하지 않는 구조로 만들면 된다
		        }
		      
		        //uploadFilePath에 지정된 폴더로 클라이언트에서 전송된 파일을 중복 문제 없는 이름으로 저장.
		        
		        
		        file = new File(uploadFilePath);//디렉토리 확인 및 생성을 위한 File 객체 생성
		        if (!file.exists()) file.mkdirs(); //디렉토리가 없을 경우 생성한다
		        //uploadFilePath에 지정된 폴더로 클라이언트에서 전송된 파일을 새로 만들어진 이름으로 저장.
		        
		        
		        fileSize = part.getSize();//Part에서 전송된 파일의 크기를 반환 받는다
		        //out.print(orgName+part.getSize());//Part에서 전송된 파일의 크기를 반환받는다. 출력 확인 후 주석 처리
		        
		        part.write(uploadFilePath + regFileName);
		      //out.write((int) part.getSize());
		        
		        
		        //=============== end:새파일 명 만들기 끝 =============================
		        
		       
		        
		        
		        
		        //File fileSaveDir = new File("C:\\project\\save_upload_file\\");
		        //if (!fileSaveDir.exists()) 	fileSaveDir.mkdirs();
				
				if(idx>0) {//P/k값을 정상적으로 받아온 경우, -1은 장애발생을 의미
					//idx, 1, orgName, regFileName, fileSize
					strSQL+=" ('"+idx++  +"', '1', '"+orgName+"', '"+regFileName+"', '"+fileSize+"'),";
					//out.println(strSQL);//확인되면 닫기
				}//end:if(idx>0)
			}//e:if(part.getSubmittedFileName()!=null) <== 실제 파일이 있을 경우 처리부
		}//e:for( Part part : request.getParts()) ==> 여러 파일 반복 끝
		
		strSQL=strSQL.substring(0, strSQL.length()-1);
		out.print(strSQL);
		//out.print(bbs.addFile(strSQL));
		//out.print(subject);
		
		
		//for문의 안이나 for문이 끝난 다음에 데이터 베이스를 넣을 수 있다.
		//두 가지 방법의 장단점이 있다.
		//for문 안에 넣으면 DB의 부담이 갈 수 있다
		//밖에 만들면 DB의 부담은 없지만 만들기가 까다롭다
		
		

		//=========== 파일 관련 ============================
		
		//아래 코드만 사용한다면 경로에 없는 파일의 경우 미리 직접 생성해둬야한다.
		//File file=new File("C:\\project\\save_upload_file\\");
		
		//fileSaveDir로 없는 파일이면 생성하도록 한다
		//File fileSaveDir=new File("C:\\project\\save_upload_file\\");
		//if(!fileSaveDir.exists()) fileSaveDir.mkdir();//생성한다
		//out.print(file.exists());//다중으로 올린 경우 올린 갯수만큼 출력되야한다.
		
		//성공이면 true, 실패면 false 출력
		//=========================================
		
		

		//=================================================
		//forward()를 통한 데이터 이동
		//import javax.servelt.RequestDispatcher; 필요
		//url 주소를 정확하게 주는 게 좋다.
		//jsp나 html은 웹 경로를 기준으로 움직인다
		//서블릿은 컨트롤러를 통해서만 클라우드로 이동한다==>서블릿은 자기 중심적이다
		//서블릿에서는 '/'가 있는게 현재 위치이고 서블릿은 자기 위치라도 /를 사용해야한다
		RequestDispatcher fw=request.getRequestDispatcher("/write.jsp");
		//wirte.jsp를 바로 실행하면 질문이다. 아무 값이 없기 때문이다
		request.setAttribute("idx", keyVal[0]);
		request.setAttribute("group", keyVal[1]);
		request.setAttribute("level", keyVal[2]);
		request.setAttribute("step", keyVal[3]);
		request.setAttribute("list", keyVal[4]);
		fw.forward(request, response); //콘트롤러 부분
		//해당 코드는 /write.jsp 즉, 게시판의 원위치로 돌아오게 한다
		//하지만 파일 첨부시 파일은 지정한 위치로 올라가며
		//mysql의 tboard에는 계속해서 값이 올라간다
		
		
//		환경이 다른 경우 forward가 동작하지 않을 수 있다
//		'/'=어떤 파일 밑에 있는 것을 의미한다.
		
//		현재 프로젝트 서버 실행 경로=localhost / project 이름/CW
//		forward는 서버내에서 움직이는 것이기에 클라이언트한테 주소가 가지 않는다
//
//		==>즉, 예시로 webapp에 ab라는 폴더 안에 jsp를 넣으면 경로가 맞지 않는다. 이 때는 절대 경로를 부여해야한다.
//		서블릿은 자기 자신이 경로 중심이다
		
		
		
		
		//doGet(request, response);
		
	}//end:protected void doPost

}//end:response.getWriter().append("Served at: ").append(request.getContextPath());

 

 

 

 

 

write.jsp에서 코드 실행 결과

 

 

게시판의 형태는 게시판 만들기-1과 동일하다

상단고정의 체크 박스는 상관 없지만 머릿글, 작성자, 제목, 내용, 비밀번호, 파일 첨부는 해야한다.

그 후 저장 버튼을 누르면 원래의 화면으로 돌아오지만 mysql과 지정한 경로로 첨부한 파일이 저장 된다.

 

 

폼을 작성하기 전

 

 

 

폼 작성 후

 

 

추가 폼 작성

같은 첨부 파일을 올렸을 경우 '첨부 파일 이름+숫자'로 새로운 파일명이 만들어지면서 저장된다

 

 

FIDX, FNUM, FGROUP, FPARTIDX는 자동적으로 값이 올라간다

FLEVEL, FSTEP, FID, FHIT, FDELETED는 아직 사용하지 않는다.

 

FTOP은 '게시판 상단에 위치시키실 경우 선택하세요' 앞의 체크 박스를 체크하였을 경우 Yes, 체크하지 않았을 경우 NO이다.

 

FSUBJECT, FCONTENT, FNAME, FPASSWD는 폼에 작성한 값이 mysql로 들어간다

FDATE는 mysql에 값을 올린 시간이 올라간다.

'2022-코딩 수업 정리' 카테고리의 다른 글

스프링 환경 설정  (0) 2022.10.05
게시판 만들기-3  (0) 2022.10.05
게시판 만들기-1  (0) 2022.09.30
JSP, HTML 활용 코드  (0) 2022.09.26
html 안에서 JSP 코딩 태그 정리  (0) 2022.09.26