데코레이터 패턴을 사용하여 우리의 기술적 이슈를 해결한 방법

Posted by 이정근 on November 24, 2018

원문은 https://dzone.com/articles/how-i-used-decorator-pattern-to-solve-my-tech-issu 입니다.

우리의 코드베이스는 수만 개의 클래스가 있는 거대한 코드베이스를 가진 하이브리드 모롤리틱 접근법을 따랐다. 코드베이스를 모두 빌드하면 의존성이 제거된 5 개의 주요 JAR 빌드파일이 생성된다.

AccountsManager 모듈을 작업중였고 , 또 다른 모듈인UpgradeManager 을 통하여 AccountsManager 을 비롯한 모든 모듈이 새롭게 전달된 RPM 경로에 의하여 자체 업그레이드를 허용할 수 있게 하였다. (그 외에도 이번글의 주제를 벗어난 수십가지 여러가지 일들이 이루어진다.)

UpgradeManager 가 바라보는 UpgradeServiceImpl

class UpgradeServiceImpl implements UpgradeService {
	// So many things here 
	public UpgradeStatus upgrade(Spec spec){
		//some more things here 
		initiateUpgrade();
	}
}

AccountsManager 는 rest 컨트롤러이다.

@RestController("/accounts")
class AccountsManagerUpgrade {
	@Autowired UpgradeService upgradeService;
	
  //So many things here 
	
  @PostMapping("/upgrade")
	public void upgrade(Spec spec) { // 새로운 버전의 RPM 경로 등 정보
		// 여기서 검증 등의 동작을 한다.
		return upgradeService.upgrade();
	}
}

문제상황

우리는 UpgradeManager 은 업그레이드가 완료됬다고 전달하기 전에 더 많은 조건을 충족해야 해서 UpgradeService 내부에 더 많은 로직을 추가해야했다.

우리는 두가지 방법을 시도하였다. 첫째로 UpgradeServiceOptionalBean 을 추가하였고 Bean은 오직 모듈안에서 생성되고 로딩하였다. 이것은 확실히 동작하였지만 알다시피 다른 사람의 코드를 만지는 것이 항상 좋은 방법은 아닙니다.

그래서, 원래의 UpgradeService 는 전혀 변경하지 않는 데코레이트적 디자인을 도입하였다. 하지만 우리는 우리식대로 그것을 구현해 보았다. 좀 더 자세히 설명하겠다.

단계 1

우리는 모듈안에 UpgradeService 를 명시적 복제(메소드 만) 하는 새로운 클래스를 생성하였다.

class AccountsManagerUpgradeStatusService {
	public UpgradeStatus upgrade(Spec spec) {
    
	}
}

단계 2

그 다음으로는 새로운 클래스의 의존성을 원래의 UpgradeService 에 추가하였고 UpgradeService 에서 원래의 업그레이드 메소드를 호출하였다.

class AccountsManagerUpgradeStatusService {
  @Autowired UpgradeService upgradeService;
  public UpgradeStatus upgrade(Spec spec) {
    upgradeService.upgrade(spec);
  }
}

이제, 아래와 같이 RestController 에서 새로운 AccountsManagerUpgradeStatusService 로 원래의 UpgradeService 를 안전하게 교체할 수 있게 되었다.

@RestController("/accounts")
class AccountsManagerUpgrade {
	//@Autowired UpgradeService upgradeService; - removed this
	@Autowired AccountsManagerUpgradeStatusService upgradeService;
  
  //added this
	//So many things here 
	@PostMapping("/upgrade")
	public void upgrade(Spec spec) { //spec contains path of new RPM , version etc
		// 여기서 검증 등의 동작을 한다.
		return upgradeService.upgrade();
	}
}

단계 3

새로운 AccountsManagerUpgradeStatusService 에서 우리는 요구사항이 있을 때마다 추가가 가능해졌다.

class AccountsManagerUpgradeStatusService {
	@Autowired UpgradeService upgradeService;
	public UpgradeStatus upgrade(Spec spec) {
		//we can do many things her now
 		UpgradeStatus status = upgradeService.upgrade(spec);
 		//do some more validation/RPM upgrade check , here and return the status
 		return status;
 	}
}

보다시피, 상태를 확인하고 원래의 Upgrade 를 건드리지 않고 업그레이드가 완료되면 더 많은 검증이나 체크를 안전하게 추가할 수 있게 되었다.

즐거운 코딩하시길!