객체지향 (소프트웨어 공학)




소프트웨어 공학 분야에서 객체지향 기술은 오래전부터 여러 분야에서 연구가 되어 왔던 아이템이다. 특히 1990년대부터 소프트웨어 분야에서는 매우 중요한 기술로 다뤄지며 생산성에 초점을 맞춘 프로그래밍 분야에서는 이제는 빼놓을 수 없는 핵심기술이다. 이는 소프트웨어의 발전이 하드웨어를 따라가지 못한다는 ‘소프트웨어 위기’를 타개할 수 있는 방법으로서 이제는 개발 주류가 된 객체지향은 특수화되고 적절하게 변경이 되며 소프트웨어 부품을 보다 체계적으로 다시 사용할 수 있는 인프라가 되었다.

 

객체지향 특징

객체지향의 특징은 무엇보다도 절차적인 프로그램과의 차이점을 보면 명확해진다.

🚩 모형의 적합성
복잡해지는 문제를 잘 모델링하기 위해서는 부분적인 자세함도 필요하지만 적절하게 자세한 것을 생략할 수 있는 추상화도 가능하여야 한다. 이는 객체지향에서 정보은닉과 메시지 교환이라는 개념을 활용하여 작업이 분산되고 감춰진다. 함수의 집합으로 구성된 절차적인 프로그래밍과 달리 객체지향 프로그램은 내부가 감춰진 객체들의 상호작용으로 구성된다. 그래서 자료를 따로 가진 객체들이 필요할 때만 협력한다. 더불어 개념이나 실체를 이산적인 객체로 모델링하여 독립된 객체로서 구현시킨다.

🚩 재사용 용이
재사용이 쉽다. 객체는 열림 특성과 닫힘 특성을 모두 갖추고 있고 재사용 부품으로서 그 역할을 한다. 닫힘은 변하지 않는 부분은 손대지 않고 그대로 다시 사용하는 것을 말하며 열림은 그 반대로 이를 변경 또는 확장하여 사용할 수 있는 것이다. 이를 가능하게 하는 개념은 바로 상속과 다형성이다. 추상화 클래스를 정의하고 파라미터를 용도에 맞게 사용할 수도 있고 캡슐화된 클래스를 그대로 쓰면서 필요에 따라 확장도 가능한 것이다.

🚩 Time to market
소프트웨어의 효율성 외에도 품질이나 유지보수 용이성, 시장 출하 시점도 소프트웨어 설계에 있어 매우 주요한 설계 기준이다. 시장에 처음 출시된 제품이 나중에 나온 제품보다 기능이 떨어지더라도 시장을 선점하고 점유한 상태를 무시할 수 없기 때문이다. 객체지향은 재사용이 용이하고 재설계가 필요 없기 때문에 개발기간을 단축할 수 있고 필요시 원하는 시점에 맞춰 제품 출하를 가능하게끔 하는 기술이다.

🚩 설계와 프로그래밍 매핑
절차적 방법에 비해 설계단계에서 프로그래밍으로의 전환이 매우 순조롭다. 즉 쉽게 매핑될 수 있다는 것인데 프로그램의 기초 단위인 객체나 클래스 개념을 말하는 것이다. 물론 코딩 자체는 다소 어려울 수 있더라도 분석 설계 단계에서 부분적인 코딩이 자유로운 객체지향은 모델링 기초 개념이 동일하여 이 또한 개발기간을 단축할 수 있는 결과를 가져올 수 있다.

 




기본개념

🚩 객체(Object)
객체는 현실 세계의 개체나 개념을 소프트웨어적으로 모델링한 것이다. 즉 필요한 자료구조와 이에 수행되는 함수들을 가진 하나의 소프트웨어 모듈로 볼 수 있다. 이는 자료구조를 갖고 상태를 가진다는 것인데 데이터와 해당 데이터를 처리하는 메소드(함수)를 함께 포함한다. 예로, 자동차 객체는 브랜드, 모델, 속도 등의 데이터와 가속, 정지와 같은 동작을 수행하는 메소드로 구성된다. 결국 객체는 상태와 행위, 정체성을 갖는, 비슷한 객체의 구조와 행동이 공통 클래스로 선언된다.

🚩 클래스(Class)
클래스는 객체를 생성하기 위한 템플릿이며 객체의 구조와 행동을 정의한다. 즉 객체의 타입이 클래스이다. 클래스는 속성(변수)과 메소드(함수)로 구성되며 객체를 생성할 때 이러한 속성과 메소드가 해당 객체에 포함된다. 자동차 클래스는 자동차 객체들이 가져야 할 속성과 메소드를 정의하는 것이 그 예이며 클래스에 속하는 객체를 이 클래스의 인스턴스라고 한다. 이런 클래스를 정의하기 위해서는 클래스가 가지는 속성을 도출하여야 한다. 다시 말해 클래스는 객체들이 갖는 속성과 적용 연산을 정의하고 있는 템플릿이다.

🚩 캡슐화(Encapsulation)
캡슐화는 객체의 상태(속성)와 행동(메소드)을 하나의 단위로 묶는 것이다. 객체는 외부에서 직접 접근하는 것을 제한하고 객체의 내부 동작을 숨기는 것으로 정보은닉을 지원하는데 이를 통해 객체의 내부 구현 세부 사항을 감추고 외부에서는 필요한 인터페이스를 통해 상호 작용할 수 있다. 캡슐화는 설계나 분석 단계에서 주어진 문제를 간단히 처리할 수 있는 추상화의 수단이 된다.

🚩 상속(Inheritance)
상속은 클래스 간의 계층 구조를 만들어 기존 클래스의 속성과 메소드를 다른 클래스가 상속받아 재사용할 수 있게 해준다. 부모 클래스(상위 클래스 또는 기본 클래스)의 특징을 자식 클래스(하위 클래스 또는 파생 클래스)가 물려받아 확장하거나 수정할 수 있다. 이를 통해 코드 재사용성과 계층적 구조를 갖는 클래스들을 구성할 수 있다. 이런 상속은 슈퍼 클래스, 서브 클래스로 클래스를 세분화하며 객체가 갖는 특유의 속성들을 추가할 수 있다. 참고로 두 개 이상의 슈퍼 클래스에서 상속받는 것을 복수 상속이라 한다.

🚩 다형성(Polymorphism)
다형성은 같은 이름의 메소드를 다양한 객체에서 호출할 때 다른 동작을 수행하도록 하는 개념으로 여러 가지 형태를 가지고 있다는 말이다. 이는 상속과 연결되어 있으며, 자식 클래스는 부모 클래스의 메소드(특정한 클래스를 위해서 오퍼레이션을 구현한 것)를 오버 라이딩(재정의)하여 자신에 맞게 수정할 수 있다. 다형성을 통해 객체 간의 유연하고 일관된 상호작용을 가능하게 한다. 다형성은 현재 코드를 변경하지 않고 새로운 클래스를 쉽게 추가할 수 있다.

 




객체지향 프로세스

객체지향 프로세스의 단계들은 선형적인 순서로 진행되는 것이 아니라 실제 개발 과정에서는 반복되거나 중첩되는 경우가 많다. 이는 소프트웨어 개발의 복잡성을 다루기 위해 계획, 설계, 구현, 검증, 유지보수 등을 체계적으로 수행하여 효율적이고 유지보수가 가능한 소프트웨어를 만들기 위한 접근법으로 큰 틀에서는 일반적인 소프트웨어 개발 프로세스와 동일하다.

✔️ 요구사항 분석
프로젝트의 목적과 필요한 기능을 파악하고 문서화한다. 이 단계에서는 사용자와 고객의 요구사항을 파악하고 이를 객체와 클래스로 변환하여 소프트웨어의 주요 기능과 특성을 정의한다. 주요 과정으로는 요구추출 단계에서는 액터, 시나리오, 여러 사례들을 찾고 구체화하며 여러 사례 간의 관계를 바탕으로 비기능적 요구를 명확화한다. 이후 요구분석을 통해 분석모형을 위한 객체, 객체 간 모형화, 연관관계, 속성 등을 찾고 정리한다.

✔️ 시스템 설계
요구사항을 바탕으로 시스템의 구조와 아키텍처를 설계한다. 클래스와 객체의 관계, 데이터베이스 구조, 인터페이스 등을 정의하고 시스템의 전체적인 동작 방식을 계획한다. 즉 설계목표를 정의하고 서브 시스템을 파악하고 여러 저장소를 설계하며 이를 패키지 다이어그램으로 정리한다.

✔️ 클래스 설계
시스템 설계를 바탕으로 클래스와 객체의 세부 사항을 설계한다. 클래스의 속성과 메소드를 식별하고 클래스 간의 관계를 설정하며 적절한 상속 구조를 설계한다. 객체를 정의하고 부품의 선택과 최적화를 통해 설계를 진행하며 상세화된 클래스 다이어그램을 작성한다.

✔️ 구현 및 테스트
클래스 설계를 기반으로 실제 코드를 작성한다. 각 클래스의 메소드를 구현, 데이터를 처리하며 객체들을 생성하고 관리한다. 이 단계에서는 객체지향 프로그래밍 언어를 사용하여 클래스와 메소드를 코드로 표현한다. 그리고 작성한 코드를 검증하여 시스템이 요구사항을 충족하는지 확인한다. 단위 테스트, 통합 테스트, 시스템 테스트 등 다양한 수준의 테스트를 수행하여 버그를 찾고 수정한다.

✔️ 배포 및 유지보수
개발한 소프트웨어를 배포하고, 사용자가 실제로 사용하면서 발생하는 문제를 해결하고 기능을 추가 또는 개선한다. 유지보수 단계에서는 시스템의 변경이나 업데이트가 필요한 경우 이를 수행한다.

✔️ 문서화, 평가와 개선
프로세스의 각 단계에서 만든 문서들을 정리하고 보완하여 개발된 소프트웨어의 설명서를 작성한다. 프로젝트가 완료된 후에는 전체 프로세스를 돌아보고 개발한 시스템의 성능과 사용성을 평가한다. 이를 통해 개선점을 발견하고 다음 프로젝트에 반영하기 위한 교훈(lessons learned)을 얻는다.