-
[수정 예정] (OCP)개방 폐쇄 원칙 위배를 극복한 전략 패턴 적용 사례개발/Spring Boot 2024. 7. 12. 14:39
public interface PhotoProvider { String crawlingImageURL(String page_url); byte[] getImageBytes(String image_url); String getHostname(); }
public abstract class AbstractPhotoProvider implements PhotoProvider { @Override public byte[] getImageBytes(String image_url) { return ConnectionAction.getImage(getHostname(), image_url); } }
@Component public class HaruFilm extends AbstractPhotoProvider { @Override public String crawlingImageURL(String page_url) { Document crawledPage = JsoupAction.getDocument(page_url); return JsoupAction.returnSrcBySelectedElements(crawledPage, "div.main_cont > img"); } @Override public String getHostname() { return "haru9.mx2.co.kr"; } }
@Component public class OnePercent extends AbstractPhotoProvider { @Override public String crawlingImageURL(String page_url) { Document crawledPage = JsoupAction.getDocument(page_url); return JsoupAction.returnSrcBySelectedElements(crawledPage, "div.main_cont > img"); } @Override public String getHostname() { return "bc1.mx2.co.kr"; } }
다음은 위 클래스 구조를 활용하는 원래 코드이다.
@Component public class CrawlingComponentImpl implements CrawlingComponent { private final HaruFilm haruFilm; private final OnePercent onePercent; @Autowired public CrawlingComponentImpl(@Qualifier("haruFilm") PhotoProvider haruFilm, @Qualifier("onePercent") PhotoProvider onePercent) { this.haruFilm = (HaruFilm) haruFilm; this.onePercent = (OnePercent) onePercent; } @Override public byte[] getImageFromHaruFilm(String page_url) { String image_url = haruFilm.crawlingImageURL(page_url); return haruFilm.getImageBytes(image_url); } @Override public byte[] getImageFromOnePercent(String page_url) { String image_url = onePercent.crawlingImageURL(page_url); return onePercent.getImageBytes(image_url); } //...
기존의 코드는 @Qualifier 어노테이션을 통해 PhotoProvider를 구현한 클래스 중 haruFilm이나 onePercent라는 이름을 가진 Bean을 주입받아 각 메서드에서 주입받은 모듈들의 서비스를 사용하여 이미지를 가져오는 기능을 구현한다.
하지만 이 코드는 OCP(개방 폐쇄 원칙)을 위반하고 있다. 새로운 PhotoProvider가 추가될 때마다 클라이언트의 코드가 변경되기 때문이다. OCP 원칙에 따르면, 확장을 위해 기존의 코드는 수정 없이 새로운 기능을 추가할 수 있어야 한다. 즉, 소프트웨어는 확장에 개방적(Open)이어야 하지만, 수정에는 폐쇄적(Close)이어야 한다.
이를 해결하기 위해 전략(Stategy Pattern)을 적용했다. 전략 패턴을 사용하여 다양한 전략을 동적으로 변경할 수 있고, 클라이언트의 코드의 변경 없이 새로운 전략을 추가할 수 있다. 새로운 구조에서는 PhotoProvider의 각 구현체가 고유의 전략을 제공하고, CrawlingComponentImpl 클래스는 PhotoProvider의 이름을 기준으로 필요한 전략을 동적으로 선택하여 이미지를 가져온다. 이를 통해 PhotoProvider가 추가되더라도 클라이언트의 코드의 변경 없이 확장이 가능해진다.
@Component public class CrawlingComponentImpl implements CrawlingComponent { private final Map<String, PhotoProvider> providers; @Autowired public CrawlingComponentImpl(List<PhotoProvider> providerList) { this.providers = providerList.stream() .collect(Collectors.toMap(provider -> provider.getClass().getSimpleName().toLowerCase(), Function.identity())); } @Override public byte[] getImage(String providerName, String page_url) { PhotoProvider provider = providers.get(providerName.toLowerCase()); String image_url = provider.crawlingImageURL(page_url); return provider.getImageBytes(image_url); } }
새로운 PhotoProvider가 추가되더라도 CrawlingComponentImpl 클래스의 코드는 변경되지 않으며, 확장에 대해서는 개방적이고 변경에 대해서는 폐쇄적인 구조를 유지할 수 있었다.
이와 같은 변경을 통해 OCP 원칙을 지키는 코드를 작성할 수 있었고, 코드의 확장성과 유지보수성이 크게 향상되었음을 느낄 수 있었다.
'개발 > Spring Boot' 카테고리의 다른 글
[수정 예정] 사진을 더 안전하게 저장하기 - AES 알고리즘 적용 (0) 2024.05.27 Redis Cache를 통해 이미지 조회 성능 개선하기 (0) 2024.05.24 이미지 압축, 해제 성능 개선하기 - Snappy 도입을 통하여 (0) 2024.05.24 유지보수가 용이하도록 아키텍처 구성하기 (0) 2024.05.24 아키텍처 개선에 대한 고민 (1) 2024.05.23