난 요즘 출퇴근길에 인프런 강의를 듣는다...^^ (출퇴근 왕복 3시간의 위엄ㅎ)
강의에서 Decorator 패턴이 나왔는데, 내 프로젝트에도 활용할 수 있을 것(유용할 것) 같아 코드를 수정하고 공유해본다.!!
(코드는 일부 각색되었읍니다)
일단 내겐 기존의 코드가 있다.
public interface OcrService {
HttpStatusCode saveAData(ARequestDto aRequestDto);
HttpStatusCode saveBData(BRequestDto bRequestDto);
}
A 데이터와 B 데이터를 저장하는 코드다.
그런데 B 데이터를 저장하는 로직이 바뀌었다.
처음에는 OCR 을 사용하려고 했는데, 수기 입력으로 변경된 것이다.
원래같으면 기존 코드를 삭제하고 새 로직을 짤 수도 있겠지만 추후에 OCR 로 이용할 가능성도 있기 때문에
기존 코드를 삭제하긴 좀 아쉬운 상황이다.
(물론 커밋 내역을 찾아서 확인하고 기존 코드를 가져올 수도 있겠지만 ... 번거롭지안은가 ㅎ)
그래서 처음에는 이렇게
public interface BService {
HttpStatusCode saveBData(BRequestDto bRequestDto);
}
B 를 저장하는 서비스를 따로 만들어 컨트롤러가 두 가지의 서비스를 이용할 수 있도록 했다.
하지만 지금!
Decorator 패턴을 배운 나는, "Decorator" 패턴을 이용해 코드를 새로 작성해보았다.
Decorator 패턴이 뭐냐면
보통 스프링에서 서비스 로직을 가진 클래스를 DI 로 이용하게 되면
Service Interface 와 그 구현 클래스를 만든 후
서비스 사용 클라이언트에서는 서비스의 인터페이스를 사용하지만,
구현 클래스에는 @Service 애노테이션을 붙여주어 이 인터페이스를 사용할 때 이 객체를 구현 객체로 사용하라! 고 알려준다.
그런데 Decorator 는, 그 서비스 구현체에서 필요한 기능이 더 추가될 때 사용한다.
인터페이스에서는 추가할 필요 없는 기능이지만 내가 특정 구현체에서 그 기능이 필요할 때 Decorator 를 만들어 기능을 추가한 후, 빈을 주입받는 것이다!!
그래서 Decorator 는 그 구현 객체와 똑같은 Service interface 를 구현하고,
ServiceImpl , Decorator 전부 똑같은 @Service 애노테이션을 추가한다.(빈을 주입받기 위해)
(하나의 인터페이스를 동시에 두 개의 객체가 구현하고, 빈을 주입받는 것임)
그 다음 그 서비스를 사용하는 클라이언트는 ServiceImpl 이 아닌 Decorator 를 사용하면 되는 것!
이것만 들으면
엥??? 그러면 하나의 인터페이스에 두 개의 구현체가 있으니 안되지 않나요???할것이다.
그 때는
@Service
@Primary
이렇게 @Primary 애노테이션을 붙여준다.
이러면 스프링이
일단 얘를 우선 순위 구현 객체로 두고, 얘가 쓰는 서비스가 있다면 그 다음 순위 구현 객체로 둔다.
그러면 스프링이 누구한테 빈을 주입해야할지 고민할 필요가 없음~ㅎㅅㅎ
@Slf4j
@Service
@Primary
@RequiredArgsConstructor
public class DataSaveDecorator implements OcrService {
private final OcrService ocrService;
private final DatabaseRepository databaseRepository;
private final DateTimeConverter dateTimeConverter;
private final UserRepository userRepository;
@Override
public HttpStatusCode saveADataDoOcr(ARequestDto aRequestDto) {
return ocrService.saveADataDoOcr(aRequestDto);
}
@Override
public HttpStatusCode saveBDataDoOcr(BRequestDto bRequestDto) throws UserNotFoundException {
return ocrService.saveBDataDoOcr(bRequestDto);
}
// B 용 새 비즈니스 로직
public HttpStatusCode saveBDataNotOcr(FormBRequestDto bRequestDto) throws UserNotFoundException {
//////
////코드
/////
}
}
이렇게 하면 기존 a 데이터 저장 로직과 b 데이터 저장 로직은 그대로 사용할 수 있고
b용 새 비즈니스 로직도 추가할 수 있음 !!! >-<
그리고 컨트롤러에서는
@Slf4j
@RestController
@RequiredArgsConstructor
public class OcrController {
private final DataSaveDecorator dataSaveDecorator;
@PostMapping("a")
public ResponseEntity<Response> a(
@RequestBody @Valid ARequestDto aRequestDto) {
dataSaveDecorator.saveADataDoOcr(aRequestDto);
return Response.ok();
}
}
이로케 데코레이터를 사용해주면 됨 ^^>
그러면 어차피 Decorator 는 Service 를 사용 중이니까 기존 서비스 로직은 그대로 사용하고
b 를 저장하는 로직이 바뀔 경우에는 Decorator 메서드만 변경해주면 됨 !!!!!ㅎ_ㅎ
OcrService 에 폼을 쓰는 B 저장 로직을 추가할까 했으나
이름도 Ocr 서비스고 OCR 용 저장 로직만 들어가는 컨셉으로 만들었기 때문에 OCR을 쓰지 않는 기능은 넣기 싫었고..
B 기능만 쓰는 서비스를 만들어 사용했지만 굳이 이런식으로 써야하나 아쉬웠는데
Decorator 패턴을 알게되고 이렇게 수정하고나니 좀 더 깔끔하고 컨셉이 확실한 코드가 만들어진 것 같다.
뿌듯뿌듯~역시 사람은 배워야해잉~
'혼자서 개발새발' 카테고리의 다른 글
Springdoc ) Spring Boot 3 이상에서는 Swagger2 대신 Springdoc(Swagger3) (1) | 2024.01.05 |
---|---|
Spring Security ) RestAPI 로 통신할 때 Session을 사용하는 자동 로그인을 사용할 때 Handling 하기 (0) | 2024.01.05 |
MAC(M2) 에 ubuntu 를 설치해서 리눅스를 환경을 이용해보자! (0) | 2023.10.19 |
JWT ) 프론트 서버와 통신하는데에 사용할 JWT 인증을 구현해보자 (0) | 2023.08.18 |
intelliJ ) POSTMAN 보다 편리하게 HTTP Test 하기 (0) | 2023.06.22 |