← 블로그로 돌아가기

연동 분기가 몰린 300줄 메서드를 변경 이유별 5단계로 나누기

기존 기능 위에 외부 시스템 연동이 계속 덧붙으며 300줄 이상으로 커진 교육 기능 메서드를, '변경되는 이유'를 기준으로 5개 단계로 분리하고 연동 처리를 핵심 업무 로직에서 떼어낸 과정을 정리했습니다.

기존 기능 위에 외부 시스템 연동이 계속 덧붙으면서 커진 메서드를, 한 번에 갈아엎지 않고 변경되는 이유가 다른 것끼리 나누는 기준으로 정리한 과정을 기록합니다.


1. 배경: 기존 기능 위에 연동이 계속 덧붙었다

AIMS는 보험 설계사의 영업을 지원하는 시스템입니다. 원래는 영업지원 업무 전반을 AIMS가 맡고 있었는데, 일부 기능을 새 영업지원 시스템인 Phase2로 옮기고 Phase2에 신규 기능을 더하면서 두 시스템이 데이터를 주고받으며 함께 동작하는 구조가 됐습니다.

이 과정에서 AIMS의 기존 기능에는 “이 데이터가 Phase2와 연동되는가”에 따른 분기가 하나씩 추가됐습니다. 처음에는 조건문 몇 줄이었지만, 연동 대상과 상황이 늘어날수록 분기가 한 메서드 안에 쌓였습니다.

그 결과 이런 코드가 늘어났습니다.

  • 한 메서드가 여러 업무 상황과 연동 여부를 모두 판단한다.
  • 한 가지 요구사항을 고치려 해도 메서드 전체를 읽어야 영향 범위를 알 수 있다.
  • 연동과 관계없는 수정이 연동 흐름을 건드려 오류가 생긴다.

2. 한 번에 고치지 않고, 손대는 구간부터 정리했다

영역 규모가 커서 전면 재작성은 무리가 있었습니다. 레거시 코드 전체를 한꺼번에 리팩토링하면 변경 범위가 너무 커지고, 운영 중인 기능의 동작을 모두 다시 검증해야 합니다.

그래서 기준을 정했습니다.

  1. 연동 오류가 잦고 요구사항 변경이 많은 구간부터 다룬다.
  2. 별도 리팩토링 일정을 잡기보다, 오류 분석이나 수정 요청이 들어왔을 때 해당 구간을 함께 정리한다.

어차피 읽고 수정해야 하는 코드를 정리하는 방식이라, 리팩토링만을 위한 별도 검증 비용을 줄이면서 개선을 이어 갈 수 있었습니다.


3. 대표 사례: 교육 기능

가장 대표적인 구간이 교육 기능이었습니다. Phase2의 미디어센터와 연동이 붙으면서, 교육 정보를 저장하는 메서드 하나가 다음 상황을 모두 처리하게 됐습니다.

  • AIMS 전용 교육과 Phase2 연동 교육
  • 교육 정보 수정
  • 신청자 수정
  • 일괄 신청
  • 일괄 취소

이 분기들이 한 메서드에 몰려 300줄 이상으로 커졌고, 이 영역에서 연동 관련 오류가 반복되고 있었습니다.

구조를 단순화하면 다음과 같은 모양이었습니다. 아래 코드는 설명을 위해 단순화한 예시이며 실제 코드가 아닙니다.

// 설명용으로 단순화한 예시. 실제 코드는 각 블록이 수십 줄이었다.
public void saveEducation(EducationRequest request) {
    // 교육 정보 수정
    if (request.isUpdate()) {
        // ... 교육 정보 저장
        if (isPhase2Linked(request)) {
            // ... Phase2 전송
        }
    }

    // 신청자 변경
    for (Applicant applicant : request.getApplicants()) {
        if (applicant.isNew()) {
            // ... 신청 처리
            if (isPhase2Linked(request)) {
                // ... Phase2 전송
            }
        } else if (applicant.isRemoved()) {
            // ... 취소 처리
            if (isPhase2Linked(request)) {
                // ... Phase2 전송
            }
        }
    }
    // ... 상황별 분기가 계속 이어짐
}

업무 처리와 연동 여부 판단이 섞여 있어서, 예를 들어 “일괄 취소 조건을 바꿔 달라”는 요청에도 연동 분기까지 함께 읽고 수정해야 했습니다.


4. 분리 기준: 변경되는 이유가 다른 것끼리 나눈다

메서드를 단순히 길이로 자르면, 나눈 뒤에도 하나의 요구사항을 바꾸려면 여러 메서드를 함께 고쳐야 하는 문제가 남습니다.

그래서 **“이 코드는 어떤 이유로 바뀌는가”**를 기준으로 나눴습니다. 같은 이유로 함께 바뀌는 코드는 한곳에 모으고, 서로 다른 이유로 바뀌는 코드는 떼어냈습니다.

단계이 단계가 바뀌는 이유
교육 정보 수정교육 자체의 정보 항목이나 저장 규칙이 바뀔 때
신청자 분류어떤 신청자를 신규·유지·취소로 볼지 판단 기준이 바뀔 때
일괄 취소취소 조건이나 취소 처리 방식이 바뀔 때
일괄 신청신청 조건이나 신청 처리 방식이 바뀔 때
Phase2 연동Phase2와 주고받는 데이터 형식이나 연동 방식이 바뀔 때

분리한 뒤의 흐름은 다음과 같습니다. 마찬가지로 설명용 예시입니다.

// 설명용으로 단순화한 예시
public void saveEducation(EducationRequest request) {
    updateEducationInfo(request);                       // 1. 교육 정보 수정
    ApplicantGroups groups = classifyApplicants(request); // 2. 신청자 분류
    cancelApplicants(groups.toCancel());                // 3. 일괄 취소
    applyApplicants(groups.toApply());                  // 4. 일괄 신청
    syncToPhase2(request);                              // 5. Phase2 연동
}

이제 최상위 메서드는 무엇을 어떤 순서로 하는지만 보여 주고, 각 단계의 세부 규칙은 해당 메서드 안에서만 다룹니다.


5. 연동 처리를 핵심 업무 로직에서 떼어냈다

가장 중요하게 본 부분은 Phase2 연동을 별도 단계로 분리한 것입니다.

분리 전에는 교육 정보 수정, 신청, 취소 같은 업무 처리 곳곳에 “연동 교육이면 Phase2에 보낸다”는 분기가 끼어 있었습니다. 분리 후에는 1~4단계의 핵심 업무 로직이 외부 연동 여부를 몰라도 되도록 구성했습니다. 연동이 필요한지 판단하고 데이터를 보내는 일은 5단계만 맡습니다.

이렇게 나누면서 얻은 효과는 다음과 같습니다.

  • 업무 규칙을 바꿀 때 연동 코드를 건드리지 않는다.
  • 연동 방식이 바뀌어도 업무 로직은 그대로 둔다.
  • 연동 오류가 생기면 확인할 곳이 한 단계로 좁혀진다.

6. 연동 방식은 Phase2 팀과 함께 바꿨다

연동을 한 단계로 모으면서, 연동 시에는 전체 신청 데이터를 일괄 전송하는 방식으로 정리했습니다.

다만 일괄 전송에는 실패했을 때 되돌릴 방법이 필요했습니다. AIMS 쪽 처리만 되돌려서는 두 시스템의 데이터가 어긋날 수 있었기 때문에, Phase2 팀과 협업해 Phase2 측에도 롤백 기능을 반영했습니다.

레거시 코드 정리가 AIMS 내부만의 문제가 아니라, 연동 상대 시스템과의 약속을 다시 맞추는 일이기도 하다는 점을 확인한 부분이었습니다.


7. 의도와 외부 제약은 주석으로 남겼다

분리한 각 단계에는 왜 이 단계가 따로 있는지와 외부 시스템 때문에 생긴 제약을 주석으로 남겼습니다.

코드만 보면 “왜 이렇게 했을까” 싶은 부분 중 상당수는 Phase2와의 연동 조건 때문에 생긴 것이었습니다. 이런 제약은 코드에 드러나지 않기 때문에, 다음에 이 코드를 고칠 사람이 같은 분석을 반복하지 않도록 기록해 두었습니다.


8. 검증: 단계적으로 바꾸고 전후 동작을 비교했다

동작을 바꾸지 않고 구조만 바꾸는 작업이기 때문에, 변경 전과 후의 동작이 같은지 확인하는 것이 핵심이었습니다.

  • 한 번에 모든 분기를 옮기지 않고 단계적으로 변경했습니다.
  • 단위 테스트로 각 단계의 동작을 확인했습니다.
  • 테스트 환경에서 통합 테스트를 진행해 Phase2 연동을 포함한 전체 흐름이 변경 전과 같은지 확인했습니다.

9. 결과

  • 300줄 이상이던 메서드를 5개 단계, 주요 메서드 60줄 이하 단위로 분리했습니다.
  • 이후 해당 영역에서 연동 오류가 재발하지 않았습니다.
  • 수정 요청이 들어오면 어느 단계를 고쳐야 하는지 먼저 정하고 그 단계만 읽으면 되는 구조가 됐습니다.

10. 회고

잘한 점

  • 메서드 길이가 아니라 변경되는 이유를 기준으로 나눈 것이 가장 효과가 컸습니다. 나눈 뒤에도 요구사항 하나가 여러 메서드에 흩어지지 않았습니다.
  • 연동을 별도 단계로 떼어내면서, 연동 오류를 볼 때 확인할 곳이 분명해졌습니다.
  • 운영 중인 시스템에서 별도 리팩토링 일정 없이, 원래 손대야 하는 구간부터 정리하는 방식으로 개선을 이어 갈 수 있었습니다.

배운 것

  1. 레거시 코드는 손대는 구간부터 정리한다 — 전체를 한 번에 바꾸기보다 오류와 변경이 잦은 곳부터 다룬다.
  2. 나누는 기준은 길이가 아니라 변경 이유다 — 같은 이유로 바뀌는 코드끼리 모은다.
  3. 외부 연동은 핵심 업무 로직에서 떼어낸다 — 업무 로직이 연동 여부를 모르게 하면 양쪽 변경이 서로를 건드리지 않는다.
  4. 연동 구조를 바꾸면 상대 시스템과도 약속을 맞춘다 — 실패와 롤백은 한쪽 시스템만으로 완성되지 않는다.
  5. 코드에 드러나지 않는 제약은 기록으로 남긴다 — 다음 사람이 같은 분석을 반복하지 않게 한다.