응집도란?
응집도는 모듈(함수, 클래스 등) 내부의 구성 요소들이 서로 얼마나 밀접하게 관련되어 있는지를 나타내는 개념입니다.
응집도 종류 영문명 설명
| 우연적 응집 |
Coincidental Cohesion |
관련 없는 기능들이 모여 있음 |
| 논리적 응집 |
Logical Cohesion |
논리적으로 관련된 기능을 묶음 |
| 시간적 응집 |
Temporal Cohesion |
같은 시(예: 초기화)에 수행되는 기능을 묶음 |
| 절차적 응집 |
Procedural Cohesion |
순서에 따라 수행되는 기능을 묶음 |
| 통신적 응집 |
Communicational Cohesion |
동일 데이터를 처리하는 기능을 묶음 |
| 기능적 응집 |
Functional Cohesion |
하나의 명확한 기능을 수행 |
| 내용적(정보적) 응집 |
Informational Cohesion |
여러 기능이 공통 데이터를 공유 |
① 우연적 응집 (Coincidental Cohesion)
서로 아무 관련없고 상관이 없는 기능들이 하나의 모듈에 들어 있는 형태를 의미합니다.
이렇게되면 유지보수도 어렵고 코드의 목적도모르게 됩니다.
// ============================================
// 1. 우연적 응집도 (Coincidental Cohesion) - 최악
// ============================================
// 모듈의 구성 요소들이 아무런 관련이 없음
// 단지 같은 파일에 있다는 이유로 묶여있음
class CoincidentalCohesion {
// 전혀 관련 없는 기능들이 한 클래스에 존재
printReport() {
console.log("리포트 출력");
}
calculateTax(amount: number): number {
return amount * 0.1;
}
sendEmail(to: string, message: string) {
console.log(`이메일 전송: ${to}`);
}
validatePassword(password: string): boolean {
return password.length >= 8;
}
}
② 논리적 응집 (Logical Cohesion)
논리적으로 관련된 기능들을 하나의 모듈에 묶은 형태입니다.
논리적으로는 유사하지만 실제로 동작은 서로 다릅니다.
예) 모든 입력 처리 라는 주제만 같고 나머지로직은 다릅니다.
// ============================================
// 2. 논리적 응집도 (Logical Cohesion)
// ============================================
// 유사한 성격의 작업들을 묶었지만, 실제로는 다른 작업을 수행
// flag나 매개변수로 어떤 작업을 할지 결정
class LogicalCohesion {
// 입력을 처리하지만, 각각 다른 타입의 입력을 처리
handleInput(type: 'mouse' | 'keyboard' | 'touch', data: any) {
switch(type) {
case 'mouse':
this.handleMouseInput(data);
break;
case 'keyboard':
this.handleKeyboardInput(data);
break;
case 'touch':
this.handleTouchInput(data);
break;
}
}
private handleMouseInput(data: any) {
console.log("마우스 입력 처리");
}
private handleKeyboardInput(data: any) {
console.log("키보드 입력 처리");
}
private handleTouchInput(data: any) {
console.log("터치 입력 처리");
}
}
③ 시간적 응집 (Temporal Cohesion)
같은 시점에 실행되는 기능들을 묶은 형태의 응집도입니다.
실행 시점이 비슷하지만 기능과 목적은 다릅니다.
예) 프로그램의 시작 또는 종료
// ============================================
// 3. 시간적 응집도 (Temporal Cohesion)
// ============================================
// 특정 시점에 함께 실행되어야 하는 기능들을 묶음
// 초기화나 종료 시에 관련 없는 작업들을 함께 처리
class SystemInitializer {
// 시스템 시작 시 실행되어야 하는 것들을 모아놓음
// 각 작업은 서로 직접적인 관련이 없음
initialize() {
this.connectToDatabase();
this.loadConfiguration();
this.startLogging();
this.initializeCache();
this.setupEventHandlers();
}
private connectToDatabase() {
console.log("데이터베이스 연결");
}
private loadConfiguration() {
console.log("설정 로드");
}
private startLogging() {
console.log("로깅 시작");
}
private initializeCache() {
console.log("캐시 초기화");
}
private setupEventHandlers() {
console.log("이벤트 핸들러 설정");
}
}
④ 절차적 응집 (Procedural Cohesion)
특정 순서에 따라 처리되어야 하는 기능들을 묶은 형태입니다.
순서는 있지만 데이터의 전달이 없이 각각 따로 실행되며 작업이 절차적으로만 이루어 집니다.
// ============================================
// 4. 절차적 응집도 (Procedural Cohesion)
// ============================================
// 순차적으로 실행되는 작업들을 묶음
// 작업들이 순서대로 실행되지만, 데이터를 공유하지 않을 수 있음
class FileProcessor {
// 파일 처리 절차를 순차적으로 실행
processFile(filename: string) {
this.openFile(filename);
this.readData();
this.validateData();
this.closeFile();
}
private openFile(filename: string) {
console.log(`파일 열기: ${filename}`);
}
private readData() {
console.log("데이터 읽기");
}
private validateData() {
console.log("데이터 검증");
}
private closeFile() {
console.log("파일 닫기");
}
}
⑤ 통신적 응집 (Communicational Cohesion)
같은 데이터를 처리하거나 동일한 입력/ 출력 데이터를 사용하는 기능들을 묶은 형태입니다. 공통 데이터의 중심이고 데이터처리 단계들이 하나로 묶여있습니다.
// ============================================
// 5. 통신적 응집도 (Communicational Cohesion)
// ============================================
// 같은 데이터를 사용하는 작업들을 묶음
// 여러 기능이 같은 입력 데이터나 출력 데이터를 공유
class CustomerReport {
// 같은 고객 데이터를 사용하는 여러 작업
generateCustomerReport(customerId: string) {
const customer = this.getCustomerData(customerId);
this.printCustomerInfo(customer);
this.printOrderHistory(customer);
this.printPaymentInfo(customer);
}
private getCustomerData(customerId: string) {
return {
id: customerId,
name: "홍길동",
orders: [],
payments: []
};
}
private printCustomerInfo(customer: any) {
console.log(`고객 정보: ${customer.name}`);
}
private printOrderHistory(customer: any) {
console.log(`주문 내역: ${customer.orders.length}건`);
}
private printPaymentInfo(customer: any) {
console.log(`결제 정보: ${customer.payments.length}건`);
}
}
⑥ 순차적 응집 (Sequential Cohesion)
한 기능의 출력이 다음 기능의 입력으로 사용되는 기능들을 묶은 형태입니다.
기능들이 순서대로 실행되고 데이터의 흐름(Data Flow)을 중심으로 연결되어 있습니다.
// ============================================
// 6. 순차적 응집도 (Sequential Cohesion)
// ============================================
// 한 작업의 출력이 다음 작업의 입력으로 사용되는 경우
// 데이터가 파이프라인처럼 흘러감
class DataPipeline {
// 각 단계의 출력이 다음 단계의 입력이 됨
processData(rawData: string): number {
const cleaned = this.cleanData(rawData);
const parsed = this.parseData(cleaned);
const validated = this.validateData(parsed);
const result = this.calculateResult(validated);
return result;
}
private cleanData(raw: string): string {
console.log("데이터 정제");
return raw.trim().toLowerCase();
}
private parseData(cleaned: string): string[] {
console.log("데이터 파싱");
return cleaned.split(',');
}
private validateData(parsed: string[]): string[] {
console.log("데이터 검증");
return parsed.filter(item => item.length > 0);
}
private calculateResult(validated: string[]): number {
console.log("결과 계산");
return validated.length;
}
}
⑦ 기능적 응집 (Functional Cohesion)
모든 기능이 하나의 명확한 목적 그리고 단일책임 원칙인 SRP를 잘 이행하고 있는 수준이고 유지보수성과 재사용성이 가장 좋습니다.
// ============================================
// 7. 기능적 응집도 (Functional Cohesion) - 최고
// ============================================
// 모든 구성 요소가 하나의 잘 정의된 작업을 수행
// 단일 책임 원칙(Single Responsibility Principle)을 따름
class EmailValidator {
// 이메일 유효성 검사라는 단일 기능만 수행
validate(email: string): boolean {
return this.hasValidFormat(email) &&
this.hasValidDomain(email) &&
this.hasValidLength(email);
}
private hasValidFormat(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
private hasValidDomain(email: string): boolean {
const domain = email.split('@')[1];
return domain !== undefined && domain.length > 0;
}
private hasValidLength(email: string): boolean {
return email.length >= 5 && email.length <= 255;
}
}
class PasswordHasher {
// 비밀번호 해싱이라는 단일 기능만 수행
hash(password: string): string {
const salt = this.generateSalt();
const hashed = this.computeHash(password, salt);
return this.formatResult(hashed, salt);
}
private generateSalt(): string {
return Math.random().toString(36).substring(7);
}
private computeHash(password: string, salt: string): string {
// 실제로는 bcrypt 같은 라이브러리 사용
return `hashed_${password}_${salt}`;
}
private formatResult(hash: string, salt: string): string {
return `${salt}:${hash}`;
}
}
class PriceCalculator {
// 가격 계산이라는 단일 기능만 수행
calculateTotal(basePrice: number, quantity: number, discountRate: number = 0): number {
const subtotal = this.calculateSubtotal(basePrice, quantity);
const discount = this.calculateDiscount(subtotal, discountRate);
const total = this.applyDiscount(subtotal, discount);
return this.roundPrice(total);
}
private calculateSubtotal(price: number, quantity: number): number {
return price * quantity;
}
private calculateDiscount(amount: number, rate: number): number {
return amount * rate;
}
private applyDiscount(amount: number, discount: number): number {
return amount - discount;
}
private roundPrice(amount: number): number {
return Math.round(amount * 100) / 100;
}
}
예제를 통해서 응집도를 알아봤습니다.
제가 작성한 코드들도 응집도가 무조건적으로 기능적으로 응집한 코드만 있는게 아니여서 무조건 적으로 기능적 응집도만 고집 할수는 없고 대신 예외 상황이 아니라면 최대한 기능적 응집도 즉 srp를 잘 이루는 코드를 짜도록 하는것을 목적으로 두고 코드를 작성해야 품질 좋은 코드를 작성 할 수 있을것 같습니다.