31. N+1 문제의 발생 이유와 해결 방법에 대해 설명해주실 수 있을까요? 해결 방법은 3가지 이상 말씀해주시면 좋습니다.
N+1 쿼리는 JPA의 프록시로 인한 지연 로딩 때문에 발생합니다. 정확한 의미는 1개의 쿼리를 실행했을 때, 내부에 존재하는 컬렉션들을 조회해오면서 생기는 문제입니다.
아래는 해결방법 입니다.
1 : Fetch Join (join Fetch를 사용합니다. 쿼리 한 번으로 해결할 수 있습니다)
2 : @EntityGraph (@EntityGraph의 attributePaths에 쿼리 수행 시 바로 가져올 필드명을 지정하면 한 쿼리에 대해서만 Lazy가 아닌 Eager 조회로 가져오게 됩니다.
3 : FetchType = LAZY -> EAGER (ManyToOne, OneToOne의 FetchType = LAZY -> EAGER 변경)
4 : 서비스 단에서 로직을 통해 SEQ을 정제해 IN 방식으로 필요한 데이터만을 Get 합니다. (Native Query를 날리면 됩니다)
해결 방법 5 : interface dto를 구현해 필요한 것만 view로 리턴 받습니다.
32. 즉시로딩과 지연로딩은 각각 언제 사용하면 좋은지 설명해주실 수 있을까요?
비지니스 로직 상 Member 데이터가 필요한 곳에 대부분 Team의 데이터 역시 같이 사용 할 필요가 있다면 FetchType을 EAGER로 설정하여 항상 Member와 Team을 같이 조회해오는 것이 더 좋다(즉시로딩).
Member를 사용하는 곳 대부분에서 Team 데이터가 필요하지 않다면 FetchType을 LAZY로 설정하여 Member만 조회하고, 드물게 Team이 필요할 땐 그 때 Team에 대한 쿼리를 한번 더 날려 조회하는것이 좋다(지연로딩).
즉시 로딩은 엔티티를 조회할 때 연관된 엔티티도 함께 조회되고 지연 로딩은 엔티티를 조회할 때 연관된 엔티티는 프록시로 조회되며 프록시를 실제 사용할 때 초기화되면서 데이터베이스를 조회합니다. 즉시로딩은 쿼리문을 두 번 따로 사용하여 조회하지 않고 연관된 엔티티를 즉시 조회하는 게 더 유리할 때 사용합니다. 지연 로딩은 필요한 시점에 연관된 엔티티를 조회하여 로딩 시간을 줄이고 불필요한 쿼리문 사용을 방지할 수 있습니다.
33. JWT에 대해 설명해주실 수 있을까요? 구체적으로 JWT를 어디서 처리하는지, 어떠한 방식으로 검증하는지, 재발급 방식과 주기는 어떻게 처리하는지, 다른 API 서비스 호출 시 어떻게 잡아서 인증 처리하는지 말씀해주시면 좋습니다.
JWT는 인증받은 사용자에게 토큰을 발급해주고, 서버에 요청을 할 때 HTTP 헤더에 토큰을 함께 보내 인증받은 사용자(유효성 검사)인지 확인한다. 서버 기반 인증 시스템과 달리 사용자의 인증 정보를 서버에 저장하지 않고 클라이언트의 요청으로만 인가를 처리하므로 Stateless 한 구조를 가진다. JWT는 Json Web Token의 약자로 인증에 필요한 정보를 암호화시킨 토큰을 뜻한다. 세션/쿠키 방식과 유사하게 클라이언트는 Access Token(JWT)을 HTTP 헤더에 실어 서버로 보낸다.
1. 사용자가 로그인 시 올바른 사용자임을 확인하고, 클라이언트에게 Access Token(JWT)을 발급해준다.
2. 클라이언트는 전달받은 토큰을 저장해 두고, 인증이 필요한 요청마다 토큰을 HTTP 헤더에 담아 보낸다.
3. 서버에서는 암호화된 토큰을 복호화 해 올바른 요청인지 확인한다.
4. 인증이 완료되고 서버는 요청에 응답한다.
34. Garbage Collector의 역할, 원리, 알고리즘에 대해 아는 만큼 설명해주실 수 있을까요?
가비지 컬렉터는 주기적으로 JVM의 heap 메모리를 점검하여 스택에서 참조되지 않는 객체를 메모리에서 해제하는 장치이다.
Mark And Sweep 알고리즘은 가비지 컬렉션이 동작하는 원리로 루트에서부터 해당 객체에 접근 가능한지에 대한 여부를 메모리 해제의 기준으로 삼는다. Mark And Sweep은 총 3가지 과정으로 나뉘게 된다.
Mark 과정 : 먼저 Root로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 잠조하고 있는지 찾아서 마킹한다.
Sweep 과정 : 참조하고 있지 않은 객체 즉 Unreachable 객체들을 Heap에서 제거한다.
Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다. (가비지 컬렉터 종류에 따라 하지 않는 경우도 있음)
35. Java Map의 내부 구현은 어떻게 이루어져 있을지 추측해보실 수 있을까요?
Map은 key와 value로 데이터를 저장하고 꺼내는 자료구조이다. 리스트와 배열과는 다르게 순차적으로 데이터를 다루지 않는다. Map은 List 처럼 인터페이스이며 Map 인터페이스를 구현한 자료형중 가장 기본적인 것이 HashMap이다. HashMap은 순서보장없이 key와 value로 데이터를 저장하고 가져오는 작업을 도와준다.
프로그래밍 시 Map을 사용할 때 순서보장이 필요할 때가 있다.
1. 정렬기준에 맞춰서 데이터 순서를 보장한다.
2. 입력순서대로 데이터의 순서를 보장한다.
1번에 해당하는 자료형은 TreeMap 이다. 정렬기준을 가지고 있으며 put() 메소드로 입력되는 key와 value 는 정렬기준에 의해 자동으로 정렬되기 때문에 순서를 보장해준다. tree라는 이름이 붙은것 처럼 정해진 정렬기준에 맞추어 데이터를 관리할 수 있다는 장점이 있다.
2번에 해당하는 자료형은 LinkedHashMap이다. 데이터를 입력된 순서대로 관리해서 데이터의 순서를 보장해준다.
36. Spring bean container 생성부터 스프링 종료까지의 사이클에 대해 알려주실 수 있을까요? @PostConstruct, @PreDestroy 어노테이션의 역할도 함께 알려주시면 좋습니다.
스프링 IoC컨테이너가 생성되면 Component-Scan으로 Bean을 등록합니다. IoC컨테이너에서 의존성을 주입합니다. 스프링은 Bean에게 콜백 메서드를 통해 초기화 시점을 알려주며 스프링 컨테이너가 종료되기 직전에도 소멸 콜백 메서드를 통해 소멸 시점을 알려주고 스프링이 종료됩니다. 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 관리하는데 그 중 최신 스프링에서 가장 권장하는 방법이 @PostConstruct, @PreDestroy 어노테이션입니다. 위 어노테이션만 붙이면 자동으로 초기화 콜백, 소멸전 콜백이 되기 때문에 매우 편리하게 사용이 가능합니다. 스프링이 아닌 다른 컨테이너에서도 동작한다는 장점이 있지만 외부 라이브러리에는 적용하지 못합니다 @PostConstruct 의존관계 주입이 완료되면 호출됩니다 (초기화 콜백) @PreDestroy 빈이 소멸되기 직전에 호출됩니다 (소멸전 콜백)
37. AOP, Interceptor, Filter 의 차이점, Request가 들어올때 거치는 순서, 각 역할들의 장점을 설명해주실 수 있을까요?
Request가 들어올때 거치는 순서 : Filter -> Interceptor -> AOP
첫 번째로 Filter는 DispatcherServlet 이전에 동작하는데 Request와 Response를 정제해주는 역할을 합니다. 이후 서블렛이 동작하고 컨트롤러에 도달하기 전 Interceptor가 동작하여 스프링 컨텍스트 내부에서 컨트롤러에 관한 요청과 응답에 대해 처리합니다. AOP는 OOP를 보완하기 위해 나타난 개념인데 OOP에서 중복을 줄일 수 없는 코드를 모듈화 해서 분리하는 개념입니다. Interceptor, Filter와 달리 메소드의 전후지점에서 다양하게 사용이 가능하고, 어노테이션 파라미터 등 다양한 방법으로 대상을 지정할 수 있기에 로깅, 에러처리와 같이 비즈니스 로직에서 세밀한 조정이 필요할 때 주로 사용합니다.