Spring Projcect/학습 관리 시스템 & 백오피스 구축

Chapter 12. 카테고리 화면

계란💕 2022. 8. 22. 17:04

12.1 강좌 카테고리 목록 구현 및 추가

  Ex)

    - admin 아래에 새로운 패키지를 만든다.category아래 리스트 파일 만든다.

   - 새로운 카테고리 컨트롤러

<hide/>
@Controller
@RequiredArgsConstructor
public class AdminCategoryController {
    private final MemberService memberService;

    @GetMapping("/admin/category/list.do")
    public String list(Model model, MemberParam parameter){

        return "admin/category/list";
    }
}

 

    - 레이아웃에 관리자 메인 페이지도 추가한다.

<hide/>
<!DOCTYPE html>
<html lang="ko"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>fastlms</title>
</head>
<body>
    <div th:fragment="fragment-body-menu">
        <div>
            <a href="/member/register">회원 가입</a>
            |
            <a href="/member/info">회원 정보</a>
            |
            <a href="/member/login">로그인</a>
            |
            <a href="/member/logout">로그아웃</a>
        </div>
        <hr/>
    </div>
    <div th:fragment="fragment-admin-body-menu">
        <a href="/admin/main.do">관리자 메인</a>
        |
        <a href="/admin/member/list.do">회원 관리</a>
        |
        <a href="#">카테고리 관리</a>
        |
        <a href="#">강의 관리</a>
        |
        <a href="/member/logout">로그 아웃</a>
        <br>
        <hr/>
    </div>
</body>
</html>
<hide/>
<!doctype html>
<html lang="ko"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>관리자 화면</title>
    <style>
    </style>
</head>
<body>
   <h1>카테고리 관리</h1>
   <div th:replace="/fragments/layout.html :: fragment-admin-body-menu" ></div>

    <div class="list">
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>카테고리명</th>
                    <th>순서</th>
                </tr>
            </thead>
            <tbody>

            </tbody>
        </table>
    </div>
</body>
</html>

  Note) 실행 결과

 

 

 

  Ex) 카테고리 클래스 만들고 데이터베이스에 들어갔는지 확인하기

 

     - 카테고리 창에 입력창 추가한다.

 

     - 컨트롤러에 add.do 추가한다.

<hide/>
@GetMapping("/admin/category/add.do")
public String list(Model model, CategoryInput parameter){
    return "admin/category/list";
}

 

    - CategoryInput() 클래스를 만든다.

 

    - Category 클래스 만들기 => @Entity를 붙이면 빨간 줄 생긴다.  

      ->자동 증가?

 

      -> 그럼 다음과 같이 키가 만들어진다.

<hide/>
package com.zerobase.fastlms.admin;
import javax.persistence.*;
@Entity
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

 

    - 레포지토리

      ->  yml 파일에 자동으로 DDL 만들어주는 부분이 세팅되어 있기 때문에 이렇게 하면 테이블이 만들어질 것이다.

<hide/>
package com.zerobase.fastlms.admin.repository;
import com.zerobase.fastlms.admin.entity.Category;
import com.zerobase.fastlms.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface CategoryRepository extends JpaRepository<Category, Long> {

}

  Note) 실행 결과

 

 

  Ex) 카테고리 리스트 구현하기

    - 카테고리 서비스 인터페이스

<hide/>
package com.zerobase.fastlms.admin.repository;
public interface CategoryService {
    /**
     * 카테고리 신규 추가
     */
    boolean add(String categoryName);
    
    /**
     *  카테고리 수정
     */
    boolean update(CategoryDto parameter);

    /**
     * 카테고리 삭제
     */
    boolean del(long id);
}

 

    - Dto 클래스

<hide/>
package com.zerobase.fastlms.admin.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryDto {
    private Long id;
    String categoryName;
    int sortValue;
    boolean usingYn;    // 사용가능한지
}

 

    - 카테고리 서비스 임플

<hide/>
package com.zerobase.fastlms.admin.service;
import com.zerobase.fastlms.admin.dto.CategoryDto;
import com.zerobase.fastlms.admin.entity.Category;
import com.zerobase.fastlms.admin.repository.CategoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CategoryServiceImpl implements  CategoryService {

    private  final CategoryRepository categoryRepository;
    
    @Override
    public List<CategoryDto> list(){
        List<CategoryDto> categoryDtoList = categoryRepository.findAll();
        List<Category> categories = categoryRepository.findAll();
        if(!CollectionUtils.isEmpty(categories)){
            categories.forEach( e-> {
                CategoryDto category= new CategoryDto();
                category.setId(e.getId());
                category.setCategoryName(e.getCategoryName());
                categoryDtoList.add(category);
            });
        }
        return categoryDtoList;
    }

    @Override
    public boolean update(CategoryDto parameter) {
        return false;
    }
    
    @Override
    public boolean del(long id) {
        return false;
    }
}

 

    - 카테고리 Dto 클래스

<hide/>
package com.zerobase.fastlms.admin.dto;
import com.zerobase.fastlms.admin.entity.Category;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CategoryDto {

    private Long id;
    String categoryName;
    int sortValue;
    boolean usingYn;    // 사용가능한지

    public static List<CategoryDto> of (List<Category> categories){

        if(categories != null){
            List<CategoryDto> categoryList = new ArrayList<>();
            for(Category x: categories){
                categoryList.add(of(x));
            }
            return categoryList;
        }
        return null;
    }

    public static CategoryDto of(Category category){

        return  CategoryDto.builder()
                .id(category.getId())
                .categoryName(category.getCategoryName())
                .sortValue(category.getSortValue())
                .build();
    }
}

 

    -  Impl의 list() 메서드 => 카테고리를 카테고리 DTO형태로 바꿔준다.

<hide/>
@Override
public List<CategoryDto> list(){
    List<Category> categories = new ArrayList<>();
    return CategoryDto.of(categories);
}

 

    - 컨트롤러 

      -> 모델에 add해주면 클라이언트(뷰) 페이지까지 내려간다.

<hide/>
@GetMapping("/admin/category/list.do")
public String list(Model model, MemberParam parameter){
    List<CategoryDto> list = categoryService.list();
    model.addAttribute("list", list);
    return "admin/category/list";
}
<hide/>
@PostMapping("/admin/category/add.do")
public String add(Model model, CategoryInput parameter){
    boolean result = categoryService.add(parameter.getCategoryName());

    return "redirect:/admin/category/list.do";
}

 

    - 카테고리 리스트

<hide/>
<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>카테고리명</th>
            <th>순서</th>
            <th>사용 여부</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="x : ${list}">
            <td th:text="${x.id}">1</td>
            <td th:text="${x.categoryName}">홍길동</td>
            <td th:text="${x.sortValue}">000</td>
            <td th:text="${x.usingYn}">1111</td>
        </tr>
    </tbody>
</table>

 

  Note) 실행 결과 - 데이터베이스에 행 추가

 

    - aaaaa를 입력한 결과

 

 

<hide/>
<!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;
        }

    </style>
</head>
<body>
   <h1>카테고리 관리</h1>
<!--   <div th:replace="/fragments/layout.html :: fragment-admin-body-menu" ></div>-->

    <div class="list">
        <div>
            <form method="post" action="/admin/category/add.do">
                <input type="text" name="categoryName" required placeholder="카테고리명 입력"/>
                <button type="submit">추가</button>
            </form>
        </div>

<!--        <div th:text="${list}">-->
<!--        </div>-->
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>카테고리명</th>
                    <th>순서</th>
                    <th>사용 여부</th>
                    <th>비고</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="x : ${list}">
                    <td th:text="${x.id}">1</td>

                    <td th:text="${x.categoryName}">홍길동</td>
                    <td th:text="${x.sortValue}">000</td>

                    <td>
                        <p th:text="${x.usingYn}">1111</p>
                    </td>
                    <td>
                        <button type="submit">삭제</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

 

    ====================== 오류 ======================

  - 오류: 추가 버튼을 누르면 데이터베이스에는 들어가는데 표에는 보이지가 않는다.

  - 원인: 임플 클래스 구현에 문제가 있었다.

  Note) 실행 결과

 

 

 

12.2 강좌 카테고리 수정, 삭제, 정렬

  Ex) 삭제 버튼 만들기

    - 리스트 파일

      -> 테스트 목적으로 alert(1)

<hide/>
<!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;
        }

    </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 (){
            $('form[name=deleteForm]').on('submit', function(){

                alert('1');
                return false;
            });
        });

    </script>
</head>
<body>
<h1>카테고리 관리</h1>
<!--   <div th:replace="/fragments/layout.html :: fragment-admin-body-menu" ></div>-->

<div class="list">
    <div>
        <form method="post" action="/admin/category/add.do">
            <input type="text" name="categoryName" required placeholder="카테고리명 입력"/>
            <button type="submit">추가</button>
        </form>
    </div>

    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>카테고리명</th>
                <th>순서</th>
                <th>사용 여부</th>
                <th>비고</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="x : ${list}">
                <td th:text="${x.id}">1</td>
                <td th:text="${x.categoryName}">홍길동</td>
                <td th:text="${x.sortValue}">000</td>
                <td>
                    <p th:text="${x.usingYn}">1111</p>
                </td>
                <td>
                    <form name="deleteForm" method="post" action="/admin/category/delete.do">
                        <input type="hidden" name="id" th:value="${x.id}"/>

                        <button type="submit">삭제</button>
                    </form>
                </td>
            </tr>
        </tbody>
    </table>
</div>
</body>
</html>

 

    - 1번 삭제 눌렀을 때

    - 삭제 css

<hide/>
<script>
    $(document).ready(function (){
        $('form[name=deleteForm]').on('submit', function(){

            if(!confirm('카테고리를 삭제하시겠습니다.? ')){
                return false;
            }
        });
    });

</script>

    - > 확인 누르면 아직까지는 화이트라벨

 

    - 삭제 버튼 확인 누르고 난 다음 화면 만들기위해 컨트롤러에 내용 추가

<hide/>
@PostMapping("/admin/category/delete.do")
public String del(Model model,CategoryInput parameter){
    boolean result = categoryService.del(parameter.getId());
    return "redirect:/admin/category/list.do";
}

 

    - 임플에 del 구현

<hide/>
@Override
public boolean del(long id) {
    categoryRepository.deleteById(id);
    return true;
}

  Note) 실행 결과 - 1번 카테고리가 삭제되었다.

 

 

 

  Ex) 카테고리 리스트에 데이터가 없는 경우 보여줄 화면 구성하기

     - colspan

<hide/>
<tr th:each="x : ${list}">
    <td th:text="${x.id}">1</td>
    <td th:text="${x.categoryName}">홍길동</td>
    <td th:text="${x.sortValue}">000</td>
    <td>
        <p th:text="${x.usingYn}">1111</p>
    </td>
    <td>
        <form name="deleteForm" method="post" action="/admin/category/delete.do">
            <input type="hidden" name="id" th:value="${x.id}"/>

            <button type="submit">삭제</button>
        </form>
    </td>
</tr>
<tr>
    <td colspan="5">
        <p class="nothing">내용이 없습니다.</p>
    </td>
</tr>

  Note) 실행 결과

 

 

  Ex) 카테고리 창에서 바로 수정하기

    - 체크 박스는 두 가지 옵션 중 선택해야하는 상황이라면 두 개에 대해 같은 이름의 변수를 줘야한다.

    - name을 왜 복잡하게 주나 했는데 아이디 별로 usingYn 이 다르므로 각각 변수 이름을 다르게 줘서 구분이 가능하다

<hide/>
<tbody>
    <tr th:each="x : ${list}">
        <td th:text="${x.id}">1</td>

        <td>
            <input  th:value="${x.categoryName}" type="text"  name="categoryName" />
        </td>

        <td>
            <input th:value="${x.sortValue}" type="text"  name="sortValue"/>
        </td>

        <td>
            <input th:checked="${x.usingYn}" type="radio" th:name="'usingYn_' + ${x.id}" value="true"/>
            <input th:checked="${!x.usingYn}" type="radio" th:name="'usingYn_' + ${x.id}" value="false"/>
        </td>
        <td>
            <form name="deleteForm" method="post" action="/admin/category/delete.do">
                <input type="hidden" name="id" th:value="${x.id}"/>

                <button type="submit">삭제</button>
            </form>
        </td>
    </tr>
    <tr th:if="${#lists.size(list) < 1}">
        <td colspan="5">
            <p class="nothing">내용이 없습니다.</p>
        </td>
    </tr>
</tbody>

  Note) 실행 결과

 

  Ex) 사용 여부 y/n 선택 기능

<hide/>
<td>
    <label th:for="'usingYn_yes_' + ${x.id}">Y</label>
    <input th:checked="${x.usingYn}" type="radio" th:id="'usingYn_yes_' + ${x.id}" th:name="'usingYn_' + ${x.id}"  value="true"/>
    <label th:for="'usingYn_no_' + ${x.id}">N</label>
    <input th:checked="${x.usingYn}" type="radio" th:id="'usingYn_no_' + ${x.id}" th:name="'usingYn_' + ${x.id}"  value="false"/>
</td>

  Note) 실행 결과

 

 

  Ex)  체크 박스

<hide/>
<td>
    <input th:checked="${x.usingYn}" type="checkbox" th:id="'usingYn_' + ${x.id}" th:name="'usingYn_' + ${x.id}"  value="true"/>
    <label th:for="'usingYn_yes_' + ${x.id}">사용</label>
</td>

  Note) 실행 결과

 

 

  Ex) 수정 삭제 버튼에 대해 같은 클래스 이름을 적용

    - CSS에서 display에 inline-block으로 지정하면 나란히 옆으로 배치된다.

<hide/>
<div class="inline-div">
    <button type="button">수정</button>
</div>
<div class="inline-div">
    <form name="deleteForm" method="post" action="/admin/category/delete.do">
            <input type="hidden" name="id" th:value="${x.id}"/>
            <button type="submit">삭제</button>
    </form>
</div>

  Note) 실행 결과 - 성공

 

 

  Ex) 자바스크립트

<hide/>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
    $(document).ready(function (){
        $('form[name=deleteForm]').on('submit', function(){
            if(!confirm('카테고리를 삭제하시겠습니다.?')){
                return false;
            }
        });
        $('button.update-button').on('click', function() {
            var $this = $(this);
            var $tr = $this.closest('tr');
            var id = $tr.find('input[name=id]').val();
            var categoryName = $tr.find('input[name=categoryName]').val();
            var sortValue = $tr.find('input[name=sortValue]').val();
            var usingYn = $tr.find('input[type=checkbox]')[0].checked;
            console.log(id, categoryName, sortValue, usingYn);
        });
    });
</script>

  Note) 실행 결과

 

 

  Ex) 수정하기

 

    - 컨트롤러 수정 메서드

<hide/>
@PostMapping("/admin/category/update.do")
public String update(Model model,CategoryInput parameter){

    boolean result = categoryService.update(parameter);
    return "redirect:/admin/category/list.do";
}

    - 자바스크립트

      -> 수정은 on('click') / 삭제는 on('submit') => 뭐가 다를까???

 

 

    cf) val와 var의 차이는?

      - val: 데이터를 바꿀 수 없는 읽기 전용 변수 (자바의 final과 같다.)

      - var: 값을 변경 가능한 변수

 

<hide/>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
    $(document).ready(function (){
        $('form[name=deleteForm]').on('submit', function(){
            if(!confirm('카테고리를 삭제하시겠습니까?')){
                return false;
            }
        });

        $('button.update-button').on('click', function() {

            if(!confirm('카테고리를 수정하시겠습니다?')){
                return false;
            }
            var $this = $(this);
            var $tr = $this.closest('tr');

            var id = $tr.find('input[name=id]').val();
            var categoryName = $tr.find('input[name=categoryName]').val();
            var sortValue = $tr.find('input[name=sortValue]').val();
            var usingYn = $tr.find('input[type=checkbox]')[0].checked;

            $updateForm = ${'form[name=updateForm]'}
            $updateForm.find('input[name=id]').val(id);
            $updateForm.find('input[name=categoryName]').val(categoryName);
            $updateForm.find('input[name=sortValue]').val(sortValue);
            $updateForm.find('input[name=usingYn]').val(usingYn);
            $updateForm.submit();
        });
    });
</script>

 

    - 임플에서 update()  메서드 구현

<hide/>
@Override
public boolean update(CategoryInput parameter) {
    Optional<Category> optionalCategory = categoryRepository.findById(parameter.getId());
    if(optionalCategory.isPresent()){
        Category category = optionalCategory.get();

        category.setCategoryName(parameter.getCategoryName());
        category.setSortValue(parameter.getSortValue());
        category.setUsingYn(parameter.isUsingYn());
        categoryRepository.save(category);
    }
    return true;
}

  Note) 실행 결과 - 정상

 

 

========================오류 =========================

   - 오류: 삭제할 때, 알림창이 나와야하는데 갑자기 안 나온다. 삭제는 정상으로 실행된다.

   - 원인: 정확한 이유는 모르겠으나 다시 돌려보니까 정상 실행된다.

 

 

 

  Ex) 리스트에서 순서에 맞춰 카테고리를 가져오기

    - 카테고리 리포지토리

<hide/>
package com.zerobase.fastlms.admin.repository;
import com.zerobase.fastlms.admin.entity.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface CategoryRepository extends JpaRepository<Category, Long> {
    Optional<List<Category>> findAllOrderBySortValueDesc();
}

 

    - 카테고리 임플 클래스

<hide/>
private Sort getSortBySortValueDesc(){
    return Sort.by(Sort.Direction.DESC, "sortValue");
}

@Override
public List<CategoryDto> list(){

    List<Category> categories = categoryRepository.findAll(getSortBySortValueDesc());
    return  CategoryDto.of(categories);
}

  Note) 실행 결과

 

====================== 오류 ======================

<hide/>

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'adminCategoryController' defined in file [C:\Users\Ran\Desktop\R\zerobase\Spring\fastlms-main\fastlms-main\target\classes\com\zerobase\fastlms\admin\controller\AdminCategoryController.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'categoryServiceImpl' defined in file [C:\Users\Ran\Desktop\R\zerobase\Spring\fastlms-main\fastlms-main\target\classes\com\zerobase\fastlms\admin\service\CategoryServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'categoryRepository' defined in com.zerobase.fastlms.admin.repository.CategoryRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! Reason: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.9.jar:5.3.9]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.9.jar:5.3.9]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.4.jar:2.5.4]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-2.5.4.jar:2.5.4]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) [spring-boot-2.5.4.jar:2.5.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) [spring-boot-2.5.4.jar:2.5.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) [spring-boot-2.5.4.jar:2.5.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) [spring-boot-2.5.4.jar:2.5.4]
	at com.zerobase.fastlms.FastlmsApplication.main(FastlmsApplication.java:12) [classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_342]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_342]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_342]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_342]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.7.2.jar:2.7.2]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'categoryServiceImpl' defined in file [C:\Users\Ran\Desktop\R\zerobase\Spring\fastlms-main\fastlms-main\target\classes\com\zerobase\fastlms\admin\service\CategoryServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'categoryRepository' defined in com.zerobase.fastlms.admin.repository.CategoryRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! Reason: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-5.3.9.jar:5.3.9]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-5.3.9.jar:5.3.9]
	... 24 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'categoryRepository' defined in com.zerobase.fastlms.admin.repository.CategoryRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! Reason: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.zerobase.fastlms.admin.repository.CategoryRepository.findAllOrderBySortValueDesc()! No property desc found for type int! Traversed path: Category.sortValue.

 

  - 오류: 서버가 안뜬다. 

    -> UnsatisfiedDependencyException: 빈을 생성하는데 해당 빈이 충돌이 된 것이다.

  - 원인: 카테고리 리포지토리 인터페이스에 구현된 함수를 잠시 주석 처리 하니까 해결 (이유는 잘 모르겠으나 아직 사용중인 메서드가 아니라 주석 처리)

    -> findBy... ()와 같은 메서드를 만들 때는 반드시 엔티티 안에 있는 변수명, 매개변수의 타입을 맞춰서 만들어야한다. 

    -> 따라서, 엔티티 안에 변수명이 없는 경우는 에러가 난다.

 

 

 

=========================== 오류 -===========================

  - 오류: 수정 버튼을 누르고 나면 name부분이 null로 초기화 되버린다.

  - 원인: 카테고리 리스트에 updateForm부분에 name="categoryName"이라고 써야하는데 오타가 나서 값이 안 들어갔다.

    -> HTML에서는 카테고리 엔티티에 대한 필드값을 잘못 적더라도 빨간줄이 그인다거나 하지 않아서 주의해야한다.

    -> 자바스크립트에서는 중괄호나 괄호가 짝이 맞지 않는 경우도 주의해야한다.