BE/Spring

MapStruct with Kotlin

Taylor Kang 2021. 6. 9. 14:39

Mapper ์ •์˜ : @Mapper

@Mapper(componentModel = "spring")
interface GenreMapper{

    @Mappings(
      Mapping(source = "genreCodes", target = "code"),
      Mapping(source = "engName", target = "enName"),
      //Mapping(source = "krName", target = "krName"),
      //Mapping(source = "imageUrl", target = "imageUrl"),
      //Mapping(source = "topImageUrl", target = "topImageUrl"),
      //Mapping(source = "colorBar", target = "colorBar")
    )
	fun toGenreDTO(genre: Genre): GenreDTO

}
  • interface์— ๋งคํ•‘ ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•˜๊ณ  org.mapstruct.Mapper ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€
  • Mapstruct Code Generator ๊ฐ€ @Mapper ๊ฐ€ ๋ถ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฐพ์•„์„œ XXXImpl ํ˜•ํƒœ๋กœ ๊ตฌํ˜„์ฒด๋ฅผ ์ƒ์„ฑํ•จ
  • ๊ตฌํ˜„์ฒด ์ƒ์„ฑ์‹œ source, target์ด ๋˜๋Š” ํด๋ž˜์Šค์˜ ์†์„ฑ๋ช…์„ ๋น„๊ตํ•˜๊ณ  ์ž๋™์œผ๋กœ ๋งคํ•‘์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ
    • 1:1 ๋งคํ•‘์ด ์›ํ™œํ•˜๊ฒŒ ๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” source, target ์ชฝ ํƒ€์ž…์„ ์–ธ์ด ๋™์ผํ•ด์•ผํ•จ
  • ์†์„ฑ๋ช…์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ @Mapping ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ๋งคํ•‘์ •๋ณด๋ฅผ ์ง์ ‘ ์ •์˜
    • ์—ฌ๋Ÿฌ๊ฐœ์˜ Mapping์„ ์ •์˜ํ• ๊ฒฝ์šฐ @Mappings ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋ฌถ์–ด์ค˜์•ผํ•จ
  • source, target class ์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ๋ฐ˜๋“œ์‹œ ์„ ์–ธ๋˜์–ด์žˆ์–ด์•ผ ํ•˜๋ฉฐ source ์—๋Š” setter๊ฐ€, target์—๋Š” getter๊ฐ€ ํ•„์š”ํ•จ (data class ์‚ฌ์šฉํ•˜๋ฉด ๋จ)
  • @Mapper(componentModel = "spring") : Impl ์€ ์Šคํ”„๋ง์˜ ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ๊ด€๋ฆฌ๋จ(@Component)

 

MapStruct๊ฐ€ ์ƒ์„ฑํ•œ ๊ตฌํ˜„์ฒด

build/generated/source/kapt/...

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-05-17T18:09:21+0900",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from kotlin-annotation-processing-gradle-1.4.30.jar, environment: Java 11.0.10 (JetBrains s.r.o.)"
)
@Component
public class GenreMapperImpl implements GenreMapper {

    @Override
    public GenreDTO toGenreDTO(Genre genre) {
        if ( genre == null ) {
            return null;
        }

        String code = null;
        String enName = null;
        String krName = null;
        String imageUrl = null;
        String topImageUrl = null;
        String colorBar = null;

        code = genre.getGenreCodes();
        enName = genre.getEngName();
        krName = genre.getKrName();
        imageUrl = genre.getImageUrl();
        topImageUrl = genre.getTopImageUrl();
        colorBar = genre.getColorBar();

        GenreDTO genreDTO = new GenreDTO( code, enName, krName, imageUrl, topImageUrl, colorBar );

        return genreDTO;
    }
}

 

Mapping method ์ž‘์„ฑ๋ฒ•

  1. source, target์˜ ์†์„ฑ๋ช…์ด ๋‹ค๋ฅธ๊ฒฝ์šฐ
    • Mapping(source = "genreCodes", target = "code")
  1. target์˜ ํŠน์ • ์†์„ฑ์„ ์ œ์™ธํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
    • ignore = true๋กœ ๋ฌด์‹œํ•  ์†์„ฑ ์ง€์ •
    @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
    interface GenreMapper {
    
        @Mapping(target = "code", ignore = true)
        fun toGenreDTO(genre: Genre): GenreDTO
    
    }
    • ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ํ•„๋“œ ๋ฌด์‹œํ•˜๋„๋ก ์ •์ฑ… ์„ค์ •
      • unmappedTargetPolicy = ReportingPolicy.IGNORE

    • unmappedTargetPolicy = ReportingPolicy.ERROR
      • ๋งคํ•‘ ์‹œ ํƒ€๊นƒ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋งคํ•‘ ์ •์ฑ…์„ ์—„๊ฒฉํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐ€๊ธฐ ์œ„ํ•ด ํƒ€๊นƒ ๊ฐ์ฒด์— ๋งคํ•‘ ์‹œ ๋งคํ•‘๋˜์ง€ ์•Š์€ ์†์„ฑ์ด ์žˆ๋‹ค๋ฉด ์ปดํŒŒ์ผ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋„๋ก ์ •์ฑ…์„ ์„ค์ •
  1. conversion ๋ฉ”์†Œ๋“œ๋ฅผ ์ง์ ‘ ์ •์˜
    • java object๋กœ ๊ฐ์‹ธ์„œ ์ „๋‹ฌ
    @Mapper
    interface GenreMapper {
    
        @Mapping(target = "code", expression = "java(genre.getCustomGenreCode())")
        fun toGenreDTO(genre: Genre): GenreDTO
    }
  1. ๋‹ค์ˆ˜์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•ฉ์น˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ๊ฐœ์˜ source parameter๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ
    • ๊ฐ ์†์„ฑ๋“ค์€ ์ด๋ฆ„์„ ๋น„๊ตํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋งคํ•‘
    • source ์†์„ฑ๋ช…์ด ๊ฒน์น˜๋Š” ๊ฒฝ์šฐ @Mapping ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์–ด๋А source ์†์„ฑ์„ ๋งคํ•‘ํ•  ๊ฒƒ์ธ์ง€ ๋ช…์‹œ
    @Mapper
    interface GenreArtistMapper {
    
        @Mappings(
                Mapping(source = "artist.id", target = "artist_id"),
                Mapping(source = "artist.name", target = "artist_name"),
                Mapping(source = "genre.code", target = "genre_code")
        )
        fun toGenreArtistDTO(artist: Artist, genre: Genre): GenreArtistDTO
    
    }
    • source ๋กœ ์ฃผ์–ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง์ ‘์ ์œผ๋กœ target์˜ ์†์„ฑ์— ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์Œ
    @Mapper
    interface GenreMapper{
    
    	@Mappings(
    		Mapping(source = "genreCodes", target = "code"),
        Mapping(source = "engName", target = "enName"),
        Mapping(source = "number", target = "genreNumber")
      )
    	fun toGenreDTO(genre: Genre, number: Int): GenreDTO
    
    }
  1. Mapping method๋ฅผ ์ง์ ‘ ๊ตฌํ˜„
    1. mapstruct๋กœ ๋งคํ•‘์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ
    2. interface์— default ๋ฉ”์†Œ๋“œ๋กœ ์ปค์Šคํ…€ ๋งคํ•‘์„ ์ถ”๊ฐ€ (kotlin์€ ๋”ฐ๋กœ keyword ๋ถ™์ด์ง€ ์•Š์•„๋„๋จ)
      • Kotlin
      @Mapper
      interface GenreMapper {
      
          fun toGenreDTO(genre: Genre) GenreDTO {
              // mapping method ์ง์ ‘ ๊ตฌํ˜„
          }
      
      }
      • Java
      @Mapper
      public interface GenreMapper {
      
          default GenreDTO toGenreDTO(Genre genre) {
              // mapping method ์ง์ ‘ ๊ตฌํ˜„
          }
      
      }

 

๋งํฌ