소프트웨어 시스템을 구축할 때 코드베이스 복잡성을 관리하는 것이 중요합니다.
Clean Code의 11장에서는 시간이 지남에 따라 더 쉽게 유지 관리하고 적응할 수 있는 모듈형 시스템 설계에 대해 논의합니다.
JavaScript 예제를 사용하여 이러한 개념을 설명할 수 있습니다.
시스템이 성장함에 따라 자연스럽게 더욱 복잡해집니다. 이러한 복잡성으로 인해 다음이 어려워질 수 있습니다.
잘 설계된 시스템은 수정이 쉽고, 테스트 및 확장이 가능해야 합니다. 이를 달성하는 비결은 모듈성과 신중한 관심사 분리에 있습니다.
클린 시스템 설계의 핵심은 모듈성 원칙입니다. 대규모 시스템을 각각 명확한 책임이 있는 더 작고 독립적인 모듈로 나누어 시스템을 보다 쉽게 관리할 수 있습니다.
각 모듈은 특정 기능을 캡슐화하여 전체 시스템을 더 쉽게 이해하고 변경할 수 있도록 해야 합니다.
예: 장바구니 시스템 구성
자바스크립트로 작성된 장바구니 시스템을 상상해 봅시다. 모든 로직을 단일 파일로 묶는 대신 시스템을 여러 모듈로 나눌 수 있습니다:
// cart.js export class Cart { constructor() { this.items = []; } addItem(item) { this.items.push(item); } getTotal() { return this.items.reduce((total, item) => total item.price, 0); } } // item.js export class Item { constructor(name, price) { this.name = name; this.price = price; } } // order.js import { Cart } from './cart.js'; import { Item } from './item.js'; const cart = new Cart(); cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); console.log(`Total: $${cart.getTotal()}`);
여기서 책임은 나누어집니다. 장바구니는 항목을 관리하고, 항목은 제품을 나타내며, order.js는 상호 작용을 조정합니다.
이러한 분리를 통해 각 모듈은 독립적이고 독립적으로 테스트하고 변경하기가 더 쉽습니다.
모듈화의 목표 중 하나는 캡슐화입니다. 즉, 시스템의 나머지 부분에서 모듈의 내부 작동을 숨기는 것입니다.
외부 코드는 잘 정의된 인터페이스를 통해서만 모듈과 상호 작용해야 합니다.
이렇게 하면 시스템의 다른 부분에 영향을 주지 않고 모듈의 내부 구현을 더 쉽게 변경할 수 있습니다.
예: 장바구니 논리 캡슐화
장바구니의 합계를 계산하는 방법을 변경하고 싶다고 가정해 보겠습니다. 이제 판매세를 계산해야 할 수도 있습니다. Cart 클래스 내부에 이 논리를 캡슐화할 수 있습니다:
// cart.js export class Cart { constructor(taxRate) { this.items = []; this.taxRate = taxRate; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum item.price, 0); return total total * this.taxRate; } } // Now, the rest of the system does not need to know about tax calculations.
시스템의 다른 부분(예: order.js)은 총계 계산 방법 변경에 영향을 받지 않습니다. 이렇게 하면 시스템이 더욱 유연해지고 유지 관리가 쉬워집니다.
대형 시스템의 일반적인 문제는 시스템의 여러 부분이 얽혀 있다는 것입니다.
모듈이 너무 많은 책임을 맡게 되면 다른 상황에서 변경하거나 재사용하기가 더 어려워집니다.
관심사 분리 원칙은 각 모듈이 하나의 특정 책임을 갖도록 보장합니다.
예: 결제 별도 처리
장바구니 예시에서 결제 처리는 별도의 모듈에서 처리되어야 합니다.
// payment.js export class Payment { static process(cart) { const total = cart.getTotal(); console.log(`Processing payment of $${total}`); // Payment logic goes here } } // order.js import { Cart } from './cart.js'; import { Payment } from './payment.js'; const cart = new Cart(0.07); // 7% tax rate cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); Payment.process(cart);
이제 결제 로직이 장바구니 관리와 분리되었습니다. 이를 통해 나중에 시스템의 나머지 부분에 영향을 주지 않고 결제 프로세스를 쉽게 수정할 수 있습니다(예: 다른 결제 제공업체와 통합).
모듈화의 가장 큰 이점 중 하나는 각 모듈을 독립적으로 테스트할 수 있다는 것입니다.
위 예에서는 결제 처리 방법에 대해 걱정할 필요 없이 Cart 클래스에 대한 단위 테스트를 작성할 수 있습니다.
예: 장바구니 단위 테스트
// cart.test.js import { Cart } from './cart.js'; import { Item } from './item.js'; test('calculates total with tax', () => { const cart = new Cart(0.05); // 5% tax cart.addItem(new Item('Book', 20)); expect(cart.getTotal()).toBe(21); });
관점을 명확하게 분리하면 각 모듈을 개별적으로 테스트할 수 있으므로 디버깅이 더 쉽고 개발 속도가 빨라집니다.
모듈이 서로 너무 많이 의존하는 경우 시스템의 한 부분이 변경되면 다른 부분에서는 예상치 못한 결과가 발생할 수 있습니다.
이를 최소화하려면 모듈 간의 느슨한 결합을 목표로 하세요.
이를 통해 각 모듈은 독립적으로 발전할 수 있습니다.
예: 종속성 주입
모듈 내부에 종속성을 하드코딩하는 대신 종속성을 인수로 전달합니다.
// cart.js export class Cart { constructor(taxRateCalculator) { this.items = []; this.taxRateCalculator = taxRateCalculator; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum item.price, 0); return total this.taxRateCalculator(total); } }
이 접근 방식을 사용하면 Cart 클래스가 더욱 유연해지고 다양한 세금 계산으로 테스트하기가 더 쉬워집니다.
즐거운 코딩 되세요! ?
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3