-
Information Hiding[옛날 글들] 설계 이야기 2024. 5. 31. 00:54728x90
소프트웨어 설계 시에 고려해야 할 기본 원리 중 가장 중요한 원리가 무엇이냐고 물어본다면 주저 없이 ‘정보 은닉(Information Hiding)’이라고 대답할 것이다. 정보 은닉(또는 정보 은폐라고도 한다)은 1972년 Davis Parnas가 발표한 “On the Criteria To Be Used in Decomposing Systems Into Modules”에 소개된 이후로 오랜 세월 동안 소프트웨어 개발 분야에 지대한 영향을 끼친 원리이다. 그러나 이런 중요성에도 불구하고 정보 은닉의 개념을 정확하게 이해하고 있는 사람은 많지 않다. 소프트웨어 개발 분야에서 가장 큰 영향력을 지닌 동시에 가장 큰 오해를 받고 있는 두 가지 원리가 있다면 바로 정보 은닉과 캡슐화(Encapsulation)일 것이다.
대부분의 사람들은 외부에서 제공된 공용 메소드를 통해서만 내부 데이터에 접근할 수 있도록 하고 직접적으로 내부 데이터에 접근할 수 없도록 막는 방법을 정보 은닉이라고 알고 있다. 이것은 정보 은닉과 데이터 캡슐화라는 두 용어 간의 유사성으로 인해 빚어진 오해이다. 데이터를 공용 메서드를 통해서만 접근하도록 허용하는 방법을 데이터 캡슐화라고 하며 정보 은닉과 데이터 캡슐화는 동일한 개념이 아니다.
간단하게 말하자면 정보 은닉은 모듈을 분할하기 위한 기본 원리다.
모듈은 서브 프로그램이라기 보다는 책임의 할당이다. 모듈화는 개별적인 모듈에 대한 작업이 시작되기 전에 정해져야 하는 설계 결정들을 포함한다. … 분할된 모듈은 다른 모듈에 대해 감추어야 하는 설계 결정에 따라 특징지어진다. 해당 모듈 내부의 작업을 가능한 적게 노출하는 인터페이스 또는 정의를 선택한다. … 어려운 설계 결정이나 변화할 것 같은 설계 결정들의 목록을 사용해서 설계를 시작할 것을 추천한다. 이러한 결정이 외부 모듈에 대해 숨겨지도록 각 모듈을 설계해야 한다.
Davis Parnas, “On the Criteria To Be Used in Decomposing Systems Into Modules”정보 은닉은 외부에 감추어야 하는 비밀에 따라 시스템을 분할하는 모듈 분할의 원리이다. 모듈은 변경될 가능성이 있는 비밀을 내부로 감추고 잘 정의되고 쉽게 변경되지 않을 공용 인터페이스를 외부에 제공하여 내부의 비밀에 함부로 접근하지 못하도록 한다.
시스템의 모듈을 분할하기 위한 설계 원리인 정보 은닉은 변경 가능한 측면을 추상화하는 인터페이스를 사용하여 변경에 대한 세부 내용을 캡슐화하거나 감추고 사용자(이 경우 다른 시스템 모듈 내의 소프트웨어)에게 일반적이고 통합된 서비스 집합을 제공한다. 이것은 각 모듈이 자신만의 작은 도메인(small domain)으로 구성되어 있음을 의미한다. 여기에서는 특별한 지식 영역 또는 전문적인 식견을 의미하기 위해 도메인(domain)이라는 용어를 사용한다.
Len Bass, Paul Clements, Rick Kazman, “Software Architecture in Practice 2nd Edition”모듈은 다음과 같은 두 가지 비밀을 감추어야 한다.
- 복잡성 : 모듈이 너무 복잡한 경우 이해하고 사용하기가 어렵다. 외부에 모듈을 추상화시킬 수 있는 간단한 인터페이스를 제공해서 모듈의 복잡도를 낮춘다.
- 변경 가능성 : 변경 가능한 설계 결정이 외부에 노출될 경우 실제로 변경이 발생할 경우 파급 효과가 커진다. 변경 발생 시 하나의 모듈만 수정하면 되도록 변경 가능한 설계 결정을 모듈 내부로 감추고 외부에는 쉽게 변경되지 않을 인터페이스를 제공하도록 한다.
정보 은닉은 데이터 캡슐화가 아니다. 정보 은닉은 복잡하거나 변경 가능한 설계 결정을 안정적인 인터페이스 뒤로 숨기는 기본 설계 원리이다. 정보 은닉의 목적은 변경에 대한 유연성을 제공하는 것이다. 정보 은닉의 원리에 따라 설계된 시스템은 변경에 대한 파급효과가 지역적이기 때문에 변경에 따르는 비용이 상대적으로 적다. 적절히 모듈화 된 코드는 주어진 코드의 일부를 이해하기 위해 필요한 정보의 양이 적기 때문에 코드를 이해하기가 더 쉽다. 또한 상호 교환해야 하는 정보의 양을 최소화하면서 독자적으로 각 모듈에 대한 작업을 진행할 수 있으므로 개발 기간을 단축시킬 수 있다.
대규모의 소프트웨어를 만들 때 겪는 주요 어려움은 프로그래머들이 다수의 업무를 동시에 진행할 수 있게 일을 나누는 것이다. 이런 일의 모듈화는 정보 은닉의 개념에 매우 의존적이다. 정보 은닉은 객체와 알고리즘이 필요하지 않은 시스템 일부에는 가능한 한 이들을 보이지 않게 한다. 적절히 모듈화된 코드는 시스템의 주어진 일부를 이해하기 위해 필요한 정보의 양을 최소화함으로써 프로그래머가 “인지하는 부하”를 줄여준다. 잘 설계된 프로그램에서는 모듈 간의 인터페이스가 가능한 “좁으며(즉, 간단하며)” 변경될 수 있는 설계적 결정 사항은 하나의 모듈에 숨겨진다. 여기서 모듈에 숨겨져 있다는 것이 중요한데, 대부분의 상용 소프트웨어는 처음의 개발보다 유지보수(오류 수정과 개선)에 들어가는 프로그래머의 시간이 훨씬 더 많기 때문이다.
Michael L. Scott, “다시 보는 프로그래밍 언어”정보 은닉의 가장 큰 힘은 자기 유사성이다. 시스템을 구성하는 작은 단위의 함수부터, 이벤트 기반 아키텍처의 한 구성 요소인 시스템에 이르기까지 모든 요소를 설계하기 위한 원리로 적용이 가능하다. 자기 유사성이라는 정보 은닉의 장점은 다양한 프로그래밍 패러다임의 변화를 주도해 왔으며 현재 주도적인 패러다임의 위치를 차지하고 있는 객체 지향 프로그래밍은 정보 은닉을 효율적으로 적용할 수 있는 메커니즘인 클래스를 언어 차원에서 제공한다.
Parnas의 모듈에 대한 정보 은닉 정의는 매우 중요한 연구 프로그램에서 공개적으로 내디딘 첫 발걸음이며, 객체 지향 프로그래밍의 지적인 조상이다. 그는 모듈을 자체 데이터 모델과 자체 작동 집합을 가진 소프트웨어 실체라고 정의했다. 모듈의 데이터에는 그 모듈의 적절한 작동들 가운데 하나를 통해서만 접근할 수 있다.
두 번째 발걸음은 여러 사상가들의 헌신으로 이루어졌다. 즉, 그들이 Parnas의 모듈을 추상 데이터 타입(Abstract Data Type)으로 업그레이드하였기에, 그것으로부터 수많은 객체들이 도출될 수 있었다. 추상 데이터 타입은 모듈 인터페이스에 대한 일관된 사고와 명시 그리고 시행하기 쉬운 액세스 기율 등을 제공한다.
세 번째로 내디딘 발걸음은 객체 지향 프로그래밍으로, 상속이라는 강력한 개념이 처음으로 도입되었고, 그에 따라 클래스들(데이터 타입들)은 클래스 계층 속의 조상들로부터 지정된 속성들을 기본적으로 갖게 된다. 우리가 객체 지향 프로그램에서 얻고자 하는 대부분은 사실상 Parnas가 내디딘 첫 발걸음에서, 즉 모듈의 캡슐화, 여기에 덧붙여 재사용을 목표로 설계되고 테스트된 모듈이나 클래스로 이뤄진 사전 구축된 라이브러리라는 아이디어에서 비롯된 것이다.
Frederick P. Brooks, Jr. “맨먼스 미신, 20주년 기념판”정보 은닉은 구조적인 설계와 객체 지향적인 설계 모두에 있어서 기본적인 구조이다. 구조적인 설계에서는, “블랙박스”라는 개념이 정보 은닉으로부터 왔다. 객체 지향적인 설계에서는 정보 은닉이 캡슐화와 모듈화를 발생케 하였으며, 추상화 개념과 연결된다.
Steve McConnell, "Code Complete 2nd Edition"그렇다면 정보 은닉과 캡슐화의 차이는 무엇일까? 대부분의 사람들은 캡슐화를 데이터를 공용 메소드로 감추는 방법, 또는 관련된 데이터와 함수를 하나의 단위로 묶는 방법이라고 생각한다. 전자는 캡슐화의 한 종류인 데이터 캡슐화를 의미하며, 후자는 추상 데이터 타입(ADT)-책임 할당의 개념으로 보면 INFORMATION EXPERT 패턴-의 개념이다. 그러나 이런 관점은 캡슐화의 한 단면만을 이야기할 뿐이다.
캡슐화는 ‘온갖 방법의 숨기기’라고 생각하면 쉽다. 캡슐화를 통해 데이터를 숨길 수 있다. 그리고 구현 코드, 파생 클래스 또는 여러 가지의 것들을 숨길 수 있다. … 따라서, 실제로 어떠한 종류의 파생 클래스가 나타나는지를 알고 있는 클라이언트를 가지지 않으면서 다형적으로 행위하는 추상 클래스가 존재할 때 이러한 캡슐화가 이루어질 수 있다.
알란 섈로웨이, 제임스 트로트, “알기 쉬운 디자인 패턴”이러한 다양한 방법의 숨기기를 통해 클래스, 클래스 그룹, 또는 패키지 전체에 가해지는 변경에 대한 파급 효과를 일정한 영역 내부로 제한시킬 수 있다.
설계에서 무엇이 변화될 수 있는지 고려하자. 이 접근법은 재설계의 원인에 초점을 맞추는 것과 반대되는 것이다. 설계에 변경을 강요하는 것이 무엇인지에 대해 고려하기보다는, 재설계 없이 변경시킬 수 있는 것이 무엇인지 고려하자. 여기에서의 초점은 많은 디자인 패턴의 주제인 변화하는 개념을 캡슐화하는 것이다.
GOF, “디자인 패턴”대부분의 컨텍스트에서 캡슐화와 정보 은닉은 동일한 의미를 지니다. 즉, 복잡한 설계 결정, 또는 변경이 발생할 수 있는 설계 결정을 안정적인 인터페이스 배후로 감추는 것이다. 따라서 캡슐화와 정보 은닉의 차이점에 대해 고민하기보다는 원리를 충족시키는 방법에 초점을 맞추는 것이 올바른 접근방법이다.
정보 은닉과 캡슐화가 전해주는 만트라는 변경에 대비하여 설계를 하라는 것이다. 이것은 Craig Larman이 “UML과 패턴의 적용”에서 제시한 PROTECTED VARIATION 패턴과 동일하며, 객체 지향 분석/설계 영역에서 이를 달성하기 위한 가장 훌륭한 방법은 OCP(Open-Closed Principle) 원리를 따르는 것이다. OCP에 대한 자세한 논의는 Rober C. Martin의 저서인 “소프트웨어 개발의 지혜”에서 자세히 다루고 있다.
그러나 변경에 대비하는 설계가 항상 좋은 것은 아니다. 변경은 실제로 변경이 발생할 때에만 그 의미가 있다. 불필요한 복잡성(Needless Complexity)의 문제는 개발자가 요구 사항에 대한 변경 내용을 막연히 예상하고 잠재적인 변경을 처리할 수 있는 불필요한 코드를 넣었을 때 발생한다. 이것은 XP의 슬로건인 “YAGNI(You Aren’t Gonna Need It)”와도 연관이 있다. YAGNI는 지금 당장 그 기능이 필요하지 않다면 정말 필요해질 때까지 해당 기능을 구현하지 말라는 의미다.
이렇게 하는 첫 번째 이유는 경제적인 면 때문이다. 만약 내일 필요하게 될 기능을 위해 작업해야 한다면, 이번 반복동안에 개발되어야 할 기능에 쏟을 노력이 손실된다는 것을 의미한다. 릴리즈 계획은 지금 해야 하는 작업에 관한 것이고 미래를 위해 다른 일을 한다는 것은 개발자와 고객 간의 합의에 반하는 것이다. … 이런 경제적인 불이익은 우리가 불필요한 것을 추가할 수도 있다는 가능성 때문에 더욱 커진다.
Martin Fowler, “Is Design Dead?”그렇다면 정보 은닉과 캡슐화의 원리를 지키면서 변경에 따르는 비용을 최소화하기 위한 방법은 무엇일까? 변경이 발생할 가능성이 높다면 변경에 대비하기 위한 설계를 하자. 만약 변경의 발생 여부가 명확하지 않다면 실제로 변경이 발생할 때까지는 그 사실을 잊도록 하자. 변경이 발생할 경우 리팩토링에 따른 위험성을 낮추기 위해 테스트 케이스라는 안전망으로 코드를 보호하자. 그리고 변경될 것이라면 최대한 빨리 변경이 발생하도록 촉진시키기 위해 다음과 같은 방법을 사용하자.
- 테스트를 먼저 작성한다. 테스트는 시스템을 사용하는 방법 중 하나이다. 테스트를 먼저 작성함으로써, 시스템을 테스트 가능한 것으로 만들 수 있다. 따라서 테스트 가능한 변경이 일어날 것이고 우리는 테스트 가능한 변경에 대해서는 놀라지 않는다. 그 때쯤이면 시스템을 테스트 가능하게 만드는 추상화를 만들었을 것이다. 우리는 나중에 일어날 다른 종류의 변경에 대해 보호하는 추상화 중 많은 것을 알아차릴 수 있을 것이다.
- 주 단위보다는 일 단위로, 아주 짧은 주기로 개발한다.
- 기반 구조 보다 기능 요소를 먼저 개발하고, 자주 이 기능 요소를 이해당사자(stakeholder)에게 보인다.
- 가장 중요한 기능 요소를 먼저 개발한다. 소프트웨어를 빨리, 그리고 자주 릴리즈한다. 가능한 빠르게, 자주 고객과 사용자 앞에서 그것을 시연한다.
Robert C. Martin, “소프트웨어 개발의 지혜”728x90'[옛날 글들] 설계 이야기' 카테고리의 다른 글
도메인 특화 언어와 단위 테스트 - 2부 (0) 2024.05.31 도메인 특화 언어와 단위 테스트 - 1부 (0) 2024.05.31 테스트 커버리지에 현혹되지 말자 (2) 2024.05.31 프레임워크 - 2부 [끝] (0) 2024.05.30 프레임워크 - 1부 (0) 2024.05.30