list.jsp

<div class="container">

	<div class="m-2">
		<form class="form-inline d-flex justify-content-end" action="/blog/board">
			<input type="hidden" name="cmd" value="search" /> 
			<input type="hidden" name="page" value="0" /> 
			<input type="text" name="keyword" class="form-control mr-sm-2" placeholder="Search">
			<button class="btn btn-primary m-1">검색</button>

		</form>
	</div>
	<div class="progress col-md-12 m-2">
		<div class="progress-bar" style="width: ${currentPosition}%"></div>
	</div>

위의 form 태그를 유심히 보면 action="/blog/board" 이후에

그 밑에 <input> 들의 name, value가 서버로 전송 될 것임을 알 수 있다.

 

action = "blog/board?cmd=search&page=0&keyword=[검색한단어]"

 

 

 

BoardController.java

else if(cmd.equals("search")) {
			String keyword = request.getParameter("keyword");
			int page = Integer.parseInt(request.getParameter("page"));

			List<Board> boards = boardService.글검색(keyword, page);
			request.setAttribute("boards", boards);

			int boardCount = boardService.글개수(keyword);
			int lastPage = (boardCount-1)/4; // 2/4 = 0, 3/4 = 0, 4/4 = 1, 9/4 = 2 ( 0page, 1page, 2page) 
			double currentPosition = (double)page/(lastPage)*100;



			request.setAttribute("lastPage", lastPage);
			request.setAttribute("currentPosition", currentPosition);
			RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
			dis.forward(request, response);
		}

cmd.equals("list")와 코드가 유사하다.

글검색() 의 결과가 저장된 boards 를 setAttribute 하여 dispatcher 를 이용해 list페이지로 이동하게 한다.

 

글검색 된 boards 도 페이지처리가 적용되어야 하기 때문에 글개수(keyword) 함수를 오버로딩하여 새로 만들어야 한다.

 

 

BoardService.java

public List<Board> 글검색(String keyword, int page){
		return boardDao.findByKeyword(keyword, page);
	}

public int 글개수(String keyword) {
		return boardDao.count(keyword);
	}

 

BoardDao.java

public List<Board> findByKeyword(String keyword, int page){
		String sql = "SELECT * FROM  board WHERE title like ? ORDER BY id DESC LIMIT ?, 4"; // 0,4   4,4   8,4
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		List<Board> boards = new ArrayList<>();
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%"+keyword+"%");
			pstmt.setInt(2, page*4); // 0 -> 0, 1 ->4, 2->8
			rs =  pstmt.executeQuery();

			// Persistence API
			while(rs.next()) { // 커서를 이동하는 함수
				Board board = Board.builder()
						.id(rs.getInt("id"))
						.title(rs.getString("title"))
						.content(rs.getString("content"))
						.readCount(rs.getInt("readCount"))
						.userId(rs.getInt("userId"))
						.createDate(rs.getTimestamp("createDate"))
						.build();
				boards.add(board);	
			}
			return boards;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}

		return null;
	}
    
    
    public int count(String keyword) {
		String sql = "SELECT count(*) FROM board WHERE title like ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;

		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "%"+keyword+"%");
			rs =  pstmt.executeQuery();
			if(rs.next()) {
				return rs.getInt(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return -1;
	}

BoardDao.list 와 코드가 유사하다.

 

 

 

list.jsp

<!-- disabled -->
	<ul class="pagination justify-content-center">

		<c:choose>
			<c:when test="${empty param.keyword}">
				<c:set var ="pagePrev" value="/blog/board?cmd=list&page=${param.page-1 }"></c:set>
				<c:set var="pageNext" value="/blog/board?cmd=list&page=${param.page+1}"/>
			</c:when>
			<c:otherwise>
				<c:set var="pagePrev" value="/blog/board?cmd=search&page=${param.page-1}&keyword=${param.keyword}"/>
				<c:set var="pageNext" value="/blog/board?cmd=search&page=${param.page+1}&keyword=${param.keyword}"/>
			</c:otherwise>
		</c:choose>

		<c:choose>
			<c:when test="${param.page == 0}">
				<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>	
			</c:when>
			<c:otherwise>
				<li class="page-item"><a class="page-link" href="${pageScope.pagePrev}">Previous</a></li>
			</c:otherwise>
		</c:choose>

		<c:choose>
			<c:when test="${lastPage == param.page}">
				<li class="page-item disabled"><a class="page-link" href="#">Next</a></li>		
			</c:when>
			<c:otherwise>
				<li class="page-item"><a class="page-link" href="${pageScope.pageNext}">Next</a></li>
			</c:otherwise>
		</c:choose>

		
	</ul>
</div>
</body>
</html>

페이지 버튼도 동적으로 바뀌게 한다. 이 부분이 가장 머리를 많이 쓰는 부분이긴 한데...

ㅠㅠ

detail.jsp

<div class="m-2">
	<c:if test="${sessionScope.principal.id == reply.userId }">
		<i onclick="deleteReply(${reply.id})" class="material-icons">delete</i>
	</c:if>
    </div>
</li>
</c:forEach>

delete 버튼이 나에게만 보이도록 c:if 문으로 userId를 비교함

 

 

boardDetail.js

function deleteReply(id){
	// 세션의 유저의 id와 reply의 userId를 비교해서 같을때만!!
	alert("댓글 아이디 : "+id);
	$.ajax({
		type : "post",
		url : "/blog/reply?cmd=delete&id="+id,
		dataType : "json"
	}).done(function(result) { //  { "statusCode" : 1 }
		if (result.statusCode == 1) {
			console.log(result);
			$("#reply-"+id).remove();
		} else {
			alert("댓글삭제 실패");
		}
	});
}


post 요청을 함, body 데이터는 필요없어서 data, ContentType 은 작성하지 않았음

 

 

 

 

ReplyDao.java

public int deleteById(int id) {
		String sql = "DELETE FROM reply WHERE id = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			int result = pstmt.executeUpdate();
			return result;

		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}

 

 

ReplyService.java

public int 댓글삭제(int id) {
		return replyDao.deleteById(id);
	}

 

 

ReplyController.java

else if(cmd.equals("delete")) {
			int id = Integer.parseInt(request.getParameter("id"));
			int result = replyService.댓글삭제(id);

			CommonRespDto commonDto = new CommonRespDto<>();
			commonDto.setStatusCode(result);  //1, -1

			Gson gson = new Gson();
			String jsonData = gson.toJson(commonDto);
			// { "statusCode" : 1 }
			Script.responseData(response, jsonData);
		}

쿼리스트링에 있는 id값을 id변수에 저장한다.

replyService.댓글삭제 의 return 값을 result 변수에 저장한다.

CommonRespDto<T>{

  int statusCode

  int T data

}

CommonRespDto 객체를 하나 만든다

새로만든 객체의 statusCode 의 값을 result로 set

data는 null 일 것이다.

 

객체를 toJson( Json하고싶은 JAVA Object ) 해서 응답한다. -> { "statusCode" : 1 }

( ajax deleteById 에게 jsonData를 result 로 응답한다. ) (항상 JSON으로 통신해야 함)

boardDetail.js - deleteById()

function deleteReply(id){
	// 세션의 유저의 id와 reply의 userId를 비교해서 같을때만!!
	//alert("댓글 아이디 : "+id);
	$.ajax({
		type : "post",
		url : "/blog/reply?cmd=delete&id="+id,
		dataType : "json"
	}).done(function(result) { //  { "statusCode" : 1 }
		if (result.statusCode == 1) {
			console.log(result);
			$("#reply-"+id).remove();
		} else {
			alert("댓글삭제 실패");
		}
	});
}

Script.responseData()

public static void responseData(HttpServletResponse response, String jsonData) {

		PrintWriter out;
		try {
			out = response.getWriter();
			out.print(jsonData);
			out.flush(); // 버퍼 비우기
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 

detail.jsp

<!-- 댓글 리스트 시작-->
	<ul id="reply__list" class="media-list">

		<c:forEach var="reply" items="${replys}">
			<!-- 댓글 아이템 -->
			<li id="reply-${reply.id}" class="media">
				<div class="media-body">
					<strong class="text-primary">${reply.userId}</strong>
					<p>${reply.content}</p>
				</div>
				<div class="m-2">
					<i onclick="deleteReply(${reply.id})" class="material-icons">delete</i>

				</div>
			</li>

		</c:forEach>


	</ul>
<!-- 댓글 리스트 끝-->

forEach 문으로 댓글들을 불러옵니다.

 

 

 

boardDetail.js

function replySave(userId, boardId) {
	var data = {
		userId : userId,
		boardId : boardId,
		content : $("#content").val()
	}
	$.ajax({
		type : "post",
		url : "/blog/reply?cmd=save",
		data : JSON.stringify(data),
		contentType : "application/json; charset=utf-8",
		dataType : "json"
	}).done(function(result) {
		if (result.statusCode == 1) {
			console.log(result);

			addReply(result.data);
			location.reload();
		} else {
			alert("댓글쓰기 실패");
		}
	});
}

location.reload(); 한 줄 추가

 

 

 

ReplyDao.java

public List<Reply> findAll(int boardId){
		String sql = "SELECT * FROM reply WHERE boardId = ? ORDER BY id DESC";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;

		List<Reply> replys = new ArrayList<>();
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, boardId);
			rs =  pstmt.executeQuery();

			// Persistence API
			while(rs.next()) { // 커서를 이동하는 함수
				Reply reply = new Reply();
				reply.setId(rs.getInt("id"));
				reply.setUserId(rs.getInt("userId"));
				reply.setBoardId(rs.getInt("boardId"));
				reply.setContent(rs.getString("content"));
				replys.add(reply);
			}
			return replys;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return null;
	}

 

 

 

ReplyService.java

public List<Reply> 글목록보기(int boardId){
		return replyDao.findAll(boardId);
	}

 

 

 

BoardController.java

protected void doProcess(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String cmd = request.getParameter("cmd");
		BoardrService boardService = new BoardrService();
        
		ReplyService replyService = new ReplyService();
        
		// http://localhost:8080/blog/board?cmd=saveForm
		HttpSession session = request.getSession();

doProcess 상단에 ReplyService 선언

 

else if(cmd.equals("detail")) {
			int id = Integer.parseInt(request.getParameter("id"));

			DetailRespDto dto = boardService.글상세보기(id); // board테이블+user테이블 = 조인된 데이터!!
			List<Reply> replys = replyService.글목록보기(id);

			if(dto == null) {
				Script.back(response, "상세보기에 실패하였습니다");
			}else {
				request.setAttribute("dto", dto);
				request.setAttribute("replys", replys);
				//System.out.println("DetailRespDto : "+dto);
				RequestDispatcher dis = request.getRequestDispatcher("board/detail.jsp");
				dis.forward(request, response);
			}
		}

detail (게시글 상세보기) 호출할 때 댓글 목록도 함께 가져오게하기

setAttribute replys 하여서 detail.jsp에서  jstl 사용할 수 있게하기

완전히 이해하려면 영상을 봐야할듯 합니다..

www.youtube.com/watch?v=9zJSkdY10oE&list=PL93mKxaRDidHvJs0PvxcZnUCrUYQZSzBT&index=30

 

detail.jsp

<!-- 댓글 박스 -->
  <div class="row bootstrap snippets">
    <div class="col-md-12">
      <div class="comment-wrapper">
        <div class="panel panel-info">
          <div class="panel-heading m-2"><b>Comment</b></div>
            <div class="panel-body">
              <input type="hidden" name="userId" value="${sessionScope.principal.id}" />
              <input type="hidden" name="boardId" value="${dto.id}" />
              <textarea id="content" id="reply__write__form" class="form-control" placeholder="write a comment..." rows="2"></textarea>
              <br>

              <button onClick="replySave(${sessionScope.principal.id}, ${dto.id})" class="btn btn-primary pull-right">댓글쓰기</button>

              <script>
							

		function replySave(userId, boardId){

                var data = {
                  userId: userId,
                  boardId: boardId,
                  content: $("#content").val()
                }
                $.ajax({
                  type: "post",
                  url: "/blog/reply?cmd=save",
                  data: JSON.stringify(data),
                  contentType: "application/json; charset=utf-8",
                  dataType: "json"
                }).done(function(result){
                  if(result.statusCode == 1){
                  $("#reply__list").prepend("<div>"+data.content+"</div>")
                }else{
                  alert("댓글쓰기 실패");
                }
               	 });
                }


                </script>
              <div class="clearfix"></div>
              <hr />

<!-- 댓글 리스트 시작-->

ajax를 사용하면 나중에 Controller에서 BufferedReader 로 읽을 수 있습니다.

 

 

 

ReplyDao.java

public int save(SaveReqDto dto) { // 회원가입
		String sql = "INSERT INTO reply(userId, boardId, content, createDate) VALUES(?,?,?, now())";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
        ResultSet rs = null;
		int generateKey;
		try {
			pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			pstmt.setInt(1, dto.getUserId());
			pstmt.setInt(2, dto.getBoardId());
			pstmt.setString(3, dto.getContent());
			int result = pstmt.executeUpdate();
			
            rs = pstmt.getGeneratedKeys();
			if(rs.next()) {
				generateKey = rs.getInt(1);
				System.out.println("생성된 키(ID) : "+generateKey);
				if(result == 1) {
					return generateKey;	
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}
    
    public Reply findById(int id){
		String sql = "SELECT * FROM reply WHERE id = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			rs =  pstmt.executeQuery();

			// Persistence API
			if(rs.next()) { // 커서를 이동하는 함수
				Reply reply = new Reply();
				reply.setId(rs.getInt("id"));
				reply.setUserId(rs.getInt("userId"));
				reply.setBoardId(rs.getInt("boardId"));
				reply.setContent(rs.getString("content"));
				return reply;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return null;
	}

sql 입니다.

 

 

SaveReqDto.java

import lombok.Data;

@Data
public class SaveReqDto {
	private int userId;
	private int boardId;
	private String content;
}

글저장에 필요한 데이터 전송 객체

 

 

ReplyService.java

import com.cos.blog.domain.reply.ReplyDao;
import com.cos.blog.domain.reply.dto.SaveReqDto;

public class ReplyService {

	private ReplyDao replyDao;

	public ReplyService() {
		replyDao = new ReplyDao();
	}

	public int 댓글쓰기(SaveReqDto dto) {
		return replyDao.save(dto);
	}
    
    public Reply 댓글찾기(int id) {
		return replyDao.findById(id);
	}
}

 

 

 

 

ReplyController.java (서블릿)

@WebServlet("/reply")
public class ReplyController extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public ReplyController() {
        super();
    }
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}

	protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String cmd = request.getParameter("cmd");
		ReplyService replyService = new ReplyService();
		// http://localhost:8080/blog/reply?cmd=save
		HttpSession session = request.getSession();

		if (cmd.equals("save")) {
			// ajax 요청으로 얻은 data를 Buffer로 받음
			BufferedReader br = request.getReader();
			String reqData = br.readLine();
			Gson gson = new Gson();
			SaveReqDto dto = gson.fromJson(reqData, SaveReqDto.class);
			System.out.println("dto : "+dto);

			CommonRespDto<Reply> commonRespDto = new CommonRespDto<>();
			Reply reply = null;
			int result = replyService.댓글쓰기(dto);
			if(result != -1) {
				reply = replyService.댓글찾기(result);
				commonRespDto.setStatusCode(1); //1, -1
				commonRespDto.setData(reply);
			}else {
				commonRespDto.setStatusCode(-1); //1, -1
			}
            
			String responseData = gson.toJson(commonRespDto); 
			System.out.println("responseData : "+responseData);
			Script.responseData(response, responseData);
		}
	}

}
function replySave(userId, boardId){

                var data = {
                  userId: userId,
                  boardId: boardId,
                  content: $("#content").val()
                }
                $.ajax({
                  type: "post",
                  url: "/blog/reply?cmd=save",
                  data: JSON.stringify(data),
                  contentType: "application/json; charset=utf-8",
                  dataType: "json"
                }).done(function(result){
                  if(result.statusCode == 1){
                  $("#reply__list").prepend("<div>"+data.content+"</div>")
                }else{
                  alert("댓글쓰기 실패");
                }
               	 });
                }

댓글쓰기 ajax가 호출되면 (cmd=save) 

BurrferedReader br에 ajax로 요청하면서 넘기는 값 { userId: ooo, boardId: ooo, content: #("content").val() }을

String 으로 reqData 변수 에 저장합니다

reqData = " { userId: ooo, boardId: ooo, content: #("content").val() } "

 

방금 만든 SaveReqDto 객체에 String reqData 를 gson.fromJson( json형태의 String , 변환할 객체.class ) 을 이용해서

JSON 데이터를 JAVA Object 화 시킵니다. ( 자바코드로 써먹을수가 있다. )

 

if-else : setStatusCode, setData해서 commonRespDto를 만든다.

 

Script.responseData() 를 써서 다시 gson.toJson( Json으로 변환하고싶은 JAVA Object ) 해서 응답한다.

(JAVA Object를 JSON으로 변환 시킵니다.)

 

 

 

 

 

Script.java

public static void responseData(HttpServletResponse response, String jsonData) {

		PrintWriter out;
		try {
			out = response.getWriter();
			out.print(jsonData);
			out.flush(); // 버퍼 비우기
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

.jsp 를 응답하는게아니라 data를 응답하기위해서 만든 함수입니다.

ReplyController 에서 사용하기위해 만들었습니다.

 

jsonData를 받아서 그냥 그대로 다시 PrintWriter 해서 응답해줍니다.

 

 

 

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

detail.jsp

<script> ajax 부분을 모두 지우고, 따로 외부 js파일로 만들것입니다.

 

<script src="/blog/js/boardDetail.js"></script>

 

boardDetail.js

function addReply(data){

	var replyItem = `<li id="reply-${data.id}" class="media">`;
	replyItem += `<div class="media-body">`;
	replyItem += `<strong class="text-primary">${data.userId}</strong>`;
	replyItem += `<p>${data.content}.</p></div>`;
	replyItem += `<div class="m-2">`;

	replyItem += `<i onclick="deleteReply(${data.id})" class="material-icons">delete</i></div></li>`;

	$("#reply__list").prepend(replyItem);
}

function deleteReply(id){
	// 세션의 유저의 id와 reply의 userId를 비교해서 같을때만!!
	alert("댓글 아이디 : "+id);
}

function replySave(userId, boardId) {

	var data = {
		userId : userId,
		boardId : boardId,
		content : $("#content").val()
	}

	$.ajax({
		type : "post",
		url : "/blog/reply?cmd=save",
		data : JSON.stringify(data),
		contentType : "application/json; charset=utf-8",
		dataType : "json"
	}).done(function(result) {
		if (result.statusCode == 1) {
			console.log(result);
			addReply(result.data);
			$("#content").val("");
		} else {
			alert("댓글쓰기 실패");
		}
	});
}


function deleteById(boardId){

	$.ajax({
		type: "post",
		url: "/blog/board?cmd=delete&id="+boardId,
		dataType: "json"
	}).done(function(result){
		console.log(result);
		if(result.statusCode == 1){
			location.href="index.jsp";
		}else{
			alert("삭제에 실패하였습니다.");
		}
	});
} 

댓글 삭제는 아직 미구현.

댓글이 prepend 되는것을  addReply 함수로 만들어서 적용시킴.

ㅎㅇ

 

 

detail.jsp

삭제버튼 옆에 수정버튼을 만들어줍시다

<div class="container">
	<c:if test="${sessionScope.principal.id == dto.userId}">
		<a href="/blog/board?cmd=updateForm&id=${dto.id}" class="btn btn-warning" >수정</a>
		<button onClick="deleteById(${dto.id})" class="btn btn-danger">삭제</button>
	</c:if>

 

 

 

updateForm.jsp

<%@page import="com.cos.blog.domain.user.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file="../layout/header.jsp" %>

<!-- 해당 페이지로 직접 URL(자원에 직접 파일.확장자) 접근을 하게 되면 또 파일 내부에서 세션 체크를 해야함. -->
<!-- 필터에 .jsp로 접근하는 모든 접근을 막아버리면 됨. -->

<div class="container">
	<form action="/blog/board?cmd=update" method="POST">
		<input type="hidden" name="id" value="${dto.id}" />
		<div class="form-group">
			<label for="title">Title:</label>
			<input type="text" class="form-control" placeholder="title" id="title" name="title"  value="${dto.title}" />
		</div>

		<div class="form-group">
			<label for="content">Content:</label>
			<textarea id="summernote" class="form-control" rows="5" id="content" name="content">
				${dto.content}
			</textarea>
		</div>

		<button type="submit" class="btn btn-primary">글쓰기 수정</button>
	</form>
</div>

  <script>
  	$('#summernote').summernote({
        placeholder: '글을 쓰세요.',
        tabsize: 2,
        height: 400
      });
  </script>
</body>
</html>

saveForm 복붙해서 만든겁니다. 

title 과 content에 작성했던 내용들이 있어야하니까 value = ${dto.title} 이렇게 넣어줬습니다

나중에 컨트롤러에서 setAttribute 해서 키값 dto로 줘야겠지요??

 

 

BoardDao.java

public int update(UpdateReqDto dto) {
		String sql = "UPDATE board SET title = ?, content = ? WHERE id = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, dto.getTitle());
			pstmt.setString(2, dto.getContent());
			pstmt.setInt(3, dto.getId());
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}

무수한 SQL문의 반복

 

 

UpdateReqDto.java

package com.cos.blog.domain.board.dto;

import lombok.Data;

@Data
public class UpdateReqDto {
	private int id;
	private String title;
	private String content;
}

글 수정에만 필요한 데이터 전송객체

 

 

 

BoardService.java

public int 글수정(UpdateReqDto dto) {
		return boardDao.update(dto);
	}

 

 

 

 

 

BoardController.java

else if(cmd.equals("updateForm")) {
			int id = Integer.parseInt(request.getParameter("id"));
			DetailRespDto dto = boardService.글상세보기(id);
			request.setAttribute("dto", dto);
			RequestDispatcher dis = request.getRequestDispatcher("board/updateForm.jsp");
			dis.forward(request, response);
		}else if(cmd.equals("update")) {
			int id = Integer.parseInt(request.getParameter("id"));
			String title = request.getParameter("title");
			String content = request.getParameter("content");

			UpdateReqDto dto = new UpdateReqDto();
			dto.setId(id);
			dto.setTitle(title);
			dto.setContent(content);

			int result = boardService.글수정(dto);

			if(result == 1) {
				// 고민해보세요. 왜 RequestDispatcher 안썻는지... 한번 써보세요. detail.jsp 호출
				response.sendRedirect("/blog/board?cmd=detail&id="+id);
			}else {
				Script.back(response,"글 수정에 실패하였습니다.");
			}
		}

updateForm 은 글 수정하기 페이지를 들어갔을 때 ,

update는 글 수정 완료 버튼을 눌렀을 때.

 

 

결과

 

 

하이요

 

 

detail.jsp

detail.jsp 상단에 삭제 버튼을 만들어 줍니다.

그리고 자바스크립트, 제이쿼리를 이용한 ajax 문법입니다.

 

이전의 게시물에 ajax 정의를 했었지만 또 하겠습니다.

ㅡㅡㅡㅡAjax 통신 정의ㅡㅡㅡㅡㅡ

Ajax (Async Javascript And XML)는 웹 페이지에서 새로운 데이터를 보여주려고 할 때 웹페이지 전체를 새로고침 하지 않고, 보여주고자 하는 데이터가 포함된 페이지의 일부 만을 로드 하기 위한 기법입니다.

Ajax는 비동기 처리 모델 (또는 non-blocking 이라고도 함)을 사용하여 데이터를 처리합니다.

 

동기 처리 모델에서 브라우저는 자바스크립트 코드를 만나면 스크립트를 처리하기 전까지 다른 작업을 일시 중지하고, 자바스크립트 코드의 처리가 끝난 후 기존 작업을 진행합니다.

반면에 Ajax를 사용하면 브라우저는 서버에 데이터를 요청한 뒤 페이지의 나머지를 로드하고 페이지와 사용자의 상호작용을 처리합니다.

 

Ajax 동작방식

 

  • 요청(request) - 브라우저가 서버에 정보를 요청한다.
    type : 요청방식 (get, post, delete, put)   //  url : 요청주소 ( Controller 에서 Servlet이 처리함 )

    data : 서버로 보낼 데이터. 

    contentType : 응답받을 데이터의 형태, 보통은 데이터를 처리하기위해 JSON 이겠지만 
    Controller의 결과값을 보면 ok , fail 의 단순 문자열이기때문에 text

    응답에 성공을 한다면, done(function(data){  '대충 실행될 명령'   } ) 분기로 가서
    응답받은 데이터( data << Ajax 함수의 data 아님. Ajax의 결과값임 )로 해야할 일을 처리합니다.

  • 서버의 동작 - 서버는 JSON, XML 등의 형식으로 데이터를 전달한다.
    최근에는 JSON을 가장 많이 사용하고 있습니다.

  • 응답(response) - 브라우저에서 이벤트가 발생하여 콘텐츠를 처리한다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

더보기

<c:if test="${sessionScope.principal.id == dto.userId}">

<button onClick="deleteById(${dto.id})" class="btn btn-danger">삭제</button>

</c:if>

 

<script>

function deleteById(boardId){

$.ajax({

type: "post",

url: "/blog/board?cmd=delete&id="+boardId,

dataType: "json"

}).done(function(result){

console.log(result);

if(result.statusCode == 1){

location.href="index.jsp";

}else{

alert("삭제에 실패하였습니다.");

}

});

}

</script>

 

<c:if test="${sessionScope.principal.id == dto.userId}">
		<button onClick="deleteById(${dto.id})" class="btn btn-danger">삭제</button>
	</c:if>

	<script>
		function deleteById(boardId){
			$.ajax({
				type: "post",
				url: "/blog/board?cmd=delete&id="+boardId,
				dataType: "json"
			}).done(function(result){
				console.log(result);
				if(result.statusCode == 1){
					location.href="index.jsp";
				}else{
					alert("삭제에 실패하였습니다.");
				}
			});
		}
	</script>

 

 

 

BoardDao.java

실제로 글이 지워지도록 sql작성

public int deleteById(int id) { // 회원가입
		String sql = "DELETE FROM board WHERE id = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}

 

 

BoardService.java

public int 글삭제(int id) {
		return boardDao.deleteById(id);
	}

 

 

CommonRespDto.java

모든 요청,응답을 이 통합Dto 하나로 관리합니다.

제네릭을 사용하네요.

package com.cos.blog.domain.board.dto;

import lombok.Data;

@Data
public class CommonRespDto<T> {
	private int statusCode; // 1, -1
	private T data;
}

 

 

BoardController.java

else if(cmd.equals("delete")) {
			int id = Integer.parseInt(request.getParameter("id"));

			// DB에서 id값으로 글 삭제
			int result = boardService.글삭제(id);

			// 응답할 json 데이터를 생성
			CommonRespDto<String> commonRespDto = new CommonRespDto<>();
			commonRespDto.setStatusCode(result);
			commonRespDto.setData("성공");

			Gson gson = new Gson();
			String respData = gson.toJson(commonRespDto);
			System.out.println("respData : "+respData);
			PrintWriter out = response.getWriter();
			out.print(respData);
			out.flush();
		}

 

CommonRespDto 는 알아두면 편리한데 제가 아직 개념정리가 덜되어있어서 설명이 어렵습니다.

gston.toJson( JSON으로 변환하고싶은 객체 )

 

 

 

 

결과

 

자신이 작성한 글에만 버튼이 보임.

ㅎㅇ

 

 

detail.jsp

${dto} 는 나중에 Controller 에서 setAttribute 해서 주는 key 값입니다.

댓글창은 아직 구현하지 않고 임시로 만들어놓습니다.

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

<%@ include file="../layout/header.jsp" %>

<div class="container">
	<br />
	<br />
	<h6 class="m-2">
		작성자 : <i>${dto.username}</i> 조회수 : <i>${dto.readCount}</i> 글 번호 : <i>${dto.id}</i>
	</h6>
	<br />
	<h3 class="m-2">
		<b>${dto.title}</b>
	</h3>
	<hr />
	<div class="form-group">
		<div class="m-2">${dto.content}</div>
	</div>

	<hr />

	<!-- 댓글 박스 -->
	<div class="row bootstrap snippets">
		<div class="col-md-12">
			<div class="comment-wrapper">
				<div class="panel panel-info">
					<div class="panel-heading m-2"><b>Comment</b></div>
					<div class="panel-body">
						<textarea id="reply__write__form" class="form-control" placeholder="write a comment..." rows="2"></textarea>
						<br>
						<button onclick="#" class="btn btn-primary pull-right">댓글쓰기</button>
						<div class="clearfix"></div>
						<hr />

						<!-- 댓글 리스트 시작-->
						<ul id="reply__list" class="media-list">

								<!-- 댓글 아이템 -->
								<li id="reply-1" class="media">		
									<div class="media-body">
										<strong class="text-primary">홍길동</strong>
										<p>
											댓글입니다.
										</p>
									</div>
									<div class="m-2">

										<i onclick="#" class="material-icons">delete</i>

									</div>
								</li>

						</ul>
						<!-- 댓글 리스트 끝-->
					</div>
				</div>
			</div>

		</div>
	</div>
	<!-- 댓글 박스 끝 -->
</div>

</body>
</html>

 

 

 

header.jsp

? 뭐에 쓰이는지 모르겠네요

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

 

 

 

 

DetailRespDto.java

글 상세보기 기능의 데이터운반을 도와주는 객체

package com.cos.blog.domain.board.dto;

import lombok.Data;

@Data
public class DetailRespDto {
	private int id;
	private String title;
	private String content;
	private int readCount;
	private String username;
	private int userId;
    
	public String getTitle() {
		return title.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}
}

 

 

 

BoardDao.java

findById(int id)

글 상세보기를 하기 위해서 Board 와 User를 join 한 결과에서 데이터를 가져옵니다.

왜냐하면 글에 대한 정보도 필요하고, 유저(작성자) 에 대한 정보도 필요하기 때문입니다.

 

updateReadCount(int id)

조회수를 1 증가시켜서 업데이트합니다.

public DetailRespDto findById(int id){
		StringBuffer sb = new StringBuffer();
		sb.append("select b.id, b.title, b.content, b.readCount, b.userId, u.username ");
		sb.append("from board b inner join user u ");
		sb.append("on b.userId = u.id ");
		sb.append("where b.id = ?");

		String sql = sb.toString();
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			rs =  pstmt.executeQuery();

			// Persistence API
			if(rs.next()) { // 커서를 이동하는 함수
				DetailRespDto dto = new DetailRespDto();
				dto.setId(rs.getInt("b.id"));
				dto.setTitle(rs.getString("b.title"));
				dto.setContent(rs.getString("b.content"));
				dto.setReadCount(rs.getInt("b.readCount"));
                dto.setUserId(rs.getInt("b.userId"));
				dto.setUsername(rs.getString("u.username"));
				return dto;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return null;
	}
    
    
    
    
    public int updateReadCount(int id) { // 회원가입
		String sql = "UPDATE board SET readCount = readCount+1 WHERE id = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}

 

BoardService.java

// 하나의 서비스안에 여러가지 DB관련 로직이 섞여 있죠.
	public DetailRespDto 글상세보기(int id) {
		// 조회수 업데이트치기
		int result = boardDao.updateReadCount(id);
		if(result == 1) {
			return boardDao.findById(id);
		}else {
			return null;
		}
	}

 

 

BoardController.java

list.jsp에서 상세보기 버튼을 누르게되면 cmd=detail 이 호출됩니다.

//list.jsp

<c:forEach var="board" items="${boards}">
		<div class="card col-md-12 m-2">
			<div class="card-body">
				<h4 class="card-title">${board.title}</h4>
				<a href="/blog/board?cmd=detail&id=${board.id}" class="btn btn-primary">상세보기</a>
			</div>
			<div>글 번호 : ${board.id }</div>
		</div>
	</c:forEach>

id값을 getParameter로 가져와서 자바코드에서 사용합니다.

글 상세보기(id)로 가져온 DetailRespDto 객체를 setAttribute 하여 dto 키값으로 jsp파일로 넘깁니다.

 

이 작업을 했기 때문에 detail.jsp 에서 ${dto} 를 사용할 수 있는것입니다.

else if(cmd.equals("detail")) {
			int id = Integer.parseInt(request.getParameter("id"));
			DetailRespDto dto = boardService.글상세보기(id); // board테이블+user테이블 = 조인된 데이터!!
			if(dto == null) {
				Script.back(response, "상세보기에 실패하였습니다");
			}else {
				request.setAttribute("dto", dto);
				//System.out.println("DetailRespDto : "+dto);
				RequestDispatcher dis = request.getRequestDispatcher("board/detail.jsp");
				dis.forward(request, response);
			}
		}

 

 

 

 

 

 

글 상세보기 결과

 

ㅎㅇㅎㅇㅎㅇ ㅠㅠ

 

 

 

list.jsp

jstl 문법의 choose - when / otherwise 를 이용해서 페이지 버튼의 disable 구현을 해보겠습니다.

currentPosition 과 lastpage 를 연산하는 코드는 아래에 있습니다 .

<div class="progress col-md-12 m-2">
		<div class="progress-bar" style="width: ${currentPosition}%"></div>
	</div>

	<!-- JSTL foreach문을 써서 뿌리세요. el표현식과 함께 -->
@@ -34,8 +34,25 @@
	<br />
	<!-- disabled -->
	<ul class="pagination justify-content-center">
		<c:choose>
			<c:when test="${param.page == 0}">
				<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>	
			</c:when>
			<c:otherwise>
				<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page-1}">Previous</a></li>
			</c:otherwise>
		</c:choose>

		<c:choose>
			<c:when test="${lastPage == param.page}">
				<li class="page-item disabled"><a class="page-link" href="#">Next</a></li>		
			</c:when>
			<c:otherwise>
				<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page+1}">Next</a></li>
			</c:otherwise>
		</c:choose>


	</ul>
</div>

 

 

BoardDao.java

board의 레코드 개수 (count 함수) 를 가져오는 함수입니다.

public int count() {
		String sql = "SELECT count(*), id FROM board";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;

		try {
			pstmt = conn.prepareStatement(sql);
			rs =  pstmt.executeQuery();
			if(rs.next()) {
				return rs.getInt(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return -1;
	}

 

 

BoardService.java

서비스 추가~

public int 글개수() {
		return boardDao.count();
	}

 

 

BoardController.java

list가 호출될때마다 lastpage 와 currentPosition 을 연산해줍니다. 

jsp에서도 사용할 수 있도록 setAttribute를 이용하여 키값과 데이터를 넘겨줍니다.

else if (cmd.equals("list")) {
			int page = Integer.parseInt(request.getParameter("page"));  // 최초 : 0, Next : 1, Next: 2
			List<Board> boards = boardService.글목록보기(page);
			request.setAttribute("boards", boards);

			// 계산 (전체 데이터수랑 한페이지몇개 - 총 몇페이지 나와야되는 계산) 3page라면 page의 맥스값은 2
			// page == 2가 되는 순간  isEnd = true
			// request.setAttribute("isEnd", true);
			int boardCount = boardService.글개수();
			int lastPage = (boardCount-1)/4; // 2/4 = 0, 3/4 = 0, 4/4 = 1, 9/4 = 2 ( 0page, 1page, 2page) 
			double currentPosition = (double)page/(lastPage)*100;

			request.setAttribute("lastPage", lastPage);
			request.setAttribute("currentPosition", currentPosition);
			RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
			dis.forward(request, response);
		}

 

 

 

3페이지 (3/4 페이지)

 

 

 

 

 

 

4페이지(마지막페이지)

 

ㅎㅇㅎㅇ

 

List.jsp

페이지 하단에 previous , next 를 완성시켜줍니다.

버튼을 누르면 page값을 변동하여 이동하게 되는거같네요

 

jstl 문법 : ${param.변수명} 으로 변수를 바로 사용할 수 있다.

( 이렇게 말해야 되는게 맞는가 ? )

<ul class="pagination justify-content-center">
		<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page-1}">Previous</a></li>
		<li class="page-item"><a class="page-link" href="/blog/board?cmd=list&page=${param.page+1}">Next</a></li>
	</ul>

 

Index.jsp

인덱스 페이지도 페이지 관리를 해주고.

<%
	RequestDispatcher dis = 
		request.getRequestDispatcher("board?cmd=list&page=0"); 
	dis.forward(request, response); // 톰켓이 생성하는 request와 response를 재사용한다. 다시 접근하는게 아니라 내부적으로 움직인다는 뜻.
%> 

 

 

BoardDao.java

LIMIT 을 추가해서 한 페이지에 게시글 4개만 불러와지도록 findAll 함수를 수정하겠습니다.

page 파라미터가 생겼어요.

public List<Board> findAll(int page){
		String sql = "SELECT * FROM  board ORDER BY id DESC LIMIT ?, 4"; // 0,4   4,4   8,4
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		List<Board> boards = new ArrayList<>();
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, page*4); // 0 -> 0, 1 ->4, 2->8
			rs =  pstmt.executeQuery();

			// Persistence API
			while(rs.next()) { // 커서를 이동하는 함수
				Board board = Board.builder()
						.id(rs.getInt("id"))
						.title(rs.getString("title"))
						.content(rs.getString("content"))
						.readCount(rs.getInt("readCount"))
						.userId(rs.getInt("userId"))
						.createDate(rs.getTimestamp("createDate"))
						.build();
				boards.add(board);	
			}
			return boards;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		
		return null;
	}

BoardService.java

page 파라미터가 생겼어요.

public List<Board> 글목록보기(int page){
		return boardDao.findAll(page);
	}

 

BoardController.java

page 파라미터가 생겼어요.

cmd=list 에서 page를 함께 받는것이기 때문에 이렇게 수정했어요.

else if (cmd.equals("list")) {
			int page = Integer.parseInt(request.getParameter("page"));  // 최초 : 0, Next : 1, Next: 2
			List<Board> boards = boardService.글목록보기(page);
			request.setAttribute("boards", boards);

			RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
			dis.forward(request, response);
		}

 

 

 

첫 페이지

 

 

마지막페이지

 

저는 미리 완성된 블로그로 사진을 캡쳐하고있어서, 양 끝 페이지에 버튼이 disable 되었습니다.

다음 게시물에서 소개해드리겠습니다.

ㅎㅇ

일단 티스토리 글쓰기 << ㅈㄴ 이상합니다 .

원래 지원하던 다음블로그 ? 에디터가 좋았는데 Flash 가 2021년에 지원중단되면서 티스토리가 지원하는 신 에디터로 글을 써야하거든요

신 에디터 사진넣는거, 이건 좋ㄷ ㅏ이말이야. 근데 글자 폰트조절을 못하겠다는거 >> 화난다 .

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

글 목록 보기를 구현해봅시다.

'글 목록' 을 볼려면 글이 저장되어있는 데이터베이스를 Select 해야할것입니다.

데이터베이스가 어떻게 생겼었는지 다시 면상이나 한 번 보죠 ~

사실 제가 글쓰는 텀이 너무 길어서 까먹었습니다.

 

MySQL

 

create user 'bloguser'@'%' identified by '비밀번호';
GRANT ALL PRIVILEGES ON *.* TO 'bloguser'@'%';
create database blog; 
use blog;


drop table user;
drop table board;
drop table reply;

CREATE TABLE user(
	id int primary key auto_increment,
    username varchar(100) not null unique,
    password varchar(100) not null,
    email varchar(100) not null,
    address varchar(100),
    userRole varchar(20),
    createDate timestamp
) ;

CREATE TABLE board(
	id int primary key auto_increment,
    userId int,
    title varchar(100) not null,
    content longtext,
    readCount int default 0,
    createDate timestamp,
    foreign key (userId) references user (id)
);

CREATE TABLE reply(
	id int primary key auto_increment,
    userId int,
    boardId int,
    content varchar(300) not null,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null,
    foreign key (boardId) references board (id) on delete cascade
);

우리가 봐야할 데이터는 Board .

 

 

BoardDao.java

public List<Board> findAll(){
		String sql = "SELECT * FROM  board ORDER BY id DESC";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		List<Board> boards = new ArrayList<>();
		try {
			pstmt = conn.prepareStatement(sql);
			rs =  pstmt.executeQuery();

			// Persistence API
			while(rs.next()) { // 커서를 이동하는 함수
				Board board = Board.builder()
						.id(rs.getInt("id"))
						.title(rs.getString("title"))
						.content(rs.getString("content"))
						.readCount(rs.getInt("readCount"))
						.userId(rs.getInt("userId"))
						.createDate(rs.getTimestamp("createDate"))
						.build();
				boards.add(board);	
			}
			return boards;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}

		return null;
	}

BoardDao에 findAll() 함수 추가.

DB에서 Board를 SELECT 해서 싸ㅡ악 가져오네요

 

BoardService.java

public List<Board> 글목록보기(){
		return boardDao.findAll();
	}

BoardService에 글목록보기() 함수 추가. ( List 를 반환합니다 ~ )

 

BoardController.java

else if (cmd.equals("list")) {
			List<Board> boards = boardService.글목록보기();
			request.setAttribute("boards", boards);
			RequestDispatcher dis = request.getRequestDispatcher("board/list.jsp");
			dis.forward(request, response);
		}

BoardController에 cmd.equlas("list") 코드 추가.

글목록보기 함수의 결과값을 boards  List변수에 담습니다.

jsp에서 boards 데이터를 쓸 수 있도록 하기위해서 request.setAttribute(key , value) 를 해줍니다.

 

 

 

List.jsp

 저 부분에 DB에서 가져온 Board 데이터를 뿌려보겠습니다. 

 

<div class="container">

	<div class="m-2">
		<form class="form-inline d-flex justify-content-end" action="/blog/board">
			<input type="hidden" name="cmd" value="search" />
			<input type="hidden" name="page" value="0" />

			<input type="text" name="keyword" class="form-control mr-sm-2" placeholder="Search">			
			<button class="btn btn-primary m-1">검색</button>

		</form>
	</div>

	<div class="progress col-md-12 m-2">
		<div class="progress-bar" style="width: 70%"></div>
	</div>

		<!-- JSTL foreach문을 써서 뿌리세요. el표현식과 함께 -->

	<c:forEach var="board" items="${boards}">
		<div class="card col-md-12 m-2">
			<div class="card-body">
				<h4 class="card-title">${board.title}</h4>
				<a href="/blog/board?cmd=detail&id=${board.id}" class="btn btn-primary">상세보기</a>
			</div>
            <div>글 번호 : ${board.id }</div>
		</div>
	</c:forEach>

	<br />
	<ul class="pagination justify-content-center">
		<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
		<li class="page-item"><a class="page-link" href="#">Next</a></li>
	</ul>
</div>

</body>
</html>

Controller 에서 jsp에서 사용하기위해서 setAttribute를 사용했다고 했었죠?

boards 라는 key 로 반복문(forEach) 을 사용하고있네요. 그리고 중간중간 ${board.title} 으로 

데이터를 get 쓰듯이 불러오고 있어요.

 

 

index.jsp에 sendRedirect를 dispatcher 로 고치셨나요 ??

<%
	RequestDispatcher dis = 
		request.getRequestDispatcher("board?cmd=list"); 
	dis.forward(request, response); // 톰켓이 생성하는 request와 response를 재사용한다. 다시 접근하는게 아니라 내부적으로 움직인다는 뜻.
%> 

 

 

 

 

 

결과

ㅎㅇ

 

글쓰기 작성페이지 SaveForm.jsp 에서는

<%@page import="com.cos.blog.domain.user.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file="../layout/header.jsp" %>

<!-- 해당 페이지로 직접 URL(자원에 직접 파일.확장자) 접근을 하게 되면 또 파일 내부에서 세션 체크를 해야함. -->
<!-- 필터에 .jsp로 접근하는 모든 접근을 막아버리면 됨. -->

<div class="container">
	<form action="/blog/board?cmd=save" method="POST">
		<input type="hidden" name="userId" value="${sessionScope.principal.id}" />
		<div class="form-group">
			<label for="title">Title:</label>
			<input type="text" class="form-control" placeholder="title" id="title" name="title">
		</div>
	
		<div class="form-group">
			<label for="content">Content:</label>
			<textarea id="summernote" class="form-control" rows="5" id="content" name="content"></textarea>
		</div>
	
		<button type="submit" class="btn btn-primary">글쓰기 등록</button>
	</form>
</div>
  <script>
  	$('#summernote').summernote({
        placeholder: '글을 쓰세요.',
        tabsize: 2,
        height: 400
      });
  </script>
</body>
</html>

type="hidden " userId 값을 전송해야한다.

그렇다면 value값은 어디서 정해지는걸까 ?

답 : ${ } (EL표현식)을 사용하여 EL내장객체인 SessionScope에 접근하여 가져온다.

 

 

else if(cmd.equals("login")) { //로그인 완료
			// 서비스 호출
			String username = request.getParameter("username");
			String password = request.getParameter("password");
			LoginReqDto dto = new LoginReqDto();
			dto.setUsername(username);
			dto.setPassword(password);
			
			User userEntity = userService.로그인(dto);
			if(userEntity != null) {
				HttpSession session = request.getSession();
				session.setAttribute("principal", userEntity); //인증주체
				//response.sendRedirect("index.jsp"); //로그인 성공
				RequestDispatcher dis = request.getRequestDispatcher("index.jsp");
				dis.forward(request, response);
			}else {
				Script.back(response, "로그인 실패");
			}
			
			
		}

key값인 principal 은 UserController에서 cmd.equals("login") 기능 내부에서

session.setAttribute() 되어서 RequestDispatcher를 사용하여 저장된다

 

 

 

이번 글에서 설명하고자 하는것은

"로그인을 하지않은 사용자가 글쓰기를 시도하려한다면?" 에 대한 방법입니다.

 

config 패키지 

ForbiddenUrlConfig.java

package com.cos.blog.config;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 이제부터는 내부에서의 모든 요청은 RequestDispatcher로 해야한다. 
// 그래야 다시 필터를 타지 않는다.

public class ForbiddenUrlConfig implements Filter{

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
    	HttpServletResponse response = (HttpServletResponse) resp;

    	System.out.println("ForbiddenUrlConfig 접근");
    	System.out.println(request.getRequestURL());
    	System.out.println(request.getRequestURI());

    	if(request.getRequestURI().equals("/blog/") || request.getRequestURI().equals("/blog/index.jsp")) {
    		chain.doFilter(request, response);
    	}else {
    		PrintWriter out = response.getWriter();
    		out.print("잘못된 접근입니다.");
    		out.flush();
    	}

	}
}

 

WEB-INF 폴더에 있는

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                       http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
	
	<description>MySQL Test App</description>
	<resource-ref>
		<description>DB Connection</description>
		<res-ref-name>jdbc/TestDB</res-ref-name>
		<res-type>javax.sql.DataSource</res-type>
		<res-auth>Container</res-auth>
	</resource-ref>
	
 	<filter>
		<filter-name>charConfig</filter-name>
		<filter-class>com.cos.blog.config.CharConfig</filter-class>
	</filter>

	<filter>
		<filter-name>forbiddenUrlConfig</filter-name>
		<filter-class>com.cos.blog.config.ForbiddenUrlConfig</filter-class>
	</filter>

	<!-- 매핑순서가 1 -->
	<filter-mapping>
		<filter-name>charConfig</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>



	<!-- 매핑순서가 2	 -->
	<filter-mapping>
		<filter-name>forbiddenUrlConfig</filter-name>
		<url-pattern>*.jsp</url-pattern>
	</filter-mapping>
</web-app>

만들었던 필터를 등록시킨다. 완전히 적용시키려면 서버를 재실행해야한다.

 

 

페이지 이동방법 설명

sendRedirect() = 외부로 갔다가 내부필터에 다시 들어옴

RequestDispatcher() = 톰캣이 생성하는 request, response를 재사용하여 덮어씌움

 

그래서 프로젝트 중에 사용했던 sendRedirect()를 모두 RequestDispatcher()로 바꿔주어야한다.

그렇지않으면 방금 만든 필터때문에 잘못된 접근 경고페이지를 보게될것이다.

바꾸어야할 파일 : UserController.java , BoardController.java .... 등등 sendRedirect() 사용했던 모든곳

 

예시

else if(cmd.equals("joinForm")) {
			//response.sendRedirect("user/joinForm.jsp");
			RequestDispatcher dis = 
					request.getRequestDispatcher("user/joinForm.jsp");
				dis.forward(request, response);
		}

 

 

 

ㅎㅇㅎㅇ

내용 정리할 시간이 너무 부족하네요 오랜만에 씁니다

 

blog 2 에서만들었던 빈깡통파일 list.jsp을 조금 채워봅시다.

list.jsp

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

<%@ include file="../layout/header.jsp" %>

<div class="container">

	<div class="m-2">
		<form class="form-inline d-flex justify-content-end" action="/blog/board">
			<input type="hidden" name="cmd" value="search" />
			<input type="hidden" name="page" value="0" />

			<input type="text" name="keyword" class="form-control mr-sm-2" placeholder="Search">			
			<button class="btn btn-primary m-1">검색</button>

		</form>
	</div>

	<div class="progress col-md-12 m-2">
		<div class="progress-bar" style="width: 70%"></div>
	</div>

		<div class="card col-md-12 m-2">
			<div class="card-body">
				<h4 class="card-title">제목</h4>
				<a href="#" class="btn btn-primary">상세보기</a>
			</div>
		</div>

	<br />
	<ul class="pagination justify-content-center">
		<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
		<li class="page-item"><a class="page-link" href="#">Next</a></li>
	</ul>
</div>

</body>
</html>

지금은 데이터가 있는 척~ 하면서 div - card 를 만들었지만 이후에, JSTL을 사용해서 글 목록을 불러와서 뿌릴겁니다.

 

 

글작성을 위한 페이지 작업

header.jsp

헤더부분에 summernote 를 사용하기위해 몇 줄 추가해줍니다.

<link
	href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css"
	rel="stylesheet">
<script
	src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script>
<%@ 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 lang="en">
<head>
<title>Cos 블로그</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script
	src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<link
	href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css"
	rel="stylesheet">
<script
	src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script>
</head>
<body>

	<nav class="navbar navbar-expand-md bg-dark navbar-dark">
		<a class="navbar-brand" href="<%=request.getContextPath()%>/index.jsp">블로그</a>
		<button class="navbar-toggler" type="button" data-toggle="collapse"
			data-target="#collapsibleNavbar">
			<span class="navbar-toggler-icon"></span>
		</button>
		<c:choose>
			<c:when test="${sessionScope.principal != null}">
				<div class="collapse navbar-collapse" id="collapsibleNavbar">
					<ul class="navbar-nav">
						<li class="nav-item"><a class="nav-link"
							href="<%=request.getContextPath()%>/board?cmd=saveForm">글쓰기</a></li>
						<li class="nav-item"><a class="nav-link"
							href="<%=request.getContextPath()%>/user?cmd=updateForm">회원정보</a>
						</li>
						<li class="nav-item"><a class="nav-link"
							href="<%=request.getContextPath()%>/user?cmd=logout">로그아웃</a></li>
					</ul>
				</div>
			</c:when>
			<c:otherwise>
				<div class="collapse navbar-collapse" id="collapsibleNavbar">
					<ul class="navbar-nav">
						<li class="nav-item"><a class="nav-link"
							href="<%=request.getContextPath()%>/user?cmd=joinForm">회원가입</a></li>
						<li class="nav-item"><a class="nav-link"
							href="<%=request.getContextPath()%>/user?cmd=loginForm">로그인</a></li>
					</ul>
				</div>
			</c:otherwise>
		</c:choose>
	</nav>
	<br>

여기서 이미 JSTL이 사용되고 있었네요 파일 상단에

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

이렇게 선언을 해주시면 jsp에서 java문법과 유사한 JSTL을 사용하실 수 있습니다.

<c: choose> , <c:when> , <c:otherwise> 가 사용되었네요 이 놈들은 if - else 처럼 사용되고있습니다.

 

 

<c:when test = "${sessionScope.principal != null}">

test 가 조건문입니다.  (principal : 로그인 인증 주체)

principal 이 null 이면 로그인 안한 상태 -> 로그인, 회원가입이 있는 View를 보여줌

principal 이 null 이 아니면 로그인 한 상태 -> 로그아웃, 글작성이 있는 View를 보여줌

 

saveForm.jsp

<%@page import="com.cos.blog.domain.user.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file="../layout/header.jsp" %>

<div class="container">
	<form action="#" method="POST">

		<form action="/blog/board?cmd=save" method="POST">
		<input type="hidden" name="userId" value="${sessionScope.principal.id}" />
			<label for="title">Title:</label>
			<input type="text" class="form-control" placeholder="title" id="title" name="title">
		</div>

		<div class="form-group">
			<label for="content">Content:</label>
			<textarea id="summernote" class="form-control" rows="5" id="content" name="content"></textarea>
		</div>

		<button type="submit" class="btn btn-primary">글쓰기 등록</button>
	</form>
</div>

  <script>
  	$('#summernote').summernote({
        placeholder: '글을 쓰세요.',
        tabsize: 2,
        height: 400
      });
  </script>
</body>
</html>

 

 

 

 

 

 

글작성준비 완료

제법 그럴싸해보이죠

이것이 라이브러리의 힘??

 

 

 

saveForm의 action에 cmd=save 를 해줬으니 Controller에 cmd.equals("save")의 기능을 만들어줘야합니다.

 

BoardController.java (Servlet 파일입니다)

package com.cos.blog.web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.cos.blog.domain.board.dto.SaveReqDto;
import com.cos.blog.domain.user.User;
import com.cos.blog.service.BoardrService;
import com.cos.blog.util.Script;

// http://localhost:8080/blog/board
@WebServlet("/board")
public class BoardController extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public BoardController() {
        super();
    }
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}

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

		String cmd = request.getParameter("cmd");
		BoardrService boardService = new BoardrService();
		// http://localhost:8080/blog/board?cmd=saveForm
		HttpSession session = request.getSession();
		if(cmd.equals("saveForm")) {
			User principal = (User) session.getAttribute("principal");
			if(principal != null) {
				response.sendRedirect("board/saveForm.jsp");
			}else {
				response.sendRedirect("user/loginForm.jsp");
			}	
		}else if(cmd.equals("save")) {
			int userId = Integer.parseInt(request.getParameter("userId"));
			String title = request.getParameter("title");
			String content = request.getParameter("content");

			SaveReqDto dto = new SaveReqDto();
			dto.setUserId(userId);
			dto.setTitle(title);
			dto.setContent(content);
			int result = boardService.글쓰기(dto);
			if(result == 1) { //정상
				response.sendRedirect("index.jsp");
			}else {
				Script.back(response, "글쓰기실패");
			}
		}
	}



}

SaveReqDto.java (글 저장'만'을 위한 Data Transfer Object)

package com.cos.blog.domain.board.dto;

import lombok.Data;

@Data
public class SaveReqDto {
	private int userId;
	private String title;
	private String content;
}

 

BoardDao.java (DB 작업)

package com.cos.blog.domain.board;

import java.sql.Connection;
import java.sql.PreparedStatement;

import com.cos.blog.config.DB;
import com.cos.blog.domain.board.dto.SaveReqDto;
import com.cos.blog.domain.user.dto.JoinReqDto;

public class BoardDao {

	public int save(SaveReqDto dto) { // 회원가입
		String sql = "INSERT INTO board(userId, title, content, createDate) VALUES(?,?,?, now())";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, dto.getUserId());
			pstmt.setString(2, dto.getTitle());
			pstmt.setString(3, dto.getContent());
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}
}

BoardService.java

package com.cos.blog.service;

import com.cos.blog.domain.board.BoardDao;
import com.cos.blog.domain.board.dto.SaveReqDto;
import com.cos.blog.domain.user.UserDao;

public class BoardrService {

	private BoardDao boardDao;

	public BoardrService() {
		boardDao = new BoardDao();
	}

	public int 글쓰기(SaveReqDto dto) {
		return boardDao.save(dto);
	}
}

 

 

 

글 작성의 흐름 설명

1. 사용자(새 클라이언트)가 블로그를 방문합니다

2. 회원가입 로그인을 이미 마친 상태이고, 글쓰기 버튼을 누릅니다. 

3. cmd=saveForm 이기 때문에 Controller의 cmd.equals("saveForm") 기능으로 분기됩니다. (글쓰기 페이지로 이동)

4. 로그인을 했으므로 userId 가 기본적으로 있고, 제목(title) , 내용 (content)를 작성하고 글작성완료 버튼을 누릅니다.

이때 cmd=save 이기 때문에 Controller의 cmd.equals("save") 기능으로 분기됩니다.

 

jsp에서 <form> 안에 있는 양식을 submit으로 웹 서버에 전송하게 되면 input타입에 name속성이 붙여진 값들을 getParameter함수로 값을 받아서 변수로 사용할 수 있게 됩니다.

 

saveForm.jsp

<form action="/blog/board?cmd=save" method="POST">
		<input type="hidden" name="userId" value="${sessionScope.principal.id}" />
			<label for="title">Title:</label>
			<input type="text" class="form-control" placeholder="title" id="title" name="title">
		</div>

		<div class="form-group">
			<label for="content">Content:</label>
			<textarea id="summernote" class="form-control" rows="5" id="content" name="content"></textarea>
		</div>

		<button type="submit" class="btn btn-primary">글쓰기 등록</button>
	</form>

input 타입의 name속성 : UserId, title, content

전송하고 싶은 값인데, 보이지 않았으면 좋겠다 -> type="hidden" 을 사용합니다.

 

BoardController.java

else if(cmd.equals("save")) {
			int userId = Integer.parseInt(request.getParameter("userId"));
			String title = request.getParameter("title");
			String content = request.getParameter("content");

			SaveReqDto dto = new SaveReqDto();
			dto.setUserId(userId);
			dto.setTitle(title);
			dto.setContent(content);
			int result = boardService.글쓰기(dto);
			if(result == 1) { //정상
				response.sendRedirect("index.jsp");
			}else {
				Script.back(response, "글쓰기실패");
			}
		}

 

request.getParameter( [form 안에있는 input의 name 값] ) 을 변수에 넣어서

내맘대로 사용할 수 있습니다.

글쓰기만을 위한 SaveReqDto에 넣어서 new SaveReqDto를 만들어주고 

boardService의 글쓰기함수에서 바로 방금만든 dto를 인수로 받아 사용합니다.

 

글쓰기 함수는 save(SaveReqDto)를 호출합니다

BoardService.java

package com.cos.blog.service;

import com.cos.blog.domain.board.BoardDao;
import com.cos.blog.domain.board.dto.SaveReqDto;
import com.cos.blog.domain.user.UserDao;

public class BoardrService {

	private BoardDao boardDao;

	public BoardrService() {
		boardDao = new BoardDao();
	}

	public int 글쓰기(SaveReqDto dto) {
		return boardDao.save(dto);
	}
}

save함수를 보겠습니다

BoardDao.java

package com.cos.blog.domain.board;

import java.sql.Connection;
import java.sql.PreparedStatement;

import com.cos.blog.config.DB;
import com.cos.blog.domain.board.dto.SaveReqDto;
import com.cos.blog.domain.user.dto.JoinReqDto;

public class BoardDao {

	public int save(SaveReqDto dto) { // 회원가입
		String sql = "INSERT INTO board(userId, title, content, createDate) VALUES(?,?,?, now())";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, dto.getUserId());
			pstmt.setString(2, dto.getTitle());
			pstmt.setString(3, dto.getContent());
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}
}

방금 만든 dto값으로 sql문을 만들어서 DB INSERT작업을 합니다.

INSERT의 결과값은 성공했으면 1 실패했다면 1 이외의 값을 return 하게 됩니다.

 

 

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

회원가입은 어떤식으로 작동할것 같은가?

1. VIEW에 회원가입을위한 정보를 작성한다.

2. 버튼을 눌러 서버로 DB에 저장해달라고 '요청' 한다.

3. 작성한 정보는 Model 객체로 만들어지고 , DAO를 통해 DB에 저장이 된다.

 

 

로그인은 어떤식으로 작동할것 같은가?

1. VIEW에 로그인을 위한 정보를 작성한다 (아이디, 패스워드)

2. DAO를 통해 작성한 정보를 바탕으로 SELECT를 한다.

3. 실행결과가 있다면 분기하여 세션을 저장하여 다른페이지로 이동할때 그 세션을 계속 불러오도록 한다(?)

 

제가 이해하기 편하도록, 제가 이해한대로 써봤습니다..

반드시 이렇다. 라고 생각하지는 말아주세요 불확실한 정보입니다

 

 

ㅎㅇ

 

정리해야될거 선(先)정리

1. 로그인페이지 , 회원가입 페이지로 이동하게 해주는 페이지

2. 로그인 Form , 회원가입 Form (아이디 중복검사, 주소검색)

3. 로그인과 회원가입만을 위한 객체 LoginReqDto , JoinReqDto 생성, 이해하기

 

나중에 게시판이되어 게시글을 보여줄

list.jsp 를 생성합니다.

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

<%@ include file="../layout/header.jsp" %>

</body>
</html>

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>인덱스 페이지입니다.</h1>
</body>
</html> 

<%
	response.sendRedirect("board/list.jsp");    
%> 

header.jsp (부트스트랩, 제이쿼리 선언)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Cos 블로그</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>
<body>

<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <a class="navbar-brand" href="#">블로그</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="collapsibleNavbar">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" href="#">회원가입</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">로그인</a>
      </li>    
    </ul>
  </div>  
</nav>
<br> 

 

 

 

대충 이런모양이 나옵니다

list.jsp

 

로그인을 누르면 나올 화면인 

loginFrom.jsp

를 생성합니다.

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

<%@include file="../layout/header.jsp" %>

<!-- x-www-form-urlencoded -->
<div class="container">
	<form action="/blog/user?cmd=login" method="post">
		<div class="form-group">
			<input type="text" id="username" name="username" class="form-control"
				placeholder="Enter Username" required"/>
		</div>

		<div class="form-group">
			<input type="password" name="password" class="form-control"
				placeholder="Enter Password" required />
		</div>
		<button type="submit" class="btn btn-primary">로그인완료</button>
	</form>
</div>
</body>
</html>

LoginForm.jsp

 

 

 

회원가입 화면인

JoinForm.jsp 

를 생성합니다

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

<%@include file="../layout/header.jsp"%>

<div class="container">
	<form action="/blog/user?cmd=join" method="post" onsubmit="return valid();">
		<div class="d-flex justify-content-end">
			<button type="button" class="btn btn-info" onClick="usernameCheck();">중복검사</button>
		</div>
		<div class="form-group">
			<input type="text" id="username" name="username" class="form-control"
				placeholder="Enter Username" required"/>
		</div>

		<div class="form-group">
			<input type="password" name="password" class="form-control"
				placeholder="Enter Password" required />
		</div>

		<div class="form-group">
			<input type="email" name="email" class="form-control"
				placeholder="Enter Email" required />
		</div>

		<div class="d-flex justify-content-end">
			<button type="button" class="btn btn-info" onClick="goPopup();">주소검색</button>
		</div>
		<div class="form-group">
			<input type="text"  name="address"  id="address" class="form-control" placeholder="Enter Address"  required/>
		</div>
		<button type="submit" class="btn btn-primary">회원가입완료</button>
	</form>
</div>


<script>
	var isChecking = false;

	function valid(){
		if(isChecking == false){
			alert("아이디 중복체크를 해주세요");
		}
		return isChecking;
	}
	function usernameCheck(){
		// DB에서 확인해서 username이 없다면 isChecking = true
		var username = $("#username").val();
		$.ajax({
			type: "POST",
			url: "/blog/user?cmd=usernameCheck",
			data: username,
			contentType: "text/plain; charset=utf-8",
			dataType: "text"  // 응답 받을 데이터의 타입을 적으면 자바스크립트 오브젝트로 파싱해줌.
		}).done(function(data){
			if(data === 'ok'){ // 유저네임 있다는 것
				isChecking = false;
				alert('유저네임이 중복되었습니다.')
			}else{
				isChecking = true;
				$("#username").attr("readonly", "readonly");
				alert("해당 유저네임을 사용할 수 있습니다.")
			}
		});
	}

	function goPopup() {
		// 주소검색을 수행할 팝업 페이지를 호출합니다.
		// 호출된 페이지(jusopopup.jsp)에서 실제 주소검색URL(https://www.juso.go.kr/addrlink/addrLinkUrl.do)를 호출하게 됩니다.
		
		var pop = window.open("/blog/user/jusoPopup.jsp", "pop",
				"width=570,height=420, scrollbars=yes, resizable=yes");
	}
	function jusoCallBack(roadFullAddr) {
		var addressEl = document.querySelector("#address");
		addressEl.value = roadFullAddr;
	}
</script>
</body>
</html>

 

ㅡㅡㅡㅡAjax 통신 정의ㅡㅡㅡㅡㅡ

Ajax (Async Javascript And XML)는 웹 페이지에서 새로운 데이터를 보여주려고 할 때 웹페이지 전체를 새로고침 하지 않고, 보여주고자 하는 데이터가 포함된 페이지의 일부 만을 로드 하기 위한 기법입니다.

Ajax는 비동기 처리 모델 (또는 non-blocking 이라고도 함)을 사용하여 데이터를 처리합니다.

 

동기 처리 모델에서 브라우저는 자바스크립트 코드를 만나면 스크립트를 처리하기 전까지 다른 작업을 일시 중지하고, 자바스크립트 코드의 처리가 끝난 후 기존 작업을 진행합니다.

반면에 Ajax를 사용하면 브라우저는 서버에 데이터를 요청한 뒤 페이지의 나머지를 로드하고 페이지와 사용자의 상호작용을 처리합니다.

 

Ajax 동작방식

function usernameCheck(){
		// DB에서 확인해서 username이 없다면 isChecking = true
		var username = $("#username").val();
		$.ajax({
			type: "POST",
			url: "/blog/user?cmd=usernameCheck",
			data: username,
			contentType: "text/plain; charset=utf-8",
			dataType: "text"  // 응답 받을 데이터의 타입을 적으면 자바스크립트 오브젝트로 파싱해줌.
		}).done(function(data){
			if(data === 'ok'){ // 유저네임 있다는 것
				isChecking = false;
				alert('유저네임이 중복되었습니다.')
			}else{
				isChecking = true;
				$("#username").attr("readonly", "readonly");
				alert("해당 유저네임을 사용할 수 있습니다.")
			}
		});
	}
//UserController.java  (Servlet)

else if(cmd.equals("usernameCheck")) { //유저네임 중복체크
			BufferedReader br = request.getReader(); //input 에 입력된 username을 읽음
			String username = br.readLine();
			System.out.println(username);
			//DB와 통신함 (service -> Dao)
			//Dao 에서 Select 해보고 int 결과값을 리턴해줌. 있으면 1 없으면 not 1
			int result = userService.유저네임중복체크(username); 
			
			PrintWriter out = response.getWriter();
			if(result == 1) {
				out.print("ok");
			}else {
				out.print("fail");
			}
			out.flush();
		}

 

  1. 요청(request) - 브라우저가 서버에 정보를 요청한다.
    바로 위에있는 JoinForm.jsp 에서 첫번째 Form의 중복검사 action이 usernameCheck() 함수를 가지고있는데,
    그 함수는 파일 하단에. 자바스크립트 함수로 정의되어 있습니다.

    이 함수가 JQuery를 이용한 Ajax 요청방법입니다. ( Web Front 에 입력된 데이터를 서버로 전송 )
    type : 요청방식 (get, post, delete, put)   //  url : 요청주소 ( Controller 에서 Servlet이 처리함 )

    data : 서버로 보낼 데이터. (위 함수에서는 var username = $("#username").val() 이 전송되는중)

    contentType : 응답받을 데이터의 형태, 보통은 데이터를 처리하기위해 JSON 이겠지만 
    Controller의 결과값을 보면 ok , fail 의 단순 문자열이기때문에 text

    응답에 성공을 한다면, done(function(data){  '대충 실행될 명령'   } ) 분기로 가서
    응답받은 데이터( data << Ajax 함수의 data 아님. Ajax의 결과값임 )로 해야할 일을 처리합니다.
    data를 result 로 바꾸는게 좀 보기 편할거같네요

  2. 서버의 동작 - 서버는 JSON, XML 등의 형식으로 데이터를 전달한다.
    최근에는 JSON을 가장 많이 사용하고 있습니다.

  3. 응답(response) - 브라우저에서 이벤트가 발생하여 콘텐츠를 처리한다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

 

JoinForm.jsp

 

 

명심할것!!!!!!! 컨트롤러 ↔ 서비스 ↔ DAO (DB)

User의 컨트롤을 담당 (로그인, 회원가입, 로그아웃)

UserController.java  (Servlet 파일)

package com.cos.blog.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;

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

import com.cos.blog.domain.user.User;
import com.cos.blog.domain.user.dto.JoinReqDto;
import com.cos.blog.domain.user.dto.LoginReqDto;
import com.cos.blog.service.UserService;
import com.cos.blog.util.Script;

//http://localhost:8000/blog/user
@WebServlet("/user")
public class UserController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
    public UserController() {
        super();
    }

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

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doProcess(request, response);
	}
	protected void doProcess(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		UserService userService = new UserService();
		String cmd = request.getParameter("cmd");

		// http://localhost:8000/blog/user?cmd=loginForm
		if(cmd.equals("loginForm")) { //로그인 페이지
			// 서비스 호출
			RequestDispatcher dis = request.getRequestDispatcher("user/loginForm.jsp");
			dis.forward(request, response);
			//response.sendRedirect("user/loginForm.jsp");
		}else if(cmd.equals("login")) { //로그인 완료
			// 서비스 호출
			String username = request.getParameter("username");
			String password = request.getParameter("password");
			LoginReqDto dto = new LoginReqDto();
			dto.setUsername(username);
			dto.setPassword(password);
			
			User userEntity = userService.로그인(dto);
			if(userEntity != null) {
				HttpSession session = request.getSession();
				session.setAttribute("principal", userEntity); //인증주체
				//response.sendRedirect("index.jsp"); //로그인 성공
				RequestDispatcher dis = request.getRequestDispatcher("index.jsp");
				dis.forward(request, response);
			}else {
				Script.back(response, "로그인 실패");
			}
			
			
		}else if(cmd.equals("joinForm")) { //회원가입 페이지
			RequestDispatcher dis = request.getRequestDispatcher("user/joinForm.jsp");
			dis.forward(request, response);
			//response.sendRedirect("user/joinForm.jsp");
			
		}else if(cmd.equals("join")) { //회원가입 완료
			// 서비스 호출
			String username = request.getParameter("username");
			String password = request.getParameter("password");
			String email = request.getParameter("email");
			String address = request.getParameter("address");
			JoinReqDto dto = new JoinReqDto();
			dto.setUsername(username);
			dto.setPassword(password);
			dto.setEmail(email);
			dto.setAddress(address);
			System.out.println("회원가입 : "+dto);
			int result = userService.회원가입(dto); //DB와 통신함 (service -> Dao)
			if(result == 1) {
				RequestDispatcher dis = request.getRequestDispatcher("index.jsp");
				dis.forward(request, response);
			}else {
				Script.back(response, "회원가입 실패 -1");
			}
		}else if(cmd.equals("usernameCheck")) { //유저네임 중복체크
			BufferedReader br = request.getReader();
			String username = br.readLine();
			System.out.println(username);
			int result = userService.유저네임중복체크(username); //DB와 통신함 (service -> Dao)
			
			PrintWriter out = response.getWriter();
			if(result == 1) {
				out.print("ok");
			}else {
				out.print("fail");
			}
			out.flush();
		}else if(cmd.equals("logout")) { //로그아웃
			HttpSession session = request.getSession();
			session.invalidate(); //즉시 세션 만료
			RequestDispatcher dis = request.getRequestDispatcher("index.jsp");
			dis.forward(request, response);
		}
	}
}

UserService.java 작성

package com.cos.blog.service;

import com.cos.blog.domain.user.User;
import com.cos.blog.domain.user.UserDao;
import com.cos.blog.domain.user.dto.JoinReqDto;
import com.cos.blog.domain.user.dto.LoginReqDto;
import com.cos.blog.domain.user.dto.UpdateReqDto;

public class UserService {
	private UserDao userDao;
	
	public UserService() {
		userDao = new UserDao();
	}
	
	
	public int 회원가입(JoinReqDto dto) {
		
		int result = userDao.save(dto);
		return result;
	}

	public User 로그인(LoginReqDto dto) {
		return userDao.findByUsernameAndPassword(dto);
	}

	public int 회원수정(UpdateReqDto dto) {

		return -1;
	}

	public int 유저네임중복체크(String username) {
		int result = userDao.findByUsername(username);
		return result;
	}
}

 

DB.java 에 아래의 함수 생성

public static void close(Connection conn, PreparedStatement pstmt) {
		try {
			conn.close();
			pstmt.close();
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

 

UserDao.java 작성 (대부분 SQL문)

 

public int save(JoinReqDto dto) { // 회원가입
		String sql = "INSERT INTO user(username, password, email, address, userRole, createDate) VALUES(?,?,?,?, 'USER', now())";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, dto.getUsername());
			pstmt.setString(2, dto.getPassword());
			pstmt.setString(3, dto.getEmail());
			pstmt.setString(4, dto.getAddress());
			int result = pstmt.executeUpdate();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt);
		}
		return -1;
	}
    
    public int findByUsername(String username) { //아이디 중복 체크
		String sql = "SELECT * FROM user WHERE username = ?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, username); //dto가 아닌 그냥 username
			rs =  pstmt.executeQuery(); //조회 결과가 1건이라도 있으면 1 , 없으면 -1

			if(rs.next()) { //1건이라도 있다 = true 일때 실행
				return 1; // 있어
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return -1; // 없어
	}
    
    public User findByUsernameAndPassword(LoginReqDto dto) { // 로그인
		String sql = "SELECT id, username, email, address FROM user WHERE username = ? and password =?";
		Connection conn = DB.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs  = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, dto.getUsername()); //dto가 아닌 그냥 username
			pstmt.setString(2, dto.getPassword());
			rs =  pstmt.executeQuery(); 

			// Persistence API
			if(rs.next()) { //1건이라도 있다 = true 일때 실행
				User user = User.builder()
						.id(rs.getInt("id"))
						.username(rs.getString("username"))
						.email(rs.getString("email"))
						.address(rs.getString("address"))
						.build();
				
				return user;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 무조건 실행
			DB.close(conn, pstmt, rs);
		}
		return null; // 없어
	}

 

한글 데이터 요청 , 응답시 한글 깨짐을 방지하기위한 

CharConfig.java 생성 (doFilter)

package com.cos.blog.config;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CharConfig implements Filter{

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
    	HttpServletResponse response = (HttpServletResponse) resp;

     	request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");

//		String username = request.getParameter("username");
//		System.out.println("username : "+username);
//		
//		PrintWriter out = response.getWriter();
//		out.println("안녕");
//		out.flush();
		chain.doFilter(request, response);
	}
}

 

 

뒤로가기를 함수로 따로 구현하기 위한 

Script.java 작성

package com.cos.blog.util;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

public class Script {
	public static void back(HttpServletResponse resp, String msg) throws IOException {
		PrintWriter out = resp.getWriter();
		resp.setContentType("text/html; charset=utf-8"); //utf-8로 응답하겠다
		out.println("<script>");
		out.println("alert('"+msg+"');");
		out.println("history.back();");
		out.println("</script>");
		out.flush();
	}

 

패키지정리는 알아보기쉽게.. (언젠간 한눈에 보일거같습니다)

 

Dto란?   Data Transfer Object 

데이터 교환만을 위한 객체로, DB에서 데이터를 얻어 Service나 Controller 등으터 보낼 때 사용하는 객체를 말한다.

User , Board 처럼 이미 모델이 만들어져있지만 각각 상황에 따라 일부 필드(변수)는 요구되지않는 요청이 생길 수 있다.

 

예를 들자면 로그인을 위한 데이터를 받고싶다고 하자. (아이디, 패스워드 만 있으면 됨)

그러나 User 모델은 로그인만을 하기위해 사용하기에는 쓸데없는 변수가 너무많다.

 

 

그럴때, 오로지 로그인만을 위한 DTO(Data Transfer Object : 데이터 전송 객체)를 만들어서 사용하는것이다.

 

미완성 게시물입니다. 이후 내용 추가

 

 

추가할 내용

유저네임 중복검사 , 로그인 구현

Ajax 통신 이해하기, (Reader, Writer)  이해하기,

(get,setAttribute) 이해하기,

RequestDispatcher 이해하기

 

집에서 새로 프로젝트 만들어서 내가 작성한내용 따라해서 리뷰하기.

개발환경 : STS4

서버 : Tomcat9.0

DB : MySQL

필요 준비물 : lombok.jar , mysql-connector-java-8.0.16.jar

 

 

앞으로의 시스템 흐름을 이해하기위해 가장 먼저 숙지 해야할 그림입니다. 

이번 시간에는 그림 아래부분에있는 DB를 생성하고 , 그것을 담을 Model객체를 만들어보겠습니다.

 

 

1. Dynamic Web Project - blog 생성 , Runtime Target = Tomcat9.0 으로 설정

더보기

저는 만드는 척만 하겠습니다.

1.a 21년 5월 23일 추가

옛날에는 새 프로젝트를 만들면 Dynamic Web Project 를 선택할 수 있었는데, STS의 버전이 업그레이드 되면서 해당 프로젝트의 생성을 권장하지 않아서 그런지 기본 선택목록에서 아예 제외된것 같습니다.

 

Web Project Plugin 설치방법

https://goodteacher.tistory.com/329

 

STS에서 Dynamic Web Project 개발

STS에서 Dynamic Web Project 개발 STS는 왜 이렇게 원래의 legacy 방밥을 통한 개발을 싫어할까? Boot를 밀고 싶은 맘이야 충분히 공감하지만 legacy도 좀 끼워주면 안되나 싶다. 사라져버린 Dynamic Web Program..

goodteacher.tistory.com

Help - install new software - ... 

 

 

2. MySQL Workbench 실행, 테이블생성 을 하기전에~~~~

유저 생성

더보기
create user 'bloguser'@'%' identified by '비밀번호';
GRANT ALL PRIVILEGES ON *.* TO 'bloguser'@'%';
create database blog;
use blog;

 

새로 만든 bloguser로 접속합시다

 

 

3. 테이블 생성

더보기
drop table user;
drop table board;
drop table reply;

CREATE TABLE user(
	id int primary key auto_increment,
    username varchar(100) not null unique,
    password varchar(100) not null,
    email varchar(100) not null,
    address varchar(100),
    userRole varchar(20),
    createDate timestamp
) ;

CREATE TABLE board(
	id int primary key auto_increment,
    userId int,
    title varchar(100) not null,
    content longtext,
    readCount int default 0,
    createDate timestamp,
    foreign key (userId) references user (id)
);

CREATE TABLE reply(
	id int primary key auto_increment,
    userId int,
    boardId int,
    content varchar(300) not null,
    createDate timestamp,
    foreign key (userId) references user (id) on delete set null,
    foreign key (boardId) references board (id) on delete cascade
);

 

 

 

4. Model 생성

더보기

com.cos.blog.domain 패키지 안에 board , reply , user 패키지생성.

각각의 패키지에 각각의 class 생성.

 

package com.cos.blog.domain.board;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Board {
	private int id;
	private int userId;
	private String title;
	private String content;
	private int readCount;
	private Timestamp createDate;
}
package com.cos.blog.domain.reply;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Reply {
	private int id;
	private int userId;
	private int boardId;
	private String content;
	private Timestamp createDate;
}
package com.cos.blog.domain.user;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
	private int id;
	private String username;
	private String password;
	private String email;
	private String address;
	private String userRole; // admin, user
	private Timestamp createDate;
}

 

lombok.jar 설치 하셔야합니다

 

 

 5. Dao(DataAccessObject) 생성

더보기

Dao란 무엇인가??

DB에 접근하여 조회, 삽입, 수정, 삭제의 기능만을 담당하는 객체입니다.

그림에서 Model과 DB 사이에 위치하고있습니다.

 

기능은 나중에 구현하고 빈 클래스만 만들어주세요

 

 

 6. DB연결 

tomcat.apache.org/tomcat-9.0-doc/index.html

 

Apache Tomcat 9 (9.0.41) - Documentation Index

This is the top-level entry point of the documentation bundle for the Apache Tomcat Servlet/JSP container. Apache Tomcat version 9.0 implements the Servlet 4.0 and JavaServer Pages 2.3 specifications from the Java Community Process, and includes many addit

tomcat.apache.org

Tomcat Document에 가보시면 DB연결을 하기위한 세팅방법이 나와있습니다.

설명해드리겠습니다.

 

 

더보기

META-INF 와 WEB-INF 에 context.xml , web.xml 을 만들어주세요.

xml 파일의 파일명은 반드시 소문자로하셔야합니다. 대문자를 인식하지못합니다.

 

두 개의  파일은 원래 Tomcat의 설정 파일인데 add 해서 사용한다는 느낌? 같습니다

 

 

 

1. context.xml

Document 에서 가져왔습니다

 주석은 필요없고 driverClassName이랑 url에 서버시간만 붙여주시면 됩니다

 

 

수정안하고 해도 가능하긴할텐데 cj.jdbc가 더 최신버전인거같습니다 .

<context>
	<Resource name="jdbc/TestDB" auth="Container"
		type="javax.sql.DataSource" maxTotal="100" maxIdle="30"
		maxWaitMillis="10000" username="bloguser" password="비밀번호"
		driverClassName="com.mysql.cj.jdbc.Driver"
		url="jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Seoul" />
</context> ​

 

 

 

2. web.xml

저도 방금 봤는데 버전 차이가 조금 있네요

밑에걸로 복붙 해주세요

 

 

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="4.0"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
	http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

	<description>MySQL Test App</description>
	<resource-ref>
		<description>DB Connection</description>
		<res-ref-name>jdbc/TestDB</res-ref-name>
		<res-type>javax.sql.DataSource</res-type>
		<res-auth>Container</res-auth>
	</resource-ref>

</web-app>

 

 3. DB.java   +   DBTest.java 생성

 

 

3-1 DB.java

 

package com.cos.blog.config;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;



public class DB {
	public static Connection getConnection() {
		try {
			Context initContext = new InitialContext();
			Context envContext  = (Context)initContext.lookup("java:/comp/env");
			DataSource ds = (DataSource)envContext.lookup("jdbc/TestDB");
			Connection conn = ds.getConnection();
			return conn;
		} catch (Exception e) {
			System.out.println("DB연결실패");
			e.printStackTrace();
		}
		return null;
	}

 

 

jdbc/oracle 과 jdbc/TestDB 가 다릅니다

DB연결하는 기능을 함수화 했습니다.

 

 

3-2 DBTest.java

프로젝트에서 우클릭 - Build Path

 

 

 

Add Library - JUnit4 선택

 

 

 

package com.cos.blog.test;

import static org.junit.Assert.assertNotNull;

import java.sql.Connection;

import org.junit.Test;

import com.cos.blog.config.DB;


public class DBTest {
	
		@Test
	public void DBConnectionTest() {
		Connection conn2 = DB.getConnection();
		assertNotNull(conn2);
	}
}

방금 DB에서 만든 getConnection() 함수를 사용합니다

 

 

 

 @Test 어노테이션을 이용하여 테스트를 할 수 있습니다. 

근데 JUnit 은 제가 아직 사용을 잘 안해봐서 모르겠습니다 

 

 

 

 

테스트 결과

옛날에 만든거라 사용법을 아직 잘 몰라서 어떻게 성공시키는지 모르겠네요

DB연결은 잘 되긴합니다

 

 

 

+ Recent posts