프로그래밍/OOP_Pattern_TDD

OOP, 설계 원칙, 디자인 패턴, 코딩 규칙, 리팩터링, 테스트 케이스(또는 TDD) 사이의 관계

라이프노트 2025. 4. 30. 09:44

객체지향 프로그래밍

  • 객체지향 프로그래밍 : 캡슐화(Capsulation), 추상화(Abstract), 상속(Inheritance), 다형성(polymorphism) 등으로 복잡한 설계 사상을 구현할 수 있으며, 구현(Implementation)과 구성(Composition) 등을 활용해 설계 원칙과 디자인 패턴 구현에 사용된다.

설계원칙

코드 설계의 품질을 높이는 몇 가지 경험의 요약이며, 코드 설계에서 고려해야 할 일반적인 원칙에 해당한다. 또한 디자인 패턴보다 더 추상적이다.

  • 단일 책임 원칙(Single Responsibility Principle) : 모듈, 클래스, 메서드는 하나의 책임 또는 기능만을 가지고 있어야 한다.
    거대하고 포괄적인클래스를 설계하는 대신, 작은 단위와 단일 기능을 가진 클래스를 설계해야 한다. 클래스에 자신의 역할(책임)과 관련이 없는 기능이 있다면 책임이 단일하지 않으니 작은 클래스로 분할되어야 한다.
     
  • 개방 폐쇄 원칙(Open-Closed Principle) : 확장할 때는 개방, 수정할 때는 폐쇄. 기능을 수정할 때 다른 클래스 또는 도메인에 영향을 주지 않도록 해야 한다. 
  • 리스코프 치환 원칙(Liskov substitution Principle) : 상위 클래스의 참조를 사용하는 경우에는 특별히 인지하지 않고도 파생 클래스의 객체를 사용할 수 있어야 한다. 하위 유형 또는 파생 클래스의 객체는 프로그램 내에서 상위 클래스가 사용(주입, 파라미터 등)되는 모든 상황에서 대체 가능하며, 논리적인 동작이 변경되지 않고, 정확성도 유지되어야 한다. 
  • 인터페이스 분리 원칙(Interface segregation principle) : 객체의 참조를 전달할 때 클라이언트에 필요한 인터페이스만 사용하도록 제한해야 한다. 인터페이스를 잘 이용하면 확장성이 뛰어난 코드를 구성할 수 있으며, 인터페이스를 통해 클라이언트가 사용할 수 있는 기능을 한정해서 오사용을 막는 등 객체지향 설계에서 매우 중요한 역할을 한다. 대부분의 디자인 패턴에서 인터페이스가 사용될 정도로 인터페이스 분리 원칙을 이해하고 적절하게 사용하는 것이 중요하다.
  • 의존관계 역전 원칙(Dependency inversion principle) : 상위 모듈은 하위 모듈에 의존하지 않아야 하며, 추상화에 의존해야만 한다. 또한 추상화가 세부 사항에 의존하는 것이 아니라, 세부 사항이 추상화에 의존해야 한다. 고수준 모듈이 저수준 모듈에 의존하지 않도록 설계해야 하며, 둘 다 추상화에 의존해야 한다는 원칙, 저수준 모듈은 추상화에 의존해서 로직이 구현되어야 하며, 고수준 모듈은 추상화에 의존해서 저수준 모듈을 사용해야 한다. 여기서 '고수준 모듈'이란 비즈니스 로직이나 사용자의 요구사항을 처리하는 로직의 흐름을 제어하는 부분이고, '저수준 모듈'이란 (파일 시스템 접근, 네트워크 통신, 데이터베이스 관리 등과 같은 보다) 구체적인 작업을 담당하는 부분입니다. DIP의 주요 목적은 모듈 간의 결합도를 줄이고, 시스템의 유연성과 확장성을 향상시키는 것입니다. 이를 통해 하나의 모듈을 변경하더라도 다른 모듈에 미치는 영향을 최소화할 수 있습니다.
  • KISS(Keep It Short and Simple) 원칙 : 클래스와 메서드를 짧고, 단순하게 유지하라. (Keep it simple, Stupid : Stupid! 단순하게 만들어.)
  • YAGNI(You ain't gonna need it) 원칙 : 너 그거 안쓸꺼야. 필요없는 것을 미리 만들지 마라. 나중에 필요할 것 같다고 미리 만들어도 안 쓰는 경우가 훨씬 많다. 
  • DRY(Don't repeat yourself) : 반복되는 코드를 만들지 마라. 반복되는 작업을 하지 마라. 코드의 재사용성을 높이려면, 코드의 결합도를 줄이고, 단일 책임 원칙을 충족시키며, Keep It Simple and Short 해야 한다.

디자인 패턴

  • 디자인 패턴 : S/W 개발에서 자주 접하는 설계 문제에 대해 객체지향 프로그래밍과 설계 원칙이 잘 적용된 해결 방법을 모은 것이다. 디자인 패턴을 적용하는 주요 목적은 디커플링(Decoupling)을 통해 코드의 확장성을 향상시키는 것이다. 디자인 패턴은 설계 원칙에 비해 더 구체적이며 구현하기 쉽다.

코딩 규칙

  • 코딩 규칙 : 코드 가독성 문제를 해결한다. 설계 원칙과 디자인 패턴에 비해 코딩 규칙은 더 구체적이고, 디자인 패턴이 전체 클래스 간 연관 구조를 다룬다면 코딩 규칙은 코드의 세부 사항에 더 중점을 둔다. 소규모 리팩터링은 주로 코딩 규칙을 참고하며, 중규모 이상의 리팩터링으로 무게가 실릴수록 설계 원칙과 디자인 패턴의 중요성이 높아진다.

리팩터링

  • 리팩터링 : 코드 품질 저하를 방지하는 효과적인 수단으로서 냄새나는(이해하기 어렵고, 중복되며, 유지보수가 힘든) 코드를 이해하기 쉽고, 유지보수하기 좋은 코드로 지속적으로 관리해 나가는 방법이다. 리팩터링은 객체지향 프로그래밍, 설계 원칙, 디자인 패턴, 코딩 규칙을 모두 활용하며 여기에 단위 테스트 자동화를 사용해 반복되는 리팩터링 과정을 빠르고, 오류(버그)없이 정확하게 수행할 수 있도록 돕는다.

테스트 주도 개발(TDD)

  • 테스트 주도 개발 : 리팩토링에서 단위 테스트를 자동화하는 코드는 리팩터링을 빠르고 신뢰성이 높게 수행하도록 돕는다. 또한 단위 테스트 자동화는 기능 구현이나 로직을 수정할 때 기대하던 동작이 여전히 잘 동작하는 것을 보장하는 역할로서 수동 테스트를 줄이고, 기능의 동작을 보장하여 리팩토링을 비롯한 기능 구현 및 테스트의 속도와 품질을 높이는 역할을 한다.
  • 테스트 주도 개발은 분석, 설계, 구현이라는 전통적인 폭포수(Waterfall) 개발 방식에서 탈피해서 빠르게 개발에 착수하고, 지속적으로 개선(리팩토링)하는 현대 개발의 가장 큰 트렌드인 애자일(Agile, 기민함, 빠름, 변경이 용이함, 지속적인 관리)한 개발을 이끈다.
  • 테스트를 먼저 작성(Red - Write a failing test)하고, 테스트를 통과하도록 기능을 구현(Green - Make the test pass)한 후, 구현한 코드를 리팩토링(Blue - Refactoring)하는 과정을 반복해서 S/W를 만들어 나간다.