13.1 강좌 entity 및 repository 구성
Ex)
- course 패키지 아래 Course 클래스 만들기
package com.zerobase.fastlms.course;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDate;
@Data
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String imagePath;;
String keyword;
String subject;
@Column(length = 1000)
String summary;
@Lob
String contents;
long price;
long salesPrice;
LocalDate salesEndDt;
}
- CourseRepository 인터페이스
package com.zerobase.fastlms.admin.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String categoryName;
int sortValue;
boolean usingYn;
}
- AdminCourseController
package com.zerobase.fastlms.course.controller;
import com.zerobase.fastlms.admin.service.CategoryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
@RequiredArgsConstructor
public class AdminCourseController {
private final CategoryService categoryService;
@GetMapping("/admin/course/list.do")
public String list (Model model) {
return "admin/course/list" ;
}
}
13.2 강좌 기능 심플화 등록 및 심플화 목록 구현
Ex)
- 컨트롤러
@GetMapping("/admin/course/add.do")
public String add (Model model) {
return "admin/course/add" ;
}
- add 페이지를 만든다. (Cource 엔티티 클래스의 변수 이름을 보고 참고해야한다.)
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 화면</title >
<style >
.detail table {
width : 100% ;
border-collapse : collapse;
}
.detail table th , .detail table td {
border : solid 1px #000 ;
}
.buttons {
margin-top : 20px ;
text-align : center;
}
.buttons a , .buttons button {
border-width : 0 ;
background-color : transparent;
text-decoration : underline;
font-size : 12px ;
line-height : 20px ;
height : 20px ;
cursor : pointer;
}
</style >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(document ).ready(function ( ) {
});
</script >
</head >
<body >
<h1 > 강좌 관리 - 강좌 등록</h1 >
<div class ="detail" >
<form method ="post" >
<table >
<tbody >
<tr >
<th >
강좌명:
</th >
<td >
<input type ="text" name ="subject" required placeholder ="강좌명 입력" />
</td >
</tr >
</tbody >
</table >
<div class ="buttons" >
<button type ="submit" > 강좌 등록 하기</button >
<a href ="/admin/course/list.do" > 목록으로 이동</a >
</div >
</form >
</div >
</body >
</html >
- 코스 서비스
package com.zerobase.fastlms.course.service;
import com.zerobase.fastlms.course.model.CourseInput;
public interface CourseService {
boolean add (CourseInput parameter) ;
}
- 이를 구현한 서비스 임플
package com.zerobase.fastlms.course.service;
import com.zerobase.fastlms.course.entity.Course;
import com.zerobase.fastlms.course.model.CourseInput;
import com.zerobase.fastlms.course.repository.CourseRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
public class CourseServiceImpl implements CourseService {
private final CourseRepository courseRepository;
@Override
public boolean add (CourseInput parameter) {
Course course = Course.builder()
.subject(parameter.getSubject())
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
return true ;
}
}
- 컨트롤러
@PostMapping("/admin/course/add.do")
public String addSubmit (Model model, CourseInput parameter) {
boolean result = courseService.add(parameter);
return "redirect:/admin/course/list.do" ;
}
============================== 오류 ==============================
- 오류: 강좌 등록하기 버튼을 누르면
- 원인: add.html 페이지에서 <form method="post"> 라고 작성해야하는데 method 대신 action을 입력했다가 오류났다.
-> 수정 후 정상 화면
Ex) 강좌 목록 보여주기
- 마이바티스로 리스트를 구성해야한다.
- common param 만든다. 공통 속성
package com.zerobase.fastlms.admin.model;
import lombok.Data;
@Data
public class CommonParam {
long pageIndex;
long pageSize;
String searchType;
String searchValue;
public long getPageStart () {
init();
return (pageIndex - 1 ) * pageSize;
}
public long getPageEnd () {
init();
return pageSize;
}
public void init () {
if (pageIndex < 1 ) {
pageIndex = 1 ;
}
if (pageSize < 10 ) {
pageSize = 10 ;
}
}
public String getQueryString () {
init();
StringBuilder sb = new StringBuilder();
if (searchType != null && searchType.length() > 0 ){
sb.append(String.format("searchType=%s" , searchType));
}
if (searchValue != null && searchValue.length() > 0 ){
if (sb.length() > 0 ){
sb.append("&" );
}
sb.append(String.format("searchValue=%s" , searchValue));
}
return sb.toString();
}
}
- 바뀐 MemberParam
package com.zerobase.fastlms.admin.model;
import lombok.Data;
@Data
public class MemberParam extends CommonParam {
String userId;
}
- courseParam
package com.zerobase.fastlms.course.model;
import com.zerobase.fastlms.admin.model.CommonParam;
import lombok.Data;
@Data
public class CourseParam extends CommonParam {
}
- courseDto
package com.zerobase.fastlms.course.dto;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class CourseDto {
Long id;
String imagePath;;
String keyword;
String subject;
String summary;
String contents;
long price;
long salesPrice;
LocalDate salesEndDt;
LocalDateTime regDt;
LocalDateTime udtDt;
long totalCount;
long seq;
}
- courseService
List<CourseDto> list (CourseParam parameter) ;
- 컨트롤러
-> 자주 쓰이는 부분을 위해 베이스 컨트롤러도 만든다.
@Controller
@RequiredArgsConstructor
public class AdminCourseController extends BaseController {
private final CourseService courseService;
@GetMapping("/admin/member/list.do")
public String list (Model model, CourseParam parameter) {
parameter.init();
List<CourseDto> courseList = courseService.list(parameter);
long totalCount = 0 ;
if (CollectionUtils.isEmpty(courseList)){
totalCount = courseList.get(0 ).getTotalCount();
}
String queryString = parameter.getQueryString();
PageUtil pageUtil = new PageUtil(totalCount, parameter.getPageSize(), parameter.getPageIndex(), queryString);
model.addAttribute("list" , courseList);
model.addAttribute("totalCount" , totalCount);
model.addAttribute("pager" , pageUtil.pager());
return "admin/member/list" ;
}
}
- 베이스
package com.zerobase.fastlms.course.controller;
import com.zerobase.fastlms.util.PageUtil;
public class BaseController {
public String getPaperHtml (long totalCount, long pageSize, long pageIndex, String queryString) {
PageUtil pageUtil = new PageUtil(totalCount, pageSize, pageIndex, queryString);
return pageUtil.pager();
}
}
- AdminCourseCtrl => (여기서 if문에 !를 빼먹었다가 오류가 났다. - TempleteInputExcp )
@GetMapping("/admin/member/list.do")
public String list (Model model, CourseParam parameter) {
parameter.init();
List<CourseDto> courseList = courseService.list(parameter);
long totalCount = 0 ;
if (!CollectionUtils.isEmpty(courseList)){
totalCount = courseList.get(0 ).getTotalCount();
}
String queryString = parameter.getQueryString();
String paperHtml = getPaperHtml(totalCount, parameter.getPageSize(), parameter.getPageIndex(), queryString);
model.addAttribute("list" , courseList);
model.addAttribute("totalCount" , totalCount);
model.addAttribute("pager" , paperHtml);
return "admin/member/list" ;
}
- AdminMemberCtrl도 변경 가능
@GetMapping("/admin/member/list.do")
public String list (Model model, MemberParam parameter) {
parameter.init();
List<MemberDto> members = memberService.list(parameter);
long totalCount = 0 ;
if (members != null && members.size() > 0 ){
totalCount = members.get(0 ).getTotalCount();
}
String queryString = parameter.getQueryString();
String pagerHtml = getPaperHtml(totalCount, parameter.getPageSize(), parameter.getPageIndex(), queryString);
model.addAttribute("list" , members);
model.addAttribute("totalCount" , totalCount);
model.addAttribute("pager" , pagerHtml);
return "admin/member/list" ;
}
- course 내부 list
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 화면</title >
<style >
.list table {
width : 100% ;
border-collapse : collapse;
}
.list table th , .list table td {
border : solid 1px #000 ;
}
p .nothing {
text-align : center;
padding : 100px ;
}
.pager {
margin-top : 10px ;
text-align : center;
}
.pager a .on {
font-weight : bold;
color : red;
}
</style >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(document ).ready(function ( ) {
});
</script >
</head >
<body >
<h1 > 강좌 관리</h1 >
<div >
<a href ="/admin/course/add.do" > 강좌 등록</a >
</div >
<div class ="list" >
<table >
<thead >
<tr >
<th > ID</th >
<th > 강좌명</th >
<th > 등록일</th >
</tr >
</thead >
<tbody >
<tr th:each ="x : ${list}" >
<td th:text ="${x.seq}" > 1</td >
<td >
<p th:text ="${x.subject}" > </p >
</td >
<td >
<p th:text ="${x.regDt}" > 2022-01-01</p >
</td >
</tr >
</tbody >
</table >
<div class ="pager" th:utext ="${pager}" > </div >
</div >
</body >
</html >
- CourseServiceImpl
package com.zerobase.fastlms.course.service;
import com.zerobase.fastlms.course.dto.CourseDto;
import com.zerobase.fastlms.course.entity.Course;
import com.zerobase.fastlms.course.mapper.CourseMapper;
import com.zerobase.fastlms.course.model.CourseInput;
import com.zerobase.fastlms.course.model.CourseParam;
import com.zerobase.fastlms.course.repository.CourseRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.List;
@Service
@RequiredArgsConstructor
public class CourseServiceImpl implements CourseService {
private final CourseRepository courseRepository;
private final CourseMapper courseMapper;
@Override
public boolean add (CourseInput parameter) {
Course course = Course.builder()
.subject(parameter.getSubject())
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
return true ;
}
@Override
public List<CourseDto> list (CourseParam parameter) {
long totalCount = courseMapper.selectListCount(parameter);
List<CourseDto> list = courseMapper.selectList(parameter);
if (!CollectionUtils.isEmpty(list)){
int i = 0 ;
for (CourseDto x : list){
x.setTotalCount(totalCount);
x.setSeq(totalCount - parameter.getPageStart() - i);
++i;
}
}
return list;
}
}
- 코스 매퍼 인터페이스 만들기
-> 마이바티스의 매퍼 어노테이션
-> Mapper를 만들면 짝을 이루는 xml 파일이 존재해야한다.
package com.zerobase.fastlms.course.mapper;
import com.zerobase.fastlms.course.dto.CourseDto;
import com.zerobase.fastlms.course.model.CourseParam;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CourseMapper {
long selectListCount (CourseParam parameter) ;
List<CourseDto> selectList (CourseParam memberParam) ;
}
cf) SQL문을 DML태그에 추가 - 동일한 쿼리를 여러 번 삽입 가능하다.
<include refid="selectListWhere" ></include >
- CouerserMapper.xml 파일
-> 아래 쪽의 <SELECT...> 뒤에 슬래시를 잘못 붙였다가 에러가 났다.
<?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 ="com.zerobase.fastlms.course.mapper.CourseMapper" >
<sql id ="selectListWhere" >
<if test ="searchValue != null and searchType != null" >
<choose >
<when test ="searchType == 'userId'" >
and user_id like concat('%', #{searchValue}, '%')
</when >
<when test ="searchType == 'userName'" >
and user_name like concat('%', #{searchValue}, '%')
</when >
<when test ="searchType == 'phone'" >
and phone like concat('%', #{searchValue}, '%')
</when >
<otherwise >
and
(
user_id like concat('%', #{searchValue}, '%')
or
user_name like concat('%', #{searchValue}, '%')
or
phone like concat('%', #{searchValue}, '%')
)
</otherwise >
</choose >
</if >
</sql >
<select id ="selectListCount" resultType ="long" >
SELECT COUNT(*)
FROM member
WHERE 1 = 1
<include refid ="selectListWhere" > </include >
</select >
<select id ="selectList" resultType ="com.zerobase.fastlms.course.dto.CourseDto" > /
SELECT *
FROM member
WHERE 1 = 1
<include refid ="selectListWhere" > </include >
LIMIT #{pageStart} , #{pageEnd}
</select >
</mapper >
- 임플 list() 메서드
@Override
public List<CourseDto> list (CourseParam parameter) {
long totalCount = courseMapper.selectListCount(parameter);
List<CourseDto> list = courseMapper.selectList(parameter);
if (!CollectionUtils.isEmpty(list)){
int i = 0 ;
for (CourseDto x : list){
x.setTotalCount(totalCount);
x.setSeq(totalCount - parameter.getPageStart() - i);
++i;
}
}
return list;
}
- courseDto
package com.zerobase.fastlms.course.dto;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class CourseDto {
Long id;
String imagePath;;
String keyword;
String subject;
String summary;
String contents;
long price;
long salesPrice;
LocalDate salesEndDt;
LocalDateTime regDt;
LocalDateTime udtDt;
long totalCount;
long seq;
}
=============================== 오류 ===============================
- 오류: 다른 페이지는 잘 뜨는데 강좌 관리 페이지에서 SQL 예외가 발생한다. SQLSyntaxErrorException
- 원인: xml 파일에서 테이블 이름 course로 바꿈. 그니고 가장 마지막의 <SELECT ...> 뒤에 슬래시가 들어가는 바람에 SQL문법 오류가 났다. ..오타 그만 내기!!
Sol) 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 ="com.zerobase.fastlms.course.mapper.CourseMapper" >
<sql id ="selectListWhere" >
<if test ="searchValue != null and searchType != null" >
<choose >
<when test ="searchType == 'userId'" >
and user_id like concat('%', #{searchValue}, '%')
</when >
<when test ="searchType == 'userName'" >
and user_name like concat('%', #{searchValue}, '%')
</when >
<when test ="searchType == 'phone'" >
and phone like concat('%', #{searchValue}, '%')
</when >
<otherwise >
and
(
user_id like concat('%', #{searchValue}, '%')
or
user_name like concat('%', #{searchValue}, '%')
or
phone like concat('%', #{searchValue}, '%')
)
</otherwise >
</choose >
</if >
</sql >
<select id ="selectListCount"
resultType ="long" >
SELECT COUNT(*)
FROM course
WHERE 1 = 1
<include refid ="selectListWhere" />
</select >
<select id ="selectList"
resultType ="com.zerobase.fastlms.course.dto.CourseDto" >
SELECT *
FROM course
WHERE 1 = 1
<include refid ="selectListWhere" />
LIMIT #{pageStart}, #{pageEnd}
</select >
</mapper >
Note) 실행 결과 - 정상 실행
========================= 오류 =========================
- 오류 :TemplateInputException
- 원인: AdminCourseController에서 list() 메서드 if문에 '!'를 빼먹었다.
if (!CollectionUtils.isEmpty(courseList)){
totalCount = courseList.get(0 ).getTotalCount();
}
-> 수정 후, 정상 화면
13.3 강좌 상세 정보 등록 및 수정 구현 (1)
Ex) 강좌 상세 정보 수정
- 리스트에 강좌명 하이퍼링크 추가
- 컨트롤러
@GetMapping(value = {"/admin/course/add.do" , "/admin/course/edit.do"})
public String add (Model model, HttpServletRequest request, CourseInput parameter) {
boolean editMode = request.getRequestURI().contains("/edit.do" );
if (editMode){
long id = parameter.getId();
CourseDto existCourse = courseService.getById(id);
if (existCourse == null ){
model.addAttribute("message" , "강좌 정보가 존재하지 않습니다." );
return "common/error" ;
}
model.addAttribute("detail" , existCourse);
}
return "admin/course/add" ;
}
- 서비스에 getById() 추가
CourseDto getById (long id) ;
- CourseServiceImpl - getById()
@Override
public CourseDto getById (long id) {
return courseRepository.findById(id).map(CourseDto::of).orElse(null );
}
- of() 메서드
public static CourseDto of (Course course) {
CourseDto courseDto = CourseDto.builder()
.id(course.getId())
.imagePath(course.getImagePath())
.keyword(course.getKeyword())
.subject(course.getSubject())
.summary(course.getSummary())
.contents(course.getContents())
.price(course.getPrice())
.salesPrice(course.getSalesPrice())
.salesEndDt(course.getSalesEndDt())
.regDt(course.getRegDt())
.udtDt(course.getUdtDt())
.build();
return courseDto;
}
- 에러 페이지
-> 방금 컨트롤러에서 model에 "massage"를 넣어서 변수를 이용 가능하다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 에러 화면</title >
</head >
<body >
<h1 > 에러 화면</h1 >
<p th:text ="${message}" > </p >
<div >
<a href ="/" > 홈으로 이동</a >
</div >
</body >
</html >
Note) 실행 결과 - 존재하지 않는 강좌 정보 페이지로 이동할 때
- 컨트롤러 수정
<hide/>
@GetMapping(value = {"/admin/course/add.do" , "/admin/course/edit.do"})
public String add (Model model, HttpServletRequest request, CourseInput parameter) {
boolean editMode = request.getRequestURI().contains("/edit.do" );
CourseDto detail = new CourseDto();
if (editMode){
long id = parameter.getId();
CourseDto existCourse = courseService.getById(id);
if (existCourse == null ){
model.addAttribute("message" , "강좌 정보가 존재하지 않습니다." );
return "common/error" ;
}
detail = existCourse;
}
model.addAttribute("editMode" , editMode);
model.addAttribute("detail" , detail);
return "admin/course/add" ;
}
- 이렇게 @PostMapping에 value를 넣어야 둘다 처리 가능하다.
@PostMapping(value={"/admin/course/add.do", "/admin/course/edit.do"})
public String addSubmit (Model model, HttpServletRequest request ,CourseInput parameter) {
boolean editMode = request.getRequestURI().contains("/edit.do" );
if (editMode){
long id = parameter.getId();
CourseDto existCourse = courseService.getById(id);
if (existCourse == null ){
model.addAttribute("message" , "강좌 정보가 존재하지 않습니다." );
return "common/error" ;
}
boolean result = courseService.set(parameter);
}else {
boolean result = courseService.add(parameter);
}
return "redirect:/admin/course/list.do" ;
}
- 서비스 - set() 메서드
boolean set (CourseInput parameter );
- 임플에 set() 구현
@Override
public boolean set (CourseInput parameter) {
Optional<Course> optionalCourse = courseRepository.findById(parameter.getId());
if (!optionalCourse.isPresent()){
return false ;
}
Course course = optionalCourse.get();
course.setSubject(parameter.getSubject());
course.setUdtDt(LocalDateTime.now());
courseRepository.save(course);
return true ;
}
Note) 실행 결과 - 수정
- 수정 후, 리스트로 이동한다.
Ex) 강좌 등록
- 컨트롤러 add()
@GetMapping(value = {"/admin/course/add.do" , "/admin/course/edit.do"})
public String add (Model model, HttpServletRequest request, CourseInput parameter) {
model.addAttribute("category" , categoryService.list());
boolean editMode = request.getRequestURI().contains("/edit.do" );
CourseDto detail = new CourseDto();
if (editMode){
long id = parameter.getId();
CourseDto existCourse = courseService.getById(id);
if (existCourse == null ){
model.addAttribute("message" , "강좌 정보가 존재하지 않습니다." );
return "common/error" ;
}
detail = existCourse;
}
model.addAttribute("editMode" , editMode);
model.addAttribute("detail" , detail);
return "admin/course/add" ;
}
- 강좌 add 페이지에 카테고리 옵션이 나오도록 한다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 화면</title >
<style >
.detail table {
width : 100% ;
border-collapse : collapse;
}
.detail table th , .detail table td {
border : solid 1px #000 ;
}
.buttons {
margin-top : 20px ;
text-align : center;
}
.buttons a , .buttons button {
border-width : 0 ;
background-color : transparent;
text-decoration : underline;
font-size : 12px ;
line-height : 20px ;
height : 20px ;
cursor : pointer;
}
</style >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(document ).ready(function ( ) {
});
</script >
</head >
<body >
<h1 > 강좌 관리 - 강좌 등록</h1 >
<div class ="detail" >
<form method ="post" >
<table >
<tbody >
<tr >
<th > 강좌 카테고리</th >
<td >
<select name ="categoryId" required >
<option value ="" > 카테고리 선택</option >
<option th:each ="x : ${category}" th:value ="${x.id}" th:text ="${x.categoryName}" > 기획</option >
</select >
</td >
</tr >
<tr >
<th >
강좌명:
</th >
<td >
<input th:value ="${detail.subject}" type ="text" name ="subject" required placeholder ="강좌명 입력" />
</td >
</tr >
</tbody >
</table >
<div class ="buttons" >
<button th:if ="${editMode}" type ="submit" > 강좌 수정 하기</button >
<button th:if ="${!editMode}" type ="submit" > 강좌 등록 하기</button >
<a href ="/admin/course/list.do" > 목록으로 이동</a >
</div >
</form >
</div >
</body >
</html >
Note) 실행 결과
Ex) 수정한 카테고리 저장하기
- categoryId에 대해서 값을 Course가 저장해야한다. Course에 멤버 추가
long categoryId;
- 임플에 add()에 추가, set()에도 동일하게 추가한다.
@Override
public boolean add (CourseInput parameter) {
Course course = Course.builder()
.categoryId(parameter.getCategoryId())
.subject(parameter.getSubject())
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
return true ;
}
Note) 실행 결과 - 업데이트 날짜가 뜬다.
13.4 강좌 상세 정보 등록 및 수정 구현 (2)
Ex)
- Category에 categoryId를 추가
- add
<tr >
<th > 강좌 카테고리</th >
<td >
<select name ="categoryId" required >
<option value ="" > 카테고리 선택</option >
<option
th:selected ="${detail.categoryId == x.id}"
th:each ="x : ${category}" th:value ="${x.id}" th:text ="${x.categoryName}" > 기획</option >
</select >
</td >
</tr >
<tr >
<th >
강좌명:
</th >
<td >
<input th:value ="${detail.subject}" type ="text" name ="subject" required placeholder ="강좌명 입력" />
</td >
</tr >
<tr >
<th >
키워드
</th >
<td >
<input th:value ="${detail.keyword}" type ="text" name ="keyword" required placeholder ="키워드 입력" />
</td >
</tr >
<tr >
<th >
요약문구
</th >
<td >
<textarea th:text ="${detail.summary}" name ="summary" required placeholder ="요약문구 입력" > </textarea >
</td >
</tr >
<tr >
<th >
내용
</th >
<td >
<textarea th:text ="${detail.contents}" name ="contents" required placeholder ="내용 입력" />
</td >
</tr >
<tr >
<th >
정가
</th >
<td >
<input th:value ="${detail.price}" name ="price" required placeholder ="정가 입력" > </input >
</td >
</tr >
<tr >
<th >
판매가
</th >
<td >
<input th:value ="${detail.salesPrice}" name ="salesPrice" required placeholder ="판매가 입력" > </input >
</td >
</tr >
<tr >
<th >
할인 종료일
</th >
<td >
<input th:value ="${detail.salesEndDt}" name ="salesEndDt" required placeholder ="할인 종료일 입력" > </input >
</td >
</tr >
Note) 실행 결과
- 수정하기 누르고 다시 들어오면 저장한 값이 보인다.
- 데이터베이스에 잘 들어간다.
Ex) 할인 종료일
- 임플 클래스에 날짜 형식 바꾸는 메서드 getLocalDate()추가
private LocalDate getLocalDate (String value) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd" );
try {
return LocalDate.parse(value, formatter);
}catch (Exception e){
}
return null ;
}
- set(), add() 에 둘다 추가해서 course에 넣는다.
LocalDate saleEndDt = getLocalDate(parameter.getSaleEndDtText());
Note) 실행 결과 - 정상
13.5 강좌 전체 삭제 및 선택 삭제 구현
Ex) 강좌선택 삭제 구현하기
- 체크 박스 만들기
<thead >
<tr >
<th >
<input type ="checkbox" />
</th >
<th > NO</th >
<th > 강좌명</th >
<th > 등록일</th >
</tr >
</thead >
<tbody id ="dataList" >
<tr th:each ="x : ${list}" >
<td >
<input type ="checkbox" th:value ="${x.id}" />
</td >
<td th:text ="${x.seq}" > 1</td >
<td >
<p >
<a th:href ="'edit.do?id=' + ${x.id}" th:text ="${x.subject}" > 강좌명</a >
</p >
</td >
<td >
<p th:text ="${x.regDt}" > 2022-01-01</p >
</td >
</tr >
</tbody >
- 버튼 삭제 자바스크립트
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous" ></script>
<script >
$(document ).ready(function ( ) {
$('#deleteButton' ).on('click' , function ( ) {
var $checked = $('#dataList input[type=checkbox]:checked' );
if ($checked.length < 1 ){
alert("삭제할 데이터를 선택해주세요." )
return false ;
}
if (!confirm('선택한 데이터를 삭제하겠습니까?' )){
return false ;
}
var idList = [];
$.each($checked, function (k, v ) {
idList.push($(this ).val());
});
console .log(idList);
console .log(idList.join(',' ));
var $deleteForm = $('form[name=deleteForm]' );
$deleteForm.find('input[name=idList]' ).val(idList.join(',' ));
$deleteForm.submit();
});
});
</script >
- deleteForm에다 삭제한 정보를 채워서 course의 delete 적용시킨다.
<form name ="deleteForm" method ="post" action ="/admin/course/delete.do" >
<input type ="hidden" name ="idList" />
</form >
- 컨트롤러에 삭제 구현
@PostMapping("/admin/course/delete.do")
public String del (Model model, HttpServletRequest request ,CourseInput parameter) {
boolean result = courseService.del(parameter.getIdList());
return "redirect:/admin/course/list.do" ;
}
- courseService
boolean del (String idList) ;
- 임플에 del() 구현
@Override
public boolean del (String idList) {
if (idList != null && idList.length() > 0 ){
String[] ids = idList.split("," );
for (String x : ids){
long id = 0L ;
try {
id = Long.parseLong(x);
}catch (Exception e){
}
if (id > 0 ){
courseRepository.deleteById(id);
}
}
}
return true ;
}
Note) 실행 결과
- 선택 삭제 누른 다음
Ex) 전체 삭제 구현
- 리스트에서 맨위 체크 박스에 selectAll이라는 이름을 준다.
<thead >
<tr >
<th >
<input id ="selectAll" type ="checkbox" />
</th >
<th > NO</th >
<th > 강좌명</th >
<th > 등록일</th >
</tr >
</thead >
cf) .attr()과 .prop()의 차이는?
- .attr(): HTML의 속성을 취급한다.
- .prop(): JavaScript의 프로퍼티를 취급한다.
- prop() 로 체크한다.
<script>
$(document ).ready(function ( ) {
$('#selectAll' ).on('click' , function ( ) {
var checked = $(this ).is(':checked' );
$('#dataList input[type=checkbox]' ).each(function (k, v ) {
$(this ).prop('checked' , checked);
});
});
$('#deleteButton' ).on('click' , function ( ) {
var $checked = $('#dataList input[type=checkbox]:checked' );
if ($checked.length < 1 ){
alert("삭제할 데이터를 선택해주세요." )
return false ;
}
if (!confirm('선택한 데이터를 삭제하겠습니까?' )){
return false ;
}
var idList = [];
$.each($checked, function (k, v ) {
idList.push($(this ).val());
});
console .log(idList);
console .log(idList.join(',' ));
var $deleteForm = $('form[name=deleteForm]' );
$deleteForm.find('input[name=idList]' ).val(idList.join(',' ));
$deleteForm.submit();
});
});
</script>
Note) 실행 결과
- 맨 위 박스 누른 화면
- 선택 삭제 누른 다음 화면 => 10개가 삭제된다.
13.6 강좌 수정 화면 스마트에디터(SmartEditor) 기능 적용
Ex) 스마트 에디터(SmartEditor)
- https://github.com/naver/smarteditor2/releases 접속하여 2-2.10.0 다운로드
- 압축 해제하고 dist폴더를 static 하위로 이동시킨다.
- http://naver.github.io/smarteditor2/user_guide/2_install/setting.html 사이트의 2.0 버전 설치 탭의 코드 참고
-> 강좌 add 파일의 아래에 추가한다.
<script type ="text/javascript" src ="../se2/js/service/HuskyEZCreator.js" charset ="utf-8" > </script >
- 위 사이트의 4번 탭을 참고해서 강의 add파일에 자바스크립트 내용을 추가한다. (res앞에 슬래시 꼭 붙인다! 안 붙이면 오류난다.)
<script type="text/javascript" src="/res/se2/js/service/HuskyEZCreator.js" charset="utf-8" ></script>
<script type ="text/javascript" >
var oEditors = [];
nhn.husky.EZCreator.createInIFrame({
oAppRef : oEditors,
elPlaceHolder : "contents" ,
sSkinURI : "/res/se2/SmartEditor2Skin.html" ,
fCreator : "createSEditor2"
});
</script >
- 시큐리티의 Configure() http.headers.frameOptions().sameOrigin()을 추가한다.
@Override
protected void configure (HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().sameOrigin();
http.authorizeRequests()
.antMatchers(
"/"
, "/member/register"
, "/member/email-auth"
, "/member/find-password"
)
.permitAll();
http.authorizeRequests()
.antMatchers("/admin/**" )
.hasAnyAuthority("ROLE_ADMIN" );
http.formLogin()
.loginPage("/member/login" )
.failureHandler(getFailureHandler())
.permitAll();
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout" ))
.logoutSuccessUrl("/" )
.invalidateHttpSession(true );
http.exceptionHandling()
.accessDeniedPage("/error/denied" );
super .configure(http);
}
Note) 실행 결과
- 다음과 같이 입력 수정