[설계-디자인패턴]팩토리패턴을 사용해야하는 이유와 예시

Dev-O·5일 전
0

Spring

목록 보기
12/12
post-thumbnail

📌 개요

  • 회사에서 소프트웨어 회사에 특화된 영업관리 모듈을 설계하고 개발하는 업무를 맡았다.
    N개의 고객사의 비즈니스에 맞게 ERP시스템을 설계하고 개발해야했다.
    표준이 되는 패키지 서비스를 갖고 모든 업체의 비즈니스를 소화해야하기 때문에 무지성으로 만들면, 업체 전용으로 프로젝트가 과생산 되는 경우가 발생했다.

  • 이렇게 되면, 향후 업체가 늘어날수록 유지보수가 힘들어지고, 추가공수가 발생하는 문제가 생긴다.

  • 예를 들면, A업체는 전자계약으로 글로사인 사용하고, B업체스마트빌을 사용할 수 있다.
    각 전자계약 업체마다 API가 다르니 업체 전용프로젝트를 만들어서 전용으로 나가는 방식을 채택하게 되면 향후에는 OO전자계약을 사용하는
    회사 N개에 N개의 무지성 클론프로젝트가 생기는 것이다.

  • 이를 효율적으로 확장하기 위해 여러가지로 찾아보다가 팩토리패턴으로 패키지서비스를 설계했다.


📌 팩토리패턴

✔️ 장점

  1. 객체 생성 코드 재사용 가능
    • 객체 생성 로직을 캡슐화하여 코드 중복을 줄임.
  2. 유연성 증가
    • 클라이언트 코드가 생성 로직에서 분리되어 새로운 타입의 객체를 쉽게 추가할 수 있음.
  3. 유지보수성 향상
    • 객체 생성 로직이 한곳에 집중되어 변경이 쉬움.
  4. 결합도 감소
    • 클라이언트 코드와 객체 생성 로직 간의 의존성을 줄임.
  • 나의 경우 여러 전자계약 시스템별로 유지보수 가능하면서, 유연하게 확장해야 했다.
  • 일단, 스프링을 활용한 팩토리패턴은 DI기능을 사용하여 객체생성을 프레임워크가 관리한다. 등록기반팩토리패턴 활용에 좋다.
  • 이유는 스프링 컨텍스트에서 팩토리 빈 생성 시점에 내가 정의 해놓은 전자계약 클래스들(Bean)을 주입받기 때문이다.

✔️ 전자계약의 종류에 따른 팩토리패턴 설계구조

✔️ 팩토리 패턴의 구성 요소

  • Product: 생성할 객체의 인터페이스 또는 상위 클래스.
  • ConcreteProduct: 실제로 생성되는 객체.
  • Factory: 객체를 생성하는 클래스 또는 메서드.

✔️ 설계코드

  • 다양한 전자계약 방식(글로사인, 스마트빌 등)을 지원하는 애플리케이션에서, 계약방식에 따라 다른 객체를 생성해야 한다고 가정합니다.
/* 각 전자계약의 Product */
public interface eContract {
	void makeContract(contractModel contract); //전자계약에 필요한 변수들을 총망라한 모델 - ERP데이터를 기반으로 만들어진다.
}
/*******************************************************************/
/* 각 전자계약의 ConcreteProduct 구현체 */
@Component
public class Glosign implements eContract { //글로사인
    @Override
    public void makeContract(contractModel contract) {
    	//글로사인 api호출 로직
    }
}
@Component
public class Smartbill implements eContract { //스마트빌
    @Override
    public void makeContract(contractModel contract) {
        //스마트빌 api 호출 로직
    }
}
/*******************************************************************/
/* 각 전자계약의 Factory Bean*/
@Component
public class ContractFactory {
    private final Map<String, eContract> contractMap = new HashMap<>();

	//생성자주입 - 빌드시 스프링컨텍스트에 위의 구현체를 바탕으로 자동으로 주입
    //팩토리에 구현해놓은 전자계약 클래스들을 등록하는 과정(등록기반팩토리)
    public ContractFactory(List<eContract> eContracts) {
        for (eContract econtract : eContracts) {
            contractMap.put(econtract.getClass().getSimpleName().toLowerCase(), econtract);
        }
    }

    public eContract getEContract(String type) {
        return contractMap.get(type.toLowerCase());
    }
}
/*******************************************************************/
// 클라이언트 코드
@RestController
public class ContractController {
    private final ContractFactory contractFactory;

    public ContractController(ContractFactory contractFactory) {
        this.contractFactory = contractFactory;
    }

    @GetMapping("/contract")
    public void contract(@RequestParam String type, @RequestParam contractModel contract) {
        eContract econtract = contractFactory.getEContract(type); 
        if (payment != null) {
            econtract.makeContract(amount); //구현해놓은 전자계약 클래스에 따라 다른 api작동
        } else {
            throw new IllegalArgumentException("Unknown payment type: " + type);
        }
    }
}
/*******************************************************************/

✔️ 디자인패턴 적용의 효과

  • 간단하게 등록방식의 팩토리패턴 설계 뼈대를 만든 것을 바탕으로 각 회사 시스템에 맞게 구현하였다.
  • 실제로 다른 전자계약 시스템을 사용한다고 하면 나는 그저, 구현체 클래스를 하나더 만들어주면 끝일 뿐이다.(혹은 interface 수정) 나머지는 팩토리패턴이 알아서 맞춰서 호출해준다.
  • 정처기때 배웠던 개방폐쇄원칙(OCP)에도 좋다고 한다. 확장에는 열려있고, 수정에는 닫혀있다는 의미다.
    여러 고객사를 상대하는 패키지 서비스를 설계할 때는 이런 부분을 많이 알아두는게 좋을 것 같다.

✔️ 추가고려사항

  • 그리고 현재 아쉬운점은 다양한 전자계약시스템에 대한 디자인패턴 적용은 완료하였으나,
    하나의 전자계약이라도 회사마다 사용방식이 다르기 때문에 이는 어떻게 효율적으로 관리할 것이냐를 또 생각해봐야겠다.
profile
Being Outstanding needs Understanding🚀

0개의 댓글