[멋쟁이사자처럼 12기] 백엔드 세션

[멋쟁이사자처럼 12기] 백엔드 세션 _ 4주차 JPA

희원킴 2024. 4. 10. 14:09

1. JPA 이란? 

: ORM(Object-Relational Mapping)은 객체와 관계형 데이터베이스 간의 데이터를 변환하는 프로그래밍 기법

객체 지향 프로그래밍 언어에서 사용되는 객체와 관계형 데이터베이스의 테이블 간의 불일치를 해결하기 위한 기술로,    객체와 테이블 간의 매핑을 통해 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스를 조작할 수 있게 합니다.

 

1-1. JPA의 장점과 단점 

 

장점

1) 특정 테이터베이스에 종속되지 않음.  데이터베이스를 개발 도중 변경한다면, 데이터베이스 마다 쿼리문이 다르기   
때문에 변경하기 어렵습니다. 그러나 JPA는 
추상화한 데이터 접근 계층을 제공하므로 설정 파일에 어떤 데이베이스를 사용할 지만 명시해주면 얼마든지 변경할 수 있습니다.
2) 객체지향적 프로그래밍 JPA를 사용하면 데이터베이스 설계 중심의 패러다임에서 객체지향적으로 설계가 가능합니다. 이를 통해 개발자는 비즈니스 로직에 집중할 수 있게 됩니다. 
3) 생산성 향상 데이터베이스 테이블에 새로운 칼럼이 추가될 경우, 해당 테이블의 칼럼을 사용하는 DTO 클래스의 필드도 모두 변경해야 합니다. 하지만 JPA 에서는 테이블과 매핑된 엔티티 클래스에 필드만 추가한다면 쉽게 관리할 수 있습니다. 또한 SQL문을 직접 작성하지 않고 객체를 사용하여 동작하므로 유지보수와 재사용 측면에서 매우 편리합니다. 

 

단점

1) 복잡한 쿼리 처리 JPA에서는 Native SQL을 통해 기존의 SQL문을 사용할 수는 있지만, 특정 데이터베이스에 종속된다는 단점이 발생합니다.
2) 성능 저하 위험 객체 간의 매핑 설계를 잘못했을 경우 성능 저하가 발생할 수 있으며, 자동으로 생성되는 쿼리가 많기 때문에 개발자가 의도하지 않았던 쿼리로 인해 성능이 저하되는 경우 존재합니다. 
3) 학습 비용 JPA를 사용하기 위해서는 배워야 할 부분이 많기에 비용의 금액이 높은 편에 해당합니다. 

 

1-2. JPA의 동작 방식

 

출처 : 서울여자대학교 멋쟁이사자처럼 12기 교육자료

 

2. 영속성 관리

 

2-1. 영속성 컨텍스트란? 

 : 엔티티를 영구 저장하는 환경

 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 합니다. 엔티티 매니저를  통   해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리합니다. 

-> 이 환경은 데이터 베이스와의 상호작용에서 엔티티 객체의 상태 변화를 관리하며 JPA의 핵심적인 기능 중 하나이다. 

 

2-2. 엔티티 생명주기

*엔티티란? : 데이터베이스에서 관리되는 데이터의 논리적인 단위 

 

4가지 상태

비영속(new/transient) 영속성 컨텍스트와 전혀 관계가 없는 상태
영속(managed) 영속성 컨텍스트에 저장된 상태
준영속(detached) 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제(removed) 삭제된 상태

 

생명주기

출처 : 서울여자대학교 멋쟁이사자처럼 12기 교육자료

 

Transient  (일시적) 엔티티 객체가 생성된 직후 , new 키워드를 사용하여 객체를 생성했을 때 해당됩니다. 
이 상태에서는 영속성 컨텍스트와는 아무런 연관이 없으며, 데이터베이스에도 저장되지 않습니다.
따라서 영속성 컨텍스트에서 관리되지 않고, 해당 엔티티 객체는 단순히 메모리에만 존재합니다.
Managed (관리됨) 엔티티 매니저를 사용하여 엔티티를 영속성 컨텍스트에 등록한 후에 해당됩니다. 이 상태에서는 영속성 컨텍스트가 해당 엔티티를 관리하며, 엔티티의 상태 변화를 추적합니다. 엔티티 객체의 필드를 수정하거나 엔티티 매니저를 통해 새로운 엔티티를 조회한 경우에도 해당됩니다.
Detached (분리됨) 관리되던 엔티티 객체가 영속성 컨텍스트와의 연관이 끊어진 상태입니다.
주로 영속성 컨텍스트에서 해당 엔티티를 제거한 후에 발생하거나, 트랜잭션이 종료된 후에 발생합니다. 이 상태에서는 영속성 컨텍스트가 더는 해당 엔티티를 추적하지 않으며, 엔티티의 상태 변화를 관리할 수 없습니다. 하지만 분리된 엔티티 객체는 여전히 메모리에 존재하며, 필요에 따라 다시 관리될 수 있습니다.
Removed (제거됨) 엔티티 매니저를 통해 삭제된 엔티티 객체의 상태입니다.
이 상태에서는 해당 엔티티를 데이터베이스에서 삭제해야 한다는 것을 의미합니다.
주로 데이터베이스에 대한 커밋 작업이 수행될 때 발생하며, 데이터베이스에서 엔티티가 삭제된 후에는 해당 엔티티 객체는 메모리에서도 제거됩니다.

 

+ 4가지 상태 

1) 비영속 순수한 객체 상태이며, 아직 저장하지 않은 상태이기에 영속성 컨텍스트나 데이터베이스와 상관 X
2) 영속 엔티티 매니저를 통해 엔티티를 영속성 컨텍스트에 저장
영속성 컨텍스트가 관리하는 엔티티를 *영속 상태
3) 준영속 영속성 컨텍스트가 관리하던 영속 상태의 엔티티를      영속성 컨텍스트가 관리하지 않으면 "준영속 상태"    
4) 삭제  엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제 

*영속 상태 : 영속성 컨텍스트에 의해 관리된다는 것을 의미합니다. 

 

2-3. 영속성 컨텍스트 사용 시의 장점

 

1) 1차 캐시

EntityManger가 조회(find)를 요청하면, 1차 캐시에 원하는 엔티티가 없는 경우 DB에 SELECT문을 실행하여 조회합니다. 그리고 결과를 1차 캐시에 저장하고 엔티티 객체를 생성하여 반환(return)한다. 이후 단일 트랜잭션에서 같은 엔티티를  조회할 경우 1차캐시에서 바로 엔티티 조회가 가능합니다. 

 

2) 동일성 보장

1차 캐시는 플러쉬(flush)가 수행되지 않는 이상 DB와 동기화 되지 않는다. 그러므로 1차 캐시에 존재하는 엔티티는 언제나 늘 동일한 엔티티이다. 이것의 장점은 트랜잭션 격리 수준을 REPEATABLE READ 등급으로 유지함에 있습니다.

REPEATABLE READ 등급은 중간에 다른 트랜잭션이 데이터를 변경하고 커밋하여도 백업영역에 저장된 커밋전 데이터를 읽어들여 데이터 일관성을 유지합니다. 영속성 컨텍스트는 처음 조회(SELECT) 했을때 1차캐시에 저장되었던 값을 조회합니다.        flush로 DB와 동기화되지 않는 이상 데이터 일관성이 계속 유지됩니다.

 

3) 트랜잭션을 지원하는 쓰기 지연

영속성 컨텍스트는 쓰기지연 SQL 저장소를 가지게 됩니다. SQL문이 생성되어도 바로 실행되지 않고 저장됩니다. 마치 저장소는 버퍼와 같습니다. 버퍼가 존재하면 잦은 I/O 발생을 막을 수 있습니다. 그리고 JPA는 원하는 시점에 개발자가 SQL문을 실행할 수 있도록, 플러쉬(flush) 기능을 제공합니다.

 

** 쓰기 지연 SQL 저장소의 장점

1. DB 커넥션 시간을 줄일 수 있다.

2. 한 트랙잭션이 테이블에 접근하는 시간을 줄일 수 있다. 

 

4) 변경 감지 (Dirty Checking)

JPA에서 엔티티의 상태 변화를 감지하여 트랜잭션이 커밋될 때 데이터베이스에 변경을 반영하는 기능입니다.

이 기능을 통해 개발자는 엔티티의 상태를 직접 추적하고 관리하지 않아도 됩니다.

 

* 변경 감지의 작동 방식 

1) 엔티티 로딩 
2) 트랜잭션 내에서 변경 
3) 트랜잭션 커밋 전 변경 감지
4) 변경 감지 실행
5) 쓰기 지연(sql 저장소)에 변경 내역 저장
6) 트랜잭션 커밋 시쓰기 지연 저장소의 변경 내역 실행 

 

: 변경 감지는 다음과 같은 방식으로 작동되며, 이러한 변경 감지 기능을 통해 개발자는 엔티티의 변경 상태를 직접 추적하고 관리하지 않아도 되며 데이터베이스에 변경을 반영하는 과정을 자동화할 수 있습니다. 

 

2-4. flush 란?

: 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것

1) em.flush( ) - 직접호출

쿼리를 미리 데이터베이스에 반영하고 싶다면 커밋 전에 직접 호출할 수 있습니다.
 flush는 1차 캐시와 상관없이 쓰기 지연 SQL 저장소에 쌓인 쿼리나 변경 감지된 내용이 데이터베이스에 반영되는 것으로, flush를 하여도 1차 캐시는 유지됩니다.

2) 트랜잭션 커밋 - 자동호출

transaction.commit( ) 이 실행되면 자동으로 flush가 호출되어 변경사항이 데이터베이스에 반영됩니다.

3) JPQL 쿼리 실행 - 자동호출

만약 아직 커밋하지 않은 영속화 대상에 JPQL 쿼리를 날렸을 때 flush가 자동호출되지 않다면 데이터를 불러올 수 없으므로, JPQL 실행 시 자동호출되도록 한 것입니다. 

 

3. 프로젝트 시작 (실습 프로젝트 진행) 

 

3-1. 프로젝트 시작

[쇼핑몰 프로젝트] 
1. 상품 등록 및 조회
2. 장바구니
3. 주문하기
4. 회원가입 / 로그인

 

3-2. 프로젝트 생성

1) Spring Initializr 페이지 접속 (https://start.spring.io/)

2) 프로젝트 세팅 

3) 프로젝트 생성 후 다운로드 / 압축 풀기

4) IntelliJ -> open -> 다운받은 파일 선택

5) 프로젝트 생성 완료 

(* Spring Initializr를 통해 생성하였기 때문에 자동 구성 기능을 실행해 주는 @ SpringBootApplication 이 추가되어 있음!) 

6) 레포지토리 fork 받기 

 

4. 엔티티 설계 

 

4-1. 데이터베이스 생성 

1) 터미널 -> mysql 열기

2) 데이터베이스 생성 (이름은 자유) 

 

4-2. 데이터베이스 연결 

3) fork 받은 프로젝트 오픈 후 디렉토리 생성

4) application-properties 설정 파일 코드 작성

5) 정상 실행 확인! 

 

** DDL AUTO 옵션 이란?

: 엔티티 객체를 참고하여 애플리케이션 실행 시점에 hibernate에서 자동으로 DDL을 만들 때의 옵션

 DDL 이란 (Data Definitin Language)의 약자로 데이터베이스를 정의할 때 사용하는 언어를 말합니다. 

1) none : 아무것도 하지 않음 (실제 배포 
2) create : 기존 테이블 삭제 후 테이블 생성
3) create-drop : 기존 테이블 삭제 후 테이블 생성 + 종료 시 테이블 삭제
4) update : 변경된 스키마 적용하여 데이터 유지
5) validate : 엔티티와 테이블 정상 매핑 확인

 

4-3. 엔티티 구성 

( *엔티티(Entity)란 데이터베이스에서 관리되는 데이터의 논리적인 단위

[쇼핑몰 프로젝트]   
1. 상품 등록 및 조회  Item
2. 장바구니 Cart, CartItem
3. 주문하기 Order, OrderItem
4. 회원가입 / 로그인 Memer

 

4-4. 연관관계의 주인 

 

  객체와 테이블이 맺는 관계의 차이                                                                                                                                           테이블 : 외래 키 하나 두 테이블의 연관관계를 관리한다. 

    •  회원 <-> 팀의 경우 회원에서 팀, 팀에서 회원 조회가 모두 가능합니다.
    • 객체는 서로 다른 단방향 관계 2개를 통해 양방향 관계를 만들 수 있습니다.                                                                    (회원 -> 팀) + (회원 <- 팀) = (회원 <-> 팀)
    • 객체가 양방향 관계인 경우, 두 객체 중 하나로 외래 키를 관리해야 합니다.                                                                     -> 이때 정해진 객체가 연관관계의 주인이 되는 것 입니다.                                                                                                                                                                                                                                                               Q. 누구를 주인으로? 
    • 테이블에서 외래 키가 있는 객체를 주인으로 지정하면 됩니다.
    • 테이블에서 외래 키가 있는 곳이 N, 없는 곳이 1입니다.                                                                                            -> N 쪽 (@ManyToOne) / 1 쪽 (@OneToMany) 
    • 외래 키가 있는 객체와 연관관계의 주인이 같으면 같은 테이블에서 관리되므로,                                                    쿼리문을 날릴 때 직관적으로 확인할 수 있다는 장점이 존재합니다. 
    • 연관관계를 매핑할 때, 우선 단방향을 모두 설정하고 필요에 따라 양방향 매핑을 추가하는 방식을 권장합니다.     
  •  

4-5. 연관관계의 매핑 

Member       1 -----------------------  N Order
Member       1 -----------------------  1 Cart

 

1) Member : Order (1:N) 

@OneToMany (일대다 매핑) 

일대다 매핑은 한 엔티티가 다른 엔티티와 일대다 관계를 맺는 것을 의미하며,

다대일 매핑과 비슷하지만 다른 엔티티가 여러 개입니다.

 

2) Member : Cart (1:1)

@OneToOne(일대일 매핑) 

일대일 매핑은 한 엔티티가 다른 엔티티와 일대일 관계를 맺는 것을 의미하며,

대부분의 경우 외래 키를 이용하여 매핑됩니다.

 

4-6. 객체와 테이블 매핑 

Entity: 데이터베이스 테이블에 대응하는 클래스

1) @Entity : 해당 어노테이션이 붙은 클래스는 JPA가 관리하는 엔티티

2) @Table : 엔티티와 매핑할 테이블을 지정해 주는 어노테이션

3) @Getter, @Setter, @ToString : lombok 어노테이션 사용으로 getter, setter, toString 메서드 자동 생성 기능

 

4-7. 필드와 칼럼 매핑

1) @Id : 테이블의 기본 키로 사용할 속성 지정

2) @Column : 필드와 매핑할 컬럼명 지정

3) @GeneratedValue : 기본 키 매핑 어노테이션 (자동 생성)

 

4-8. Member Entity , Order Entity , Cart Entity 설계

[쇼핑몰 프로젝트]   
1. 상품 등록 및 조회  Item
2. 장바구니 Cart, CartItem
3. 주문하기 Order, OrderItem
4. 회원가입 / 로그인 Memer

 

 

1) Member Entity 설계

: 회원 관리를 위해 필요한 멤버 변수

1.  PK id(Long)
2. 이름  name(String)
3. 아이디 (이메일)   email(String)
- (type: boolean)인 unique 속성 부여 
4. 비밀번호 password(String)
5. 주소 address(String)
6. 역할 (관리자 / 일반)  role(enum)
7. 등록 시간  createdBy(LocalDateTime) 
8. 수정 시간  modifiedBy(LocalDateTime)

 

1) 객체와 테이블 매핑, PK 선언

2) enum 클래스 추가

3) 나머지 변수 추가 

 

2) Order Entity 설계

: 주문을 위해 필요한 멤버 변수

1. PK id(Long)
2. 주문 회원 Member 매핑
3. 주문 시간 OrderDate(LocalDateTime) 
4. 주문 상태 OrderStatus(enum)
5. 등록 시간 createdBy(LocalDateTime) 
6. 수정 시간  modifiedBy(LocalDateTime)

* Member 단방향 매핑

: @JoinColumn은 외래 키가 될 컬럼을 지정해준다. 

(name 속성은 Member 테이블의 컬럼 명이 아니라, Order 테이블의 외래 키 컬럼 명이다.)

 

3) Cart Entity 설계

: 장바구니를 위해 필요한 멤버 변수

1. PK id(Long)
2. 담은 회원  Member 매핑
3. 등록 시간 createdBy(LocalDateTime)
4. 수정 시간  modifiedBy(LocalDateTime)

 

 

4-9. 다대다 연관관계 

Item N ------------------- N Cart

 

: 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없습니다.

-> 그러므로 연결 테이블을 추가해서 일대다 / 다대일 관계로 풀어내야 합니다.

-> 테이블은 다대다 관계 구현이 불가능 하지만, 객체는 컬렉션을 통해 객체 2개로 다대다 관계 구현이 가능합니다. 

   (@ManyToMany, @JoinTable 어노테이션 사용하여서) 

 

1) 다대다 매핑의 한계

    주문 시간, 수량 같은 추가 데이터 항목을 연결 테이블에 넣을 수 없습니다.

    예상하지 못한 쿼리가 발생할 수 있다.

2) 다대다 매핑의 한계 극복

    연결 테이블용 엔티티를 추가하여 다대다를 일대다 / 다대다 매핑 형태로 변경합니다.

Item  N ---------------------- 1 CartItem   1 -------------------- N             Cart

 

4-10. Item Entity, CartItem Entity, OrderItem Entity 설계 

 

1)  Item Entity 설계

: 장바구니를 위해 필요한 멤버 변수

1. PK id(Long)
2. 상품명 itemName(String)
3. 가격 price(Integer)
4. 재고 stock(Integer)
5. 상품 설명 itemDetail(String)
6. 상품 상태  itemSellStatus (enum 클래스 생성,    판매중/품절)
7. 등록 시간  createdBy(LocalDateTime) 
8. 수정 시간  modifiedBy(LocalDateTime)

 

2) CartItem Entity 설계

: Item과 Cart 엔티티 다대다 매핑을 위한 CartItem Entity

1. PK id(Long)
2. 장바구니 Cart 매핑
3. 담은 상품 Item 매핑
4. 개수 count(Integer)
5. 등록 시간 createdBy(LocalDateTime) 
6. 수정 시간  modifiedBy(LocalDateTime)

 

3) OrderItem Entity 설계 

: Item과 Order 엔티티 다대다 매핑을 위한 CartItem Entity

 

1. PK id(Long)
2. 주문 Order 매핑
3. 주문한 상품 Item 매핑
4. 가격 orderPrice(Integer)
5. 주문 수량 count(Integer)
5. 등록 시간 createdBy(LocalDateTime) 
6. 수정 시간  modifiedBy(LocalDateTime)

 

5. 실습 

5-1. [회원 가입 기능 테스트]

 

*회원가입 기능 구현을 위해 필요한 요소들 

1. 회원가입 시 화면에서 넘어오는 정보를 받을 MemberFormDto 생성
2. MemberFormDto로부터 정보를 넘겨 받을 Member 엔티티 생성
3. Member 엔티티를 데이터베이스에 저장하기 위한 MemberRepository 생성
4. 회원가입 기능 테스트 

 

[회원 가입 기능 테스트 실습 과정]

1. MemberFormDto 설계

* Dto(Data Transfer Object) : 계층 간 데이터 교환을 위해 사용하는 객체

 

2. Member 엔티티 생성 메소드 만들기

: Member 엔티티 내부에 엔티티 생성 메소드 추가하기

 

3. MemberRepository 생성

1) entity 패키지와 같은 경로에 repository 패키지 생성

2) MemberRepository 인터페이스 생성 (우클릭->New->Java Class -> Interface 선택) 

3) JpaRepository 상속받기 (extends 사용)

* JpaRepository 인터페이스를 상속받는 인터페이스를 정의하면, 해당 인터페이스를 구현하는 클래스는 JPA에서 제공하는 메서드들을 사용할 수 있습니다. (데이터베이스의 추가, 조회, 수정, 삭제)

 

4. MemberRepositoryTest 

1) MemberRepositoryTest에 커서를 두고 (Ctrl+N / Cmd+N -> Test 생성)

2) MemberRepositoryTest  클래스에 어노테이션 추가 및 MemberRepositoryTest의 메소드 사용을 위해 의존성 주입받기

 

5. 회원가입 테스트 메소드 추가

1) @Test, @DisplayName 어노테이션 추가

2) 회원가입 테스트 로직 추가

 

6. 테스트 코드 실행하여 엔티티 생성 로그 확인!