본문 바로가기

개인프로젝트(수강프로그램)

강좌목록

# 강좌 엔터티 및 레터지토리 구성

 

- 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