![[Tistory] 카테고리 조회 (카테고리가 없으면 redirect)](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog%3Ftitle%3D%255BTistory%255D%2520%25EC%25B9%25B4%25ED%2585%258C%25EA%25B3%25A0%25EB%25A6%25AC%2520%25EC%25A1%25B0%25ED%259A%258C%2520%28%25EC%25B9%25B4%25ED%2585%258C%25EA%25B3%25A0%25EB%25A6%25AC%25EA%25B0%2580%2520%25EC%2597%2586%25EC%259C%25BC%25EB%25A9%25B4%2520redirect%29%2520%26logoUrl%3Dhttps%253A%252F%252Finblog.ai%252Finblog_logo.png%26blogTitle%3DCoding_study&w=2048&q=75)
Contents
1. 게시글 쓸 때, 카테고리 조회2. CategoryJPARepository3. DTO 만들기4. PostService5. PostController6. writeForm.mustache7. 화면8. 어라? 카테고리 네임도 공백으로 등록하면 등록됨..8-1. CategoryService 로직 추가8-2. category - writeForm.mustache 스크립트 추가8-3. 화면 - 폼 제출 안됨! 굿9. SweetAlert을 사용 - 카테고리가 없으면 알림 alert창 띄우고 싶다9-1. post - writeForm.mustache 스크립트 추가9-2. PostController 수정10. 화면 - 등록 잘됨1. 게시글 쓸 때, 카테고리 조회

여기에 categoryList 뿌릴 것이다.
2. CategoryJPARepository
// 카테고리 리스트 뿌리기 @Query("select new site.metacoding.blogv3.category.CategoryResponse$CategoryNameDTO(c.id, c.categoryName) " + "from Category c where c.user.id = :sessionUser order by c.categoryName") List<CategoryResponse.CategoryNameDTO> findByUserId(@Param("sessionUser") Integer sessionUser);
DTO로 바로 받을 때 생성자 있어야함~
CategoryResponse.CategoryNameDTO 쿼리 오류??…
원래 쿼리를 이렇게 썼는데…
// 카테고리 리스트 뿌리기 @Query("select new site.metacoding.blogv3.category.CategoryResponse.CategoryNameDTO(c.id, c.categoryName) " + "from Category c where c.user.id = :sessionUser order by c.categoryName") List<CategoryResponse.CategoryNameDTO> findByUserId(@Param("sessionUser") Integer sessionUser);
CategoryResponse.CategoryNameDTO 오류가 터진다?!!
Validation failed for query for method public abstract java.util.List 쿼리 유효성에 실패?????
CategoryResponse$CategoryNameDTO 쓰니가 오류 안터지고 CategoryResponse.CategoryNameDTO 쓰니까 오류 터지는데 이유
[ 중첩 클래스?? 무슨 차이지?? ]
CategoryResponse$CategoryNameDTO
와 CategoryResponse.CategoryNameDTO
의 차이는 문법적인 관점에서 접근[ 해결 ]

화면을 보면 게시판 쓸때 카테고리 리스트가 안에 있기 때문에
1. 구조적 관계
PostResponse.WriteFormDTO는 게시판(Post) 작성에 필요한 데이터 폼을 표현하는 DTO이고
이 안에 카테고리 리스트(List<CategoryResponse.CategoryNameDTO>)를 필드로 가지고 있죠.
하지만 CategoryResponse.CategoryNameDTO는 독립된 클래스이며, 단지 이 필드에서 카테고리 정보를 포함하는 용도로 사용되는 것 (CategoryResponse에서만 클래스가 정의 된 것)
2. 쿼리의 $ 사용 이유
이와 상관없이 JPQL에서는 중첩 클래스처럼 보일 때 바이트코드 표기법을 사용해야 하기 때문에
CategoryResponse$CategoryNameDTO처럼 $ 표기법을 사용해야 했던 것이며,
이것은 클래스의 실제 구조와는 상관없이 JPQL의 동작 방식에 따른 규칙
3. 결론
CategoryResponse를 참고해서 내부적으로 CategoryNameDTO 찾는다는 것!!
3. DTO 만들기
- CategoryResponse
@Data public static class CategoryNameDTO{ private Integer id; private String categoryName; public CategoryNameDTO(Integer id, String categoryName) { this.id = id; this.categoryName = categoryName; } }
- PostResponse
@Data public static class WriteFormDTO { private List<CategoryResponse.CategoryNameDTO> categoryNameDTO; public WriteFormDTO(List<CategoryResponse.CategoryNameDTO> categoryNameDTO) { this.categoryNameDTO = categoryNameDTO; } }
PostResponse에서 CategoryNameDTO를 innerDTO 만들어서 사용할 수도 있을거 같은데… 일단 CategoryResponse 에서 CategoryNameDTO 만들어줌 게시판 작성할때 카테고리랑 같이 insert시키려면 나중에 변경할 수 도 있을 것 같음
4. PostService
@Transactional public PostResponse.WriteFormDTO writeform(Integer sessionUserId){ User sessionUser = userJPARepo.findById(sessionUserId) .orElseThrow(() -> new RuntimeException("회원 정보가 존재하지 않습니다.")); // categoryJPARepo에서 sessionUser.getId()로 유저의 카테고리 목록을 조회하여 categoryList에 담음 List<CategoryResponse.CategoryNameDTO> categoryList = categoryJPARepo.findByUserId(sessionUser.getId()); System.out.println("categoryList = " + categoryList); // categoryList를 사용하여 PostResponse.WriteFormDTO 객체를 생성하고, 이를 writeFormDTO 변수에 할당 PostResponse.WriteFormDTO writeFormDTO = new PostResponse.WriteFormDTO(categoryList); return writeFormDTO; }
5. PostController
@GetMapping("/s/post/write-form") public String postWriteForm(HttpServletRequest request) { User user = (User) session.getAttribute("sessionUser"); PostResponse.WriteFormDTO writeFormDTOList = postService.writeform(user.getId()); System.out.println("writeFormDTOList = " + writeFormDTOList); request.setAttribute("model", writeFormDTOList); return "post/writeForm"; }
writeFormDTOList가 비어 있으면 카테고리 생성 페이지로 리다이렉트

@GetMapping("/s/post/write-form") public String postWriteForm(HttpServletRequest request) { User user = (User) session.getAttribute("sessionUser"); PostResponse.WriteFormDTO writeFormDTOList = postService.writeform(user.getId()); System.out.println("writeFormDTOList = " + writeFormDTOList); // 리스트가 비었으면 카테고리 생성 페이지로 리다이렉트 if (writeFormDTOList.getCategoryNameDTO().isEmpty()) { return "redirect:/s/category/write-form"; // 카테고리 생성 페이지로 리다이렉트 } request.setAttribute("model", writeFormDTOList); return "post/writeForm"; }
[ isEmpty ] → 컬렉션이 비어있는지 여부를 확인

[ 값 잘 받아옴 ]

6. writeForm.mustache

7. 화면

8. 어라? 카테고리 네임도 공백으로 등록하면 등록됨..


8-1. CategoryService 로직 추가
@Transactional public void save(String categoryName, Integer sessionUserId) { if (categoryName == null || categoryName.trim().isEmpty()) { throw new RuntimeException("카테고리 이름을 입력해주세요."); } User sessionUser = userJPARepo.findById(sessionUserId) .orElseThrow(() -> new RuntimeException("회원 정보가 존재하지 않습니다.")); Optional<Category> categoryOP = categoryJPARepo.findByCategoryNameAndUserId(categoryName, sessionUserId); if (categoryOP.isPresent()) { throw new RuntimeException("이미 존재하는 카테고리입니다.");} categoryJPARepo.save(Category.builder() .categoryName(categoryName) .user(sessionUser) .build()); }

그런데 생각해보니까 바로 폼 제출 막으면 되잖아!!
8-2. category - writeForm.mustache 스크립트 추가
- trim() 함수??
JavaScript 문자열 함수로, 문자열의 양 끝에 있는 공백 문자(스페이스, 탭, 줄바꿈 등)를 제거
즉, 사용자가 입력한 값에 불필요한 공백이 있을 경우, 이를 제거하여 깨끗한 문자열을 얻을 수 있다.
// JavaScript를 통해 폼 유효성 검사 $("#categoryForm").on("submit", function (e) { const categoryName = $("#categoryName").val().trim(); if (!categoryName) { alert("카테고리명을 입력해주세요."); e.preventDefault(); // 폼 제출 막기 } });
- 폼 유효성 검사에서 공백을 제거하지 않으면, 사용자가 빈 문자열이나 공백만 입력해도 폼이 제출될 수 있다. 이를 방지하기 위해 trim()을 사용해, 입력된 내용이 진짜로 있는지 확인할 수 있게 한다.
8-3. 화면 - 폼 제출 안됨! 굿

9. SweetAlert을 사용 - 카테고리가 없으면 알림 alert창 띄우고 싶다

9-1. post - writeForm.mustache 스크립트 추가
아…
swal.fire
안써서 ㅜㅜ alart창 안띄워져서 한참 헤맸네 ㅜㅜ 아.. 바보 연달아서 안하니까 바로 까먹어버림 ㅜㅜ .fire 잊지말자!!!

서버가 전달한 noCategory 값을 받아와서 JavaScript 변수로 사용
이 값은 서버에서 전달된 true 또는 false 값
9-2. PostController 수정
//게시글 쓰기 폼 @GetMapping("/s/post/write-form") public String postWriteForm(HttpServletRequest request) { User user = (User) session.getAttribute("sessionUser"); PostResponse.WriteFormDTO writeFormDTOList = postService.writeform(user.getId()); request.setAttribute("model", writeFormDTOList); // 카테고리 네임 비어 있는지 확인 if (writeFormDTOList.getCategoryNameDTO().isEmpty()) { request.setAttribute("noCategory", true); } else { request.setAttribute("noCategory", false); } return "post/writeForm"; }
noCategory 값이 true일 경우 (즉, 카테고리가 없는 경우) SweetAlert을 사용하여 경고창을 띄움.
사용자가 경고창의 확인 버튼을 누르면 카테고리 생성 페이지로 리다이렉트 됨
10. 화면 - 등록 잘됨


Share article