본문 바로가기
개발/리팩토링

일급 컬렉션 (First-Class Collection)으로 코드 품질 향상 도전하기

by nineteen 2025. 4. 23.
반응형

문제 상황

제가 느낀 문제는 다음과 같았습니다:

  • 서로 다른 도메인이 같은 컬렉션 타입을 사용하고 있어, 구분하기 어렵다
  • 같은 컬렉션 타입의 비즈니스 로직들이 서비스 레이어에 흩어져 있어서 관리가 어렵고 직관적으로 이해하기 어렵다

예를 들면, 두 개의 도메인이 같은 Map<Type, Boolean>(적절한 예시인지는 모르겠지만..) 타입을 사용하고 있었던 겁니다.
서비스 레이어에서 이 타입의 변수를 만들고 수정하는 작업을 하는데, 이게 어떤 도메인과 관련된 코드인지 구분하기가 어려웠어요.
심지어 제가 직접 작성한 코드인데도 다시 보면 이해하는데 시간이 걸렸기에..
나도 보기 힘든데, 다른 사람은 더 힘들겠다는 생각에 개선하기로 마음먹었습니다..!

 

개선 방법

일급 컬렉션 도입

제가 시도한 개선은 일급 컬렉션을 도입하는 것이었습니다. 같은 타입을 사용하던 두 도메인을 각각의 일급 컬렉션으로 분리했습니다:
아래 코드는 예시 코드입니다. 어색한 부분이 있을 수 있겠지만, 감안하고 봐주세요!

// AS-IS: 단순한 컬렉션 사용
val studentScores: Map<String, Int> = mapOf(
    "math" to 90,
    "science" to 85,
    "english" to 95
)

val courseGrades: Map<String, Int> = mapOf(
    "math" to 90,
    "science" to 85,
    "english" to 95
)

// TO-BE: 각 도메인별 일급 컬렉션 사용
class StudentScores private constructor(
    private val scores: Map<String, Int>,
) {
    fun getAverage(): Double {
        return scores.values.average()
    }

    fun getHighestScore(): Int {
        return scores.values.maxOrNull() ?: 0
    }

    fun getSubjectScore(subject: String): Int {
        return scores[subject] ?: 0
    }

    companion object {
        fun from(scores: Map<String, Int>): StudentScores {
            return StudentScores(scores)
        }
    }
}

class CourseGrades private constructor(
    private val grades: Map<String, Int>,
) {
    fun calculateFinalGrade(): String {
        val average = grades.values.average()
        return when {
            average >= 90 -> "A"
            average >= 80 -> "B"
            average >= 70 -> "C"
            else -> "D"
        }
    }

    fun getPassingSubjects(): List<String> {
        return grades.filter { it.value >= 70 }.keys.toList()
    }

    companion object {
        fun from(grades: Map<String, Int>): CourseGrades {
            return CourseGrades(grades)
        }
    }
}

이렇게 일급 컬렉션을 도입함으로써:

  • 각 도메인이 독립적인 클래스로 관리되어, 각 클래스에 적절한 책임을 위임할 수 있게 되었습니다.
  • 코드의 의도가 더 명확해져서 다른 개발자들도 이해하기 쉬워졌습니다. (나의 바람)

 

개선 결과

개선을 통해 다음과 같은 효과를 얻을 수 있었습니다:

  • 각 도메인이 독립적인 클래스로 분리되어 명확한 책임을 갖게 했습니다.
  • 관련된 로직이 클래스 내부에 위임되어 관리를 용이하게 했습니다.
  • private 생성자와 정적 팩토리 메소드를 통해 객체 생성 과정을 제어.
    • 저의 경우, 객체를 생성할 때 선행되어야 할 작업이 있어서 정적 팩토리 메소드만 사용해서 객체를 생성할 수 있도록 했습니다..!

'개발 > 리팩토링' 카테고리의 다른 글

엔티티 조회 대신 존재 여부만 확인하기  (0) 2025.03.07