Java 개발자가 Kotlin data class를 만나면 가장 먼저 체감하는 변화

개발자솔짜미·2026년 4월 2일
post-thumbnail

Java + Spring Boot 개발자로 실무를 하다 보면 DTO, 응답 객체, 이벤트 메시지, 캐시 객체처럼 데이터를 담기 위한 클래스를 정말 많이 만들게 된다.

문제는 이런 클래스들이 비즈니스 로직보다 반복 코드가 훨씬 많다는 점이다.

필드 선언, 생성자, getter, equals, hashCode, toString까지 작성하다 보면 정작 중요한 도메인 설계보다 보일러플레이트 코드가 더 눈에 들어온다.

Kotlin을 처음 배우면서 가장 먼저 생산성 차이를 체감하게 되는 문법 중 하나가 바로 data class였다.

이번 글에서는 Java 개발자 관점에서 data class가 무엇인지, Java DTO와 어떻게 다른지, 어떤 점이 편리한지, 그리고 실무에서 반드시 주의해야 할 포인트까지 정리해본다.


data class란?

data class는 말 그대로 데이터 보관 목적의 전용 클래스다.

특히 아래와 같은 객체에 가장 적합하다.

  • Request DTO
  • Response DTO
  • Query 결과 DTO
  • 이벤트 메시지
  • Redis 캐시 객체
  • 외부 API 응답 매핑 객체

Kotlin은 데이터 보관에 필요한 핵심 메서드를 자동 생성해준다.

  • getter
  • equals()
  • hashCode()
  • toString()
  • copy()
  • componentN()

즉 Java에서 반복적으로 작성하던 DTO 코드 대부분이 사라진다.


Java DTO와 비교

Java에서 가장 흔한 DTO는 이런 형태다.

public class ProductResponse {
    private Long id;
    private String name;
    private int price;

    public ProductResponse(Long id, String name, int price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "ProductResponse{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

Lombok을 쓰더라도 보통 이렇게는 남는다.

@Getter
@AllArgsConstructor
@ToString
public class ProductResponse {
    private Long id;
    private String name;
    private int price;
}

Kotlin에서는 이렇게 끝난다.

data class ProductResponse(
    val id: Long,
    val name: String,
    val price: Int
)

Java 개발자 입장에서 가장 충격적인 포인트는 클래스의 목적이 코드에 그대로 드러난다는 점이다.

“이 객체는 데이터를 담기 위한 용도다”가 문법 자체에 녹아 있다.


어떤 점이 편리한가?

1) DTO 코드량이 압도적으로 줄어든다

Spring Boot에서 API 하나 만들 때 DTO가 최소 2~4개는 나온다.

  • request
  • response
  • service result
  • query projection

Java에서는 클래스 수가 많아질수록 getter, constructor, equals 작성 비용이 커진다.

Kotlin은 선언만 남는다.

data class CreateOrderRequest(
    val productId: Long,
    val quantity: Int,
    val couponId: Long?
)

실무에서 DTO가 많은 이커머스, 정산, 검색 시스템일수록 체감이 크다.


2) toString 자동 생성으로 로그 확인이 편하다

운영 로그나 디버깅에서 DTO 내용을 바로 확인하기 좋다.

val dto = ProductResponse(1L, "맥북", 2000000)
println(dto)

출력 결과

ProductResponse(id=1, name=맥북, price=2000000)

Java에서 toString() 직접 만들던 습관이 거의 사라진다.


3) copy()가 실무에서 정말 강력하다

개인적으로 Java 개발자가 가장 크게 체감하는 생산성 차이는 copy()다.

기존 객체 일부만 변경한 새 객체를 쉽게 만들 수 있다.

val original = ProductResponse(1L, "맥북", 2000000)
val discounted = original.copy(price = 1800000)

이커머스에서 자주 등장하는 아래 시나리오에 매우 잘 맞는다.

  • 할인 가격 계산
  • 주문 상태 변경 응답
  • 캐시 데이터 일부 갱신
  • 이벤트 payload 수정

Java에서는 builder나 새 생성자를 다시 호출해야 한다.


4) 구조 분해 선언이 가능하다

val product = ProductResponse(1L, "맥북", 2000000)
val (id, name, price) = product

복합 반환값을 다룰 때 생각보다 유용하다.

특히 작은 유틸성 로직에서 생산성이 좋다.


실무에서 가장 좋은 사용처

너처럼 Spring Boot 백엔드 기준으로 가장 좋은 위치는 아래다.

Request / Response DTO

data class ProductCreateRequest(
    val name: String,
    val price: Int
)

QueryDSL Projection DTO

data class ProductSearchResult(
    val productId: Long,
    val productName: String,
    val sellerName: String,
    val stock: Int
)

Redis 캐시 객체

data class ProductCache(
    val id: Long,
    val price: Int,
    val stock: Int
)

반드시 주의할 점

1) JPA Entity에는 기본적으로 비추천

이건 Java 개발자가 Kotlin으로 넘어오면서 가장 많이 실수하는 부분이다.

data class를 Entity에 그대로 쓰면 편해 보이지만 실제로는 위험하다.

@Entity
data class Product(
    @Id
    val id: Long,
    val name: String
)

왜 위험하냐면 아래 문제가 생긴다.

  • equals() / hashCode() 자동 생성
  • JPA 프록시 객체와 비교 충돌
  • 지연 로딩 필드 비교 시 예상치 못한 쿼리
  • 양방향 연관관계 순환 참조
  • 엔티티 identity 보장 이슈

특히 Hibernate Proxy와 충돌하는 순간 디버깅이 어려워진다.

결론

Entity는 일반 class 사용이 안전하다.


2) 너무 큰 객체에 copy() 남용 주의

copy()는 새 객체를 만든다.

필드가 많고 중첩 객체가 깊은 DTO에서 과도하게 사용하면 메모리 사용량과 가독성이 나빠질 수 있다.

특히 대량 배치 처리 DTO에서는 주의가 필요하다.


3) mutable property(var) 남용 주의

data class ProductResponse(
    var price: Int
)

가능하면 val 중심으로 immutable하게 유지하는 것이 좋다.

실무에서는 불변 객체가 버그를 줄인다.


Java 개발자에게 추천하는 학습 순서

가장 빠르게 익숙해지는 방법은 기존 Java DTO를 하나씩 바꾸는 것이다.

추천 순서:

  1. Response DTO
  2. Request DTO
  3. QueryDSL Projection DTO
  4. Redis 캐시 DTO
  5. 이벤트 메시지

반대로 Entity는 가장 마지막에 신중하게 접근해야 한다.


마무리

Kotlin의 data class는 단순히 코드를 줄이는 문법이 아니다.

Java + Spring Boot 개발자에게는 DTO 설계 방식 자체를 더 의도 중심으로 바꾸는 도구에 가깝다.

  • 코드량 감소
  • 로그 가독성 향상
  • copy 기반 안전한 객체 변경
  • DTO 유지보수성 향상

특히 이커머스처럼 DTO가 많은 도메인에서는 생산성 차이가 매우 크다.

다만 JPA Entity에 그대로 적용하는 것은 위험할 수 있으므로,
DTO 전용으로 먼저 익히고 Entity는 일반 class로 유지하는 습관이 가장 안전하다.

처음 Kotlin을 배우는 Java 개발자라면,
현재 운영 중인 Spring Boot 프로젝트의 Response DTO부터 data class로 바꿔보는 것만으로도 Kotlin의 장점을 빠르게 체감할 수 있다.

profile
I DEVELOP THEREFORE, I AM 😄

0개의 댓글