관리 메뉴

bright jazz music

guestbook : 09. guestbook 게시글 조회 처리 본문

Framework/Spring

guestbook : 09. guestbook 게시글 조회 처리

bright jazz music 2022. 7. 20. 22:23

● 현재는 게시글의 내용을 조회할 수 없다.

● 따라서 게시글을 을 클릭했을 때 내용을 조회할 수 있도록 처리한다.

 

1. service 인터페이스 수정 (GuestbookService.java)

//GuestbookService.java (인터페이스)
package com.example.guestbook.service;

import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import org.springframework.stereotype.Service;

//@Service 어노테이션은 GuestbookImple에 적어준다.
public interface GuestbookService { //GuestbookImple 클래스에서 이 인터페이스를 상속한다.

    Long register(GuestbookDTO dto); // GuestbookImpl 클래스에서 오버라이딩
    
    //getList()메서드
    PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestDTO);

    //dto에서 Entity로의 변환작업
    default Guestbook dtoToEntity(GuestbookDTO dto) {
        Guestbook entity = Guestbook.builder()
                .gno(dto.getGno())
                .title(dto.getTitle())
                .content(dto.getContent())
                .writer(dto.getWriter())
                .build();
        return entity;
    }

    //Entity에서 DTO로의 변환작업
    default GuestbookDTO entityToDto(Guestbook entity) {
        GuestbookDTO dto = GuestbookDTO.builder()
                .gno(entity.getGno())
                .title(entity.getTitle())
                .content(entity.getContent())
                .writer(entity.getWriter())
                .regDate(entity.getRegDate())   //빼먹어서 추가함
                .modDate(entity.getModDate())   //빼먹어서 추가함
                .build();
        return dto;
    }

    //***게시글을 조회하는 메서드. 접근 제한자는 default이다. 생략되어 있다.
    GuestbookDTO read(Long gno);
}

/*
* default 메서드:
*   인터페이스의 실제 내용을 가지는 코드를 default라는 키워드로 생성 가능.
*   default 메서드를 이용하면 기존에 추상 클래스를 통해 전달해야 하는 실제 코드를
*   인터페이스에 선언할 수 있음.
*
*   '인터페이스 -> 추상클래스 -> 구현 클래스'의 형태에서 추상클래스를 생략하는 것이 가능해짐.
* */

 

2. service 구현계층 수정 (GuestbookServiceImpl.java)

 

//GuestbookImpl.java

package com.example.guestbook.service;

import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
import com.example.guestbook.entity.Guestbook;
import com.example.guestbook.repository.GuestbookRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.function.Function;

@Service //스프링이 빈으로 처리하도록 @Service 어노테이션 추가
@Log4j2
@RequiredArgsConstructor // <== 의존성 자동 주입!
public class GuestbookServiceImpl implements GuestbookService {

    private final GuestbookRepository repository; //jpa 처리를 위해 repository 주입! 반드시 fianl 사용

    @Override //GuestbookService에서 상속한 메서드 오버라이딩
    public Long register(GuestbookDTO dto) {

        log.info("DTO--------------------------------------");
        log.info(dto);

        Guestbook entity = dtoToEntity(dto); // GuestbookService에서 default를 이용하여 생성한 메서드

        log.info(entity);

        repository.save(entity); // 처리 저장.
//        return null;
        return entity.getGno(); //처리 후에는 엔티티의 gno 리턴
    }

    @Override
    public PageResultDTO<GuestbookDTO, Guestbook> getList(PageRequestDTO requestedDTO){
        Pageable pageable = requestedDTO.getPageable(Sort.by("gno").descending());

        Page<Guestbook> result = repository.findAll(pageable);

        Function<Guestbook, GuestbookDTO> fn = (entity -> entityToDto(entity));

        return new PageResultDTO<>(result, fn);
    }

    //게시글 조회 메서드 추가
   @Override
    public GuestbookDTO read(Long gno) {

       Optional<Guestbook> result = repository.findById(gno);

        return result.isPresent() ? entityToDto(result.get()) : null;
        //삼항연사자. GuestbookRepository에서 findById()를 통해 엔티티 객체를 가져온다면
       // 이를 DTO로 바꾸어서 반환한다.
    }
}

 

3. Controller 계층 수정 (GuestbookController.java)

//GuestbookController.java

package com.example.guestbook.controller;

import com.example.guestbook.dto.GuestbookDTO;
import com.example.guestbook.dto.PageRequestDTO;
import com.example.guestbook.dto.PageResultDTO;
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 //자동 주입을 위한 어노테이션
public class GuestbookController {

    private final GuestbookService service; // final로 선언

    @GetMapping("/")
    public String index(){
        return "redirect:/guestbook/list";
    }

    @GetMapping("/list")
    public String list(PageRequestDTO pageRequestDTO, Model model){

        log.info("list...................." + pageRequestDTO);

        // model에 result를 key로 담아서 list 페이지에 뿌려준다.
        model.addAttribute("result", service.getList(pageRequestDTO));

        return "/guestbook/list";
    }

    /*
        SpringDAta JPA를 이용하는 경우 @Pageable 어노테이션으로 Pageable 타입을 이용할 수도 있고,
        application.properties에 0이 아닌 1부터 페이지 번호를 시작하도록 받을 수 있도록 처리할 수도 있다.
        예제에서는 그냥 0부터 받는 방식을 사용하였다. 추후 검색조건 등과 같이 추가로 전달되어야 하는 데이터가
        많을 경우 더욱 복잡해질 수 있기 때문이다.
     */



    @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";
        /*
        * 등록 작업은 GET 방식에서는  화면을 보여주고 POST 방식에서는 처리 후에 목록페이지로 이동하도록 설계.
        * 이 때 RedirectAttributes를 이용해서 한 번만 화면에서 'msg'라는 이름의 변수를 사용할 수 있도록 처리.
        * addFlashAttribute()는 단 한 번만 데이터를 전달하는 용도로 사용함.
        * --> 브라우저에 전달되는 'msg'를 이용해서 화면 창에 모달 창을 보여주는 용도로 사용.
        * */
    }

    @GetMapping("/read")	//requestDTO이다. result가 아니다.
    public void read(long gno, @ModelAttribute("requestDTO") PageRequestDTO requestDTO, Model model){
        log.info("gno: " + gno);
        GuestbookDTO dto = service.read(gno);
        model.addAttribute("dto", dto);
    }


}

 

 

 

4. view 계층 수정 (read.html)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content})}">
    <th:block th:fragment="content">

        <h1 class="mt-4">GuestBook Read Page!!</h1>

        <div class="form-group">
            <label>Gno</label>
            <input type="text" class="form-control" name="Gno" th:value="${dto.gno}" readonly>
        </div>

        <div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" name="Title" th:value="${dto.title}" readonly>
        </div>

        <div class="form-group">
            <label>Content</label>
            <textarea area class="form-control" rows="5" name="Content" readonly>[[${dto.content}]]</textarea>
        </div>

        <div class="form-group">
            <label>Writer</label>
            <input type="text" class="form-control" name="writer" th:value="${dto.writer}" readonly>
        </div>

        <div class="form-group">
            <label>RegDate</label>
            <input type="text" class="form-control" name="regDate"
                   th:value="${#temporals.format(dto.regDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>

        <div class="form-group">
            <label>ModDate</label>
            <input type="text" class="form-control" name="modDate"
                   th:value="${#temporals.format(dto.modDate, 'yyyy/MM/dd HH:mm:ss')}" readonly>
        </div>

        <a th:href="@{/guestbook/modify(gno=${dto.gno}, page=${requestDTO.page})}">
            <button type="button" class="btn btn-primary">Modify</button>
        </a>

        <a th:href="@{/guestbook/list(page=${requestDTO.page})}">
            <button type="button" class="btn btn-info">List</button>
        </a>

    </th:block>
</th:block>

</html>

 

5. 확인

게시글 번호#(링크) 클릭
Modify버튼은 기능을 구현하지 않아서 누르면 404오류 발생. List버튼을 누르면 목록으로 이동

 

Comments