· inner-circle

코틀린의 확장함수 활용해 Converter Extension 해보자

계층 사이 DTO 변환 코드가 서비스에 흩어지는 걸 막으려고 코틀린 확장 함수로 Converter를 분리하고, 관심사 분리와 재사용 측면에서 얻은 효과를 주문 서비스 코드로 정리했다.

작성날짜: 24.09.17

상황

  • 자바와 달리 코틀린에서 보다 쉽게 Converter Extention을 하고 싶었음

코틀린의 확장함수를 사용하다

코틀린 확장함수란?

  • 기존 클래스에 새로운 함수를 추가할 수 있는 기능
  • 원본 소스 코드를 수정하지 않고도 클래스의 기능 확장 가능
// 확장함수 사용 예
fun String.addExclamation(): String = this + "!"
val result = "Hello".addExclamation()

// 기존 자바 사용 예
String result = StringUtils.addExclamation("Hello");

자바와의 차이점

특성코틀린 확장 함수자바 유사 기능
구문fun String.addExclamation(): String = this + "!"public static String addExclamation(String str) { return str + "!"; }
호출 방식"Hello".addExclamation()StringUtils.addExclamation("Hello")
정의 위치클래스 외부에서 정의 가능별도의 유틸리티 클래스 내에서 정의
가시성정의된 파일 내에서만 사용 가능 (import 필요)일반적으로 public static 메서드로 전역적 사용 가능
오버라이딩불가능 (가상 메서드가 아님)가능 (인스턴스 메서드의 경우)
null 안전성컴파일 시점에서 null 체크 제공기본적으로 제공되지 않음
리시버this가 확장되는 객체를 가리킴해당 개념 없음
타입 추론지원제한적 지원 (Java 10 이상에서 var 키워드 사용 시)
인라인화가능 (inline 키워드 사용)불가능 (JVM 최적화에 의존)
프로퍼티 확장지원해당 개념 없음

이제부터 사용해보자

타 프로젝트 사용 구조를 살펴보자

image

좀 더 자세히 살펴볼까?

image

  • 대략적으로 이 정도 흐름으로 파악하고 실제 도입을 시도!

실제 적용된 주문서비스 내 코드

image
fun OrderProduct.toOrderProducts(): OrderProduct {
    return OrderProduct(
        id = this.id,
        orderId = this.orderId,
        productId = this.productId,
        quantity = this.quantity,
        price = this.price,
        discountedPrice = this.discountedPrice,
        createdAt = this.createdAt,
        updatedAt = this.updatedAt
    )
}

...

orderProducts = this.orderProducts.map { it.toOrderProducts() }

도입 효과는?

1. 관심사의 분리:

  • 데이터 변환 로직을 별도의 함수로 분리함으로써 코드의 구조가 더 깔끔
  • 각 객체(User, UserDTO, CreateUserRequest)는 자신의 책임에만 집중 가능

2. 코드 재사용성:

  • 이러한 변환 함수들은 애플리케이션의 여러 부분에서 재사용 가능
  • 예를 들어, 사용자 생성, 수정, 조회 등 여러 API에서 동일한 변환 로직 사용 가능

3. 유지보수성 향상:

  • 변환 로직이 한 곳에 집중되어 있어 수정이 필요할 때 쉽게 찾고 변경 가능.
  • 새로운 필드가 추가되거나 변경될 때, 변환 함수만 수정

4. 타입 안정성:

  • Kotlin의 강력한 타입 시스템을 활용하여 컴파일 시점에 많은 오류를 잡을 수 있음
  • 잘못된 타입의 데이터가 전달되는 것을 방지

5. 계층 간 데이터 흐름 명확화:

  • 예를 들어, 클라이언트 요청(CreateUserRequest) → 도메인 객체(User) → DTO(UserDTO) 등의 데이터 흐름을 명확하게 표현

6. 보안 강화:

  • DTO를 사용함으로써 클라이언트에 노출되지 않아야 할 민감한 정보를 숨길 수 있음

7. 테스트 용이성:

변환 함수들은 독립적이고 순수한 함수이므로 단위 테스트를 작성하기 쉬움

8. 확장성:

  • 새로운 요구사항이 생겼을 때 기존 코드를 크게 수정하지 않고도 새로운 변환 함수를 쉽게 추가 가능

9. API 버전 관리:

  • 다른 버전의 API를 위해 다른 DTO를 사용해야 할 때, 새로운 변환 함수를 쉽게 추가 가능

회고

  • 사용하는 언어의 장점을 살려 코드 작성하는 부분은 중요

참고문헌