14.1 강좌 목록 구현
- 지금까지는 회사의 입장에서 서버에 데이터를 저장하는 것과 같은 애플리케이션을 만들었다면 앞으로는 회원의 입장에서 사용할 인터페이스를 만들어보자.
Ex) 메인 페이지에서 강좌 목록으로 이동할 수 있도록 구현
- CourseController
@GetMapping("/course")
public String course (Model model, CourseParam parameter) {
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list" , list);
return "course/index" ;
}
- 인덱스 페이지 구성
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 정보 페이지</title >
</head >
<body >
<h1 > 강좌 정보 페이지</h1 >
<div th:text ="${list}" >
</div >
</body >
</html >
- 인터페이스에 frontList() 메서드 만든다. 프론트에 필요한 리스트만 나오도록 새로 메서드를 만든다.기존의 list()와 다름
List<CourseDto> frontList (CourseParam courseParam) ;
- CourseDto클래스 일부
-> publice static List<CourseDto> of(Course) 메서드는 Course 객체를 => CourseDto로 형변환 해주는 느낌
-> 두 가지의 of() 메서드 오버로딩
public static CourseDto of (Course course) {
CourseDto courseDto = CourseDto.builder()
.id(course.getId())
.categoryId(course.getCategoryId())
.imagePath(course.getImagePath())
.keyword(course.getKeyword())
.subject(course.getSubject())
.summary(course.getSummary())
.contents(course.getContents())
.price(course.getPrice())
.salePrice(course.getSalePrice())
.saleEndDt(course.getSaleEndDt())
.regDt(course.getRegDt())
.udtDt(course.getUdtDt())
.build();
return courseDto;
}
public static List<CourseDto> of (List<Course> course) {
if (course == null ){
return null ;
}
List<CourseDto> courseList = new ArrayList<>();
for (Course x : course){
courseList.add(CourseDto.of(x));
}
return courseList;
}
- Impl 클래스
@Override
public List<CourseDto> frontList (CourseParam courseParam) {
List<Course> optionalCourseDto = courseRepository.findAll();
return CourseDto.of(optionalCourseDto);
}
Note) 실행 결과 - 강좌 목록 탭 누른 결과
Ex) 강좌 정보 페이지
- 현재 상태
- 인덱스 페이지
-> 스타일 태그에 line-through 를 넣으면 글에 취소선을 그을 수 있다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 정보 페이지</title >
<style >
span .price {
text-decoration : line-through;
}
</style >
</head >
<body >
<h1 > 강좌 정보 페이지</h1 >
<ul >
<li th:each ="x : ${list}" >
<div >
<a th:href ="'/course/' + ${x.id}" >
<h3 th:text ="${x.subject}" > 강좌명</h3 >
</a >
<div >
<p th:text ="${x.summary}" > </p >
판매가: <span class ="price" th:text ="${x.price}" > </span >
할인가: <span th:text ="${x.salePrice}" > </span >
</p >
</div >
</div >
</li >
</ul >
</body >
</html >
Note) 실행 결과 - course 페이지
Ex)
- 특정 카테고리에 대해 몇 개의 정보가 등록됐는지 알 수 있다.
SELECT c.* , (SELECT COUNT(*)
FROM course WHERE category_id = c.id) AS course_count
FROM category c;
- 카테고리 매퍼는 멤버매퍼 참고해서 만든다.
- 인터페이스에 추가
/**
* 프론트 카테고리 정보
*/
List<CategoryDto > frontList(CategoryDto parameter);
- 임플
@Override
public List<CategoryDto> frontList (CategoryDto parameter) {
return categoryMapper.select(parameter);
}
-> 이제 카테고리 컨트롤에서 카테고리 목록을 가져올 수 있다.
- 컨트롤러
@GetMapping("/course")
public String course (Model model, CourseParam parameter) {
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list" , list);
List<CategoryDto> categoryList = categoryService.frontList(CategoryDto.builder().build());
model.addAttribute("categoryList" , categoryList);
return "course/index" ;
}
Note) 실행 결과
Ex)
- 인덱스 페이지
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 정보 페이지</title >
<style >
span .price {
text-decoration : line-through;
}
</style >
</head >
<body >
<h1 > 강좌 정보 페이지</h1 >
<div >
<a href ="?" > 전체</a >
<th:block th:each ="y : ${categoryList}" >
|
<a th:href ="'/course?categoryId=' + ${y.id}" > <span th:text ="${y.categoryName}" > 카테고리명</span > </a >
</th:block >
<hr />
</div >
<ul >
<li th:each ="x : ${list}" >
<div >
<a th:href ="'/course/' + ${x.id}" >
<h3 th:text ="${x.subject}" > 강좌명</h3 >
</a >
<div >
<p th:text ="${x.summary}" > </p >
판매가: <span class ="price" th:text ="${x.price}" > </span >
할인가: <span th:text ="${x.salePrice}" > </span >
</p >
</div >
</div >
</li >
</ul >
</body >
</html >
- 컨트롤러
-> @RequestParam(name=" "): 카테고리id 값을 가져올 수 있다.
-> 그 다음, frontList()를 이용해서 전달한다.
@GetMapping("/course")
public String course (Model model,
CourseParam parameter) {
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list" , list);
List<CategoryDto> categoryList = categoryService.frontList(CategoryDto.builder().build());
model.addAttribute("categoryList" , categoryList);
return "course/index" ;
}
- 카테고리 서비스 임플
@Override
public List<CategoryDto> frontList (CategoryDto parameter) {
return categoryMapper.select(parameter);
}
- frontList()
@Override
public List<CourseDto> frontList (CourseParam parameter) {
if (parameter.getCategoryId() < 1 ) {
List<Course> courseList = courseRepository.findAll();
return CourseDto.of(courseList);
}
return courseRepository.findByCategoryId(parameter.getCategoryId()).map(CourseDto::of).orElse(null );
}
- 코스 리포짓
package com.zerobase.fastlms.course.repository;
import com.zerobase.fastlms.course.entity.Course;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface CourseRepository extends JpaRepository <Course , Long > {
Optional<List<Course>> findByCategoryId(long categoryId);
}
Note) 실행 결과 - 프로그래밍 탭 누른 화면
Ex) 강의의 전체 개수 표시
- 인덱스
<a href ="?" >
전체 (<span th:text ="${courseTotalCount}" > 0</span > )
</a >
- 컨트롤러
@GetMapping("/course")
public String course (Model model,
CourseParam parameter) {
List<CourseDto> list = courseService.frontList(parameter);
model.addAttribute("list" , list);
List<CategoryDto> categoryList = categoryService.frontList(CategoryDto.builder().build());
int courseTotalCount = 0 ;
if (categoryList != null ){
for (CategoryDto x : categoryList){
courseTotalCount += x.getCourseCount();
}
}
model.addAttribute("categoryList" , categoryList);
model.addAttribute("courseTotalCount" , courseTotalCount);
return "course/index" ;
}
Note) 실행 결과
14.2 강좌 상세 정보
Ex)
- 컨트롤러 courseDetail()
@GetMapping("/course/{id}")
public String courseDetail (Model model,
CourseParam parameter) {
CourseDto detail = courseService.frontDetail(parameter.getId());
model.addAttribute("detail" , detail);
return "course/detail" ;
}
- detail 페이지
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 상세 페이지</title >
<style >
span .price {
text-decoration : line-through;
}
</style >
</head >
<body >
<h1 > 강좌 상세 정보</h1 >
<div th:replace ="/fragments/layout.html :: fragment-body-menu" > </div >
<div >
<h2 > 강좌명: <span th:text ="${detail.subject}" > 강좌</span > </h2 >
<div th:utext ="${detail.contents}" > </div >
</div >
<div >
<p > 가격: <span th:text ="${detail.price}" > 0</span > </p >
<p > 할인 가격: <span th:text ="${detail.salePrice}" > 0</span > </p >
</div >
<div >
<button type ="button" > 수강신청</button >
<a href ="/course" > 강좌 목록</a >
</div >
</body >
</html >
- frontDetail()
CourseDto frontDetail (long id) ;
@Override
public CourseDto frontDetail (long id) {
Optional<Course> optionalCourse = courseRepository.findById(id);
if (optionalCourse.isPresent()){
return CourseDto.of(optionalCourse.get());
}
return null ;
}
- ThymeLeaf의 th:text
- ThymeLeaf의 th:utext
-> 스마트에디터에서 편집한대로 그대로 나온다.
Note) 실행 결과
14.3 강좌 신청 (1)
Ex) 수강 신청
- TakeCourse클래스
package com.zerobase.fastlms.course.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TakeCourse {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
long courseId;
String userId;
long payPrice;
String status;
LocalDateTime regDt;
}
- TakeCourseRepository
package com.zerobase.fastlms.course.repository;
import com.zerobase.fastlms.course.entity.TakeCourse;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TakeCourseRepository extends JpaRepository <TakeCourse , Long > {
}
-> 실행하면 자동으로 테이블이 세팅된다.
- Ajax 라이브러리를 가져온다. https://github.com/axios/axios
GitHub - axios/axios: Promise based HTTP client for the browser and node.js
Promise based HTTP client for the browser and node.js - GitHub - axios/axios: Promise based HTTP client for the browser and node.js
github.com
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" ></script>
- 디테일 자바스크립트
-> axios.post를 이용해서 데아터를 던진다.
-> axios.post().then() ..... StringBuilder에서 메서드 붙여서 추가하는 것과 비슷하다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 상세 페이지</title >
<style >
</style >
<script src ="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" > </script >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(function ( ) {
$('#submitForm' ).on('submit' , function ( ) {
var $thisForm = $(this );
var url = '/api/course/req.api' ;
var parameter = {
id : $thisForm.find('input[name=id]' ).val()
};
axios.post(url, parameter).then(function (response ) {
console .log(response);
}).catch(function (err ) {
console .log(err);
});
return false ;
});
});
</script >
</head >
<body >
<h1 > 강좌 상세 정보</h1 >
<div >
<h2 > 강좌명: <span th:text ="${detail.subject}" > 강좌</span > </h2 >
<div th:utext ="${detail.contents}" > </div >
</div >
<div >
<p > 가격: <span th:text ="${detail.price}" > 0</span > </p >
<p > 할인 가격: <span th:text ="${detail.salePrice}" > 0</span > </p >
</div >
<div >
<form id ="submitForm" method ="post" >
<input type ="hidden" name ="id" th:value ="${detail.id}" >
<button type ="submit" > 수강신청</button >
<a href ="/course" > 강좌 목록</a >
</form >
</div >
</body >
</html >
cf) @Controller와 @RestController 차이점
-> @Controller: 뷰 엔진의 형태로 반환
-> @RestController: Json 형태로 반환
- ApiCourseCotroller 클래스
-> RestController를 이용했기 때문에 문자열이 반환된다.
-> @RequestBody를 붙여야만 스프링이 자동으로 매핑해준다.
package com.zerobase.fastlms.course.controller;
import com.zerobase.fastlms.admin.service.CategoryService;
import com.zerobase.fastlms.course.model.TakeCourseInput;
import com.zerobase.fastlms.course.service.CourseService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class ApiCourseController extends BaseController {
private final CourseService courseService;
private final CategoryService categoryService;
@PostMapping("api/course/req.api")
public ResponseEntity<?> courseReq(Model model,
@RequestBody TakeCourseInput parameter){
return ResponseEntity.ok().body(parameter);
}
}
- TakeCourseInput 추가한다.
package com.zerobase.fastlms.course.model;
import com.zerobase.fastlms.admin.model.CommonParam;
import lombok.Data;
@Data
public class TakeCourseInput {
long courseId;
}
14.4 강좌 신청 (2)
- 수강 신청 누른 실행 결과
Ex)
- 컨트롤러
-> 수강 신청하려면 로그인한 내 아이디도 필요하다.
-> 시큐리티에 있는 Principal 인터페이스를 매개변수로 선언하면 스프링이 로그인 정보를 principal에 주입해준다.
- 테스트 코드
@PostMapping("api/course/req.api")
public ResponseEntity<?> courseReq(Model model,
@RequestBody TakeCourseInput parameter,
Principal principal){
parameter.setUserId(principal.getName());
return ResponseEntity.badRequest().body("수강신청에 실패했습니다." );
}
Note) 실행 결과
- bad request(에러 코드: 400) 정상 작동한다. preview, response에도 똑같이 나온다.
- 서비스에 req() 구현 => 수강 신청이 가능하게 만든다.
- 디테일 페이지
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 상세 페이지</title >
<style >
</style >
<script src ="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" > </script >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(function ( ) {
$('#submitForm' ).on('submit' , function ( ) {
if (!confirm("수강 신청을 하시겠습니까?" )){
return false ;
}
var $thisForm = $(this );
var url = '/api/course/req.api' ;
var parameter = {
courseId : $thisForm.find('input[name=id]' ).val()
};
axios.post(url, parameter).then(function (response ) {
console .log(response);
}).catch(function (err ) {
console .log(err);
});
return false ;
});
});
</script >
</head >
<body >
<h1 > 강좌 상세 정보</h1 >
<div >
<h2 > 강좌명: <span th:text ="${detail.subject}" > 강좌</span > </h2 >
<div th:utext ="${detail.contents}" > </div >
</div >
<div >
<p > 가격: <span th:text ="${detail.price}" > 0</span > </p >
<p > 할인 가격: <span th:text ="${detail.salePrice}" > 0</span > </p >
</div >
<div >
<form id ="submitForm" method ="post" >
<input type ="hidden" name ="id" th:value ="${detail.id}" >
<button type ="submit" > 수강신청</button >
<a href ="/course" > 강좌 목록</a >
</form >
</div >
</body >
</html >
Note) 실행 결과
- status code: 200 => 정상 실행된다는 뜻
- TakeCourseCode를 만든다.
package com.zerobase.fastlms.course.entity;
public interface TakeCourseCode {
String STATUS_REQ = "REQ" ;
String STATUS_COMPLETE = "COMPLETE" ;
String STATUS_CANCEL = "CANCEL" ;
}
- req()
-> status에 요청값을 넣는다.
@Override
public boolean req (TakeCourseInput parameter) {
Optional<Course> optionalCourse = courseRepository.findById(parameter.getCourseId());
if (!optionalCourse.isPresent()){
return true ;
}
Course course = optionalCourse.get();
TakeCourse takeCourse = TakeCourse.builder()
.courseId(parameter.getCourseId())
.userId(parameter.getUserId())
.payPrice(course.getSalePrice())
.regDt(LocalDateTime.now())
.status(TakeCourse.STATUS_REQ)
.build();
takeCourseRepository.save(takeCourse);
return true ;
}
Ex) 이미 수강 신청한 경우 처리 방법
- userId에 대한 courseId가 이미 있는지 확인한다.
- status가 CANCEL인 경우는 신청 가능해야한다.
- 리포지토리
package com.zerobase.fastlms.course.repository;
import com.zerobase.fastlms.course.entity.TakeCourse;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Collection;
public interface TakeCourseRepository extends JpaRepository <TakeCourse , Long > {
long countByCourseIdAndUserIdAndStatusIn (long courseId, String userId, Collection<String> statusList) ;
}
- 컨트롤러
-> 배열을 이용해서 수강 신청 완료했는지 요청 중인지 파악하여 count를 이용한다.
@Override
public boolean req (TakeCourseInput parameter) {
Optional<Course> optionalCourse = courseRepository.findById(parameter.getCourseId());
if (!optionalCourse.isPresent()){
return true ;
}
Course course = optionalCourse.get();
String[] statusList = {TakeCourse.STATUS_REQ, TakeCourse.STATUS_COMPLETE};
long count = takeCourseRepository.countByCourseIdAndUserIdAndStatusIn(course.getId()
, parameter.getUserId(), Arrays.asList(statusList));
if (count > 0 ){
return false ;
}
TakeCourse takeCourse = TakeCourse.builder()
.courseId(parameter.getCourseId())
.userId(parameter.getUserId())
.payPrice(course.getSalePrice())
.regDt(LocalDateTime.now())
.status(TakeCourse.STATUS_REQ)
.build();
takeCourseRepository.save(takeCourse);
return true ;
}
Note) 실행 결과
14.5 강좌 신청 (3)
Ex) 수강신청이 안되는 상황에 따라 알림 보여주는 방법
- 아까와는 다른게 수강 신청의 결과를 boolean이 아닌 문자열 형태로 반환할 것이다.
- req() 다시 작성한다.
@Override
public ServiceResult req (TakeCourseInput parameter) {
ServiceResult result = new ServiceResult();
Optional<Course> optionalCourse = courseRepository.findById(parameter.getCourseId());
if (!optionalCourse.isPresent()){
result.setResult(false );
result.setMessage("강좌 정보가 존재하지 않습니다." );
return result;
}
Course course = optionalCourse.get();
String[] statusList = {TakeCourse.STATUS_REQ, TakeCourse.STATUS_COMPLETE};
long count = takeCourseRepository.countByCourseIdAndUserIdAndStatusIn(course.getId()
, parameter.getUserId(), Arrays.asList(statusList));
if (count > 0 ){
result.setResult(false );
result.setMessage("이미 신청한 강좌 정보가 존재합니다." );
return result;
}
TakeCourse takeCourse = TakeCourse.builder()
.courseId(parameter.getCourseId())
.userId(parameter.getUserId())
.payPrice(course.getSalePrice())
.regDt(LocalDateTime.now())
.status(TakeCourse.STATUS_REQ)
.build();
takeCourseRepository.save(takeCourse);
result.setResult(true );
result.setMessage("" );
return result;
}
- 컨트롤러도 다시 작성
@PostMapping("api/course/req.api")
public ResponseEntity<?> courseReq(Model model,
@RequestBody TakeCourseInput parameter,
Principal principal){
parameter.setUserId(principal.getName());
ServiceResult result = courseService.req(parameter);
if (!result.isResult()){
ResponseResult responseResult = new ResponseResult(false , result.getMessage());
return ResponseEntity.ok().body(responseResult);
}
ResponseResult responseResult = new ResponseResult(true );
return ResponseEntity.ok().body(responseResult);
}
Note) 실행 결과 - 잘못된 courseId 번호에 대해 수강신청하는 경우
- 값이 있지만 이미 신청한 경우는?
- 자바스크립트
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 상세 페이지</title >
<style >
</style >
<script src ="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" > </script >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(function ( ) {
$('#submitForm' ).on('submit' , function ( ) {
if (!confirm("수강 신청을 하시겠습니까?" )){
return false ;
}
var $thisForm = $(this );
var url = '/api/course/req.api' ;
var parameter = {
courseId : $thisForm.find('input[name=id]' ).val()
};
axios.post(url, parameter).then(function (response ) {
console .log(response);
console .log(response.data);
var msg = response.data || '' ;
if (msg != '' ){
alert(msg);
}
}).catch(function (err ) {
console .log(err);
});
return false ;
});
});
</script >
</head >
<body >
<h1 > 강좌 상세 정보</h1 >
<div >
<h2 > 강좌명: <span th:text ="${detail.subject}" > 강좌</span > </h2 >
<div th:utext ="${detail.contents}" > </div >
</div >
<div >
<p > 가격: <span th:text ="${detail.price}" > 0</span > </p >
<p > 할인 가격: <span th:text ="${detail.salePrice}" > 0</span > </p >
</div >
<div >
<form id ="submitForm" method ="post" >
<input type ="hidden" name ="id" th:value ="${detail.id}" >
<button type ="submit" > 수강신청</button >
<a href ="/course" > 강좌 목록</a >
</form >
</div >
</body >
</html >
Note) 실행 결과
14.6 강좌 신청 (4)
- 지금까지는 데이터가 서버로 전송될 때, 문자열 형태로 전송이 되고 있다.
- 이 부분을 어떻게 바꿀 수 있을까?
Ex)
- result.json 파일
{
"header": {
"result": true,
"message": ""
},
"body": {
}
}
- ResponseResult
package com.zerobase.fastlms.common.model;
import lombok.Data;
@Data
public class ResponseResult {
ResponseResultHeader header;
Object body;
public ResponseResult (boolean result, String message) {
header = new ResponseResultHeader(result, message);
}
public ResponseResult (boolean result) {
header = new ResponseResultHeader(result);
}
}
- ResponseResultHeader
package com.zerobase.fastlms.common.model;
import lombok.Data;
@Data
public class ResponseResultHeader {
boolean result;
String message;
public ResponseResultHeader (boolean result, String message) {
this .result = result;
this .message = message;
}
public ResponseResultHeader (boolean result) {
this .result = result;
}
}
- 컨트롤러에서 데이터를 내릴 때, ResponseResult를 내리도록 한다.
-> 중복하여 수강신청했을 때 콘솔 화면
- detail
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 강좌 상세 페이지</title >
<style >
</style >
<script src ="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js" > </script >
<script src ="https://code.jquery.com/jquery-3.6.0.min.js" integrity ="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin ="anonymous" > </script >
<script >
$(function ( ) {
$('#submitForm' ).on('submit' , function ( ) {
if (!confirm("수강 신청을 하시겠습니까?" )){
return false ;
}
var $thisForm = $(this );
var url = '/api/course/req.api' ;
var parameter = {
courseId : $thisForm.find('input[name=id]' ).val()
};
axios.post(url, parameter).then(function (response ) {
console .log(response);
console .log(response.data);
response.data = response.data || {};
response.data.header = response.data.header || {};
if (!response.data.header.result){
alert(response.data.header.message);
return false ;
}
alert("강좌가 정상적으로 실행되었습니다." );
location.href ='/' ;
}).catch(function (err ) {
console .log(err);
});
return false ;
});
});
</script >
</head >
<body >
<h1 > 강좌 상세 정보</h1 >
<div >
<h2 > 강좌명: <span th:text ="${detail.subject}" > 강좌</span > </h2 >
<div th:utext ="${detail.contents}" > </div >
</div >
<div >
<p > 가격: <span th:text ="${detail.price}" > 0</span > </p >
<p > 할인 가격: <span th:text ="${detail.salePrice}" > 0</span > </p >
</div >
<div >
<form id ="submitForm" method ="post" >
<input type ="hidden" name ="id" th:value ="${detail.id}" >
<button type ="submit" > 수강신청</button >
<a href ="/course" > 강좌 목록</a >
</form >
</div >
</body >
</html >
Note) 실행 결과 - 중복으로 신청한 경우
Note) 실행 결과 - 새로운 강좌를 신청한 경우, 아래의 창이 뜨고 난 다음 메인 페이지 이동