2025-10-13

1일 1아티클

LY

분기 처리 전략

전략 없는 전략


  interface Loggable {
      val logType: LogType
      val logLevel: LogLevel
      val logDescription: String
      val timestamp: Long
      val codeLocation: StackTraceElement
      ...
  }

  // 어떤 속성을 로그로 출력할지 결정하는 열거형
  enum class LogAttribute { LOG_TYPE, LOG_LEVEL, LOG_DESCRIPTION, ...}

  // 입력된 LogAttribute에 따라 로그 메시지 구성 결정
  fun createLogMessage(loggable: Loggable, attributesToLog: Set<LogAttribute>): String {
    ...
  }

  // LogAttribute의 순서는 정적으로 정해져 있어서 변경할 필요가 없다고 가정
  // ex. '로그 레벨은 메시지의 시작 부분에 위치해야 한다'는 규칙 존재
  // This order might be different from `ordinal` of `LogAttribute`.
  val ORDERED_ATTRIBUTES_TO_LOG: List<LogAttribute> = listOf(
    LOG_LEVEL,
    LOG_TYPE,
    LOG_DESCRIPTION,
    ...
  )

  // 문제점?
  fun createLogMessage(loggable: Loggable, attributesToLog: Set<LogAttribute>): String =
      ORDERED_ATTRIBUTES_TO_LOG.asSequence()
          .filter(attributesToLog::contains)
          .map { attribute ->
              when (attribute) {
                  LogAttribute.LOG_LEVEL -> getLogLevelText(loggable)
                  LogAttribute.LOG_TYPE -> getLogTypeText(loggable)
                  ...
              }
          }.joinToString()

  1. 분기를 반복문 내부에 직접 작성 → 함수 흐름 파악을 위해 각 분기 파악 필요
  2. 타입 순서와 분기에 대응 어려움

개선 방안

  1. 반복문 제거
    • 타입을 나타내는 컬렉션이 충분히 작을 때, 각 요소를 if로 직접 분기
    • 단점1 : 모든 타입이 포함되었는지 확인하는 단위 테스트 작성이 어려움
    • 단점2 : 컬렉션 유틸리티 함수(joinToString) 사용 불가
        
       val message = StringBuilder()
       if (attributesToLog.contains(LogAttribute.LOG_LEVEL)) {
           message.append(getLogLevelText(loggable))
       }
       if (attributesToLog.contains(LogAttribute.LOG_TYPE)) {
           message.append(getLogTypeText(loggable))
       }
    
    
  2. 분기 추출
    • 조건 분기를 보조 함수로 추출
    • 가독성 개선
    • 단점 : ORDERED_ATTRIBUTES_TO_LOGgetLogText 간 대응 어려움
    • 분기에 else 사용 시 포괄성 보장이 어려우므로, 사용 자제
    
       fun createLogMessage(loggable: Loggable, attributesToLog: Set<LogAttribute>): String =
           ORDERED_ATTRIBUTES_TO_LOG.asSequence()
               .filter(attributesToLog::contains)
               .map { attribute -> attribute.getLogText(loggable) }
               .joinToString()
    
       private fun LogAttribute.getLogText(loggable: Loggable): String = when(this) {
           LogAttribute.LOG_TYPE -> ...
           ...
       }    
    
    
  3. 로직을 타입에 포함
    • 전략 패턴 등의 구조를 적용
    • 함수 흐름 파악 및 포괄성 보장 용이
    • 단점 : 해당 타입이 광범위하게 사용될 때 특정 기능 고유의 로직/값 포함 시 의존 관계 복잡성 증가
    
       enum class LogAttribute {
         LOG_TYPE {
           override fun getLogText(loggable: Loggable) = ...
         },
         LOG_LEVEL {
           override fun getLogText(loggable: Loggable) = ...
         },
         ...;
    
         abstract fun getLogText(loggable: Loggable)
       }
    
       fun createLogMessage(loggable: Loggable, attributesToLog: Set<LogAttribute>): String =
           ORDERED_ATTRIBUTES_TO_LOG.asSequence()
               .filter(attributesToLog::contains)
               .map { attribute -> attribute.getLogText(loggable) }
               .joinToString()
    
    
  4. 관계 명시 튜플
    • 타입과 타입의 순서, 로직의 연관성 명시를 위한 튜플 생성
    • 포괄성 보장 X → 단위 테스트 작성으로 보완 가능
    
       class AttributeTextModel(val attributeType: LogAttribute, val textCreator: (Loggable) -> String)
    
       val ORDERED_ATTRIBUTES_TO_LOG: List<AttributeTextModel> = listOf(
           AttributeTextModel(LogAttribute.LOG_LEVEL, ::getLogLevelText),
           AttributeTextModel(LogAttribute.LOG_TYPE, ::getLogTypeText),
           ...
       )
    
       fun createLogMessage(loggable: Loggable, attributesToLog: Set<LogAttribute>): String =
           ORDERED_ATTRIBUTES_TO_LOG.asSequence()
               .filter { attributesToLog.contains(it.attributeType) }
               .map { it.textCreator(loggable) }
               .joinToString()
    
    

반복문 내에 큰 조건 분기가 있다면, 분기와 로직 간 대응 관계 이해가 쉽도록 재구조화

오늘 배운 것

  1. AI 선형 회귀 모델

내일 할 일

  1. AI

참고자료

results matching ""

    No results matching ""