유스케이스와 사용자 흐름
모임 신청을 사용자 목표·정상 흐름·대안 흐름으로 명세하고 상태를 보존하는 실행으로 확인합니다.
모임 신청 화면에 버튼을 하나 붙였다고 해서 ‘신청 기능’이 완성되지는 않습니다. 사용자는 자리가 남았는지 알고 싶고, 누른 뒤 실제로 참가자가 되었는지 확인하고 싶습니다. 이미 신청한 사람이 다시 눌렀을 때와 마지막 자리가 찼을 때의 응답도 필요합니다. 이 본문에서는 기능 이름을 사용자의 목표와 시스템의 응답으로 풀어 씁니다.
변수·함수·배열·객체를 읽을 수 있으면 시작할 수 있습니다. 목표는 모임 신청이라는 유스케이스를 쓰고, 정상 흐름과 대안 흐름을 실행 결과에 연결하는 것입니다. UI 모양이나 데이터베이스 구조는 아직 결정하지 않습니다.
버튼 대신 목표를 정한다
유스케이스(use case, 사용 사례)는 시스템과 상호작용하는 외부 역할이 목표를 이루는 행동과 응답의 묶음입니다. 여기서 액터(actor)는 특정 사람의 이름이 아니라 ‘참가하려는 회원’, ‘모임을 관리하는 운영자’와 같은 역할입니다. 알림 서비스처럼 다른 시스템도 액터가 될 수 있습니다.
‘신청 버튼 누르기’는 클릭 행위이고 ‘모임에 참가자로 등록되기’는 사용자의 목표입니다. 버튼이 없어도 명령줄에서 같은 목표를 이룰 수 있습니다. 반대로 같은 버튼이 여러 상태를 처리할 수 있으므로 화면 버튼 수와 유스케이스 수를 같게 세지 않습니다. 유스케이스가 반드시 가장 작은 함수 하나여야 하는 것도 아닙니다.
시스템 경계는 우리가 이번에 책임질 범위를 뜻합니다. 이 실습은 이미 주어진 회원 ID와 모임 상태를 받아 신청을 판정합니다. 로그인 화면, 이메일 전송, 결제는 범위 밖입니다. ‘회원 ID를 받는다’는 표현은 실제 회원 인증까지 구현했다는 뜻이 아닙니다. 범위를 적어 두어야 구현하지 않은 기능을 완성했다고 오해하지 않습니다.
정상 흐름과 끝난 상태를 함께 쓴다
| 항목 | 모임 신청 유스케이스 |
|---|---|
| 목표·주 액터 | 회원이 열린 모임에 참가자로 등록된다 |
| 시작 조건 | 회원 ID와 모임이 주어지고 아직 신청하지 않았다 |
| 계기 | 회원이 신청을 요청한다 |
| 정상 흐름 | 모임 확인 → ID 확인 → 모집 상태·중복·정원 확인 → 참가자 추가 → 결과 응답 |
| 성공 후 조건 | 해당 회원이 참가자 목록에 한 번 존재한다 |
| 실패 후 조건 | 참가자 목록을 바꾸지 않고 실패 이유를 알려 준다 |
시작 조건이 항상 성립한다고 가정하고 검사 코드를 없애지는 않습니다. 문서의 정상 흐름은 이해를 돕는 대표 시나리오이며 실제 입력에는 조건을 어기는 요청도 들어옵니다. 실패 후 상태를 써 두면 ‘실패 메시지는 나왔지만 명단에 추가되었다’ 같은 결함을 찾을 수 있습니다.
시나리오는 유스케이스에서 실제로 일어날 수 있는 한 경로입니다. 자리가 한 명분 남은 모임에 처음 신청한 사람이 성공하는 경로와, 이미 신청한 사람이 재요청하는 경로는 같은 신청 목표 안의 서로 다른 시나리오입니다. 이 단원은 특별한 UML 기호를 외우기 전에 문장과 표로 이를 설명합니다.
사용자의 목표를 확인 가능한 결과로 연결한다
아직 참가하지 않은 유효 ID로 열린 모임에 신청한다. 해당 ID가 명단에 한 번 존재한다. 각 단계의 값과 전환 이유를 비교합니다.
SOFTWARE ENGINEERING · CH3-1
사용자의 목표를 확인 가능한 결과로 연결한다
버튼이나 함수의 개수보다 목표·응답·끝난 상태를 먼저 정합니다.
회원의 참가 목표에서 신청 요청, 시스템의 정책 확인, 결과와 명단 확인으로 이어지는 정상 흐름이며 대안 흐름은 본문 표에 명시합니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 신청 목표와 요청부터 결과 확인까지 진행합니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 신청 목표와 요청 아직 참가하지 않은 유효 ID로 열린 모임에 신청한다. |
|
|
| 정책 확인 ID·모집·중복·정원 조건을 확인한다. |
|
|
| 결과 확인 해당 ID가 명단에 한 번 존재한다. |
|
|
사용자의 목표를 확인 가능한 결과로 연결한다
아직 참가하지 않은 유효 ID로 열린 모임에 신청한다. 해당 ID가 명단에 한 번 존재한다. 각 단계의 값과 전환 이유를 비교합니다.
SOFTWARE ENGINEERING · CH3-1
사용자의 목표를 확인 가능한 결과로 연결한다
버튼이나 함수의 개수보다 목표·응답·끝난 상태를 먼저 정합니다.
회원의 참가 목표에서 신청 요청, 시스템의 정책 확인, 결과와 명단 확인으로 이어지는 정상 흐름이며 대안 흐름은 본문 표에 명시합니다.
이 예의 조건
- 본문과 전체 도표의 예를 따라 신청 목표와 요청부터 결과 확인까지 진행합니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 신청 목표와 요청 아직 참가하지 않은 유효 ID로 열린 모임에 신청한다. |
|
|
| 정책 확인 ID·모집·중복·정원 조건을 확인한다. |
|
|
| 결과 확인 해당 ID가 명단에 한 번 존재한다. |
|
|
관계 기호는 필요한 질문에 사용한다
UML 유스케이스 그림은 액터·시스템 경계·사용 사례를 연결합니다. 액터와 사례의 연관 선은 상호작용이 있다는 뜻이며 시간 순서가 아닙니다. 그림만으로 중복 신청 시의 결과나 실패 후 명단을 충분히 알 수 없으므로 명세 문장을 함께 둡니다.
include는 여러 사례에서 반드시 사용해야 하는 공통 행동을 포함할 때, extend는 조건에 따라 기본 사례에 추가 행동을 넣을 때 설명하는 관계입니다. 포함하는 사례에서 포함되는 사례로, 확장하는 사례에서 확장 대상 사례로 화살표를 읽습니다. 이름이 비슷한 extends 클래스 상속 문법과 같은 뜻으로 사용하지 않습니다.
예를 들어 여러 업무가 같은 자격 확인을 반드시 수행하면 공통 행동으로 묶을 수 있습니다. 반면 신청 실패는 신청 목표 안의 대안 흐름만으로 설명해도 되며, 모든 if문을 새 유스케이스나 extend로 만들 필요는 없습니다. 액터의 일반화도 한 역할이 다른 역할의 상호작용을 이어받는 관계가 필요한지 확인한 뒤 씁니다. 지금 사례는 회원과 운영자의 목표를 문장으로 구별하는 것으로 충분합니다.
거절도 사용자 흐름의 일부다
대안 흐름은 ‘오류가 나면 실패한다’로 끝내지 않고 어디서 갈라지고 무엇을 보존하는지 씁니다.
| 조건 | 결과 코드 | 상태에 대한 약속 |
|---|---|---|
| ID가 문자열이 아니거나 지정 형식을 어김 | INVALID_MEMBER | 변화 없음 |
| 모임이 모집 종료 상태 | CLOSED | 변화 없음 |
| 같은 ID가 이미 목록에 존재 | ALREADY_REGISTERED | 한 번만 유지 |
| 참가자 수가 정원 이상 | FULL | 변화 없음 |
| 모든 검사 통과 | REGISTERED | 새 목록에 ID 한 개 추가 |
검사 순서도 관찰 가능한 동작입니다. 열린 모임이 꽉 찼지만 요청자가 이미 참가자라면 이 교재의 계약은 ALREADY_REGISTERED를 돌려줍니다. 중복 확인이 정원 확인보다 앞에 있기 때문입니다. 모집 종료 상태에서 같은 요청을 보내면 먼저 확인하는 CLOSED가 반환됩니다. 둘 중 어느 규칙이 무조건 정답이라기보다, 정한 우선순위를 문서·코드·테스트가 같이 따라야 합니다.
작은 실행으로 문서를 검증한다
아래 함수는 입력 객체를 직접 수정하지 않습니다. 성공할 때 새로운 객체와 새 참가자 배열을 반환하고, 호출자는 반환된 state를 다음 호출에 전달합니다. 이 방식을 쓰면 요청 전후 상태를 나란히 확인하기 쉽습니다.
저장소 최상위 폴더에서 다음 명령을 실행합니다. Node.js가 설치되어 있어야 하며 패키지를 추가로 설치하지 않습니다. 두 번째 인수를 생략하면 이 장의 예제를 모두 실행합니다.
node dev-note/scripts/software-engineering-lab/examples/ch3.mjs ch3-1function register(session, memberId) {
if (typeof memberId !== "string" || !/^[a-z][a-z0-9-]{0,23}$/.test(memberId)) {
return { ok: false, code: "INVALID_MEMBER", state: session };
}
if (session.status !== "OPEN") {
return { ok: false, code: "CLOSED", state: session };
}
if (session.participants.includes(memberId)) {
return { ok: false, code: "ALREADY_REGISTERED", state: session };
}
if (session.participants.length >= session.capacity) {
return { ok: false, code: "FULL", state: session };
}
return {
ok: true,
code: "REGISTERED",
state: { ...session, participants: [...session.participants, memberId] },
};
}
const start = {
id: "book-01", title: "함께 읽는 첫 책",
capacity: 1, status: "OPEN", participants: [],
};
const first = register(start, "member-a");
const duplicate = register(first.state, "member-a");
const full = register(first.state, "member-b");
console.log(first.code, first.state.participants.join(","));
console.log(duplicate.code, full.code);
console.log("처음 상태의 인원:", start.participants.length);예상 출력은 첫 줄 REGISTERED member-a, 둘째 줄 ALREADY_REGISTERED FULL, 마지막 줄 처음 상태의 인원: 0입니다. 먼저 성공한 결과의 state를 두 번째 호출에 넘겼다는 점을 확인합니다. 계속 start만 넘기면 두 요청 모두 빈 명단을 보고 성공하므로 연속 요청을 모델링한 것이 아닙니다.
includes는 목록에 같은 문자열이 있는지 검사하고 ...는 객체와 배열의 값을 복사해 새 상태를 만듭니다. 회원 ID는 소문자 영문으로 시작하는 1~24자 문자열이며, 이후에는 소문자·숫자·하이픈만 허용하는 공통 프로젝트 정책을 따릅니다. 정규식의 ^와 $는 전체 문자열을 검사하고 {0,23}은 첫 글자 뒤 최대 23자를 뜻합니다. member-a는 유효하지만 UPPER, 1min, 앞뒤에 공백이 있는 입력은 거절합니다. 대문자나 공백을 자동으로 바꾸어 다른 ID로 처리하지 않습니다.
자주 생기는 오해를 반례로 확인한다
‘화면에서 신청 버튼을 숨기면 중복 신청을 막았다’는 설명은 충분하지 않습니다. 화면을 열어 둔 회원이 옛 상태의 버튼을 누르거나 같은 요청을 다시 보내면 시스템이 직접 중복을 검사해야 합니다. UI의 안내와 도메인 규칙의 검사는 서로 다른 책임입니다.
또한 이 실행은 한 프로세스에서 순서대로 요청을 보낸 예입니다. 두 요청이 동시에 마지막 자리를 읽고 각자 저장하는 문제까지 해결하지는 않습니다. 여기서 얻은 정상·대안 시나리오는 이후 저장소와 동시성 설계를 검증할 출발점입니다.
독립 문제: 장비 대여 목표를 써 본다
촬영 동아리는 카메라를 회원에게 빌려 줍니다. 장비 하나는 한 번에 한 회원에게만 빌릴 수 있습니다. 수리 중이면 대여할 수 없고, 이미 같은 장비를 빌린 회원의 재요청은 ‘이미 대여 중’을 돌려줍니다. 수리 여부를 가장 먼저 판단하기로 했습니다.
카메라 대여 유스케이스의 주 액터·계기·성공 후 조건·실패 후 조건을 작성하고 정상 한 개, 대안 두 개의 초기 상태와 결과를 적습니다. 마지막으로 ‘장비 선택’과 ‘대여 완료’ 중 무엇이 사용자 목표에 더 적합한지 이유를 설명합니다. 구현은 하지 않아도 됩니다.
힌트
대여자가 없는지와 장비가 사용 가능한지는 서로 다른 정보입니다. 수리 중인데 기존 대여자가 남아 있는 입력을 넣으면 검사 순서를 설명할 수 있습니다.
풀이
주 액터는 대여하려는 회원, 계기는 특정 장비의 대여 요청입니다. 성공 후에는 장비의 대여자 ID가 요청자이며 실패 후에는 기존 대여자와 장비 상태가 보존되어야 합니다. 정상은 ‘사용 가능·대여자 없음’에서 대여자 추가, 대안은 ‘수리 중’에서 거절, ‘사용 가능·같은 대여자’에서 이미 대여 중입니다. ‘다른 대여자 있음’도 추가 대안입니다.
수리 중이며 같은 회원이 대여자로 남아 있어도 먼저 검사하는 수리 상태 때문에 수리 중 결과를 냅니다. 이것을 명시하지 않으면 각 개발자가 다른 우선순위를 구현할 수 있습니다. 장비 선택은 목표를 위한 중간 단계이며 대여 완료는 사용자가 얻는 결과라 유스케이스 이름에 더 적합합니다.
다시 연결하기
요구사항은 지켜야 할 약속을 정하고, 유스케이스는 그 약속이 사용자와 시스템의 상호작용에서 어떻게 나타나는지 풀어 줍니다. 다음 본문에서는 status와 참가자 목록이 어떤 의미를 가지며 어떤 사건에서 바뀌는지 상태 모델과 데이터 모델로 확인합니다.
원자료와 더 읽기
장원사이버평생교육원 「소프트웨어공학」 PDF 물리 105–127쪽의 주제를 바탕으로 설명·도표·실습을 새로 구성했습니다.
OMG UML 2.5.1 공식 명세에서 유스케이스와 모델 표기의 범위를 확인할 수 있습니다.