이 글은 주로 DDD Europe 2024의 Domain Re-discovery Patterns for Legacy Code - Richard Groß 강연 을 번역하고 소개하는 글입니다.
인트로
오늘 소개하려는 강의의 저자인 Richard Groß는 본인 소개에 소프트웨어 고고학자software archeologist라 적었는데 참 재밌는 표현이다.
이 강연이 궁극적으로 무슨 말을 하려는지에 대한 초강력 힌트이기도 하다.
특히 처음 만나는 레거시 코드 파악은 정말 어렵다. 그간의 모든 역사와 내부 지식들이 겹겹이 쌓여있고 알 수 없는, 마치 상형문자 같은 Magic word들로 가득하다.. 지식 장벽Knowlege Silo은 견고하고 높다. 도저히 정복할 수 없을 것처럼 보인다.
레거시 코드 관리는 고고학이라는 말이 참 잘어울린다.
기존 코드를 파악하고파헤치고 이해함으로써 우리는 기존의 방향과 목적, 미래에 지속적으로 전달해야 할 가치를 분명하게 이해할 수 있다.
강연 내용
이 강연은 기존 코드에서 도메인 개념을 추출하고, 이를 기반으로 시스템을 개선하는 방법을 다룬다. 특히 코드 분석 기법과 실험적인 접근 방식을 활용해 도메인 모델을 효과적으로 복원하는 다양한 패턴을 설명한다.
액티브 패턴 vs 패시브 패턴
패턴은 크게 패시브 패턴(Passive Patterns)과 액티브 패턴(Active Patterns)으로 나뉜다.
• 패시브 패턴: 코드의 현재 상태를 분석하여 도메인 개념을 추출하는 접근 방식.
• 액티브 패턴: 코드를 변경하거나 실험적인 기능을 적용해 도메인 지식을 적극적으로 재발견하는 접근 방식.
패시브 패턴 (Passive Patterns)
- 코드 태그 클라우드 (Code Tag Cloud)
코드베이스에서 변수명, 클래스명, 메서드명을 추출해 태그 클라우드를 생성한다.
- 가장 많이 사용되는 단어를 시각화하여 시스템의 핵심 개념을 빠르게 파악할 수 있다.
- 특정 키워드가 과도하게 사용되거나 불분명한 용어가 포함되어 있을 경우, 리팩토링이 필요할 수 있다.
- 활동 로깅 (Activity Logging)
애플리케이션의 실행 로그를 분석하여 사용 패턴을 파악한다.
- 실제로 많이 호출되는 코드와 거의 호출되지 않는 코드를 식별할 수 있다.
- 자주 사용되는 기능은 개선의 우선순위를 높이고, 거의 사용되지 않는 기능은 제거할 후보로 고려할 수 있다.
- 클러스터 투자 (Cluster Invest)
코드베이스의 파일 크기와 코드 라인 수(LoC)를 분석해 트리맵(Treemap)을 생성한다.
- 특정 코드 영역이 비정상적으로 크다면 해당 부분이 중요한 도메인 로직을 포함할 가능성이 높다.
- 지나치게 큰 파일은 단일 책임 원칙(SRP) 위반 가능성이 있으며, 이를 분할하는 것이 바람직할 수 있다.
- 복잡도 투자 (Complexity Invest)
코드의 복잡도를 분석해 가장 관리가 어려운 영역을 찾아낸다.
- 사이클로매틱 복잡도(Cyclomatic Complexity)와 같은 메트릭을 사용해 코드의 복잡도를 측정한다.
- 복잡도가 높은 영역은 도메인 로직이 숨겨져 있을 가능성이 크므로 분석이 필요하다.
- 지식 장벽 (Knowledge Silos)
코드를 변경한 개발자의 기록을 분석하여 특정 코드 영역을 소수의 개발자가 주로 다루는지 확인한다.
- 지식이 특정 개발자에게 집중된 경우, 해당 코드가 이해하기 어렵거나 문서화가 부족할 가능성이 크다.
- 팀 내 코드 공유를 강화하고, 문서화를 통해 지식 분산을 유도해야 한다.
- 조정 병목 (Coordination Bottlenecks)
협업 과정에서 충돌이 자주 발생하는 코드를 찾아낸다.
- 버전 관리 시스템(Git)의 충돌 데이터를 분석하면, 병목 지점을 파악할 수 있다.
- 코드의 높은 결합도를 낮추고, 모듈화를 통해 협업 효율을 높이는 것이 중요하다.
- 다중 레벨 종속성 그래프 (Multi-Level Dependency Graph)
코드 간의 의존성을 그래프로 시각화하여 분석한다.
- 종속성이 지나치게 복잡하면 유지보수가 어렵고, 예상치 못한 사이드 이펙트가 발생할 가능성이 크다.
- 의존성이 높은 코드를 리팩토링해 모듈화하는 것이 바람직하다.
- 시간적 결합 (Temporal Coupling)
같은 커밋에서 자주 변경되는 파일을 분석하여 결합도를 측정한다.
- 항상 함께 변경되는 파일은 응집도가 높거나, 분리할 필요가 있는 모듈일 가능성이 있다.
- 비슷한 변경이 반복된다면, 해당 코드의 재사용성을 개선하는 것이 필요하다.
- 엔티티 소유권 (Entity Ownership)
데이터베이스 엔티티를 사용하는 코드의 위치를 분석해 소유권을 확인한다.
- 엔티티를 여러 모듈이 동시에 변경하면 데이터 무결성이 깨질 가능성이 높다.
- 엔티티의 책임을 명확히 하고, 변경 가능 범위를 제한하는 것이 중요하다.
액티브 패턴 (Active Patterns)
- 레거시 토글 (Legacy Toggle)
사용되지 않는 기능을 비활성화하여 실제로 필요한지 테스트한다.
- 설정 플래그(Feature Toggle)를 추가해 기능을 끄고, 사용자 피드백을 수집한다.
- 불필요한 기능은 제거하여 코드 복잡도를 줄인다.
- 복잡도 제한 (Complexity Limit)
특정 기능을 간소화하는 실험을 진행해 유지보수성을 높인다.
- 예를 들어, 복잡한 조건문을 전략 패턴(Strategy Pattern)으로 대체하거나, 가드절(Guard Clause)로 단순화할 수 있다.
- 개선 전후의 성능과 유지보수성을 비교해 최적의 해결책을 도출한다.
- 지식 공유 (Knowledge Sharing)
지식 사일로가 발견된 영역에 대해 적극적으로 문서화와 코드 리뷰를 진행한다.
- 특정 개발자가 독점적으로 관리하는 코드가 있다면, 페어 프로그래밍을 통해 공유를 유도한다.
- 팀 내 공유 세션을 열어 도메인 개념을 정리하는 것도 효과적이다.
- 시간적 분리 (Temporal Decoupling)
시간적으로 결합된 코드를 비동기 방식으로 전환한다.
- 예를 들어, 서로 의존성이 높은 프로세스를 메시지 큐(Kafka, RabbitMQ 등)를 사용해 분리할 수 있다.
- 이를 통해 코드의 유연성을 높이고, 배포 단위를 줄일 수 있다.
- 엔티티 소유권 경계 설정 (Entity Ownership Bounds)
각 모듈이 관리해야 할 데이터의 책임을 명확히 구분한다.
- 데이터 변경 권한을 특정 모듈로 제한해 예상치 못한 데이터 변경을 방지한다.
- 도메인 별로 데이터 소유권을 정리하고, 읽기와 쓰기 작업을 명확히 분리하는 것이 중요하다.
- 역 오브젝트 마더 (Inverse Object Mother)
애플리케이션을 빈 상태에서 시작하여 필요한 도메인 객체를 분석한다.
- 핵심 기능을 구현하기 위해 필수적인 엔티티와 로직만 포함하도록 설계한다.
- 불필요한 상태를 줄여 도메인 모델을 간결하게 만들 수 있다.
결론
이 패턴들은 레거시 코드에서 도메인 개념을 재발견하고, 유지보수성을 향상시키는 데 효과적인 방법들이다.
- 패시브 패턴을 활용해 코드의 현재 상태를 분석하고, 도메인 개념이 숨겨진 부분을 찾아낸다.
- 액티브 패턴을 적용해 시스템을 직접 변경하면서 도메인 모델을 명확하게 정리한다.
이러한 접근 방식을 활용하면, 레거시 코드에서도 도메인 지식을 복원하고, 코드 품질을 개선할 수 있다. 내용이 다소 길어 추후 인상 깊은 부분만 따로 보충을 하려 한다.