본문 바로가기

유지보수와 기술 부채

수정·적응·완전화·예방 유지보수의 목적을 구분하고 동작 보존 변경을 회귀 증거와 연결합니다.

운영 중 “같은 참가자가 중복으로 보인다”는 결함과 “새 Node.js 환경에서 실행하고 싶다”는 요청은 모두 변경이지만 이유가 다릅니다. 유지보수는 전달 후에도 소프트웨어를 계속 적합하게 만드는 활동입니다.

변경의 네 목적

수정 유지보수는 발견한 오류를 고칩니다. 적응 유지보수는 실행 환경·외부 인터페이스의 변화에 맞춥니다. 완전화 유지보수는 사용성·기능·성능 등을 개선합니다. 예방 유지보수는 미래의 오류나 변경 위험을 줄입니다. 한 변경이 여러 목적을 가질 수도 있습니다.

모임의 중복 검사 순서 수정은 오류 교정이고, 저장 모듈의 지원 환경 변경은 적응입니다. 오류 안내를 이해하기 쉽게 바꾸는 일은 개선이고, 저장 유효성 검사를 명확히 분리하는 일은 예방 목적을 가질 수 있습니다. 유형 이름을 붙인 뒤 실제 동작 변경·위험·검증을 따로 분석합니다.

리팩터링과 기술 부채

리팩터링은 관찰 가능한 동작을 보존하며 내부 구조를 개선하는 변경입니다. 결과 코드 우선순위를 바꾸는 일은 요구 변경 또는 오류 수정이지 단순 리팩터링이 아닙니다. 동작 보존의 범위에는 정상 결과뿐 아니라 거절 상태·오류 계약도 포함됩니다.

기술 부채는 편의를 위해 선택한 구조나 미뤄 둔 작업이 이후 변경 비용·위험을 만드는 상태로 설명할 수 있습니다. 오래된 코드나 마음에 안 드는 이름을 모두 부채라고 부르지 않습니다. 왜 남았는지, 어떤 위험이 있는지, 언제 해결할지 기록합니다.

변경 목적을 먼저 확인하고 동작 보존을 검증한다

취소 표현을 바꾸되 외부 계약은 보존한다. 이전 상태도 유지되어야 한다. 각 단계의 값과 전환 이유를 비교합니다.

SOFTWARE ENGINEERING · READING CLUB

변경 목적을 먼저 확인하고 동작 보존을 검증한다

같은 취소 함수라도 오류 수정과 리팩터링의 기대 변화는 다릅니다.

변경 목적을 먼저 확인하고 동작 보존을 검증한다변경 이유: 정책 결함? 환경 변화?, 구조 개선? 미래 위험? → 기대 동작 범위: 오류 교정: 의도한 변화, 리팩터링: 외부 계약 유지 → 회귀와 개선 증거: 취소 결과·이전 상태, 변경·검토 비용의 변화변경 이유정책 결함? 환경 변화?구조 개선? 미래 위험?선택기대 동작 범위오류 교정: 의도한 변화리팩터링: 외부 계약 유지확인회귀와 개선 증거취소 결과·이전 상태변경·검토 비용의 변화
변경 목적을 먼저 확인하고 동작 보존을 검증한다변경 이유: 정책 결함? 환경 변화?, 구조 개선? 미래 위험? → 기대 동작 범위: 오류 교정: 의도한 변화, 리팩터링: 외부 계약 유지 → 회귀와 개선 증거: 취소 결과·이전 상태, 변경·검토 비용의 변화
변경 이유정책 결함? 환경 변화?구조 개선? 미래 위험?
선택선택변경 이유 · 정책 결함? 환경 변화? · 구조 개선? 미래 위험? → 기대 동작 범위 · 오류 교정: 의도한 변화 · 리팩터링: 외부 계약 유지
기대 동작 범위오류 교정: 의도한 변화리팩터링: 외부 계약 유지
확인확인기대 동작 범위 · 오류 교정: 의도한 변화 · 리팩터링: 외부 계약 유지 → 회귀와 개선 증거 · 취소 결과·이전 상태 · 변경·검토 비용의 변화
회귀와 개선 증거취소 결과·이전 상태변경·검토 비용의 변화

줄 수가 줄었다는 사실만으로 개선을 확정하지 않습니다. 요구 변화와 동작 보존 변경을 구분하여 기록합니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 변경 목적부터 회귀 확인까지 진행합니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
변경 목적

취소 표현을 바꾸되 외부 계약은 보존한다.

  • 표현·관측 결과 1: before [min,hana]
  • 표현·관측 결과 2: 취소 대상 min
  • 보존할 기대값 확정
새 표현 실행

내부 표현의 차이보다 결과·상태를 비교한다.

  • 표현·관측 결과 1: CANCELLED
  • 표현·관측 결과 2: after [hana]
  • min만 제거
회귀 확인

이전 상태도 유지되어야 한다.

  • 표현·관측 결과 1: before [min,hana] 유지
  • 표현·관측 결과 2: after [hana]
  • 관찰 동작 보존 단언; 전체 경계의 완전한 검증으로 확대 금지

변경 목적을 먼저 확인하고 동작 보존을 검증한다

취소 표현을 바꾸되 외부 계약은 보존한다. 이전 상태도 유지되어야 한다. 각 단계의 값과 전환 이유를 비교합니다.

SOFTWARE ENGINEERING · READING CLUB

변경 목적을 먼저 확인하고 동작 보존을 검증한다

같은 취소 함수라도 오류 수정과 리팩터링의 기대 변화는 다릅니다.

변경 목적을 먼저 확인하고 동작 보존을 검증한다변경 이유: 정책 결함? 환경 변화?, 구조 개선? 미래 위험? → 기대 동작 범위: 오류 교정: 의도한 변화, 리팩터링: 외부 계약 유지 → 회귀와 개선 증거: 취소 결과·이전 상태, 변경·검토 비용의 변화변경 이유정책 결함? 환경 변화?구조 개선? 미래 위험?선택기대 동작 범위오류 교정: 의도한 변화리팩터링: 외부 계약 유지확인회귀와 개선 증거취소 결과·이전 상태변경·검토 비용의 변화
변경 목적을 먼저 확인하고 동작 보존을 검증한다변경 이유: 정책 결함? 환경 변화?, 구조 개선? 미래 위험? → 기대 동작 범위: 오류 교정: 의도한 변화, 리팩터링: 외부 계약 유지 → 회귀와 개선 증거: 취소 결과·이전 상태, 변경·검토 비용의 변화
변경 이유정책 결함? 환경 변화?구조 개선? 미래 위험?
선택선택변경 이유 · 정책 결함? 환경 변화? · 구조 개선? 미래 위험? → 기대 동작 범위 · 오류 교정: 의도한 변화 · 리팩터링: 외부 계약 유지
기대 동작 범위오류 교정: 의도한 변화리팩터링: 외부 계약 유지
확인확인기대 동작 범위 · 오류 교정: 의도한 변화 · 리팩터링: 외부 계약 유지 → 회귀와 개선 증거 · 취소 결과·이전 상태 · 변경·검토 비용의 변화
회귀와 개선 증거취소 결과·이전 상태변경·검토 비용의 변화

줄 수가 줄었다는 사실만으로 개선을 확정하지 않습니다. 요구 변화와 동작 보존 변경을 구분하여 기록합니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 변경 목적부터 회귀 확인까지 진행합니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
변경 목적

취소 표현을 바꾸되 외부 계약은 보존한다.

  • 표현·관측 결과 1: before [min,hana]
  • 표현·관측 결과 2: 취소 대상 min
  • 보존할 기대값 확정
새 표현 실행

내부 표현의 차이보다 결과·상태를 비교한다.

  • 표현·관측 결과 1: CANCELLED
  • 표현·관측 결과 2: after [hana]
  • min만 제거
회귀 확인

이전 상태도 유지되어야 한다.

  • 표현·관측 결과 1: before [min,hana] 유지
  • 표현·관측 결과 2: after [hana]
  • 관찰 동작 보존 단언; 전체 경계의 완전한 검증으로 확대 금지

예를 들어 “현재 저장은 한 프로세스만 지원”은 의도적인 범위 제약입니다. 여러 서버로 확장하면서 그 제약을 그대로 둘 때 경쟁 신청의 위험과 해결 작업이 생깁니다. 제약을 숨기지 않는 것이 이후 계획의 출발점입니다.

취소 동작 보존 확인

examples/ch9.mjs의 앞부분을 실행합니다. 코어의 내부 취소 표현을 바꾸어도 결과·새 상태·이전 상태가 유지되는지 확인할 사례입니다.

import assert from "node:assert/strict";
import { createSession, register, cancel } from "../core.mjs";

// Preserve the external rule while changing the internal filtering expression.
const initial = createSession({ id: "book-1", title: "첫 책", capacity: 2 });
const before = register(register(initial, "min").state, "hana").state;
const after = cancel(before, "min");
assert.equal(after.code, "CANCELLED");
assert.deepEqual(after.state.participants, ["hana"]);
assert.deepEqual(before.participants, ["min", "hana"]);
console.log("maintenance regression PASS");

function completionRate({ completed, attempted }) {
  if (!Number.isInteger(attempted) || attempted <= 0 ||
      !Number.isInteger(completed) || completed < 0 || completed > attempted) {
    throw new RangeError("invalid observation counts");
  }
  return completed / attempted;
}
assert.equal(completionRate({ completed: 8, attempted: 10 }), 0.8);
console.log("task completion", completionRate({ completed: 8, attempted: 10 }));
console.log("sample is a teaching fixture, not observed user research");

maintenance regression PASS는 취소한 ID만 빠지고 이전 명단은 유지된다는 단언의 증거입니다. 뒤의 0.8은 다음 문서의 측정 예입니다. 이 사례만으로 모든 취소 경계가 보존됐다고 결론 내리지 않습니다. 마감 뒤 취소·없는 ID·잘못된 ID의 회귀도 코어 테스트에 포함됩니다.

변경 절차

재현 조건과 기존 계약을 확인하고, 가장 작은 동작 보존 변경을 만든 뒤 관련 회귀를 실행합니다. 새 구조가 실제로 읽기·변경·검증을 쉽게 했는지 검토합니다. 큰 이름 변경과 정책 변경을 한 덩어리로 처리하면 실패 원인을 찾기 어려워질 수 있습니다.

독립 문제: 무조건 다시 쓰자는 제안

팀원이 “옛 코드니까 전부 다시 쓰자”고 제안합니다. 현재 명단 복원은 안정적이고 요구 문서는 부족합니다. 결정 전에 확보할 근거와 안전하게 시작할 작은 작업을 제시하세요.

힌트

겉으로 보이는 나이보다 실제 변경 비용과 기존 동작의 증거를 확인합니다.

해설

실제 결함·변경 빈도·읽기 어려운 책임·지원 환경·복구 계약을 확인합니다. 먼저 정상 저장 복원과 중복·정원 거절을 특성화하는 테스트를 만들고, 저장 경계를 분리하는 작은 구조 개선으로 위험을 줄일 수 있습니다. 요구를 모르는 상태의 전체 재작성은 숨은 계약을 잃을 수 있습니다. 전체 재작성의 이익과 비용이 입증되면 별도로 계획합니다.

검증 없이 “더 깔끔하다”는 인상만으로 변경을 승인하지 않습니다. 다음 문서에서 측정과 검토를 개선의 근거로 사용하는 방법을 살핍니다.