오늘은 SOLID 원칙 중 DIP 원칙에 대해서 공부한 것을 정리해 보려고 합니다.
DIP : 의존성 역전 원칙
상위 모듈은 하위 모듈에 의존하지 말고, 둘 다 추상화에 의존해야 한다.
의존한다는 것은 Java 에서 구체적으로 표현하면 다음과 같습니다.
public interface ImageService {
public List<String> upload(MultipartFile[] file);
}
//@Service
@RequiredArgsConstructor
public class LocalImageService implements ImageService {
@Value("${project.upload.path}")
private String defaultUploadPath;
private String makeDir() {
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString();
//File.separator -> window 에서는 / ?
String uploadPath = defaultUploadPath+File.separator+date;
File uploadDir = new File(uploadPath);
if(!uploadDir.exists()) {
uploadDir.mkdirs();
}
return "/" + date;
}
@Override
public List<String> upload(MultipartFile[] files) {
String uploadPath = makeDir();
List<String> uploadedFilePaths = new ArrayList<>();
for(MultipartFile file : files) {
String originalFileName = file.getOriginalFilename();
//파일명 겹칠 수 있으므로 UUID 사용
String uploadFilePath = uploadPath+"/" + UUID.randomUUID().toString()+"_"+originalFileName;
File uploadFile = new File(defaultUploadPath + "/" + uploadFilePath);
uploadedFilePaths.add(uploadFilePath);
try{
file.transferTo(uploadFile);
} catch (Exception e) {
}
}
return uploadedFilePaths;
}
}
@Service
@RequiredArgsConstructor
public class CloudImageService implements ImageService {
private final S3Client s3Client;
@Value("${spring.cloud.aws.s3.bucket}")
private String bucket;
@Override
public List<String> upload(MultipartFile[] file){
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/")).toString();
List<String> uploads = new ArrayList<>();
// 내 계정에 asw 권한을 가지고 있어야 함
try{
for(MultipartFile fileItem : file){
String fileName = date + UUID.randomUUID().toString()+"_"+ fileItem.getOriginalFilename();
PutObjectResponse response = s3Client.putObject(
PutObjectRequest.builder()
.contentType(fileItem.getContentType())
//yaml 에 넣어준 것 여기에 자동으로 들어갈까?
//아니다 ...
.bucket(bucket)
.key(fileName)
.build(),
RequestBody.fromBytes(fileItem.getBytes())
);
uploads.add("/" + fileName);
}
}catch (Exception e){
e.printStackTrace();
}
return uploads;
}
}
위에 코드를 그림으로 표현하면 다음과 같습니다.
ProductSerice 가 상위 모듈이고 LocalImageService, CluodImageService 가 하위 모듈 입니다.
상위 모듈, 하위 모듈 둘다 추상화에 의존해야 한다는 말의 의미는 ProductSerice 라는 고수준 모듈과 LocalImageService, CluodImageService 모듈이 ImageService 라는 추상화(Interface)에 의존해야 한다는 것을 DIP 라고 합니다.
이건 하나의 구체적인 예시 중 하나로 DIP 원칙을 준수하는 방법은 여러가지 있습니다.
DI , 서비스 로케이터, 팩토리 .... 등등
하지만 핵심은 상위 모듈과 하위 모듈이 추상화를 통해 의존해야 한다는 것입니다.
상위 모듈 → 추상화 ← 하위 모듈
이런 형식을 만족하면서 구현해야 한다는 원칙이 DIP 입니다!
DI 도 예시를 한번 들어보면
DI(Dependency Injection) 는 객체를 외부에서 주입해주는 방법입니다.
아래 DI 를 하지 않았을 때 코드입니다.
public class CloudImageService {
public void uploadImage(String imageName) {
System.out.println("Uploading " + imageName + " to cloud storage.");
}
}
public class ProductService {
private CloudImageService imageService; // ❌ 직접 의존 (DIP 위반)
public ProductService() {
this.imageService = new CloudImageService(); // ❌ 직접 객체 생성 (강한 결합)
}
public void uploadProductImage(String imageName) {
imageService.uploadImage(imageName); // ❌ 특정 구현체에 의존
}
}
이 코드는 ProductService (상위 모듈) → CloudImageService (하위모듈) 를 바로 의존하고 있습니다.
public ProductService() {
this.imageService = new CloudImageService(); // ❌ 직접 객체 생성 (강한 결합)
}
new 를 통해서 직접적으로 구체적인 클래스에 의존하고 있습니다. 이건 DIP 위반입니다.
그렇다면 DI 를 통해 DIP 원칙을 준수하는 코드를 한번 보겠습니다.
public class ProductService {
private ImageService imageService; // ✅ 추상화(인터페이스)에 의존
public ProductService(ImageService imageService) { // ✅ 생성자 주입
this.imageService = imageService;
}
public void uploadProductImage(String imageName) {
imageService.uploadImage(imageName); // ✅ 특정 구현체를 몰라도 됨!
}
}
public class Main {
public static void main(String[] args) {
ImageService imageService = new CloudImageService(); // ✅ 클라우드 사용
// ImageService imageService = new LocalImageService(); // ✅ 로컬 저장소 사용
ProductService productService = new ProductService(imageService); // ✅ DI 적용!
productService.uploadProductImage("example.png");
}
}
이렇게 되면 ProductService (상위 모듈) → ImageService(추상화) ← CloudImageService (하위모듈)
이런 식으로 외부에서 객체를 주입해줌으로써 DIP 원칙을 준수하는 객체지향 기술이 DI 입니다!
핵심은 DIP 란 상위 모듈과 하위 모듈이 추상화를 통해 서로 의존하는 것이고
상위 모듈 → 추상화 ← 하위모듈
이런 형태를 유지하게 하는 것이 DIP 입니다!
'OOP' 카테고리의 다른 글
Spring 의 IoC 와 DI (0) | 2025.02.04 |
---|---|
MVC 패턴 (1) | 2025.01.08 |
USE CASE (1) | 2024.12.02 |