소프트웨어 개발의 주요 단계를 다시 정리해보면 다음의 5단계를 거쳐서 소프트웨어를 개발한다.
1. specification
2. design
3. implementation
4. test
5. maintenance
specification 단계에서는 사용자의 요구사항을 파악하고, use case diagram 을 통해 시스템의 기능을 시각화하였다.
또한 사용자의 액션에 따라서 시스템이 어떻게 응답해야 하는지 use case description 을 작성하여 시스템에서 개발해야 하는 부분이 어디인지도 명확하게 정의할 수 있었다.
이번 글에서는 design 단계를 진행한다.
design 단계에서는 앞서 파악한 내용을 분석하여 실제로 프로그램을 개발할 때 어떤 클래스와 메서드를 사용할 지 정의한다.
이 과정을 마치고나면 커뮤니케이션 다이어그램과 클래스 다이어그램을 통해 설계 내용을 문서화할 수 있다.
유즈 케이스로부터 클래스 다이어그램을 한번에 그리는 것은 쉽지 않다.
따라서 그 중간에 몇 가지 단계를 거쳐서 클래스 다이어그램을 그린다.
클래스 다이어그램을 그리는 과정은 크게 다음과 같다.
1. 하나의 use case 를 선택한다.
2. use case collaboration 을 작성하여 이 use case 구현에 필요한 객체와 객체간 관계를 파악한다.
3. communication diagram 을 작성하여 이 use case 구현 흐름을 객체와 객체간 소통(메세지 전달) 과정으로 표현한다.
4. communication diagram 에 등장한 객체(클래스), 메세지(메서드) 를 기반으로 클래스 다이어그램으로 변환한다.
5. 모든 use case 에 대해 이를 반복한 뒤, 같은 클래스에 대한 클래스 다이어그램을 모아 하나로 합친다.
6. 클래스 간 assocation 을 표현한다.
Use Case Collaboration
collaboration 은 use case 를 '실체화' 한 것으로, 위 그림과 같이 점선 타원형으로 표현한다.
이름 그대로 이 use case 를 실행하는 과정에서 오브젝트 사이의 협력 관계를 나타내며,
collaboration 안에는 객체와 객체 사이 관계를 use case diagram 그리는 것과 비슷하게 표현한다.
collaboration 을 자세히 표현하면 이렇게 그린다. (타원형 테두리는 점선이어야 한다.)
collaboration 상단에는 use case 이름을 적어준다.
그 아래의 여러 네모는 정해진 기능을 수행하는 오브젝트를 나타내며,
실선은 오브젝트와 오브젝트 사이에 커뮤니케이션이 존재함을 의미한다.
오브젝트 이름을 적을 때는 '오브젝트 이름 : 클래스 이름' 형식으로 적는다.
이때 오브젝트 이름은 생략할 수 있다.
따라서 클래스 이름 앞에 : 이 붙어있으면 '오브젝트' 를 나타낸다고 이해하면 된다.
이렇게 collaboration 을 그림으로써 communication 다이어그램을 그리기 전에
이 기능을 수행하는데 필요한 오브젝트는 무엇이 있고, 이 오브젝트가 누구랑 소통하는지 그 큰 그림을 먼저 그릴 수 있다.
Communication Diagram
Class 의 스테레오타입
클래스는 그 클래스의 인스턴스가 수행하는 역할에 따라 크게 3가지 스테레오타입으로 구분된다.
각 클래스는 이 스테레오 타입에 맞는 동작만 해야하며, 다른 타입의 클래스에서 수행할 동작을 하면 안된다.
1. boundary class
시스템 경계에서 액터와 직접 상호작용하는 클래스
보통 UI 클래스가 boundary class 에 해당하며, 액터로부터 입력을 받고, 데이터를 전달하거나,
전달받은 데이터를 액터가 볼 수 있게 출력하는 역할을 수행한다.
2. entity class
어플리케이션의 특정 도메인에 대한 정보를 저장하고, 자신의 데이터를 조작하는 서비스를 제공하는 클래스를 말한다.
학생 관리 시스템에서 '학생' 데이터를 저장하고 다루는 Student class가 있다면 이 클래스는 entity class 이다.
3. control class
바운더리 클래스와 엔티티 클래스가 각자 자신의 역할만을 수행한다면,
컨트롤 클래스는 이 두 클래스를 활용해서 전체 비즈니스 로직을 풀어내는 클래스이다.
즉, 컨트롤 클래스는 바운더리 클래스로부터 입력 데이터를 전달받고,
이 데이터를 기반으로 엔티티 클래스의 기능을 통해 새로운 데이터를 생성하거나 수정하거나 삭제하는 등의 작업을 수행한다.
Message 라벨을 적는 방법
커뮤니케이션 다이어그램은 객체가 어떤 순서로 메세지를 보내는지 그 흐름을 그리는 다이어그램이다.
메세지는 assocation 선 위에 적어주는데, 다음과 같은 방법을 사용하여 적을 수 있다.
- 기본 형태
번호: 메세지()
ex) 3. createNewMember()
- 재귀 형태
번호.번호.번호... : 메세지()
ex) 3.1.5 saveMember()
이 예시의 경우, 3번 메서드의 호출 내부에서 3.1번 메서드가 호출되고, 그 안에서 3.1.5 메서드가 호출되는 형태를 나타낸다.
- 조건문
번호 [조건] : 메세지()
ex) 3.1 [size > 5] : c = count()
c 라는 변수에 count() 함수를 호출한 return 값을 저장한다. 이렇게 변수에 값을 저장하는 형태로도 기술할 수 있다.
- 반복문
번호 *[반복문] : 메세지()
ex) 4 *[for all clients] : getName()
[ ] 안에 있는 조건을 만족하는 동안 반복한다
예시
use case description 이 다음과 같이 있다고 해보자.
Actor Action | System Response |
1. 식당 이름 리스트를 보여준다. | |
2. 식당을 선택한다. | |
3. 식당의 메뉴 리스트를 보여준다. | |
4. 메뉴를 선택한다. | |
5. 해당 메뉴의 옵션 리스트를 보여준다. | |
6. 폼을 작성하여 새로운 옵션을 추가한다. | |
7. 새로운 옵션이 추가되었다는 확인 메세지를 출력한다. |
이 use cae description 을 커뮤니케이션 다이어그램으로 그려보자.
먼저 필요한 클래스들을 생각해야 한다.
모든 use case 의 기능은 control class 에서 시작한다.
액터가 시스템과 상호작용하는데 필요한 유저 인터페이스를 시스템이 먼저 띄워줘야 하기 때문이다.
그리고 그렇게 상호작용하는 기능을 수행할 boundary class 역시 반드시 필요하다.
따라서 모든 use case 는 최소한 하나의 control class, boundary class 를 각각 가진다.
따라서 다이어그램을 그릴 때도 이 두 클래스부터 그리기 시작하면 된다.
AddOption 은 옵션 추가 기능을 수행하는 control class, AddOptionUI 는 boundary class 이다.
다음으로 이 기능을 수행하는데 필요한 entity class를 찾는다.
먼저 시스템이 수행해야하는 첫 번째 기능, 식당 이름을 보여주려면 식당 이름을 갖고 있는 식당 엔티티가 필요하다.
다음으로는 메뉴 정보를 갖고 있을 메뉴 엔티티, 마지막으로 옵션 정보를 가지고 있을 옵션 엔티티가 필요하다.
따라서 위와 같이 필요한 엔티티 클래스까지 표현한다.
이제 엔티티 클래스 간 커뮤니케이션을 표시하자.
먼저 AddOption 컨트롤 클래스가 생성되면, AddOption 클래스는 사용자에게 식당 리스트를 보여주어야 한다.
이때 식당 리스트는 AddOption 컨트롤 클래스가 보유하고 있고, AddOptionUI 로 식당 이름 데이터를 선별해서 전달한다.
커뮤니케이션 다이어그램으로는 이렇게 표현할 수 있다.
화살표의 방향은 화살표의 목적지에 있는 객체에게 메세지를 전달한다는 뜻으로, 화살표의 목적지 클래스에 정의된 메서드를 호출한다는 뜻과 같다.
그림을 보면 먼저 AddOption이 보유한 모든 식당 엔티티에 대해 이름을 가져온 뒤, startInterface 의 매개변수로 전달하여 호출한다.
이때 커뮤니케이션 다이어그램에서는 매개변수와 메서드의 리턴 타입 등을 명시하지 않으므로, 지금은 메서드 이름만 기술한다.
메서드의 매개변수와 리턴 타입 등은 나중에 클래스 다이어그램을 작성할 때 진행한다.
이제 startInterface() 함수로 인해 사용자에게 식당 이름 리스트가 보이는 상황이다.
사용자(액터)는 식당 이름을 선택한다.
그러면 해당 식당이 갖고 있는 모든 메뉴 리스트를 조회해서 다시 보여주어야 한다.
이 과정을 다이어그램으로 표현하면 위와 같다.
3. 사용자가 레스토랑을 선택하면 (AddOptionUI 의 selectRestaurant() 호출)
3.1. AddOption 컨트롤 클래스의 showRestaurantMenu() 메서드를 호출하여 컨트롤 클래스에 사용자가 선택한 식당 정보를 전달한다.
(= AddOptionUI 도 AddOption 의 레퍼런스를 갖고 있다.)
3.1.1 컨트롤 클래스는 전달받은 식당 정보에 해당하는 엔티티 클래스의 listMenu() 메서드를 호출하여 모든 메뉴 엔티티를 가져온다.
(메뉴 정보는 식당이 갖고 있으므로)
3.1.2 그리고 메뉴 엔티티를 순회하면서 메뉴의 상세 정보를 getMenuDetails() 메서드로 조회해온다.
최종적으로는 이 정보를 바운더리 클래스에 넘겨서 사용자의 인터페이스에 메뉴 리스트가 보이도록 할 것이다.
이때 바운더리 클래스에 데이터를 넘길 때는 startInterface() 와 같은 메서드가 아니라 호출 메서드의 return 값으로 넘긴다.
식당을 선택했다면, 메뉴를 선택해야 한다.
이 역시 다음과 같이 비슷하게 그릴 수 있다.
4. 사용자가 메뉴를 선택하면
4.1 해당 메뉴의 옵션을 조회하는 컨트롤 클래스 메서드를 호출한다.
4.1.1 그 안에서는 메뉴의 옵션 리스트를 조회하고
4.1.2 모든 옵션 리스트를 돌면서 상세 정보를 조회하여 반환하면
4.1 메서드가 옵션 데이터를 반환하면서 UI 에서 옵션 정보를 화면에 띄울 것이다.
마지막으로 사용자가 새로운 옵션을 생성하는 로직 흐름을 표현해보자.
5 createOption() 메서드를 호출하여 생성할 옵션 데이터를 컨트롤 클래스에 전달하면
5.1 addNewOption() 메서드에서 Menu 엔티티에 옵션을 추가하는 메서드(5.1.1)를 호출한다.
Menu 에서는 5.1.1.1 Option 생성자를 호출하여 새로운 옵션 인스턴스를 만든다.
이때 생성자를 통해서 만들어지는 새 인스턴스를 표현하기 위해 기존 Option 클래스와 별개로 새로운 인스턴스 이름을 newOption 으로 하는 인스턴스를 구분하여 표현하였다.
(위의 이름없이 사용한 Option 은 일반적인 옵션 오브젝트를 가리킨다.)
Class Diagram
커뮤니케이션 다이어그램을 그리면 각 클래스에 정의되어야 하는 메서드 정보를 알 수 있다.
하나의 use case 에 대해 그린 커뮤니케이션 다이어그램으로부터 알아낸 각 클래스의 메서드 정보를 기반으로 클래스 다이어그램을 먼저 그릴 수 있는데, 이 클래스 다이어그램을 가리켜 use case class diagram 이라고 부른다.
예를 들어 AddOptionUI 클래스는 위와 같이 다이어그램으로 표현할 수 있다.
제일 위에는 클래스의 스테레오타입과 이름을 기술하고, 그 아래에는 attribute 를, 그 아래에는 메서드 목록을 적는다.
이때도 아직은 메서드의 매개변수와 리턴값 정보는 적지 않는다.
이제 이 과정을 모든 use case 에 대해 반복한 뒤, 각 use case class diagram을 하나로 통합하면 하나의 클래스에 필요한 모든 메서드를 알 수 있게 된다.
이렇게 그린 클래스 다이어그램을 가리켜 analysis class diagram 이라고 한다.
마지막으로 analysis class diagram 에 매개변수 타입, 리턴 타입, attribute 타입 등을 모두 기술한다.
(이 행동을 가리켜 detailed design 을 한다고 표현한다.)
이 과정의 결과로 나온 클래스 다이어그램을 가리켜 design class diagram 이라고 부른다.
disign class diagram 까지 나오면 이제는 그 설계대로 함수를 채우는 구현만 하면 끝난다.
오브젝트를 클래스 다이어그램에서 표현할 때는 위 그림과 같이 클래스 이름 앞에 : 을 쓰고, : 앞에 오브젝트 이름을 기술한다.
또한 오브젝트는 attribute 영역만 갖고 있으며, 이 영역에는 해당 오브젝트가 가진 실제 데이터를 함께 기술한다.
Association & Link
오브젝트와 오브젝트 사이의 관계를 나타낼 때, 이렇게 선으로 서로 커뮤니케이션하는 관계임을 나타낼 수 있다.
오브젝트와 오브젝트 사이의 관계를 가리켜 Link 라고 말한다.
link 는 두 오브젝트 사이에 논리적인 관계가 있음을 나타낸다.
Association 은 각 클래스에 속한 오브젝트 사이에 관계가 존재할 수 있다는 가능성을 나타낸다.
따라서 위 경우 Member 클래스와 Classroom 클래스 사이에도 선이 그어지며, Association 이 존재한다.
association 을 찾을 때는 '오브젝트' 간 link를 찾고, link가 있는 모든 오브젝트의 클래스에 대해 association 을 그려준다.
association 은 무조건 관계가 존재한다는 뜻이 아니다.
멤버 인스턴스가 100만 개 있을 때, 99만9999개가 classroom 인스턴스와 관계가 없더라도, 1개가 classroom 인스턴스와 관계가 있다면, Member 클래스와 Classroom 클래스 사이에는 association 이 존재한다.
즉, class 가 object 에 대한 정의를 나타낸다면, association 은 link 에 대한 정의를 나타낸다고 볼 수 있다.
(association의 instance 는 link 라는 뜻이기도 하다.)
association 위에는 관계 이름과 방향을 기술할 수 있다.
또한 association 에는 multiplicity 를 기술할 수 있다.
이는 각 association 이 가질 수 있는 cardinality 의 범위를 나타낸다.
(cardinality 는 쉽게 말해 몇 대 몇 관계인지를 나타낸다고 생각하면 된다)
이때 cardinality 는 우리가 알고 있는 상식을 기반으로 정하는 것이 아님에 주의해야 한다.
반드시 시스템 룰에 따라서 결정해야 한다.
예를 들어 은행과 계좌 사이의 관계를 결정한다고 할 때
어떤 은행은 한 사람에 하나의 계좌만 만들 수 있다고 결정할 수 있고
어떤 은행은 한 사람에 여러 계좌를 만들 수 있다고 결정할 수 있다.
또 은행에 고객이 존재하면 반드시 계좌를 만들어야 한다고 결정할 수도 있고 (최소 1개의 계좌)
일단 회원가입만 하면 고객이고, 계좌는 없어도 된다고 결정할 수도 있다. (최소 0개의 계좌)
은행과 계좌 사이의 multiplicity 는 이렇게 은행에서 정한 룰에 따라 정해야 하지, 우리의 상식과 경험에 의거하여 정하면 안된다.
이 그림은 association 에 muliplicity 까지 기술한 모습을 보여준다.
multiplicity 는 각각 한쪽의 입장에서 바라본 경우의 수 범위를 적어주면 된다.
예를 들어 하나의 classroom 입장에서 자신은 0명 이상의 멤버를 가질 수 있으므로
시작 범위 0, 끝 범위 정해지지 않음(*) 으로 기술한다.
거꾸로 한 명의 멤버 입장에서 자신은 하나의 classroom 에만 속할 수 있다.
따라서 시작 범위는 1, 끝 범위도 1 이다.
범위가 같으므로 숫자 하나로 표기한다.
multiplicity 를 표기하는 방법을 예시로 들면 다음과 같다.
1..* = 1, 2, 3, ....
3..* = 3, 4, 5, ....
0..1 = 0, 1
1..7 = 1, 2, 3, 4, 5, 6, 7
3, 5, 19 = 3, 5, 19
1, 3, 5, 7 .. * = 1, 3, 5, 7, 8, 9, .....
마지막의 1, 3, 5, 7 .. * 을 가리켜 1, 3, 5, 7, 9, .. 와 같은 홀수 나열로 헷갈리지 않게 주의하자.
Aggregation & Composition
두 오브젝트 사이의 관계는 IS-A 관계 또는 HAS-A 관계로 구분된다.
has-a 관계는 위에서 정리한 association 으로 표시한다.
이때 has-a 관계에는 aggregation 과 composition 2가지 특별한 관계가 존재한다.
이 두 관계는 모두 whole-part 관계를 말한다. (일대다 관계)
위에서 살펴본 member - classroom 관계를 살펴보면, classroom 이 whole 이고, member 가 part 가 되는 관계임을 알 수 있다.
정리하면 association 관계 중에서 특별히 whole-part 관계를 띄는 경우,
이 관계는 aggregation 또는 composition 으로 나타낼 수 있다.
aggregation 은 일반적인 whole-part 관계를 나타낸다.
classroom - member 은 whole-part 관계이므로 aggregation으로 나타낼 수 있다.
aggregation 을 다이어그램에 나타낼 때는 위와 같이 whole 쪽에 속이 빈 다이아몬드 기호를 그려준다.
composition 은 aggregation 보다 더 강한 관계로, 다음의 2가지 속성을 추가로 만족해야 한다.
1. 각 part는 한번에 하나의 whole 에만 속할 수 있다.
2. whole 이 사라지면 part 도 사라진다.
예를 들어 학생과 수업 관계를 생각해보자.
학생은 여러 수업을 들을 수 있고, 수업도 여러 학생을 포함하고 있다.
이런 다대다 관계는 composition 이 될 수 없다.
각 학생은 한번에 하나의 whole 에 속하지 않으며, 수업이 사라진다고 학생도 사라지지 않기 때문이다.
하지만 위의 member-classroom 속성을 보면
시스템적으로 모든 학생은 하나의 교실에 반드시 속해야 하기 때문에 교실이 없어지면 학생도 존재할 수 없다.
따라서 위 관계는 composition 관계이다.
composition 관계는 위와 같이 속이 채워진 다이어몬드를 whole 쪽에 그려줌으로써 표현한다.
수업에서는 비빔밥 예시가 나왔다.
하나의 재료 인스턴스는 하나의 비빔밥 인스턴스에만 속하며, 비빔밥 인스턴스가 바닥에 엎어지면 그 안에 속한 모든 재료도 버릴 수 밖에 없다.
따라서 비빔밥과 재료는 composition 관계이다.
만약 바닥에 엎어진 재료를 다른 비빔밥에 그냥 넣는 것이 가능하다면... 이는 aggregation 일 것이다.
그 밖에도 여러 가지 관계를 생각해볼 수 있다.
자동차와 타이어의 관계를 보면, 타이어 인스턴스는 분명 한번에 하나의 자동차 인스턴스에만 속할 수 있다.
만약 자동차를 폐차할 때 타이어도 같이 버려진다면 이 둘은 composition 관계일 것이고,
자동차를 폐차할 때 타이어는 빼고 버리며 타이어를 재활용한다면 이 둘은 aggregation 관계일 것이다.
윈도우 창과 버튼사이의 관계도 생각해보면
하나의 버튼 인스턴스는 하나의 윈도우 창에만 속한다.
(그 버튼의 동작이 여러 윈도우 창에 동시에 영향을 주지 않는다.)
윈도우 창을 닫으면 그 창에 속한 모든 버튼은 같이 사라진다.
따라서 윈도우 창과 버튼 사이의 관계는 composition 이다.
association, aggregation, composition 을 코드로 나타내보면 다음과 같이 나타낼 수 있다.
class A {
B b;
}
association 은 단순 has-a 관계이므로 이렇게 나타낼 수 있다.
A 가 사라질 때 B가 꼭 사라져야 하는 것은 아니므로 B 타입 포인터를 갖고 있는 것으로 묘사해도 상관없다.
또한 꼭 멤버 변수로 갖고 있어야만 하는 것도 아니다. 함수의 매개변수로 받는 식으로 B의 참조를 받을 수도 있다.
class C {
D* d[MAX];
}
aggregation 은 이렇게 나타낼 수 있다.
먼저 whole-part 관계를 만족해야 하므로 여러 d 객체를 갖고 있어야 한다.
이때 C가 사라진다고 d 객체가 꼭 사라져야 하는 것은 아니므로 위 그림과 같이 포인터 배열을 갖고 있는 것으로 구현할 수 있다.
class E {
F f[MAX];
}
composition 은 이렇게 나타낼 수 있다.
whole-part 관계이므로 E 가 f 객체를 여러개 갖고 있고, E 객체가 사라지면 f 객체들도 모두 사라진다.
또한 f 객체 각각은 다른 E 객체에 중복으로 속하지 않는다.
Generalization
두 클래스가 대체로 비슷하고 일부분만 다르다면, 두 클래스의 비슷한 점을 묶어낸 상위 클래스를 만들 수 있다.
(바텀-업 방식)
만약 이 상태에서 상위 클래스의 모든 속성을 갖고 있는 새로운 클래스가 추가된다면 상위 클래스 하위에 새로운 클래스를 더 추가한다.
(탑-다운 방식)
이 상위 클래스와 하위 클래스 사이의 관계는 IS-A 관계이며, 클래스 다이어그램에서 화살표로 표현할 수 있다.
그림으로는 이렇게 나타낼 수 있다.
정리하면 requirement analysis 단계를 수행하는 과정은 다음과 같다.
1. 유즈케이스 하나를 정해서
2. use case collaboration 을 그려서 필요한 객체와 객체간 관계를 찾고
3. 커뮤니케이션 다이어그램을 그려서 객체간 상호작용을 묘사한 뒤
4. 커뮤니케이션 다이어그램의 메세지 전달 흐름을 기반으로 use case class diagram 을 그리고
5. 모든 유즈 케이스에 대해 이를 반복한 뒤, 같은 클래스에 대한 다이어그램을 하나로 합쳐 analysis class diagram 을 그리고
6. association 과 generalization 을 표현한다.
최종적으로는 매개변수 타입을 그려 design class diagram 까지 그려야 하는데, 이는 뒤에서 정리해본다.
'CS > 소프트웨어공학' 카테고리의 다른 글
[소프트웨어공학] 15. Detailed Design (0) | 2025.06.05 |
---|---|
[소프트웨어공학] 14. Object Interaction (0) | 2025.06.04 |
[소프트웨어공학] 12. Configuration and Version Management (0) | 2025.04.21 |
[소프트웨어공학] 11. Use Case Diagram (1) | 2025.04.21 |
[소프트웨어공학] 10. Requirement Capture (0) | 2025.04.20 |