1. jpa 가 무엇인가요 ?
JPA(Java Persistence/영속 API)는 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스입니다. 이는 자바의 ORM(Object Relational Mapping) 기술 표준으로, 객체와 테이블을 매핑함으로써 객체 지향적인 코드 작성을 가능하게 하고, SQL 작성 없이 데이터베이스 데이터를 다룰 수 있게 합니다.
JPA는 인터페이스로서 자체적으로는 아무런 기능도 없습니다. 실제 기능을 제공하는 것은 JPA를 구현한 ORM 프레임워크들이며, 대표적으로는 Hibernate, EclipseLink, OpenJPA 등이 있습니다. 스프링에서는 이런 JPA 구현체를 이용하여 데이터베이스를 쉽게 다룰 수 있도록 Spring Data JPA를 제공하고 있습니다. JPA는 다음과 같은 특징들을 가지고 있습니다:
- 객체 중심 개발: JPA를 사용하면 개발자는 SQL Query를 직접 다루지 않고, 객체를 통해 데이터를 처리할 수 있습니다. 이렇게 해서 객체 지향적인 설계를 보다 쉽게 할 수 있게 됩니다.
- 데이터베이스 독립성: JPA는 데이터베이스마다 다른 SQL 문법에 의존하지 않습니다. 즉, 데이터베이스가 바뀌더라도 JPA 코드를 변경할 필요가 없습니다.
- 자동 생성된 SQL: 개발자가 직접 SQL을 작성할 필요 없이, JPA가 자동으로 SQL을 생성하고 실행합니다.
- 자동 DDL 생성: 개발자가 직접 테이블을 생성할 필요 없이, 엔티티 클래스에 선언된 정보를 바탕으로 JPA가 테이블을 생성합니다.
JPA는 이러한 장점들 외에 코드의 가독성과 유지보수에도 도움을 주지만, 성능 최적화나 복잡한 쿼리 작성 등에서는 직접 SQL을 다루는 것에 비해 제약 사항이 있을 수 있습니다.
2. JPA는 왜 사용하나요? ORM에 대해 설명해보세요.
JPA(Java Persistence API)를 사용하는 주요 이유는 객체 지향 프로그래밍과 관계형 데이터베이스간의 패러다임 불일치를 해결하기 위함입니다. 객체 지향 프로그래밍은 상속, 다형성, 데이터와 기능의 캡슐화 등을 제공하지만, 관계형 데이터베이스는 이러한 개념들을 지원하지 않습니다. JPA와 같은 ORM(Object-Relational Mapping) 프레임워크를 사용하면 이러한 차이를 보다 쉽게 극복할 수 있습니다.
ORM은 객체와 관계형 데이터베이스의 데이터를 서로 매핑하는 기술입니다. 이는 객체 지향 프로그래밍 언어에서 사용하는 객체를 관계형 데이터베이스의 테이블에 매핑함으로써, 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 연산을 수행할 수 있게 해줍니다.
ORM과 JPA를 사용하면 다음과 같은 이점을 얻을 수 있습니다:
- 개발 생산성 향상: 개발자가 직접 SQL을 작성하지 않아도 되므로, 개발 과정이 단순화되고 개발 시간이 줄어듭니다. 이는 특히 CRUD(Create, Read, Update, Delete) 연산에서 큰 장점으로 작용합니다.
- 코드의 가독성 향상: SQL 쿼리 대신 객체 지향적인 코드를 작성할 수 있으므로, 코드의 가독성과 유지 관리성이 향상됩니다.
- 데이터베이스 독립성: JPA는 특정 데이터베이스에 종속적이지 않습니다. 즉, 데이터베이스가 변경되더라도 JPA를 사용하는 코드를 수정할 필요가 없습니다.
- 표준화: JPA는 자바의 ORM 기술에 대한 표준 스펙입니다. 이는 다양한 JPA 구현체 간에 일관된 API를 제공하므로, 구현체를 변경하더라도 코드의 변화를 최소화할 수 있습니다.
ORM이나 JPA를 사용하면서 주의해야할 점은, 성능 최적화나 복잡한 쿼리 작성 등에서는 SQL을 직접 다루는 것에 비해 제약 사항이 있을 수 있습니다. 따라서 이러한 특성을 잘 이해하고, 필요에 따라서는 JPA와 SQL을 적절히 혼용하는 것이 중요합니다.
2-1. ORM, JPA, Spring Data JPA 를 구분해서 설명해 보세요.
- ORM (Object-Relational Mapping): ORM은 객체 지향 프로그래밍 언어에서 사용하는 객체와 관계형 데이터베이스의 데이터를 서로 매핑하는 기술입니다. ORM은 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 연산을 수행할 수 있게 하여, 개발 생산성을 향상시키고 코드의 가독성을 높입니다.
- JPA (Java Persistence API): JPA는 자바 언어를 위한 ORM 기술 표준입니다. JPA는 인터페이스의 모음으로, 여러 ORM 프레임워크가 JPA 표준을 준수하도록 하여 개발자가 다양한 ORM 프레임워크를 쉽게 교체하거나 사용할 수 있게 해줍니다. Hibernate, EclipseLink, OpenJPA 등이 JPA를 구현한 대표적인 프레임워크입니다.
- Spring Data JPA: Spring Data JPA는 스프링 프레임워크에서 제공하는 모듈 중 하나로, JPA를 좀 더 편리하게 사용할 수 있게 해주는 기능을 제공합니다. Spring Data JPA를 사용하면, 개발자가 JPA를 직접적으로 사용하는 대신, 간단한 인터페이스 정의만으로 Repository를 구현할 수 있습니다. 이로 인해 개발 과정이 더욱 간단해지고, 코드의 복잡성이 줄어듭니다.
요약하면, ORM은 객체와 관계형 데이터베이스 사이의 매핑을 처리하는 기술입니다. JPA는 ORM을 자바 언어에서 사용할 수 있도록 표준화한 것이며, Hibernate 등이 이를 구현한 프레임워크입니다. Spring Data JPA는 JPA를 더 쉽게 사용할 수 있도록 스프링에서 제공하는 추상화된 레이어입니다.
3. JPA에서 객체와 테이블 간의 매핑(mapping)에 사용되는 어노테이션에 대해 설명해보세요.
- @Entity: 클래스가 엔티티임을 선언하고, 이 클래스의 인스턴스가 데이터베이스에 저장될 수 있음을 나타냅니다.
- @Table: 엔티티가 매핑될 데이터베이스 테이블을 지정합니다. @Table을 생략하면 클래스 이름이 테이블 이름으로 사용됩니다.
- @Id: 엔티티의 기본 키(primary key)를 나타내는 필드에 선언합니다.
- @GeneratedValue: 기본 키의 생성 전략을 지정합니다. AUTO, IDENTITY, SEQUENCE, TABLE 등 다양한 전략을 선택할 수 있습니다.
- @Column: 필드가 매핑될 데이터베이스 컬럼을 지정합니다. @Column을 생략하면 필드 이름이 컬럼 이름으로 사용됩니다.
- @OneToOne, @OneToMany, @ManyToOne, @ManyToMany: 엔티티 간의 관계를 나타냅니다.
- @JoinColumn: 관계를 맺는 필드를 데이터베이스의 외래 키로 매핑합니다.
- @Embeddable, @Embedded: 임베디드 타입(embedded type) 즉, 값 타입(Value Type)을 정의하고 사용할 때 쓰입니다.
- @Transient: 해당 필드가 데이터베이스에 매핑되지 않도록 합니다. 이 필드는 데이터베이스에 저장되거나 검색되지 않습니다.
- @Temporal: 날짜 타입을 매핑할 때 쓰입니다. Date, Calendar 타입을 매핑할 때는 반드시 이 어노테이션을 사용해야 합니다.
@GeneratedValue 어노테이션은 데이터베이스에서 자동으로 생성되는 ID 값을 처리하는 방법을 설정합니다. 이 어노테이션을 사용하는 방법에는 AUTO, IDENTITY, SEQUENCE, TABLE 등 4가지가 있습니다.
- AUTO: 이것은 기본값입니다. 데이터베이스를 보고 알아서 선택해 줍니다. 예를 들어 MySQL을 사용하면 IDENTITY 방식을, Oracle을 사용하면 SEQUENCE 방식을 선택해 줍니다.
- IDENTITY: 이 방식은 데이터베이스에 데이터를 저장할 때마다, 데이터베이스가 자동으로 ID 값을 만들어줍니다. 예를 들어, MySQL에서는 AUTO_INCREMENT 기능이 이에 해당합니다.
- SEQUENCE: 이 방식은 데이터베이스에 미리 정해진 숫자 순서(시퀀스)를 따라 ID 값을 만들어줍니다. 주로 Oracle 데이터베이스에서 사용됩니다.
- TABLE: 이 방식은 별도의 테이블을 만들어 그 테이블을 사용해 ID 값을 만들어 줍니다. 이 방식은 모든 데이터베이스 시스템에서 사용할 수 있지만, 별도의 테이블을 관리해야 하는 번거로움이 있습니다.
4. JPA에서 연관관계 매핑이란 무엇인가요? 이를 사용하는 이유는 무엇인가요?
JPA에서의 연관관계 매핑은 데이터베이스의 테이블 간의 관계를 객체의 참조로 표현하는 것입니다. 관계형 데이터베이스에서는 외래 키를 사용하여 테이블 간의 관계를 표현하지만, 객체 지향 프로그래밍에서는 객체 참조를 사용하여 객체 간의 관계를 표현합니다. 연관관계 매핑은 이 두 가지 관계 표현 방식을 연결하는 역할을 합니다.
JPA에서는 @ManyToOne, @OneToMany, @OneToOne, @ManyToMany와 같은 어노테이션을 사용해 다음 네 가지 종류의 연관관계를 매핑할 수 있습니다.
- @ManyToOne / @OneToMany: 하나의 객체가 다른 여러 객체를 참조하는 관계입니다. 예를 들어, 하나의 게시판에 여러 게시물이 있을 수 있습니다.
- @OneToOne: 하나의 객체가 다른 하나의 객체를 참조하는 관계입니다. 예를 들어, 하나의 사용자는 하나의 프로필 정보를 가질 수 있습니다.
- @ManyToMany: 여러 객체가 여러 객체를 상호 참조하는 관계입니다. 예를 들어, 여러 학생이 여러 과목을 수강할 수 있습니다.
연관관계 매핑을 사용하는 이유는 객체 지향 프로그래밍의 특성과 관계형 데이터베이스의 특성을 모두 잘 활용하기 위해서입니다. 객체 지향 프로그래밍에서는 객체 간의 관계를 객체 참조로 표현하면 코드가 더욱 직관적이고 이해하기 쉬워지며, 데이터의 일관성을 유지하는 데 도움이 됩니다. 반면 관계형 데이터베이스에서는 외래 키를 사용해 테이블 간의 관계를 표현하면 데이터의 무결성을 보장할 수 있습니다. JPA의 연관관계 매핑은 이 두 가지 방식을 모두 적절하게 활용하도록 돕습니다.
5. JPA에서는 객체지향 프로그래밍과 관계형 데이터베이스의 패러다임 불일치 문제를 어떻게 해결하나요?
객체지향 프로그래밍과 관계형 데이터베이스는 각각의 패러다임이 있으므로, 이 두 세계를 연결하는 데는 몇 가지 문제가 발생합니다. 이를 "패러다임 불일치" 문제라고 부르며, JPA는 이러한 문제를 해결하기 위한 몇 가지 방법을 제공합니다.
- 상속: 객체지향 프로그래밍에서는 클래스 간에 상속 관계를 가질 수 있지만, 관계형 데이터베이스에서는 이러한 상속 관계를 표현하기 어렵습니다. JPA는 @Inheritance 어노테이션을 통해 객체의 상속 관계를 데이터베이스 스키마에 매핑할 수 있게 해줍니다.
- 연관관계: 객체지향 프로그래밍에서는 객체들 사이에 참조를 통한 연관관계를 가질 수 있습니다. 그러나 관계형 데이터베이스에서는 이러한 참조를 외래 키를 통해 연결해야 합니다. JPA는 이를 @OneToOne, @OneToMany, @ManyToOne, @ManyToMany 등의 어노테이션을 통해 객체 간의 참조 관계를 데이터베이스의 외래 키 관계에 매핑하도록 해줍니다.
- 객체 그래프 탐색: 객체지향 프로그래밍에서는 연관된 객체를 자유롭게 탐색할 수 있습니다. 즉, 객체 참조를 통해 연관된 객체를 가져올 수 있습니다. JPA는 이런 객체 그래프 탐색을 데이터베이스에 저장된 데이터에 대해서도 가능하게 해줍니다.
- 비교하기: 자바에서는 equals()와 hashCode() 메서드를 사용해 두 객체를 비교할 수 있습니다. JPA는 동일한 트랜잭션 내에서 같은 식별자를 가진 엔티티가 같은 것으로 취급되도록 도와줍니다.
이렇게 JPA는 객체지향 프로그래밍과 관계형 데이터베이스 사이의 패러다임 불일치 문제를 해결함으로써, 개발자가 객체지향적인 코드를 작성하는 데 집중할 수 있도록 돕습니다.
6. JPA에서 성능 최적화를 위해 어떤 전략들을 사용할 수 있나요?
- Lazy Loading: Lazy Loading은 연관된 엔티티를 필요할 때까지 로딩하지 않는 방법입니다. 이를 통해 필요하지 않은 데이터의 로딩을 피하고, 성능을 향상시킬 수 있습니다.
- Eager Loading: 반면에, 한 번의 쿼리로 여러 연관된 엔티티를 한꺼번에 로딩하는 Eager Loading도 유용할 수 있습니다. 이 방식을 적절히 사용하면 여러 번의 쿼리를 줄이고, 성능을 향상시킬 수 있습니다.
- Second Level Cache(2차 캐시): JPA는 Second Level Cache를 지원합니다. 이는 이미 로딩된 엔티티를 캐시에 저장하고, 같은 엔티티를 요청할 때 데이터베이스 대신 캐시에서 가져오는 기능입니다. 이를 통해 데이터베이스 액세스를 줄이고 성능을 향상시킬 수 있습니다.
- Batch Processing(배치처리): JPA는 배치 처리를 지원합니다. 이는 한 번의 데이터베이스 트랜잭션에서 여러 엔티티의 변경을 한꺼번에 처리하는 방법입니다. 이를 통해 네트워크 비용을 줄이고, 성능을 향상시킬 수 있습니다.
- Fetch Join(페치조인): JPA의 JPQL에서 제공하는 Fetch Join을 사용하면 관련된 엔티티를 한 번의 쿼리로 함께 조회할 수 있습니다. 이를 통해 쿼리의 수를 줄일 수 있습니다.
- N+1 문제 해결: N+1 문제는 쿼리를 너무 많이 실행하는 것을 말합니다. 이 문제는 Fetch Join이나 Batch Size 설정 등을 통해 해결할 수 있습니다.
- SQL 최적화: JPA는 JPQL을 사용해 SQL을 추상화하지만, 때로는 성능 최적화를 위해 네이티브 SQL을 직접 사용해야 할 때가 있습니다.
- 엔티티 최적화: 불필요한 연관관계, 양방향 연관관계 등을 최적화하여 성능을 향상시킬 수 있습니다.
- Second Level Cache(2차 캐시): JPA는 데이터를 두 번째 저장 공간인 2차 캐시에 보관하는 기능을 제공합니다. 여기에 보관된 데이터는 한 번 로딩되면 다시 사용할 수 있으므로, 같은 데이터를 다시 요청하더라도 데이터베이스에 직접 접근할 필요가 없습니다. 이를 통해 데이터베이스의 부하를 줄이고 성능을 향상시킬 수 있습니다. 그러나 이 기능을 사용할 때는 캐시된 데이터가 최신 상태를 유지하도록 주의해야 합니다.
2차 캐시는 주로 변동이 적고 자주 조회되는 데이터에 적합합니다. 예를 들어, 국가 코드나 지역 코드와 같은 데이터는 변동이 거의 없으며, 많은 곳에서 참조하므로 2차 캐시에 저장하는 것이 효과적입니다. @Cacheable // 캐싱 활성화 - Batch Processing(배치 처리): JPA는 여러 데이터를 한 번에 처리하는 배치 처리 기능을 지원합니다. 예를 들어, 100개의 데이터를 변경하려면 일반적으로 100번의 작업이 필요합니다. 그러나 배치 처리를 사용하면 이 100개의 작업을 한 번에 처리할 수 있습니다. 이렇게 하면 데이터베이스에 보내는 요청 횟수를 줄일 수 있으므로 성능을 향상시킬 수 있습니다.
다량의 데이터를 한 번에 처리할 때 사용합니다
for (int i = 0; i < size; i++) {
entityManager.persist(entities.get(i));
if (i % 50 == 0) { // 50개마다 flush와 clear를 호출
entityManager.flush();
entityManager.clear(); - Fetch Join(페치 조인): JPA에서는 페치 조인이라는 기능을 통해 관련된 여러 데이터를 한 번에 조회할 수 있습니다. 예를 들어, 사용자와 그들이 작성한 게시글을 모두 조회하려면 일반적으로 두 번의 조회가 필요합니다. 그러나 페치 조인을 사용하면 사용자와 게시글을 한 번의 조회로 함께 가져올 수 있습니다. 이렇게 하면 데이터베이스에 보내는 쿼리 수를 줄일 수 있습니다.
연관된 엔티티를 한 번의 쿼리로 함께 조회하고 싶을 때 사용합니다
String jpql = "SELECT p FROM Post p JOIN FETCH p.comments WHERE p.id = :id";
TypedQuery<Post> query = em.createQuery(jpql, Post.class);
query.setParameter("id", postId);
Post post = query.getSingleResult();
Post 엔티티와 그에 연관된 Comment 엔티티를 한 번의 쿼리로 함께 조회하고 있습니다. 이렇게 하면 Post 엔티티를 조회할 때마다 Comment 엔티티를 별도로 조회하는 쿼리를 발생시키는 것을 방지할 수 있습니다.
2023.05.23 - [Spring] - JPA(Java Persistence API), ORM(Object-Relational Mapping) pt.1
2023.06.07 - [Mockterview] - JPA 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading), N+1
'Spring' 카테고리의 다른 글
스프링 프레임워크(Spring Framework), 생성자 주입, Singletone Bean (0) | 2023.05.25 |
---|---|
스프링 3계층 아키텍처, Annotation(@) (0) | 2023.05.25 |
절차지향(Procedural), 객체지향(Object-Oriented), 관점지향(Aspect-Oriented) 프로그래밍 pt.2 (0) | 2023.05.25 |
절차지향(Procedural), 객체지향(Object-Oriented), 관점지향(Aspect-Oriented) 프로그래밍 pt.1 (0) | 2023.05.25 |
영속성 컨텍스트(Persistence Context), 1차 캐시 (0) | 2023.05.23 |