https://github.com/MungDon/board.git
https://github.com/MungDon/board-jpa
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long boardSid; // 게시글 시퀀스
이번에 Entity를 작성하면서 테이블 PK를 IDENTITY로 지정하였다 이는 단순하고 쉽게 설정 할 수 있어 쉽기는 했지만 단점이있다 한번 알아보자
IDENTITY 는 먼저 Entity를 DB에 저장한 후, 식별자를 조회해 Entity의 식별자로 할당한다
SEQUENCE 는 em.persist() 호출 전에 먼저 DB Sequence 를 먼저 조회하고 그 조회한 식별자를 Entity 영속 상태로 저장한다.
그 후 Transaction을 commit 하여 Flush 가 발생할 때 해당 Entity 를 DB에 저장한다.
기본적으로 영속성 컨텍스트에 엔티티가 영속되기 위해서는 ID 가 필요하다
그런데 insert 될 데이터는 아직 ID 값이 존재하지 않는다
이런상황에서 IDENTITY 는 우선 엔티티의 내용을 repository 의 save() 메서드 실행 시점에 바로 DB로 insert를 날리고
그 할당된 ID 값을 애플리케이션의 영속성 컨텍스트 안에 있는 엔티티에 ID 값을 할당한다.
"즉", @Transactional 로 인해 트랜잭션이 만들어졌음에도 flush가 트랜잭션 종료 시점이 아니라 save() 메소드 호출 할때이다.
SEQUENCE는 엔티티를 영속성 컨텍스트에 영속 시키전에 DB에게 이번에 데이터가 insert 될 경우 할당 될 ID 값이 무엇인지 호출한다.
해당 값을 리턴 받고 그 ID 값을 엔티티에 할당 한 후 영 영속성 컨텍스트에 엔티티가 영속되기 위해서는 ID 가 필요하다
그런데 insert 될 데이터는 아직 ID 값이 존재하지 않는다
이런상황에서 IDENTITY 는 우선 엔티티의 내용을 repository 의 save() 메서드 실행 시점에 바로 DB로 insert를 날리고
그 할당된 ID 값을 애플리케이션의 영속성 컨텍스트 안에 있는 엔티티에 ID 값을 할당한다.
"즉", @Transactional 로 인해 트랜잭션이 만들어졌음에도 flush가 트랜잭션 종료 시점이 아니라 save() 메소드 호출 할때이다.
SEQUENCE는 엔티티를 영속성 컨텍스트에 영속 시키전에 DB에게 이번에 데이터가 insert 될 경우 할당 될 ID 값이 무엇인지 호출한다.
해당 값을 리턴 받고 그 ID 값을 엔티티에 할당 한 후 영
진행하면서 어려웠던 점 및 새롭게 배우게 된 점
이번에 게시판 진행하면서 제일 까다로웠던 부분은 역시 스프링 시큐리티였다.
시큐리티를 사용하면서 Delete , Put 요청을 진행하였는데 계속하여 403 에러가 떴다 이유를 찾아보니 스프링 시큐리티는 delete 요청에 csrf 토큰을 검증하게 되어있단 것을 알았다.
ajax 요청시에는 <head>부분에
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
이런식으로 추가해서 ajax 요청과 함께 넘겨주면되고
Form 으로 넘길시에는
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
이런식으로 넘겨주면 된다는 것을 알게되었다 실제 넘겨보니 403 에러는 사라지고 정상적으로 작동되었다
※ 또 한가지 방법이 있긴한데
.csrf((csrf)->csrf
.ignoringRequestMatchers(new AntPathRequestMatcher
("/board/**")))
이런식으로 board 경로로 오는 모든 요청을 시큐리티가 csrf 토큰 검증을 안하게 된다
하지만 CSRF 공격을 막지 못한다는 아주 나이스한 문제점이 생겨버린다 검증 기능을 끄는것은 신중하게 필요한 곳에만 해야겠다고 쓰면서 느꼈다.
JPA 게시판을 만들면서 mybatis와 다르다고 느낀점은 @Setter 를 지양하여야하기때문에 (어떤 기능의 set 인지 불확실하고 public set메서드로 생성되기 때문에 데이터의 신뢰도가 떨어지고 값이 변할수 있다) update 나 다른 값 변경 메서드를 만들어서 쓰거나 생성자, Builder 패턴으로 주로 데이터를 set해주었다 많이 번거롭긴했지만 그래도 이런식으로 코드를 작성하여 데이터의 신뢰도를 높일 수 있고 오히려 가독성이 많이 좋아졌다.
package com.example.ex.entity;
import java.time.LocalDateTime;
import com.example.ex.dto.request.ReqBoardModify;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long boardSid; // 게시글 시퀀스
private String title; // 게시글 제목
private String content; // 게시글 내용
@ManyToOne
private Member member; // 작성자
private int hits; // 조회수
private String deleteYn; // 삯제여부
private LocalDateTime createdDate = LocalDateTime.now(); // 작성 일시
private LocalDateTime modifiedDate; // 수정 일시
@Builder// 빌더패턴으로 정보저장
public Board(String title, String content, Member member,String deleteYn) {
this.title = title;
this.content = content;
this.member = member;
this.deleteYn = deleteYn;
}
/*게시글 수정*/
public void update(ReqBoardModify req) {
this.title = req.getTitle();
this.content = req.getContent();
this.modifiedDate = LocalDateTime.now();
}
/*조회수 증가*/
public void increaseHits() {
this.hits++;
}
/*게시글 삭제(논리)*/
public void delete() {
this.deleteYn = "Y";
}
}