JPA

[JPA] JPA란 ? (1)

jongh0 2025. 8. 20. 14:02

 

JPA (JAVA Persistence API)

 

 자바에서 객체를 데이터베이스에 저장하고 관리하기 위한 인터페이스와 기능을 제공하는 API

JPA를 사용하면 객체와 관계형 데이터베이스 간의 매핑을 손쉽게 처리할 수 있으며

데이터베이스의 CRUD 작업을 간편하게 수행이 가능해짐

 

여기서 ORM(Object-Relational Mapping)이라는 용어와 연관성이 있는데,

우리가 일반적으로 알고있는 어플리케이션 Class 와 RDB(Relational DataBase)의 테이블을 매핑(연결)한다는 뜻으로 기술적으로 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화(데이터가 계속 유지되도록) 해주는 것이다.

 

 

# JPA 장점

- 생산성 (쿼리 X, SQL 자동)

- 유지보수 (엔티티 변경 유연)

- 패러다임 불일치 해결 (상속, 연관관계, 객체 그래프, 동등비교)

- 성능 (캐싱, 지연로딩)

- DB 독립

 

- 엔티티를 영속성컨테이너에 저장할 때, 최초 상태를 복사해서 저장하는것을 스냅샷 이라고한다.

 

 

[회원가입/로그인 JPA 실습해보기]

 

JPA의 구조는 다음과 같다.

 

1. MemberEntity

2. MemberDto

3. MemberApiController 

4. MemberService

5. MemberRepository

 


 

1. MemberEntity

 

해당 코드에서 일단 @Entity 어노테이션을 통해서 JPA의 컨텍스트에 담기는 요소인 Entity 클래스임을 명시해주었다.

그리고 @Table (name="Member") 어노테이션을 통해서 해당 Entity 클래스가 어떤 테이블과 매핑될지 테이블 이름을 지정한다.

@SequenceGenerator 어노테이션을 통해서 시퀀스생성자 명, 시퀀스 명, 시퀀스 증가량에 대해서 설정하였다.

 

그리고 테이블의 컬럼으로 no, id, pwd, nick, 생성일, 소프트삭제여부에 대해서 정의하였고, 생성자를 통해서 객체가 생성될 때, 생성일과 소프트삭제 여부의 기본값이 설정되도록 정의하였다.

no 필드에는 어노테이션으로 @Id@GenerateValue 어노테이션이 달려있는데 이것은 기본키 값을 어떤 방식으로 생성할지 전략을 지정하는 어노테이션 (시퀀스를 사용하도록 설정)

 

 

 

1-1. application.properties 설정

application.properties

 JPA의 편리함에 있어서 직접 테이블을 ORACLE로 생성하지 않아도 된다는 것이다.

그냥 Entity만 만들어서 build 한다고 생성되는 것은 아니고, 엔티티 클래스(@Entity로 선언된 클래스)를 기반으로 실제 DB 테이블을 생성하는 기능은 application.properties에서 따로 코드를 적어주어야 한다. 주석처리되지 않은 코드중에서 spring.jpa.hibernate.ddl-auto = create-drop 해당 코드는 어플리케이션 구동 시, 테이블이 실행되고, 어플리케이션을 종료했을 때, 테이블을 삭제한다.

 

 

 

 

2-1. ReqDto

ReqDto

 기존에는 한가지의 vo라는 객체로 userId, userPwd, userNick 에 대해서 요청/응답 데이터를 다루는 구조를 사용했는데, 이제는 다르다. 물론 Entity객체로 사용해버리면 vo처럼 사용이 가능하지만, Entity는 그저 JPA에 사용되는 컨텍스트에 사용되는 요소일 뿐 요청과 응답 데이터를 다루는데 사용되는것은 부적합하다. 그리고 요청과 응답에 대해서 굳이 옮기지 않아도 되는 데이터가 존재할 수 있다. 

 

로그인 기능을 예시로 들자면, 처음에는 아이디와 패스워드만 입력받으면 되므로, 두가지 필드만 있으면 된다.

 

2-2. RespDto

 

RespDto

 

응답으로는 패스워드를 줄 필요 없이 필요한 닉네임요소만 응답하면 된다. 

이런식으로 Dto(Data tranfer object) 를 통해서 원하는 데이터만 받아오고 응답하면 된다.

 

3. MemberApiController

 


@RestController 어노테이션을 통해서 @Controller + @ResponseBody 기능을 챙기고,

@RequestMapping 어노테이션을 통해서 모든 메서드에 공통 URL 경로 (prefix) 를 부여

@RequiredArgsConstructor 어노테이션을 통해서 final 키워드가 붙어있는 필드를 대상으로 생성자를 자동 생성(의존성 주입)

 

 여기서 일단 로그인 메서드만 확인해보자. 로그인할 때, 사용자가 입력한 userId, userPwd 2가지 데이터에 대해서 fetch 함수를 통해서 요청을 보내고 reqDto파라미터로 받아왔다. 그리고 Service클래스의 login 메서드에 파라미터로 reqDto 객체를 넘겨준다.

 

 

4. MemberService

 

Service 어노테이션

 

Repository 클래스와의 의존성 주입을 위해서 @RequiredArgsContructor 어노테이션을 사용하였고,

@Transactional 어노테이션을 통해서 Service의 모든 메서드에 대해서 함수가 제대로 종료될 때, 커밋이 되도록 설정하였다.

 

MemberService

 

 유저가 입력한 userId, userPwd 데이터가 들어있는 reqDto에 대해서 Repository에 login(reqDto) 을 통해서 반환 값을 Entity로 받았다. 일단 Repository 클래스의 login이 어떤 메서드이고 어떤 entity를 반환했는지 확인해보자

 

 

MemberRepository

 

일단 Repository에서는 @RequiredArgsConstructor 어노테이션을 통해서 em 객체에 대해서 EntityManger 의존성 주입되었고, 해당 매니저를 통해서 login 메서드에서 JPA 컨텍스트나 DB와의 작업을 수행할 수 있다.

 

 login 메서드를 확인해보자, EntityManager를 사용하기 위해서는 Entity 객체 데이터를 사용한다.

reqDto에 담긴 userId, userPwd를 inputId, inputPwd 로 선언하고, EntityManager클래스의 createQuery() 메서드에 파라미터로jpql문MemberEntity.class 을 넣어주면서 실행하면, 쿼리문을 실행할 수 있다.

 

 쿼리문은 기존에 ORACLE 쿼리문과 사뭇 다른데, 테이블이 적히는 FROM 뒤에는 DB와 매핑된 MemberEntity을 기입하고 별칭을 달아주어야한다 (m) 그리고 SELECT에 전체 컬럼을 불러오는 * 대신 m을 적어서 해당 엔티티 객체 전체를 반환한다 . (*은 사용

불가능)

 

 

WHERE 절 뒤에는 별칭.필드명 = :inputId 구조로 콜론+변수를 설정해 줄 수 있다.

(+ 추가로 position parameter 구조로 ?1 ?2 이런식으로 쿼리문에 설정해놓고 setParameter() 메서드로 순차적으로 채워 줄 수 있음)

 

그냥 파라미터 값이 기입되어서 쿼리문이 실행되는것은 아니고, setParameter(1, 변수명) 메서드를 통해서 쿼리문의 값을 채워줄 수 있다. 그리고 getSingleResult() 메서드는 createQuery() 메서드에 대해서 반환 값이 TypedQuery 데이터형태로 반환되는데, 쓰기쉬운 단하나의 Entity 객체로 변환시키는 메서드가 getSingleResult() 이다. 만약 SELECT 쿼리문에대한 결과가 2개 이상이라면, getResultList() 메서드를 사용하면 된다.

그러면 반환 데이터형이 List<Entity>로 반환된다.

 

그러면 userId와 userPwd가 입력한 데이터와 동일했다면 적합한 Entity 형태의 rows 데이터가 한개 반환되었을 것이다.

 

다시 Service 클래스로 돌아와서...

 

MemberService

 

반환받은 entity를 변수에 할당하고, new RespDto(); 를 통해서 응답Dto로 만들어둔 respDto 객체를 생성하였다. 

Entity는 EntityManager와의 상호작용을 하며 컨텍스트를 다루기 위한 객체이며,  영속성 관리를 위한 객체이다.

그래서 직접 Entity에 대해서 반환하지 않고 userNick만 반환하면 되므로 RespDto 객체를 생성한 것이다.

 

그리고 생성한 respDto에 setter를 통해서 entity 객체로부터 userNick만 getter메서드로 받아오고 respDto를 반환한다.

 

그리고 MemberApiController에서  반환받은 respDto를 다시 브라우저 단으로 응답하는 구조로 JPA JDBC 구조가 완성된다.