문제

대표값


N명의 학생의 수학점수가 주어집니다. N명의 학생들의 평균(소수 첫째자리 반올림)을 구하고,
N명의 학생 중 평균에 가장 가까운 학생은 몇 번째 학생인지 출력하는 프로그램을 작성하세
요.


평균과 가장 가까운 점수가 여러 개일 경우 먼저 점수가 높은 학생의 번호를 답으로 하고, 높
은 점수를 가진 학생이 여러 명일 경우 그 중 학생번호가 빠른 학생의 번호를 답으로 합니다.


▣ 입력설명
첫줄에 자연수 N(5<=N<=100)이 주어지고, 두 번째 줄에는 각 학생의 수학점수인 N개의 자연
수가 주어집니다. 학생의 번호는 앞에서부터 1로 시작해서 N까지이다.


▣ 출력설명
첫줄에 평균과 평균에 가장 가까운 학생의 번호를 출력한다.
평균은 소수 첫째 자리에서 반올림합니다.


▣ 입력예제 1
10
45 73 66 87 92 67 75 79 75 80


▣ 출력예제 1
74 7


예제설명)
평균이 74점으로 평균과 가장 가까운 점수는 73(2번), 75(7번), 75(9번)입니다. 여기서 점수가 높은
75(7번), 75(9번)이 답이 될 수 있고, 75점이 두명이므로 학생번호가 빠른 7번이 답이 됩니다.


내 풀이

# 내 풀이
n = int(input())
arr = list(map(int,input().split()))
sum = 0
# 평균 구하기
for x in arr :
    sum += x
avg = round(sum/n)

#print(avg)
# 차이 구하기
diffArr = []
for i in range(n):
    diffArr.append(abs(avg-arr[i]))

#print(diffArr)
# 절대값의 최소값 구하기
arrMin = float('inf')
index = -1
for i in range(n):
    if diffArr[i] < arrMin:
        arrMin = diffArr[i]
        index = i
#print(arrMin)
#print(-arrMin)
#print(index)

#절대값이 최소값인 것 찾기
result = 0
for i in range(n):
    if diffArr[i] == -arrMin:
        result = i + 1
        break
else:
    result = index + 1

#print(result)

print(avg, result)

점수


강사님 풀이

#import sys
#sys.stdin=open("input.txt", "r")
n = int(input())
a = list(map(int,input().split()))
min = 2147000000

#평균 구하기
ave = round(sum(a)/n) # sum 은 리스트의 모든 값 합해

#평균과 가까운 값 탐색
for idx, x in enumerate(a): # a 리스트의 값 (0,a[0]) 이 반환 idx = 0, x = a[0] 이 됨
    tmp = abs(x-ave)
    if tmp < min :
        min = tmp
        score = x # 차이의 절대값이 가장 작은 값일때 점수를 저장
        res = idx + 1 # 그때의 학생번호 
    elif tmp == min : # 같은
        if x > score : # 현재 점수가 저번에 저장된 점수보다 크면
            score = x
            res = idx + 1
print(ave,res)

# 절대값이 작은 값일땐 계속 작은값을 넣는식, 절대값이 같은 값일땐 큰값을 넣음
# 73 75 73 75 일때, 평균이 74
# 처음에 x 가 73일때 1이 min 이 된다, tmp 는 1
# 다음에 x 가 75알때 tmp 가 1 이므로 elif 에서 75 > score인 73 이므로 75가 score 가 됨
# 다음에 x 가 73일때 tmp 가 1 이므로 elif 에서 73 > score인 75 false 이므로 넘어감
# 다음에 x 가 75일때 tmp 가 1 이므로 elif 에서 75 > score인 75 false 이므로 넘어감
# 답은 가장 앞에 있는 75

# enumerate 활용

# 최대값, 최소값 구하기

# 대표값 문제 오류 수정, 반올림 round_half_up 하기

# 대표값 문제 오류 수정
# 우리가 아는 반올림은 round_half_up 방식 ( 5 이상 올림 )
# Python 의 round 는 round_half_even 방식을 택한다

# round_half_even 방식
a = 4.500
print(round(a)) # 4
# 정확히 하프 지점에 있으면 짝수(even)값으로 근사값 해줌
# ex) 4 와 5 의 하프지점에 있으므로 4로 간다

a = 4.5111 # 정확한 하프가 아니면 올라간다
print(round(a)) # 5

a = 5.5000
print(round(a)) # 6 (짝수로 간다)

a = 66.5
print(round(a)) # 66

# 소수 첫째자리에서 무조건 반올림 하는 법

a = 66.5
a = a + 0.5
# 66.5 여도 0.5 더하면 67, 66.6 이어도 0.5 더하면 67.1 로 자리올림 일어남
a=int(a) # 소수점 없어짐, 내림됨
print(a) # 처음의 a 에서 반올림한 67이 출력

# 66.4 이면 0.5 더해도 66.9 -> 66 이므로 올림안됨, 맞음

복습

- 목록 가져오기 Service 클래스에서 목록 추출 후 결과를 공유설정

- View 페이지에서 EL 로 출력하기 전에 공유설정되어야함

 

Service 클래스에서 DTO 객체가 공유되면

		request.setAttribute("board", board);

- View 페이지에서 ${공유되는 네임값.필드명} 으로 출력

		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value="${board.board_name}"/>
		</td>

 

Spring 학습 전 앞으로 할 내용

Maven

- 라이브러리 관리자 중 하나

- Spring 프로젝트를 만들면 Maven 으로 라이브러리 관리하는게 기본

 

ORM 프레임워크 : MyBatis

- 기존의 DAO 클래스 안에 있는 내용을 xml 에 따로 뺀다, 간단해짐

- Spring 은 MyBatis 와 연동하는 경우가 많다


- 수정 폼, Controller 클래스에서 연결하는것도 다 구현했다

- 글 수정 Service 클래스, 수정하는 DAO 메소드만 구현하면 된다

 

 게시판 프로그램 : 글 수정 Service 클래스

- service 패키지 안에 글 수정 Service 클래스인 BoardModifyjava.java 를 작성하자

- BoardModify.java (수정 후 1)

package service;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardModify implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardModify");
		
		response.setContentType("text/html; charset=utf-8");
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		String board_pass = request.getParameter("board_pass");
		
		BoardBean board = new BoardBean();
		board.setBoard_num(board_num);
		board.setBoard_name(request.getParameter("board_name"));
		board.setBoard_subject(request.getParameter("board_subject"));
		board.setBoard_content(request.getParameter("board_content"));

		BoardDAO dao = BoardDAO.getInstance();
		BoardBean old = dao.getDetail(board_num);
		
		// 비번 비교
		if(old.getBoard_pass().equals(board_pass)) { // 비번 일치시
			int result = dao.update(board);		// update SQL문 실행
			if(result == 1) System.out.println("글수정 성공");
		} else {								// 비번 불일치
			out.println("<script>");
			out.println("alert('비번이 일치하지 않습니다');");
			out.println("history.go(-1)");
			out.println("</script>");
			out.close();
			
			return null;
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/BoardListAction.do?page=" + page);
//		forward.setPath("/BoardDetailAction.do?board_num="+board_num+ "&page=" + page); // 상세정보로 갈 수도 있다
		return forward;
	}
}

1. 글 수정폼에서 입력된 정보들이 넘어오므로 한글값 인코딩을 한다

- 비번 불일치시 출력할 alert 창을 위해 out 객체를 생성하고 현재 문서 한글값 인코등을 한다

2.  글 수정 폼에서 hidden 으로 전달된 글 번호, 페이지 번호를 받아와서 저장한다

- 페이지번호, 글 번호는 목록 페이지 -> 상세 정보 -> 글 수정 폼 -> 글 수정 으로 넘어왔다

- 글 번호는 수정시에 필요하고, 페이지 번호는 수정 후 원래 페이지로 돌아갈 때 필요하다

3. DTO 객체 board 를 생성하고, 수정폼에서 넘어온 값들을 받아서 객체 board 에 저장

4. 사용자가 수정폼에 입력한 비번을 받고 DB에 저장된 비번과 비교해서 일치하는지 확인

- 사용자가 수정폼에 입력한 비번은 form 에서 넘어온 값이므로 request.getParamter("board_pass") 로 가져온다

- 상세정보를 가져오는 메소드 getDetail() 을 호출해서 DB 에 저장된 1개 글에 대한 정보를 가져와서 DTO 객체 old 에 저장

5. 비번 일치시 DAO 의 update() 메소드를 실행해서 실제 수정을 수행함

- 이후 DAO 에 갔다가 여기로 다시 돌아온다

6. 비번 불일치시 out 객체로 비밀번호가 틀렸다는 메세지를 뿌린 후 이전 페이지인 수정폼으로 돌아간다

- 비번 불일치시 포워딩 시키면 안되므로 포워딩 객체를 생성하기 전에 return null 로 함수 execute() 를 종료

7. 수정에 성공하면 목록 페이지로 가기 위해 "/BoardListAction.do" 로 요청하여 목록 가져오기를 한다

- 목록페이지 포워딩 시 원래 페이지로 가기 위해 페이지 번호를 get 방식 전달, 목록 페이지에서 그 페이지번호를 받아서 그 페이지에 해당하는 글들을 가져와서 출력해준다

- 상세페이지로 갈 수도 있다, 상세페이지로 갈때는 페이지번호 뿐 아니라 글번호도 가져가야한다

 

DAO 클래스 글 수정 기능 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 update() 부분 코드만

	// 글 수정
	public int update(BoardBean board) {
		int result = 0;
		
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
			String sql = "update model2 set board_name=?,board_subject=?,";
			sql += "board_content=? where board_num=?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, board.getBoard_name());
			pstmt.setString(2, board.getBoard_subject());
			pstmt.setString(3, board.getBoard_content());
			pstmt.setInt(4, board.getBoard_num());
			result = pstmt.executeUpdate(); // SQL문 실행
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}		
		}
		return result;
	}

- 수정 폼에서 수정을 위해 작성하는 값들은 이름, 제목, 내용 뿐이므로 이 값들만 update 해주면 된다

- DAO update()가 끝나고 Service 클래스로 돌아간다

 

- Service 클래스, DAO update() 작성 후 수정을 해보자

- index.jsp 실행해서 목록 페이지로 온 다음, 상세 페이지로 가고, 수정폼으로 가고, 수정해보기

- 수정이 잘 된다

 

문제점

- 수정 성공시 성공했다는 메세지가 뜨지 않음

- 수정 성공시 메세지는 alert() 으로 출력되지 않는다

 

문제 해결

			response.sendRedirect("./board/updateresult.jsp?page="+page);

 

- 포워딩할때 Controller 클래스를 거치는 것이 원칙이지만 여기선 바로 수정 성공 메세지를 출력하는 View 페이지로 간다

- 포워딩 시에 페이지번호를 가져가서, 그 페이지 내에서 <script></script> 내에서 출력을 하고 목록페이지로 돌아가야함

- 만약 메세지를 띄우고 싶다면 출력을 담당하는 다른 페이지로 포워딩해서 이 메세지를 띄워야한다

+ 이 작업을 아래의 forward 객체로 포워딩하면 안된다, 여기서 바로 Redirect 방식으로 포워딩해야함

- 아래의 forward 코드는 주석으로 막고 return null 을 해야한다

- Controller 클래스에 "/update.do" 요청을 처리하는 코드를 작성하자

 

- 다음은 수정 성공 후 메세지를 출력할 updateresult.jsp 파일 생성

- updateresult.jsp

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

<script>
	alert(${param.page});
	alert("글 수정 성공");
	location.href = "<%=request.getContextPath()%>/BoardListAction.do?page="+ ${param.page};
</script>

 

- 글 수정을 하고 메세지 출력 후 목록페이지로 잘 가는지 확인해야한다

- 원래 페이지인 1 페이지로 돌아온다, URL 을 보면 알 수 있음

+ 7 페이지의 글을 수정해도 다시 7 페이지로 돌아왔음을 확인 했다

+ 나중에는 이렇게 복잡하게 메세지를 출력하지 않는다


게시판 프로그램 : 글 삭제

- 먼저 상세 페이지 View 페이지인 qna_board_view.jsp 의 버튼 중 '삭제' 버튼에 링크를 걸어야한다

- qna_board_view.jsp 부분

<input type="button" value="삭제"
onClick="location.href='./BoardDeleteAction.do?board_num=${board.board_num}&page=${page}'">

- 상세페이지에서 글번호 board_num 과 페이지번호 page 가 넘어온다

- 글 삭제 폼으로 가기 위해 "/BoardDeleteAction.do" 로 요청을 한다

 

 Controller 클래스에서 글 삭제폼으로 가기 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardDeleteAction.do 로 요청이 올때의 경우를 처리

		// 삭제 폼
		} else if(command.equals("/BoardDeleteAction.do")) {
			try {
				forward = new ActionForward();
				forward.setRedirect(false); // dispatcher 방식으로 포워딩
				forward.setPath("./board/qna_board_delete.jsp");
			} catch (Exception e) {
				e.printStackTrace();				
			}
		}

- 삭제 폼은 DB와 연동할 필요가 없으므로 Service 클래스로 가지 않는다

- 요청을 Controller 페이지에서 받으면 바로 삭제폼 View 페이지 qna_board_delete.jsp 로 포워딩한다

- 여기서 forword 객체를 생성하고 포워딩 방식, 포워딩 페이지를 설정하면 아래 포워딩 코드에 의해 바로 View 로 포워딩 됨

- Dispatcher 방식으로 포워딩해야만 param 으로 값을 받을 수 있다

+ Dispatcher 방식으로 포워딩해야 호출하는 페이지의 request, response 객체를 공유하기때문이다

 

- board 폴더 안에 삭제폼 View 페이지인 qna_board_delete.jsp 생성 및 작성

- qna_board_delete.jsp

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

<html>
<head>
	<title>게시판 삭제</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>
<!-- 글번호, 페이지번호를 request 객체로 받기 -->
board_num1 : <%=request.getParameter("board_num") %> <br>
page1 : <%=request.getParameter("page") %> <br>
<!-- 글번호, 페이지번호를 param 내장객체로 받기 -->
board_num2 : ${param.board_num} <br>
page2 : ${param.page }

<form action="<%=request.getContextPath() %>/BoardDelete.do" method="post">
<input type="hidden" name="board_num" value="${param.board_num}">
<input type="hidden" name="page" value="${param.page}">

<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">게시판 삭제</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="삭제">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_modify.jsp 를 복붙 후 수정

- 상세페이지에서 넘겨준 글번호, 페이지번호 를 받아서 글 삭제 Service 클래스 BoardDelete.do 로 넘겨준다

- 삭제 시 "/BoardDelete.do" 로 요청

- 삭제 시 삭제 Service 클래스인 BoardDelete.java 로 글 번호, 원래 페이지 번호, 비밀번호가 넘어가야한다

+ 삭제할 글 번호를 알아야하기때문에, 원래 페이지로 돌아가야하므로, 비밀번호 일치 확인을 해야하므로

- 입력양식은 비번 입력 양식만 남긴다

 

수정폼 vs 삭제폼

- 수정 폼은 DB와 연동해서 Service 클래스에서 객체 board 를 공유했고 그걸 수정폼에 뿌렸었다

- 삭제 폼은 DB와 연동하지 않았으므로 param 객체로 글번호, 페이지번호를 가져와서 hidden 으로 글 수정 Service 클래스로 넘긴다

+ Dispatcher 방식으로 포워딩했으므로 상세페이지의 request, response 객체를 공유하므로 param 으로 그 글의 글 번호, 원래 페이지 번호를 받아옴

 

param 내장 객체 (qna_board_delete.jsp 부분)

<!-- 글번호, 페이지번호를 request 객체로 받기 -->
board_num1 : <%=request.getParameter("board_num") %> <br>
page1 : <%=request.getParameter("page") %> <br>
<!-- 글번호, 페이지번호를 param 내장객체로 받기 -->
board_num2 : ${param.board_num} <br>
page2 : ${param.page }

- param 은 getParameter() 와 같은 코드

- 코드가 간단해짐

 

- 글 삭제 Service 클래스인 BoardDelete.java 를 service 패키지 안에 생성하자

 

게시판 프로그램 : 글 삭제 Service 클래스

- service 패키지 안에 글 삭제 Service 클래스인 BoardDelete.java 를 생성 및 작성하자

- BoardDelete.java

package service;

import java.io.File;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardDelete implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardDelete");
		
		response.setContentType("text/html; charset=utf-8");
		request.setCharacterEncoding("utf-8");
		
		PrintWriter out = response.getWriter();
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		String board_pass = request.getParameter("board_pass");
		
		String path = request.getRealPath("boardupload");
		System.out.println(path);
		
		BoardDAO dao = BoardDAO.getInstance();
		BoardBean old = dao.getDetail(board_num); // 상세정보 구하기
		System.out.println(old.getBoard_pass());
		System.out.println(board_pass);
		
		// 비번 비교
		if(old.getBoard_pass().equals(board_pass)) {	// 비번 일치시
			int result = dao.delete(board_num);	// delete SQL문 실행
			if(result == 1) System.out.println("삭제 성공");
			
			// 첨부파일이 있는 경우에 첨부파일 삭제
			if(old.getBoard_file() != null) {
				
				File file = new File(path);
				file.mkdir();
				
				// boardupload 디렉토리의 모든 첨부 구해오기
				File[] f = file.listFiles();
				for(int i=0; i<f.length; i++) {
					if(f[i].getName().equals(old.getBoard_file())) {
						f[i].delete();	// 첨부파일 삭제
					}
				}
			}
		} else {	// 비번 불일치시
			out.println("<script>");
			out.println("alert('비번이 일치하지 않습니다.');");
			out.println("history.go(-1);");
			out.println("</script>");
			out.close();
			
			return null;
		}
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false);
		forward.setPath("/BoardListAction.do?page=" + page);
		return forward;
	}

}

1. 삭제 폼에서 한글값이 넘어올 수 있으므로 한글값 인코딩 처리를 한다

- 비번이 일치하지 않을때 alert 창에 출력하기 위해 문서 한글 인코딩 처리도 하고 out 객체도 생성한다

2. 삭제 폼에서 hidden 으로 넘어온 글 번호와 페이지 번호를 받아서 변수에 저장한다

- 목록 페이지 -> 상세페이지 -> 삭제폼 -> 삭제 Service 클래스 까지 글 번호와 페이지 번호가 넘어온 것이다

3. 삭제 폼에서 사용자가 입력한 비밀번호도 받아서 DAO 의 getDetail() 메소드로 상세정보를 구해온다

4. 비번 일치시 DAO 의 delete() 메소드를 호출하여 글을 삭제한다

- 매개변수로는 글 번호를 넘겨준다

5. 삭제한 글에 해당하는 첨부파일이 있다면 첨부파일도 지우도록 한다

* 첨부파일 지우기 설명은 아래에

6. 비번 불일치시 out 객체를 통해 alert 창에 메세지를 뿌리고 이전 페이지인 삭제 폼으로 돌아간다 

- 이후 return null 을 작성 아래쪽의 포워딩 객체를 생성하고 설정하는 곳으로 가지 않고 함수를 종료시킨다

7. 글 삭제 성공 후 forward 객체를 생성하고 설정, 포워딩 페이지는 목록을 가져오는 "/BoardListAction.do" 로 한다

- get 방식으로 페이지 번호를 넘겨줘서, 원래 페이지로 돌아갈 수 있게 한다

 

첨부파일 지우기 (BoardDelete.java 부분)

			// 첨부파일이 있는 경우에 첨부파일 삭제
			if(old.getBoard_file() != null) {
				
				File file = new File(path);
				file.mkdir();
				
				// boardupload 디렉토리의 모든 첨부 구해오기
				File[] f = file.listFiles();
				for(int i=0; i<=f.length; i++) {
					if(f[i].getName().equals(old.getBoard_file())) {
						f[i].delete();	// 첨부파일 삭제
					}
				}
			}

- 첨부파일 지우기는 DAO 안에서 처리해도 되고, Service 클래스의 delete() 호출 아래에서 처리해도 된다

1) 첨부파일이 저장된 진짜 경로를 getRealPath() 로 구해서 path 변수에 저장한다

2) 상세정보를 구해온 객체 old.getBoard_file 을 통해 첨부파일이 있는지 없는지 판별가능, 있을때 삭제

3) File 객체 f를 생성할때 생성자의 매개변수에 생성될 디렉토리의 위치인 path 변수가 들어간다

4) file.listFiles() 로 boardupload 디렉토리의 모든 첨부파일을 구해온다

5. 반복문을 통해서 모든 파일을 돌면서, 파일명이 delete()로 삭제한 글의 DB 속에 저장된 파일명과 일치하면 파일 삭제

 

Controller 클래스에서 글 삭제 Service 클래스 BoardDelete.java로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardDelete.do 로 요청이 올때의 경우를 처리

		// 글 삭제
		} else if(command.equals("/BoardDelete.do")) {
			try {
				action = new BoardDelete();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

 

DAO 클래스 글 삭제 기능 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 delete() 부분 코드만

	// 글 삭제
	public int delete(int board_num) {
		int result = 0;
		
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
			String sql = "delete from model2 where board_num = ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, board_num);
			result = pstmt.executeUpdate(); // SQL문 실행
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}			
		}
		return result;
	}

 

- index.jsp 를 실행해서 목록페이지로 오고 10 페이지의 글 하나를 삭제해보자

- 삭제 후 다시 목록페이지로 왔다, 원래 페이지였던 10 페이지로 돌아왔다

C:\Users\admin\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\model2board\boardupload

- Desert21.jpg 첨부파일이 삭제되었는지 확인하기 위해 콘솔에 찍힌 path 를 탐색기에 검색

- 삭제 되었음을 확인 가능하다

JSP Model 2 끝


라이브러리 관리

- 지금까지는 Dynamic Web Project 를 만들고, 직접 라이브러리를 구해서 lib 폴더 안에 모든 라이브러리를 저장했다

- Spring 에서는 Maven 으로 라이브러리를 관리한다

- Maven 은 라이브러리 관리자 중 하나이다

 

라이브러리 관리자 maven

- spring project 에선 기본적으로 maven 으로 라이브러리를 관리함

- springboot project 에선 maven 또는 gradle 로 라이브러리를 관리함, 선택 가능

- 환경설정 파일 안에 라이브러리를 추가하는 식이다

 

라이브러리 관리자 gradle

- android project

- springboot projec

 

Maven 프로젝트 생성

 

Dynamic Web Project vs Maven Project

- 이때까지 Dynamic Web Project 로 프로젝트를 생성해 왔다

- 이젠 Maven Project 로 프로젝트를 생성하자

- Maven Project는 기본적으로 maven 으로 라이브러리를 관리하게끔 설정됨

- Maven Project는 Dynamic Web Project 와 폴더구조나 라이브러리 관리 방식이 크게 다르다

 

- Catalog 를 Internal 로 바꾼다

- 어떤 archetype 을 선택하냐에 따라 프로젝트 모양이 달라짐

- 일반적으로 웹 어플리케이션 프로젝트를 생성할때는 이 maven-archetype-webapp 를 선택

 

- Group id 값은 도메인명 역순을 주로 입력함. 현재는 com.myhome

- Artifact id 값은 프로젝트가 생성되면 프로젝트 명이 된다. 현재는 maventest

- Finish 누르면 maventest 프로젝트가 생성된다

- 기존의 Dynamic Web Project 와 구조가 다름

- pom.xml 파일이 Maven 의 환경설정 파일이다

+ 환경설정 파일은 xml 파일로 되어있는 경우가 많음

- 파일들에서 에러가 발생한다

 

maven project 에러발생

- maven 프로젝트 생성후 index.jsp 파일에 에러가 발생

- Libraries 에 Apache Tomcat Library 를 추가하면 에러가 사라진다.

- 여기 Apache Tomcat Library 가 없다

 

Apache Tomacat Library 추가하는 방법

- 그럼 빨간 에러가 사라진다

- index.jsp 를 실행해보자

 

Maven Project 구조

maintest/src/main 안의 3개 폴더

- 들어가는 내용들이 정해져 있다

 

1. java 폴더

- 확장자가 .java 인 모든 파일은 반드시 이 폴더 안에 저장

- dynamic web project 의 src 와 같은 역할

 

2. resources 폴더

- MaBatis 의 환경 설정 파일들이 주로 들어가있다

- 기존 DAO 클래스 안의 SQL문을 xml 파일로 따로 뺀다, 그 SQL문 파일들이 저장된다

 

3. webapp 폴더

- View 페이지가 저장되는 위치

- index 파일도 여기 안에 만들어야한다, webapp/WEB-INF 폴더 안에 index 파일저장

- 하지만 WEB-INF 폴더 안에 라이브러리는 더이상 없다, pom.xml 에서 라이브러리를 추가함

- dynamic web project 의 WebContent 와 같은 역할

 

pom.xml 파일

- maventest 프로젝트 하위

- Maven 의 환경설정 파일, 필요한 라이브러리를 여기에 추가한다

- 필요한 라이브러리를 pom.xml 추가시 원격 저장소에서 로컬 저장소로 다운받는다

 

- pom.xml 파일을 살펴보자

 

pom.xml 파일

- pom.xml (원하는 라이브러리 추가 전)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.myhome</groupId>
  <artifactId>maventest</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>maventest Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>maventest</finalName>
  </build>
</project>

- Maven 의 환경설정 파일

- 가장 바깥의 Root Element 가 project 이다, 전체가 <project></project> 로 덮여있다

- Maven Project 생성시 설정했던 내용들이 들어가있다, Group Id, ArtifactId 등

- Dependencies Element 안에 Dependency Element 들이 있다

- 이 Dependencies Element 안에 우리가 원하는 라이브러리를 추가해야함, 의존 라이브러리라고 부른다

- 여기선 junit 라이브러리 추가,  junit 은 테스트 용도로 제공되는 라이브러리

 

라이브러리 가져오기

- 먼저 Maven Repository 에서 의존 라이브러리를 검색한다

- cos 라이브러리를 찾아서 가져와보자

 

Maven Repository: Search/Browse/Explore (mvnrepository.com)

- 이 버전을 클릭

 

- 복사한 코드를 pom.xml 의 dependencies element <dependencies></dependencies> 안에 추가한다

- 추가 후 저장시 원격 저장소에서 로컬 저장소(내 컴퓨터) 로 다운로드를 시작한다

 

- pom.xml (원하는 라이브러리 추가 후, 미완성)

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>maventest</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>maventest Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
	
		<!-- https://mvnrepository.com/artifact/servlets.com/cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		
	</dependencies>
	<build>
		<finalName>maventest</finalName>
	</build>
</project>

- 이렇게 여기 dependency element 로 추가되는 라이브러리를 의존 라이브러리 라고 한다

+ Spring 에서는 maven 으로 라이브러리를 관리함

 

로컬 저장소 위치

- C:\Users\admin\.m2\repository

- 내 컴의 로컬 저장소(local repository) 위치이다.

- pom.xml 에 추가되어 원격 저장소에서 로컬 저장소로 다운받아진 라이브러리들이 여기 저장되어 있다.

 

- Windows - Preperences 로 들어와서 Maven ->  Use Settings 선택

- 내 컴의 로컬 저장소 위치이다

- maven 으로 라이브러리 관리 시 기본적으로 이렇게 로컬 저장소가 생성된다

- 특정 라이브러리를 다운하다가 문제가 생기면 로컬 저장소 안의 파일들을 다 지워도 된다, 새로 실행시 처음부터 다시 다운받음

- 만약 특정 라이브러리가 다운이 안된다면, 이미 그 라이브러리를 잘 다운 받은 컴퓨터의 레파지토리를 복사해서 내 컴에 덮어씌운다

 

- 저 주소를 복사해서 탐색기에 쳐보자

- 라이브러리들이 보인다

- 로컬 저장소안의 라이브러리를 지우고 싶으면 이클립스를 종료하고 지워야함

- 이클립스를 종료하고 모두 삭제하면 다시 이클립스를 실행할때 다시 다 다운받는다

 

Oracle JDBC 라이브러리 추가

pom.xml 에 Oracle Repository 추가

- Oracle은 공식 저장소에서 지원되는 라이브러리들이 다운이 안되는 경우가 많이 발생함

- 그래서 비공식적인 오라클 Repository 를 먼저 따로 추가해야함

- pom.xml 의 dependencies Element "위"에 오라클 Repository 를 가져오는 이 코드를 복사

	<!-- 오라클 repository -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

- 그리고 오라클 JDBC 라이브러리를 dependencies Element 안에 추가

- 이 코드 잘 작동하지 않는다, 이미 다운받은 Repository 를 덮어쓰자

		<!-- 오라클 JDBC Library -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

 

- pom.xml (오라클 Reopository 추가, 오라클 JDBC Library 추가)

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>maventest</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>maventest Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<!-- 오라클 repository -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

	<dependencies>
		<!-- 오라클 JDBC Library -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/servlets.com/cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

	</dependencies>
	<build>
		<finalName>maventest</finalName>
	</build>
</project>

- 오라클 JDBC 라이브러리를 다운받을때 문제가 생긴다

 

MySQL JDBC 라이브러리 추가

 

- 지금 설치한 mysql 버전이 8 점대 임을 고려해서 8 점대 중 8.0.28 버전 다운

 

- dependencies element 안에 이 코드를 복붙

 

프레임워크 MyBatis 라이브러리 추가

- 지금은 Spring 을 안쓰므로 MyBatis 만 있으면 된다

- dependencies element 안에 이 코드를 복붙

 

JSTL 라이브러리 추가

- 하나만으로 처리가 안되는 경우가 있으므로 두가지 라이브러리를 가져옴

- 나중엔 JSTL 연결 라이브러리도 필요

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
</dependency>

- 이걸 pom.xml 의 dependencies element 안으로 가져옴

 

문제점

- 오라클 JDBC 라이브러리를 가져올때만 문제가 생겼다

 

문제 해결 방법

- 이미 다운을 받은 레파지토리를 복사해서 내 레파지토리로 붙여넣는다

- 이미 오라클 JDBC 라이브러리를 다운받은 레파지토리를 클라우드에서 다운받아서 압축 해제하고 그 안의 내용을 모두 내 로컬 레파지토리로 복붙하자

- 이클립스를 종료하고 내 로컬 레파지토리의 내용은 모두 삭제 하고, 이 repository 압축 풀은 모든 내용을 복붙하기

- 다 삭제하고 복붙 완료한 내 레파지토리

- 다시 이클립스를 켠다

 

- 복붙한 라이브러리 버전과 다른 경우는 다시 pom.xml 에 쓰인 버전으로 다운받는다

- 그럼 오라클 JDBC 라이브러리 코드를 작성해도 에러가 나타나지 않음

- 빨간 에러가 사라졌다

- pom.xml 에 작성된 라이브러리들만 여기에 나타난다

 

- pom.xml (최종)

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.myhome</groupId>
	<artifactId>maventest</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>maventest Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<!-- 오라클 repository -->
	<repositories>
		<repository>
			<id>codelds</id>
			<url>https://code.lds.org/nexus/content/groups/main-repo</url>
		</repository>
	</repositories>

	<dependencies>
		<!-- 오라클 JDBC Library -->
 		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.28</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.7</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		
		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>jstl-impl</artifactId>
			<version>1.2</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/servlets.com/cos -->
		<dependency>
			<groupId>servlets.com</groupId>
			<artifactId>cos</artifactId>
			<version>05Nov2002</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

	</dependencies>
	<build>
		<finalName>maventest</finalName>
	</build>
</project>

 

maven 프로젝트 vs spring 프로젝트

- 현재는 maven 프로젝트를 생성했을때 pom.xml 에 의존 라이브러리는 junit 만 있었다

- Spring 프로젝트 만들면 기본적인 라이브러리가 다 들어가있다

+ Spring 프로젝트도 라이브러리 관리는 maven 라이브러리 관리자를 사용

 

Spring 프로젝트 만들기

- Spring 프로젝트는 이클립스 메뉴 없어서 지금은 만들 수 없다

1. 플로그인에 추가

2. STS 란 프로그램 사용, SpringBoot 만들 수 있는 메뉴도 추가되어있다

 

maven 프로젝트 import, export

- maven 프로젝트를 import / export 할때 war 파일로 import / export 하면 안된다

- spring 프로젝트도 import / export 할때 war 파일로 import / export 하면 안된다

- maven 프로젝트를 war 파일로 import / export 하면 Maven Project 가 Dynamic Web Project 로 풀려버림

 

- war 파일로 export -> import 했을때 프로젝트는 Dynamic Web Project 가 된다

 

maven 프로젝트 export 하는 법

- maven 프로젝트를 export 할때는 프로젝트를 통째로 Ctrl + C 로 복사해서 다른 곳에(바탕화면) 붙여넣기 한다

- 이렇게 폴더로 나타난다

 

maven 프로젝트 import 하는 법

- 현재는 이미 이클립스에 maventest 라는 프로젝트가 있으므로 Finish 가 활성화되어있지 않음

- 이클립스의 maventest 프로젝트를 삭제 후 다시 import 하자

- maventest 프로젝트가 maven project 그대로 유지되면서 import 되었다

Controller (Servlet) 클래스

- WebServlet 클래스 등은 Apache Tomcat 의 servlet-api.jar 파일에 있다

 

흐름 정리

- 클라이언트가 요청하면 무조건 Controller 클래스로 가고, 적절한 Service 클래스로 넘겨준다 거기서 DAO 의 메소드를 호출하여 DB와 연동

- DAO 에서 검색한 결과를 Service 클래스에서 공유설정하고 Controller 클래스에서 포워딩해서 View 에서 공유된 값을 가져와서 출력


문제점 1

- 등록 버튼을 눌러서 글을 등록한 뒤 목록페이지에서 F5(새로고침) 를 누르면 똑같은 데이터가 추가된다

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 새로고침을 누를때마다 하나씩 추가됨

 

문제점 1 해결

- BoardAddAction.java (원문 글 작성 Service 클래스) 에서 setRedirect(false) 를 true 로 설정해서 Redirect 방식으로 포워딩하면 이 문제가 해결된다

 

문제점 2

- 글 작성 성공 후 글 목록 가져오기를 "/BoardListAction.do" 로 요청할때 get이 나와야하는데 post 방식이 나온다

- 포워딩을 하면 get 방식으로 전달되지만, 콘솔창에는 post 방식으로 뜬다

+ 폼으로 /BoardListAction.do 로 오는게 아니므로 post 가 아닌 get 방식이다

 

문제점 2 이유

- 위는 글 작성 요청 아래는 글 작성이 끝난 후 글 목록을 가져오는 요청

- dispatcher 방식으로 포워딩되면 URL 주소가 바뀌지 않으므로 이전에 글 작성할때 사용했던 post 방식으로 나옴

 

문제점 1 & 문제점 2 해결 방법

- setRedirect 를 true 로 바꿈

- setPath 에서 혹시 경로를 못찾아가면 . 를 앞에 붙여줌

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 새로고침해도 같은 데이터가 들어가지 않는다

 

문제점 정리

- Get 방식처럼 단순히 조회하는 요청은 Dispatcher 방식을 사용

- Post, Put 방식처럼 무언가를 변화시키는 요청에는 Redirect 방식을 사용

- Dispatcher 방식이 Redirect 방식보다 성능상 좋다. 

- Post, Put 같은 무언가를 변화시키는 요청에는 Redirect 방식을 사용

- 클라이언트가 Post 요청으로 무언가를 변화시킨 후 새로고침을 눌러버리면 웹 브라우저에서 새로고침은 마지막에 서버에 전송한 데이터를 다시 전송하기 때문에 다시 URL에 접속되면서 Post 요청이 한번 더 전송되기 때문

ex) 상품 등록 요청을 하였는데 재요청을 보내버리면 2개의 상품이 중복 등록가 돼버리는 상황이 발생하게 된다.

 

다른 페이지로 이동

- 아직 페이지를 선택할 수 있는 부분을 만들지 않았으므로 URL 에 입력해서 찾아가보자

- 이 페이지에 맞는 10 개의 글(데이터) 을 뿌려줌

 

파생변수 number

	<c:set var="num" value="${listcount - (page - 1) * 10}"/>

- 각 페이지에 출력될 시작 번호를 저장하고 있다

- 마지막의 10 은 한 페이지에 출력할 데이터 개수 limit 를 의미한다

- 그 번호에서 반복문으로 1씩 감소하여서 출력

 

더미 데이터 만들기

- 페이지 처리 구현시 보기 위해 더미 데이터 여러개를 만든다

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료


게시판 프로그램 : 목록 가져오기 (이어서)

- 목록가져오는 것은 구현했고, 공유된 리스트, 기본변수, 파생변수들을 View 에서 가져와서 출력하는 과정에 있다

- 목록은 뿌렸고 페이지 처리, 페이지 선택할 수 있는 것 만들고 있다

- qna_board_list.jsp (최종)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<a href="./BoardForm.do">글작성</a> <br>
글갯수 : ${listcount } 개 <br>

<%
	int count = ((Integer)request.getAttribute("listcount")).intValue();
%>
글갯수 : <%=count %> 개 <br>

<table border=1 width=700 align=center>
	<caption>게시판 목록</caption>
	<tr>
		<td>번호</td>
		<td>제목</td>
		<td>작성자</td>
		<td>날짜</td>
		<td>조회수</td>
	</tr>
	
	<c:set var="num" value="${listcount - (page - 1) * 10}"/>
	<c:forEach var="b" items="${boardlist}">
	<tr>
		<td> ${num}
			<c:set var="num" value="${num-1}"/>
		</td>
		<td>
			<!-- 댓글 제목 앞에 여백 처리 -->
			<c:if test="${b.board_re_lev > 0}">
				<c:forEach var="i" begin="0" end="${b.board_re_lev}">
					&nbsp;
				</c:forEach>
			</c:if>
			<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">${b.board_subject}</a>
		</td>
		<td>
			${b.board_name}
		</td>
		<td>
			<fmt:formatDate value="${b.board_date}"
							pattern="yyyy-MM-dd HH:mm:ss EEEE"/>
		</td>
		<td>${b.board_readcount}</td>
	</tr>
	</c:forEach>
</table> <br><br>

<!-- 페이지 처리 -->
<center>
<c:if test="${listcount > 0}">

<!-- 1page로 이동 -->
<a href="./BoardListAction.do?page=1" style="text-decoration:none"> << </a>

<!-- 이전 블럭으로 이동 -->
<c:if test="${startPage > 10}">
	<a href="./BoardListAction.do?page=${startPage-10}">[이전]</a>
</c:if>

<!-- 각 블럭에 10개의 페이지 출력 -->
<c:forEach var="i" begin="${startPage}" end="${endPage}">
	<c:if test="${i == page}"> <!-- 현재 페이지 -->
		[${i}]
	</c:if>
	<c:if test="${i != page}"> <!-- 현재 페이지가 아닐때 -->
		<a href="./BoardListAction.do?page=${i}">[${i}]</a>
	</c:if>
</c:forEach>

<!-- 다음 블럭으로 이동 -->
<c:if test="${endPage < pageCount}">
	<a href="./BoardListAction.do?page=${startPage+10}">[다음]</a>
</c:if>

<!-- 마지막 페이지로 이동 -->
<a href="./BoardListAction.do?page=${pageCount}" style="text-decoration:none"> >> </a>

</c:if>
</center>

 

페이지 처리 / 페이지 메뉴 바 설계

각 블럭에 10개의 페이지 출력 (qna_board_list.jsp 부분)

<!-- 각 블럭에 10개의 페이지 출력 -->
<c:forEach var="i" begin="${startPage}" end="${endPage}">
	<c:if test="${i == page}"> <!-- 현재 페이지 -->
		[${i}]
	</c:if>
	<c:if test="${i != page}"> <!-- 현재 페이지가 아닐때 -->
		<a href="./BoardListAction.do?page=${i}">[${i}]</a>
	</c:if>
</c:forEach>

 

- forEach 문으로 startPage 와 endPage 까지 반복문을 돌려서 1 2 3 ... 10 로 페이지 선택하는 걸 만듬

- 현재 페이지가 아닌 경우 클릭한 페이지 번호를 넘기면서 "/BoardListAction.do" 로 요청한다

- 이러면 Controller 클래스를 다시 찾아가서 BoardListAction.java Service 클래스로 가서 다시 목록을 가져온다

- 이때 BoardListAction.java 의 아래 코드에서

		if(request.getParameter("page") != null) {
			page = Integer.parseInt(request.getParameter("page"));
		}

- 사용자가 선택한 페이지가 page 변수에 저장되고 그걸 이용해서 데이터를 가져옴

- 그리고 page 변수로부터 유도된 변수 및 리스트를 BoardListAction.java 아래에서 공유설정함

		// 공유 설정
		request.setAttribute("page", page);

- 그걸 다시 View qna_board_list.jsp 에서 받는다

 

첫 페이지로 이동, 마지막 페이지로 이동 (qna_board_list.jsp 부분)

 

<!-- 1page로 이동 -->
<a href="./BoardListAction.do?page=1" style="text-decoration:none"> << </a>

- 1 page 로 이동

<!-- 마지막 페이지로 이동 -->
<a href="./BoardListAction.do?page=${pageCount}" style="text-decoration:none"> >> </a>

- 총 페이지 수 == 마지막 페이지

- pageCount 는 총 페이지 수이고, 공유되어 받아온 값이다

- >> 클릭시 마지막 페이지로 이동

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

 

이전 블럭 (qna_board_list.jsp)

<!-- 이전 블럭으로 이동 -->
<c:if test="${startPage > 10}">
	<a href="./BoardListAction.do?page=${startPage-10}">[이전]</a>
</c:if>

- 블럭단위로 이동

- startPage 는 본인이 속한 블럭의 첫 페이지 의미

- startPage > 10 은 '1 페이지가 아니면' 이라는 의미, ${startPage - 10} 은 현재 블럭의 첫 페이지에서 -10 을 하면 이전 블럭의 첫 페이지로 이동

ex) 13 페이지에 있다가 [이전] 클릭시 현재 블럭 startPage 인 11 에서 -10 을 하여 1 페이지로 이동, 즉 1 블럭의 startPage 로 이동

 

다음 블럭 이동 (qna_board_list.jsp)

<!-- 다음 블럭으로 이동 -->
<c:if test="${endPage < pageCount}">
	<a href="./BoardListAction.do?page=${startPage+10}">[다음]</a>
</c:if>

- endPage 는 본인이 속한 블럭의 끝 페이지를 의미

- endPage < pageCount 은 '마지막 페이지가 아니면' 이라는 의미, ${startPage + 10} 은 현재 블럭의 첫 페이지에서 +10 을 하면 다음 블럭의 첫 페이지로 이동

 

 

블럭과 파생변수 startPage, endPage

- 두번째 블럭의 startPage 는 11, endPage 는 20

 

- 페이지를 클릭할때마다

- 페이지 번호를 누를때마다 계속 DB와 연동해서 목록을 가져오는 작업, request 로 공유하는 작업, View 에서 받아서 뿌리는 작업이 수행된다

 


+ 같은 페이지 처리를 JSTL 쓰지 않고 했을때

- Model 1 게시판 페이지 메뉴바 만드는 코드이다 : https://laker99.tistory.com/117

<!-- 페이지 링크 설정 -->
<center>
<%
if(count > 0) {
	// pageCount : 총 페이지 수
	int pageCount = count/page_size + ((count%page_size==0) ? 0 : 1);
	System.out.println("pageCount : " + pageCount);
	
	// startPage : 각 블럭의 시작 페이지 번호 : 1, 11, 21 ...
	// endPage : 각 블럭의 끝 페이지 번호 :    10, 20, 30 ...
	int startPage = ((currentPage-1)/10) * 10 + 1;
	int block = 10;	// 1개의 블럭의 크기 : 10개의 페이지로 구성
	int endPage = startPage + block - 1;
	
	// 가장 마지막 블럭에 endPage값을 pageCount로 수정
	if(endPage > pageCount) {	//
		endPage = pageCount;
	}
%>	
	<!-- 1page로 이동 -->
	<a href="list.jsp?page=1" style="text-decoration:none"> << </a>
<%
	// 이전 블럭으로 이동
	if(startPage > 10){	%>
		<a href="list.jsp?page=<%=startPage-10 %>">[이전]</a>
<%	}

	// 각 블럭당 10개의 페이지 출력
	for(int i = startPage; i <= endPage; i++) {
		if(i == currentPage) { %>
			[<%=i %>]
<%		} else {%>
			<a href="list.jsp?page=<%=i %>">[<%=i %>]</a>
<%		}
	}// for end
	
	// 다음 블럭으로 이동
	if(endPage < pageCount) {
%>	
		<a href="list.jsp?page=<%=startPage+10 %>">[다음]</a>	
<%	} %>
	<!-- 마지막 페이지로 이동 -->
	<a href="list.jsp?page=<%=pageCount %>" style="text-decoration:none"> >> </a>	
<%}%>
</center>


게시판 프로그램 : 상세 페이지

- 제목 클릭시 상세 페이지로 넘어간다

- 글번호 num 과 페이지 번호를 전달해야한다 ( 총 3번 전달 중 첫번째)

- 목록페이지 -> 상세페이지 -> 수정/삭제 폼 -> 수정/삭제

- 처음 2 번은 get 방식으로 전달, 마지막은 post 로 전달

- qna_board_list.jsp 부분

<td>
	<a href="./BoardDetailAction.do?board_num=${b.board_num}&page=${page}">${b.board_name}</a>
</td>

- 상세 페이지로 가기 위해 /BoardDetailAction.do 로 요청한다

 

 게시판 프로그램 : 상세 페이지 Service 클래스

- service 패키지 안에 상세 페이지 Service 클래스인 BoardDetailAction.java 를 생성 및 작성하자

- BoardDetailAction.java (수정 전)

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardDetailAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardDetailAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		dao.readcountUpdate(board_num);	// 조회수 1 증가
		BoardBean board = dao.getDetail(board_num);	// 상세정보 구하기
		
		// 글내용 줄바꿈 기능
		String content = board.getBoard_content().replace("\n", "<br>");
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("content", content);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_view.jsp");
		
		return forward;
	}

}

- 목록 페이지에서 제목을 클릭해서 넘어왔고, 글번호와 페이지번호가 넘어왔다, 그 값을 변수 board_num 과 page 로 받음

- 글번호는 int 형으로 형변환한다

<조회수 1 증가>

- 조회수 값을 1 증가시키는 update SQL 을 수행하는 메소드 readcountUpdate() 가 필요하다

- 그 글의 조회수 컬럼을 1 증가시켜야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 따로 리턴을 받을 필요는 없다, 아무것도 리턴 받지 않고 있음

* DAO 클래스 readcountUpdate() 메소드 작성은 아래에

<상세 정보 구하기>

- 상세 정보를 구해오는 메소드 select SQL 을 수행하는 메소드 getDetail() 이 필요하다

- 그 글의 상세 정보를 구해와야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 글 1개의 상세정보를 리턴하므로 리턴 자료형은 DTO

* DAO 클래스 getDetail() 메소드 작성은 아래에

<DAO -> Service 로 돌아왔을때> (BoardDetailAction.java 부분)

		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("content", content);
		request.setAttribute("page", page);

- View 페이지로 돌아간다, 돌아갈때 2가지 변수 글번호, 페이지번호 뿐 아니라 검색된 결과를 돌려받은 객체 board 도 가져가야한다

- 이때, 객체 board, 줄바꿈 처리를 한 content , 페이지번호 page 3개를 공유설정한다

- 글번호 num 은 board 객체 안의 컬럼 board_num 에 있음

- content 는 board 에서 가져와서 줄바꿈 처리를 한 것이다

- 상세페이지 출력 View 페이지인 qna_board_view.jsp 파일로 포워딩한다.

- 이 Service 클래스를 호출했던 Controller 클래스로 돌아감


Model 1 vs Model 2

- Model 1 에서는 두 기능을 하나의 메소드에 처리했다, Model 2 에서는 따로 메소드를 만듬

+ MyBatis 로 바꿔서 처리하면 1개의 메소드 내에서 2개의 sql 문 사용하기 힘들어지므로 지금부터 다른 메소드로 처리

 

Controller 클래스에서 상세 페이지 Service 클래스 BoardDetailAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardDetailAction.do 로 요청이 올때의 경우를 처리

		// 상세 페이지
		} else if(command.equals("/BoardDetailAction.do")) {
			try {
				action = new BoardDetailAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}
		}

- 연결 이후 Service 클래스까지 잘 넘어간다

 

DAO 클래스 조회수 1 증가 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 readcountUpdate() 부분 코드만

	// 조회수 1 증가
	public void readcountUpdate(int board_num) {
		Connection con = null;
		PreparedStatement pstmt = null;
		
		try {
			con = getConnection();
			
			String sql = "update model2 set board_readcount = board_readcount + 1 ";
			sql += "where board_num = ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, board_num);
			pstmt.executeUpdate();	// SQL문 실행
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}		
		}
	}

- 데이터를 돌려줄 필요없으므로 리턴 자료형 void

- 그 글의 조회수 컬럼을 1 증가시켜야하므로 그 글을 특정할 수 있는 글번호 board_num 이 전달되고, 그걸로 SQL문 where 절 작성

 

DAO 클래스 상세정보 구하기 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 getDetail() 부분 코드만

	// 상세 페이지
	public BoardBean getDetail(int board_num) {
		BoardBean board = new BoardBean();
		
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			con = getConnection();
			
			String sql = "select * from model2 where board_num = ?";
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, board_num);
			rs = pstmt.executeQuery();	// SQL문 실행
			
			if(rs.next()) {
				board.setBoard_num(rs.getInt("board_num"));
				board.setBoard_name(rs.getString("board_name"));
				board.setBoard_pass(rs.getString("board_pass"));
				board.setBoard_subject(rs.getString("board_subject"));
				board.setBoard_content(rs.getString("board_content"));
				board.setBoard_file(rs.getString("board_file"));
				board.setBoard_re_ref(rs.getInt("board_re_ref"));
				board.setBoard_re_lev(rs.getInt("board_re_lev"));
				board.setBoard_re_seq(rs.getInt("board_re_seq"));
				board.setBoard_readcount(rs.getInt("board_readcount"));
				board.setBoard_date(rs.getTimestamp("board_date"));
			}
		} catch (Exception e) {
			e.printStackTrace();			
		} finally {
			if(rs != null) try { rs.close(); } catch(Exception e) {}
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}
		}
		return board;
	}

- 그 글의 상세 정보를 구해와야하므로 메소드 호출시 그 글을 특정할 수 있는 글번호 board_num 을 전달함

- 글 1개의 상세정보를 리턴하므로 리턴 자료형은 DTO

- select 문을 작성하므로 ResultSet rs 객체를 생성하고 그 객체로 검색된 데이터들을 받는다
- DTO 객체 board 를 생성하고, 그 board 에 검색된 데이터를 setter 메소드로 세팅한 후 리턴한다

 

- 다음은 board 폴더 하위에 상세 페이지를 출력하는 View 페이지인 qna_board_view.jsp 파일을 생성

- qna_board_view.jsp (수정 전, 버튼 구현이 완성 안됨, 수정/삭제/댓글 버튼 링크 안걸었다)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<table border=1 width=400 align=center>
	<caption>상세 페이지</caption>
	<tr>
		<td>제목</td>
		<td>${board.board_subject}</td>
	</tr>
	<tr>
		<td>내용</td>
		<td>
			<pre>${board.board_content}</pre>
			${content}
		</td>
	</tr>
	<tr>
		<td>첨부파일</td>
		<td>
			<!-- 첨부파일이 있을때 첨부파일 출력 -->
			<c:if test="${board.board_file != null}">
				<a href="./board/file_down.jsp?file_name=${board.board_file}">
					${board.board_file}
				</a>
			</c:if>
		</td>
	</tr>
	<tr>
		<td colspan=2 align=center>
			<input type="button" value="댓글">
			<input type="button" value="수정">
			<input type="button" value="삭제">
			<input type="button" value="목록"
			onClick="location.href='./BoardListAction.do?page=${page}'">
		</td>
	</tr>
</table>
</body>
</html>

<EL>

- Service 클래스인 BoardDetailAction.java 에서 board 객체를 공유설정했으므로 ${board.board_subject} 처럼 네임값.필드명 으로 간단히 출력 가능

<내용 출력>

- 내용 content 을 출력하는 방법 2가지를 모두 썻다

1. 공유된 content 를 바로 EL 로 출력

2. 공유된 board 객체에서 board.board_content 로 내용을 가져와서 <pre></pre> 로 감싸기

<첨부파일> (qna_board_view.jsp 부분)

			<!-- 첨부파일이 있을때 첨부파일 출력 -->
			<c:if test="${board.board_file != null}">
				<a href="./board/file_down.jsp?file_name=${board.board_file}">
					${board.board_file}
				</a>
			</c:if>

- if 태그로 첨부파일이 null 이 아니면, 즉 첨부파일이 있으면 첨부파일명을 출력

- 첨부파일명에 링크를 걸어 클릭시 첨부파일을 다운받도록 한다 첨부파일명을 클릭시 file_down.jsp 로 이동

- file_down.jsp 로 이동하며 다운로드 받을 파일명 ${board.board_file} 을 file_name 변수에 담아 전달한다

<목록 버튼>

- '목록' 버튼 클릭시 "/BoardListAction.do" 요청을 하면서 현제 페이지 번호를 목록 구하는 곳으로 전달한다

- page 변수에 값이 저장되었으므로 원래 페이지로 돌아갈 수 있다

<댓글 버튼>

- '댓글' 버튼 클릭시 "/BoardReplyAction.do" 요청을 하면서 글번호 board_num 과 원래 페이지번호 page 를 전달

- 특정 글 아래에 댓글을 달게되므로 글번호 board_num 이 필요하다

- 댓글 작성 완료 후 원래의 페이지로 돌아가기 위해서 page 번호가 필요하다

- 나머지 버튼들은 나중에 링크 연결하기

 

Q. 왜 파일 다운하기 위해 file_down.jsp 로 갈떄는 원문 글 작성폼으로 갈때와 달리 Controller 안거치고 jsp 로 가는지 모르겠다

A. 원칙은 Controller 클래스 거쳐서 가야하지만 이 file_down.jsp 파일은 예외적으로 바로 JSP 파일로 가기


게시판 프로그램 : 댓글 작성

- 상세페이지에서 '댓글' 버튼 클릭시 "/BoardReplyAction.do" 요청을 get 방식으로 한다

- Controller 클래스에서 이 요청을 처리하고 댓글 작성폼 Service 클래스인 BoardReplyAction.java 로 가야함

 

게시판 프로그램 : 댓글 작성폼 Service 클래스

- 댓글 작성폼으로 가기 전, 부모글의 정보가 필요하고, 부모글의 정보를 가져올떄 DB 연동을 해야하므로 필요한 Service 클래스이다

- 댓글 작성폼으로 이동 전 필요하므로 이름을 "댓글 작성폼 Service 클래스" 로 한다

- service 패키지 안에 BoardReplyAction.java 클래스 생성 및 작성

- BoardReplyAction.java

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardReplyAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardReplyAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		
		// 부모글의 상세 정보 구하기
		BoardBean board = dao.getDetail(board_num);
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_reply.jsp");
		return forward;
	}

}

- 상세 페이지 View 인 qna_board_view.jsp 에서 넘어온 글번호 board_name 와 페이지번호를 받는다

- 댓글 처리 관련 변수 3개를 처리하기 위해서는 부모글의 상세 정보 (변수 3개)를 먼저 구해야하므로 getDetail() 사용

- getDetail() 의 매개변수로 부모 글의 번호가 될 board_name 를 전달한다

ex) 부모의 board_re_lev 에서 + 1 을 하는 등 부모의 정보 필요

- DB에서 받아온 부모 글의 정보를 저장한 객체 board 와 페이지번호 page 를 공유설정한다.

- 댓글 작성폼 View 페이지 qna_board_reply.jsp 로 포워딩한다, request 로 공유설정했으므로 dispatcher 방식으로 포워딩

 

Controller 클래스에서 댓글 작성폼 Service 클래스 BoardReplyAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardReplyAction.do 로 요청이 올때의 경우를 처리

		// 댓글 폼
		} else if(command.equals("/BoardReplyAction.do")) {
			try {
				action = new BoardReplyAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

 

- 댓글 작성폼 qna_board_reply.jsp 를 생성 및 작성하자

- qna_board_reply.jsp

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

<html>
<head>
	<title>댓글 게시판</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardReply.do" method="post">
<input type="hidden" name="board_num" value="${board.board_num}">
<input type="hidden" name="page" value="${page}">
<input type="hidden" name="board_re_ref" value="${board.board_re_ref}">
<input type="hidden" name="board_re_lev" value="${board.board_re_lev}">
<input type="hidden" name="board_re_seq" value="${board.board_re_seq}">
<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">댓글 게시판</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value="re."/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15"></textarea>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="댓글">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_write.jsp 의 내용 복붙 후 수정

- 첨부파일 부분을 삭제, enctype 도 삭제하고, action 값을 "/BoardReply.do" 로 수정

- 부모글의 댓글 관련 컬럼 3개와, 글번호, 페이지번호 총 5개를 hidden 으로 값을 전달해야한다

- 그리고 댓글 작성 폼에 입력된 값들도 전달

- 글번호는 부모글의 글 번호를 의미, 그래서 공유된 부모글 정보 객체인 board 에서 부모글의 글번호를 가져옴

 

게시판 프로그램 : 댓글 작성 Service 클래스

- 실제로 댓글을 작성하는 역할을 한다

- service 패키지 안에 BoardReply.java 클래스 생성 및 작성

- BoardReply.java 

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardReply implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardReply");
		
		request.setCharacterEncoding("utf-8");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		int board_re_ref = Integer.parseInt(request.getParameter("board_re_ref"));
		int board_re_lev = Integer.parseInt(request.getParameter("board_re_lev"));
		int board_re_seq = Integer.parseInt(request.getParameter("board_re_seq"));
		String page = request.getParameter("page");
		
		BoardBean board = new BoardBean();
		board.setBoard_num(board_num);
		board.setBoard_re_ref(board_re_ref);
		board.setBoard_re_lev(board_re_lev);
		board.setBoard_re_seq(board_re_seq);
		board.setBoard_name(request.getParameter("board_name"));
		board.setBoard_pass(request.getParameter("board_pass"));
		board.setBoard_subject(request.getParameter("board_subject"));
		board.setBoard_content(request.getParameter("board_content"));
		
		BoardDAO dao = BoardDAO.getInstance();
		int result = dao.boardReply(board); // 댓글 작성
		if(result == 1) System.out.println("댓글 작성 성공");
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(true);
		forward.setPath("./BoardListAction.do?page="+page);
		return forward;
	}

}

- 댓글 작성 폼에서 한글값이 넘어올 수 있으므로 한글값 인코딩

- hidden 으로 넘어온 5개 값과 댓글 작성 입력 양식에서 넘어온 값들도 request.getParameter() 로 받는다

- 생성한 DTO 객체 board에 받아온 값들( 부모 글 정보 + 댓글 작성 입력양식에 사용자가 작성한 값들) 을 setter 메소드로 세팅한다

- 부모 글에 관한 정보를 저장한 변수들은 SQL문 안에서 사용될것이므로 int 형으로 변환한다

- 댓글 작성 메소드인 boardReply() 메소드를 호출하면서 부모 글 정보와 입력한 데이터가 담긴 board 객체를 매개변수로 전달해줌

- 원래 페이지로 돌아가기 위해 목록을 가져오는 요청인 "/BoardListAction.do" 로 포워딩 하면서 get 방식으로 현재 페이지 번호를 넘겨준다, 이로 인해 원래 페이지로 돌아갈 수 있음

* DAO 클래스의 boardReply() 작성은 아래에

 

Controller 클래스에서 댓글 작성 Service 클래스 BoardReply로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardReply.do 로 요청이 올때의 경우를 처리

		// 댓글 작성
		} else if(command.equals("/BoardReply.do")) {
			try {
				action = new BoardReply();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

 

DAO 클래스 댓글 작성 메소드 작성

- BoardDAO.java 전체 코드는 모두 구현 후 올리기

- BoardDAO.java 추가된 boardReply() 부분 코드만

	// 댓글 작성
	public int boardReply(BoardBean board) {
		int result = 0;
		Connection con = null;
		PreparedStatement pstmt = null;
		
		// 부모글에 대한 정보
		int re_ref = board.getBoard_re_ref(); // 글 그룹 번호
		int re_lev = board.getBoard_re_lev(); // 댓글의 깊이
		int re_seq = board.getBoard_re_seq(); // 댓글의 출력순서
		
		try {
			con = getConnection();
			
			String sql = "update model2 set board_re_seq = board_re_seq + 1";
			sql += " where board_re_ref = ? and board_re_seq > ?";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setInt(1, re_ref);
			pstmt.setInt(2, re_seq);
			pstmt.executeUpdate(); // SQL문 실행
			
			sql = "insert into model2 values(model2_seq.nextval,";
			sql += "?,?,?,?,?,?,?,?,?,sysdate)";
			
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, board.getBoard_name());
			pstmt.setString(2, board.getBoard_pass());
			pstmt.setString(3, board.getBoard_subject());
			pstmt.setString(4, board.getBoard_content());
			pstmt.setString(5, ""); // board_file
			pstmt.setInt(6, re_ref); // board_re_ref
			pstmt.setInt(7, re_lev+1); // board_re_lev
			pstmt.setInt(8, re_seq+1); // board_re_seq
			pstmt.setInt(9, 0); // board_readcount
			result = pstmt.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();		
		} finally {
			if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
			if(con != null) try { con.close(); } catch(Exception e) {}			
		}
		return result;
	}

- 매개변수로는 부모글의 정보와 댓글 작성 폼에 적힌 데이터가 저장된 DTO 객체 board 가 넘어온다

- 부모글에 대한 3가지 정보를 변수에 저장

- 2가지 SQL 문을 사용해야 한다

<update 문>

- 댓글의 board_re_seq 는 자신의 부모글 보다 seq 값이 큰 글들만 seq 값을 1 씩 증가시킴

1) 부모가 원문이면 그 원문의 모든 댓글의 seq 값을 1 씩 증가

2) 부모가 댓글이면 그 부모보다 먼저 달린 형제 댓글들과 그 형제 댓글들의 대댓글들의 seq 값을 1씩 증가

<insert 문>

- 댓글 번호는 마찬가지로 sequence 로 넣는다

- 댓글의 board_re_ref 는 부모글의 board_re_ref 와 일치해야함

- 댓글의 board_re_lev 는 부모글의 board_re_lev 보다 1 커야함

- board_re_lev 와 board_re_seq 는 부모 글의 board_re_lev 와 board_re_seq 에서 1 증가한 값을 넣음

 

- 이제 상세 페이지에서 '댓글' 을 클릭해서 댓글 폼으로 간 후 댓글을 작성하자

 

문제점

- 안나온다

- 또 get 으로 전송하는데도 post 가 나옴

 

문제 해결

- BoardReply.java 의 setRedirect() 를 true 로 바꾸기

* 링크가 잘못되었다, 제목에 링크가 들어가야함, 추후 수정 완료

- 댓글 작성 후 목록페이지가 잘 나타난다

 

- 이제 댓글과 대댓글을 더 많이 써보자


게시판 프로그램 : 글 수정

- 수정 폼으로 넘어가기 전 DAO 에서 글의 상세정보를 불러와야한다

- 그러므로 "/BoardModifyAction.do" 로 요청함

 

- 상세 페이지 하단에서 '수정' 눌렀을때 수정폼 Service 클래스로 넘어가기 위해 "/BoardModifyAction" 로 요청

- qna_board_view.jsp 부분

<input type="button" value="수정"
onClick="location.href='./BoardModifyAction.do?board_num=${board.board_num}&page=${page}'">

 

- 상세페이지에서 글번호 board_num 과 페이지번호 page 가 넘어온다

 

 게시판 프로그램 : 글 수정폼 Service 클래스

- service 패키지 안에 글 수정폼 Service 클래스인 BoardModifyAction.java 를 생성 및 작성하자

- BoardModifyAction.java

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.BoardDAO;
import model.BoardBean;

public class BoardModifyAction implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardModifyAction");
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		String page = request.getParameter("page");
		
		BoardDAO dao = BoardDAO.getInstance();
		
		// 상세 정보 구하기
		BoardBean board = dao.getDetail(board_num);
		
		// 공유 설정
		request.setAttribute("board", board);
		request.setAttribute("page", page);
		
		ActionForward forward = new ActionForward();
		forward.setRedirect(false); // dispatcher 방식으로 포워딩
		forward.setPath("./board/qna_board_modify.jsp");
		return forward;
	}

}

- 수정폼으로 가기 전에 상세정보를 먼저 구해야 한다, 그 후에 수정폼으로 포워딩해야함

- 가장 먼저 상세페이지에서 get 방식으로 넘어온 글번호 board_num 과 페이지번호 page 를 가져온다

- DAO 의 getDetail() 메소드로 board_num 을 사용해서 글의 상세정보를 가져온다

- 가져온 상세정보를 받은 객체 board 를 공유설정하고, 페이지 번호 page 도 공유설정한다

+ 글 번호는 객체 board 안에도 있으므로 따로 공유설정 하지 않음

 

Controller 클래스에서 글 수정폼 Service 클래스 BoardModifyAction로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardModifyAction.do 로 요청이 올때의 경우를 처리

		// 수정 폼
		} else if(command.equals("/BoardModifyAction.do")) {
			try {
				action = new BoardModifyAction();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}
		}

 

- 다음은 글 수정폼 View 페이지인 qna_board_modify.jsp 파일을 생성하자

- qna_board_modify.jsp

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

<html>
<head>
	<title>게시판 수정</title>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="<%=request.getContextPath() %>/board/script.js"></script>
</head>
<body>

<form action="<%=request.getContextPath() %>/BoardModify.do" method="post">
<input type="hidden" name="board_num" value="${board.board_num}">
<input type="hidden" name="page" value="${page}">

<table cellpadding="0" cellspacing="0" align=center border=1>
	<tr align="center" valign="middle">
		<td colspan="5">게시판 수정</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">글쓴이</div>
		</td>
		<td>
			<input name="board_name" id="board_name" type="text" size="10" maxlength="10" 
				value="${board.board_name}"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">비밀번호</div>
		</td>
		<td>
			<input name="board_pass" id="board_pass" type="password" size="10" maxlength="10" 
				value=""/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12" height="16">
			<div align="center">제 목</div>
		</td>
		<td>
			<input name="board_subject" id="board_subject" type="text" size="50" maxlength="100" 
				value="${board.board_subject}"/>
		</td>
	</tr>
	<tr>
		<td style="font-family:돋음; font-size:12">
			<div align="center">내 용</div>
		</td>
		<td>
			<textarea name="board_content" id="board_content" cols="67" rows="15">${board.board_content}</textarea>
		</td>
	</tr>
	<tr bgcolor="cccccc">
		<td colspan="2" style="height:1px;">
		</td>
	</tr>
	<tr><td colspan="2">&nbsp;</td></tr>
	<tr align="center" valign="middle">
		<td colspan="5">			
			<input type=submit value="수정">
			<input type=reset value="취소">
		</td>
	</tr>
</table>
</form>

</body>
</html>

- qna_board_reply.jsp 파일의 내용을 복붙 후 수정

- 수정폼에 수정할 정보를 입력하고 '수정' 버튼 클릭시 글 수정 요청인 "/BoardModify.do" 로 요청이 간다 

- hidden 으로는 글 번호인 board_num 과 페이지번호 page 가 넘어간다

- 글 수정 요청인 "/BoardModify.do" 로 가면서 hidden 으로 2개, 입력양식으로 4개의 값들, 총 6개의 값이 글 수정 Service 클래스 BoardModify.java 로 전달됨

- Service 클래스 BoardModifyAction.java 에서 공유했던 board 객체에서 이름을 "${board.board_name}" 로 가져와서 입력양식에 value 속성으로 넣기

- 이름 뿐 아니라 제목, 내용도 마찬가지로 처리한다

 

- 구현 후 상세페이지에서 '수정' 클릭시 나타나는 수정폼에 정보들이 나타남

 

- 비밀번호를 입력하고 '수정' 버튼 클릭시 /BoardModify.do 로 요청함

 

- 실제 수정을 처리해주는 Service 클래스인 BoardModifyjava 클래스를 생성하자

 

 게시판 프로그램 : 글 수정 Service 클래스

- service 패키지 안에 글 수정 Service 클래스인 BoardModifyjava.java 를 생성 및 작성하자

- BoardModify.java (수정 전 1)

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BoardModify implements Action{

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		System.out.println("BoardModify");
		
		ActionForward forward = new ActionForward();
		return forward;
	}
}

- 아직 작성 안했고 여기까지 도착했는지 출력만 하고 있다, 내일 작성

 

Controller 클래스에서 글 수정 Service 클래스 BoardModify로 전달 (BoardFrontController.java 부분)

- BoardFrontController.java 전체 코드는 나중에

- BoardFrontController.java 부분

- BoardModify.do 로 요청이 올때의 경우를 처리

		// 글 수정
		} else if(command.equals("/BoardModify.do")) {
			try {
				action = new BoardModify();
				forward = action.execute(request, response);
			} catch (Exception e) {
				e.printStackTrace();				
			}	
		}

+ Recent posts