당니의 개발자 스토리
자동, 수동의 올바른 실무 운영 기준 본문
자동, 수동의 올바른 실무 운영 기준
드디어 자동 의존관계 주입의 마지막 시간이네요. 마무리하는 겸 자동과 수동의 올바른 실무 운영 기준에 대해서 말씀드릴께요. 컴포넌트 스캔이랑 의존관계 자동주입 다 포함해 가지고요.

먼저, 편리한 컴포넌트 스캔이나 자동 의존관계 주입을 기본으로 사용하는 게 좋습니다.
고민이 되실 거예요. 어떤 경우에는 컴포넌트 스캔과 자동 주입을 사용하고, 어떤 경우에는 설정 정보를 통해서 수동으로 빈을 등록하고, 의존관계도 수동으로 주입하는 게 좋은지 고민이 되실 텐데,


결론부터 얘기하면 스프링이 나오고 시간이 갈수록 점점 자동으로 진화하고 있는 추세예요. 스프링은 @Component 뿐만 아니라 @Controller , @Service , @Repository 처럼 계층에 맞추어서 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원해요.
거기에다가 최근 스프링 부트가 나오면서, 스프링 부트는 컴포넌트 스캔을 기본으로 사용하고 있어요. 스프링 부트의 다양한 스프링 빈들도 조건이 맞으면 자동으로 등록하도록 설계가 되어있습니다.

그래서 설정 정보를 기반으로 애플리케이션을 구성하는 부분과 실제 동작하는 부분을 명확하게 나누는 게 객체지향에서는 사실 이상적이지만, 개발자 입장에서 보면 고역이에요. 스프링 빈을 하나 등록할 때, 컴포넌트 하나만 넣어주면 끝나는 일을 객체 하나 만들고 또 Configuration가서 @Bean 하고, 메서드 만들고 new 하고 구현 객체를 return 하고 의존관계 필요한 거 넣어주고 이러면, 이게 한 수백 개 되면 고역이죠.
그래서 일일이 빈을 적고 객체를 생성하고 하는 게 상당히 번거롭습니다. 또 관리해야 될 빈이 많아서 설정정보가 커지면, 이 설정정보를 관리하는 것 자체가 부담돼요.
그리고 결정적으로 자동 빈 등록을 사용해도 OCP, DIP를 지킬 수 있습니다.
수동 빈 등록은 사실 실용성으로 보면 훨씬 낫죠.

그러면 수동 빈 등록은 언제 사용하면 좋을까?
애플리케이션 개발해보면, 크게 비즈니스 업무 로직과 기술 지원 로직으로 나눌 수가 있어요.
이 업무 로직은 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 그리고 데이터베이스 관련된 쿼리나 JPA 이런 게 있는 데이터 계층의 로직을 처리하는 리포지토리 등이 모두 업무 로직이에요.
뭔가 업무 요구 사항이 들어와요. 그럼 고쳐야 되는 것들이 다 업무 로직이에요. 보통 비즈니스 요구 사항에 맞춰 개발할 때 추가되거나 변경돼요.
또 하나는 기술 지원 빈이 있어요. 기술적인 문제나 공통적인 관심사를 처리할 때 주로 사용이 되는데요.
예를 들어서, 데이터베이스에 연결하는 것, 그리고 공통 로그 처리 같은 경우에는 업무 로직을 지원하기 위한 하부 기술이나 공통 기술들이죠. 이렇게 두 가지로 크게 나눌 수가 있어요.

업무 로직 빈 같은 경우에는 숫자가 굉장히 많아요. 한번 개발해야 하면 컨트롤러, 서비스, 리포지토리를 다 만들어야 되잖아요. 그리고 업무 로직 빈은 어느정도 유사한 패턴이 있어요. 컨트롤러, 서비스, 리포지토리 이러한 패턴이 있죠. 이런 경우엔 자동 기능을 적극 사용하는 것이 좋습니다. 왜냐하면 너무 뻔하거든요.
원래는 자동으로 하면 파악이 어려운 경우가 많은데, 업무 로직은 보통 문제가 발생해도 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉽습니다.
그런데 기술 지원 로직 같은 경우에는 업무 로직과 비교해서 그 수가 매우 적어요. AOP 이런 거에 들어가서 공통 로그 처리를 하게 되면, 애플리케이션 전반에 걸쳐서 빈 하나가 광범위하게 영향을 주게 되거든요.
그리고 업무 로직은 문제가 발생했을 때 어디가 문제인지 업무상 여기가 문제다! 라고 딱 나오는데, 기술 지원 로직은 굉장히 광범위하게 영향을 미치기 때문에 이게 잘 되고 있는지 아닌지 조차 파악이 어려운 경우가 많아요.
그래서 이런 기술 지원 로직들은 가급적 수동 빈 등록을 사용해서 명확하게 밖으로 드러내는 게 좋습니다.

이전에 Configuration(설정 정보)은 애플리케이션 루트에 두는게 좋다고 했었죠. 그렇게 해서 애플리케이션 루트를 딱 까면 기술 지원 로직과 관련된 스프링 빈들이 나오는 거예요. 그러면 '아 얘들이 이렇게 전체적으로 업무에 영향을 미치는구나' 파악이 되겠죠.
그래서 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 딱 설정 정보에 바로 나타나게 하는 것이 유지보수 하기에 좋습니다.
경험상 기술 지원 객체를 컴포넌트 스캔으로 넣으면, 이거를 매뉴얼에 넣어놓거나, 딱 위치를 지정을 해줘야 돼요.

그런데 비즈니스 로직 중에서 다형성을 적극 활용할 때는 수동 빈 등록을 하는 게 더 유리한 경우도 있어요.
DiscountService를 다시 보면,

얘를 딱 봤어요. 우리가 모든 걸 잊고 신규 개발로 투입이 됐어요. 그러면 이 코드를 보고, "뭔가가 맵에 들어오고 호출이 되나보다. 그런데 Map에 있는 DiscountPolicy에는 뭐가 들어오지?" 라고 하면서 한 눈에 알 수 있는 방법이 없습니다. DiscountPolicy를 까봐야 그 구현체를 아는 거죠.
그런데 내가 만약에 이 부분을 별도의 설정 정보로 만들고 수동으로 등록하면,

나중에 내가

이 부분을 보면, 'DiscountPolicy에 관련된 거군' 하면서 바로 DiscountPolicyConfig 설정 정보로 들어가서 'DiscountPolicy에는 rateDiscountPolicy랑 fixDiscountPolicy가 있네' 하고 설정 정보에서 한 눈에 보입니다.
한 눈에 보인다는 거는 다음에 필요할 때도, 여기 등록하거나 빼거나 이런 걸 한 눈에 다 볼 수가 있습니다. 그래서 수동으로 빈 등록했을 때, 유지보수 하기가 더 좋은 경우도 많아요.

그래서 정리해보면, DiscountService 가 의존관계 자동 주입으로 Map<String, DiscountPolicy> 에 주입을 받는 상황을 생각해봅시다. 여기에 어떤 빈들이 주입될 지, 각 빈들의 이름은 무엇일지 코드만 보고 한번에 쉽게 파악이 안돼요.
내가 개발했으니까 지금은 크게 관계없지만, 같이 개발하다 보면 만약에 다른 개발자가 나한테 이 코드를 딱 던져서 내가 뭔가를 해야 돼요.

그러면 딱 이걸 딱 보는 순간, 추상화가 내가 할 땐 좋은데 남이 할 땐 잘못된 추상화 받으면 괴롭습니다. 딱 봤을 때 뭐가 들어 올지 파악이 안 된단 말이에요.

그러면 설정 정보를 만들어서 작업이 돼있으면 "아, 얘네들은 DiscountPolicyConfig 안에서만 무조건 등록을 하고 쓰나보다. 이렇게 두 개가 등록되어 있구나" 라는 게 딱 정리가 됩니다.
그런데 그게 아니면,

내가 직접 뭐가 들어올지 DiscountPolicy 코드를 열고 들어가봐야 돼요. (cmd + option + B)
이게 한 두개만 그렇지만 엄청 많으면 또 고민 되겠죠. 그래서 이렇게 수동 등록을 하는게 좋은 경우도 있습니다. 물론 자동 등록을 하는게 나을 수도 있어요. 딱 답이 없어요.
아무튼 이런 경우 수동 빈으로 등록하거나 또는 자동으로 하면 특정 패키지에 같이 묶어두는게 좋아요.

왜냐하면 이 DiscountPolicy에 대한 구현체들은 최소한 패키지라도 묶여 있어야 유지보수가 가능하지, 패키지 마저 나눠져있으면 유지보수하기에 최악입니다.
어쨌든 수동으로 등록하든, 컴포넌트 스캔을 쓰든, 이런 비즈니스 로직 중에서 다형성을 적극 활용할 때의 핵심은 딱 보고 이해가 되어야 합니다.

이렇게 하면 설정 정보만 봐도 한눈에 빈의 이름은 뭐고 어떤 빈들이 등록되고 주입될지 파악이 되죠. 그래도 빈 자동 등록을 사용하고 싶으면 파악하기 좋게 DiscountPolicy 의 구현 빈들만 따로 모아서 특정 패키지에 모아두는 게 좋습니다.
참고로 스프링과 스프링 부트가 자동으로 등록하는 수 많은 빈들은 예외입니다. 얘네들이 자동으로 등록하는 건 이유가 있거든요. 그래서 그대로 쓰시는게 좋고, 이런 부분들은 스프링 자체를 잘 이해하고 스프링 의도대로 사용하는게 좋아요.
예를 들어서 스프링 부트의 경우에 데이터베이스에 연결하는 DataSource같은 것도 어떻게 보면 기술 지원 로직이잖아요.
기술 지원과 관련된 스프링 빈인데, 이런 것들까지 스프링 부트가 자동으로 등록을 해줘요. 그런데 이런 부분은 데이터베이스 접속 정보를 스프링 부트가 환경 설정 파일에 적으면, 값이 들어가서 자동으로 데이터 소스가 생성되게 해주거든요. 이런 거는 잘 쓸 수 있게 이미 다 매뉴얼화 되어 있기 때문에 직접 등록한다기 보다는 스프링 부트가 제공하는 대로 그대로 쓰는 게 좋고요.
그런데 만약에 내가 '아 이걸로 좀 부족해. 스프링 부트가 제공하는 기능으로 부족해서 내가 좀 더 커스텀 해야 돼' 이런 경우에만 직접 스프링 빈으로 등록하시는 게 좋습니다.
그리고 스프링 부트나 스프링 내부적으로 빈을 등록하는 게 아니라, 내가 직접 기술 지원에 관련된 객체를 스프링 빈으로 등록해야 되면 수동으로 등록해서 명확하게 밖으로 들어내는 게 좋습니다.
그래서 정리를 해보면,

편리한 자동 등록과 @Autowired 이런 거를 기본으로 적극 활용하자!
대신에 직접 등록하는 기술 지원 객체는 수동으로 등록하자.
그리고 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자. 이 정도로 정리할 수 있겠습니다.
'스프링 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
| 인터페이스 InitializingBean, DisposableBean (0) | 2024.01.26 |
|---|---|
| 빈 생명주기 콜백 시작 (0) | 2024.01.26 |
| 조회한 빈이 모두 필요할 때, List, Map (0) | 2024.01.25 |
| 애노테이션 직접 만들기 (0) | 2024.01.25 |
| @Autowired 필드 명, @Qualifier, @Primary (0) | 2024.01.24 |