7/2 TIL

·2024년 7월 1일

오늘은 기술면접을 했다.

  1. JPA에서 Lazy Loading과 Eager Loading의 차이점은 무엇인가요? 각각의 장단점에 대해 설명해 주세요.
    lazy loading(지연 로딩)은 연관 데이터가 실제로 필요할 때 로딩되는것이고, eager loading(즉시 로딩)은 연관 데이터가 엔티티랑 함께 즉시 로딩되는 방식을 의미합니다.

lazy loading의 장점은 초기 로딩 시간이 짧고, 메모리 사용을 절약할 수 있으며, 전체 데이터를 한번에 로딩하지 않기때문에 성능을 향상시킬 수 있습니다.
단점은 연관 데이터 접근 시 추가 쿼리가 발생하여 성능 저하가 있을 수 있다는 것과, 영속성 컨텍스트밖에서 연관데이터에 접근 시 lazy initalization exception이 발생할 수 있습니다.

eager loading의 장점은 연관 데이터를 즉시 사용할 수 있고, 추가쿼리가 발생하지 않는다는 것이 있다.
단점은 초기 로딩 시간이 길어지고, 메모리 사용량이 증가하고, 대용량 데이터의 경우 성능 저하 될 수 있다.

<팀플 했던거
SecurityConfig와 AuthService 클래스 간의 순환 의존성 - ObjectProvider를 사용하여 AuthService를 지연 로딩 하여 순환 의존성을 해결.

기존 private final AuthService authservice; 방식
=> 순환 의존성으로 인해 실행 오류가 발생.
private final ObjectProvider
authServiceProvider; 로 수정 & ObjectProvider 관련 로직을 추가하여 순환 의존성에 문제를 해결.>

<꼬리질문 대비 - cs질문이 또있음??? ???
Lazy Loading은 연관된 데이터를 실제로 접근할 때 로드하는 방식입니다. 따라서 해당 엔티티가 영속성 컨텍스트 안에 있어야 연관된 데이터를 데이터베이스에서 가져올 수 있습니다. 영속성 컨텍스트 밖에서 Lazy 로딩된 데이터에 접근하려 하면, 이미 엔티티 매니저가 종료된 상태이므로 데이터베이스에 접근할 수 없게 됩니다.

영속성 컨텍스트: JPA에서 엔티티 객체의 생명주기를 관리하는 환경입니다. 엔티티 매니저(EntityManager)가 제공하며, 트랜잭션 범위 내에서 동작합니다.
트랜잭션: 데이터베이스 작업의 단위로, 성공할 땐 전체 작업이 실행되고, 실패했을 땐 전체 작업이 롤백됩니다.

lazy initalization exception 해결 방안 :
1. 트랜잭션 범위 내에서 모든 데이터 접근 - 모든 연관 데이터 접근을 트랜잭션 안에서 수행
2. eager loading 사용 - 연관 데이터를 즉시 로딩하도록 설정
3. fetch join 사용 - JPQL을 사용하여 연관데이터 한번에 로드
4. Hibernate.initalize() 사용 - 연관데이터 명시적으로 초기화

  1. JPA에서 N+1 문제를 해결하기 위한 방법을 설명해 주세요. (왜 발생할 수밖에 없는지 jpa의 구조적인 것.)
    (JPA에서 N+1 문제는 자주 발생하는 성능 문제로, 특정 엔티티를 조회할 때 연관된 엔티티들을 개별 쿼리로 가져오는 현상을 말합니다. 예를 들어, N개의 엔티티를 조회할 때 각 엔티티에 대해 추가적인 쿼리가 실행되면서 총 N+1개의 쿼리가 발생하게 됩니다. 이는 데이터베이스 성능에 큰 영향을 미칠 수 있습니다.)

  2. Fetch Join 사용 -> 연관된 엔티티를 한번의 쿼리로 함께 로딩할 수 있음.(join(지연로딩)과 유사하지만 연관된 엔티티의 필드를 eager loading(즉시 로딩)하는 방식)
    JPQL에서 JOIN FETCH 구문을 사용하여 구현할 수 있음.
    ex) String jpql = "SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :userId";
    User user = entityManager.createQuery(jpql, User.class)
    .setParameter("userId", userId)
    .getSingleResult();

  3. Entity Graph 사용 -> 특정 엔티티를 로딩할 때 연관된 엔티티들을 명시적으로 지정할 수 있음. 어노테이션을 통해 구현할 수 있음. JPQL을 사용하지 않고도 N+1 문제를 해결할 수 있는 유연한 방법
    ex) @Entity
    @NamedEntityGraph(name = "User.orders", attributeNodes = @NamedAttributeNode("orders"))

  1. Batch Fetching 사용 -> 하이버네이트의 기능. 한번에 여러 엔티티들을 로드할 수 있음. 하이버네이트의 설정 파일이나 어노테이션을 통해 설정할 수 있음.
    ex) @BatchSize(size = 10) // hibernate.default_batch_fetch_size=10
  1. Subselect Fetching -> 하이버네이트의 기능. 서브쿼리를 사용하여 관련 엔티티들을 한번에 로드할 수 있음. 성능 최적화에 도움 -> 대용량 데이터에서 효과적임.
    ex) @Fetch(FetchMode.SUBSELECT)

<꼬리질문 대비 -
JPQL : JPA의 일부, 객체지향 쿼리 언어. SQL과 유사하지만 데이터베이스 테이블이 아닌 JPA 엔티티 객체를 대상으로 쿼리 수행 -> 데이터베이스에 독립적인 방식으로 객체지향적인 쿼리 작성 가능.

  • 특징 : 객체지향적 쿼리, 플랫폼 독립성, 동적쿼리 작성 가능.
  • 테이블 대신 엔티티 클래스 활용
  • 주요기능 : 집계함수, 서브쿼리, 네임드 쿼리
  • SQL과의 차이점 : SQL은 데이터베이스 테이블 대상, JPQL은 엔티티 객체 대상 / JPQL은 객체 지향적, SQL은 데이터베이스 독립적 / JPQL은 엔티티 클래스를 사용하여 타입 세이프티 보장

하이버네이트 : 자바 애플리케이션에서 데이터베이스와 상호작용하기 위해 널리 사용되는 객체관계매핑(ORM) 프레임워크.
자바 객체를 관계형 데이터베이스의 테이블로 매핑하고, 데이터베이스와의 상호작용을 간소화하여 개발자가 SQL을 직접 작성하지 않아도 데이터베이스 작업을 수행할 수 있게함.

  • 특징 : 객체-관계 매핑(ORM), HQL, 캐싱(1차 캐시, 2차캐시를 통해 성능 최적화), 자동화된 DDL 생성(엔티티 클래스 정의를 기반으로 데이터베이스 테이블을 자동으로 생성하거나 업데이트할 수 있음)
    트랜잭션 관리, 확장성(다양한 데이터베이스 지원, 플러그인 아키텍쳐를 통해 쉽게 확장할 수 있음)
  • 장점 : 생산성 향상, 유지보수 용이(매핑을 통해 코드의 가독성, 유지보수성 높임), 데이버테이스 독립성(HQL, ORM을 통해 특정 데이터베이스에 종속되지 않는 코드 작성 가능)
  • 단점 : 복잡도(초기 설정, 학습곡선이 높을 수 있음), 성능 이슈(잘못된 사용 또는 과도한 캐싱 설정으로 인해 성능문제 발생할 수 있음)
  1. 레이어별로 나누어서 Slice Test 를 하는 이유에 대해서 설명해주세요.
  • 시스템의 각 레이어를 독립적으로 테스트하여, 코드의 품질과 유지보수성을 높이고, 버그를 조기에 발견하여 수정하기 위함.
    Slice Test : 특정 레이어를 분리하여 테스트하는 방법
  • 이유 : 단일 책임 원칙, 의존성 격리, 빠른 피드백, 유닛 테스트와 통합 테스트의 균형, 문제 식별의 용이성
  1. 좋아요 기능을 구현할 때, 특정 사용자가 특정 게시글을 이미 좋아요 했는지 확인하는 방법을 설명해 주세요.
    -1. 좋아요 엔티티에 좋아요를 누른 사용자id와 좋아요가 눌린 게시글의 id 포함. /
    -2. 좋아요 레포에 사용자와 게시글을 기준으로 좋아요 정보를 조회하는 메서드 정의
    optional를 반환하여 좋아요가 이미 눌린 상태인지 확인
    -3. 좋아요 서비스의 likepost를 통해, 게시물 존재 여부를 확인하고,
    사용자가 자신의 게시물을 좋아요 하려는 경우를 예외처리,
    이미 좋아요를 눌렀는지 확인하여 중복 좋아요 방지.
    새로운 postlike엔티티생성, 저장.
    unlikepost는 좋아요가 눌린적이 있는지 확인하고, 좋아요 취소

    // 이미 좋아요를 눌렀는지 확인
    Optional commentLike = commentLikeRepository.findByUserAndComment(user, comment);
    if (commentLike.isPresent()) {
    throw new CustomException(ErrorEnum.COMMENT_ALREADY_LIKED);
    }

피드백 : 테이블 스키마부터 들어가도 ㄱㅊ음. 조회할때 정보 들고오기. flow-process위주로 설명하면 됨. 서드파티api의 오류를 제어할 수는 없지만, 어떤 오류인지 파악은 할 수 있음.
서킷 브레이커 쓴 프로젝트에 퍼센트같은거 물어볼 수도 있음.

트랜잭션 : 데이터베이스에서 하나의 논리적 작업 단위. 여러 연산을 하나의 일관된 작업으로 처리하는 것을 의미.
- 주요 목적 : 데이터의 일관성, 신뢰성 유지.
- 성공적으로 완료되면 모든 변경사항이 데이터베이스에 커밋되고, 실패하면 롤백되어 이전 상태로 되돌림.

트랜잭션(데이터베이스)의 ACID - 트랜잭션의 안정성, 신뢰성을 보장함.
1. 원자성(Atomicity) : 트랜잭션은 모두 성공하거나 모두 실패해야함
(보장방법 : 트랜잭션 로그를 사용하여 실패 시 롤백 수행
ex)은행 계좌 이체에서 출금하는 작업, 입금하는 작업이 하나의 트랜잭션으로 이루어져야 함.)
2. 일관성(Consistency) : 트랜잭션이 실행되기 전과 후에 데이터베이스의 일관성이 유지되어야 함. 트랜잭션이 성공적으로 완료되면 데이터베이스는 유효한 상태를 유지해야함
(보장방법 : 제약 조건, 트리거, 외래키 등을 통해 데이터의 유효성 유지
ex)은행 시스템에서의 모든 거래의 합계는 트랜잭션 전후에 동일해야 함.)
3. 격리성(Isolation) : 여러 트랜잭션이 동시에 실행될 때, 각 트랜잭션은 다른 트랜잭션의 영향을 받지 않아야 함. 즉 하나의 트랜잭션이 완료될 때까지 다른 트랜잭션은 중간 상태를 볼 수 없어야 함.
(보장방법 : 트랜잭션 격리 수준 설정 - read uncommitted, read committed, repeatable read, serializable
ex)두개의 트랜잭션이 동시에 같은 데이터를 수정하려고 할 때, 하나의 트랜잭션이 완료될 때까지 다른 트랜잭션은 해당 데이터에 접근할 수 없음.
다른 트랜잭션이 계좌 a, b의 잔액을 읽을 때 이체가 완료되기 전까지 중간상태를 볼 수 없어야 함.)
4. 지속성(durablity) : 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되어야 함. 시스템 오류가 발생하더라도 트랜잭션의 결과는 손실되지 않아야 함.
(보장방법 : 트랜잭션 로그, 백업 및 복구 매커니즘을 통해 데이터 영구 저장.)
ex) 은행 계좌 이체가 완료된 후, 시스템이 재부팅되더라도 이체결과는 유지되어야함.

피드백 : 어떻게 팬텀리드를 해결할 수 있는가? 구체적인 해결방법. 트랜잭션 전파(메소드를 호출했을 때 기존의 트랜잭션에 넣을지 아니면 새로 트랜잭션을 열지). 트랜잭션 시간 제한(타임아웃)을 많이 둠. '어떤 상황을 주고 이 때 트랜잭션을 어떻게 써서 해결할 수 있는가?', 금융권에서 실제로 트랜잭션을 사용하는 상황을 많이 만들어보기. 데이터베이스에서도 트랜잭션을 사용해보기. b+tree, b-tree 등 데이터베이스 알고리즘 잘 알아야됨. 금융권 관련된 도메인에서 부분부분 만들어보기, 토스 창업자 자서전읽고 독후감.(회사 설립과정을 알고 어필하는건 장점이 될것), 옛날 노후화되어있을 때의 환경에서(토스, 카뱅 없을 때) 개발해보기 - (얼마나 힘들엇을가 리드미에 적기.) 데이터베이스 아주아주 중요함 - 책하나 사서 보기.

트랜잭션의 상태
1. Active(활성) : 트랜잭션이 실행 중이며, 데이터 조작 연산이 이루어지는 상태
2. Partially Committed(부분 커밋) : 마지막 연산이 완료된 상태
3. Committed(커밋) : 트랜잭션이 성공적으로 완료되어 변경 사항이 데이터베이스에 영구적으로 반영된 상태
4. Failed(실패) : 트랜잭션이 실패하거나 중단된 상태
5. Terminated(종료) : 트랜잭션이 종료된 상ㅌ, 성공적이든 실패든 완료된 상태

트랜잭션 격리 수준 - 트랜잭션 간 간섭을 얼마나 허용할지 결정함. 데이터베이스의 일관성, 격리성을 유지하면서 성능을 최적화하는데 중요한 역할을 함.
가장 낮은 수준에서 가장 높은 수준으로 격리 수준을 설정할 수 있음.
1. Read Uncommitted(읽기 미확정) : 가장 낮은 격리 수준. 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있음
- 잠금을 거의 사용하지 않아 성능이 좋지만, 데이터 일관성 문제가 발생할 수 있음.
- 문제점 : 더티 리드(한 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있음), 비반복적 읽기, 팬텀 리드가 발생할 수 있음.

      2. Read Committed : 대부분의 상용 데이터베이스의 기본 격리 수준. 트랜잭션이 커밋된 데이터만 읽을 수 있음. 더티 리드 방지
                                   - 문제점 : 비반복적 읽기(동일한 쿼리를 두번 실행할 때 다른 결과를 얻을 수 있음), 팬텀리드가 발생할 수 있음.
                                   
      3. Repeatable Read : 트랜잭션이 처음 읽은 데이터를 일관되게 읽을 수 있음.
                                   - 트랜잭션이 시작된 이후, 다른 트랜잭션이 해당 데이터를 변경할 수 없음. 더티리드, 비반복적읽기 방지
                                   - 문제점 : 팬텀리드(트랜잭션이 범위 쿼리를 수행할 때, 다른 트랜잭션이 해당 범위 내에 새로운 행을 삽입할 수 있음)가 발생할 수 있음.

      4. Serializable : 가장 높은 수준의 격리성 제공, 트랜잭션을 순차적으로 실행하여 완전한 일관성 보장
                           - 팬텀 리드뿜남 아니라 모든 데이터베이스 이상 현상 방지   
                           - 문제점 : 성능 저하 발생 가능.

트랜잭션 응용 사례

  • 금융 시스템: 계좌 이체 시 한 계좌에서 출금되고 다른 계좌에 입금되도록 보장.
  • 전자상거래 플랫폼: 결제 성공 시 재고 수준을 조정하여 과판매를 방지.
  • 재고 관리: 판매나 구매 후 재고 수량을 정확하게 업데이트.
  • 예약 시스템: 항공기나 호텔 예약 시 중복 예약을 방지.
  • 의료 시스템: 환자 기록 업데이트 시 데이터 일관성 유지​

트랜잭션 락 : 데이터베이스에서 동시에 실행되는 여러 트랜잭션이 동일한 데이터에 접근할 때 데이터의 일관성을 유지하고 충돌을 방지하기 위해 사용되는 메커니즘입니다. 데이터베이스의 ACID 속성중 일관성, 격리성을 보장하는 데 중요한 역할을 합니다.

  • 종류 : 1. 공유 락 : 데이터를 읽기 위해 설정하는 락. 여러 트랜잭션이 동시에 공유 락을 설정할 수 있으며, 공유 락이 걸린 데이터는 다른 트랜잭션에 의해 읽힐 수 있지만, 쓰기는 불가능.
    -- 공유 락 설정 : SELECT FROM accounts WHERE account_id = 123 FOR SHARE;
    2. 배타 락 : 데이터를 쓰기 위해 설정하는 락. 배타 락이 걸린 데이터는 다른 트랜잭션이 읽거나 쓸 수 없음. 즉 한 트랜잭션만이 해당 데이터를 수정할 수 있음.
    -- 배타 락 설정 : SELECT
    FROM accounts WHERE account_id = 123 FOR UPDATE;
    3. 갱신 락 : 데이터를 업데이트하기 위해 설정하는 락. 공유락과 배타락의 중간 형태. 갱신락은 배타락으로 변환될 수 있고, 동시에 여러 트랜잭션이 설정할 수 없음. 주로 교착상태를 방지하기 위해 사용.

  • 주요 개념 -> 트랜잭션 락의 성능, 안정성에 영향을 미침.
    1. 락 경합 : 여러 트랜잭션이 동일한 데이터에 접근하려고 할 때 발생하는 충돌 상황, 경합이 심할수록 성능 저하가 발생할 수 있습니다.
    2. 교착 상태 : 두 개 이상의 트랜잭션이 서로가 필요로 하는 자원을 락 걸어 무한 대기 상태에 빠지는 상황. DBMS(데이터베이스 관리 시스템)는 교착 상태를 감지하고, 교착 상태를 해소하기 위해 한 트랜잭션을 롤백하는 등의 조치를 취함.
    3. 락 타임아웃 : 트랜잭션이 특정시간 내에 락을 획득하지 못하면 실패하는 상황. 타임아웃 설정을 통해 시스템의 응답성을 높일 수 있음.

꼬리질문 대비
1. 트랜잭션 롤백 시 어떤 상황에서 문제가 발생할 수 있나요?
Partial Rollback: 트랜잭션의 일부가 롤백되지 않고 남아있는 경우, 데이터 일관성이 깨질 수 있습니다.
Resource Locking: 롤백이 오래 걸리는 경우, 자원(예: 테이블, 행)이 계속 잠겨 있어 다른 트랜잭션이 이를 사용할 수 없는 상황이 발생할 수 있습니다.
Deadlock(데드락): 여러 트랜잭션이 상호 의존적인 자원을 잠그고 있어 롤백도 해결할 수 없는 교착 상태가 발생할 수 있습니다

  1. 분산 트랜잭션(Distributed Transaction)이란 무엇이며, 어떤 상황에서 사용되나요?
    분산 트랜잭션(Distributed Transaction)**은 여러 데이터베이스 노드에 걸쳐 있는 트랜잭션입니다. 하나의 트랜잭션이 여러 데이터베이스 시스템이나 자원 관리자(파일 시스템, 메시징 시스템 등)를 동시에 사용할 때 필요합니다. 이 트랜잭션은 모든 노드에서 ACID 속성을 유지해야 하며, 이를 위해 2PC (Two-Phase Commit) 프로토콜 등을 사용합니다. 분산 트랜잭션은 주로 다음과 같은 상황에서 사용됩니다:

    은행 시스템: 다양한 지점에서의 계좌 이체
    온라인 쇼핑몰: 주문 처리 시 재고와 결제를 분리된 시스템에서 처리
    대규모 엔터프라이즈 시스템: 다양한 서비스와 데이터베이스 간의 일관된 데이터 관리

  1. NoSQL 데이터베이스에서 트랜잭션을 어떻게 지원하나요?
    NoSQL 데이터베이스는 일반적으로 수평 확장성과 성능을 우선시하기 때문에 트랜잭션 지원이 제한적입니다. 그러나 특정 시나리오에서는 트랜잭션을 지원하기도 합니다:

MongoDB: 단일 도큐먼트에 대한 원자적 연산을 지원하며, 버전 4.0부터는 다중 도큐먼트 ACID 트랜잭션도 지원합니다.
Cassandra: 기본적으로 트랜잭션을 지원하지 않지만, 경량 트랜잭션(Lightweight Transactions)을 통해 일부 원자적 연산을 지원합니다.
HBase: 부분적으로 트랜잭션을 지원하며, 다중 행의 원자적 연산을 위해 CheckAndMutate 연산을 사용합니다

profile
응애개발자

0개의 댓글