개발/🖤 UMC 백엔드 과정 🖤

🖤 UMC 서버 7주차 스터디 - [ Springboot ] JPA를 통한 엔티티 설계, 매핑 & 프로젝트 파일 구조 이해 🖤

정소은 2023. 11. 22. 09:24

 

 

 

1.  Springboot 초기 설정

 

플러그인 설치

 

Atom Material Icons 설치

 

 

패키지 설정

 

 

domain

JPA에서 사용하기 위한 엔티티 클래스를 저장하기 위한 패키지

 

controller

http 요청에 대한 응답을 주는 클래스의 모임

응답을 주기 위한 과정들을 service에서 처리하도록 한다

 

service

비즈니스 로직이 필요한 클래스들의 모임

contoller에서 service의 메소드를 호출

service는 repository의 메소드를 호출

 

repository

database와 통신을 하는 계층

Spring Data JPA를 이용해서 만든 repository를 이용

 

dto

클라이언트가 body에 담아서 보내는 데이터를 받기 위한 클래스

Database에서 받아온 데이터를 클라이언트에게 보여주기 위한 클래스

 

Database에서 받아온 데이터를 dto를 한번 거쳐서 클라이언트에게 보여주는 이유는

Database에서 받아온 데이터를 그대로 응답으로 주게 될 경우

Database의 변경 사항이 생겼을 때 당연히 엔티티에도 변경이 생기게 되고

엔티티의 변경이 프론트엔트 개발자에게까지 영향을 주기 때문이다

 

converter

데이터 형식 간의 변환을 수행하는 역할

* entity -> dto 

* entity의 생성

   service에서 entity를 생성하도록 할 수도 있지만

   converter에서 entity의 생성을 맡게 함으로써

   service가 비즈니스 로직에만 집중할 수 있도록 하는 것이 단일 책임 원칙 측면에서 좋다

 

converter의 사용 위치

- service에서 dto 생성

   service에서 converter를 통해 dto를 controller에게 리턴해주는 것

   * controller에 엔티티가 노출X -> 보안적인 측면에서 유리

- controller에서 dto 생성

   service에서 entity를 리턴하고

   controller에서 converter를 통해 dto를 만들어서 응답을 주는 것

   * service의 함수가 범용성이 커짐 -> 유지보수 측면에서 유리

 

 

 

2.   Entity 매핑

 

Entity : 데이터베이스 테이블

 

DB 준비

로컬 DB 혹은 RDS에 DB 생성 -> 나는 RDS를 잘 관리할 자신이 없기 때문에... 로컬 DB를 선택했다

 

mysql install

$brew install mysql

 

mysql 기본 설정

$mysql.server start

비밀번호를 생성하고 환경 설정을 진행했다

 

mysql 접속

$mysql -u root -p

 

Database 생성

CREATE DATABASE [DB명]
show databases;

 

application.yml 설정

 application.yml - 외부에서 환경 변수 삽입

로컬 DB 연결

spring:
  datasource:
    url: jdbc:mysql://localhost:3306
    username: root
    password: number5598
    driver-class-name: com.mysql.cj.jdbc.Driver
  sql:
    init:
      mode: never
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        show_sql: true
        format_sql: true
        use_sql_comments: true
        hbm2ddl:
          auto: create
        default_batch_fetch_size: 1000

 

entity 생성 - domain 패키지

 

아래의 ERD를 기준으로 entity 매핑을 진행한다

 

enum 설정

정해진 값들 중에 특정 값이 저장되는 요소들 ( Gender, MemberStatus 등등 )의 경우

enum을 사용하는 것이 좋다

 

Created_at, Updated_at -> base 패키지 생성

Created_at이나 Updated_at의 경우, 모든 엔티티에 포함되는 요소이기 때문에

따로 패키지를 만들어 abstract Class로 생성해놓는 것이 좋다

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseEntity {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

 

member

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor

public class Member extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberId;

    private String name;

    private String address;

    private String specAddress;

    @Enumerated(EnumType.STRING)
    private Gender gender;
    
    @Enumerated(EnumType.STRING)
    private SocialType socialType;
    
    @Enumerated(EnumType.STRING)
    private MemberStatus status;

    private LocalDate inactiveDate;

    private String email;

    private Integer point;
}

 

food_category

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class foodCategory extends BaseEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long foodCategoryId;

    private String name;
}

 

terms

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Terms extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long termsId;

    private String title;

    private String body;

    private Boolean optional;
}

 

review

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Review extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long reviewId;

    private String title;

    private Float score;
}

 

region

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Region extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long regionId;

    private String name;
}

 

store

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Store extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long storeId;

    private String name;

    private String address;

    private Float score;
}

 

mission

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Mission extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long missionId;

    private Integer reward;

    private LocalDate deadline;

    private String missionSpec;
}

 

 

매핑 테이블

MemberAgree

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberAgree extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberAgreeId;
}

 

MemberMission

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberMission extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberMissionId;

    @Enumerated(EnumType.STRING)
    private MissionStatus status;
}

 

 

MemberPrefer

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberPrefer extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberPreferId;
}

 

 

연관관계 매핑

연관 관계 주인 : 실제 데이터베이스에서 외래키를 가지는 엔티티

 

단방향 연관관계

연관 관계 주인에게만 연관 관계 주입

1:N 관계의 경우 N에 해당하는 테이블 외래키를 가지며 연관 관계 주인에 해당

1:1 관계의 경우 둘 중 하나, 원하는 엔티티를 연관 관계 주인으로 설정

 

예시 ) 

 

member와 member_prefer, member와 review 가 1 : N 관계를 맺고 있다

여기서 member_prefer와 review가 연관 관계 주인에 해당한다

 

MemberPrefer

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberPrefer extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "memberId")
    private Member member;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "foodCategoryId")
    private FoodCategory foodCategory;

}

- 연관 관계 주입 : @ManyToOne 어노테이션 사용

   fetch = FetchType.LAZY 는 지연 로딩을 의미한다

- 외래키의 이름 설정 : @JoinColumn 어노테이션 사용

   @JoinColumn에서 'name=' 뒤에는 연관 관계 설정할 entity의 PK 이름을 넣는다

 

 

양방향 연관관계

연관 관계 주인과 연관 관계 주인이 아닌 엔티티에게 모두 연관 관계 주입

1:N 관계에서 1에 해당하는 엔티티에게 설정

N에 해당하는 엔티티가 삭제되어도 1에 해당하는 엔티티를 보존하기 위함

 

예시 )

 

Term

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Terms extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long termsId;

    private String title;

    private String body;

    private Boolean optional;

    @OneToMany(mappedBy = "terms", cascade = CascadeType.ALL)
    private List<MemberAgree> memberAgreeList = new ArrayList<>();
}

- 연관 관계 주입 : @OneToMany 어노테이션 이용

   mappedBy를 통해 N에 해당하며 @ManyToOne 이 설정된 멤버변수를 연결한다

   cascade = CascadeType.ALL 은 Member의 변화에 따라 Review, MemberPrefer 등의 엔티티가 영향을 받는다는 것을 의미한다

 

 

칼럼별 세부 설정

칼럼 별로 유니크, 디폴트 값, null 이 가능한지 등 세부적인 설정을 해줄 수 있다

@Column(세부 설정 내용)