[24.07.01] 내일배움캠프 일차 JAVA TIL - 모의 면접 준비
2024. 7. 1. 13:39ㆍT.I.L
오늘 한 일
- JPA 심화 완강
- AWS 강의 수강
질문
- JPA에서 Lazy Loading과 Eager Loading의 차이점은 무엇인가요? 각각의 장단점에 대해 설명해 주세요.
- JPA에서 N+1 문제를 해결하기 위한 방법을 설명해 주세요.
- 통합 테스트와 단위 테스트의 차이점에 대해서 설명해주세요.
- 통합 테스트과 단위 테스트의 장/단점에 대해서 설명해주세요.
- 레이어별로 나누어서 Slice Test 를 하는 이유에 대해서 설명해주세요.
- 테스트 코드를 직접 짰을 때, 느낀 테스트 코드 작성의 필요성을 설명해주세요.
- JPA와 Hibernate의 차이점은 무엇인가요?
- QueryDSL을 사용하여 복잡한 동적 쿼리를 작성하는 방법을 설명해 주세요.
- 프로젝트에서 좋아요 기능을 구현할 때, 특정 사용자가 특정 게시글을 이미 좋아요 했는지 확인하는 방법을 설명해 주세요.
- JPA에서 Lazy Loading과 Eager Loading의 차이점은 무엇인가요? 각각의 장단점에 대해 설명해 주세요.
- 차이점: 엔티티가 로딩될 때 즉시 관련된 모든 엔티티를 함께 로딩합니다. → 한번의 쿼리로 연관된 데이터를 모두 가져옴 (이미 필요한 데이터가 로딩되어 있음)
- 장점:
- 데이터베이스에서 한 번의 쿼리로 필요한 데이터를 모두 가져오므로 접근 속도가 빠릅니다.
- 트랜잭션 내에서 필요한 모든 데이터가 이미 로딩되어 있어 안전합니다.
- 단점:
- 필요하지 않은 데이터까지 모두 로딩하므로 메모리 사용량이 많아질 수 있습니다.
- 초기 로딩 시간이 길어질 수 있습니다.
- 차이점: 관련된 엔티티가 실제로 사용될 때까지 로딩을 지연시킵니다.
- 참조 객체들의 데이터들은 무시하고 해당 엔티티의 데이터만을 가져오는 방식
- 장점:
- 초기 로딩 시 필요한 데이터만 가져오기 때문에 메모리 사용량이 적습니다.
- 초기 로딩 속도가 빠릅니다.
- 단점:
- 관련된 엔티티를 실제로 사용할 때마다 추가 쿼리가 발생할 수 있습니다.
- 트랜잭션 외부에서 접근 시 LazyInitializationException이 발생할 수 있습니다.
- LAZY 로딩은 엔티티가 영속 상태일 때만 가능합니다. 준영속 또는 비영속 상태에서는 LAZY 로딩이 불가능하며 LazyInitializationException이 발생합니다.
- 따라서 트랜잭션이 종료된 후 LAZY 로딩을 시도하면 예외가 발생하므로, 필요한 데이터는 트랜잭션 내에서 미리 로딩하거나, DTO를 사용하여 필요한 데이터를 트랜잭션 내에서 변환하여 사용해야 합니다.
- @ManyToOne과 @OneToOne 관계의 기본 로딩 방식은 EAGER입니다.
- @OneToMany와 @ManyToMany 관계의 기본 로딩 방식은 LAZY입니다.
- EAGER 로딩(즉시 로딩) ~ToOne에서 기본값
- JPA에서 N+1 문제를 해결하기 위한 방법을 설명해 주세요.
- 정의: 하나의 쿼리로 N개의 엔티티를 조회한 후, 각 엔티티와 연관된 데이터를 조회하기 위해 추가로 N번의 쿼리가 실행되는 문제를 말합니다. 결과적으로 총 N+1번의 쿼리가 실행되어 성능에 큰 영향을 미칩니다.
- → 데이터 개수만큼 엔티티 연관관계 조회 쿼리가 발생하는 현상
- Fetch Join:
- 하나의 쿼리로 연관된 엔티티를 함께 조회
- 예: SELECT p FROM Parent p JOIN FETCH p.children
- 장점: 쿼리 수가 줄어들어 성능이 개선됩니다.
- 단점: 너무 많은 데이터를 한 번에 로딩할 경우 메모리 사용량이 많아질 수 있습니다.
- Batch Fetching: (배치 사이즈 조절)
- 한번에 여러 연관된 엔티티를 배치로 가져오는 방식입니다.
- JPA 설정에서 @BatchSize를 사용하여 일괄 로딩 크기를 지정할 수 있습니다.
- 예: @BatchSize(size = 10)
- Entity Graph:
- 엔티티 그래프를 정의하여 특정 조회 시점에 필요한 연관 엔티티를 명시적으로 지정합니다.
- 예: @EntityGraph(attributePaths = {"children"})
- 장점: 동적으로 필요한 데이터를 선택적으로 로딩할 수 있습니다.
- 통합 테스트와 단위 테스트의 차이점
- 단위 테스트:
- 정의: 애플리케이션의 가장 작은 단위(보통 메서드나 클래스)를 독립적으로 테스트하는 것.
- 목적: 특정 기능이 올바르게 작동하는지 확인.
- 특징:
- 다른 컴포넌트와 독립적으로 테스트.
- Mock 객체를 많이 사용.
- 빠른 실행 속도.
- 개발 초기에 작성 (TDD : 테스트 코드를 먼저 작성하는 개발 방법론)
해당 부분만 독립적으로 테스트하기 때문에 어떤 코드를 리팩토링하여도 빠르게 문제 여부를 확인할 수 있다. 테스팅에 대한 시간과 비용을 절감할 수 있다. 새로운 기능 추가 시에 수시로 빠르게 테스트 할 수 있다. 리팩토링 시에 안정성을 확보할 수 있다. 코드에 대한 문서가 될 수 있다.
또한 좋고 깨끗한 테스트 코드는 FIRST라는 5가지 규칙을 따라야 한다. Fast: 테스트는 빠르게 동작하여 자주 돌릴 수 있어야 한다. Independent: 각각의 테스트는 독립적이며 서로 의존해서는 안된다. Repeatable: 어느 환경에서도 반복 가능해야 한다. Self-Validating: 테스트는 성공 또는 실패로 bool 값으로 결과를 내어 자체적으로 검증되어야 한다. Timely: 테스트는 적시에 즉, 테스트하려는 실제 코드를 구현하기 직전에 구현해야 한다.
- 정의: 여러 단위가 결합된 기능이나 모듈이 올바르게 상호작용하는지 테스트하는 것.
- 최소 두개 이상의 단위가 잘 작동하는 지, 개발 후반부에 시행합니
- 목적: 시스템의 각 부분이 함께 제대로 작동하는지 확인.
- 특징:
- 실제 환경과 비슷한 조건에서 테스트.
- 데이터베이스, 네트워크 등 외부 시스템과의 통합 테스트 포함.
- 상대적으로 느린 실행 속도.
- 단위 테스트와 통합 테스트의 장/단점
- 단위 테스트:
- 장점:
- 빠른 피드백: 코드 변경 후 즉각적인 피드백 가능.
- 디버깅 용이: 특정 기능만 테스트하므로 문제를 쉽게 찾아낼 수 있음.
- 코드 리팩토링: 코드 변경 시 안정성을 보장.
- 단점:
- 전체 시스템의 동작 보장 어려움: 개별 단위가 올바르게 작동해도 통합 시 문제가 발생할 수 있음.
- Mocking의 필요성: 실제 환경과 다를 수 있음.
- 장점:
- 시스템 전반의 동작 확인: 실제 사용 환경과 유사한 조건에서 테스트.
- 외부 시스템과의 통합 확인: 데이터베이스, 네트워크 등과의 상호작용 테스트 가능.
- 단점:
- 느린 실행 속도: 실제 시스템과의 통합 때문에 시간 소요.
- 디버깅 어려움: 문제 발생 시 원인 파악이 어려울 수 있음.
- 레이어별로 나누어서 Slice Test를 하는 이유
- 장점:
- Slice Test:
- 특정 레이어(Controller, Service, Repository 등)에 대한 테스트를 수행하여 각 레이어의 동작을 검증하는 테스트 방법.
- @WebMvcTest
- @WebFluxTest
- @DataJpaTest
- @JsonTest
- @RestClientTest
- 신속한 피드백: 단위 테스트처럼 빠르게 실행되어 신속한 피드백을 제공.
- 격리된 환경: 특정 레이어만 테스트하므로 다른 레이어의 영향을 받지 않고 독립적으로 테스트 가능.
- 문제 파악 용이: 각 레이어별로 테스트하므로 문제가 발생한 레이어를 쉽게 파악할 수 있음.
- 효율적인 리소스 사용: 통합 테스트에 비해 필요한 리소스가 적고, 설정이 간단함.
- 특정 레이어(Controller, Service, Repository 등)에 대한 테스트를 수행하여 각 레이어의 동작을 검증하는 테스트 방법.
- 이유:
- 테스트 코드 작성의 필요성
- 코드 안정성 확보: 변경 시 기존 기능이 올바르게 작동하는지 확인할 수 있어 안정성을 높임.
- 디버깅 용이성: 버그 발생 시 빠르게 원인을 파악하고 수정 가능.
- 자동화 가능: 지속적인 통합(CI) 및 배포(CD) 과정에서 자동화된 테스트 실행으로 품질 보장.
- 문서화 역할: 테스트 코드는 코드의 사용 방법과 예상 결과를 보여주는 문서화 역할을 함.
- 리팩토링 지원: 기존 코드의 동작을 보장하면서 리팩토링할 수 있어 유지보수에 용이.
- 테스트 코드를 작성하면서 느낀 필요성:
- JPA와 Hibernate의 차이점
- 정의: 자바에서 ORM(Object-Relational Mapping)을 위한 표준 인터페이스.
- 역할: 애플리케이션과 데이터베이스 사이의 데이터 매핑을 위한 표준을 제공.
- 구현체: Hibernate, EclipseLink, OpenJPA 등이 JPA의 구현체.
- 영속성 컨텍스트(persistence context) : 엔티티를 영구 저장하는 환경 데이터베이스와 영속성 컨텍스트를 통해 연결되있는 엔티티(객체)
- 정의: JPA를 구현한 ORM 프레임워크 중 하나.
- 역할: JPA의 표준을 따르며, 추가적인 기능(캐싱, 데이터베이스 벤더별 기능 등)을 제공.
- 특징: 다양한 기능과 유연성을 제공하여 JPA를 사용하는 개발자들에게 자주 선택됨.
- spring starter - data jpa 의존성에 이미 구현체가 포함되어있어 jpa와 호환이 잘된
객체에 비지니스 책임을 위임할 수 있어요. 객체를 불러올 때, 연관된 객체 또한 함께 불러오기 때문에, SQL에 의존적이지 않은 개발을 할 수 있어요. 즉, SQL 중심이 아닌 객체 중심의 개발이 가능해요
- 그러나 query가 복잡해지면 ORM으로 표현하는데 한계가 있고, 성능이 raw query에 비해 느리다는 단점이 있는데요,
- ORM을 이용하면 SQL Query가 아닌 직관적인 코드(메서드)로서 데이터를 조작할 수 있습니다.
- ORM이란 객체와 DB의 테이블이 매핑을 이루는 것을 말합니다. (Java 진영에 국한된 기술은 아닙니다.) - 애플리케이션과 데이터베이스 사이의 중간계층 - 트랜잭션이 완료되면 데베에 반
- JPA (Java Persistence API):
- QueryDSL을 사용하여 복잡한 동적 쿼리를 작성하는 방법
- 기본 설정:
- build.gradle에 QueryDSL 관련 의존성 추가:
- groovy코드 복사 dependencies { implementation 'com.querydsl:querydsl-jpa' annotationProcessor 'com.querydsl:querydsl-apt' }
- Q 클래스 생성:
- 엔티티 클래스를 기반으로 Q 클래스를 생성.
- 예: QMember qMember = QMember.member;
- 쿼리 작성:
- JPAQueryFactory를 사용하여 동적 쿼리를 작성:
- java코드 복사 JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); List<Member> members = queryFactory.selectFrom(QMember.member) .where(QMember.member.age.gt(30)) .fetch();
- 동적 조건 추가:
- BooleanBuilder를 사용하여 조건을 동적으로 추가:
- java코드 복사 BooleanBuilder builder = new BooleanBuilder(); if (age != null) { builder.and(QMember.member.age.gt(age)); } if (name != null) { builder.and(QMember.member.name.eq(name)); } List<Member> members = queryFactory.selectFrom(QMember.member) .where(builder) .fetch();
- 기본 설정:
- 프로젝트에서 좋아요 기능을 구현할 때, 특정 사용자가 특정 게시글을 이미 좋아요 했는지 확인하는 방법
- 엔티티 설정:
- User와 Post 엔티티 사이에 Like 엔티티를 만들어 다대다 관계를 매핑.
- 예:
- java코드 복사 @Entity public class Like { @Id @GeneratedValue private Long id; @ManyToOne private User user; @ManyToOne private Post post; }
- 레포지토리 메서드 작성:
- 특정 사용자가 특정 게시글을 좋아요 했는지 확인하는 쿼리 작성:
- java코드 복사 public interface LikeRepository extends JpaRepository<Like, Long> { boolean existsByUserAndPost(User user, Post post); }
- 서비스에서 사용:
- 서비스 클래스에서 레포지토리 메서드를 사용하여 좋아요 상태 확인:
- java코드 복사 @Service public class LikeService { @Autowired private LikeRepository likeRepository; public boolean isLikedByUser(User user, Post post) { return likeRepository.existsByUserAndPost(user, post); } }
- 엔티티 설정:
- 트랜잭션이 뭐고 왜 필요한지, 예시를 들어서 설명해주세요
- 정의: 데이터베이스의 상태를 변화시키는 작업의 논리적 단위
- 특징 (ACID): 원자/일관/독립/영속
- Atomicity: 모든 작업이 완전히 성공하거나 완전히 실패해야 합니다.
- Consistency: 트랜잭션이 완료되면 데이터베이스가 일관성 있는 상태로 유지됩니다.
- Isolation: 트랜잭션은 다른 트랜잭션으로부터 독립적으로 실행되어야 합니다.
- Durability: 트랜잭션이 성공하면 그 결과는 영구적으로 반영됩니다.
- 데이터 일관성 유지
- 오류 발생 시 데이터 복구 가능
- 동시에 여러 사용자 접근 시 데이터 무결성 보장
- 은행 계좌 이체: A 계좌에서 B 계좌로 돈을 이체하는 경우, A 계좌에서 출금과 B 계좌로 입금이 모두 성공해야 합니다. 둘 중 하나라도 실패하면 전체 작업이 취소되어야 합니다.
- 트랜잭션 어노테이션 사용:
- java코드 복사 @Transactional public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) { // 출금 로직 // 입금 로직 }
- 어노테이션 동작 원리:
- @Transactional 어노테이션이 붙은 메서드는 프록시 객체에 의해 트랜잭션이 시작되고 종료됩니다.
- 트랜잭션이 시작될 때 데이터베이스 커넥션이 생성되고, 메서드 실행이 완료되면 커밋 또는 롤백됩니다.
- 예외가 발생하면 트랜잭션이 롤백되고, 성공적으로 완료되면 커밋됩니다
<엔티티의 생명주기 4가지>
: 엔티티를 영구 저장하는 환경, 논리적인 개념
: 애플리케이션 - DB 사이에서 객체를 보관하는 가상의 DB 역할 (→ 플러시 시점에 DB에 반영)
- 비영속 상태 (new/transient)
객체만 생성하고 엔티티와 연결X
- 영속 상태 (managed) → @Id로 매핑된 키값이 있어야함
객체를 생성하고 저장한 상태,
영속성 컨텍스트가 관리하는 엔티티(1차캐시/쓰기지연저장소에 있는 상태)
ex) em.persist(member);
//객체를 생성만 한 상태(비영속)
Member member = new Member();
member.setId("member1");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
- 준영속 상태 (detached)
엔티티가 영속성 컨텍스트에서 분리 ex) em.detached( );
- 삭제 상태 (removed)
엔티티 삭제 ex) em.remove( );
'T.I.L' 카테고리의 다른 글
[24.07.03] 내일배움캠프 일차 JAVA TIL - AOP (0) | 2024.07.05 |
---|---|
[24.07.02] 내일배움캠프 일차 JAVA TIL - Docker (0) | 2024.07.02 |
[24.06.27] 내일배움캠프 일차 JAVA TIL - CI/CD (0) | 2024.06.27 |
[24.06.24] 내일배움캠프 47일차 JAVA TIL - 테스트 코드 개념과 TDD 기초 (0) | 2024.06.25 |
[24.06.21] 내일배움캠프 46일차 JAVA TIL - 관심사 분리 (0) | 2024.06.24 |