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()
- 분기를 반복문 내부에 직접 작성 → 함수 흐름 파악을 위해 각 분기 파악 필요
- 타입 순서와 분기에 대응 어려움
개선 방안
- 반복문 제거
- 타입을 나타내는 컬렉션이 충분히 작을 때, 각 요소를
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)) } - 타입을 나타내는 컬렉션이 충분히 작을 때, 각 요소를
- 분기 추출
- 조건 분기를 보조 함수로 추출
- 가독성 개선
- 단점 :
ORDERED_ATTRIBUTES_TO_LOG와getLogText간 대응 어려움 - 분기에 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 -> ... ... } - 로직을 타입에 포함
- 전략 패턴 등의 구조를 적용
- 함수 흐름 파악 및 포괄성 보장 용이
- 단점 : 해당 타입이 광범위하게 사용될 때 특정 기능 고유의 로직/값 포함 시 의존 관계 복잡성 증가
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() - 관계 명시 튜플
- 타입과 타입의 순서, 로직의 연관성 명시를 위한 튜플 생성
- 포괄성 보장 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()
반복문 내에 큰 조건 분기가 있다면, 분기와 로직 간 대응 관계 이해가 쉽도록 재구조화
오늘 배운 것
- AI 선형 회귀 모델
내일 할 일
- AI