[Boostcourse] 풀스택/웹 프로그래밍(풀스택)

Spring Core : Java Config 사용한 IoC & DI { @Configuration, @ComponentScan, @Component, @Autowired }

Ben의 프로그램 2023. 7. 22. 23:15
728x90
수업목표
이번 시간에는 Java Config 와 Annotation 을 이용해 스프링에서 사용하는 빈을 정의하고 DI 하는 방법에 대해서 알아보겠습니다. 

 

Java Config : @Configuration & @Bean 활용하여 Config 파일 생성하기
이전 시간에 위와 같은 XML 파일을 이용하여 Bean 파일들에 대한 설정을 스프링에게 알려주었습니다. 이것을 Java Configuration 과 Annotation 들을 활용하여 설정하는 법에 대해서 공부를 해보려고 합니다. 

ApplicationConfig 라는 자바 클래스를 생성하고 그 위에 바로 @Configuration 이라는 Annotation 을 활용하여 Config 클래스라는 것을 명시해줍니다. 

그 다음에 Bean 을 등록하게 될 텐데요. Bean 을 등록할 때는 @Bean 이라는 어노테이션을 활용합니다. XML 파일을 자주 사용하지 않은 사람들은 XML 파일 자체가 생소할 수 있는데요. Bean 을 등록하는 방법도 우리에게 익숙한 방법인 메서드를 만드는 것처럼 Bean 을 등록할 수 있게 되었습니다.

어떤 메서드인지 살펴보겠습니다. 해당 메서드를 호출하면 Car 객체를 만들고 해당 객체의 setEngine 메서드를 주입받은 Engine 객체를 넣어서 실행합니다. 그리고 이렇게 설정된 Car 객체를 리턴시키는 것이 car( ) 메서드입니다. 

자바를 사용하던 분들에게는 익숙하게 접근 가능합니다. Engine 을 생성하는 Bean 도 생성해보겠습니다. Engine( ) 빈은 단순하게 Engine 객체를 생성해서 리턴해줍니다. 

한번 정리를 하고 이어서 넘어가겠습니다. 지금 클래스와 메서드 위에 @Bean, @Configuration 같은 것들은 Annotation 이라고 부르는데요. jdk 5 버전부터 지원이 되기 시작하였습니다. 그것보다 낮은 버전에서는 사용할 수 없다는 의미입니다. Annotation 이라는 말 자체는 '주석'이라는 의미이지만 자바 Annotation 은 특수한 의미를 부여하는 역할을 수행합니다. 이런 특수한 의미는 컴파일 혹은 런타임시 해석이 될 수 있습니다. 스프링은 설정을 위해서 다양한 Annotation 을 제공합니다. 그 중에서 @Configuration 은 스프링 설정 Annotation 이라고 이해할 수 있습니다. 자바는 이런 @Configuration 같은 Annotation 을 읽어들여서 CoI 와 DI 를 구현하게 됩니다. 여기서 중요한 점이 하나 더 있는데요.  @Configuration Annotation 이 붙은 클래스를 인식하면 내부의 @Bean 이 붙은 메서드들을 자동으로 실행을 시킵니다. @Bean 메서드들은 모두 반환 객체를 가지고 있는데요. 반환된 객체들을 Singleton 방식으로 관리를 하게 해줍니다. 

 

 

 

Java Config : @Configuration & @Bean 활용하여 Bean 공장 생성하기
이번에는 우리가 설정한 설정 파일인 ApplicationConfig 파일을 실제로 실행시켜주는 자바 파일을 하나 생성해보겠습니다. Bean 을 생성해주는 공장 파일(Context)을 만든다는 의미입니다. 

ApplicationContextExam03 파일을 만든 이후에 ApplicationContextExam02 에서 작성한 코드를 그대로 가져옵니다. 하는 것은 동일한데 약간의 수정만 하면 되기 때문인데요. 위의 코드에서 이전에는 xml 파일에 작성한 <Bean> 태그 정보를 읽어오기 위해서 ClassPathXmlAPplicationContext 메서드를 활용했었는데요. 이제는 Annotation 을 활용하여 작성을 하였기 때문에 다른 메서드를 사용한다는 차이점이 있습니다. 다음과 같이 코드를 수정합니다.

AnnotationConfigApplicationContext( ) 메서드를 이용해서 Xml 파일이 아니라 ApplicationConfig.class 파일을 읽어들여서 ApplicatoinContext(공장) 객체를 생성하였습니다. 이 공장 객체를 통해서 Bean 을 읽으면 되는 거죠. 여기서 알고 있어야 하는 것은 공장이 생성되면서 @Bean Annotation 을 가진 메서드들을 싱글톤으로 생성하여 갖고 있는 상태가 됩니다. 즉, Bean 들을 공장은 이미 갖고 있다는 것이죠. 그리고 사용자가 getBean 으로 요청했을 때 알 맞는 결과를 사용자에게 반환하게 됩니다. 

여기서 한 가지만 더 수정을 해주어야 하는데요. 현재 Bean 의 이름, 즉 @Bean Annotation 을 갖고 있는 메서드 이름이 Bean 의 이름인데요. getBean( ) 메서드의 인자로 "c" 가 아니라 "car" 를 넣어줘야 정상적으로 공장이 생성하여 갖고 있는 Bean 을 꺼내오게 될 겁니다. 

getBean( ) 메서드의 인자를 car 로 수정해주고 코드를 실행시켜 보겠습니다. 

정상적으로 동작하는 것을 볼 수 있습니다. 

그런데, 메서드의 이름을 정확하게 가져오는 것은 오타가 발생할 수도 있고 무엇보다 귀찮습니다. 이럴 경우에는 getBean( ) 메서드의 파라미터로 요청하는 클래스 타입을 넣어주어도 괜찮습니다. Car.class 이런 식으로 말이죠. 

getBean( ) 메서드의 인자로 요청하는 클래스 타입을 넣어주게 되면 위와 같이 @Bean 메서드의 이름이 바뀌어도 정상적으로 동작하게 됩니다. 왜 이렇게 동작하냐면 ApplicationContext 객체(공장)가 관리하는 것 중 Car 객체에 해당하는 것을 가져오기 때문에 이름과는 상관이 없기 때문입니다. 

그런데 여기서 조금 더 살펴보면 좋은 부분이 c.setEngine(e) 이 부분입니다. Engine e 라는 매개변수를 갖는 cor( ) 메서드에게 Engine e 에 해당하는 인자를 준 적이 없는데, 어떻게 이 코드가 동작하는 것일까요? 이런 것이 가능한 이유는 @Bean Annotation 을 갖고 있는 메서드의 리턴 값을 모두 먼저 받은 다음에 저장하고 있다가 저런 식으로 매개변수로 요청을 하면 저장되어 있는 값 중에 해당하는 객체가 있다면 그 객체를 인자로 자동으로 넣어주고 있기 때문에 이런 코드가 동작 가능 했던 것입니다. 

 

 

Java Config : @Component & @Autowired 어노테이션 활용하여 Bean 공장 생성하기
방금 작성한 ApplicationContextExam03 공장보다 더 편리한 작성 방법이 있습니다. 

ApplicationConfig2 라는 클래스를 하나 우선 만들겠습니다. 이 클래스에 @Configuration 어노테이션을 주어서 Config 임을 알려줍니다. 그 다음에 @ComponentScan 어노테이션을 추가해주는데, '알아서 너가 어노테이션을 읽어서 등록해줘' 라는 의미를 갖습니다. @ComponentScan 어노테이션을 사용하기 위해서는 반드시 패키지 명을 알려주어야합니다. @ComponentScan 을 할 현재 작업중인 패키지를 알려주는 거죠. @Component("kr.or.connect.diexam01") 로 작성하였습니다. 이때 @ComponentScan 에서 읽어들이는 Annotation 은 Controller & Service & Repository & Component 이고 이런 어노테이션들을 읽어들여서 해당 어노테이션을 갖고 있는 모든 객체들을 Bean 으로 등록을 시켜줍니다. 


@ComponentScan 이 어떻게 동작하는지 알기 위해서 Car 클래스와 Engine 클래스에 @Conponent 어노테이션을 줘보겠습니다. 

그런 다음 Car 클래스의 setEngine( ) 메서드를 삭제합니다. 그 대신 Engine v8 변수에 @Autowired 를 붙여줍니다. @Autowired 어노테이션은 '너가 알아서 Engine 타입의 객체가 생성되어 있는 것이 있으면 알아서 여기에 주입시켜줘' 를 의미합니다. @Autowried 가 알아서 해주기 때문에 Setter 메서드는 굳이 필요하지 않게 되었습니다. 

 

Java Config : @Component & @Autowired 활용한 Bean 공장 사용하기
이제 수정한 Annotation 을 사용하는 공장을 사용하는 파일을 만들겠습니다. ApplicationContext 공장을 사용하는 Exam04 파일을 생성하는데, 이전에 생성한 Exam03 과 큰 차이는 당연히 없겠죠. 인자값만 ApplicationConfig2.class 로 수정합니다. 

해당 코드를 실행시키면 다음과 같은 오류가 발생하는데요. 현재 @Configuration 어노테이션이 붙은 파일이 ApplicationConfig 와 ApplicatoinConfig2 로 2개가 있기 때문에 발생하는 오류입니다. 간단한 해결하는 방법은 안쓰는 @Configuration 을 삭제하면 됩니다. 다른 방법도 있습니다. 

우선 그 전에 알아두어야 하는 것이 있습니다. @ComponentScan 을 통해서 @Component 를 파악하면 클래스 제목의 첫글자를 소문자로 바꾼 것을 id 로 갖게 됩니다. 그리고 id 를 직접 정해줄 수도 있습니다. 이렇게 이름을 정해주면 문제가 발생하지 않게 됩니다. 

다시 실행을 해보면 정상적으로 동작하는 것을 확인할 수 있습니다. 

 

 

@Service, @Controller, @Repository, @Component 어노테이션들의 역할
Spring 에서 Service, Controller, Repository, Component 어노테이션은 Bean 에 붙이는 어노테이션들입니다. 해당 Bean 이 수행하는 일이 무엇이냐에 따라서 어노테이션을 다르게 붙여주는 것이라고 이해해주시면 됩니다. 구분하려고 말이죠! 이런 어노테이션이 붙어있는 객체들을 @ComponentScan 을 이용하면 해당 객체들을 읽어내서 메모리에 올리고  DI를 주입하도록 합니다. 

 

@ComponentScan 과 @Component 이 있는데도 @Bean 어노테이션을 사용하는 이유
Spring 에서 Service, Controller, Repository, Component 어노테이션이 붙어있지 않은 객체들은 @Bean 이라는 어노테이션을 통해서 직접 생성해주는 방식으로 클래스를 관리할 수도 있습니다. 이렇게 @ComponentScan 을 사용하면 더 편리한데 왜 @Bean 어노테이션을 활용하는 방법을 배웠을까요? 그 이유는 @ComponentScan 은 약속된 어노테이션이 붙은 것들만 읽어오게 됩니다. 그런데, 나중에  Spring JDBC 등 다른 라이브러리가 갖고 있는 객체들을 사용하였을 때는 그 라이브러리를 열어서 어노테이션을 붙일 수는 없는 문제가 발생합니다. 그럴 때는 @Bean 어노테이션을 활용하여 해당 Bean 을 등록하면 편리하게 사용가능합니다. 

이렇게 Java config 를 사용하여 IoC와 DI를 사용해보았습니다. 

 

출처 : boostcourse 웹 프로그래밍(풀스택) 
https://www.boostcourse.org/web316/lecture/20655?isDesc=false