[Spring Boot] 게시판 구현 1 - Entity 및 Repository 만들기

joyful·2021년 4월 29일
15

Java/Spring

목록 보기
9/28
post-custom-banner

🔧 환경 설정

  • Spring Boot 2.4.3
  • OpenJDK 15
  • Gradle

※ 이 글에서는 Spring Boot 초기 설정 및 FrontEnd 관련 내용은 다루지 않습니다.


📝 Setting

여러가지 이유로 이 프로젝트에서는 Spring Data Jpa를 적용할 것이다. JPA 기능을 사용하기 위하여 먼저 build.gradleorg.springframework.boot:spring-boot-starter-data-jpacom.h2database:h2 의존성들을 등록한다.

build.gradle

dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	runtimeOnly 'com.h2database:h2'
}
  • spring-boot-starter-data-jpa
    • Spring Boot용 Spring Data Jpa 추상화 라이브러리
    • Spring Boot 버전에 맞춰 자동으로 JPA 관련 라이브러리들의 버전 관리
  • H2
    • 인메모리 관계형 데이터베이스
    • 별도의 설치 필요 없이 프로젝트 의존성만으로 관리 가능
    • 메모리에서 실행되므로 애플리케이션을 재시작할 때마다 초기화
      → 테스트 용으로 많이 사용

의존성을 등록했다면 비로소 JPA 기능을 사용할 수 있다.


근데.. 도메인이 뭐지? 🤔

도메인이란 게시글, 회원 등 소프트웨어에 대한 요구사항 혹은 문제 영역이라고 이해하면 된다.

기존에 MyBatis와 같은 쿼리 매퍼를 사용한 경험이 있다면 DAO 패키지를 떠올릴테지만, DAO와는 조금 결이 다르다. 도메인 클래스가 쿼리를 담는 xml과 쿼리의 결과를 담는 클래스의 역할을 모두 수행한다.


위의 사진과 같이 도메인 관리를 위하여 domain 패키지를 하나 생성하고, 내부에 기능별 패키지와 관련 클래스를 생성한다.
여기서는 간단히 게시글을 작성하는 주체인 Member와 게시글을 저장할 Board만 구현한다.

📝 Entity

아래의 클래스들은 실제 DB의 테이블과 매칭될 클래스들이며, 보통 Entity 클래스라고도 불린다. JPA를 사용할 경우, 바로 이 Entity 클래스의 수정을 통해 DB 데이터에 작업한다.

Member.java

@Getter
@NoArgsConstructor
@Entity
@Table(name = "member")
public class Member {
    //필드
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id", unique = true, nullable = false)
    private Long id;

    @Column(length = 15, nullable = false)
    private String name;

    @Column(length = 100, nullable = false)
    private String password;

    @Column(length = 50, nullable = false)
    private String email;

    @Column(length = 20, nullable = false)
    private String contact;

    @OneToMany(mappedBy = "member", cascade = CascadeType.MERGE, orphanRemoval = true)
    private List<Board> board = new ArrayList<>();

    //빌더
    @Builder
    public Member(String name, String password, String email, String contact) {
        this.name = name;
        this.password = password;
        this.email = email;
        this.contact = contact;
    }
}

Board.java

@Getter
@NoArgsConstructor
@Entity
@Table(name = "board")
public class Board {
    //필드
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "board_id")
    private Long id;

    @ManyToOne(cascade = CascadeType.MERGE, targetEntity = Member.class)
    @JoinColumn(name = "member_id", updatable = false)
    private Member member;

    @Column(nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT")
    private String content;

    //빌더
    @Builder
    public Item(Member member, String title, String content) {
        this.member = member;
        this.title = title;
        this.content = content;
    }
}

💻 코드 분석

  1. @Entity
    • 테이블과 링크될 클래스
    • 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍(_)으로 테이블 이름을 매칭
      ex) SalesManagersales_manager
  2. @Id
    • 해당 테이블의 PK 필드
  3. @GeneratedValue
    • PK의 생성 규칙 표시
    • Spring Boot 2.0부터는 auto_increment를 위해서 GenerationType.IDENTITY 옵션 추가 필수
  4. @Column
    • 테이블의 칼럼임을 표시
    • 필수 x, 기본값 외에 변경이 필요한 옵션이 존재할 경우 사용
  5. @ManyToOne@OneToMany
    • 관계 매핑을 위한 애노테이션
  6. @NoArgsConstructor
    • 기본 생성자 자동 추가
  7. @Getter
    • 클래스 내 모든 필드의 Getter 메소드 자동 생성
  8. @Builder
    • 해당 클래스의 빌더 패턴 클래스 생성
    • 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

📝 Repository

  • JPA에서의 DB Layer 접근자를 의미한다.
  • 인터페이스를 생성 후 JpaRepository<Entity 클래스, PK 타입>을 상속하면 기본적인 CRUD 메소드가 자동으로 생성된다.
  • @Repository를 추가할 필요가 없다.

MemberRepository.java

public interface MemberRepository extends JpaRepository<Member, Long>{

}

BoardRepository.java

public interface BoardRepository extends JpaRepository<Board, Long>{

}

🧨 주의사항

  1. Entity의 PK는 Long 타입의 Auto_increment를 사용하도록 하고, 주민등록번호 같은 비즈니스상의 유니크 키나 복합키 등은 다음과 같은 이유 때문에 유니크 키로 별도로 추가하는 것을 권장한다.

    ① FK를 맺을 때 다른 테이블에서 복합키 전부를 갖고 있거나 중간 테이블을 하나 더 둬야 하는 상황이 발생
    ② 인덱스에 좋지 않은 영향을 미침
    ③ 유니크한 조건 변경시 PK 전체 수정이 필요할 가능성 존재

  2. Entity 클래스에서는 Setter 메소드 대신 @Builder를 통해 제공되는 빌더 클래스를 사용하도록 한다.
    • getter/setter를 무작정 생성하는 경우, 해당 클래스의 인스턴스 값들이 언제 어디서 변경되는지 코드상으로 명확하게 구분할 수 없어 차후 기능 변경 시 불편하다.

    • 생성자 vs 빌더 클래스

      생성자빌더 클래스
      역할생성 시점에 값을 실음생성자와 동일
      특징코드 실행 전까지 값을 실을 필드를
      명확히 지정하는 것이 불가
      코드 실행 전에 값을 실을 필드를
      명확히 인지 가능

      • 기본적으로는 생성자를 통해 값을 DB에 삽입하고 값 변경은 해당 이벤트에 맞는 public 메소드를 호출하여 처리한다.
      • 생성자는 인자로 잘못된 값을 전달하는 등의 문제 발생의 여지가 존재하므로 빌더 클래스를 사용하는 것을 권장한다.

  3. Entity 클래스와 기본 Entity Repository는 밀접한 관계를 맺고 있으므로 함께 관리하도록 한다.

출처 : 스프링 부트와 AWS로 혼자 구현하는 웹 서비스(이동욱 저)

profile
기쁘게 코딩하고 싶은 백엔드 개발자
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 12일

오호 이해가 잘 되었습니다 감사합니다 ㅎ

답글 달기