면접 (Java)/기술면접

[면접] 기술 면접 - Java (5)

hihyuk 2024. 3. 9. 15:58
  • 즉시로딩과 지연로딩은 각각 언제 사용하면 좋은지 설명해주실 수 있을까요?

  " ORM(Object-Relational Mapping)에서 즉시로딩(Eager Loading)과 지연로딩(Lazy Loading)은 데이터를 언제 불러올지 결정하는 두 가지 방법입니다.
즉시로딩은 필요한 데이터를 처음 쿼리할 때 모두 불러오는 방식입니다. 예를 들어, 사용자 정보와 그 사용자의 상세 정보가 항상 같이 필요할 때 쓰면 좋습니다. 이 방법은 한 번의 쿼리로 필요한 모든 정보를 불러올 수 있어 편리하지만, 필요 없는 정보까지 불러올 수 있으니 주의가 필요합니다.
지연로딩은 실제로 데이터가 필요할 때까지 로딩을 미루는 방법입니다. 사용자의 게시글 목록만 먼저 보고, 게시글을 클릭했을 때 그 게시글의 댓글을 불러오는 상황에 적합합니다. 이 방식은 초기 로딩 시간을 단축시키고, 필요하지 않은 데이터에 대한 접근을 줄여줍니다.
즉, 즉시로딩은 모든 관련 데이터가 함께 필요할 때 사용하고, 지연로딩은 데이터가 필요할 때만 불러오고 싶을 때 사용하는 것이 좋습니다. 상황에 따라 적절한 로딩 전략을 선택하면, 애플리케이션의 성능을 향상시킬 수 있습니다. "

 

꼬리 질문 > 지연 로딩을 사용할 때 발생할 수 있는 문제점과 그 해결 방법에 대해 설명해주실 수 있나요?

 

  "지연 로딩(Lazy Loading)을 사용할 때 주의해야 할 문제점 중 하나는 LazyInitializationException입니다. 이 예외는 지연 로딩된 엔티티에 접근하려고 할 때, 해당 엔티티의 로딩을 위한 세션이 이미 닫혀 있는 경우에 발생합니다. 이 문제는 특히, 엔티티를 로딩한 세션의 범위를 벗어나서 엔티티에 접근할 때 자주 발생합니다.

이 문제를 해결하는 방법 중 하나는 Open Session in View(OSIV) 패턴을 적용하는 것입니다. 이 패턴은 HTTP 요청이 시작될 때 세션을 열고, 요청이 완료될 때까지 세션을 열어두어, 뷰 템플릿에서도 지연 로딩된 엔티티에 접근할 수 있게 합니다. 하지만, 이 방법은 세션을 오래 열어 두어야 하므로, 리소스 사용에 있어서 비효율적일 수 있습니다.

또 다른 해결 방법은 @Transactional 어노테이션을 사용하여 비즈니스 로직이 수행되는 동안에만 세션을 유지하는 것입니다. 이 방법은 필요한 엔티티를 서비스 계층에서 미리 로딩하여, LazyInitializationException이 발생하지 않도록 합니다.

마지막으로, Fetch Join을 쿼리에 사용하여 필요한 엔티티를 처음부터 Eager 로딩하는 방법도 있습니다. 이는 특정 엔티티를 항상 함께 사용해야 하는 경우 유용하며, LazyInitializationException을 방지할 수 있습니다.

지연 로딩은 초기 로딩 성능을 개선할 수 있는 유용한 전략이지만, 사용 시 LazyInitializationException 같은 문제에 주의해야 하며, 상황에 따라 적절한 해결 방법을 선택하는 것이 중요합니다."

 

꼬리 질문 >  지연 로딩을 사용할 때, 성능상의 이점을 최대화하기 위한 전략은 무엇이 있을까요?

 

  "지연 로딩(Lazy Loading) 전략을 채택할 때, 성능 최적화를 위해 몇 가지 중요한 전략을 고려할 수 있습니다. 첫 번째는 접근 패턴을 분석하는 것입니다. 애플리케이션에서 데이터 접근 패턴을 면밀히 분석하여, 지연 로딩이 필요한 시점과 그에 따른 최적의 로딩 전략을 결정해야 합니다. 예를 들어, 특정 엔티티에 대한 접근이 자주 발생하지 않거나, 접근 시점이 예측 가능하다면 지연 로딩을 통해 불필요한 초기 로딩 비용을 줄일 수 있습니다.

두 번째 전략은 프록시 객체나 가상 프록시의 사용입니다. ORM 프레임워크는 지연 로딩을 구현하기 위해 프록시 객체나 가상 프록시를 사용하여 연관된 엔티티의 실제 로딩을 요청이 발생하는 시점까지 연기합니다. 이를 통해 초기 로딩 시간을 단축시키고, 애플리케이션의 응답성을 향상시킬 수 있습니다.

세 번째로, N+1 문제에 대한 대비가 필요합니다. 지연 로딩을 사용할 때, 연관된 엔티티를 로딩하는 과정에서 N+1 쿼리 문제가 발생할 수 있습니다. 이를 방지하기 위해, 적절한 Fetch 전략 설정이나 배치 사이즈 조정 등을 통해 쿼리 수를 최적화할 수 있습니다. 또한, 실제 엔티티 접근이 필요한 시점에만 로딩을 수행하도록 로직을 구성하여, 불필요한 데이터베이스 접근을 최소화해야 합니다.

마지막으로, 성능 모니터링 및 최적화는 지속적인 과정입니다. 애플리케이션의 운영 환경에서 실시간으로 성능 데이터를 모니터링하고, 이를 기반으로 지연 로딩 전략을 지속적으로 조정하고 최적화하는 것이 중요합니다. 이는 애플리케이션의 성능을 지속적으로 관리하고 개선하기 위한 필수적인 접근 방식입니다.

이러한 전략들을 적절히 활용함으로써, 지연 로딩을 사용하는 동안 성능상의 이점을 극대화하고, 애플리케이션의 전반적인 효율성과 응답성을 향상시킬 수 있습니다."

 

  • Spring bean container 생성부터 스프링 종료까지의 사이클에 대해 알려주실 수 있을까요? @PostConstruct, @PreDestroy 어노테이션의 역할도 함께 알려주시면 좋습니다.

  " Spring Framework에서 빈 컨테이너는 애플리케이션의 스프링 빈(객체)을 만들고 관리하는 중심 역할을 합니다. 애플리케이션이 시작할 때부터 종료될 때까지 빈의 생명주기를 책임집니다.
애플리케이션이 시작하면, 컨테이너는 설정 파일을 읽어 빈을 생성하고, 필요한 곳에 빈을 연결해 줍니다(의존성 주입). 이때, ‘@PostConstruct’가 붙은 메서드는 빈이 만들어지고 난 후에 호출되어, 초기화 작업을 합니다.
애플리케이션이 실행되는 동안, 컨테이너는 이 빈들을 관리하다가 애플리케이션이 종료될 때 ‘@PreDestroy’가 붙은 메서드를 호출하여 빈을 안전하게 정리합니다.
간단히 말해, 스프링 컨테이너는 애플리케이션의 시작에서 끝까지 빈의 생성부터 소멸까지 관리합니다. ‘@PostConstruct’는 객체가 만들어진 직후 초기화를, ‘@PreDestroy’는 객체가 사라지기 전에 정리를 담당합니다. 이런 과정을 통해 애플리케이션의 효율적인 리소스 관리를 도와줍니다. "

 

꼬리 질문 >  @PostConstruct@PreDestroy 어노테이션을 사용할 때, 스프링의 라이프사이클 콜백과 어떻게 다르게 작동하나요?

 

  "@PostConstruct와 @PreDestroy 어노테이션은 자바 EE에서 제공하는 표준 어노테이션으로, 스프링 프레임워크에서도 이를 지원합니다. 이 어노테이션들은 각각 빈의 생명주기에서 초기화 후와 소멸 전에 필요한 작업을 정의하는 데 사용됩니다.

스프링의 라이프사이클 콜백과의 주요 차이점은 @PostConstruct와 @PreDestroy가 자바 표준을 따른다는 점입니다. 반면, 스프링은 자체적인 라이프사이클 콜백 인터페이스인 InitializingBean과 DisposableBean을 제공합니다. InitializingBean 인터페이스의 afterPropertiesSet 메서드는 프로퍼티 설정이 완료된 후에 호출되며, DisposableBean 인터페이스의 destroy 메서드는 빈이 소멸되기 직전에 호출됩니다.

@PostConstruct와 @PreDestroy 어노테이션을 사용하는 방식은 메서드에 직접 어노테이션을 선언함으로써 더 선언적이며, 별도의 인터페이스를 구현하지 않아도 되기 때문에 코드의 간결성을 유지할 수 있습니다. 또한, 이 어노테이션들은 메서드 레벨에서 작동하기 때문에, 초기화와 소멸 과정에서 필요한 로직을 더 유연하게 처리할 수 있습니다.

하지만, 스프링의 InitializingBean, DisposableBean 인터페이스를 사용하는 방식은 스프링 컨테이너에 더 밀접하게 연관되어 있으며, 스프링 특화 기능을 활용할 수 있다는 이점이 있습니다. 선택은 주로 개발자의 선호도나 특정 상황에 따라 달라질 수 있습니다.

요약하자면, @PostConstruct와 @PreDestroy는 더 넓은 자바 커뮤니티에서 사용될 수 있는 표준 방식을 제공하며, 스프링의 라이프사이클 콜백 인터페이스는 스프링에 특화된, 더 세밀한 제어가 가능한 방식을 제공합니다."

 

  • AOP, Interceptor, Filter 의 차이점, Request가 들어올때 거치는 순서, 각 역할들의 장점을 설명해주실 수 있을까요?

  " AOP, Interceptor, 그리고 Filter는 애플리케이션에서 필요한 작업을 자동으로 처리하는 세 가지 주요 기술입니다.
Filter는 웹 요청과 응답을 다루는 서블릿 필터로, 애플리케이션이 받는 HTTP 요청의 전처리와 후처리를 담당합니다. 이는 인증 검사, 로깅, 요청 데이터 수정 등의 일반적인 작업에 주로 사용됩니다. Filter는 스프링 컨텍스트 이전 단계에서 작동하여, 모든 요청에 대해 먼저 실행됩니다.
Interceptor는 스프링 MVC에서 컨트롤러가 요청을 처리하기 전과 후, 그리고 뷰가 렌더링되기 전에 특정 작업을 수행합니다. 인터셉터는 요청 검증, 로깅, 사용자 인증 등에 유용하며, 스프링 빈과의 연동이 용이하여 스프링 기반의 세부적인 제어가 가능합니다.
AOP(Aspect-Oriented Programming)는 애플리케이션 전반에 걸친 공통 기능(예: 보안, 트랜잭션 관리)을 핵심 비즈니스 로직에서 분리하여 관리합니다. AOP는 특정 조건(메서드 호출 전후, 예외 발생 시)에 자동으로 실행되는 코드를 정의할 수 있게 해줍니다.
요청이 처리되는 순서는 Filter -> Interceptor -> AOP입니다.
각각은 다음과 같은 장점을 가집니다.
Filter는 스프링과 독립적으로 모든 웹 요청에 대한 전처리와 후처리가 가능합니다.
Interceptor:는 스프링 컨텍스트 내에서 작동하여 스프링의 기능을 활용한 세밀한 작업 제어가 가능합니다.
AOP는 애플리케이션의 공통 기능을 분리하여 코드의 재사용성과 유지보수성을 향상시킵니다.
이 세 가지 기술은 스프링 애플리케이션의 다양한 단계에서 필요한 처리를 효율적으로 수행하도록 도와줍니다. "

 

꼬리 질문 >  AOP, Interceptor, Filter 간의 차이점을 이해했습니다. 그렇다면 이 세 가지 중 어떤 것을 사용할 때 프로그램의 성능에 미치는 영향을 가장 많이 고려해야 하나요?

 

  "성능 관점에서 볼 때, 이 세 가지 기술 모두 다른 상황과 요구 사항에 따라 성능에 영향을 줄 수 있습니다. 하지만, AOP의 사용이 프로그램의 성능에 가장 큰 영향을 미칠 수 있습니다. 이는 AOP가 메소드 호출의 전후, 예외 발생 등 다양한 지점에서 추가적인 로직을 실행하기 때문입니다. 특히, 공통 관심 사항을 많은 메소드에 걸쳐 적용할 경우, 이러한 추가 로직이 시스템의 전반적인 응답 시간에 영향을 줄 수 있습니다.

Filter와 Interceptor도 리소스를 소모합니다. 특히, 매 요청마다 실행되는 로직이 복잡하거나 시간이 많이 소요될 경우 성능에 영향을 줄 수 있습니다. 그러나, 이들은 주로 HTTP 요청의 사전 처리와 사후 처리에 집중되어 있기 때문에, AOP보다는 일반적으로 성능에 미치는 영향이 더 제한적일 수 있습니다.

성능을 최적화하기 위해서는 AOP를 사용할 때 특히 주의해야 합니다. 필요한 곳에만 AOP를 적용하고, 실행 시간이 긴 로직은 배경 작업으로 처리하는 것이 좋습니다. 또한, Filter와 Interceptor의 경우, 가능한 한 간단하고 빠르게 실행될 수 있도록 로직을 최적화하는 것이 중요합니다.

결국, 이 세 가지 기술을 사용할 때는 각각의 사용 사례를 명확히 이해하고, 성능 테스트를 통해 실제 영향을 평가한 후 적절한 조치를 취하는 것이 중요합니다."