[ITWILL : JSP]Javabean 11 : MultipartRequest클래스

ITWILL학원 : 31강 JSP기초 BY 정규태강사

  • MultipartRequest클래스 (p305)
    MultipartRequest 클래스의 생성자와 메소드들을 사용하여 파일 업로드 기능 구현

https://slidesplayer.org/slide/14223134/
https://slidesplayer.org/slide/14223134/
https://slidesplayer.org/slide/14223134/

1. 라이브러리 설치

  1. servlets페이지 방문.
  2. 왼쪽 카테고리 COS File Upload Library 클릭 하여 com.oreilly.servlet로 이동.
  3. 맨 하단에 Download 에서 Version 밑에 zip압축파일 다운로드.
  4. lib폴더 안의 cos.jar복사하여 프로젝트의 WebContent\WEB-INF\lib에 붙여넣기.

2. fwriteForm.jsp 작성

  1. cos.jar라이브러리설치 (위에 내용 참고)
  2. 폼태그속성 enctype=”multipart/form-data”, method=”post”
  3. D드라이브에 `upfile’ 폴더생성
    • D드라이브말고 원하는 경로 지정가능.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%
String id = (String) session.getAttribute("id");
if( id == null){
%>
<script type="text/javascript">
alert("로그인 후 이용가능합니다");
location.href="../member/loginForm.jsp"
</script>
<%
}

//1. cos.jar라이브러리설치 (위에 내용 참고)
//2. 폼태그속성 enctype="multipart/form-data", method="post" get방식은 처리되지 않음.
//3. D드라이브에 `upfile' 폴더생성- D드라이브말고 원하는 경로 지정가능.
%>

<fieldset>
<legend>게시판 글쓰기(p305~)</legend>
<form action="fwritePro.jsp" method="post" enctype="multipart/form-data">
글쓴이 : <input type="text" name="name" required><br>
비밀번호 : <input type="password" name="pw" required><br>
제목 : <input type="text" name="subject" maxlength="15" required><br>
내용 : <br>
<textarea rows="10" cols="35" name="content" placeholder="여기에 작성해주세요" required></textarea><br>
파일 : <input type="file" name="file">
<input type="submit" value="글등록" class="btn">
<button type="reset" class="btn">초기화</button>
<input type="button" value="목록으로" class="btn" onclick="location.href='boardList.jsp'">
</form>
</fieldset>

3. fwritePro.jsp 작성

파일업로드구현

  1. 파일 업로드 => cos.jar (MultipartRequest 클래스)
  2. 파일이 저장되는 위치 (D:/upfile)
  3. 파일 업로드 최대 크기
    • 크기를 왜 지정해야할까? 그 전에 파일을 업로드하면 어디로 저장되는지 알아야한다
    • 서버의 upfile폴더안으로 들어간다.
    • 따라서 유한한 디스크이고 메모리라서 최대 크기 지정이 필수이다.
    • 보통 20~250MB이다.
    • 1MB = 1024바이트 * 1024바이트 계산 꼭 하기
  4. MultipartRequest생성 : import하기 (다운받았던 cos.jar라이브러리)
    • 아래 코드처럼 객채생성하면 에러메세지가 뜬다
      1
      MultipartRequest multi = new MultipartRequest();
    • The constructor MultipartRequest() is undefined 기본생성자가 없는 클래스이다
    • 따라서 아래처럼 괄호안에 오버로딩되는 생성자를 작성해야한다.
    • MultipartRequest(요청정보,파일 업로드할 폴더,파일 업로드 제한 크기,인코딩방식,파일 이름동일시 처리객체)
      1
      MultipartRequest multi = new MultipartRequest(request, savePath, maxSize, "UTF-8", new DefaultFileRenamePolicy());
    • new DefaultFileRenamePolicy 객체의 역할 : 우리가 동일한 파일을 다운로드받는 경우 파일명에 파일명(1), 파일명(2)등등 자동으로 구분할 수 있도록 rename해주는 역할을 한다. 즉 중복처리하는 객체.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%
//파일 업로드 => cos.jar (MultipartRequest 클래스)
//1. 파일이 저장되는 위치 (D:/upfile)
String savePath = "d:\\upfile";

//2. 파일 업로드 최대 크기 => 5MB
int maxSize = 5 * 1024 * 1024;

//3. MultipartRequest 객체생성
MultipartRequest multi = new MultipartRequest(request, savePath, maxSize,"UTF-8", new DefaultFileRenamePolicy());

//4. 파일 업로드 완료 확인
out.println("파일 업로드 완료!");
%>
  • 테스트하면 만들었던 upfile폴더안에 저장되어있다. 성!공!적!
  • 하지만 이 방법은 보안상 매우 위험하다
    • 누구나 주소를 입력하여 upfile폴더에 접근가능하기때문이다.
    • 이제 디비와 연결해보자.

4. 디비연결

위의 코드에서 아래 코드를 더 추가해야한다.

  • multi객체 안에 request가 있으므로 request.getParameter()가 아닌 multi.getParameter()로 가지고 와야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//6. 파일의 정보 + 글 정보를 디비에 저장
//6-1. 글정보저장하는 객체생성
BoardBean bb = new BoardBean();
//6-2. 전달되는 정보 저장
// bb.setName(request.getParameter("name")); enctype과 함께 쓸 수없다
bb.setName(multi.getParameter("name"));
bb.setPw(multi.getParameter("pw"));
bb.setSubject(multi.getParameter("subject"));
bb.setContent(multi.getParameter("content"));

// bb.setFile(multi.getParameter("file")); //DB에 파일저장 불가->아래코드로 진행
bb.setFile(multi.getFilesystemName("file"));
System.out.println("upfile에 저장된 파일명 : "+multi.getFilesystemName("file"));
System.out.println("사용자가 올린 원본 파일명 : "+multi.getOriginalFileName("file"));

//6-3. 따로 저장한 정보 저장
bb.setIp(request.getRemoteAddr());

//7. boardDAO객체생성
BoardDAO bdao = new BoardDAO();

//8. 글쓰기 메서드 호출
bdao.insertBoard(bb);

//9. 목록페이지로 이동
response.sendRedirect("boardList.jsp");

DB에도 잘 들어가는 걸 확인할 수 있다.
동일한 페이지 두번 작성하면 upfile에 저장된 파일명은 자동적으로 뒤에 숫자가 붙어 고유한 파일명으로 업로드가능하다.

[ITWILL : WEB]TEXT속성

[ITWILL : WEB]마진과 패딩, 인라인과 블록 차이

ITWILL학원 : 10강 WEB(HTML, CSS) BY 김영희

1. margin 마진

  • 테두리기준 바깥여백
  • margin왼쪽값과 오른쪽값이 서로 같으면 요소는 항상 중앙에 온다.
    • 따라서 margin의 좌우값을 auto를 지정
  • margin : 위 오른 아래 왼

2. padding 패딩

  • 테두기리준 안쪽여백
  • padding : 위 오른 아래 왼

3. margin과 padding 차이

https://uxengineer.com/padding-vs-margin/

4. inline과 block 차이

  • 인라인요소(한줄에 나란히 오는 요소, 줄바꿈이 없는 요소) :
    • 상하 margin과 height와 width 아예 적용 안됨
    • 좌우 margin가능
    • img태그와 input태그는 예외

https://dev.to/devansvd/did-you-know-html-elements-had-an-element-named-element-2al7

[ITWILL : JSP]Javabean 10 : 게시판만들기(답글쓰기)

ITWILL학원 : 30강 JSP기초 BY 정규태강사

1. 답글처리구현 순서

  • 일반글 = 답글이 아닌 글.
  • num : 일반글+답글 둘 다 작성할때마다 계속 증가된 값으로 사용(순차적)
  • re_ref : 답글의 그룹번호(일반글번호와 동일), 답글을 다는 원글의 그룹번호를 사용
  • re_lev (level) : 답글의 레벨값 (깊이), 원글을 기준으로한다 ex)한번내려가면1 두번내려가면 2
  • re_seq (sequence) : 답글의 순서, 원글에 답글이 몇개인지하는 순서대로 카운트.

글목록 예시

  • 일반글은 ref만 num값과 동일하게 초기화
  • 답글은 lev, seq는 0으로 초기화

만약 여기서 3번글의 답글 1개를 추가한다면?
6 / 답급3-1 / 3 / 1 / 1

만약 여기서 3번글의 답글 1개를 추가한다면?
6 / 답글3-1 / 3 / 1 / 1

만약 여기서 3번글의 답글 1개를 또 추가한다면?
7 / 답글3-2 / 3 / 1 / 2

만약 여기서 3번답글인 3-1에 답글 1개를 추가한다면?
8 / 답글3-1-1 / 3 / 2 / 3

만약 여기서 4번밑에 답글을 단다면?
9 / 답글4-1 / 4 / 1 / 1

만약 여기서 일반글 작성한다면?
10 / 일반글 / 10 / 0 / 0

만약 여기서 답글4-1에 답글을 단다면?
11 / 답글4-1-1 / 4 / 2 / 2

2. reWriteForm.jsp 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%
//한글처리
request.setCharacterEncoding("UTF-8");

//전달한 데이터 저장 (bno,re_ref, re_lev, &re_seq)
int bno = Integer.parseInt(request.getParameter("bno"));
int re_ref = Integer.parseInt(request.getParameter("re_ref"));
int re_lev = Integer.parseInt(request.getParameter("re_lev"));
int re_seq = Integer.parseInt(request.getParameter("re_seq"));

//어떻게 가져갈 것인가? 폼태그안에 담아갈까? 주소창에 GET방식으로 가져갈까?
//bean의 멤버변수에 있는 변수는 폼태그안에 담아가면된다. =>hidden 인풋태그
%>
<fieldset>
<legend>게시판 답글쓰기</legend>
<form action="reWritePro.jsp" method="post" name="fr">
<input type="hidden" name="bno" value="<%=bno%>">
<input type="hidden" name="re_ref" value="<%=re_ref%>">
<input type="hidden" name="re_lev" value="<%=re_lev%>">
<input type="hidden" name="re_seq" value="<%=re_seq%>">
글쓴이 : <input type="text" name="name" required><br>
비밀번호 : <input type="password" name="pw" required><br>
제목 : <input type="text" name="subject" maxlength="15" value="[답글] " required><br>
내용 : <br>
<textarea rows="10" cols="35" name="content" placeholder="여기에 답글을 작성해주세요" required></textarea><br>
<input type="submit" value="답글등록" class="btn">
<button type="reset" class="btn">초기화</button>
<input type="button" value="목록으로" class="btn" onclick="location.href='boardList.jsp'">
</form>
</fieldset>

3. reWritePro.jsp 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%
// 한글 처리
request.setCharacterEncoding("UTF-8");
// 액션태그 사용 파라미터값 저장
// useBean,setProperty
%>
<jsp:useBean id="bb" class="com.itwillbs.board.BoardBean"/>
<jsp:setProperty property="*" name="bb"/>
<%
System.out.println(bb);
// ip 정보 저장
bb.setIp(request.getRemoteAddr());

// BoardDAO 객체 생성 - reInsertBoard(객체)
BoardDAO bdao = new BoardDAO();
bdao.reInsertBoard(bb);

// 페이지 이동(글 목록)
response.sendRedirect("boardList.jsp");
%>

4. BoardDAO.java에서 reInsertBoard(BoardBean bb)메서드 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public void reInsertBoard(BoardBean bb){
//1.지역변수생성
int num = 0;
//2. 디비연결
try {
getCon();
//sql(게시판의 글번호 중 최댓값 계산) & pstmt
//2-1. 답글번호 계산
sql = "select max(bno) from itwill_board";
pstmt = con.prepareStatement(sql);
// 실행
rs = pstmt.executeQuery();
// 데이터 처리
if(rs.next()){
num = rs.getInt(1)+1;
}
System.out.println(" 답글 번호 : "+num);

//2-2. 답글 순서 재배치
//re_ref(같은 그룹기준)으로 re_seq값이 기존의 값보다 큰 값이 있을경우 seq값을 1증가시킴
sql = "update itwill_board set re_seq=re_seq+1 "
+ "where re_ref=? and re_seq>?";
pstmt = con.prepareStatement(sql);

pstmt.setInt(1, bb.getRe_ref());
pstmt.setInt(2, bb.getRe_seq());

pstmt.executeUpdate();

//2-3. 답글 추가 동작
sql="insert into itwill_board "
+ "values(?,?,?,?,?"
+ ",?,?,?,?,now()"
+ ",?,?)";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, num);//bno는 가지고있는게 아니라 계산해서 만든것이므로 num사용
pstmt.setString(2, bb.getName());
pstmt.setString(3, bb.getPw());
pstmt.setString(4, bb.getSubject());
pstmt.setString(5, bb.getContent());
pstmt.setInt(6, bb.getReadcount());
pstmt.setInt(7, bb.getRe_ref()); //기존 원글의 그룹번호와 동일
pstmt.setInt(8, bb.getRe_lev()+1); // 기존의 값 + 1
pstmt.setInt(9, bb.getRe_seq()+1); // 기존의 값 + 1
pstmt.setString(10, bb.getFile());
pstmt.setString(11, bb.getIp());

//3. 실행
pstmt.executeUpdate();
System.out.println("답글쓰기 성공");
} catch (Exception e) {
e.printStackTrace();
} finally {
closeDB();
}
} //reInsertBoard닫음

5. BoardList.jsp에서 답글 들여쓰기 추가 구현

이미지와 조건문을 통해 구현

  • img src=”level.gif” width=”<%=wid%>” height=”15”
  • img src=”re.gif”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<fieldset>
<legend>땡땡게시판</legend>
<div id="contents">
게시판 총 글의 수 : <%=cnt%> 개
<%
if(id == null){
%>
<input class="btn" type="button" value="로그인" onclick="location.href='../member/main.jsp'">
<%
}else if( id != null){
%>
<input class="btn" type="button" value="로그아웃" onclick="location.href='../member/logout.jsp'">
<input class="btn" type="button" value="글쓰기" onclick="location.href='writeForm.jsp'">
<%
}
%>
<br>
<table>
<tr>
<th>글번호</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>작성일</th>
<th>IP</th>
</tr>
<%//반복문
//ArrayList는 가변배열이므로 length가 없고 size가 존재한다.
//size()메서드는 배열의 요소의 갯수를 리턴
for(int i=0;i<boardList.size(); i++){
//ArrayList 한칸의 정보 ->BoardBean 객체 하나로 이동
BoardBean bb = (BoardBean) boardList.get(i);
%>
<tr>
<td><%=bb.getBno()%></td>
<td>
<%
//답글일때만 이미지넣기
//변수 wid를 이용하여 들여쓰기 처리
int wid = 0;
if(bb.getRe_lev() > 0){
wid= 10 * bb.getRe_lev(); //레벨값의 10을 곱한 값만큼 이미지 가로길이를 길게해줌
%>
<img src="level.gif" width="<%=wid%>" height="15">
<img src="re.gif">
<% } %>
<a href="content.jsp?bno=<%=bb.getBno()%>&pageNum=<%=pageNum%>"><%=bb.getSubject()%></a></td>
<td><%=bb.getName()%></td>
<td><%=bb.getReadcount()%></td>
<td><%=bb.getDate()%></td>
<td><%=bb.getIp()%></td>
</tr>
<%
}
%>
</table>
</div>
</fieldset>

[ITWILL : JAVA]내부클래스

ITWILL학원 : 20강 JAVA BY 윤미영강사

1. 내부클래스(p363)

  • 클래스가 서로 밀접한 관련이 있을때 객체생성없이 사용하기 용이하다
  • 주로 이미 만들어진 내부클래스라이브러리를 사용.
  • 공통점 : static 변수는 가질 수 없지만, final static 변수는 가질 수 있다.
    • 만약 상수가 아닌 변수형태로 선언이 되어있다면 그건 앞에 final이 생략되어진 것임.
    • 변수를 사용하고싶다면 차라리 외부클래스의 멤버변수로 선언하는 것이 낫다.
  • 내부클래스의 종류 4가지 :

https://github.com/burberry-check/java-basic/wiki/%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4,-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4

2. 인스턴스 내부클래스

  • 내부클래스를 외부클래스의 멤버변수 선언위치에 선언하는 것.

  • 인스턴스내부클래스는 외부클래스의 멤버변수를 자신의 멤버변수처럼 불러와서 쓸 수 있다.

  • 단독으로 사용하기도한다.

  • 내부클래스의 메서드 사용하는 방법 :

    1. 외부클래스 객체생성 : 외부클래스명 A = new 외부클래스명();
    2. 내부클래스 객체생성 : 내부클래스명 B = A.new 내부클래스명();
    3. 내부클래스의 메서드 사용 : B.method();
    • 이러한 것을 단독사용이라고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class InstanceInner {
private int outerMember = 1;

private void outerMethod(){
Inner inner = new Inner();
inner.innnerMember = 11;
inner.innerMethod();
}

class Inner{
int innnerMember = 10;
//static int staticMember = 10; staitc멤버변수 선언 불가. 만약 상수가 아닌 변수형태로 선언이 되어있다면 그건 앞에 static이 생략되어진 것임.
static final int finalStaticMember = 20; //단 static final형태는 선언할 수 있다.

private void innerMethod(){
System.out.printf("out : %d, inner %d%n", outerMember, innnerMember);
}
}

//내부클래스 메서드 사용은 어떻게 할까?
public static void main(String[] args) {
InstanceInner ilt = new InstanceInner(); //1.외부클래스객체생성
Inner inner = ilt.new Inner(); //2.내부클래스객채생성
inner.innerMethod(); //3.내부클래스의 메서드사용

}
}

3. 지역클래스(local class)

  • 내부클래스가 외부클래스의 메서드안에서 정의되어지는 것.
  • 지역클래스는 단독으로 사용하지않고 내부에서 객체생성 후 호출해서 사용.
    • 단독사용의 의미 : 직접 메서드를 불러와서사용,
    • 지역클래스인 경우 메서드를 호출하는 순간 객채생성하고 바로 사용할 수 있게해야한다. 즉 단독사용처럼 직접 클래스이름을 불러 사용하는 것이 아니라 지역클래스가 속한 외부클래스메서드를 부르면서 그 속에 있는 클래스가 자동으로 사용되게끔 하는 것이다.
1
2
3
4
5
6
7
8
9
10
class OuterClass(){
...
public methodA(){ //멤버메서드
class InnerClass(){ //로컬클래스
...
}
}
}

//객체생성 후 호출해서 주로 사용

4. 익명클래스(p373)

  • 인터페이스를 구현할 목적으로 사용.
  • 실무에서 자주 사용.
  • 익명클래스의 override는 인터페이스 규칙에 의해 public을 붙여줘야한다
  • 익명클래스를 어떨때 사용할까?
    • 독립적인 클래스 사용 : 인터페이스를 구현한 클래스를 여러번 사용할때 용이.Intf a는 언제어디서나 불러서 사용가능
    • 익명클래스 사용 : 인터페이스를 구현한 클래스를 딱 한번 사용할때 용이. 익명클래스는 Intf b에서만 사용가능. 이름이 없기때문에 불러서 사용할 수 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Intf{
void methodInf();
}

class A implements Intf{
@Override
public void methodInf() {
System.out.println("인터페이스 메서드 오버라이딩");
}
}

public class test {

public static void main(String[] args){

//기본적인 interface구현(독립적인 클래스사용)
Intf a = new A();
a.methodInf();

//익명클래스를 이용한 interface구현
Intf b = new Intf(){
@Override
public void methodInf() {
System.out.println("익명클래스로 인터페이스 메서드 오버라이딩");
}
};
b.methodInf();
}
}

5. 내부클래스 예시

다양한 내부클래스를 생성한 뒤 main메서드로 호출해보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
interface InfoPrintable{
public void printInfo();
}

public class VariousInnerClassTest {

//멤버메서드(매개변수가 인터페이스)
public void useInnerClass(InfoPrintable inner){
inner.printInfo();
}

//멤버메서드
public void useSpeakerTest(){

//로컬 내부클래스생성(메서드안에 선언한 내부클래스=로컬클래스)
class EngSpeaker implements InfoPrintable{
@Override
public void printInfo() {
System.out.println("로컬 내부 클래스");
}
}//EngSpeaker닫음

//멤버메서드사용하여 클래스 부르기
//1. 익명의 내부클래스 생성
useInnerClass(new InfoPrintable(){
@Override
public void printInfo() {
System.out.println("익명의 내부클래스");
}
});

//2. 로컬내부클래스부르기
useInnerClass(new EngSpeaker());

//3. 인스턴스내부클래스부르기
useInnerClass(new InstanceInnerClass());

//4. 걍 밖에있는클래스
useInnerClass(new SomeClass());

}//useSpeakerTest메서드닫음

//인스턴스 내부클래스(외부클래스의 멤버에 생성)
class InstanceInnerClass implements InfoPrintable{
@Override
public void printInfo() {
System.out.println("인스턴스 내부클래스 사용");
}

}//InstanceInnerClass닫음


//만든 내부클래스를 main메서드로 호출해보자
public static void main(String[] args){

VariousInnerClassTest vt = new VariousInnerClassTest();
vt.useSpeakerTest();

}

}//VariousInnerClassTest닫음

//걍 다른 클래스인데 인터페이스구현
class SomeClass implements InfoPrintable{

@Override
public void printInfo() {
System.out.println("걍 상관없는 일반 클래스");
}

}

[ITWILL : JAVA]로깅, 디버깅

ITWILL학원 : 20강 JAVA BY 윤미영강사

1. 로깅(Logging)

  • 로깅 : 사용자의 요청사항을 출력하는 것 이외에 애플리케이션에서 발생하는 사건들을 기록하는 것.

참고링크 : java.util.Logging.Logger의 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SimpleLoggingFile {

private final static Logger logger = Logger.getLogger(SimpleLoggingFile.class.getName());

public static void main(String[] args) throws SecurityException, IOException {
FileHandler fHandler = new FileHandler("mylog_%g.log");
logger.addHandler(fHandler);
logger.log(Level.INFO, "INTO LOG");
logger.info("메서드 타입 인포 로그");
logger.warning("메서드 타입 경고 로그");
logger.severe("메서드 타입 시비어 로그");
}

}

//출력값
723, 2020 11:11:40 오전 ch9.SimpleLoggingFile main
정보: INTO LOG
723, 2020 11:11:40 오전 ch9.SimpleLoggingFile main
정보: 메서드 타입 인포 로그
723, 2020 11:11:40 오전 ch9.SimpleLoggingFile main
경고: 메서드 타입 경고 로그
723, 2020 11:11:40 오전 ch9.SimpleLoggingFile main
심각: 메서드 타입 시비어 로그
  • 더불어 mylog_0.log파일이 생성되었다.

  • 출력된 로그는 콘솔과 함께 파일에도 기록된다.

  • 실무에서는 logging패키지의 클래스보다 주로 logback과 같은 별도의 로깅 프레임워크들을 사용함.

2. 디버깅

  • 리커시브방법 : 함수가 자기 자신을 계속 호출하는 구조 = 재귀함수

  • break point : 프로그램 동작 중 디버깅을 위해서 동작을 멈추는 지점

    • 중단점을 지정해 놓으면 그 전까지 프로그램이 동작하다가 브레이크포인트에서 사용자의 다음동작을 기다린다.
    • step into를 하면 변수뷰창에서 valables가 어떻게 변화하는지 알수있다.

[ITWILL : JSP]Javabean 9 : 게시판만들기(글삭제하기)

ITWILL학원 : 30강 JSP기초 BY 정규태강사

1. 글삭제하기기능구현 순서

  1. deleteForm.jsp 작성
  2. deletePro.jsp 작성
  3. BoardDAO.java에서 bdao.deleteBoard(bno, pw)메서드 생성
  4. 기존의 boardList.jsp에 id가 있을 경우 추가
  5. 기존의 content.jsp 로그인상태에 따라 다르게 보이는 버튼 추가

2. deleteForm.jsp 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%
//1. 한글처리
request.setCharacterEncoding("UTF-8");

//2. 파라미터저장
int bno = Integer.parseInt(request.getParameter("bno"));
String pageNum = request.getParameter("pageNum");

%>
<fieldset>
<legend>게시글 삭제</legend>
<form action="deletePro.jsp?pageNum=<%=pageNum%>" method="post">
<input type="hidden" name="bno" value="<%=bno%>">
비밀번호 : <input type="password" name="pw"><br>
<input type="submit" value="글삭제하기" class="btn">
<input type="button" value="뒤로가기" class="btn" onclick="history.back()">
</form>
</fieldset>

3. deletePro.jsp 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<%
//1. 한글처리
request.setCharacterEncoding("UTF-8");
//2. 파라미터저장(액션태그사용 & pageNum은 파라미터로)
int bno = Integer.parseInt(request.getParameter("bno"));
String pageNum = request.getParameter("pageNum");
String pw = request.getParameter("pw");
//3. DAO생성
BoardDAO bdao = new BoardDAO();
//4. 정보수정메서드 updateBoard(bb)
// -> 결과를 정수형 데이터로 리턴 (1=>정상처리, 0=>비번오류, -1=>해당글없음)
int result = bdao.deleteBoard(bno, pw);
if(result == 1){
%>
<script type="text/javascript">
alert("정상적으로 글 삭제되었습니다");
location.href="boardList.jsp";
</script>
<%
}else if(result == 0){
%>
<script type="text/javascript">
alert("비밀번호 오류");
history.back();
</script>
<%
}else{
%>
<script type="text/javascript">
alert("존재하지않는 게시글입니다");
history.back();
</script>
<%
}
%>

3. BoardDAO.java에서 bdao.deleteBoard(bno, pw)메서드 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public int deleteBoard(int bno, String pw){
int result = -1;

try {
getCon();
sql="select pw from itwill_board where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, bno);
rs = pstmt.executeQuery();
if(rs.next()){
if(pw.equals(rs.getString("pw"))){
sql="delete from itwill_board where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, bno);
pstmt.executeUpdate();
System.out.println("글삭제 성공");
result = 1;
}else{
result = 0;
System.out.println("글삭제 중 비번오류");
}
}else{
result = -1;
System.out.println("글삭제 중 select오류");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
closeDB();
}
return result;
}//deleteBoard()닫음

4. 기존의 main.jsp에 게시판바로가기 버튼 추가

글삭제의 경우 아무나 할 수 없어야한다.
따라서 로그인한 사람만이 글을 쓰고 글을 삭제할 수 있도록 구현해보자.
이전에 구현했던 main.jsp에 게시판글바로볼 수 있는 버튼을 추가하고 location.href='../board/boardList.jsp을 연결해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%//1.한글처리, 파라미터 
request.setCharacterEncoding("UTF-8");
String id = (String) session.getAttribute("id");//object를 string으로 다운캐스팅
String name = request.getParameter("name");
//1-1. id없이는 진입불가, id없는 경우 로그인페이지로 이동
if(id == null){
response.sendRedirect("loginForm.jsp");
}
%>
<h2><%=id %>님 환영합니다.</h2>
<%=name %><br>

<input type="button" value="ITWILL게시판가기" class="btn" onclick="location.href='../board/boardList.jsp'">
<input type="button" value="회원정보조회" class="btn" onclick="location.href='memberinfo.jsp'">
<input type="button" value="회원정보수정" class="btn" onclick="location.href='updateForm.jsp'">
<input type="button" value="로그아웃" class="btn" onclick="location.href='logout.jsp'">
<input type="button" value="회원탈퇴" class="btn" onclick="location.href='deleteForm.jsp'">

<!-- 관리자일때만 메뉴확인가능 -->
<% if(id != null){
if(id.equals("admin")){ %>
<input type="button" value="회원전체목록(관리자용)" class="btn" onclick="location.href='memberList.jsp'">
<%
}
}
%>

5. 기존의 boardList.jsp에 id가 있을 경우 추가

  • id가 없을 경우 : 로그인버튼만 표시
  • id가 있을 경우 : 로그아웃, 글쓰기버튼 표시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<fieldset>
<legend>땡땡게시판</legend>
<div id="contents">
게시판 총 글의 수 : <%=cnt%>
<%
if(id == null){
%>
<input class="btn" type="button" value="로그인" onclick="location.href='../member/main.jsp'">
<%
}else if( id != null){
%>
<input class="btn" type="button" value="로그아웃" onclick="location.href='../member/logout.jsp'">
<input class="btn" type="button" value="글쓰기" onclick="location.href='writeForm.jsp'">
<%
}
%>
<br>
<table>
<tr>
<th>글번호</th>
<th>제목</th>
<th>작성자</th>
<th>조회수</th>
<th>작성일</th>
<th>IP</th>
</tr>
<%//반복문
//ArrayList는 가변배열이므로 length가 없고 size가 존재한다.
//size()메서드는 배열의 요소의 갯수를 리턴
for(int i=0;i<boardList.size(); i++){
//ArrayList 한칸의 정보 ->BoardBean 객체 하나로 이동
BoardBean bb = (BoardBean) boardList.get(i);
%>
<tr>
<td><%=bb.getBno()%></td>
<td><a href="content.jsp?bno=<%=bb.getBno()%>&pageNum=<%=pageNum%>"><%=bb.getSubject()%></a></td>
<td><%=bb.getName()%></td>
<td><%=bb.getReadcount()%></td>
<td><%=bb.getDate()%></td>
<td><%=bb.getIp()%></td>
</tr>
<%
}
%>
</table>
</div>
</fieldset>

6. 기존의 content.jsp 로그인상태에 따라 다르게 보이는 버튼 추가

  • 아이디가 있을 경우
    • 로그아웃버튼, 글쓰기버튼이 보임
    • 글 수정 및 삭제, 답글쓰기 버튼 보임
  • 아이디가 없을 경우
    • 로그인버튼만 보임. 로그아웃,글쓰기버튼 안보임
    • 글 수정 및 삭제, 답글쓰기버튼 안보임
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<fieldset>
<legend>글 내용 보기</legend>
<table border="solid,1px">
<tr>
<th>글번호</th>
<td><%=bno%></td>
<th>조회수</th>
<td><%=bb.getReadcount()%></td>
</tr>
<tr>
<th>작성자</th>
<td><%=bb.getName()%></td>
<th>작성일</th>
<td><%=bb.getDate()%></td>
</tr>
<tr>
<th>제목</th>
<td colspan="3"><%=bb.getSubject()%></td>
</tr>
<tr>
<th>첨부파일</th>
<td colspan="3"><%=bb.getFile()%></td>
</tr>
<tr>
<th>내용</th>
<td colspan="3" height="300px"><%=bb.getContent()%></td>
</tr>
<tr>
<td colspan="4" style="text-align:center">

<% //현재페이지에 로그인 정보가 없거나 글쓴이 이름과 아이디가 다를 경우 수정,삭제버튼을 숨긴다
//1.세션값 가져오기
String id = (String) session.getAttribute("id");//objectstring으로 다운캐스팅
//2. 아이디가 존재하면서 이름과 아이디가 같은 경우
if( id != null && id.equals(bb.getName())){
%>
<input type="button" value="글수정" class="btn" onclick="location.href='updateForm.jsp?bno=<%=bb.getBno()%>&pageNum=<%=pageNum%>'">
<input type="button" value="글삭제" class="btn" onclick="location.href='deleteForm.jsp?bno=<%=bb.getBno()%>&pageNum=<%=pageNum%>'">
<input type="button" value="답글쓰기" class="btn" onclick="location.href='reWriteForm.jsp?bno=<%=bb.getBno()%>&re_ref=<%=bb.getRe_ref() %>&re_lev=<%=bb.getRe_lev()%>&re_seq=<%=bb.getRe_seq()%>'">
<%
}
%>
<input type="button" value="목록으로" class="btn"
onclick="location.href='boardList.jsp?pageNum=<%=pageNum%>'">
<!-- location.href='boardList.jsp만 하면 5페이지보고있다가 다시 1페이지로 돌아가버린다
이때 historyback하면 조회수가 올라가지않는다.
따라서 pageNum을 가져와서 사용하면된다
-->
</td>
</tr>
</table>
</fieldset>

[ITWILL : JSP]Javabean 8 : 게시판만들기(글내용보기, 글수정하기)

ITWILL학원 : 29강 JSP기초 BY 정규태강사

1. 순서

  1. content.jsp 생성
    • html 테이블작성
  2. boardDAO.java에 updateReadCount(int bno)메서드 생성
  3. boardDAO.java에 getBoard(int bno)메서드 생성
  4. 글수정할 수 있는 updateForm.jsp 생성
  5. 글수정할 수 있는 updatePro.jsp 생성
  6. boardDAO.java에 updateBoard(bb)메서드 생성

2. content.jsp 생성

글목록에서 제목을 눌렀을때 컨텐츠를 볼 수 있게 만드는 페이지이다.

  • 목록으로 되돌아가는 버튼 만들때 주의점
    • location.href=boardList.jsp만 하면 5페이지보고있다가 다시 1페이지로 돌아가버린다
    • 이때 historyback하면 조회수가 올라가지않는다.
    • 따라서 pageNum을 가져와서 사용하면된다
    • location.href=boardList.jsp?pageNum=<%=pageNum%>으로 해줘야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<%
//1. html테이블작성

//글번호(pk제약조건의 컬럼필수)에 해당하는 글의 정보를 가져오기
//2. 전달된 데이터 저장(bno, pageNum)

int bno = Integer.parseInt(request.getParameter("bno"));
String pageNum = request.getParameter("pageNum");

//3. DAO객체생성
BoardDAO bdao = new BoardDAO();

//4. 글의 조회수정보를 1증가 : DB에서처리 -> DAO객체에 updateReadCount(bno)메서드 생성
bdao.updateReadCount(bno);

//5. 화면(테이블)에 출력 (getBoard(bno))
// DAO객체에 글정보를 가져오는 메서드 생성
BoardBean bb = bdao.getBoard(bno);
//System.out.println(bdao.getBoard(bno));
%>

<fieldset>
<legend>글 내용 보기</legend>
<table border="solid,1px">
<tr>
<th>글번호</th>
<td><%=bno%></td>
<th>조회수</th>
<td><%=bb.getReadcount()%></td>
</tr>
<tr>
<th>작성자</th>
<td><%=bb.getName() %></td>
<th>작성일</th>
<td><%=bb.getDate() %></td>
</tr>
<tr>
<th>제목</th>
<td colspan="3"><%=bb.getSubject() %></td>
</tr>
<tr>
<th>첨부파일</th>
<td colspan="3"><%=bb.getFile() %></td>
</tr>
<tr>
<th>내용</th>
<td colspan="3" height="300px"><%=bb.getContent() %></td>
</tr>
<tr>
<td colspan="4" style="text-align:center">
<input type="button" value="글수정" class="btn" onclick="location.href='updateForm.jsp?bno=<%=bb.getBno()%>&pageNum=<%=pageNum%>'">
<input type="button" value="글삭제" class="btn">
<input type="button" value="답글쓰기" class="btn">
<input type="button" value="목록으로" class="btn"
onclick="location.href='boardList.jsp?pageNum=<%=pageNum%>'">
<!-- location.href='boardList.jsp만 하면 5페이지보고있다가 다시 1페이지로 돌아가버린다
이때 historyback하면 조회수가 올라가지않는다.
따라서 pageNum을 가져와서 사용하면된다
-->
</td>
</tr>
</table>
</fieldset>

3. boardDAO.java에 updateReadCount(int bno)메서드 생성

조회수 올리는 메서드를 생성하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void updateReadCount(int bno){
try {
//1. 디비연결
getCon();
//2. sql작성 & pstmt생성
//select를 안하는 이유? 기존의 데이터를 굳이 가져올 필요가 없기 때문
sql = "update itwill_board set readcount=readcount+1 where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, bno);
//3. 실행
pstmt.executeUpdate();
System.out.println("조회수 1증가 완료");
} catch (Exception e) {
System.out.println("조회수 1증가 실패");
e.printStackTrace();
} finally {
//4. 자원해제
closeDB();
}
}

4. boardDAO.java에 getBoard(int bno)메서드 생성

글 내용을 다 가져와서 오는 메서드를 만들어보자
이는 글제목을 클릭시 화면에 내용을 보여줄때 사용된다.

  • 내코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public BoardBean getBoard(int bno){
BoardBean bb = new BoardBean();
try {
//1. 디비연결
getCon();
//2. sql작성, pstmt
//sql = "select * from itwill_board";
sql = "select * from itwill_board where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, bno);
//3. 실행 -> rs저장
rs = pstmt.executeQuery();
//4. 데이터처리
if(rs.next()){
//4-1.BoardBean 객체생성해서 그 안에 rs데이터 저장
bb.setBno(rs.getInt("bno"));
bb.setContent(rs.getString("content"));
bb.setDate(rs.getDate("date"));
bb.setFile(rs.getString("file"));
bb.setIp(rs.getString("ip"));

bb.setName(rs.getString("name"));
bb.setPw(rs.getString("pw"));
bb.setRe_lev(rs.getInt("re_lev"));
bb.setRe_ref(rs.getInt("re_ref"));
bb.setRe_seq(rs.getInt("re_seq"));

bb.setReadcount(rs.getInt("readcount"));
bb.setSubject(rs.getString("subject"));
//여기까지가 한 행의 데이터를 저장한 것임

}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeDB();
}
return bb;
}//getBoard닫음
  • 강사님코드
    • BoardBean객체를 언제 생성하는 지가 내 코드와 달랐다. BoardBean객체를 미리 생성할 필요가 없었다.
    • try catch로 예외가 발생하거나, rs에 데이터가 없는 경우에는 객체를 생성할 필요가 없기 때문이다.
    • 글정보 저장완료 후 리턴값 bb정보를 습관적으로 확인하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//글 정보를 가져오는 메서드구현
public BoardBean getBoard(int bno){
//rs에 데이터가 없으면 객체생성할 필요가 없음. 따라서 객채생성안하고 null함.
BoardBean bb = null;
try {
//1. 디비연결
getCon();
//2. sql작성, pstmt
//sql = "select * from itwill_board";
sql = "select * from itwill_board where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, bno);
//3. 실행 -> rs저장
rs = pstmt.executeQuery();
//4. 데이터처리
if(rs.next()){
//4-1.BoardBean 객체생성해서 그 안에 rs데이터 저장
// 글내용에서 보이지 않는 컴럼들도 만들어야할까?
// 만들기를 추천함 why? 매개변수인 bno자체가 pk를 가졌다.
// 지금뿐만아니라 다음 기능이 필요할때 불러와서 사용할 수 있다.
// 재사용성
bb = new BoardBean();
bb.setBno(rs.getInt("bno"));

(중략)

bb.setSubject(rs.getString("subject"));
//여기까지가 한 행의 데이터를 저장한 것임
}
//글정보 저장완료 후 정보확인
System.out.println("해당 글 저장 완료");
System.out.println(bb);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeDB();
}
return bb;
}//getBoard닫음

5. updateForm.jsp생성

글을 수정할수있는 페이지를 만들어보자.

  • 아래 태그 두개는 동일할까?

    • <input type="button" value="목록으로" class="btn" onclick="location.href='boardList.jsp?bno=<%=bno%>'">
    • <input type="button" value="목록으로" class="btn" onclick="location.href='boardList.jsp?bno=<%=bb.getBno()%>'">
  • YES! 동일하다!

  • 인풋 히튼타입 : 화면에는 보이지않지만 데이터 저장 및 전달이 가능해서 데이터 이동용으로 주로 사용

  • BoardBean안에 포함되지 않는 pageNum 변수등은 어떻게 전달할까?

    • 액션페이지 주소줄에 get방식으로 전달
    • 만약 (BoardBean에 포함되지않은) 전달될 데이터가 개인정보등 중요한 데이터라면 get방식으로 전달해선 절대 안된다(=>정보유출문제발생)
      • 그럼 중요한 정보들은 전달할수없는걸까? CAN! 1. 클로저이용 또는 2.BoardBean구조자체를 변경
    • 결론: 가능하면 BoardBean형태의 데이터만 저장해서 처리할 수 있도록 해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%//1. 파라미터저장
int bno = Integer.parseInt(request.getParameter("bno"));
String pageNum = request.getParameter("pageNum");

BoardDAO bdao = new BoardDAO();
BoardBean bb = bdao.getBoard(bno);
%>

<fieldset>
<legend>게시판 글쓰기</legend>
<form action="updatePro.jsp?pageNum=<%=pageNum %>" method="post" name="fr">
<!-- 인풋 히튼타입 : 화면에는 보이지않지만 데이터 저장 및 전달이 가능해서 데이터 이동용으로 주로 사용 -->
<input type="hidden" name="bno" value="<%=bb.getBno() %>">
글쓴이 : <input type="text" name="name" required value="<%=bb.getName() %>"><br>
비밀번호 : <input type="password" name="pw" required ><br>
제목 : <input type="text" name="subject" required value="<%=bb.getSubject() %>"><br>
내용 : <br>
<textarea rows="10" cols="35" name="content" placeholder="여기에 작성해주세요" required>
<%=bb.getContent() %></textarea><br>
<input type="submit" value="수정하기" class="btn">
<button type="reset" class="btn">초기화</button>
<input type="button" value="목록으로" class="btn" onclick="location.href='boardList.jsp'">
</form>
</fieldset>

6. updatePro.jsp생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<%//1. 한글처리
request.setCharacterEncoding("UTF-8");
//2. 파라미터저장(액션태그사용 & pageNum은 파라미터로)
String pageNum = request.getParameter("pageNum");
%>
<jsp:useBean id="bb" class="com.itwillbs.board.BoardBean"></jsp:useBean>
<jsp:setProperty property="*" name="bb"/>
<% //3. DAO생성
BoardDAO bdao = new BoardDAO();
//4. 정보수정메서드 updateBoard(bb)
// -> 결과를 정수형 데이터로 리턴 (1=>정상처리, 0=>비번오류, -1=>해당글없음)
int result = bdao.updateBoard(bb);
if( result == 1){
%>
<script type="text/javascript">
alert("글 수정 완료!");
location.href="boardList.jsp?pageNum=<%=pageNum%>";
</script>
<%
}else if(result == 0){
%>
<script type="text/javascript">
alert("비번이 일치하지않습니다");
history.back();
</script>
<%
}else{
%>
<script type="text/javascript">
alert("존재하지 않는 글입니다");
history.back();
</script>
<%
}
%>

7. boardDAO.java에 updateBoard(bb)메서드 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public int updateBoard(BoardBean bb){
int result = 0;

try {
//DB연결
getCon();
System.out.println("디비연결완료");
//sql & pstmt
sql = "select pw from itiwill_board where bno=?";
pstmt = con.prepareStatement(sql);
System.out.println("pstmt 객체생성완료");
pstmt.setInt(1, bb.getBno());

rs = pstmt.executeQuery();
System.out.println("pstmt 실행");
if(rs.next()){
//비번비교 후 일치하면 update 쿼리실행
if(bb.getPw().equals(rs.getString("pw"))){
sql="update itwill_board set name=?, subject=?, content=? where bno=?";
pstmt = con.prepareStatement(sql);
pstmt.setString(1, bb.getName());
pstmt.setString(2, bb.getSubject());
pstmt.setString(3, bb.getContent());
pstmt.setInt(4, bb.getBno());
pstmt.executeUpdate();
System.out.println("글수정완료 - 비번일치");
//리컨값 변경
result =1;
}else{//비번불일치
System.out.println("글수정실패 - 비번불일치");
result = 0;
}
}else{
System.out.println("글수정실패 - 해당글없음");
result = -1;
}

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
closeDB();
}
return result;
}//updateBoard닫음