Spring boot

2024.10.08 Blog 프로젝트 만들기(JPA) 게시글 쓰기

정훈5 2024. 10. 8. 11:20

 

학습목표

JPA를 활용하여 게시글 작성 기능을 구현하는 방법을 학습합니다.

이 과정에서는 데이터 저장, DTO 사용, 컨트롤러 구현 등을 다루며, ORM의 영속성 관리에 대한 이해를 심화합니다.

 

BoardRepository save 메서드 구현하기

목표: Board 엔티티를 데이터베이스에 저장하는 save 메서드를 구현합니다.

 

boardRepository.java 추가

@ModelAttribute  @RequestBody 에 대한 차이점을 이해하자

더보기
/**
     * em.persist(board) --> 비영속 상태인 엔티티를 영속상태로 전환
     * @param board
     * @return
     */
    @Transactional
    public Board save(Board board) {
        em.persist(board);
        return board;
    }

 

boardDTO.java

더보기
package com.tenco.blog_v1.board;

import com.tenco.blog_v1.user.User;
import lombok.Data;

public class BoardDTO {

    @Data
    public static class SaveDTO {
        private String title;
        private String content;

        public Board toEntity(User user) {
            return Board.builder()
                    .title(this.title)
                    .content(this.content)
                    .user(user)
                    .build();
        }
    }
}

 

BoardController.java

게시글 저장 save( ) 메서드 수정

더보기
package com.tenco.blog_v1.board;

import com.tenco.blog_v1.user.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Controller
public class BoardController {

    // DI
    // @Autowired
    // 네이티브 쿼리 연습
    private final BoardNativeRepository boardNativeRepository;
    // JPA API, JPQL
    private final BoardRepository boardRepository;
    // 세션을 받기위한 준비
    private final HttpSession session;

    // 특정 게시글 요청 확인
    // 주소설계 - http://localhost:8080/board/1
    @GetMapping("/board/{id}")
    public String detail(@PathVariable(name = "id") Integer id, HttpServletRequest request) {

        // JPA API 사용
        //  Board board = boardNativeRepository.findById(id);
        //  request.setAttribute("board", board);

        // JPQL FETCH join 사용
           Board board = boardRepository.findByIdJoinUser(id);
           request.setAttribute("board", board);

        return "board/detail";
    }

    /**
     * http://localhost:8080/
     * @param model
     * @return
     */
    @GetMapping("/")
    public String index(Model model) {
//List<Board> boardList = boardNativeRepository.findAll();
List<Board> boardList = boardRepository.findAll();

        model.addAttribute("boardList", boardList);
        log.warn("여기까지 오나?");
//        log.info("boardList : " + boardList);
        // templates 폴더 안에 있는 index.mustache 실행
        return "index";
    }

    // 주소설계 - http://localhost:8080/board/save-form
    // 게시글 작성 화면
    @GetMapping("/board/save-form")
    public String saveForm() {

        return "board/save-form";
    }
    
    // 게시글 저장
    // 주소설계 - http://localhost:8080/board/save
    @PostMapping("/board/save")
    public String save(@ModelAttribute BoardDTO.SaveDTO reqDto) {
        User sessionUser = (User)session.getAttribute("sessionUser");

        if(sessionUser == null) {
            return "redirect:/login-form";
        }

        // 파라미터가 올바르게 전달 되었는지 확인하기 위해서 사용
        log.warn("save 실행: 제목={}, 내용={}", reqDto.getTitle(), reqDto.getContent());

       // boardNativeRepository.save(title, content);

        // SaveDTO에서 toEntity 메서드를 사용해서 Board 엔티티로 변환하고 인수값으로 User 정보를 넣었다.
        // 결국 Board 엔티티로 반환이 된다.
        boardRepository.save(reqDto.toEntity(sessionUser));
        return "redirect:/";
    }



    // 주소설계 - http://localhost:8080/board/10/delete (form 활용하기 때문에 delete 선언)
    // form 태그에서는 GET, POST 방식만 지원하기 때문이다.
    @PostMapping("/board/{id}/delete")
    public String delete(@PathVariable(name = "id") Integer id) {
        boardNativeRepository.deleteById(id);
        return "redirect:/";
    }

    // 게시글 수정 화면 요청
    @GetMapping("/board/{id}/update-form")
    public String update(@PathVariable(name = "id")Integer id, HttpServletRequest request) {
        Board board = boardNativeRepository.findById(id);
        request.setAttribute("board", board);
        // src/main/resource/templates/board/update-form.mustache
        return "board/update-form";
    }

    // 게시글 수정 요청 기능
    // board/{id}/update

    @PostMapping("/board/{id}/update")
    public String update(@PathVariable(name = "id")Integer id, @RequestParam(name = "title") String title, @RequestParam(name = "content") String content) {
        // 업데이트 기능이 완료되면 디테일로 자기자신
        boardNativeRepository.updateById(id, title, content);
        return "redirect:/board/detail" + id;
    }


}

 

login-form.mustache  value값 추가

더보기
{{> layout/header}}


<main class="container p-5 content">
    <div class="card">
        <div class="card-header"> <b>{{name}}</b> </div>
        <div class="card-body">
            <form action="/login" method="post" enctype="application/x-www-form-urlencoded">
                <div class="mb-3">
                    <input type="text" class="form-control" placeholder="enter your name" name="username" value="길동">
                </div>

                <div class="mb-3">
                    <input type="password" class="form-control" placeholder="enter your password" name="password" value="1234">
                </div>

                <button type="submit" class="btn btn-primary form-control">로그인</button>

            </form>
        </div>

    </div>
</main>

{{> layout/footer}}

 

save-form.mustache  value값 추가

더보기
{{> layout/header}} {{!    Partial 태그 (부분 템플릿 태그) }}

<main class="container p-5 content">
    <article>
        <div class="card">
            <div class="card-header"><b>글쓰기 화면입니다</b></div>
            <div class="card-body">
                <form action="/board/save" method="post">
                    <div class="mb-3">
                        <input type="text" class="form-control" placeholder="Enter title" name="title" value="샘플 제목1">
                    </div>
                    <div class="mb-3">
                        <textarea class="form-control" rows="5" name="content">샘플 컨텐트1</textarea>
                    </div>
                    <button class="btn btn-primary form-control">글쓰기완료</button>
                </form>
            </div>
        </div>
    </article>
</main>

{{> layout/footer}}

 

JPA API를 만들어 보자

 

BoardRepository.java

더보기
/**
     * 나중에 JPA API를 만들어 보자
     * 게시글 삭제
     */
    @Transactional
    public void deleteById1(int id) {
       Board board = em.find(Board.class, id);

        if(board != null) {
            em.remove(board);
        }
    }

 

BoardController.java

더보기
/** JPA API를 통해서 만들어보자?
     * 주소 테스트 http://localhost:8080/board/1/delete1
     * @param id
     * @return
     */
    @PostMapping("/board/{id}/delete1")
    public String deleteTest(@PathVariable(name = "id") Integer id) {
        User sessionUser = (User)session.getAttribute("sessionUser");

        // 회원정보 없으면 로그인 페이지 이동
        if(sessionUser == null) {
            return "redirect:/login-form";
        }