# 강좌 엔터티 및 레터지토리 구성
- Course
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
long categoryId;
String imagePath;
String keyword;
String subject;
@Column(length = 1000)
String summary;
@Lob
String contents;
long price;
long salePrice;
LocalDate saleEndDt;
}
# 강좌 기능 심플화 등록 및 심플화 구현
- list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.list table{
width: 100%;
border-collapse: collapse;
}
.list table th, .list table td{
border: solid 1px #000;
}
</style>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
</head>
<body>
<h1>강좌관리</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div>
<a href="/admin/course/add">강좌 등록</a>
</div>
<div class="list">
<table>
<thead>
<tr>
<th>ID</th>
<th>카테고리명</th>
<th>순서</th>
<th>사용여부</th>
<th>비고</th>
</tr>
</thead>
</table>
</div>
</body>
</html>
- add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.detail table{
width: 100%;
border-collapse: collapse;
}
.detail table th, .detail table td{
border: solid 1px #000;
}
.buttons a, .buttons button {
border-width: 0;
background-color: transparent;
text-decoration: underline;
font-size: 14px;
line-height: 20px;
height: 20px;
color: #000;
cursor: pointer;
}
</style>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
</head>
<body>
<h1>강좌등록</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<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">목록 이동</a>
</div>
</form>
</div>
</body>
</html>
- CourseController
@GetMapping("/admin/course/list")
public String list(Model model){
return "admin/course/list";
}
@GetMapping("/admin/course/add")
public String add(Model model){
return "admin/course/add";
}
@PostMapping("/admin/course/add")
public String addSubmit(Model model, CourseDto param){
boolean result = courseService.add(param);
return "redirect:/admin/course/list";
}
}
+) Tip
오류 : Incorrect string value: '\xEC\x95\x88\xEB\x85\x95'
한글성정이 안될때 DB에 UTF8 을 지정하면 된다
ALTER TABLE 테이블명 CONVERT TO CHARSET UTF8
- CourseDto
@Data
public class CourseDto {
String subject;
}
- CourseServiceImpl
@Override
public boolean add(CourseDto param) {
Course course = Course.builder()
.subject(param.getSubject())
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
return true;
}
- 강좌목록 구현
리스트는 mybatis를 사용해서 구현(회원 목록과 유사)
- CourseController
private final CourseService courseService;
@GetMapping("/admin/course/list")
public String list(Model model, CourseParam param){
param.init();
List<CourseDto> courseList = courseService.list(param);
long totalCount = 0;
if(courseList != null && courseList.size() > 0){
totalCount = courseList.get(0).getTotalCount();
}
String queryString = param.getQueryString();
String pagerHtml = getPapaerHtml(totalCount, param.getPageSize(), param.getPageIndex(), queryString);
model.addAttribute("list", courseList);
model.addAttribute("pager", pagerHtml);
model.addAttribute("totalCount", totalCount);
return "admin/course/list";
}
- CommonParam
MemberParam과 CourseParam이 겹치는 부분이 있기에 공통적인 클래스를 생성
@Data
public class CommonParam {
int pageIndex;
int pageSize;
String searchType;
String searchValue;
// pageIndex = 1 , 0 // pageIndex = 2 ,10 ...
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 builder = new StringBuilder();
// 검색결과가 있다는 조건
if (searchType != null && searchType.length() > 0){
builder.append(String.format("searchType=%s",searchType));
}
if (searchValue != null && searchValue.length() > 0){
if(builder.length() > 0){
builder.append("&");
}
builder.append(String.format("searchValue=%s",searchValue));
}
return builder.toString();
}
}
- BasicController
페이징 부분도 유사하게 쓰이는 부분이어서 공통으로 묶어준 후 Course나 Member에서 상속받아서 사용한다
public class BaseController {
public String getPapaerHtml(long totalCount, long pageSize, long pageIndex, String queryString){
PageUtil pageUtil = new PageUtil(totalCount, pageSize, pageIndex, queryString);
return pageUtil.pager();
}
}
- CourseController
+) 마찬가지로 Member에도 똑같이 페이징 처리 적용
@GetMapping("/admin/course/list")
public String list(Model model, CourseParam param){
param.init();
List<CourseDto> courseList = courseService.list(param);
long totalCount = 0;
if(courseList != null && courseList.size() > 0){
totalCount = courseList.get(0).getTotalCount();
}
String queryString = param.getQueryString();
// BasicController에서 메소드를 가져와 사용한다
String pagerHtml = getPapaerHtml(totalCount, param.getPageSize(), param.getPageIndex(), queryString);
model.addAttribute("list", courseList);
// 페이징 처리는 공통으로 상속받은 클래스를 변수에 담아서 넘겨준다
model.addAttribute("pager", pagerHtml);
model.addAttribute("totalCount", totalCount);
return "admin/course/list";
}
- list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.list table{
width: 100%;
border-collapse: collapse;
}
.list table th, .list table td{
border: solid 1px #000;
}
.pager{
margin-top: 20px;
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>
</head>
<body>
<h1>강좌관리</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>
<div>
<a href="/admin/course/add">강좌 등록</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}">2021.02.02</p>
</td>
</tr>
</tbody>
</table>
<div class="pager" th:utext="${pager}">
</div>
</div>
</body>
</html>
이제 받아온 값들을 리스트에서 뿌린다
- CourseServiceImpl
@Override
public List<CourseDto> list(CourseParam param) {
long totalCount = courseMapper.selectListCount(param);
List<CourseDto> list = courseMapper.selectList(param);
// 각 칼럼에 totalCount를 넣어주는 식
if(!CollectionUtils.isEmpty(list)){
int i = 0;
for (CourseDto m : list){
m.setTotalCount(totalCount);
m.setSeq(totalCount - param.getPageStart() - i);
i++;
}
}
return list;
}
}
- CourseMapper
@Mapper
public interface CourseMapper {
long selectListCount(CourseParam param);
List<CourseDto> selectList(CourseParam param);
}
- CourseMapper.xml
중복을 줄이기 위해 공통 메서드를 selectListWhere로 묶고 뺀 부분은 include로 넣어준다
<?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="searchType != null and searchValue != 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"
parameterType="com.zerobase.fastlms.course.model.CourseParam"
resultType="long">
select count(*) from course
where 1 = 1
<include refid="selectListWhere" />
</select>
<select id="selectList"
parameterType="com.zerobase.fastlms.course.model.CourseParam"
resultType="com.zerobase.fastlms.course.model.CourseDto">
select * from course
where 1 = 1
<include refid="selectListWhere" />
limit #{pageStart}, #{pageEnd}
</select>
</mapper>
- CourseDto
@Data
public class CourseDto {
Long id;
long categoryId;
String imagePath;
String keyword;
String subject;
String summary;
String contents;
long price;
long salePrice;
Date saleEndDt;
LocalDateTime regDt;//등록일(추가날짜)
LocalDateTime udtDt;//수정일(수정날짜)
// 페이징 카운트트
long totalCount;
long seq;
}
# 강좌 상세정보 등록 및 수정 구현
- 상세정보 수정(하나의 html로 수정/등록 구현)
- list.html
admin/course/edit 처럼 써도 되지만 같은 경로를 가진 html일때는 edit만 써도 이동된다
<td>
<p>
<a th:href="'edit?id=' + ${x.id}" th:text="${x.subject}">강좌명</a>
</p>
</td>
- CourseController
edit 이나 add 나 둘 다 동일한 내용이기에 주소 값을 중복해서 작성한다
editMode가 될려면 request주소를 가져와야 한다
+) 에러를 처리하는 페이지도 따로 설정
@GetMapping(value = {"/admin/course/add", "/admin/course/edit"})
public String add(Model model, HttpServletRequest request, CourseInput param){
// add인지 edit인지 구분하는 메서드
boolean editMode = request.getRequestURI().contains("/edit");
if(editMode){
long id = param.getId();
CourseDto existCourse = courseService.getById(id);
// 수정할 데이터가 없으면
if(existCourse == null){
// 에러처리
model.addAttribute("message", "강좌정보가 존재하지 않습니다");
return "common/error";
}
// 에러가 없다면 화면에 데이터를 내려야함
model.addAttribute("detail", existCourse);
}
return "admin/course/add";
}
- CourseService
// 강좌 상세정보
CourseDto getById(long id);
- CourseServiceImpl
@Override
public CourseDto getById(long id) {
return courseRepository.findById(id).map(CourseDto::of).orElse(null);
}
- CourseDto
빌더를 통한 엔티티와 Dto 데이터 변환
public static CourseDto of(Course course) {
return CourseDto.builder()
.id(course.getId())
.imagePath(course.getImagePath())
.keyword(course.getKeyword())
.subject(course.getSubject())
.summary(course.getSummary())
.contents(course.getContents())
.price(course.getPrice())
.salePrice(course.getSalePrice())
.regDt(course.getRegDt())
.build();
}
- CourseController
@GetMapping(value = {"/admin/course/add", "/admin/course/edit"})
public String add(Model model, HttpServletRequest request, CourseInput param){
// add인지 edit인지 구분하는 메서드
boolean editMode = request.getRequestURI().contains("/edit");
if(editMode){
long id = param.getId();
CourseDto existCourse = courseService.getById(id);
// 수정할 데이터가 없으면
if(existCourse == null){
// 에러처리
model.addAttribute("message", "강좌정보가 존재하지 않습니다");
return "common/error";
}
// 에러가 없다면 화면에 데이터를 내려야함
model.addAttribute("detail", existCourse);
}
// 강좌 등록/ 수정을 식별해야 하기때문에 editMode로 구분해준다다
model.addAttribute("editMode", editMode);
return "admin/course/add";
}
- add.html
<form method="post">
<table>
<tbody>
<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">목록 이동</a>
</div>
</form>
이렇게 하면 subject null 오류 발생 > detail에 값이 없어서 발생
controller에서 add 할때 detail을 같이 내려주면 된다
아래처럼 설정해준다면 detail이 db에서 값을 가져오지 않더라도 값을가져와 화면을 바인딩한다
@GetMapping(value = {"/admin/course/add", "/admin/course/edit"})
public String add(Model model, HttpServletRequest request, CourseInput param){
// add인지 edit인지 구분하는 메서드
boolean editMode = request.getRequestURI().contains("/edit");
CourseDto detail = new CourseDto();
if(editMode){
long id = param.getId();
CourseDto existCourse = courseService.getById(id);
// 수정할 데이터가 없으면
if(existCourse == null){
// 에러처리
model.addAttribute("message", "강좌정보가 존재하지 않습니다");
return "common/error";
}
// null 값이 아닐때 existCourse를 넣어준다
detail = existCourse;
// 에러가 없다면 화면에 데이터를 내려야함
model.addAttribute("detail", existCourse);
}
// 강좌 등록/ 수정을 식별해야 하기때문에 editMode로 구분해준다다
model.addAttribute("editMode", editMode);
model.addAttribute("detail", detail);
return "admin/course/add";
}
이제 데이터를 날려야 하니 PostMapping을 작성해야 하고 여기도 마찬가지로 mode를 체크해줘야 한다
- CourseController
@PostMapping(value = {"/admin/course/add", "/admin/course/edit"})
public String addSubmit(Model model, CourseDto param, HttpServletRequest request){
boolean editMode = request.getRequestURI().contains("/edit");
if(editMode){
long id = param.getId();
CourseDto existCourse = courseService.getById(id);
// 수정할 데이터가 없으면
if(existCourse == null){
// 에러처리
model.addAttribute("message", "강좌정보가 존재하지 않습니다");
return "common/error";
}
// edit모드일때는 수정
boolean result = courseService.set(param);
}else {
// edit모드가 아닐때는 등록
boolean result = courseService.add(param);
}
return "redirect:/admin/course/list";
}
- CourseService
// 강좌 상세정보
CourseDto getById(long id);
- CourseServiceImpl
@Override
public boolean set(CourseDto param) {
Optional<Course> optionalCourse = courseRepository.findById(param.getId());
// null 이면 수정할 데이터가 없는것
if(!optionalCourse.isPresent()){
return false;
}
// 수정할 데이터가 있다면
Course course = optionalCourse.get();
course.setSubject(param.getSubject());
course.setUdtDt(LocalDateTime.now());
courseRepository.save(course);
return false;
}
이제 강좌에 대한 카테고리를 만들 것
- CourseController
@GetMapping(value = {"/admin/course/add", "/admin/course/edit"})
public String add(Model model, HttpServletRequest request, CourseInput param){
// 카테고리 항목을 추가할 것이니 카테고리 정보를 내려줘야함
model.addAttribute("category", categoryService.list());
...
}
}
- add.html
<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>
+) 카테고리 리스트가 끝났다면 카테고리 저장은 ServiceImpl에서 저장, 수정 부분에 추가해주면 된다
# 강좌 상세정보 등록 및 수정 구현
목록에서 카테고리를 선택하고 들어갔을 때 카테고리가 선택이 되어있는 로직 구현
- add.html
th:selected="${detail.categoryId == x.id}"
+ ) Dto에 categoryId 추가하기
<tbody>
<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="내용 입력"></textarea>
</td>
</tr>
<tr>
<th>
정가
</th>
<td>
<input th:value="${detail.price}" type="text" name="price" required placeholder="정가 입력" />
</td>
</tr>
<tr>
<th>
판매가
</th>
<td>
<input th:value="${detail.salePrice}" type="text" name="salePrice" required placeholder="판매가 입력" />
</td>
</tr>
<tr>
<th>
할인 종료일
</th>
<td>
<input th:value="${detail.saleEndDt}" type="text" name="saleEndDtText" placeholder="할인 종료일 입력" />
</td>
</tr>
</tbody>
</table>
저장 / 수정하기
- CourseInput
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CourseInput {
long id;
long categoryId;
String subject;
String keyword;
String summary;
String contents;
long price;
long salePrice;
String saleEndDtText;
//삭제를 위한
String idList;
}
- CourseServiceImpl
@Override
public boolean add(CourseDto param) {
Course course = Course.builder()
.categoryId(param.getCategoryId())
.subject(param.getSubject())
.keyword(param.getKeyword())
.summary(param.getSummary())
.contents(param.getContents())
.price(param.getPrice())
.salePrice(param.getSalePrice())
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
return true;
}
@Override
public boolean set(CourseDto param) {
Optional<Course> optionalCourse = courseRepository.findById(param.getId());
// null 이면 수정할 데이터가 없는것
if(!optionalCourse.isPresent()){
return false;
}
// 수정할 데이터가 있다면
Course course = optionalCourse.get();
course.setCategoryId(param.getCategoryId());
course.setSubject(param.getSubject());
course.setKeyword(param.getKeyword());
course.setSummary(param.getSummary());
course.setContents(param.getContents());
course.setPrice(param.getPrice());
course.setSalePrice(param.getSalePrice());
course.setUdtDt(LocalDateTime.now());
courseRepository.save(course);
return false;
}
# 강좌 정렬
<select id="selectList"
parameterType="com.zerobase.fastlms.course.model.CourseParam"
resultType="com.zerobase.fastlms.course.model.CourseDto">
select * from course
where 1 = 1
<include refid="selectListWhere" />
order by reg_dt desc
limit #{pageStart}, #{pageEnd}
</select>
# 할인 종료일 문구 저장
2021-06-06 , 이런식으로 넘어온다는 전제
private LocalDate getLocalDate(String value){
// 문자열을 LocalDate로 바꿀 수 있는지 확인
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
return LocalDate.parse(value, format);
}catch (Exception e){
}
return null;
}
@Override
public boolean add(CourseInput param) {
LocalDate saleEndDt = getLocalDate(param.getSaleEndDtText());
Course course = Course.builder()
...
// 받아온 메서드 값을 넣어준다
.saleEndDt(saleEndDt)
.regDt(LocalDateTime.now())
.build();
courseRepository.save(course);
# 강좌 삭제 및 일괄, 선택 삭제 구현
- 선택삭제
먼저 삭제할 대상 체크박스 넣기
체크박스 체크 후 선택 삭제 기능부터 구현
삭제시 id값이 필요하니 checkbox에 넣어서 처리
jquery를 사용하여 페이지 이벤트 처리 후 삭제 요청
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
...
<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 (){
// 체크된 값이 있도록 체크값을 가져온다, 체크박스 중에서 checked상태를 가져오면 된다
var $checked = $('#dataInput input[type=checkbox]:checked')
console.log($checked.length);
if($checked.length < 1){
alert('삭제할 데이터를 선택하세요');
return false;
}
if(!confirm('선택한 데이터를 삭제하시겠습니까?')){
return false;
}
// id값을 가져올 변수를 배열에 담는다
var idList = [];
// $checked값을 key, value 로 돌려서 id 값을 찾아온다
$.each($checked, function (k, v){
idList.push($(this).val());
});
// $deleteForm에 있는 id 값을 , 로 구분해서 날려준다
var $deleteForm = $('form[name=deleteForm]');
$deleteForm.find('input[name=idList]').val(idList.join(','));
$deleteForm.submit();
});
});
</script>
</head>
<body>
...
<tbody class="dataInput">
<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?id=' + ${x.id}" th:text="${x.subject}">강좌명</a>
</p>
</td>
<td>
<p th:text="${x.regDt}">2021.02.02</p>
</td>
</tr>
</tbody>
</table>
<div class="pager" th:utext="${pager}">
</div>
</div>
<form name="deleteForm" method="post" action="/admin/course/delete">
<input type="hidden" name="idList" >
</form>
</body>
</html>
- CourseController
@PostMapping("/admin/course/delete" )
public String delete(Model model, CourseInput param, HttpServletRequest request){
boolean result = courseService.del(param.getIdList());
return "redirect:/admin/course/list";
}
- CourseService
// 강좌 내용삭제
boolean del(String idList);
- CourseServiceImpl
@Override
public boolean del(String idList) {
// , 단위의 문자열이기 때문에 null 체크
if(idList != null && idList.length() > 0){
String[] ids = idList.split(",");
for (String x : ids){
// id를 정수혈태로 담아야 하기에 long타입
long id = 0L;
try {
id = Long.parseLong(x);
}catch (Exception e){
}
if(id > 0){
courseRepository.deleteById(id);
}
}
}
return true;
}
페이징 처리
list.html
<p class="total-count">전체 <span th:text="${totalCount}"></span>개</p>
- 전체삭제
한 페이지에서 전체삭제를 누르면 해당 페이지가 전부 삭제되는 로직 구현
<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);
});
});
...
});
</script>
<div class="list">
<table>
<thead>
<tr>
<th>
<input id="selectAll" type="checkbox" /> // 이부분
</th>
<th> NO </th>
<th>
강좌명
</th>
<th>
등록일
</th>
</tr>
</thead>
...
</table>
</div>
+) 트러블 슈팅
삭제시 버튼을 클릭하면 계속 삭제할 데이터가 없다고 나왔다. 그렇다는 것은
var $checked = $('#dataLIst input[type=checkbox]:checked');
if ($checked.length < 1) {
alert(' 삭제할 데이터를 선택해 주세요. ');
return false;
}
여기서 check값이 0보다 작아서 선택이 아예 안되었다는 건데 계속 원인을 찾고 코드를 몇번 썼다 지웠다 했는데
<tbody class="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?id=' + ${x.id}" th:text="${x.subject}">강좌명</a>
</p>
</td>
<td>
<p th:text="${x.regDt}">2021.01.01</p>
</td>
</tr>
</tbody>
보이나..? tbody에서는 class로 주었는데 $checked 변수에는 class 값인 #으로 받고있었다
클라이언트 단을 꽤 오래하지 않아서 이제야 찾았다
class = . / id = # 으로 받는다
# 강좌 수정화면 스마트에디터 기능 적용
static을 resources 밑에 만들고 다운받은 스마트 에디터를 넣는다
- course/add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
...
<tr>
<th>강좌명 : </th>
<td>
<input th:value="${detail.subject}" type="text" name="subject" required placeholder="강좌명 입력">
</td>
</tr>
<tr>
<th>
내용
</th>
<td>
// 스마트 에디터를 쓸 곳에 id="contents" 지정
<textarea th:text="${detail.contents}" id="contents" name="contents" required placeholder="내용 입력"></textarea>
</td>
</tr>
<tr>
<th>
정가
</th>
<td>
<input th:value="${detail.price}" type="text" name="price" 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">목록 이동</a>
</div>
</form>
</div>
<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>
</body>
</html>
이 상태를 실행하면 권한설정 문제로 아래처럼 에러가 뜬다
- SecurityConfiguration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final MemberService memberService;
@Bean
PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
UserAuthenticationFailureHandeler getFailureHandeler(){
return new UserAuthenticationFailureHandeler();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// 이부분 추가!
http.headers().frameOptions().sameOrigin();
이제 적용은 되는데 수정이 안된다
- add.html
마지막으로 이 내용 추가
<script>
$(document).ready(function (){
$('submitForm').on('submit', function (){
oEditors.getById["contents"].exec("UPDATE_CONTENTS_FIELD", []);
});
});
</script>
+) 트러블..
계속 내용을 저장이나 수정해도 값이 안나오고 공백으로 나왔다
위를 자세히보면 어제와 똑같은 실수.. submitForm에 id값을 안줬다 #을 줬어야 하는데!!!!!!!!
진짜 스크립트는 한 부분 실수하면 찾기가 너무 어려운것같다
'개인프로젝트(수강프로그램)' 카테고리의 다른 글
강좌 목록 구현 (0) | 2022.04.25 |
---|---|
강좌 목록 구현, 강좌 신청 (0) | 2022.04.22 |
카테고리 화면 (0) | 2022.04.19 |
회원상세 및 상태처리 (0) | 2022.04.19 |
회원목록 (0) | 2022.04.18 |