팀프로젝트를 하면서 jpa를 활용해 db에서 데이터를 가져올 때 엔티티에서 다른 엔티티를 참조하는 경우 lazy를 활용하였는데, lazy로 설정된 참조 엔티티의 경우 프록시 객체가 임시로 주입되고 실제로 참조 엔티티를 사용할 경우에 쿼리를 한번 더 실행해 참조 엔티티를 주입하는 방식이다.
이와 마찬가지로 스프링에서도 @Lazy와 @Eager가 있는데 두 어노테이션이 어떤 차이가 있는지 확인해보자.
| Lazy Initialization | Eager Initialization | |
|---|---|---|
| 초기화 시점 | 빈이 어플리케이션에서 처음 사용될 때 | 어플리케이션이 시작할 때 |
| 기본값 | X | O |
| 적용방법 | @Lazy @Lazy(value=true) | 따로 명시하지 않아도 됨 또는 @Lazy(value=false) |
| 초기화 에러 시점 | runtime exception발생 | 어플리케이션이 실행되지 않음 |
| 사용빈도 | 거의 사용되지 않음 | 대부분 eager사용 |
| 메모리 사용량 | 빈이 초기화되기 전까지 메모리를 덜 차지함 | 비교적 많은 메모리 사용 |
| 권장 | 거의 사용할 일이 없음 | 대부분 @Eager를 사용 |
@Component
@Lazy
class ClassB {
private ClassA classA;
public ClassB(ClassA classA) {
System.out.println("Some Initialization logic");
this.classA = classA;
}
public void doSomeThing() {
System.out.println("do something");
}
}
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
public static void main(String[] args) {
try (var context =
new AnnotationConfigApplicationContext
(LazyInitializationLauncherApplication.class)) {
// System.out.println("Initialization of context is competed");
//
// context.getBean(ClassB.class).doSomeThing();
}
}
}


@Component
class ClassB {
private ClassA classA;
public ClassB(ClassA classA) {
System.out.println("Some Initialization logic");
this.classA = classA;
}
public void doSomeThing() {
System.out.println("do something");
}
}
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
public static void main(String[] args) {
try (var context =
new AnnotationConfigApplicationContext
(LazyInitializationLauncherApplication.class)) {
// System.out.println("Initialization of context is competed");
//
// context.getBean(ClassB.class).doSomeThing();
}
}
}

이것으로 @Lazy와 @Eager의 차이점에 대해서 알아보았다.
위에서 확인한 것처럼 @Lazy는 실무에서 대부분 사용할 일이 없으므로 특별한 경우가 아니라면 @Eager를 사용하자.