Post

중첩 클래스 스프링 빈 등록에 대한 분석

토비님의 유튜브 영상을 보던 중 내부 클래스를 스프링 컨테이너에 등록 가능한지 확인해보는 컨텐츠가 있었다. 제목을 읽고 확실히 답을 해볼 수 없었기에 영상을 기반으로 정리를 해보고자 한다.

내부 클래스 vs 정적 중첩 클래스

분석에 앞서, 기존에 헷갈렸던 개념에 대해 한번 정리를 하고자 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class NestedClass {

  @Component
  public static class StaticNestedClass{

  }

  public static void main(String[] args) {
    // 외부 클래스와 상관 없이 생성 가능!
    StaticNestedClass staticNestedClass = new StaticNestedClass();
  }
}

위의 코드에서 StaticNestedClass는 정적 중첩 클래스라고 부른다. 해당 클래스는 NestedClass라는 외부 클래스 안에 있지만 정확하게는 외부 클래스와 관계가 없다. StaticNestedClass를 생성할때는 외부 클래스가 생성되지 않았더라도 독립적으로 객체를 생성할 수 있다. 반면 내부 클래스의 경우에는 반드시 외부 클래스를 통해서만 생성이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class NestedClass {

    @Component
    public class InnerClass{

    }

    public static void main(String[] args) {
        // InnerClass 생성에 앞서서 NestedClass를 생성해야 한다.
        InnerClass innerClass = new NestedClass().new InnerClass();
    }
}

내부 클래스와 정적 중첩 클래스의 기본적인 특성에 대해서만 살펴봤다. 단어의 의미를 조금만 더 명확히 하자면 외부 클래스 안에 생성하는 모든 클래스를 중첩 클래스라 말하고 그중에서 static을 사용해 생성하는 클래스를 정적 중첩 클래스, 일반 클래스를 생성하는걸 내부 클래스(Inner Class)라고 부른다.

스프링 빈 등록 가능 여부 분석

이번 포스팅의 목적인 스프링 빈 등록 가능 여부를 테스트 해보자. 먼저 정적 중첩 클래스부터 진행해보겠다. SpringBootApplication에서 SpringContext를 받아온 후, getBean()을 통해 스프링 컨테이너에 등록된 빈을 찾는 방식으로 진행할 예정이다.

1
2
3
4
5
6
7
8
@SpringBootApplication
public class SpringTestJavaApplication {

   public static void main(String[] args) {
       ConfigurableApplicationContext ac = SpringApplication.run(SpringTestJavaApplication.class, args);
       System.out.println(ac.getBean(StaticNestedClass.class)); // 스프링 컨테이너에 등록됐는지 체크
    }
}

bean1

내가 등록한 정적 중첩 클래스가 정상적으로 스프링 빈으로 등록이 됐다. 이번에는 외부 클래스의 @Configuration을 빼고 테스트 해보겠다. 조금이라도 변수가 될 것 같은 부분을 모두 세분화해보자.

1
2
3
4
5
6
7
public class NestedClass {
 
    @Component
    public static class StaticNestedClass{
 
    }
}

다운로드

이번에도 똑같이 잘 나온다. 이를 통해 정적 중첩 클래스의 경우 예외 없이 스프링 빈으로 등록될 수 있다는 결과를 얻었다.

다음으로 내부 클래스를 테스트해보자.

1
2
3
4
5
6
7
8
9
@Configuration
public class NestedClass {
 
    @Component
    public class InnerClass{
 
    }
}

다운로드 (1)

이 경우에 스프링 빈으로 잘 등록된다. 중첩 클래스는 전부다 스프링 빈으로 잘 등록되는걸까? 마지막으로 외부 클래스의 @Configuration을 떼고 테스트해보자.

1
2
3
4
5
6
7
public class NestedClass {
 
    @Component
    public class InnerClass{
 
    }
}

다운로드 (2)

이번에는 다른 결과가 나왔다! InnerClass라는 이름으로 등록된 스프링 빈이 없다고 한다. 성공과 실패 케이스의 유일한 다른점은 외부 클래스에 @Configuration이 붙었는지 여부이다. 정말 마지막으로 @Configuration 어노테이션이 붙어야만 하는건지 확인하기 위해 @Component로 변경해보겠다.

1
2
3
4
5
6
7
8
9
@Component
public class NestedClass {
 
    @Component
    public class InnerClass{
 
    }
}

다운로드 (3)

@Component를 외부 클래스에 붙였을때도 정상 동작한다. 우리는 여기서 내부 클래스를 스프링 빈으로 등록하기 위해서는 외부 클래스도 스프링 빈으로 등록이 되야 한다라는 가정을 해볼 수 있다. 토비님의 영상에 따르면 내부 클래스는 생성될때 외부 클래스가 먼저 생성이 되있어야 한다. 내부 클래스를 스프링 빈으로 등록할때 스프링은 자동으로 외부 클래스 또한 스프링 빈으로 등록되있는지 확인하는 작업을 진행한다. 이 예제에서는 NestedClass라는 외부 클래스 또한 @Component를 통해 스프링 빈으로 등록되있다. 따라서 이 외부 클래스 스프링 빈을 통해 InnerClass 스프링 빈을 생성하는 것이다.

결론

우연히 보게 된 토비님의 영상으로부터 스프링의 기본에 대해 조금 더 딥다이브 해볼 수 있는 기회였다. 개발을 학습하면서 생각보다 ‘안다는 착각’을 하고 모호하게 넘어가는 경우가 많은 것 같다. 그런 태도를 버리고 조금이라도 확신이 들지 않으면 학습 테스트를 통해 정확한 지식을 추구하려는 자세가 개발자로서 꼭 필요한 역량이 아닐까 생각해본다.

Reference

토비님 유튜브

This post is licensed under CC BY 4.0 by the author.