55일차(1)/Spring(15) : Cafe 게시판 기능 구현 (목록보기, 새글 작성)
- Cafe 게시판 만들기
- Dao, Mapper 완성
- getList 메소드 (페이징처리, 검색기능)
- 새글 작성 메소드
cafe 기능 생성
- controller / dto / dao / service 패키지 생성
CafeDto
package com.sy.spring04.cafe.dto;
public class CafeDto {
private int num;
private String writer;
private String title;
private String content;
private int viewCount;
private String regdate;
private int startRowNum;
private int endRowNum;
private int prevNum; //이전 글의 글번호
private int nextNum; //다음 글의 글번호
public CafeDto() {}
public CafeDto(int num, String writer, String title, String content, int viewCount, String regdate, int startRowNum,
int endRowNum, int prevNum, int nextNum) {
super();
this.num = num;
this.writer = writer;
this.title = title;
this.content = content;
this.viewCount = viewCount;
this.regdate = regdate;
this.startRowNum = startRowNum;
this.endRowNum = endRowNum;
this.prevNum = prevNum;
this.nextNum = nextNum;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getViewCount() {
return viewCount;
}
public void setViewCount(int viewCount) {
this.viewCount = viewCount;
}
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
public int getStartRowNum() {
return startRowNum;
}
public void setStartRowNum(int startRowNum) {
this.startRowNum = startRowNum;
}
public int getEndRowNum() {
return endRowNum;
}
public void setEndRowNum(int endRowNum) {
this.endRowNum = endRowNum;
}
public int getPrevNum() {
return prevNum;
}
public void setPrevNum(int prevNum) {
this.prevNum = prevNum;
}
public int getNextNum() {
return nextNum;
}
public void setNextNum(int nextNum) {
this.nextNum = nextNum;
}
}
CafeDao 인터페이스
package com.sy.spring04.cafe.dao;
import java.util.List;
import com.sy.spring04.cafe.dto.CafeDto;
public interface CafeDao {
//글목록
public List<CafeDto> getList(CafeDto dto);
//글의 갯수
public int getCount(CafeDto dto);
//글 추가
public void insert(CafeDto dto);
//글정보 얻어오기
public CafeDto getData(int num);
//조회수 증가 시키기
public void addViewCount(int num);
//글 삭제
public void delete(int num);
//글 수정
public void update(CafeDto dto);
}
- 검색 키워드를 담아오기 위해 dto를 받아오는 메소드로 만든 것.
CafeDaoImpl
package com.sy.spring04.cafe.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.sy.spring04.cafe.dto.CafeDto;
@Repository
public class CafeDaoImpl implements CafeDao{
@Autowired
private SqlSession session;
@Override
public List<CafeDto> getList(CafeDto dto) {
/*
* 검색 기능은
* 1. 제목+내용
* 2. 제목
* 3. 작성자
* 검색 3가지 기능을 제공할 예정이다.
*/
return session.selectList("cafe.getList", dto);
}
@Override
public int getCount(CafeDto dto) {
return session.selectOne("cafe.getCount", dto);
}
@Override
public void insert(CafeDto dto) {
session.insert("cafe.insert", dto);
}
@Override
public CafeDto getData(int num) {
return session.selectOne("cafe.getData", num);
}
//조회수 올리는 메소드
@Override
public void addViewCount(int num) {
session.update("cafe.addViewCount", num);
}
@Override
public void delete(int num) {
session.delete("cafe.delete", num);
}
@Override
public void update(CafeDto dto) {
session.update("cafe.update", dto);
}
}
CafeMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cafe">
<sql id="searchCondition">
<choose>
<when test="title != null and content != null">
WHERE title LIKE '%'||#{title}||'%'
OR content LIKE '%'||#{content}||'%'
</when>
<when test="title != null">
WHERE title LIKE '%'||#{title}||'%'
</when>
<when test="writer != null">
WHERE writer LIKE '%'||#{writer}||'%'
</when>
</choose>
</sql>
<select id="getList" parameterType="cafeDto" resultType="cafeDto">
SELECT *
FROM
(SELECT result1.*, ROWNUM AS rnum
FROM
(SELECT num,writer,title,content,viewCount,regdate
FROM board_cafe
<include refid="searchCondition"/>
ORDER BY num DESC) result1)
<![CDATA[
WHERE rnum >= #{startRowNum} AND rnum <= #{endRowNum}
]]>
</select>
<select id="getCount" parameterType="cafeDto" resultType="int">
SELECT NVL(MAX(ROWNUM), 0)
FROM board_cafe
<include refid="searchCondition"/>
</select>
<insert id="insert" parameterType="cafeDto">
INSERT INTO board_cafe
(num, writer, title, content, viewCount, regdate)
VALUES(board_cafe_seq.NEXTVAL, #{writer}, #{title},
#{content}, #{viewCount}, SYSDATE)
</insert>
<select id="getData" parameterType="int" resultType="cafeDto">
SELECT num, writer, title, content, viewCount,
TO_CHAR(regdate, 'YY.MM.DD HH24:MI') AS regdate
FROM board_cafe
WHERE num=#{num}
</select>
<update id="addViewCount" parameterType="int">
UPDATE board_cafe
SET viewCount=viewCount+1
WHERE num=#{num}
</update>
<delete id="delete" parameterType="int">
DELETE FROM board_cafe
WHERE num=#{num}
</delete>
<update id="update" parameterType="cafeDto">
UPDATE board_cafe
SET title=#{title}, content='${content}'
WHERE num=#{num}
</update>
</mapper>
configuration에 mapper추가해두기
DaoImpl insert, getData 작성
@Override
public void insert(CafeDto dto) {
session.insert("cafe.insert", dto);
}
@Override
public CafeDto getData(int num) {
return session.selectOne("cafe.getData", num);
}
getData 작성
- num을 가지고가서 row 하나를 select 해오기
mapper - insert, getData
<insert id="insert" parameterType="cafeDto">
INSERT INTO board_cafe
(num, writer, title, content, viewCount, regdate)
VALUES(board_cafe_seq.NEXTVAL, #{writer}, #{title},
#{content}, #{viewCount}, SYSDATE)
</insert>
<select id="getData" parameterType="int" resultType="cafeDto">
SELECT num,writer,title,content,viewCount,TO_CHAR(regdate, 'YY.MM.DD HH24:MI') AS regdate,
FROM board_cafe
WHERE num=#{num}
</select>
- addViewCount, delete, update 문 작성
@Override
public void addViewCount(int num) {
session.update("cafe.addViewCount", num);
}
@Override
public void delete(int num) {
session.delete("cafe.delete", num);
}
@Override
public void update(CafeDto dto) {
session.update("cafe.update", dto);
}
mapper 작성
<update id="addViewCount" parameterType="int">
UPDATE board_cafe
SET viewCount=viewCount+1
WHERE num=#{num}
</update>
<delete id="delete" parameterType="int">
DELETE FROM board_cafe
WHERE num=#{num}
</delete>
<update id="update" parameterType="cafeDto">
UPDATE board_cafe
SET title=#{title}, content='${content}'
WHERE num=#{num}
</update>
- 작업에 실패했을 때의 처리는 모아서 할 예정!
- Spring에는 exception Controller가 따로 있다.여기서 모아서 처리한다.
- myBatis에는 ${ }도 있다. (jsp가 아니므로 EL은 아니다)
- 전처리 구문이라고 한다. 실행하기전에 미리 저렇게 작성해놓고 실행하는 것!
- #{ } 은 내부적으로 ?으로 바뀌고, 여기에 setString이 들어가는 것이다.
- ${ } 는 content안에 들어있는 내용을 이 sql문을 실행하기 전에 미리 실행해서 값을 불러와놓고
sql문의 실행을 시작하는 것이다.(그래서 따옴표로 감싸야한다)
- 안의 내용이 그대로 들어가므로 보안상의 문제가 발생할 수 있다.
- 어떤 변수에 특정 문자열을 넣어놓고 ${ }를 사용해서 집어넣을 수도 있다.
- mapper에 searchCondition 작성 (검색기능 관련)
<sql id="searchCondition">
<choose>
<when test="title != null and content != null">
WHERE title LIKE '%'||#{title}||'%'
OR content LIKE '%'||#{content}||'%'
</when>
<when test="title != null">
WHERE title LIKE '%'||#{title}||'%'
</when>
<when test="writer != null">
WHERE writer LIKE '%'||#{writer}||'%'
</when>
</choose>
</sql>
- 이런 경우에 전처리구문을 사용하면 || 연결연산자를 사용하지 않아도 되어서 편리하다!
- 실행될 때 이 자리에 해당 내용이 쏙 들어간다!
Dao - getList
@Override
public List<CafeDto> getList(CafeDto dto) {
/*
* 검색 기능은
* 1. 제목+내용
* 2. 제목
* 3. 작성자
* 검색 3가지 기능을 제공할 예정이다.
*/
return session.selectList("cafe.getList", dto);
}
- getList 메소드 작성. dto에 제목내용/제목/작성자 가 담겨서 넘어올 것이다.
- 이것을 바탕으로 동적 sql문을 작성해야 한다.
- sql문 작성, id도 부여
- 자주 쓰이는 내용을 이렇게 미리 저장해놓고 id를 사용해서 불러다가 사용할 수 있다.
- 동적으로 sql을 만들어주는 구문
- 이렇게 작성해서 빠르게 불러와 사용할 수 있다.
- include로 적어주면, 실행하기 전에 그 자리에 내용을 불러다놓고 실행한다고 생각하면 된다.
- 다른 file에 있으면 namespace. 형태로 점을 찍어서 찾아갈 수 있다
ex) file.searchCondition과 같이...
- 전체 글의 개수(getCount) 메소드에도 필요하다.
** <![CDATA[ ]]> 란? xml 문서에서 사용하는 문법이다.
- json vs xml 어플리케이션에서 자주 사용하는 문서는 둘 중 하나이다.
- json에서는 {"key" : value} 로 작성
- xml은 <key> value </key>, <data key="value" /> 형태로 작성
- 그렇기 때문에 xml문서는 <> 꺾쇠기호에 굉장히 예민하다.
- < 나 >가 하나라도 잘못 들어가 있으면 인식오류가 발생한다.
- 따라서 내용에 문제를 발생시킬 소지가 있다. sql문 안에 <!-- --> 주석을 적지 않는 이유!
- 연산자 중 >=, <= 이 기호가 문제를 일으킨다. xml문서이기 때문에!
- CDATA 블럭은 이런 문제를 방지하기 위해서 있는 기호이다.
< ! [CDATA [ ] ] >
- XML에서 만든 정해진 block이다. 이런 식으로 쓰고 이 안에 쿼리문을 적으면 이 안에서는 < > 를 인식하지 않는다.
- BETWEEN은 오라클에서만 써야한다. 그런데 <= >= 를 쓰면 오류가 날 수 있다.
→ 이럴 때 CDATA 기호를 사용한다. 에러를 발생시키지 않기 위해!
- DaoImpl - getCount 메소드 작성
- service도 만들어주면 된다.
CafeService 인터페이스생성
package com.sy.spring04.cafe.service;
import javax.servlet.http.HttpServletRequest;
import com.sy.spring04.cafe.dto.CafeDto;
public interface CafeService {
public void getList(HttpServletRequest request); //목록불러오기
public void getDetail(HttpServletRequest request);
public void saveContent(CafeDto dto); //글 저장
public void updateContent(CafeDto dto);
public void deleteContent(int num, HttpServletRequest request);
public void getData(HttpServletRequest request); //글 수정하기 위해 정보 불러오는 기능
}
CafeServiceImpl
package com.sy.spring04.cafe.service;
import java.net.URLEncoder;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import com.sy.spring04.cafe.dao.CafeDao;
import com.sy.spring04.cafe.dto.CafeDto;
@Service
public class CafeServiceImpl implements CafeService{
@Autowired
private CafeDao cafeDao;
@Override
public void getList(HttpServletRequest request) {
//한 페이지에 몇개씩 표시할 것인지
final int PAGE_ROW_COUNT=5;
//하단 페이지를 몇개씩 표시할 것인지
final int PAGE_DISPLAY_COUNT=5;
//보여줄 페이지의 번호를 일단 1이라고 초기값 지정
int pageNum=1;
//페이지 번호가 파라미터로 전달되는지 읽어와 본다.
String strPageNum=request.getParameter("pageNum");
//만일 페이지 번호가 파라미터로 넘어 온다면
if(strPageNum != null){
//숫자로 바꿔서 보여줄 페이지 번호로 지정한다.
pageNum=Integer.parseInt(strPageNum);
}
//보여줄 페이지의 시작 ROWNUM
int startRowNum=1+(pageNum-1)*PAGE_ROW_COUNT;
//보여줄 페이지의 끝 ROWNUM
int endRowNum=pageNum*PAGE_ROW_COUNT;
/*
[ 검색 키워드에 관련된 처리 ]
-검색 키워드가 파라미터로 넘어올수도 있고 안넘어 올수도 있다.
*/
String keyword=request.getParameter("keyword");
String condition=request.getParameter("condition");
//만일 키워드가 넘어오지 않는다면
if(keyword==null){
//키워드와 검색 조건에 빈 문자열을 넣어준다.
//클라이언트 웹브라우저에 출력할때 "null" 을 출력되지 않게 하기 위해서
keyword="";
condition="";
}
//특수기호를 인코딩한 키워드를 미리 준비한다.
String encodedK=URLEncoder.encode(keyword);
//FileDto 객체에 startRowNum 과 endRowNum 을 담는다.
CafeDto dto=new CafeDto();
dto.setStartRowNum(startRowNum);
dto.setEndRowNum(endRowNum);
//만일 검색 키워드가 넘어온다면
if(!keyword.equals("")){
//검색 조건이 무엇이냐에 따라 분기 하기
if(condition.equals("title_content")){//제목 + 내용 검색인 경우
dto.setTitle(keyword);
dto.setContent(keyword);
}else if(condition.equals("title")){ //제목 검색인 경우
dto.setTitle(keyword);
}else if(condition.equals("writer")){ //작성자 검색인 경우
dto.setWriter(keyword);
} // 다른 검색 조건을 추가 하고 싶다면 아래에 else if() 를 계속 추가 하면 된다.
}
//파일 목록을 select 해 온다.(검색 키워드가 있는경우 키워드에 부합하는 전체 글)
List<CafeDto> list=cafeDao.getList(dto);
//전체 글의 갯수(검색 키워드가 있는경우 키워드에 부합하는 전체 글의 갯수)
int totalRow=cafeDao.getCount(dto);
//하단 시작 페이지 번호
int startPageNum = 1 + ((pageNum-1)/PAGE_DISPLAY_COUNT)*PAGE_DISPLAY_COUNT;
//하단 끝 페이지 번호
int endPageNum=startPageNum+PAGE_DISPLAY_COUNT-1;
//전체 페이지의 갯수 구하기
int totalPageCount=(int)Math.ceil(totalRow/(double)PAGE_ROW_COUNT);
//끝 페이지 번호가 이미 전체 페이지 갯수보다 크게 계산되었다면 잘못된 값이다.
if(endPageNum > totalPageCount){
endPageNum=totalPageCount; //보정해 준다.
}
//응답에 필요한 데이터를 view page 에 전달하기 위해 request scope 에 담는다
request.setAttribute("list", list);
request.setAttribute("pageNum", pageNum);
request.setAttribute("startPageNum", startPageNum);
request.setAttribute("endPageNum", endPageNum);
request.setAttribute("totalPageCount", totalPageCount);
request.setAttribute("keyword", keyword);
request.setAttribute("encodedK", encodedK);
request.setAttribute("totalRow", totalRow);
request.setAttribute("condition", condition);
}
@Override
public void getDetail(HttpServletRequest request) {
// TODO Auto-generated method stub
}
@Override
public void saveContent(CafeDto dto) {
cafeDao.insert(dto);
}
@Override
public void updateContent(CafeDto dto) {
// TODO Auto-generated method stub
}
@Override
public void deleteContent(int num, HttpServletRequest request) {
// TODO Auto-generated method stub
}
@Override
public void getData(HttpServletRequest request) {
// TODO Auto-generated method stub
}
}
- 필드명을 cafeDao로 적어준다.
- 지금까지는 dao 하나에만 의존했지만, 여러개의 dao에 의존할 수도 있다. 구분하기 위해 이름을 다르게 작성!
- insert, 페이징처리, 검색 기능 구현하기
CafeController
package com.sy.spring04.cafe.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.sy.spring04.cafe.dto.CafeDto;
import com.sy.spring04.cafe.service.CafeService;
@Controller
public class CafeController {
@Autowired
private CafeService service;
@RequestMapping("/cafe/list")
public String list(HttpServletRequest request) {
service.getList(request);
return "cafe/list";
}
@RequestMapping("/cafe/insertform")
public String insertForm() {
return "cafe/insertform";
}
@RequestMapping("/cafe/insert")
public String insert(CafeDto dto, HttpSession session) {
//글 작성자는 세션에서 얻어낸다.
String writer=(String)session.getAttribute("id");
//dto는 글의 제목과 내용만 있으므로 글작성자는 직접 넣어준다.
dto.setWriter(writer);
service.saveContent(dto);
return "cafe/insert";
}
}
- 글 목록을 받아오면 service의 메소드에서 사용해준다.
service - getList 작성
@Override
public void getList(HttpServletRequest request) {
//한 페이지에 몇개씩 표시할 것인지
final int PAGE_ROW_COUNT=5;
//하단 페이지를 몇개씩 표시할 것인지
final int PAGE_DISPLAY_COUNT=5;
//보여줄 페이지의 번호를 일단 1이라고 초기값 지정
int pageNum=1;
//페이지 번호가 파라미터로 전달되는지 읽어와 본다.
String strPageNum=request.getParameter("pageNum");
//만일 페이지 번호가 파라미터로 넘어 온다면
if(strPageNum != null){
//숫자로 바꿔서 보여줄 페이지 번호로 지정한다.
pageNum=Integer.parseInt(strPageNum);
}
//보여줄 페이지의 시작 ROWNUM
int startRowNum=1+(pageNum-1)*PAGE_ROW_COUNT;
//보여줄 페이지의 끝 ROWNUM
int endRowNum=pageNum*PAGE_ROW_COUNT;
/*
[ 검색 키워드에 관련된 처리 ]
-검색 키워드가 파라미터로 넘어올수도 있고 안넘어 올수도 있다.
*/
String keyword=request.getParameter("keyword");
String condition=request.getParameter("condition");
//만일 키워드가 넘어오지 않는다면
if(keyword==null){
//키워드와 검색 조건에 빈 문자열을 넣어준다.
//클라이언트 웹브라우저에 출력할때 "null" 을 출력되지 않게 하기 위해서
keyword="";
condition="";
}
//특수기호를 인코딩한 키워드를 미리 준비한다.
String encodedK=URLEncoder.encode(keyword);
//FileDto 객체에 startRowNum 과 endRowNum 을 담는다.
CafeDto dto=new CafeDto();
dto.setStartRowNum(startRowNum);
dto.setEndRowNum(endRowNum);
//만일 검색 키워드가 넘어온다면
if(!keyword.equals("")){
//검색 조건이 무엇이냐에 따라 분기 하기
if(condition.equals("title_content")){//제목 + 내용 검색인 경우
dto.setTitle(keyword);
dto.setContent(keyword);
}else if(condition.equals("title")){ //제목 검색인 경우
dto.setTitle(keyword);
}else if(condition.equals("writer")){ //작성자 검색인 경우
dto.setWriter(keyword);
} // 다른 검색 조건을 추가 하고 싶다면 아래에 else if() 를 계속 추가 하면 된다.
}
//파일 목록을 select 해 온다.(검색 키워드가 있는경우 키워드에 부합하는 전체 글)
List<CafeDto> list=cafeDao.getList(dto);
//전체 글의 갯수(검색 키워드가 있는경우 키워드에 부합하는 전체 글의 갯수)
int totalRow=cafeDao.getCount(dto);
//하단 시작 페이지 번호
int startPageNum = 1 + ((pageNum-1)/PAGE_DISPLAY_COUNT)*PAGE_DISPLAY_COUNT;
//하단 끝 페이지 번호
int endPageNum=startPageNum+PAGE_DISPLAY_COUNT-1;
//전체 페이지의 갯수 구하기
int totalPageCount=(int)Math.ceil(totalRow/(double)PAGE_ROW_COUNT);
//끝 페이지 번호가 이미 전체 페이지 갯수보다 크게 계산되었다면 잘못된 값이다.
if(endPageNum > totalPageCount){
endPageNum=totalPageCount; //보정해 준다.
}
//응답에 필요한 데이터를 view page 에 전달하기 위해 request scope 에 담는다
request.setAttribute("list", list);
request.setAttribute("pageNum", pageNum);
request.setAttribute("startPageNum", startPageNum);
request.setAttribute("endPageNum", endPageNum);
request.setAttribute("totalPageCount", totalPageCount);
request.setAttribute("keyword", keyword);
request.setAttribute("encodedK", encodedK);
request.setAttribute("totalRow", totalRow);
request.setAttribute("condition", condition);
}
- mapper에서 동적 sql문으로 때에 따라 필요한 내용만 들어갈 수 있게 한다.
- when문 안에 들어있는 부분이 해당 구문이 들어가는 조건!
- 그러면 service에서 원하는 조건에 맞는 데이터만 불러오도록 한다.
뷰페이지 - 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" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/views/cafe/list.jsp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<a href="${pageContext.request.contextPath }/cafe/insertform">새글 작성</a>
<h3>CAFE 게시판 목록 보기</h3>
<table class="table table-striped">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>조회수</th>
<th>등록일</th>
<th>삭제</th>
</tr>
</thead>
<tbody>
<c:forEach var="tmp" items="${list }">
<tr>
<td>${tmp.num }</td>
<td>${tmp.writer }</td>
<td>
<a href="detail?num=${tmp.num }">${tmp.title }</a>
</td>
<td>${tmp.viewCount }</td>
<td>${tmp.regdate }</td>
<td>
<c:if test="${tmp.writer eq sessionScope.id }">
<a href="javascript:deleteConfirm(${tmp.num })">삭제</a>
</c:if>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<nav>
<ul class="pagination">
<%--
startPageNum 이 1 이 아닌 경우에만 Prev 링크를 제공한다.
&condition=${condition}&keyword=${encodedK}
--%>
<c:if test="${startPageNum ne 1 }">
<li class="page-item">
<a class="page-link" href="list?pageNum=${startPageNum-1 }&condition=${condition}&keyword=${encodedK}">Prev</a>
</li>
</c:if>
<c:forEach var="i" begin="${startPageNum }" end="${endPageNum }">
<li class="page-item ${pageNum eq i ? 'active' : '' }">
<a class="page-link" href="list?pageNum=${i }&condition=${condition}&keyword=${encodedK}">${i }</a>
</li>
</c:forEach>
<%--
마지막 페이지 번호가 전체 페이지의 갯수보다 작으면 Next 링크를 제공한다.
--%>
<c:if test="${endPageNum lt totalPageCount }">
<li class="page-item">
<a class="page-link" href="list?pageNum=${endPageNum+1 }&condition=${condition}&keyword=${encodedK}">Next</a>
</li>
</c:if>
</ul>
</nav>
<!-- 검색 폼 -->
<form action="list" method="get">
<label for="condition">검색조건</label>
<select name="condition" id="condition">
<option value="title_content" ${condition eq 'title_content' ? 'selected' : '' }>제목 + 내용</option>
<option value="title" ${condition eq 'title' ? 'selected' : '' }>제목</option>
<option value="writer" ${condition eq 'writer' ? 'selected' : '' }>작성자</option>
</select>
<input type="text" name="keyword" placeholder="검색어..." value="${keyword }"/>
<button type="submit">검색</button>
</form>
<c:if test="${not empty condition }">
<p>
<strong>${totalRow }</strong> 개의 자료가 검색 되었습니다.
<a href="list">리셋</a>
</p>
</c:if>
</div>
<script>
function deleteConfirm(num){
let isDelete=confirm("삭제 하시겠습니까?");
if(isDelete){
location.href="delete?num="+num;
}
}
</script>
</body>
</html>
- jstl과 EL로 forEach문 돌면서 출력
- 검색 폼은 file에서 사용했던 내용을 그대로 사용해도 된다.
- filename이었던 부분만 content로 수정해서!
- 이렇게 리스트가 완성된다.
- 새글 작성 메소드
- 인터셉터에 cafe 하위항목 추가하기
- 스마트에디터 추가
SmartEditor-popup-photo_uploader.jsp
- 파일이 업로드되는 경로를 수정해주기. resources/upload로!
- 2개 파일을 수정해주면 된다.
insertform 컨트롤러
@RequestMapping("/cafe/insertform")
public String insertForm() {
return "cafe/insertform";
}
- 스마트에디터 사용시의 주의사항! button 요소에 꼭 저 함수가 걸려있어야 한다.
- smarteditor 가 들어가있는 경로수정
- textarea의 크기가 스마트에디터의 크기가 된다.
- css로 textArea의 크기를 제어하면 크기 조절 가능!
- 그리고 name 속성의 value를 content로 해두어야 한다.
- 밑에 걸려있는 javascript 함수들이 적용되는 곳이 모두 content 라고 지정되어 있는 것을 기억!
- 만들어진 스마트에디터 폼
- 스마트에디터에서 파일을 저장하면 upload 폴더에 저장된다.
- 폴더는 미리 만들어두어야 한다!!
컨트롤러- insert 메소드
@RequestMapping("/cafe/insert")
public String insert(CafeDto dto, HttpSession session) {
//글 작성자는 세션에서 얻어낸다.
String writer=(String)session.getAttribute("id");
//dto는 글의 제목과 내용만 있으므로 글작성자는 직접 넣어준다.
dto.setWriter(writer);
service.saveContent(dto);
return "cafe/insert";
}
- session으로 id를 얻어내고 제목/내용은 dto에서 얻어내기
@RequestMapping("/cafe/insert")
public ModelAndView insert(CafeDto dto, ModelAndView mView) {
service.saveContent(dto);
mView.setViewName("cafe/insert");
return mView;
}
- 내가 잘못 작성했던 메소드.. session으로 id값을 얻어오지 않았다 ㅠㅠ
- 서비스- 새글 저장 메소드
@Override
public void saveContent(CafeDto dto) {
cafeDao.insert(dto);
}
- 이 dto 안에는 지금 writer, title, content가 들어있을 것!
- mapper에 3가지 값이 들어가고, viewCount에는 0이라는 초기값이 들어간다.
- insert 뷰페이지 작성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/views/cafe/insert.jsp</title>
</head>
<body>
<script>
alert("새글이 추가 되었습니다.");
location.href="${pageContext.request.contextPath}/cafe/list";
</script>
</body>
</html>
- 응답
'국비교육(22-23)' 카테고리의 다른 글
55일차(3)/Spring(17) : 게시판 글 삭제, 글 수정 기능 구현 (0) | 2022.12.25 |
---|---|
55일차(2)/Spring(16) : 글 상세보기, 이전글/다음글 불러오기 (0) | 2022.12.25 |
54일차(2)/Spring(14) : 자료실 파일 다운로드, 삭제, 검색 기능 구현 (0) | 2022.12.22 |
54일차(1)/Spring(13) : 자료실 게시판 만들기, 파일 업로드 기능 구현 (1) | 2022.12.22 |
53일차(1)/Spring(12) : ajax 요청 JSON으로 응답하기, 파일 업로드 처리 (0) | 2022.12.21 |