Spring | 스프링 특징을 사용한 프로젝트별 한 코드로 작성을 하는 방식

DoItDev·2022년 1월 20일
0
post-thumbnail

Overview

spring 프레임 워크를 사용하는 큰 이유중 하나는 ioc 와 di 를 사용하기 위함이다.

그렇기에 한 코드 베이스에서 프로젝트 별로 각각의 if 절로 비지니스 로직을 푸는 것이 아니라 spring 의 특징을 사용한 방식에 대해서 필자의 경험을 통해서 풀어 보려고 한다.

현재의 요건은 다음 과 같았다.

  1. 해당하는 코드 베이스로 a 라는 곳 그리고 b ... N 번까지 프로젝트를 진행을 한다.
  2. 로직이 실행되는 "관점" 이 아니라 해당 프로젝트가 "실행" 되면 정적으로 변경이 되어야 한다.

위의 요건대로 비지니스 로직을 풀기란 사실 쉽다. runtime 중에 단순하게 Flag 값을 주어서 if 절 혹은 switch 절로 풀 수 있다.


public void getLogic(String type){

if ("A".equals(type)){
// A관련 로직
}else {
// 다른 로직
}

}

하지만 spring 의 ioc 와 di 를 이해가 있다고 한다면, 해당 하는 로직처럼 설계를 하는 것이 아니라 조금더 쉽게 혹은 우하하게 spring 을 사용해서 풀 수 있다고 필자는 생각을 하게 되었다.

다음과 같은 상황을 아래에 좀더 자세하게 설명을 해보려고 한다.

어떻게 풀지?

spring 에서 ioc 의 경우 필자가 알기로는 Bean 이 spring boot 가 실행이 될때 Bean 으로 등록이 되는 것으로 알고 있다.

또한 메모리 관점에서 spring bean 의 경우 참조되는 metaspace 에 주소값이 존재를 하고 해당 bean 이 생성이 될떄 Heap 에서 인스턴스가 생성이 되는 것으로 알고 있다.
(생성된 인스턴스는 미리 metaspace에 올라간 참조값을 바라보게 된다)
기본적으로 싱글톤 패턴이기에 해당 사항 처럼 메모리 흐름을 가져가는 것으로 알고있다.

그렇기 때문에 비지니스 로직에서 if 절을 사용해서 로직을 만드는 것이 아니라 추상화를 시켜서 해당 사항을 풀어 보려고 한다.

먼저 이 글에 앞어서 필자가 해주고 싶은 말은 어떤 방식으로 사용을 해도 무방하다 코딩에서 정답이 없기 때문에 상황에 맞게 사용을 하면 좋다

클래스 다이어그램

public interface BigBuilder {
	int getBig();
    String getName();
}

추상화 시킬 인터페이스가 필요하다. 이 인터페이스의 경우 각각의 공통으로 사용할 기능을 넣어 둔다.

public class A implements BigBuilder{
	
    @Overrid
    public int getBig(){
    	... 재정의 ...
    }
    
    @Overrid
    public String getName(){
    	... 재정의 ...
    }
}

public class B implements BigBuilder{
	
    @Overrid
    public int getBig(){
    	... 재정의 ...
    }
    
    @Overrid
    public String getName(){
    	... 재정의 ...
    }
}

A, B 라는 두 클래스를 만들었고 BigBuilder 상속을 받아서 각각의 메소드를 재정의(오버라이드) 시켜 주었다. 각각의 프로젝으 별로 재정의 하였다.

java에서는 각각 팩토리 클래스를 정의를 해준뒤 객체 생성의 메소드를 이용해서 각각의 클래스 별로 인스턴스화 해주었다

해당하는 클래스 다이어그램의 큰 장점의 경우 의존성을 낮추고,결합을 낮게하고 확장성을 높여준다 구현객체들 사이에 추상화를 제공한다.

단점으로 생성할 객체가 늘어날때 마다 클래스를 추가해주어야 한다.

Bean 주입 단계

하지만 스프링에서는 Bean 주입이 있기 때문에 빈주입을 통해서 사용하는법을 알려주려고 한다.

@Configuration
public class BigBuilderConfiguration {

    @Profile("A")
    @Bean("bigBuilder")
    public BigBuilder bigBuilder() {
        return new A();
    }

    @Profile("B")
    @Bean("bigBuilder")
    public BigBuilder bigBuilder() {
        return new B();
    }

}

configuration 클래스를 설정을 해주었다. bigBuilder 라는 빈에 A 일때 혹은 B일때 각각 다른 참조 클래스를 주입을 해준다.

이렇게 빈주입을 해준 뒤 field 에서 @Autowired , @requiredargsconstructor 등 으로 사용이 가능하다 하지만 필자의 경우 static 으로 올려놓고 사용을 하려고 했다.

트러블슈팅

단 여기서 중요한 점의 경우 @Profile 를 통해서 미리 spring 이 실행이 될 때 각각의 active profile 로 정의를 해주어야 된다. (정적 비지니스 로직)

만약 해당 사항의 경우 name 이 bigbuilder 라고 정의가 되어 있기에 spring 에서 bean 으로 올리는 단계에서 어떤 bean 을 등록을 해줄때 찾지 못하는 현상이 발생이 된다.

bean 을 정의를 해줄 때에는 key 값과 value 값 의 Map 형태로 Bean 을 등록해주는 것으로 필자는 알고 있다.
그렇기 때문에 동일한 key 값을 갖는다고 한다면, bean definition 단계에서 에러가 발생이 되고 spring 이 정상적으로 동작을 하지 않게 된다. 그렇기 때문에 Profile 을 사용을 통해서 외부 주입을 하는 방식으로 지정을 하였다.

물론 Runtime 시에 Bean 을 변경하는 케이스가 아니기에 해당 방법을 사용하기로 하였다

Service Layer

@Service
public class BigService {

    private final BigBuilder digBuilder;

	public BigService(BigBuilder digBuilder){
    	this.digBuilder = digBuilder;
    }
}

서비스 레이어에서 사용을 할 때는 interface 을 통해서 주입을 해준다. interface 로 주입을 하는 이유는 service layer 에서 코드 변경 없이 사용함이 위함이다. 해당 코드를 만약 Class 로 지정을 해준다고 한다면 해당 프로젝트들이 변경을 할 때 마다 코드 수정이 이루어지게 된다.

그렇기 때문에 interface 로 주입을 해준다면, Bean 을 DI 할 때 Bean Name 기준으로 Bean DI 로 해주기 때문에 해당하는 방식으로 주입을 해주어 사용해야 SOLID 패턴에서 리스코프치환 원칙에 위배 되지 않게 되고 OOP 에서 해당하는 특징중 다형성을 통해서 확장을 시킬 수 있다고 필자는 생각 든다.

profile
Back-End Engineer

0개의 댓글