4. Design Practices
이번 섹션에서는 실제로 디자인을 할 때 사용할 수 있는 휴리스틱과, 더 나은 결과를 얻기 위한 단계들을 설명한다.
Iterate
디자인은 비결정적이기 때문에, 디자인 사이클을 여러 번 반복(iterate)하면서, 서로 다른 접근 방식을 테스트할 수 있다. 이렇게 사이클을 돌리면서, high-level view와 low-level view를 왔다갔다하게 된다. high-level view에서 나온 큰 그림은, 저수준 설계의 세부사항을 더 잘 이해할 수 있도록 하고, low-level view를 잘 설계한다면 high-level 결정에 더 현실적인 기반을 제시한다.
C++ 예시를 들면 다음과 같다. 처음에 최대값을 찾는 함수를 만들었지만, low level에서 오류가 날 수 있으므로, 다시 설계헤서 안전성을 높이는 코드다.
// 초기 설계: 최대값을 찾는 단순한 함수
int findMaxInitial(const std::vector<int>& numbers) {
int max = numbers[0];
for (int num : numbers) {
if (num > max) {
max = num;
}
}
return max;
}
// 개선된 설계: 범위 검사를 추가하여 안전성을 높임
int findMaxImproved(const std::vector<int>& numbers) {
if (numbers.empty()) {
throw std::invalid_argument("The input vector is empty.");
}
int max = numbers[0];
for (const int& num : numbers) {
max = (num > max) ? num : max;
}
return max;
}
Divide and Conquer
다익스트라 선생님은 일찍이 복잡한 프로그램의 모든 디테일을 한 사람이 관리할 수 없다는 사실을 깨닫고, 분할-정복 메커니즘을 제시해서 큰 문제를 작은 subproblem들로 나누어서 개발하고, 이를 합치는 과정을 통해 complexity를 줄여야 한다고 주장했다. 이 방법은 현재는 SW개발의 대원칙에 해당한다.
Top-Down and Bottom-Up Design Approaches
교과서에서 좋아하는 단어들이다. Computer Networks : Top-Down Approach라는 책으로 컴퓨터네트워크개론을 쉽게 공부했던 기억이 나는데, Top-Down approach란 high-level Abstraction으로부터 설계를 시작하자는 것이다. 즉, base class나, 공통된 design을 먼저 만들고, specific component들을 base class에서 나오게 해서 붙이자는 것이다.
반면 Bottom-up design은 specific한 work들을 합치면서, general task를 해결할 수 있다는 논리에서 시작한다. 때로는 이렇게 낮은 level에서 시작해서, 일을 더해가면서 최종적인 프로젝트를 수행하는 것이 좋을 수도 있다. 그럼 좀더 자세히 Top-Down과 Bottom-Up의 장단점을 분석하자.
Top-Down은 상위 레벨에서 시작해서 큰 문제를 작은 문제로 나누는 것이므로, 인간 두뇌의 처리 방식과 유사하며, 하위 세부 설계사항에 대해서는 미뤄도 된다.(변경하기도 쉽다.) 이렇듯 상위에서 하위로 논리적으로 접근할 수 있다는 점에서 장점을 가진다. 하지만, 하위 레벨의 세부사항이 상위 설계에서의 부정확함으로 인해 예상치 못한 복잡성을 가져올 수 있다.
Bottom-Up은 기존에 작성했던 요소를 "계속 붙여갈" 수 있다. 즉 재사용성이 좋다. 또한, 시스템에서 필요한 유틸리티 기능들을 빠르게 설계할 수 있다. 그러나, 하위 레벨의 요소들이 상위 레벨과 맞지 않을 가능성이 존재한다. 즉 하위 요소를 조합하는 과정에서 큰 그림이 명확하지 않아 시스템이 제대로 동작하지 않을 수 있다. Bottom-Up 개발의 예시로는 "테스트 기반 개발"(TDD) 처럼 작은 유닛을 설계하면서 확장하거나, "컴포넌트 개발"과 같이 공통 모듈, 유틸리티에서 조합하는 방향이 있다.
하지만 중요한 건, 둘 중 하나가 우월하지 않고, 두 접근 방식은 서로를 "보완"하는 측면이 있다는 것이다. 따라서 이를 적절히 혼합하면서 사용하는 것이 중요하다.
Experimental Prototyping
프로토타입의 가장 중요한 특징은 무엇인가! 바로 완성품을 위해 "버려야 한다" 라는 것이다. 결국 디자인은 Iterative 하기 때문에, 계획을 몇 번 갈아엎어야 한다. 따라서 "최소한"의 버릴 코드를 성능 요구사항에 맞게 설계하고, 이 코드는 절대 프로덕션 코드로 발전되지 않아야 한다. 결과를 얻으면 폐기해야 한다.
이러한 프로토타입 코드를 잘 작성하는 팁은 다음과 같다.
- 구체적으로 요구사항 작성하기 - 이 데이터베이스 프레임워크가 괜찮을까?(X) -> 이 데이터 베이스가 조건 X 에서 초당 1000건의 트랜잭션을 처리할 수 있나?(O)
- 기술적으로 분리하기 - C++ 프로젝트라고 가정했을 떄, 프로토타입 코드는 파이썬으로 만들어서 버릴 코드 구분하기
- 명시적으로 표시하기 - 같은 언어라면, 클래스나 패키지 이름에 prototype_ 접두사 등 사용하기
무엇보다 중요한건, 명확한 목표, 목적, 실험계획이 있을때 프로토타입 코드를 "간결"하고 빠르게 만들어야 한다는 것이다.
이것들이 없다면 프로토타입 코드는 만드지 않는 것이 낫다.
Collaborative Design
오늘날 혼자서 할 수 있는 대형 프로젝트는 존재하지 않는다. 그러면 어떤 형태로, 창의적이고 높은 수준의 프로젝트를 완성할 수 있을까? 다음고 같은 방식을 추천한다.
- 비공식적(Informal) 아이디어 교환
- 동료 책상으로 가서 간단하게 아이디어를 주고받고, 즉각적인 피드백을 받을 수 있음.
- 화이트보드 브레인스토밍
- 회의실에서 동료들과 함께 화이트보드에 설계 대안을 그려보며 논의
- 복잡한 설계도 시각적으로 정리
- 페어 프로그램이(Pair Programming)
- 추천하는 방식으로, 두 명이 한 컴퓨터에서 함께 코드를 작성하며 설계를 구체화
- 실시간 피드백과 문제해결이 빠름
- 공식적, 비공식적인 리뷰
- Self Review
- 외부 전문가 조언
5. Comments on Popular Methodologies
저자는, 시대에 따라서 좋다고 생각되는 설계 방식은 달라지고 있다고 강조한다. 왜냐하면, 애초에 설계(design)이란, 최적의 과정(optimal solution)이 없기 때문이다. 따라서, 설계 방식 또한 휴리스틱하게, flexible하게 변경할 수 있어야 한다.
이러한 측면에서 볼 때, 극단적인 방법론은 지양해야 한다. 극단적인 방법론은 다음과 같다.
- BDUF(Big Design Up Front) : 모든 설계를 코드 작성 전에 완벽히 마무리하자는 방법론으로, 전체 구조를 명확히 할 수 있지만, 요구사항은 항상 바뀔 수 있으므로 위험한 방법론이다.
- NDUP(No Design Up Front) : 설계를 하지 않고 바로 코딩(..) 을 하자는 주장으로, 빠르게 결과물을 얻을 수 있지만, 유지보수와 확장성이 떨어지므로 위험하다.
따라서 저자가 추천하는 방식은 다음과 같이 "필요한 만큼"만 설계하고, 부족한 부분이 발견되면 보완하는 방식이다.
- LDUF(Little Design Up Front) / ENUF(Enough Design Up Front) : 이는 필요한 만큼만 설계하고 코딩을 시작하자는 주장으로, 현대에서 가장 유명한 설계 방법인 Agile 과 유사하다.
'Software Engineering > Code Complete, 2nd Edition' 카테고리의 다른 글
[Code Complete] CH5: Design in Construction(2) (0) | 2024.12.31 |
---|---|
[Code Complete] CH5: Design in Construction(1) (0) | 2024.12.26 |
[Code Complete] CH4: Key Construction Decisions (0) | 2024.12.26 |
[Code Complete] CH3: Measure Twice, Cut Once: Upstream Prerequisites(2) (0) | 2024.12.26 |
[Code Complete] CH3: Measure Twice, Cut Once: Upstream Prerequisites(1) (0) | 2024.12.24 |