이전 포스팅에서 우리는 기획자와 개발자가 유저 스토리를 통해 비즈니스 가치를 어떻게 맞추고 일정을 추정하는지 알아보았다.

 

https://hsunnystory.tistory.com/267

 

[Sofeware Enginnering] 애자일(Agile) 요구사항 관리: 유저 스토리와 일정 산정의 기술

"기능 개발하는데 며칠이나 걸리나요?" 개발자라면 기획자나 PM에게 이 질문을 받을 때마다 스트레스를 받을 것이다. 코드를 한 줄도 짜보기 전인데, 우리는 알 수 없는 변수(레거시 코드, 인프라

hsunnystory.tistory.com

 

기획팀과 완벽하게 유저 스토리를 합의하고 스프린트를 시작했다고 가정해 보자. 그런데 막상 코드를 열어보니 클래스 하나가 3,000줄이고 DB 쿼리가 스파게티처럼 얽혀있다. 코드를 한 줄 고칠 때마다 어디서 버그가 터질지 몰라 벌벌 떨어야 한다면, 아무리 훌륭한 스크럼 프로세스를 돌려도 그 팀은 절대 애자일해질 수 없다.

애자일은 일하는 방식만으로 완성되지 않는다. 반드시 탄탄한 엔지니어링이 뒷받침되어야 한다. 이번 포스팅에서는 무너져가는 애자일 팀을 구원할 기술적 실천법, XP(eXtreme Programming)와 TDD(테스트 주도 개발)에 대해 알아본다.

 

1. XP (eXtreme Programming)란

XP는 애자일 소프트웨어 개발 방법론 중 하나다. 익스트림이라는 이름 때문에 오해하기 쉽지만, 무모하게 밤을 새우며 코딩하라는 뜻이 아니다.

XP의 핵심 철학은 소프트웨어 개발에서 좋은 것으로 알려진 실천법들(Practices)을 극한(Extreme)의 수준까지 끌어올려 적용하자는 것이다.

 

코드를 테스트 하는 것이 좋은가?

-> 모든 코드를 작성하기 전에 테스트부터 작성하자(TDD)

 

코드 리뷰를 하는 것이 좋은가?

-> 아예 두 명이 한 모니터를 보며 실시간으로 상시 리뷰를 하자 (Pair Programming)

 

코드를 통합하는 것이 좋은가?

하루에도 수십번 씩 코드를 합치고 배포하자 (CI/CD)

 

2. TDD(테스트 주도 개발)

애자일 환경에서는 요구사항이 매 스프린트마다, 심지어 매일 변한다. 이렇게 변화가 잦은 환경에서 개발자가 시스템을 안정적으로 유지할 수 있는 유일한 무기는 자동화된 테스트 코드다.

테스트 코드가 없는 레거시 시스템에서 기획팀이 기능을 추가로 요청하면, 개발자는 방어적으로 변하고 일정을 보수적으로 잡게 된다. 

하지만 탄탄한 단위 테스트(Unit Test)가 뒷받침되어 있다면, 개발자는 코드를 과감히 수정하고, 즉시 테스트를 돌려 기존 기능이 망가지지 않았음을 1초 만에 증명할 수 있다. TDD는 개발자에게 심리적 안정감을 주어 요구사항 변경을 환영하게 만든다.

 

3. [Bad Code vs Good Code] 테스트 용이성이 설계에 미치는 영향

TDD의 진짜 목적은 테스트를 작성하는 것 자체가 아니라, 테스트하기 쉬운 구조(Testable Architecture)로 객체를 설계하도록 강제하는 데 있다. 테스트 하기 힘든 코드는 높은 확률로 유지보수 하기 힘든 나쁜 설계다.

 

오늘 날짜를 기준으로 할인 이벤트를 진행하는 로직을 예로 들어보자.

 

[Bad Code] 테스트가 불가능한 강하게 결합된 코드

메서드 내부에서 LocalDate.now()를 직접 호출하고 있다. 이 코드는 오늘이 아니면 할인 로직이 정상 작동하는지 테스트할 방법이 없다. 테스트를 하려면 컴퓨터의 시스템 시간을 바꿔야 하는 촌극이 벌어진다.

 

@Service
public class DiscountService {
    
    // 강한 결합: 내부에서 시간을 직접 통제하므로 외부에서 테스트 불가능
    public long calculateDiscount(long originalPrice) {
        LocalDate today = LocalDate.now(); 
        
        if (today.getMonth() == Month.DECEMBER) { // 12월엔 10% 할인
            return (long) (originalPrice * 0.9);
        }
        return originalPrice;
    }
}

 

[Good Code] TDD를 통해 도출된 테스트하기 쉬운 설계

테스트 코드를 먼저 작성하려고 시도했다면, "시간을 내가 제어할 수 없는데 어떻게 검증하지?" 라는 고민에 부딪힌다. 그 결과, 시간(또는 정책)을 외부에서 주입(Dependency Injection) 받도록 설계가 개선 된다.

 

// 핵심 비즈니스 룰은 외부에서 주입받을 수 있도록 인터페이스 혹은 파라미터로 열어둔다.
@Service
public class DiscountService {
    
    // 외부에서 날짜를 주입받음으로써, 언제든 원하는 날짜로 테스트가 가능해진다.
    public long calculateDiscount(long originalPrice, LocalDate targetDate) {
        if (targetDate.getMonth() == Month.DECEMBER) {
            return (long) (originalPrice * 0.9);
        }
        return originalPrice;
    }
}

 

// 단위 테스트 (12월 정책 검증)
@Test
@DisplayName("12월에는 10% 할인이 적용되어야 한다")
void discountTest_December() {
    // given
    DiscountService service = new DiscountService();
    LocalDate christmas = LocalDate.of(2026, 12, 25); // 통제 가능한 시간 주입
    
    // when
    long discountedPrice = service.calculateDiscount(10000L, christmas);
    
    // then
    assertThat(discountedPrice).isEqualTo(9000L); // 확신을 가지고 리팩토링 가능
}

 

이렇게 TDD 사이클(Red-Green-Refactor)을 거치면 클래스의 책임이 명확해지고(SRP), 결합도가 낮아지며, 확장에 열려있는(OCP) 클린 코드가 자연스럽게 도출된다.

 

4. 코드의 공동 소유(Collective Ownership)와 페어 프로그래밍

또 하나의 주목해야 할 XP의 실천법은 코드의 공동 소유다.

"결제 로직은 OOO만 아니까 OOO님 휴가 가면 결제 쪽 버그 수정은 다음 주로 미루자"

실무에서 흔이 겪는 버스 팩터(Bus Factor: 특정 팀원이 이탈했을 때 프로젝트가 마비되는 지수)의 문제다.

 

XP는 이를 해결하기 위해 페어 프로그래밍(Pair Programming)과 엄격한 코드 리뷰(Code Review)를 권장한다. 두 명의 개발자가 하나의 키보드를 공유하며 실시간으로 코드를 짜면, 도메인 지식과 개발 노하우가 자연스럽게 동기화된다. 팀 전체가 시스템의 모든 코드를 이해하고 수정할 수 있는 상태, 이것이 애자일팀이 병목 없이 속도를 내는 비결이다.

 

5. 결론

스크럼(Scrum)이나 칸반(Kanban)이 요구사항을 효율적으로 관리하고 팀의 소통을 돕는 뼈대(프로세스)라면 XP와 TDD는 그 뼈대에 살을 붙이고 시스템이 무너지지 않게 지탱하는 근육(엔지니어링)이다.

 

관리자 레벨에서는 눈에 보이는 지라 보드와 스프린트 진행도에 집착하기 쉽다. 하지만 시니어 엔지니어라면, 프로세스의 화려함에 속지 않고 묵묵히 테스트 코드를 짜고 자동화 파이프라인을 구축하는 기술적 실천(Enginnering Practice)을 팀 문화로 정착시키는 데 앞장서야 한다. 탄탄한 엔지니어링 없는 애자일은 그저 재앙을 향해 전력 질주 하는 것과 다름없다.

+ Recent posts