본문 바로가기

결합도와 응집도

모듈 사이의 의존과 내부 목적을 구별하고 실제 변경 실험으로 경계를 평가합니다.

프로그램을 파일 열 개로 나누었는데 파일 하나를 고칠 때 아홉 개도 함께 바꿔야 한다면, 나누기 전보다 이해하기 쉽다고 말하기 어렵습니다. 반대로 같은 규칙을 고칠 때 한 모듈 안에서 필요한 부분을 찾을 수 있다면 변경을 좁힐 수 있습니다. 결합도는 모듈 사이, 응집도는 모듈 안을 보는 기준입니다.

이 본문은 추상적인 점수를 매기기보다 ‘무엇이 바뀌었을 때 어디가 영향을 받는가’를 비교합니다. 앞 본문의 책임·공개 입력·부수효과를 구별할 수 있으면 진행할 수 있습니다.

결합도는 의존을 읽는 질문이다

결합도(coupling)는 모듈 사이의 의존 정도입니다. A가 B의 함수 이름만 알고 있는지, 내부 배열 구조·파일 경로·전역 변수까지 알고 있는지에 따라 변경 영향이 달라집니다. 호출 자체를 없애야 한다는 뜻은 아닙니다. 모듈은 협력해야 기능을 완성합니다.

예를 들어 신청 정책 함수가 application.cache.current.sessions[0].members를 읽으면 캐시의 구조와 첫 번째 위치에 의존합니다. 캐시를 없애거나 배열 순서를 바꾸면 정원 규칙은 그대로여도 정책 함수가 깨집니다. 필요한 participantCount와 capacity를 받으면 그런 저장 구조 의존을 줄일 수 있습니다.

작은 인터페이스라도 의미가 모호하면 결합이 약하다고 보기 어렵습니다. check(3, 5, true)는 숫자 단위와 true 의미를 호출자가 알아야 합니다. 반면 이름 붙은 객체 입력은 어떤 정보가 필요한지 드러냅니다. 인수가 무조건 적으면 좋다는 규칙보다 필요한 의미와 변화 가능성을 봅니다.

응집도는 함께 묶일 이유를 읽는 질문이다

응집도(cohesion)는 모듈 내부의 요소들이 하나의 목적에 얼마나 관련되어 있는지입니다. 참가 명단 조회·추가·제거는 같은 명단의 규칙을 관리하므로 모일 이유가 있습니다. 파일 이름 결정·장학 계산·UI 색상 선택이 ‘오늘 추가했다’는 이유로 misc에 모이면 함께 변경할 이유가 약합니다.

높은 응집도는 코드 내용이 짧다는 뜻도 같은 단어가 들어간다는 뜻도 아닙니다. ‘출력’이라는 이름으로 모임 안내 출력과 영수증 파일 저장을 묶었는데 문구와 회계 형식이 독립적으로 변한다면 목적이 갈라집니다. 필요한 기능을 이해할 때 무관한 규칙까지 읽어야 하는지 확인합니다.

높은 응집도와 낮은 결합도는 서로 보완하는 방향입니다. 내부에 같은 책임을 모아 두고 외부에는 필요한 계약을 드러내면 내부 수정이 밖으로 덜 퍼집니다. 모든 의존이 사라지거나 모든 변경이 한 파일로 끝난다는 보장은 아닙니다.

SOFTWARE ENGINEERING · CH4-2

저장 구조 대신 필요한 사실로 계산한다

정원 계산은 참가자 수·정원만 읽고 파일 이름을 알지 않습니다.

저장 구조 대신 필요한 사실로 계산한다저장된 모임에서 참가자 수와 정원을 추출해 자리 계산과 안내 표현에 전달하며 계산은 저장 파일 이름에 의존하지 않습니다.저장된 모임목록 · 제목 · 저장 위치필요한 사실로 변환참가자 수 · 정원 · 제목자리 계산과 표현파일 경로를 읽지 않음

저장된 모임에서 참가자 수와 정원을 추출해 자리 계산과 안내 표현에 전달하며 계산은 저장 파일 이름에 의존하지 않습니다.

전통적인 분류를 실제 코드에 연결한다

원자료에는 결합도·응집도의 전통적인 이름이 나옵니다. 이름 순서를 암기하는 대신 현재 코드에서 어떤 위험을 찾는지 연결합니다.

결합의 형태관찰할 예개선 질문
데이터 결합필요한 숫자·ID를 인수로 전달값의 의미·단위가 명확한가
스탬프 결합거대한 객체를 넘겨 일부만 사용불필요한 구조까지 알아야 하는가
제어 결합플래그로 상대의 내부 동작을 선택서로 다른 책임을 별 함수로 드러낼 수 있는가
공통 결합여러 모듈이 전역 명단을 함께 수정상태 소유자와 변경 통로가 분명한가
내용 결합상대 내부 데이터·처리를 직접 변경공개 계약으로 협력할 수 있는가

객체 하나를 넘겼다고 자동으로 나쁜 스탬프 결합은 아닙니다. 모임 도메인 정책이 모임 속성을 함께 읽는 것은 자연스러울 수 있습니다. 외부 형식이 바뀔 때 불필요한 영향이 생기는지를 실제 변경으로 봅니다. 제어 플래그도 표현이 명확한 옵션에는 쓸 수 있지만 mode 값에 따라 무관한 업무를 처리하면 책임이 흐려집니다.

응집도는 기능적·순차적·통신적·절차적·시간적·논리적·우연적 등의 이름으로 설명되기도 합니다. 하나의 목적을 수행하는 기능적 묶음, 앞 결과가 다음 입력인 순차적 묶음, 같은 데이터를 처리하는 통신적 묶음과 비교해 ‘같은 시간에 실행한다’거나 ‘비슷한 종류다’라는 이유만으로 묶은 코드의 변경 위험을 살펴봅니다. 이 분류는 설계 토론을 돕는 관점이며 정확한 품질을 숫자로 보장하는 측정법은 아닙니다.

필요한 데이터만 받는 실행

hasAvailableSeat는 자리 여부만 계산합니다. 이 함수가 true라고 신청이 무조건 성공하는 것은 아닙니다. 회원 ID·종료·중복 검사도 필요하기 때문입니다. availabilityText는 안내에 필요한 제목과 인원 수를 읽으며 참가자들의 실제 ID는 받지 않습니다.

저장소 최상위 폴더에서 다음 명령을 실행합니다. Node.js가 설치되어 있어야 하며 패키지를 추가로 설치하지 않습니다. 두 번째 인수를 생략하면 이 장의 예제를 모두 실행합니다.

node dev-note/scripts/software-engineering-lab/examples/ch4.mjs ch4-2
ch4-2 실행 예제
function hasAvailableSeat({ participantCount, capacity }) {
  return participantCount < capacity;
}
function availabilityText({ title, participantCount, capacity }) {
  const remaining = Math.max(0, capacity - participantCount);
  return title + ": 남은 자리 " + remaining;
}

const session = {
  id: "book-05", title: "설계를 읽다",
  capacity: 3, status: "OPEN", participants: ["member-a"],
};
const counts = {
  title: session.title,
  participantCount: session.participants.length,
  capacity: session.capacity,
};
console.log("자리 있음:", hasAvailableSeat(counts));
console.log(availabilityText(counts));
const renamedStorage = { fileName: "new-data.json", session };
console.log("저장 위치 변경 후:", hasAvailableSeat(counts));

예상 출력은 자리 있음: true, 설계를 읽다: 남은 자리 2, 저장 위치 변경 후: true입니다. renamedStorage의 파일 이름을 바꿔도 두 계산은 그 객체를 읽지 않기 때문에 영향을 받지 않습니다. 저장 파일 이름을 숨겼다는 설계 주장은 이렇게 실제 의존 여부로 확인합니다.

Math.max는 안내 숫자가 음수로 표시되지 않게 합니다. 이 표시 보정이 정원 초과 상태를 정상으로 바꾸는 것은 아닙니다. 명단이 정원을 넘는 입력은 데이터 검증에서 별도로 찾아야 합니다. 화면 문구와 도메인 불변식의 책임을 섞지 않습니다.

분리를 평가하는 작은 변경 실험

첫 변경은 ‘남은 자리’ 문구 수정입니다. 표현 함수만 바꾸고 자리 계산 결과가 같은지 확인합니다. 둘째 변경은 참가자를 배열 대신 조회 결과로 받는 것입니다. 변환 경계에서 인원 수를 만들면 자리 함수는 유지할 수 있습니다. 셋째 변경은 ‘운영자는 정원에서 제외한다’는 업무 변경입니다. 참가자 수의 의미가 달라져 정책과 집계 계약도 재검토해야 합니다.

이처럼 표현 변경·저장 변경·업무 변경이 같은 범위에 영향을 주지는 않습니다. 모든 변경을 막겠다는 설계보다, 예상 변경과 영향 범위를 설명하는 설계가 검토하기 쉽습니다.

반례: 여러 전역 변수를 작은 함수가 함께 수정한다

함수 한 개가 두 줄이어도 전역 participants, availableSeats, lastMessage를 함께 바꾸면 순서와 동기화에 의존합니다. participants만 갱신하고 availableSeats를 빠뜨리면 같은 사실이 두 곳에서 달라집니다. 작은 함수 수를 늘리기보다 상태를 한 곳에서 관리하고 인원 수를 계산할 방법을 검토합니다.

또한 배열을 모듈 밖으로 돌려주어 호출자가 push할 수 있으면 이름만 캡슐화한 것입니다. 다음 본문에서는 공개 연산을 통해 내부 상태를 보호하는 방법을 확인합니다.

독립 문제: 알림 도구의 경계를 다시 그린다

notifyTools에는 수업 취소 이메일 본문 만들기, SMS 전송, 이미지 크기 변환, 출석 점수 계산이 있습니다. 이메일 문구는 교무 담당자가, 전송 업체는 운영 담당자가 바꿉니다. 테스트에서 실제 SMS가 보내지는 것도 문제입니다.

새 모듈 묶음을 제안하고 전송 업체 변경이 점수 계산에 영향을 주지 않게 할 입력·출력 계약을 씁니다. ‘모두 알림 전에 실행하므로 시간적 응집이 있다’는 이유로 원래 묶음을 유지하는 판단을 평가합니다.

힌트

함께 실행되는 시간과 함께 바뀌는 목적은 다릅니다. 본문 생성은 문자열을 반환하게, 실제 전송은 별도 부수효과 경계에 두어 생각합니다.

풀이

buildCancellationMessage는 수업·취소 이유에서 메시지를 만들고, sendMessage는 수신 대상과 메시지를 받아 업체에 전달합니다. 이미지 변환과 점수 계산은 알림과 다른 책임이므로 별도 모듈입니다. 테스트는 메시지 생성 결과와 전송에 넘기는 값을 검증하고 실제 업체 연결은 별도로 확인합니다.

같은 시점에 실행된다는 이유만으로 묶으면 이미지 형식 변경이 알림 도구 전체에 영향을 줍니다. 업무 흐름을 연결하는 상위 함수는 둘 수 있지만, 각각의 책임과 계약은 분리해야 변경을 이해하기 쉽습니다.

다시 볼 기준

결합도는 필요한 의존을 명확히 남기고 불필요한 내부 의존을 줄이는 기준입니다. 응집도는 같은 책임을 이해하는 데 필요한 요소를 모으는 기준입니다. 다음 본문에서 객체와 인터페이스로 이 경계를 표현하되, 클래스 문법을 쓰는 것만으로 품질이 보장되지 않는다는 점을 확인합니다.


원자료와 더 읽기

장원사이버평생교육원 「소프트웨어공학」 PDF 물리 159–169쪽의 주제를 바탕으로 설명·도표·실습을 새로 구성했습니다.

JavaScript 파일의 공개 경계는 MDN 모듈 안내를 참고합니다. 전통적인 분류는 이름의 서열보다 변경 영향의 사례로 사용합니다.