프로그래밍/Java

[Spring] 스프링 삽질일지 5일차 - 게시판 목록, 게시판 조회, 게시판 수정 및 삭제 기능 구현(미완성)

Be개발자 2021. 5. 4. 04:29

지난번까지 게시판 작성 기능을 구현하였다.

오늘 목표는 다음과 같다.

 

1. 게시판 목록 기능

2. 게시판 조회 기능

3. 게시판 수정 기능

 


1. 게시판 목록 기능

 

우리가 게시판 목록에서 조회하고자 하는 정보는 상품 이미지(IMAGE), 상품 설명(CONTENT), 가격(COST), 작성자(WRITER), 조회수(CLICK_CNT), 작성일(CREATE_DATE), 상태(STATE)이다.

 

메인화면에 존재 가능한 게시글의 개수는 총 3개이며, 각 글의 번호는 시간 오름차순으로 한다. 이때 STATE가 '판매중', '예약중'이 우선순위를 가지며, '판매완료'는 이 둘보다 뒤에 와야 한다.

 

즉 1순위는 STATE이고, STATE 안에서도 시간 순으로 정렬해야 한다.

 

boardMapper.xml에 들어가서 코드를 추가해준다.

 

<select id="list" resultType='com.board.domain.BoardVO'>
  SELECT ID, IMAGE, CONTENT, COST, WRITER, CLICK_CNT, CREATE_DATE, STATE
  FROM  BOARD_TABLE
  ORDER BY FIELD(STATE,'판매완료') ASC, CREATE_DATE ASC
</select>

BoardDAO로 들어가 boardMapper와 관련된 코드를 작성해준다.

package com.board.dao;

import java.util.List;

import com.board.domain.BoardVO;

public interface BoardDAO {
	
	// Write a post
	public void write(BoardVO boardVO) throws Exception;
	
	// Post list lookup
	public List<BoardVO> list() throws Exception;
}

 

그 후, BoardDAOImpl에서 list() 함수를 구현해준다.

package com.board.dao;

import java.util.List;

import javax.inject.Inject;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import com.board.domain.BoardVO;

@Repository
public class BoardDAOImpl implements BoardDAO {
	
	@Inject
	private SqlSession sqlSession;
	
	//Write a post
	@Override
	public void write(BoardVO boardVO) throws Exception {
		sqlSession.insert("boardMapper.insert", boardVO);
	}
	// Post list lookup
	@Override
	public List<BoardVO> list() throws Exception{
		return sqlSession.selectList("boardMapper.list");
	}
}

list() 함수의 반환값인 sqlSession.selectList("boardMapper.list")는 boardMapper.xml에서 mapper의 namespace가 boardMapper이고 그중에 id가 list인것을 갖고오라는 의미이다.

 

이제 BoardDAO와 연결되는 BoardService를 구현해줘야 한다.

// Post list lookup
public List<BoardVO> list() throws Exception;

// Post list lookup
public List<BoardVO> list() throws Exception{
	return dao.list();
}

 

이제 BoardController로 가 /list URL 경우에 대해 만들어 줘야 한다.

MariaDB > DAO > Service > Controller로 가져온 데이터들을 jsp에 출력하는 작업을 해야 한다.

model은 데이터를 담을 그릇이고 addAttribute("list", service.list())는 service.list()에 담긴 데이터를 "list"라는 이름으로 담을것이다 라는 뜻으로 해석하시면됩니다. 

 

이제 src/main/webapp/WEB-INF/views/board에 list.jsp를 새로 생성해준다.

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

	<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>
  <style>
    .container{
       
    }
     
    *{
      box-sizing: border-box;
    }
    .container{
      border:1px solid black;
    }
    .container>*{
      border:1px solid red;
    }
    .container>.content>*{
      border:1px solid green;
    }
    .container .content{
      padding-left:200px;
      padding-right:150px;
      overflow: hidden;
    }
    .container .content>*{
      float:left;
      padding-bottom:2500px;
      margin-bottom:-2500px;
    }
    .container .content main{
      width:100%;
    }
    .container .content nav{
      width:200px;
      margin-left: -100%;
      left:-200px;
      position: relative;
    }
    .container .content aside{
      width:150px;
      margin-right:-150px; 
    }
    .container footer{
      clear: both;
    }
     
    @media (max-width:599px){
      .container .content{
        padding-left:0;
        padding-right:0;
        overflow: visible;
      }
      .container .content>*{
        float:none;
        padding-bottom:0;
        margin-bottom:0;
      }
      .container .content main{
        width:100%;
      }
      .container .content nav{
        width:auto;
        margin-left: 0;
        left:0;
        position: relative;
      }
      .container .content aside{
        width:auto;
        margin-right:0; 
      }
    }
    
    ul{
   list-style:none;
   }
  </style>
</head>
 
<body>
  <div class="container">
    <header>
      <a href="http://localhost:8080"><h1>천둥 마켓</h1></a>
      
    </header>
    <section class="content">
      <main>
      
			<section id="container">
				<form role="form" method="post" action="/board/write">
					<table>
						<tr><th>번호</th><th>내용</th><th>가격</th><th>작성자</th><th>조회수</th><th>등록일</th><th>상태</th></tr>
						
						<c:forEach items="${list}" var = "list">
							<tr>
								<td><c:out value="${list.id}" /></td>
								<td><c:out value="${list.image}" /></td>
								<td><c:out value="${list.content}" /></td>
								<td><c:out value="${list.cost}" /></td>
								<td><c:out value="${list.writer}" /></td>							
								<td><c:out value="${list.click_cnt}" /></td>
								<td><fmt:formatDate value="${list.create_date}" pattern="yyyy-MM-dd"/></td>
								<td><c:out value="${list.state}" /></td>
							</tr>
						</c:forEach>
						
					</table>
				</form>
			</section>
	  	
      		
      </main>
      <nav>
 
		<h4>카테고리</h4>
	        <hr>

        <ul>
	        
	          <li>- ALL</li>
	          <li>- Toys</li>
	          <li>- Clothes</li>
	          <li>- Fruits</li>
	          <li>- Electronics</li>
	          <br>
	          <br>
	          <br>
	          <a href ="http://localhost:8080/board/writeView"><input type='button', value='글쓰기'/></a>
        </ul>
        
         
   
      </nav>
      <aside>
        여기는 광고자리입니당
      </aside>
    </section>
    <footer>
      FOOTER
    </footer>
  </div>
</body>
 
</html>

그 후, 다음과 같은 코드를 적어준 뒤, 서버를 실행한다.

서버 실행 후, http://localhost:8080/board/list 에 들어가보면 다음과 같이 현재 데이터베이스에 저장된 내역들이 출력되는 것을 확인할 수 있다.

 

세부적인 레이아웃은 추후에 고칠 예정이다... 현재 가장 큰 문제는 이미지가 파일명 그대로 저장되어버린 것...ㄸㄹㄹ

이거 또한 기능을 다 구현한 뒤에 고칠 예정이다...

그럼 이제 해야할 일은 좌측의 카테고리를 클릭하면 해당 카테고리에 해당하는 것들만 출력해주는 기능을 만들어볼 것이다.

 

1-1. 카테고리별 게시판 목록 출력

 

현재 카테고리 구성은 다음과 같다.

 

-ALL

-Toys

-Clothes

-Fruits

-Electronics

 

이는 위에서 다룬 게시판 목록 출력 방식과 작동 방식은 동일하지만, 유일하게 다른 것은 데이터베이스에서 정보를 얻어오는 boardMapper.xml 부분에 각 카테고리 별로 추가할까 생각 중이다.

 

<select id="list_Toys" resultType="com.board.domain.BoardVO">
  SELECT ID, IMAGE, CONTENT, COST, WRITER, CLICK_CNT, CREATE_DATE, STATE
  FROM  board_table
  WHERE CATEGORY = "Toys"
  ORDER BY FIELD(STATE,"판매완료") ASC, 
  CREATE_DATE ASC
</select>

<select id="list_Clothes" resultType="com.board.domain.BoardVO">
  SELECT ID, IMAGE, CONTENT, COST, WRITER, CLICK_CNT, CREATE_DATE, STATE
  FROM  board_table
  WHERE CATEGORY = "Clothes"
  ORDER BY FIELD(STATE,"판매완료") ASC, 
  CREATE_DATE ASC
</select>

<select id="list_Fruits" resultType="com.board.domain.BoardVO">
  SELECT ID, IMAGE, CONTENT, COST, WRITER, CLICK_CNT, CREATE_DATE, STATE
  FROM  board_table
  WHERE CATEGORY = "Fruits"
  ORDER BY FIELD(STATE,"판매완료") ASC, 
  CREATE_DATE ASC
</select>

<select id="list_Electronics" resultType="com.board.domain.BoardVO">
  SELECT ID, IMAGE, CONTENT, COST, WRITER, CLICK_CNT, CREATE_DATE, STATE
  FROM  board_table
  WHERE CATEGORY = "Electronics"
  ORDER BY FIELD(STATE,"판매완료") ASC, 
  CREATE_DATE ASC
</select>

 

com.board.dao 패키지의 BoardDAO.java에 다음 코드를 추가해준다.

// Post list lookup
public List<BoardVO> list() throws Exception;
	
// Post list_Toys lookup
public List<BoardVO> list_Toys() throws Exception;

// Post list_Clothes lookup
public List<BoardVO> list_Clothes() throws Exception;

// Post list_Fruits lookup
public List<BoardVO> list_Fruits() throws Exception;

// Post list_Electronics lookup
public List<BoardVO> list_Electronics() throws Exception;

BoardDAO에서 선언만 한 것을 BoardDAOImpl.java에서 구현해준다.

// Post list_Toys lookup
	@Override
	public List<BoardVO> list_Toys() throws Exception{
		return sqlSession.selectList("boardMapper.list_Toys");
	}
		
	// Post list_Clothes lookup
	@Override
	public List<BoardVO> list_Clothes() throws Exception{
		return sqlSession.selectList("boardMapper.list_Clothes");
	}
	
	
	// Post list_Fruits lookup
	@Override
	public List<BoardVO> list_Fruits() throws Exception{
		return sqlSession.selectList("boardMapper.list_Fruits");
	}
		
	// Post list_Electronics lookup
	@Override
	public List<BoardVO> list_Electronics() throws Exception{
		return sqlSession.selectList("boardMapper.list_Electronics");
	}

 

이제 BoardDAO와 연결되는 BoardService로 가 구현해준다. 사실상 모든 게시판 목록 출력방식과 똑같은 짓을 노가다로 반복하고 있는 것이다..

// Post list lookup
public List<BoardVO> list() throws Exception;

// Post list_Toys lookup
public List<BoardVO> list_Toys() throws Exception;

// Post list_Clothes lookup
public List<BoardVO> list_Clothes() throws Exception;

// Post list_Fruits lookup
public List<BoardVO> list_Fruits() throws Exception;

// Post list_Electronics lookup
public List<BoardVO> list_Electronics() throws Exception;

BoardDAO에서 선언한 내용을 BoardDAOImpl.java에서 구현해준다.

	// Post list_Toys lookup
	@Override
	public List<BoardVO> list_Toys() throws Exception{
		return dao.list_Toys();
	}
	
	// Post list_Clothes lookup
	@Override
	public List<BoardVO> list_Clothes() throws Exception{
		return dao.list_Clothes();
	}
	
	// Post list_Fruits lookup
	@Override
	public List<BoardVO> list_Fruits() throws Exception{
		return dao.list_Fruits();
	}
	
	// Post list_Electronics lookup
	@Override
	public List<BoardVO> list_Electronics() throws Exception{
		return dao.list_Electronics();
	}

 

BoardController.java에 아래 코드를 추가해준다.

// Post list_Toys lookup
@RequestMapping(value = "/list_Toys", method = RequestMethod.GET)
public String list_Toys(Model model) throws Exception{
logger.info("list_Toys");

model.addAttribute("list_Toys",service.list_Toys());


return "board/list_Toys";
}

// Post list_Clothes lookup
@RequestMapping(value = "/list_Toys", method = RequestMethod.GET)
public String list_Clothes(Model model) throws Exception{
logger.info("list_Clothes");

model.addAttribute("list_Clothes",service.list_Clothes());


return "board/list_Clothes";
}

// Post list_Fruits lookup
@RequestMapping(value = "/list_Fruits", method = RequestMethod.GET)
public String list_Fruits(Model model) throws Exception{
logger.info("list_Fruits");

model.addAttribute("list_Fruits",service.list_Fruits());


return "board/list_Fruits";
}

// Post list_Electronics lookup
@RequestMapping(value = "/list_Electronics", method = RequestMethod.GET)
public String list_Electronics(Model model) throws Exception{
logger.info("list_Electronics");

model.addAttribute("list_Electronics",service.list_Electronics());


return "board/list_Electronics";
}

 

이제 카테고리가 있었던 home.jsp, writeView.jsp, list.jsp에서 카테고리 부분 코드를 다음과 같이 고쳐준다.

 <a href ="http://localhost:8080/board/list"><li>- ALL</li></a>
 <a href ="http://localhost:8080/board/list_Toys"><li>- Toys</li></a>
 <a href ="http://localhost:8080/board/list_Clothes"><li>- Clothes</li></a>
 <a href ="http://localhost:8080/board/list_Fruits"><li>- Fruits</li></a>
 <a href ="http://localhost:8080/board/list_Electronics"><li>- Electronics</li></a>

 

그 후, src/main/webapp/WEB-INF/views/board에 각 카테고리별로 jsp 파일을 만들어준다.

list_Toys.jsp, list_Clothes.jsp, list_Fruits.jsp, list_Electronics.jsp

list_Toys.jsp, list_Clothes.jsp, list_Fruits.jsp, list_Electronics.jsp 코드는 전체적인 코드가 list.jsp와 유사하다. 여기서 고쳐줘야 하는 부분은 딱 이거 하나이다.

예를 들어 list_Clothes.jsp 파일의 경우 게시판 목록을 출력하는 부분인 for-each문의 items 값을 list_Clothes로 수정하면 된다. 나머지 list들에 대해서도 마찬가지이다.

 

 

2. 게시판 조회

 

 

게시판 목록에서 물건의 상태(state)를 클릭 시, 게시글로 진입하게 구현하고자 한다.

 

boardMapper.xml로 가 게시글을 조회하는 쿼리문을 작성해준다. 사용자가 state 버튼을 클릭했을 때 해당 게시글의 번호를 갖는 게시글을 조회하기 위한 코드이다.

 

<select id="read" parameterType="int" resultType="com.board.domain.BoardVO">
  SELECT	ID, IMAGE, WRITER, CREATE_DATE, CLICK_CNT, CONTENT, COST
  FROM board_table
  WHERE ID = #{id}
</select>

 

그 후, BoardDAO에 read 함수를 추가해준다.

// View Posts
public BoardVO read(int id) throws Exception;

 

BoardDAO에 선언만 한 read 함수를 BoardDAOImpl에서 구현해준다.

 

// View Posts
	@Override
	public BoardVO read(int id) throws Exception{
		return sqlSession.selectOne("boardMapper.read", id);
	}

 

이제 BoardService에 코드를 추가해준다.

// View Posts
	public BoardVO read(int id) throws Exception;

BoardServiceImpl에서 구현해준다.

// View Posts
	@Override
	public BoardVO read(int id) throws Exception{
		return dao.read(id);
	}

 

BoardController에 다음 코드를 추가해준다.

// View post
@RequestMapping(value = "/readView", method = RequestMethod.GET)
	public String read(BoardVO boardVO, Model model) throws Exception{
	logger.info("read");

	model.addAttribute("read", service.read(boardVO.getId()));

	return "board/readView";
}

 

boardVO.getId()를 통해 현재 게시글의 Id(고유번호)를 얻어와서 read라는 이름으로 값을 저장한다.

 

이제 JSP 파일을 만들어 주러 가자.

src/main/webapp/WEB-INF/views/board에 readView.jsp 파일을 생성해준다.

 

 

cmd 창을 켜서 netstat -ano 를 치니 8080 포트를 사용하고 있는 PID를 발견했다.

 

이제 이 프로세스를 taskkill /f /pid 4712 명령어를 통해 종료시키자. 참고로 프로세스를 종료시킬 땐 cmd를 관리자 권한으로 실행시켜야 한다. 관리자 권한은 cmd 검색 > 우클릭 > 관리자 권한으로 실행 으로 들어갈 수 있다. 명령어를 치면 pid 4712 프로세스가 종료된 것을 확인할 수 있다.

 

이제 서버를 다시 실행시켜보자!

음 잘 돌아간다~

 

라고 생각했는데 무언가 이상한걸 발견했다.

루트 주소가 '/'가 아닌 '/프로젝트명'으로 바뀌어 있다... 이런....  침착하게 예전에 쓴 방식을 다시 떠올렸다.

예전 방식

 

근데 이게 웬걸 context root가 '/'로 설정되어 있다.

그래서 그 다음으로 확인해본 것이 Servers 폴더의 server.xml 파일이다.

 

path가 "/assign"으로 되어있다... "/"로 수정해주자 ㅎㅎ

그러고 서버를 다시 실행하니 제대로 된다.

 

http://localhost:8080/board/list 에 들어간다.

그 후, 상품의 상태를 나타내는 것을 클릭하면

 

정상적으로 게시판 조회가 가능한 것을 확인할 수 있다. 게시글을 조회할 때 조회수가 증가하는 것은 추후에 구현할 예정이다.

 

3. 게시글 수정 및 삭제

 

게시글 수정 기능을 구현하기 전에 HeidiSQL에서 데이터가 제대로 변경이 되는지 확인해봤다.

ID가 9인 행은 WRITER가 "가나다"이다. 이를 다음 쿼리문을 통해 "가나다변경후"로 수정해준 뒤, 제대로 수정되었는지 확인해본다.

 

UPDATE board_table SET WRITER = "가나다변경후" WHERE ID = 9;
SELECT * FROM BOARD_TABLE

 

 

실행 결과 제대로 ID가 9일때 WRITER가 "가나다변경후"로 제대로 바뀐 것을 확인할 수 있다.

 

이제 데이터가 삭제되는지 확인해보자.

 

DELETE FROM board_table WHERE ID = 9;
SELECT * FROM BOARD_TABLE

 

 

ID가 9인 행이 사라진 것을 확인할 수 있다. 

데이터의 수정 및 삭제가 제대로 이뤄지는 것을 확인했다. 이제 스프링에서 이를 구현하기 위해 boardMapper,xml에 수정 역할을 하는 update와 삭제 역할을 하는 delete를 추가해준다.

<update id="update" parameterType="com.board.domain.BoardVO">
		UPDATE board_table
		WRITER, USERPASS, CATEGORY, CONTENT, COST, IMAGE, STATE
		   SET WRITER = #{writer},
		   	   USERPASS = #{content}
		   	   CATEGORY = #{category}
		   	   CONTENT = #{content}
		   	   COST = #{cost}
		   	   IMAGE = #{image}
		   	   STATE = #{state}
		   	   CREATE_DATE = #{create_date}
		 WHERE ID = #{id} 
	</update>
	
	<delete id="delete" parameterType="int">
		DELETE 
		  FROM board_table
		 WHERE ID = #{id}
	</delete>

 

BoardDAO에 추가해준다.

// Post Edit
	public void update(BoardVO boardVO) throws Exception;
	
	// Delete a post
	public void delete(int id) throws Exception;

 

BoardDAOImpl에 구현해준다.

// Post Edit
	@Override
	public void update(BoardVO boardVO) throws Exception{
		sqlSession.update("boardMapper.update", boardVO);
	}
		
	// Delete a post
	@Override
	public void delete(int id) throws Exception{
		sqlSession.delete("boardMapper.delete", id);
	}

 

BoardService에 코드를 추가한다.

// Post Edit
public void update(BoardVO boardVO) throws Exception;

// Delete a post
public void delete(int id) throws Exception;

 

BoardService에 구현한다.

// Post Edit
	@Override
	public void update(BoardVO boardVO) throws Exception{
		dao.update(boardVO);
	}
			
	// Delete a post
	@Override
	public void delete(int id) throws Exception{
		dao.delete(id);
	}

 

게시물을 수정하거나 삭제 이후, 게시글이 존재하던 카테고리로 리다이렉션하게 설정한다.

// Post Edit
	@RequestMapping(value = "/update", method = RequestMethod.POST)
	public String update(BoardVO boardVO) throws Exception{
		logger.info("update");
		
		service.update(boardVO);
		String category = boardVO.getCategory();
		
		return "redirect:/board/list_"+category;
	}

	// Delete a Post
	@RequestMapping(value = "/delete", method = RequestMethod.POST)
	public String delete(BoardVO boardVO) throws Exception{
		logger.info("delete");
		
		service.delete(boardVO.getId());
		String category = boardVO.getCategory();
		
		return "redirect:/board/list_"+category;
	}

 

readView.jsp로 간다.

수정, 삭제, 취소 버튼은 jquery로 제어할 것이기때문에 head안에 jqueryCDN을 추가해준다.

 

그리고 table 태그 아래에 버튼을 추가해준다.

 

<button type="submit" class="update_btn">게시물 수정</button>
<button type="submit" class="delete_btn">게시물 삭제</button>

 

 

 

 

이제 src/main/webapp/WEB-INF/views/board폴더에 updateView.jsp를 만들고 readView.jsp의 코드를 복사해온 뒤 head 태그 아래에 다음 코드를 추가해준다.

 

11번 게시글에 들어가 수정을 해보자.

게시물 수정 버튼을 눌러 수정화면으로 들어가자.

 

11번 게시글의 내용을 위와 같이 수정해준다. 카테고리를 Electronics로 수정해줬기 때문에 현재의 카테고리인 Electronic 리스트로 리다이렉션되는 것을 확인할 수 있다. 또한 내용이 정상적으로 수정된 것을 확인할 수 있다.

 

 

이제 게시글 삭제를 해보자

 

 

14번 게시글을 삭제해보겠다.(지수 사랑해요. 제 넷플릭스 프로필도 지수임)

현재 지수의 카테고리는 Clothes로 게시물 삭제 시 Clothes 리스트로 리다이렉션되어야 한다.

 

 

 

+) 2021.05.04

문제가 생겼다.

이렇게 코드를 작성하면 "수정"의 경우 category 값이 update된 category로 바뀌어 제대로 redirect되지만, "삭제"의 경우 boardVO 값이 삭제되면서 category가 null값이 된다. 그리하여 board/list_null 로 redirect되는 대참사가 일어난다.

이는 추후에 고쳐야 할 문제이다...

일단 board/list로 redirect 하게끔 수정해야겠다....


 

melonpeach.tistory.com/14?category=806570 (게시판 목록 기능)

haenny.tistory.com/53 (서버 루트 경로 변경)

melonpeach.tistory.com/17?category=806570  (게시글 수정 및 삭제 기능)

kuzuro.blogspot.com/2018/10/10.html (게시글 수정 및 삭제 기능)

 

으어 힘들어 죽겠다...

내일은 게시판 다듬기랑 페이징 구현을 해야겠다...... 화이팅...