본문으로 바로가기

[우테캠PRO 미션] 2주차 - JPA

category 정리/우테켐PRO 미션 2021. 7. 20. 00:12

2주차 - JPA

우테캠PRO 2기의 2주차 미션은 JPA!!
현업에서는 거의 xbatis나 JDBC를 사용해서 DB작업을 했었다,,, (요즘 대부분 JPA를 사용한다던데...) 그래서 혼자 JPA를 공부하고 싶어서 온라인 강의를 끊어서 들었었는데 잘 이해가 안갔었다,, Database를 객체지향으로 프로그래밍을 한다니..?? 근데 역시 우테캠.. 제이슨님의 명강의를 듣고 JPA는 정말 재밌었고 신기하기도 했던..
2주차는 진짜 금방 끝났고 수정부분도 그렇게 많지는 않았다.. 잘한건가?? ㅎㅎ,, 아무튼 조만간 인프런에서 김영한님의 JPA 강의를 결제할 예정!!
JPA의 강의내용 은 이미 정리를 해뒀으니 어떤 피드백을 받았는지 다시한번 복기하는 시간을 가져야겠다.

1단계 - 엔티티 매핑

1단계는 이미 구현되어 있는 DDL을 보고 @Entity 객체를 생성해 보는 것!
create table answer
(
    id          bigint generated by default as identity,
    contents    clob,
    created_at  timestamp not null,
    deleted     boolean   not null,
    question_id bigint,
    updated_at  timestamp,
    writer_id   bigint,
    primary key (id)
)
create table delete_history
(
    id            bigint generated by default as identity,
    content_id    bigint,
    content_type  varchar(255),
    create_date   timestamp,
    deleted_by_id bigint,
    primary key (id)
)
create table question
(
    id         bigint generated by default as identity,
    contents   clob,
    created_at timestamp    not null,
    deleted    boolean      not null,
    title      varchar(100) not null,
    updated_at timestamp,
    writer_id  bigint,
    primary key (id)
)
create table user
(
    id         bigint generated by default as identity,
    created_at timestamp   not null,
    email      varchar(50),
    name       varchar(20) not null,
    password   varchar(20) not null,
    updated_at timestamp,
    user_id    varchar(20) not null,
    primary key (id)
)

alter table user
    add constraint UK_a3imlf41l37utmxiquukk8ajc unique (user_id)
Entity 맵핑은 그렇게 어려운 작업이 아니어서 간단한 피드백 몇 개와 함께 바로 merge를 해주셨다, 리뷰어님은 JunilHwang 님!! 😀 엄청 친절하시고 답변도 잘 달아주셨던 리뷰어님! 😁
  • JPA도 객체지향이기 때문에 공통 컬럼은 추상화하고 상속받아서 사용할 수 있다, ( @Id 같은 key값은 추상화를 하지 않는 것을 추천!!)
  • TDD는 보통 1개의 domain에 1개의 test code를 작성할 것

2단계 - 연관 관계 매핑

2단계는 연관 관계를 매핑해주는 것이다.

이전 단계에서 엔티티 설계가 이상하다는 생각이 들었다면 객체 지향 설계를 의식하는 개발자고, 그렇지 않고 자연스러웠다면 데이터 중심의 개발자일 것이다. 객체 지향 설계는 각각의 객체가 맡은 역할과 책임이 있고 관련 있는 객체끼리 참조하도록 설계해야 한다.

Question question = findQuestionById(questionId);
List<Answer> answers = answerRepository.findByQuestionIdAndDeletedFalse(questionId);

위 방식은 객체 설계를 테이블 설계에 맞춘 방법이다. 특히 테이블의 외래 키를 객체에 그대로 가져온 부분이 문제다. 왜냐하면 관계형 데이터베이스는 연관된 객체를 찾을 때 외래 키를 사용해서 조인하면 되지만 객체에는 조인이라는 기능이 없다. 객체는 연관된 객체를 찾을 때 참조를 사용해야 한다.

Question question = findQuestionById(questionId);
List<Answer> answers = question.getAnswers();
이 부분도 정리를 했겠지만은 외래키로 연관 관계를 가지고 있는 객체들은 서로 참조를 하고 있기 때문에 위의 answerRepository를 통해 answer를 조회하는 것이 아닌 question에서 get메서드를 통해 answer를 조회하는 것!
각 연관관계는 @OneToOen, @OneToMany 등의 annotation으로 관계를 맺어준다.
  • 첫번째 피드백은 LAZY 와 EAGER 설정! 참조 데이터를 함께 select할 것인지 get으로 데이터를 조회할 때 select할 것인지! 결정

  • @Wehere annotation으로 조건절을 정의할 수 있다.


  • create date와 update date 를 해당 row가 저장되는 시점의 시간을 적용할 수 있는 annotation도 찾아서 적용시켰다.

3단계 - 3단계 - 질문 삭제하기 리팩터링

마지막 3단계는 질문을 삭제하는 것! 아래와 같은 요구사항을 정의해 구현하도록 한다.
* 질문 데이터를 완전히 삭제하는 것이 아니라 데이터의 상태를 삭제 상태(deleted - boolean type)로 변경한다.
* 로그인 사용자와 질문한 사람이 같은 경우 삭제할 수 있다.
* 답변이 없는 경우 삭제가 가능하다.
* 질문자와 답변 글의 모든 답변자 같은 경우 삭제가 가능하다.
* 질문을 삭제할 때 답변 또한 삭제해야 하며, 답변의 삭제 또한 삭제 상태(deleted)를 변경한다.
* 질문자와 답변자가 다른 경우 답변을 삭제할 수 없다.
* 질문과 답변 삭제 이력에 대한 정보를 DeleteHistory를 활용해 남긴다.
이 3단계를 프로덕션 코드를 구현하기 전에 @Mock 프레임워크를 사용하여 테스트 코드를 작성하고 테스트코드에 맞춰 프로덕션 코드를 구현했다. @Mock 은 처음 써봤는데 생각보다 재밌는 기능이고 편리한점도 있는 반면 불편한 점도 있었다. 다음 시간에 정리를 한 번 할 계획이다!
  • 3단계 pr에 앞서서 각 layer 계층들의 역할에 대해 질문을 드렸는데 잘 답변해주셨다 ㅎㅎ 감사합니다😀😀

  • 우태캠PRO를 진행하면서 많이 썻고 앞으로도 사용할 예정인 일급컬렉션!! 일급컬렉션에 대한 내용도 한 번 다뤄야겟다. @Embedded@Embeddable annotation으로 일급컬렉션으로 리펙토링을 할 수 있다.

일급 컬렉션을 마지막 피드백으로 2주차 - JPA가 마무리 되었다!! 수고수고~!

추가 피드백

  • 애플리케이션의 엔티티의 생성 시간과 마지막 수정 시간을 관리할 필요가 있다면 수동으로 매번 추가하는 대신 Auditing 기능을 이용하여 자동으로 추가해 줄 수 있다

    1. @Configuration 클래스에 @EnableJpaAuditing 을 추가한다.
    @EnableJpaAuditing
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    1. 엔티티에 콜백 리스너를 추가한다.
    @EntityListeners(AuditingEntityListener.class)
    @Entity
    public class Line {
    1. 성 날짜와 마지막 수정 날짜 프로퍼티에 @CreatedDate@LastModifiedDate를 추가한다.
     @EntityListeners(AuditingEntityListener.class)
     @Entity
     public class Line {
     @CreatedDate
     private LocalDateTime createdDate;
    
     @LastModifiedDate
     private LocalDateTime modifiedDate;
    
      }
  • @MappedSuperclass를 사용하여 중복 코드를 분리할 수 있다.

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

    ...

}

public class Line extends BaseEntity {

마치며

이 번 강의와 미션을 통해 영속화된 데이터와 해당 데이터들의 Transaction 들에 관해서 배울 수 있었다. 영속성 전이, 변경 감지 등의 신기하고 재밌는 내용들이 많이 있었다. JPA는 앞으로도 계속 사용할 수 도 있고 더 발전된 기술이 나올 수 도 있기 때문에 인프런에서 강의를 한번 더 들어야겠다. 책은 이미 구매완료 ㅎㅎ;;
화이팅