dependencies {
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}
MapStruct 의존성 추가.
[MapStruct 적용전]
public MessageDto toDto(Message message) {
return new MessageDto(
message.getId(),
message.getCreatedAt(),
message.getUpdatedAt(),
message.getContent(),
message.getChannel().getId(),
userMapper.toDto(message.getAuthor()),
message.getAttachments().stream()
.map(binaryContentMapper::toDto)
.toList()
);
}
이런식으로 Entity를 DTO로 만드는 작업을 코드로 작성해야한다.
[MapStruct 적용후]
@Mapper(
componentModel = "spring",
uses = {BinaryContentMapper.class}
)
public interface UserMapper {
UserDto toDto(User user);
}
@Mapper : 이 인터페이슨느 MapStruct 매퍼라고 MapStruct에게 알려주는 역할.
컴파일 시점에 MapStruct가 구현 클래스를 자동 생성한다.
@Component
public class UserMapperImpl implements UserMapper {
@Autowired
private BinaryContentMapper binaryContentMapper;
@Override
public UserDto toDto(User user) {
return new UserDto(
user.getId(),
user.getUsername(),
user.getEmail(),
binaryContentMapper.toDto(user.getProfile()),
user.getOnline()
);
}
}
이런식으로 구현코드를 자동으로 생성하게 된다.
componentModel = "spring" : Spring Bean으로 등록하라는 옵션
즉 MapStruct가 생성하는 구현클래스에 자동으로 붙는다.
@Component
public class UserMapperImpl implements UserMapper
그래서 Service에서
@RequiredArgsConstructor
@Service
public class UserService {
private final UserMapper userMapper;
}
이런식으로 Spring이 자동으로 주입해서 사용할 수 있다.
uses = {BinaryContentMapper.class} : 다른 Mapper를 같이 사용하겠다는 의미.
UserDto에는 BinaryContentDto profile 변수가 있다. 이는 BinaryContent Entity를 BinaryContentDto로
변환하는것이 필요한다. 따라서 UserMapper내에 BinaryContentMapper.class를 사용해야한다.
MapStruct 적용예시
public record ReadStatusDto(
UUID id,
UUID userId,
UUID channelId,
Instant lastReadAt
) {
}
ReadStatus Entity를 MapStruct를 이용하여 ReadStatus로 매핑하도록하는 ReadStatusMapper를 만들것이다.
@Getter
@Entity
@Table(name = "READ_STATUSES")
@NoArgsConstructor
public class ReadStatus extends BaseEntity {
@LastModifiedDate
private Instant updatedAt;
//
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "channel_id")
private Channel channel;
@Column(nullable = false)
private Instant lastReadAt;
ReadStatus와 ReadStatusDto와 다른것은 userId와 channelId로 들어갔다는점.
이를 ReadStatusMapper에 명시해줘야한다.
@Mapper(componentModel = "spring")
public interface ReadStatusMapper {
@Mapping(source = "user.id",target = "userId")
@Mapping(source = "channel.id",target = "channelId")
ReadStatusDto toDto(ReadStatus readStatus);
}
@Mapping을 사용하고, ReadStatusDto의 userId는 ReadStatus Entity의 user의 id를 사용한것이라고 명시한다.
channelId도 같다.
'코드잇 스프린트 > 실습' 카테고리의 다른 글
| MapStruct는 <T> 제네릭 타입 메서드 자체는 구현코드 생성 못한다. (0) | 2026.03.10 |
|---|---|
| BinaryContent 저장로직 고도화 (0) | 2026.03.09 |
| Entity를 Controller까지 그대로 노출 할때 발생할 수 있는 문제점 (0) | 2026.03.09 |
| 디스코드 프로젝트 실습: Entity 정의하기(JPA 어노테이션,cascade,고아객체) (0) | 2026.03.05 |
| JPA사용 직접 작성한 DDL로 테이블 생성(schema.sql) (0) | 2026.03.04 |