Files
java-examples/model-mapper/README.md
Elex c1bbaac367 ```diff
--- a/buildSrc/src/main/kotlin/elex-application.gradle.kts
+++ b/buildSrc/src/main/kotlin/elex-application.gradle.kts
@@ -1,24 +0,0 @@
-/*
- * Examples for Java
- *
- * Copyright (c) 2021. Elex. All Rights Reserved.
- * https://www.elex-project.com/
- */
-
-
-plugins {
-	id("elex-java")
-	application
-}
-
-tasks.jar {
-	manifest {
-		attributes(mapOf(
-			"Implementation-Title" to project.name,
-			"Implementation-Version" to project.version,
-			"Implementation-Vendor" to "ELEX co.,pte.",
-			"Main-Class" to application.mainClass,
-			"Automatic-Module-Name" to "com.elex_project.${project.name}"
-		))
-	}
-}
--- a/buildSrc/src/main/kotlin/elex-base.gradle.kts
+++ b/buildSrc/src/main/kotlin/elex-base.gradle.kts
@@ -1,83 +0,0 @@
-/*
- * Examples for Java
- *
- * Copyright (c) 2021. Elex. All Rights Reserved.
- * https://www.elex-project.com/
- */
-
-plugins {
-	java
-}
-
-group = "com.elex-project"
-version = "1.0-SNAPSHOT"
-description = ""//todo
-
-repositories {
-	maven {
-		url = uri(project.findProperty("repo.url") as String)
-	}
-}
-
-java {
-	withSourcesJar()
-	withJavadocJar()
-	sourceCompatibility = org.gradle.api.JavaVersion.VERSION_11
-	targetCompatibility = org.gradle.api.JavaVersion.VERSION_11
-}
-
-configurations {
-	compileOnly {
-		extendsFrom(annotationProcessor.get())
-	}
-	testCompileOnly {
-		extendsFrom(testAnnotationProcessor.get())
-	}
-}
-
-tasks.jar {
-	manifest {
-		attributes(mapOf(
-			"Implementation-Title" to project.name,
-			"Implementation-Version" to project.version,
-			"Implementation-Vendor" to "ELEX co.,pte."
-		))
-	}
-}
-
-tasks.compileJava {
-	options.encoding = "UTF-8"
-}
-
-tasks.compileTestJava {
-	options.encoding = "UTF-8"
-}
-
-tasks.test {
-	useJUnitPlatform()
-}
-
-tasks.javadoc {
-	if (JavaVersion.current().isJava9Compatible) {
-		(options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
-	}
-	(options as StandardJavadocDocletOptions).encoding = "UTF-8"
-	(options as StandardJavadocDocletOptions).charSet = "UTF-8"
-	(options as StandardJavadocDocletOptions).docEncoding = "UTF-8"
-
-}
-dependencies {
-	//implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
-	implementation("org.slf4j:slf4j-api:2.0.6")
-	implementation("org.jetbrains:annotations:24.0.0")
-
-	implementation("com.elex-project:abraxas:4.11.0")
-
-	compileOnly("org.projectlombok:lombok:1.18.26")
-	annotationProcessor("org.projectlombok:lombok:1.18.26")
-	testAnnotationProcessor("org.projectlombok:lombok:1.18.26")
-
-	implementation("ch.qos.logback:logback-classic:1.4.5")
-	testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
-	testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
-}
--- a/buildSrc/src/main/kotlin/elex-library.gradle.kts
+++ b/buildSrc/src/main/kotlin/elex-library.gradle.kts
@@ -1,95 +0,0 @@
-/*
- * Examples for Java
- *
- * Copyright (c) 2021. Elex. All Rights Reserved.
- * https://www.elex-project.com/
- */
-
-plugins {
-	id ("elex-java")
-	`java-library`
-	`maven-publish`
-}
-
-publishing {
-	publications {
-		create<MavenPublication>("mavenJava") {
-			from(components["java"])
-			pom {
-				// todo
-				name.set(project.name)
-				description.set(project.description)
-				url.set("https://")
-				inceptionYear.set("2021")
-				properties.set(mapOf(
-					"myProp" to "value",
-					"prop.with.dots" to "anotherValue"
-				))
-				organization {
-					name.set("Elex co.,Pte.")
-					url.set("https://www.elex-project.com/")
-				}
-				licenses {
-					license {
-						// todo
-						name.set("BSD 3-Clause License")
-						url.set("licenseUrl")
-						comments.set("")
-					}
-				}
-				developers {
-					developer {
-						id.set("elex")
-						name.set("Elex")
-						url.set("https://www.elex.pe.kr/")
-						email.set("developer@elex-project.com")
-						organization.set("Elex Co.,Pte.")
-						organizationUrl.set("https://www.elex-project.com/")
-						roles.set(arrayListOf("Developer", "CEO"))
-						timezone.set("Asia/Seoul")
-						properties.set(mapOf("" to ""))
-					}
-				}
-				contributors {
-					contributor {
-						name.set("")
-						email.set("")
-						url.set("")
-					}
-				}
-				scm {
-					// todo
-					connection.set("scm:git:https://github.com/my-library.git")
-					developerConnection.set("scm:git:https://github.com/my-library.git")
-					url.set("https://github.com/my-library/")
-				}
-			}
-		}
-	}
-
-	repositories {
-		maven {
-			name = "mavenElex"
-			val urlRelease = uri(project.findProperty("repo.release.url") as String)
-			val urlSnapshot = uri(project.findProperty("repo.snapshot.url") as String)
-			url = if (version.toString().endsWith("SNAPSHOT")) urlSnapshot else urlRelease
-			// Repository credential, Must be defined in ~/.gradle/gradle.properties
-			credentials {
-				username = project.findProperty("repo.username") as String
-				password = project.findProperty("repo.password") as String
-			}
-		}
-		maven { //todo
-			name = "mavenGithub"
-			url = uri("https://maven.pkg.git.elex-project.com/elex/tmpl-java-library")
-			credentials {
-				username = project.findProperty("github.username") as String
-				password = project.findProperty("github.token") as String
-			}
-		}
-	}
-}
-
-dependencies {
-
-}
--- a/buildSrc/src/main/kotlin/elex-war.gradle.kts
+++ b/model-mapper/build.gradle.kts
@@ -6,10 +6,9 @@
  */

 plugins {
-	id ("elex-java")
-	war
+    id("elex-java")
 }

 dependencies {
-
+    implementation("org.modelmapper:modelmapper:3.+")
 }
--- /dev/null
+++ b/model-mapper/logback.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Examples for Java
+  ~
+  ~ Copyright (c) 2021. Elex. All Rights Reserved.
+  ~ https://www.elex-project.com/
+  -->
+
+<configuration>
+
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<root level="TRACE">
+		<appender-ref ref="CONSOLE" />
+	</root>
+</configuration>
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,5 +6,4 @@
  */

 plugins {
-    id("com.github.ben-manes.versions") version "0.39.0"
 }
--- /dev/null
+++ b/model-mapper/src/main/java/kr/pe/elex/examples/User.java
@@ -0,0 +1,12 @@
+package kr.pe.elex.examples;
+
+import lombok.Builder;
+import lombok.Data;
+@Builder
+@Data
+public class User {
+	private long id;
+	private String name;
+	private int age;
+	private Address address;
+}
--- a/buildSrc/src/main/kotlin/elex-java.gradle.kts
+++ b/buildSrc/src/main/kotlin/elex-java.gradle.kts
@@ -1,14 +1,75 @@
--/*
- * Examples for Java
- *
- * Copyright (c) 2021. Elex. All Rights Reserved.
- * https://www.elex-project.com/
- */
-
 plugins {
-	id ("elex-base")
+	java
+	//id("com.github.ben-manes.versions") version "0.39.0"
+}
+
+group = "com.elex-project"
+version = "1.0-SNAPSHOT"
+description = ""//todo
+
+repositories {
+	maven {
+		url = uri(project.findProperty("repo.url") as String)
+	}
+}
+
+java {
+	sourceCompatibility = org.gradle.api.JavaVersion.VERSION_17
+	targetCompatibility = org.gradle.api.JavaVersion.VERSION_17
+}
+
+configurations {
+	compileOnly {
+		extendsFrom(annotationProcessor.get())
+	}
+	testCompileOnly {
+		extendsFrom(testAnnotationProcessor.get())
+	}
+}
+
+tasks.jar {
+	manifest {
+		attributes(mapOf(
+			"Implementation-Title" to project.name,
+			"Implementation-Version" to project.version,
+			"Implementation-Vendor" to "ELEX co.,pte."
+		))
+	}
+}
+
+tasks.compileJava {
+	options.encoding = "UTF-8"
 }

+tasks.compileTestJava {
+	options.encoding = "UTF-8"
+}
+
+tasks.test {
+	useJUnitPlatform()
+}
+
+tasks.javadoc {
+	if (JavaVersion.current().isJava9Compatible) {
+		(options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
+	}
+	(options as StandardJavadocDocletOptions).encoding = "UTF-8"
+	(options as StandardJavadocDocletOptions).charSet = "UTF-8"
+	(options as StandardJavadocDocletOptions).docEncoding = "UTF-8"
+
+}
 dependencies {
+	//implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
+	implementation("org.slf4j:slf4j-api:2.0.6")
+	implementation("org.jetbrains:annotations:24.0.0")

+	implementation("com.elex-project:abraxas:4.13.0")
+
+	compileOnly("org.projectlombok:lombok:1.18.26")
+	annotationProcessor("org.projectlombok:lombok:1.18.26")
+	testAnnotationProcessor("org.projectlombok:lombok:1.18.26")
+
+	implementation("ch.qos.logback:logback-classic:1.4.5")
+	testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
+	testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
 }
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -15,6 +15,7 @@
     "xml", "jackson", "jsoup", "markdown", "network", "httpd",
     "properties", "serial-io",
     "mustache", "thymeleaf", "locale", "quartz", "sysinfo",
-    "imaging", "stream", "sound", "midi", "gson", "security"
+    "imaging", "stream", "sound", "midi", "gson", "security",
+    "model-mapper"
 )
 include("feed")
--- /dev/null
+++ b/model-mapper/src/main/java/kr/pe/elex/examples/Example.java
@@ -0,0 +1,24 @@
+package kr.pe.elex.examples;
+
+import org.modelmapper.ModelMapper;
+
+public class Example {
+	public static void main(String... args){
+		User alice = User.builder()
+				.id(100).age(34)
+				.name("Alice")
+				.address(Address.builder().city("Emerald City").street("Main Street")
+						.build())
+				.build();
+
+		ModelMapper modelMapper = new ModelMapper();
+		modelMapper.getConfiguration().setSkipNullEnabled(true);
+
+		UserDto userDto =modelMapper.map(alice, UserDto.class);
+
+		System.out.println(userDto.toString());
+
+		User2Dto user2Dto = modelMapper.map(alice, User2Dto.class);
+		System.out.println(user2Dto.toString());
+	}
+}
--- /dev/null
+++ b/model-mapper/src/main/java/kr/pe/elex/examples/AddressDto.java
@@ -0,0 +1,9 @@
+package kr.pe.elex.examples;
+
+import lombok.Data;
+
+@Data
+public class AddressDto {
+	private String city;
+	private String street;
+}
--- /dev/null
+++ b/model-mapper/README.md
@@ -0,0 +1,215 @@
+# ModelMapper 소개
+
+ModelMapper는 Java 객체 간의 매핑을 쉽게 해주는 라이브러리입니다. 복잡한 객체 변환 코드를 작성할 필요 없이 객체의 속성들을 자동으로 매핑해주어 개발 시간을 단축하고 코드의 가독성을 높여줍니다.
+
+## ModelMapper의 주요 특징
+
+- **타입 안전성**: 컴파일 시점에서 타입 오류를 확인할 수 있습니다.
+- **자동 매핑**: 이름과 타입이 일치하는 속성들을 자동으로 매핑합니다.
+- **커스텀 매핑**: 복잡한 매핑 로직을 위한 커스텀 컨버터 지원
+- **깊은 매핑**: 중첩된 객체 구조도 쉽게 매핑 가능
+- **설정 유연성**: 다양한 매핑 전략 및 설정 제공
+
+## ModelMapper 사용 방법
+
+### 1. 의존성 추가
+
+Maven을 사용한다면 pom.xml에 다음 의존성을 추가합니다:
+
+```xml
+<dependency>
+    <groupId>org.modelmapper</groupId>
+    <artifactId>modelmapper</artifactId>
+    <version>3.0.0</version>
+</dependency>
+```
+
+Gradle을 사용한다면 build.gradle에 다음을 추가합니다:
+
+```groovy
+implementation 'org.modelmapper:modelmapper:3.0.0'
+```
+
+### 2. 기본 사용법
+
+```java
+// ModelMapper 인스턴스 생성
+ModelMapper modelMapper = new ModelMapper();
2025-03-01 17:32:49 +09:00

6.3 KiB

ModelMapper 소개

ModelMapper는 Java 객체 간의 매핑을 쉽게 해주는 라이브러리입니다. 복잡한 객체 변환 코드를 작성할 필요 없이 객체의 속성들을 자동으로 매핑해주어 개발 시간을 단축하고 코드의 가독성을 높여줍니다.

ModelMapper의 주요 특징

  • 타입 안전성: 컴파일 시점에서 타입 오류를 확인할 수 있습니다.
  • 자동 매핑: 이름과 타입이 일치하는 속성들을 자동으로 매핑합니다.
  • 커스텀 매핑: 복잡한 매핑 로직을 위한 커스텀 컨버터 지원
  • 깊은 매핑: 중첩된 객체 구조도 쉽게 매핑 가능
  • 설정 유연성: 다양한 매핑 전략 및 설정 제공

ModelMapper 사용 방법

1. 의존성 추가

Maven을 사용한다면 pom.xml에 다음 의존성을 추가합니다:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.0.0</version>
</dependency>

Gradle을 사용한다면 build.gradle에 다음을 추가합니다:

implementation 'org.modelmapper:modelmapper:3.0.0'

2. 기본 사용법

// ModelMapper 인스턴스 생성
ModelMapper modelMapper = new ModelMapper();

// 소스 객체에서 대상 객체로 매핑
DestinationType destination = modelMapper.map(source, DestinationType.class);

3. 실제 사용 예시

다음은 사용자 정보를 담은 엔티티와 DTO 간의 변환 예시입니다:

// 엔티티 클래스
public class User {
    private Long id;
    private String email;
    private String password;
    private String firstName;
    private String lastName;
    private Address address;
    
    // 생성자, getter, setter 생략
}

public class Address {
    private String street;
    private String city;
    private String country;
    private String zipCode;
    
    // 생성자, getter, setter 생략
}

// DTO 클래스
public class UserDTO {
    private Long id;
    private String email;
    private String fullName;
    private AddressDTO address;
    
    // 생성자, getter, setter 생략
}

public class AddressDTO {
    private String street;
    private String city;
    private String country;
    private String zipCode;
    
    // 생성자, getter, setter 생략
}

// 매핑 예시
public class UserService {
    private final ModelMapper modelMapper;
    
    public UserService() {
        this.modelMapper = new ModelMapper();
        
        // firstName과 lastName을 fullName으로 매핑하는 커스텀 설정
        modelMapper.createTypeMap(User.class, UserDTO.class)
            .addMappings(mapper -> {
                mapper.map(src -> src.getFirstName() + " " + src.getLastName(),
                          UserDTO::setFullName);
            });
    }
    
    public UserDTO convertToDTO(User user) {
        return modelMapper.map(user, UserDTO.class);
    }
    
    public User convertToEntity(UserDTO userDTO) {
        return modelMapper.map(userDTO, User.class);
    }
}

4. 고급 매핑 기능

커스텀 매핑 설정

// 속성 이름이 다른 경우 명시적 매핑
modelMapper.createTypeMap(Source.class, Destination.class)
    .addMapping(Source::getSourceProperty, Destination::setTargetProperty);

// 조건부 매핑
modelMapper.getConfiguration().setPropertyCondition(context -> {
    // 조건에 따라 true/false 반환
    return context.getSource() != null;
});

// 타입 변환 매핑
Converter<String, Date> stringToDateConverter = 
    ctx -> ctx.getSource() == null ? null : new SimpleDateFormat("yyyy-MM-dd").parse(ctx.getSource());

modelMapper.createTypeMap(Source.class, Destination.class)
    .addMappings(mapper -> mapper.using(stringToDateConverter)
                                .map(Source::getDateAsString, Destination::setDate));

매핑 전략 설정

// 필드 매핑 전략 (필드 이름 기준 매핑)
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

// 접근 방식 설정 (필드 직접 접근)
modelMapper.getConfiguration().setFieldAccessLevel(AccessLevel.PRIVATE);
modelMapper.getConfiguration().setFieldMatchingEnabled(true);

5. 컬렉션 매핑

// List 매핑
List<UserDTO> userDTOs = users.stream()
    .map(user -> modelMapper.map(user, UserDTO.class))
    .collect(Collectors.toList());

// Type Token 사용하여 컬렉션 매핑
Type listType = new TypeToken<List<UserDTO>>() {}.getType();
List<UserDTO> userDTOs = modelMapper.map(users, listType);

실제 사용 사례

Spring Boot 애플리케이션에서 ModelMapper를 빈으로 등록하여 사용하는 예시:

@Configuration
public class ApplicationConfig {
    
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        
        // 엄격한 매핑 전략 설정
        modelMapper.getConfiguration()
            .setMatchingStrategy(MatchingStrategies.STRICT)
            .setSkipNullEnabled(true)
            .setFieldMatchingEnabled(true)
            .setFieldAccessLevel(AccessLevel.PRIVATE);
            
        return modelMapper;
    }
}

@Service
public class ProductService {
    
    private final ProductRepository productRepository;
    private final ModelMapper modelMapper;
    
    @Autowired
    public ProductService(ProductRepository productRepository, ModelMapper modelMapper) {
        this.productRepository = productRepository;
        this.modelMapper = modelMapper;
    }
    
    public ProductDTO getProduct(Long id) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Product not found"));
            
        return modelMapper.map(product, ProductDTO.class);
    }
    
    public List<ProductDTO> getAllProducts() {
        List<Product> products = productRepository.findAll();
        
        return products.stream()
            .map(product -> modelMapper.map(product, ProductDTO.class))
            .collect(Collectors.toList());
    }
}

결론

ModelMapper는 Java 애플리케이션에서 객체 간 매핑을 간소화하는 강력한 도구입니다. 자동 매핑 기능으로 반복적인 코드를 줄이고, 커스텀 매핑 설정으로 복잡한 변환 로직도 깔끔하게 처리할 수 있습니다. 특히 엔티티와 DTO 간의 변환이 많은 웹 애플리케이션에서 유용하게 활용할 수 있습니다.