ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ch4 - 방명록 프로젝트 톺아보기
    SpringBoot 2023. 12. 28. 17:42

     (책 - 코드로 배우는 스프링 부트 웹 프로젝트)

    https://github.com/uqualid/SBW-ex4-guestbook

     

    프로젝트 파일 구성

    html과 css의 경우 제공되는 탬플릿을 따라 구성되어 있고, 외에는 여타 프로젝트와 동일하게 

    entity, DTO, repository, service(+ Impl) , controller로 구성되어 있다)

     

     

     

    GuestBook entity 구성

      변수명 속성
    게스트북 아이디 gno Long, PK
    제목 title String
    내용 content String
    작성자 writer String
    생성시간
    regDate
    LocalDateTime
    수정시간 modDate LocalDateTime

    생성시간과 수정시간은 BaseEntity 추상 클래스에서 

    AuditingEntityListener를 통해 자동으로 설정될 수 있게 구성되어 있다 
     
     
    Repository
    package com.example.guestbook.repository;
    
    import com.example.guestbook.entity.Guestbook;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.querydsl.QuerydslPredicateExecutor;
    
    public interface GuestbookRepository extends JpaRepository<Guestbook, Long>, QuerydslPredicateExecutor<Guestbook> {
    }
     

    repository에서는 JpaRepository를 상속받아 JpaRepository에 이미 구성되어 있는 인터페이스를 활용하도록 하였고 Spring Data Jpa에서 Querydsl을 활용할 수 있는 QuerydslPredicateExecutor도 함께 상속받았다 

     

     

    dto의 경우 Guestbook, PageRequest, PageResult DTO로 구성되어 있는데 

    GuestbookDTO의 경우 기본 Guestbook 엔티티 관련 속성만 포함되어 있고

     Page DTO의 경우 목록 구현을 위한 요소들이 주로 구성되어 있다 

     

    package com.example.guestbook.dto;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    
    @Builder
    @AllArgsConstructor
    @Data
    public class PageRequestDTO {
        private int page;
        private int size;
        private String type;
        private String keyword;
    
    
        public PageRequestDTO(){
            this.page = 1;
            this.size = 10;
        }
    
        public Pageable getPageable(Sort sort){
            return PageRequest.of(page -1, size, sort);
        }
    }
    package com.example.guestbook.dto;
    
    import lombok.Data;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    
    import java.util.List;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    @Data
    public class PageResultDTO<DTO, EN> {
    
        //DTO리스트
        private List<DTO> dtoList;
    
        //총 페이지 번호
        private int totalPage;
    
        //현재 페이지 번호
        private int page;
        //목록 사이즈
        private int size;
    
        //시작 페이지 번호, 끝 페이지 번호
        private int start, end;
    
        //이전, 다음
        private boolean prev, next;
    
        //페이지 번호  목록
        private List<Integer> pageList;
    
        public PageResultDTO(Page<EN> result, Function<EN, DTO> fn){
            dtoList = result.stream().map(fn).collect(Collectors.toList());
            totalPage = result.getTotalPages();
            makePageList(result.getPageable());
        }
    
        private void makePageList(Pageable pageable){
            this.page = pageable.getPageNumber() +1; //0부터 시작이므로 1 추가
            this.size = pageable.getPageSize();
    
            //temp end page
             int tempEnd = (int)(Math.ceil(page/10.0)) * 10;
    
             start = tempEnd - 9;
             prev = start > 1;
             end = totalPage > tempEnd ? tempEnd : totalPage;
             next = totalPage > tempEnd;
             pageList = IntStream.rangeClosed(start,end).boxed().collect(Collectors.toList());
        }
    }

     

     

    GuestbookService에서는 dto와 entity 간의 변환을 돕는

    dto to entity와 entity to dto 외에 기본 CRUD 관련 기능을 모두 Impl 클래스에서 override 하여 구현하고 있는데 

    사실 이 부분은 그냥 Service 내에서 구현해 줘도 된다... 고 알고 있음 

     

    register - 등록

    read - 단일 열람(읽기)

    remove - 삭제

    modify - 수정

    getList - 목록 / QBaseEntity(Querydsl query type) 를 통해 검색 조건에 따른 Guestbook 목록을 결과로 보여주고 있음 
     
     
    controller 
    package com.example.guestbook.controller;
    
    
    import com.example.guestbook.dto.GuestbookDTO;
    import com.example.guestbook.dto.PageRequestDTO;
    import com.example.guestbook.service.GuestbookService;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    
    @Controller
    @RequestMapping("/guestbook")
    @Log4j2
    @RequiredArgsConstructor // 자동 주입 annotation
    public class GuestbookController {
    
        private final GuestbookService service; // final 선언
    
        @GetMapping("/")
        public String index(){
            return "redirect:/guestbook/list";
        }
    
    
        @GetMapping({"/list"})
        public void list(PageRequestDTO pageRequestDTO, Model model){
            log.info("list............" + pageRequestDTO);
            model.addAttribute("result", service.getList(pageRequestDTO));
        }
    
        @GetMapping("/register")
        public void register(){
            log.info("register get...");
        }
    
        @PostMapping("/register")
        public String registerPost(GuestbookDTO dto, RedirectAttributes redirectAttributes){
    
            log.info("dto..." + dto);
    
            //새로 추가된 엔티티의 번호
            Long gno = service.register(dto);
    
            redirectAttributes.addFlashAttribute("msg", gno);
    
            return "redirect:/guestbook/list";
        }
    
        @GetMapping({"/read", "/modify"})
        public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model){
            log.info("gno: "  +gno);
            GuestbookDTO dto = service.read(gno);
            model.addAttribute("dto", dto);
        }
    
        @PostMapping("/remove")
        public String remove(long gno, RedirectAttributes redirectAttributes){
            log.info("gno: ", gno);
            service.remove(gno);
            redirectAttributes.addFlashAttribute("msg", gno);
            return "redirect:/guestbook/list";
        }
    
        @PostMapping("/modify")
        public String modify(GuestbookDTO dto, @ModelAttribute("requestDTO") PageRequestDTO requestDTO,
                             RedirectAttributes redirectAttributes){
            log.info("post modify.....................................");
            log.info("dto: " + dto);
    
            service.modify(dto);
    
            redirectAttributes.addAttribute("page", requestDTO.getPage());
            redirectAttributes.addAttribute("type", requestDTO.getType());
            redirectAttributes.addAttribute("keyword", requestDTO.getKeyword());
            redirectAttributes.addAttribute("gno", dto.getGno());
    
    
            return "redirect:/guestbook/read";
        }
    }

    각 기능에 따른 REST API 처리 방법과 링크를 구분해 요청을 처리한다 

     

    ApplicationTests 실행 후에 볼 수 있는 목록 페이지는 이러하다

    10개 단위로 글을 볼 수 있으며 각 게시글 번호에 하이퍼링크가 부여되어 있어 단일 조회가 가능하다 

     

    초기 페이지 우측의 REGISTER 버튼을 누르면 글을 작성할 수 있으며 제목, 내용, 작성자를 입력한다 

    앞서 언급한 바와 같이 생성 시간/ 수정 시간은 자동으로 부여된다 

     

     

    검색 시에는 키워드를 구분하여 원하는 게시글을 찾을 수 있다 

     

     

    단일 조회 시에 나오는 화면으로 Modify 버튼을 클릭하면 게시글을 수정 혹은 가능하다 

    (List는 초기 목록 화면으로) 

     

     

     


    Modify 버튼 선택 시의 화면인데, html 상에서
    textarea와 readonly를 구분하여 수정이 가능한 영역을 구분할 수 있다 

    댓글