본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Computer Science

소프트웨어 설계와 공학

추가
목차44개 항목
출처와 참고

클래스 다이어그램 표기는 ByteByteGo의 System Design 101 가이드 「UML Class Diagrams Cheatsheet」를 바탕으로 보강했다. 「계층과 API」, 「버전 관리의 기본 동작」 절은 CrashCourse의 「Crash Course Computer Science」 강의를 바탕으로 보강했다. 개발 생명주기와 방법론, 요구공학, 구조적·객체지향 분석과 설계, 형상 관리 절은 고려사이버대학교 「소프트웨어공학」(2024-1학기)과 「소프트웨어 분석설계」(2023-2학기) 강의 내용으로 보강했다. UML을 쓰는 이유, 다이어그램 14종, 클래스 다이어그램의 쓰임과 연관 방향, 활동·시퀀스 다이어그램의 차이는 GitHub 저장소 「Grokking the Object Oriented Design Interview」를 바탕으로 보강했다. 반복적·RAD 모델, 타당성 분석, 요구사항 추출 기법과 MoSCoW, 설계 수준과 하향식·상향식 설계, 인터페이스 황금률, 복잡도 척도, 유지보수와 재공학, CASE 도구는 GitHub 자료 모음 「Subjective-studies_OS-CN-DBMS」의 소프트웨어 공학 튜토리얼을 바탕으로 보강했다.

좋은 소프트웨어 설계의 핵심은 "고치기 쉽고 다시 쓰기 쉬운 구조"다. 소프트웨어 공학은 이를 정해진 비용과 일정 안에서 믿을 만한 품질로 해내는 방법을 다룬다. 무엇을 만들지 정하는 요구공학, 어떻게 나눌지 정하는 분석·설계, 제대로 만들었는지 확인하는 테스트, 그리고 이 모두를 굴리는 프로젝트 관리와 형상 관리가 큰 축이다. 시험에서는 개발 과정의 틀(생명주기 모델, 방법론), 모듈 독립성, 디자인 패턴, 모델링 도구, 관리 기법(비용 산정, 형상 관리)으로 나눠 묻는다. 비용 산정·일정·위험 관리는 프로젝트 관리 노트에 있다.

개발 프로세스와 생명주기

프로세스는 고객 요구를 만족하는 제품을 만들려고 절차·방법, 도구·장비, 인력을 하나로 묶는 접착제다. "제품의 품질은 그것을 만드는 프로세스의 품질이 정한다"(Watts Humphrey)는 원칙 때문에, 계획을 세운다는 것은 곧 프로세스를 정의한다는 뜻이 된다. 단계마다 착수 기준(Entry), 할 일(Task), 검증 기준(Verification), 완료 기준(eXit)을 적어 프로세스를 정의하는 방법을 ETVX라고 한다.

용어뜻예
개발 생명주기(SDLC)어떻게 개발할지를 정한 최상위 수준의 프로세스. 단계, 접근 방식, 마일스톤·일정·비용 산정의 기준을 준다폭포수, 나선형, V-모델
개발 방법론생명주기의 각 단계에서 무엇을 어떻게 할지 구체적으로 정한 것. 기법, 산출물 양식, 점검 항목을 준다UP, 애자일(스크럼, XP)

어느 모델을 고르든 거치는 활동은 비슷하다. 모델은 이 활동을 한 번에 순서대로 밟느냐, 작게 여러 번 돌리느냐가 다를 뿐이다.

의사소통 → 요구사항 수집 → 타당성 분석 → 시스템 분석 → 설계 → 구현 → 테스트 → 통합 → 설치·배포 → 운영·유지보수

타당성 분석(feasibility study) 은 요구를 대략 모은 직후, 이 프로젝트를 할 가치가 있는지 판정하는 단계다. 기술적 타당성(지금 기술·인력으로 만들 수 있나), 경제적 타당성(비용 대비 이익이 남나), 운영적 타당성(만든 뒤 조직이 실제로 쓰고 유지할 수 있나)을 보고, 일정·법적 제약을 더하기도 한다. 여기서 아니라고 나오면 설계에 들어가기 전에 멈춘다.

생명주기 모델

접근 방식은 둘로 나뉜다. 빅뱅(Big Bang) 방식은 요구사항부터 테스트까지 한 번에 순서대로 밟고 끝에 한 번 배포한다. 진화적(Evolutionary) 방식은 요구·설계·구현·테스트를 작은 주기로 여러 번 돌린다. 영어권 교재에서 "빅뱅 모델"은 이 분류가 아니라 계획 없이 인력과 돈을 코딩에 몰아넣고 결과를 기대하는 모델을 가리키기도 한다. 요구가 불분명한 학습·실험용 작은 프로젝트에나 맞고, 아래 주먹구구식과 같은 부류다.

모델방식특징맞는 상황
주먹구구식(Build & Fix)-분석·설계 없이 코딩하고 만족할 때까지 고친다. 관리·유지보수가 어려워 체계적 모델 연구의 계기가 됐다아주 작은 프로그램
폭포수(Waterfall)빅뱅이전 단계 산출물이 다음 단계 입력이다. 앞 단계가 끝나야 다음으로 가고, 고객은 후반에야 동작하는 소프트웨어를 본다규모가 작고 요구사항이 명확할 때
반복적(Iterative)·점진적(Incremental)진화적작은 규모로 전 단계를 한 바퀴 돌아 동작하는 소프트웨어를 만들고, 바퀴마다 기능을 더한다. 바퀴마다 결과물이 그 자체로 완결되고, 사이사이 위험을 다시 점검한다. 관리 단위가 작아지는 대신 반복할수록 자원이 더 든다요구가 조금씩 드러나고 일찍 일부라도 써야 할 때. UP·애자일의 바탕
원형(Prototyping)진화적주요 기능만 담은 시제품을 빨리 만들어 고객과 평가하며 요구를 확정한다. 원형은 확인 뒤 버리고, 성능·보안은 보통 고려하지 않는다초기에 요구사항을 알기 어려울 때
나선형(Spiral)진화적폭포수와 원형의 장점에 위험 분석을 더했다. 계획 → 위험 분석 → 개발 → 고객 평가를 반복한다. 위험 분석이 틀리면 문제가 되고 관리가 복잡하다비용이 크고 기간이 긴 프로젝트
RAD(Rapid Application Development)진화적재사용 컴포넌트, CASE·화면 생성 도구, 사용자 참여로 60~90일 정도의 짧은 주기에 동작하는 시스템을 만든다. 모듈로 나눌 수 있고 사용자가 계속 참여할 수 있어야 한다업무 시스템처럼 기능을 잘게 나눌 수 있고 납기가 급할 때
V-모델빅뱅왼쪽 개발 단계와 오른쪽 테스트 단계를 짝지어 검증과 확인을 강조한다. 테스트 설계를 개발과 병행한다품질·안전 요구가 높은 시스템 (테스트 레벨)

방법론: UP와 애자일

UP(Unified Process) 는 Jacobson·Booch·Rumbaugh가 만든 객체지향 방법론이다. 유스케이스로 요구를 분석하고, 아키텍처를 중심에 두며, 위험 관리를 중시하고, 반복적·점진적으로 개발한다. 시간 순서로 네 단계를 거치되 각 단계 안에서 요구·설계·구현·테스트가 모두 조금씩 일어난다는 점이 폭포수와 다르다.

단계초점
도입(Inception)타당성을 평가해 진행 여부를 정하고 요구사항 전체를 대략 파악한다
정련(Elaboration)요구 분석과 아키텍처를 확정하고 상세 설계·구현 계획을 세운다. 핵심 기능만 구현·테스트한다
구축(Construction)구현과 테스트에 집중한다
전이(Transition)배포하고 사용자 환경에서 인수 테스트와 사용자 교육을 한다

애자일(Agile) 은 변경을 받아들이고 빠른 배포를 강조하는 반복적·점진적 방법이다. 2001년 애자일 선언문은 프로세스·도구보다 개인과 상호작용을, 포괄적 문서보다 동작하는 소프트웨어를, 계약 협상보다 고객 협력을, 계획 준수보다 변화 대응을 더 중시한다. 대표 방법론은 XP, 스크럼, FDD, 린, AUP이고, 공통 실천은 짧은 반복, 데일리 스탠드업, 유저 스토리, 페어 프로그래밍, 리팩토링, 지속적 통합, TDD, 회고다. 스크럼의 스프린트 회고는 주기가 끝날 때 무엇을 개선할지 돌아보고 기록하는 활동이다. 폭포수와 애자일을 고르는 기준은 애자일과 스프린트에 있다.

리팩토링은 동작은 바꾸지 않고 코드 구조를 개선하는 것이다. 중복 코드, 방대한 클래스, 장황한 메서드 같은 코드 스멜이 대상이다(리팩터링 원칙).

요구공학

요구사항은 문제를 풀기 위해 제품이 갖춰야 할 기능과 지켜야 할 제약이다. 기능적 요구사항은 시스템이 제공할 기능·서비스, 비기능적 요구사항은 성능·보안·신뢰성 같은 제약과 품질 속성이다. 설계는 요구사항(무엇을)을 어떻게 만들지 정하는 일이고, 테스트는 만든 것이 요구사항을 지키는지 확인하는 일이라, 요구사항이 흔들리면 뒤의 모든 산출물이 같이 흔들린다. 잘못된 요구사항을 운영 단계에서 고치는 비용은 분석 단계에서 고치는 비용의 수십 배 이상으로 불어난다.

구분누구의 말인가예 (경비 정산 앱)
고객 요구사항 (Needs)고객·사용자가 바라는 것. 모호해도 된다"영수증 입력이 너무 번거롭다"
제품 요구사항개발 관점에서 구체화한 것. 검증할 수 있어야 한다"영수증 사진을 올리면 금액·날짜·가맹점명을 자동으로 채운다", "인식 결과는 3초 안에 보여 준다"

고객 요구사항 하나가 제품 요구사항 여러 개로 갈라지는 것이 보통이다. 이 변환을 체계적으로 하는 공학적 방법이 요구공학이다.

flowchart LR
  subgraph dev["요구사항 개발"]
    direction LR
    el["추출<br>(후보)"] --> an["분석<br>(합의)"] --> sp["명세<br>(공식 문서)"] --> vf["검증<br>(일관성 확인)"]
  end
  subgraph mgmt["요구사항 관리"]
    direction TB
    cm["변경 관리"] ~~~ tr["추적 관리"]
  end
  dev --> bl["베이스라인"] --> mgmt

개발 쪽은 요구사항을 만드는 일이고, 관리 쪽은 합의한 요구사항이 프로젝트 끝까지 유지되게 하는 일이다.

요구사항 추출

  1. 비즈니스 이해: 시장 동향과 현재 업무의 문제를 보고 개발 범위를 정한다.
  2. 이해관계자 식별: 사용자, 발주 고객, 운영자뿐 아니라 경쟁사, 협력 업체, 관련 법규·표준도 이해관계자다.
  3. 기대사항 수집: 인터뷰, 설문, 브레인스토밍, 마인드맵, 벤치마킹, 워크숍 중 기간·비용에 맞는 방법을 고른다.
기법방법강점과 한계
인터뷰질문을 미리 정한 구조화(폐쇄형)와 대화 흐름을 따르는 비구조화(개방형), 일대일과 그룹깊이 들을 수 있다. 그룹 인터뷰는 여러 사람이 서로 빠진 요구를 메운다
설문·질문지정해진 문항과 보기를 여러 이해관계자에게 돌린다많은 사람에게 싸게 묻는다. 보기에 없는 문제는 놓친다
브레인스토밍비공식 토론에서 나온 의견을 모두 기록한다숨은 아이디어를 끌어낸다. 정리·우선순위가 따로 필요하다
관찰현장에 가서 기존 시스템과 실제 업무 흐름, 문제 대처 방식을 본다사용자가 말하지 않는 암묵적 절차가 드러난다
과업 분석새 시스템이 대신할 작업을 단계별로 쪼개고, 쓰던 시스템이 있으면 그것을 분석한다기존 기능의 누락을 막는다
도메인 분석해당 분야 전문가와 문헌으로 그 분야의 일반·특수 요구를 찾는다고객이 당연하게 여겨 말하지 않는 규칙을 찾는다
프로토타이핑세부 기능 없이 화면부터 만들어 보여 주고 피드백을 받는다고객이 자기 요구를 모를 때 강하다

이해관계자는 자기가 원하는 것을 정확히 말하지 못하는 경우가 많다. 한 가지 방법에 기대지 말고 여러 방법을 섞어 빈틈을 메운다. 사용자 조사 방법은 사용자 리서치에 있다.

요구사항 분석

  • 전제 조건 식별: 고객이 이미 정해 둔 조건. 출시 시점, 대상 시장, 지켜야 할 법규, 기존 대비 개선 목표 같은 것들이다.
  • 운용 시나리오 분석: 어떤 환경(오프라인, 저사양 단말, 야외)에서 어떤 모드(평상시, 점검 중, 장애 시)로 쓰이는지 정리한다.
  • 기능·비기능 요구사항 분석: 시스템이 무엇을 하는지와 얼마나 잘 하는지를 나눈다.
분류묻는 것예
기능무엇을 해야 하나행위, 외부 인터페이스, 제어, 고장 감지·회피 같은 안전 기능
품질얼마나 믿을 만한가신뢰성, 유지보수성, 사용성, 보안, 재사용성
성능얼마나 빠르고 많이응답 시간, 처리량, 용량, 정확도
제약무엇에 묶여 있나외부 환경, 표준·법규, 조직의 비용·일정·인력

모은 요구사항에는 우선순위를 붙인다. 흔히 쓰는 MoSCoW 분류는 Must have(없으면 출시할 수 없다), Should have(중요하지만 미뤄도 동작한다), Could have(있으면 좋다), Won't have this time(이번 범위에서 뺀다)이다. Must는 반드시 구현하고, Should는 이해관계자와 협상하며, Could와 Won't는 다음 배포로 넘긴다.

비기능 요구사항은 숫자로 쓴다. "빠르게 응답한다"는 검증할 수 없고, "사용자 요청의 95%를 2초 안에 응답한다"는 검증할 수 있다. 분석 방법은 데이터 흐름을 따라 하향식으로 쪼개는 구조적 분석과, 사용자 시나리오에서 출발하는 객체지향 분석(유스케이스)으로 나뉜다(아래 「소프트웨어 설계」).

요구사항 명세

분석 결과를 소프트웨어 요구사항 명세서(SRS) 로 정리한다. SRS의 목차는 IEEE 830(현재는 ISO/IEC/IEEE 29148이 대체)이 대표적이다. 외부 인터페이스(사용자·하드웨어·소프트웨어·통신), 기능 요구사항(정보 흐름, 프로세스 설명, 데이터 사전), 비기능 요구사항 순으로 구성한다. 표기법은 정형화 정도에 따라 셋으로 나뉜다.

표기법수단장점한계
비정형 (Informal)자연어, 조직 내부의 그림누구나 읽고 쓸 수 있다사람마다 다르게 읽는다
준정형 (Semiformal)DFD, UML 같은 범용 모델링 언어기능·구조·동작을 같은 기호로 공유한다표기법을 배워야 한다
정형 (Formal)수학 기호, 정형 명세 언어사람과 도구가 똑같이 해석한다. 자동 검증이 가능하다작성 비용이 크다. 안전 필수 시스템 위주

자연어 명세의 모호함을 줄이는 간단한 방법이 EARS(Easy Approach to Requirements Syntax)다. 문장을 몇 가지 정해진 틀에 맞춰 쓴다.

패턴틀예
항상시스템은 ~해야 한다앱은 모든 금액을 원 단위 정수로 저장해야 한다
이벤트~하면, 시스템은 ~해야 한다사용자가 영수증 사진을 올리면, 앱은 3초 안에 인식 결과를 보여 줘야 한다
상태~인 동안, 시스템은 ~해야 한다오프라인인 동안, 앱은 입력 내용을 기기에 임시 저장해야 한다
원치 않는 상황만약 ~라면, 시스템은 ~해야 한다만약 인식한 금액이 100만 원을 넘으면, 앱은 사용자에게 확인을 요청해야 한다
선택 기능~가 있는 경우, 시스템은 ~해야 한다법인카드 연동을 쓰는 경우, 앱은 카드 승인 내역과 영수증을 자동으로 짝지어야 한다

틀에 맞추면 트리거, 조건, 기대 동작이 문장마다 드러나서 그대로 테스트 케이스가 된다.

요구사항 검증

요구사항 단계에는 아직 코드가 없으니 리뷰로 검증한다. 막연히 읽지 말고 기준별 체크리스트를 쓴다.

기준확인할 질문
완전성모든 기능과 운용 모드·환경을 다뤘나? 비기능 요구사항이 빠지지 않았나? 아직 모르는 값은 TBD로 표시했나?
명확성두 가지로 읽히는 문장은 없나? 서로 부딪히는 요구사항은 없나? 다이어그램 표기는 정확한가?
실현 가능성일정과 조직의 기술 수준 안에서 만들 수 있나? 우선순위가 정해져 있나?
추적 가능성요구사항마다 고유 ID가 있나? 상위의 이해관계자·시스템 요구사항과 연결되나?

변경 관리와 추적

검증을 마친 요구사항은 베이스라인으로 확정하고(아래 「형상 관리」의 베이스라인과 같은 개념), 그 뒤의 변경은 반드시 절차를 거친다. 베이스라인 전의 수정은 아직 개발 중인 것이라 변경 관리 대상이 아니다.

flowchart LR
  rq["변경 요청<br>(고객·개발자)"] --> ia["영향 분석"] --> rv{"변경 심사"}
  rv -->|"승인"| ex["변경 실시"] --> ck["변경 확인"]
  rv -->|"반려"| rj["요청자에게 사유 회신"]

심사 때 보는 것은 변경의 난이도, 이미 만든 산출물(설계·코드·테스트)에 미치는 영향, 일정과 비용 안에서 가능한지다. "요구한 대로 만들었는데 고객은 다른 것을 기대했다"는 상황이 되풀이되면, 변경 요청을 대화로 처리하지 말고 이 흐름에 올린다.

추적 관리는 요구사항과 다른 산출물 사이의 연결을 기록해서, 하나가 바뀌면 영향을 받는 곳을 바로 찾게 한다. 수직 추적은 요구사항 → 설계 → 코드처럼 개발 단계를 따라 내려가고, 수평 추적은 V-모델의 같은 높이끼리(고객 요구사항 ↔ 인수 테스트, 요구사항 분석 ↔ 시스템 테스트) 잇는다. 보통 추적 매트릭스로 관리한다.

요구사항 ID설계코드테스트 케이스상태
REQ-OCR-001영수증 인식 모듈 설계receipt/ocr.tsTC-031, TC-032통과
REQ-OCR-002금액 검증 규칙receipt/validate.tsTC-040실패 1건
REQ-OFF-001오프라인 저장소 설계(미구현)미작성진행 중

테스트가 없는 요구사항, 요구사항이 없는 코드가 매트릭스의 빈칸으로 드러난다.

공공SW 사업의 요구사항 분류

국내 공공 SW 사업의 제안요청서(RFP)는 요구사항을 정해진 유형과 코드로 나눠 쓴다. 「공공SW사업 제안요청서 작성을 위한 요구사항 상세화 실무 가이드라인」의 분류는 다음과 같다.

코드유형코드유형
SFR기능COR제약사항
PER성능PMR프로젝트 관리
ECR시스템 장비 구성PSR프로젝트 지원
INR인터페이스MPR유지관리 수행
DAR데이터MHR유지관리 인력
TER테스트CNR컨설팅
SER보안ENR공사
QUR품질--

RFP를 읽거나 쓸 때 "SFR-012"처럼 붙은 ID가 이 코드다. 기능 요구사항만 사업마다 내용이 완전히 달라서 공통 항목 없이 상세화 수준을 정해 쓰고, 나머지 유형은 작성 항목이 어느 정도 정해져 있다. 기획서를 태스크로 쪼개는 실무 방식은 BDD·SDD로 기획서를 태스크로 나누기에 있다.

소프트웨어 설계

설계는 소프트웨어를 이루는 요소와 그 구조를 정하는 활동이고, 수준에 따라 둘로 나뉜다.

구분다른 이름무엇을 정하나
아키텍처 설계상위 설계, 예비 설계구성 요소(모듈·컴포넌트) 사이의 관계로 된 전체 구조와 인터페이스
상세 설계하위 설계각 구성 요소 내부의 자료구조(변수 이름·타입·값)와 알고리즘 로직

세 단계로 나누는 교재도 있다. 이때는 시스템을 상호작용하는 구성 요소로 보는 아키텍처 설계, 그것을 하위 시스템과 모듈, 모듈 간 상호작용으로 풀어 내는 상위(high-level) 설계, 모듈 내부의 논리 구조와 인터페이스를 정하는 상세 설계 순이다. 설계 산출물(설계서, 의사 코드, 논리 다이어그램)은 구현에 들어가기 전에 설계 리뷰나 정형 검증 도구로 확인한다. 설계 결함은 늦게 발견할수록 고치는 비용이 커진다.

아키텍처는 건물의 청사진 역할을 한다. 이해관계자 사이의 공통 의사소통 수단이 되고, 개발 조직의 구조와 일정·비용 예측에 영향을 주고, 변경이 생겼을 때 원인을 찾게 해 주고, 재사용할 구성 요소를 확보하는 수단이 된다. 무엇보다 비기능 요구(품질 속성)를 반영하는 자리다. 같은 기능이라도 모니터링 모듈을 따로 두느냐에 따라 신뢰성이 달라진다. 품질 속성의 표준 분류는 ISO/IEC 25010에, 아키텍처 스타일은 소프트웨어 아키텍처 패턴에 있다.

설계 원리

원리뜻예 (만년 달력)
추상화세부를 정하기 전에 상위 수준에서 먼저 설계한다과정: "입력 → 윤년 판정 → 요일 계산 → 출력" 흐름만 먼저 정한다. 데이터: 연·월·일을 "날짜" 하나로 본다. 제어: "윤년이면 A, 아니면 B"를 "요일 계산" 한 단계로 묶는다
단계적 상세화상위 설계를 점점 구체적인 설계로 나눠 내려간다 (Niklaus Wirth)"처리"를 "입력 검증, 총 날짜 계산, 윤년 판정, 요일 결정"으로 나눈다
모듈화함수와 데이터로 이루어진 독립 단위로 나눈다각 단위가 이름을 갖고, 따로 컴파일되고, 다른 곳에서 재사용될 수 있어야 모듈이다

좋은 모듈은 정보 은닉, 높은 응집도, 낮은 결합도를 갖는다(아래 「모듈과 모듈 독립성」).

시스템을 나눠 가는 방향은 둘이다.

방향진행맞는 상황
하향식(top-down)시스템 전체를 하나로 보고 하위 시스템·구성 요소로 쪼개기를 가장 낮은 수준까지 반복한다. 단계적 상세화와 같은 방향이다처음부터 새로 만들고 세부를 아직 모를 때
상향식(bottom-up)가장 기본적인 구성 요소부터 만들고, 그것을 조합해 상위 구성 요소를 올려 간다. 위로 갈수록 추상화 수준이 높아진다기존 시스템의 부품·라이브러리를 재사용해 새 시스템을 만들 때

실제로는 어느 한쪽만 쓰지 않고 섞는다. 테스트에서 같은 두 방향으로 모듈을 붙여 가는 통합 순서는 통합 테스트에 있다.

구조적 방법과 객체지향 방법

관점구조적객체지향
출발점데이터 흐름과 처리 ("어떤 처리가 일어나는가")사용자 시나리오와 책임 ("무엇이 책임을 지는가")
분석 산출물시스템 문맥도, DFD유스케이스 다이어그램·명세
설계 단위태스크, 모듈, 함수컴포넌트, 클래스, 메서드
잘 맞는 곳센서·주기 처리가 많은 C 기반 임베디드·실시간 시스템도메인 개념이 풍부한 업무 시스템

객체지향 쪽 흐름은 아래 「UML로 하는 객체지향 분석·설계」에 있다. 함수 지향 설계는 DFD로 데이터 흐름을 그리고 그 위의 처리를 함수로 나누므로, 상태가 중요하지 않고 입력을 출력으로 바꾸는 시스템에 잘 맞는다. 함수가 전역 상태를 바꾸면 다른 모듈이 영향을 받는다는 약점이 있어서, 상태가 중요한 도메인은 상태와 동작을 객체로 묶는 객체지향 설계가 낫다.

사용자 인터페이스 설계

사용자에게 시스템은 곧 화면이다. 기능이 맞아도 쓰기 어려우면 받아들여지지 않으므로, 인터페이스 요구(쉬운 탐색, 일관된 요소, 빠른 응답, 피드백, 도움말, 합리적인 기본값)도 요구사항으로 다룬다. 설계 원칙으로 가장 많이 인용되는 것이 Ben Shneiderman의 인터페이스 설계 8가지 황금률이다.

규칙뜻
일관성을 지킨다비슷한 상황에는 같은 동작 순서, 같은 용어, 같은 배치를 쓴다
누구나 쓸 수 있게 한다초보에게는 설명을, 자주 쓰는 사람에게는 단축키·매크로 같은 지름길을 준다 (초판의 「숙련자용 단축키」를 넓힌 것)
유익한 피드백을 준다모든 조작에 반응을 보인다. 잦고 사소한 조작에는 가볍게, 드물고 중요한 조작에는 분명하게
끝맺음이 있는 대화를 설계한다작업을 시작·중간·끝으로 묶고, 끝에 완료를 알려 다음으로 넘어가게 한다
오류를 막고 쉽게 처리하게 한다심각한 실수를 할 수 없게 만들고, 실수하면 원인과 고치는 방법을 간단히 알려 준다
되돌리기를 쉽게 한다취소할 수 있다는 믿음이 불안을 줄이고 낯선 기능을 써 보게 한다
사용자가 주도한다고 느끼게 한다시스템이 끌고 가는 대신 사용자가 행동을 시작하고 시스템이 반응한다
단기 기억 부담을 줄인다화면을 단순하게 하고, 기억해야 할 코드나 순서를 화면에 보여 준다

화면을 평가할 때 쓰는 닐슨의 10가지 휴리스틱과 사용성 테스트는 사용자 리서치에 있다.

구조적 분석과 설계

구조적 방법론은 데이터가 어디서 들어와 어떻게 바뀌어 어디로 나가는지를 기준으로 시스템을 위에서 아래로 쪼갠다.

단계하는 일산출물
요구사항 분석데이터 흐름을 중심으로 기능을 분석한다시스템 문맥도, 데이터 흐름도(DFD), 데이터 사전, 프로세스 명세
아키텍처 설계기능을 구현할 실행 단위와 모듈을 정한다태스크 모델(동적 관점), 모듈 구조(정적 관점)
상세 설계모듈 안의 함수 구조를 정한다구조 차트

아래에서는 주차장 차단기 제어 소프트웨어를 예로 단계를 따라간다.

시스템 문맥도: 경계를 긋는다

만들 소프트웨어 전체를 프로세스 하나(번호 0) 로 두고, 주고받는 외부 개체(터미네이터)와 정보 흐름만 그린다. 목적은 범위 확정이다.

flowchart LR
  sensor["차량 감지 센서"] -->|"차량 진입"| p0(("차단기 제어 0"))
  cam["번호판 카메라"] -->|"번호판 정보"| p0
  timer["타이머"] ==>|"주기 신호"| p0
  p0 -->|"개폐 명령"| motor["차단기 모터"]
  p0 -->|"안내 문구"| board["전광판"]

정보 흐름은 값이 중요한 데이터 흐름(번호판 문자열)과, 언제 일어났는지가 중요한 제어 흐름(타이머 인터럽트, 차량 진입 신호)으로 나뉜다. 문맥도 옆에는 흐름마다 의미와 값의 형태, 주기를 표로 적는다(예: "차량 진입: True/False, 센서 인터럽트로 입력").

데이터 흐름도: 개발자가 이해할 때까지 쪼갠다

문맥도의 프로세스 0을 1, 2, 3으로, 다시 1.1, 1.2로 나눈다. 기호는 데이터 프로세스, 제어 프로세스, 데이터 흐름, 제어 흐름, 데이터 저장소다. 한 프로세스가 출력을 여러 개 내보내면 아직 덜 쪼갠 것으로 보고 더 나눈다.

flowchart LR
  in1["번호판 정보"] --> p11(("1.1 번호판 인식"))
  in2["차량 진입"] --> p12(("1.2 센서 입력"))
  p11 -->|"차량 번호"| p13(("1.3 출입 판정"))
  p12 -->|"진입 여부"| p13
  p13 -->|"개폐"| p21(("2.1 모터 제어"))
  p13 --- ds[("등록 차량 목록<br>(데이터 저장소)")]

최하위 프로세스에는 역할 유형과 실행 유형을 붙인다.

구분유형뜻
역할Device센서·디스플레이 같은 장치를 다룬다 (데이터 프로세스)
역할Control상태를 제어한다 (제어 프로세스, 상태 머신으로 명세)
역할Function특정 알고리즘을 처리한다 (데이터 프로세스)
실행Synchronous다른 프로세스와 맞물려 데이터를 주고받는다 (일반 함수 호출)
실행Asynchronous독립적으로 돌며 이벤트에 반응한다 (인터럽트 처리)
실행Periodic주기적으로 실행된다 (타이머로 5ms마다 센서 읽기)

데이터 사전과 프로세스 명세

데이터 사전은 DFD에 나온 데이터의 이름, 설명, 값의 범위를 정의한다.

데이터설명값의 범위
차량 번호인식한 번호판 문자열7~8자, 한글 1자 포함
진입 여부차단기 앞 차량 유무True / False
개폐 명령모터에 보내는 명령Open / Close

프로세스 명세는 최하위 프로세스마다 쓴다. 데이터 프로세스는 자연어로, 제어 프로세스는 상태 머신으로 쓴다.

프로세스 ID  1.2 센서 입력
유형        Periodic Device
입력        센서 값, 타이머 신호
출력        진입 여부 (→ 1.3 출입 판정)
처리        1. 10ms마다 센서 값을 읽는다.
            2. 세 번 연속 감지되면 진입 여부를 True로 보낸다.

상태 머신의 전이는 이벤트(파라미터) [조건] / 행위로 쓴다.

stateDiagram-v2
  direction LR
  state "닫힘" as closed
  state "열림" as opened
  closed --> opened : 판정(결과) [결과 = 허가] / "모터 열기"
  opened --> closed : 진입 여부(값) [값 = False] / "모터 닫기"

태스크 모델과 모듈 구조

태스크는 운영체제가 따로 스케줄하는 실행 단위(스레드)다. DFD의 프로세스를 그대로 태스크로 만들면 너무 많아지므로 세 단계로 줄인다.

  1. 태스크 후보 식별: 혼자 돌아야 하는 프로세스(Asynchronous, Periodic, Control)를 후보로 뽑는다.
  2. 잔여 프로세스 할당: 후보가 되지 못한 프로세스(Synchronous, Function)를 앞뒤로 이어 실행되는(절차적 응집) 후보에 붙인다.
  3. 태스크 통합: 시간 제약을 어기지 않는 선에서, 같은 타이머로 깨어나는 태스크끼리(시간적 응집) 합친다.

입력이 연달아 들어와도 잃으면 안 되는 태스크 사이에는 비동기 메시지(큐) 를 쓴다. 각 태스크가 어느 프로세스를 품고 있는지는 추적 관계로 남겨 둔다.

모듈은 구현 단위다. C라면 .c/.h 파일 하나이거나 그 묶음인 디렉터리다. 태스크를 모듈로 옮기고, 여러 모듈이 같이 쓰는 기능은 라이브러리 모듈로 뽑고, 함수 호출 관계로 모듈 간 의존을 그린 뒤, 모듈마다 ID·이름·설명·인터페이스를 적는다.

모듈 ID   PK_001        모듈명  출입 판정
설명      번호판과 센서 입력을 종합해 출입 허가 여부를 정한다
인터페이스 Decide_Entry(): 허가 여부를 반환한다

공통 기능을 라이브러리로 뽑으면 재사용은 좋아지지만 의존하는 모듈이 늘어 결합도가 올라갈 수 있다. 어느 쪽이 나은지는 매번 저울질한다.

구조 차트

구조 차트는 모듈 안의 함수 호출 관계와 파라미터 이동을 계층으로 그린다. 위의 상자가 아래 상자를 호출하고, 화살표 옆에 넘기는 데이터를 적는다. 일반 호출, 비동기 호출, 반복, 조건 같은 기호가 있다.

flowchart TD
  de["Decide_Entry()"] -->|"차량 번호"| rp["Read_Plate()"]
  de -->|"진입 여부"| rs["Read_Sensor()"]
  rp --> np["Normalize_Plate()"]
  rs --> db["Debounce_Signal()"]

모듈 구조를 바탕으로 DFD의 흐름을 호출 구조로 옮긴 초기 구조 차트를 그리고, 기능을 더 쪼개고 공통 기능을 뽑아 최종본으로 다듬는다. 여기까지 오면 함수 이름과 시그니처가 거의 정해져서 바로 코딩으로 넘어갈 수 있다.

모듈과 모듈 독립성

모듈은 성능 향상과 수정·재사용 편의를 위해 기능 단위로 나눈 부분이다. 모듈 독립성은 결합도는 약하게, 응집도는 강하게, 크기는 작게 할수록 높아진다.

결합도와 응집도

  • 결합도(Coupling): 모듈 간 의존 정도, 약할수록 좋다. 약한 순서로 자료 → 스탬프 → 제어 → 외부 → 공통 → 내용. 단계별 설명은 결합도를 낮추는 전략에 있다.
  • 응집도(Cohesion): 모듈 내부 요소의 관련 정도, 강할수록 좋다. 강한 순서로 기능적 → 순차적 → 교환적 → 절차적 → 시간적 → 논리적 → 우연적. 단계별 설명은 응집도를 높이는 전략에 있다.

Fan-in / Fan-out

Fan-in은 어떤 모듈을 호출하는 상위 모듈 수(높으면 재사용성이 좋다), Fan-out은 어떤 모듈이 호출하는 하위 모듈 수다.

복잡도 척도

"측정할 수 없으면 통제할 수 없다"(Tom DeMarco). 소프트웨어 척도(metric)는 규모(LOC, 기능 점수), 복잡도, 품질(단계별 결함 수, 출시 후 고객이 보고한 결함 수), 프로세스, 자원(공수, 시간)을 수치로 나타낸다. 모듈의 복잡도를 재는 대표 척도는 둘이다.

McCabe 순환 복잡도(Cyclomatic Complexity, 1976) 는 제어 흐름 그래프에서 선형 독립 경로의 수를 센다. 판단문(if, while, for, case, &&·||)이 많을수록 커진다.

V(G) = E - N + 2      E: 간선 수, N: 노드 수 (연결된 그래프 하나)
V(G) = P + 1          P: 판단 노드(분기 조건) 수
V(G) = 닫힌 영역 수 + 1   평면 그래프로 그렸을 때

간선 10개, 노드 8개면 10 - 8 + 2 = 4다. 이 값은 모든 독립 경로를 한 번씩 지나는 데 필요한 테스트 케이스 수의 상한이 되어, 화이트박스 테스트의 기본 경로 수로 쓰인다. 모듈당 10 이하가 흔한 권장 기준(McCabe)이고, 넘으면 함수를 나눈다. 정적 분석 도구와 린터(ESLint complexity, SonarQube)가 자동으로 잰다.

Halstead 척도(1977) 는 프로그램을 연산자와 피연산자 토큰의 나열로 보고 소스 코드에서 정적으로 계산한다.

기호뜻식
n1, n2서로 다른 연산자 수, 서로 다른 피연산자 수-
N1, N2연산자 전체 출현 수, 피연산자 전체 출현 수-
n어휘(vocabulary)n1 + n2
N길이(length)N1 + N2
V부피(volume)N × log₂ n
D난이도(difficulty)(n1 / 2) × (N2 / n2)
E노력(effort)D × V

같은 기능을 짧은 어휘로 표현할수록 부피와 노력이 작다. 두 척도 모두 "읽기 어려운 코드"를 숫자로 찾아내는 신호일 뿐, 숫자를 맞추려고 코드를 쪼개는 것이 목적이 되면 안 된다.

계층과 API

함수가 수십만 개로 늘면 함수 단위로도 관리가 안 된다. 관련 함수와 변수를 객체로 묶고, 객체 안에 다시 객체를 넣어 계층을 만든다. 자동차 소프트웨어라면 Car 안에 Engine, 그 안에 CruiseControl·IgnitionControl이 들어가고, 호출은 car.engine.cruiseControl.setSpeed(55)처럼 바깥에서 안으로 내려간다. 팀도 이 경계를 따라 나눈다.

다른 팀이 만든 모듈을 쓸 때 기댈 것은 문서와 API(Application Programming Interface) 다. API는 모듈이 밖에 공개하는 함수와 데이터의 목록이다. IgnitionControl은 setRPM()을 public으로 열어 크루즈 컨트롤이 부를 수 있게 하고, 점화 플러그를 직접 점화하는 함수는 private으로 닫아 자기 안에서만 부른다. 필요한 것만 골라 공개하는 것이 정보 은닉이다. 내부를 바꿔도 API가 같으면 쓰는 쪽은 고칠 것이 없으므로, 정보 은닉은 결합도를 낮추는 실제 수단이 된다.

디자인 패턴 (GoF)

GoF 패턴 23개는 목적에 따라 생성(객체를 만드는 방식: Singleton, Factory Method, Builder 등), 구조(객체를 조합하는 방식: Adapter, Decorator, Proxy 등), 행위(객체가 협력하고 책임을 나누는 방식: Observer, Strategy, Template Method 등)로 나뉜다. 패턴별 비유·푸는 문제와 헷갈리는 짝은 GoF 디자인 패턴에 있다.

객체지향

  • 구성 요소: 객체(데이터 + 메서드), 클래스(공통 속성·연산을 가진 객체의 틀, 객체는 클래스의 인스턴스), 메시지(객체 간 상호작용 수단)
  • 기본 원칙: 캡슐화, 정보 은닉, 상속, 추상화, 다형성
  • 럼바우(OMT) 분석 기법: 객체 → 동적 → 기능 모델링 순서
    • 객체(정보) 모델링: 객체 다이어그램, ERD. 구조를 나타낸다
    • 동적 모델링: 상태 다이어그램. 시간에 따른 변화를 나타낸다
    • 기능 모델링: 자료 흐름도(DFD). 입력이 출력으로 바뀌는 과정을 나타낸다

UML

UML(Unified Modeling Language)은 객체지향 분석·설계의 결과를 그림으로 기록하는 표준 표기다. UML의 그림은 객체지향 개념을 옮긴 것이므로 객체지향 개념을 먼저 알아야 읽힌다.

쓰는 이유내용
빠른 이해시스템의 구조와 동작을 한눈에 파악한다
분할복잡한 시스템을 이해하기 쉬운 조각으로 나눈다
소통설계 결정을 그림으로 전달한다. 개발자뿐 아니라 업무 담당자, 아키텍트도 읽는다
독립성특정 플랫폼·언어·기술에 묶이지 않아 개념을 추상화하기 쉽다
인수인계새 팀에 시스템을 넘기기 쉽다

현재 표준은 다이어그램 14종을 정의한다. 구조 7종(클래스, 객체, 패키지, 컴포넌트, 복합체 구조, 배치, 프로파일)과 행위 7종(유스케이스, 활동, 시퀀스, 상태, 커뮤니케이션, 상호작용 개요, 타이밍)이다. 다이어그램은 정적인 구조와 동적인 행위로 나뉜다. 유스케이스 다이어그램 작성법과 유스케이스 명세는 유스케이스 다이어그램에 있다.

구조 다이어그램

다이어그램표현하는 것
클래스클래스의 속성·연산과 클래스 사이 관계. 시스템 구조를 파악하고 문제점을 찾는다
객체특정 시점의 인스턴스(객체)들과 그 관계
컴포넌트실제 구현 모듈(컴포넌트) 사이의 관계와 인터페이스. 구현 단계
배치결과물·프로세스·컴포넌트가 놓이는 물리적 노드와 통신 경로. 구현 단계
복합체 구조클래스·컴포넌트가 복합 구조일 때 그 내부 구조
패키지유스케이스·클래스 같은 요소를 묶은 패키지 사이의 관계(<<import>> 의존)
프로파일스테레오타입·태그 값·제약으로 UML을 특정 도메인·플랫폼에 맞게 확장하는 정의

행위 다이어그램

다이어그램표현하는 것
유스케이스사용자(Actor) 관점의 요구 기능(Use Case). 기능 모델링
시퀀스객체들이 주고받는 메시지를 시간 순서로
커뮤니케이션메시지와 함께 객체 간 연관 관계까지
상태한 객체의 상태가 이벤트에 따라 어떻게 바뀌는지
활동처리 로직이나 조건에 따른 흐름을 순서대로. 기능 모델링
상호작용 개요여러 상호작용 다이어그램 사이의 제어 흐름
타이밍객체 상태 변화와 시간 제약

관계

관계 기호는 결국 코드 모양의 차이다.

관계의미표기코드에서의 모습
연관(Association)서로 관련이 있음A ───▶ B 실선상대를 필드로 계속 참조한다
집합(Aggregation)부분-전체지만 부분이 독립적으로 존재 가능A ◇─── B 빈 마름모가 전체 쪽부분을 밖에서 받아 갖는다. 다른 객체와 공유할 수 있다
합성(Composition)부분이 전체에 종속A ◆─── B 채운 마름모가 전체 쪽전체가 부분을 직접 생성해서 갖는다. 전체가 사라지면 부분도 사라진다
일반화(Generalization)상속 관계A ───▷ B 실선, 빈 삼각형이 부모 쪽extends
실체화(Realization)인터페이스 구현A - - ▷ B 점선, 빈 삼각형이 인터페이스 쪽implements
의존(Dependency)짧은 시간 동안 사용하는 관계A - - > B 점선 화살표메서드 파라미터나 지역 변수로 잠깐 쓴다
class Post {
  private comments: Comment[] = [];          // 연관 (Post 1 ── 0..* Comment)
}
 
class Comment {
  private meta = new CommentMeta();          // 합성: 직접 생성
  constructor(private author: Member) {}     // 집합: 밖에서 받은 Member를 공유
}
 
class CommentCtrl {
  register(form: CommentForm) { /* ... */ }  // 의존: 파라미터로만 사용
}

클래스 다이어그램 표기

클래스 다이어그램은 업계 전반에서 설계를 공유할 때 널리 쓰는 UML이다. 관계 표기는 위 표와 같고, 나머지 구성 요소는 다음과 같다.

요소뜻표기
클래스객체의 속성과 행동을 정한 설계도세 칸 상자: 이름 / 속성 / 메서드
속성(Attribute)클래스의 데이터 필드가시성 이름: 타입[개수] = 기본값, 예: - name: String
메서드(Method)클래스가 할 수 있는 행동가시성 이름(파라미터: 타입): 반환 타입, 예: + getName(): String
인터페이스구현 클래스가 반드시 제공할 메서드의 계약<<interface>> 스테레오타입
추상 클래스직접 인스턴스를 만들지 않는 상위 클래스클래스 이름을 기울임꼴로 쓴다
열거형(Enumeration)이름 붙은 값의 집합(상품 분류, 월)<<enumeration>> 스테레오타입
접근 제한자공개 범위+ public, - private, # protected, ~ package
다중성관계 양끝의 개수1 정확히 하나, 0..1 없거나 하나, 0..*·* 없거나 여러 개, 1..* 하나 이상, n..m n개에서 m개

연관은 기본이 양방향이다. 두 클래스가 서로의 존재와 관계를 안다. 한쪽만 상대를 알면 단방향 연관이고, 아는 쪽에서 모르는 쪽으로 화살표를 그린다. 항공 예약 시스템으로 보면 다음과 같다.

관계예
양방향 연관 + 다중성FlightInstance에는 조종사(Pilot)가 정확히 2명(2), 조종사 한 명은 여러 운항 회차(*)를 맡는다
단방향 연관Flight는 Aircraft를 알지만 기체는 어떤 항공편에 쓰이는지 모른다
집합Aircraft는 Airline 없이도 존재한다
합성WeeklySchedule은 Flight에 속해, 항공편이 사라지면 일정도 사라진다
일반화Crew, Pilot, Admin은 모두 Person이다
의존FlightReservation은 Payment를 쓴다

클래스 다이어그램은 객체지향 언어로 바로 옮겨지는 유일한 UML이라 가장 널리 쓴다. 쓰임은 애플리케이션의 정적 구조 분석·설계, 시스템 책임 기술, 컴포넌트·배치 다이어그램의 기반, 코드 생성(순공학)과 코드에서 다이어그램 복원(역공학)이다. 면접 사례에 적용한 예는 객체 지향 설계 면접에 있다.

GoF 패턴 대부분이 클래스 다이어그램으로 설명된다(GoF 디자인 패턴).

UML로 하는 객체지향 분석·설계

다이어그램을 어떤 순서로, 무엇에서 무엇을 뽑아 그리는지가 실제 설계 과정이다.

flowchart LR
  uc["유스케이스 다이어그램·명세<br>(요구사항 분석)"] --> cls["클래스 다이어그램<br>(구조 설계)"] --> beh["시퀀스·활동·상태 머신<br>(동작 설계)"]
  cls --> cmp["컴포넌트·패키지·배치<br>(묶음과 물리 구성)"]

화살표는 한 방향이 아니다. 시퀀스 다이어그램을 그리다 클래스에 없던 메서드가 나오면 클래스 다이어그램으로 돌아가 고친다. 모든 단계가 반복하며 정제하는 과정이다. 아래 예는 블로그의 「댓글 작성」 유스케이스다(명세는 유스케이스 명세).

1. 클래스 식별: 명사 추출. 명세에서 명사를 모두 뽑고(회원, 댓글, 입력창, 내용, 스팸, 목록, 이미지…), 시스템 경계 밖(회원, 스팸 필터 서비스는 액터), 중복(내용은 댓글의 일부), 너무 추상적인 것을 걸러 낸다. 남은 후보를 역할에 따라 세 스테레오타입으로 나눈다.

유형맡는 일예
경계(Boundary)시스템과 바깥(사용자 화면, 외부 시스템)을 잇는다CommentUI, SpamFilterClient
제어(Control)유스케이스의 업무 로직을 처리한다CommentCtrl
실체(Entity)오래 보관하는 데이터를 관리한다Comment, Attachment

명세에 직접 나오지 않아도 화면과 데이터 사이에서 로직을 맡을 제어 클래스는 새로 만들어 넣는 경우가 많다. 이 구분은 MVC나 계층형 아키텍처의 뼈대와 거의 같다. 책임을 나누는 기준은 책임 주도 설계에 있다.

2. 속성과 행위: 명사와 동사. 속성은 클래스에 남은 명사에서(Comment는 내용, 작성자, 작성 시각), 행위는 명세의 동사에서 뽑는다("입력창을 펼친다"는 CommentUI, "스팸 여부를 확인한다"는 CommentCtrl). 메시지 내용만 다른 같은 동작은 하나로 합친다. "오류 문구를 보여 준다"와 "안내 문구를 보여 준다"는 showMessage(text) 하나다.

3. 시퀀스 다이어그램: 메시지의 시간 순서. 참여 객체(생명선)를 가로로 놓고, 위에서 아래로 시간이 흐르며 주고받는 메시지를 그린다. 객체가 일하는 구간은 긴 사각형(활성화)으로 표시하고, 메시지는 동기(채운 화살촉), 비동기(열린 화살촉), 반환(점선)으로 나뉜다.

sequenceDiagram
  actor M as 회원
  participant UI as :CommentUI
  participant C as :CommentCtrl
  participant S as :SpamFilterClient
  participant D as :Comment
  M->>UI: 등록 클릭
  UI->>C: register(form)
  C->>S: check(body)
  S-->>C: ok
  C->>D: save()
  C-->>UI: 목록

조건과 반복은 결합 단편(combined fragment) 상자로 감싼다. opt는 if, alt는 if / else, loop는 for·while, break는 조건이 참이면 나머지를 건너뛰고 빠져나가기, par는 동시 실행, strict는 적힌 순서 강제다. 그리다 보면 "스팸이면?" 같은 빠진 흐름과 새 메서드가 나오고, 이것을 클래스 다이어그램에 돌려 반영한다.

4. 활동 다이어그램: 실행 흐름. 업무 프로세스, 유스케이스 흐름, 메서드의 알고리즘을 순서도처럼 그린다.

노드의미
행위 노드하나의 동작. 일반 동작 호출, 다른 활동 호출, 특정 클래스 연산 호출, 시간 이벤트 수신
초기 / 최종 노드시작점. 최종은 활동 최종(모든 흐름 종료)과 흐름 최종(그 흐름만 종료)으로 나뉜다
결정 / 병합마름모. 조건(가드)이 참인 한 흐름으로만 간다 / 갈라진 흐름 중 하나라도 오면 통과
분기 / 결합굵은 막대. 흐름을 복제해 동시에 진행 / 모든 흐름이 도착해야 하나로 합친다
파티션수영 레인. 누가 그 동작을 맡는지 영역으로 나눈다

결정·병합은 "둘 중 하나", 분기·결합은 "둘 다 동시에"다. 이 차이를 헷갈리면 병렬 처리를 순차로 그리게 된다.

활동 다이어그램과 시퀀스 다이어그램은 둘 다 흐름을 그리지만 보는 것이 다르다.

구분활동 다이어그램시퀀스 다이어그램
담는 것처리 흐름. 조건과 순서를 강조한다객체 사이의 상호작용(메시지 교환)
모델링 관점기능 모델링: 외부 입력이 연산과 내부 저장소를 거쳐 외부 출력이 되는 값의 흐름동적 모델링: 상태, 상태 전이, 전이를 일으키는 이벤트
쓰는 곳업무 프로세스, 유스케이스의 실행 단계, 내부 연산유스케이스 하나(또는 그 일부)의 상세한 호출 순서, 복잡한 연산의 논리 탐색

5. 상태 머신 다이어그램: 한 객체의 일생. 상태가 복잡하게 바뀌는 대상(주문, 결제, 장비)을 골라 상태와 전이를 그린다. 전이 라벨은 구조적 방법의 상태 머신과 같은 이벤트(파라미터) [조건] / 행위이고, 조건에 따라 여러 전이 중 하나를 고르는 지점은 선택(choice) 노드로 그린다.

stateDiagram-v2
  direction LR
  state "작성 중" as draft
  state "게시됨" as posted
  state "숨겨짐" as hidden
  state "삭제됨" as deleted
  [*] --> draft
  draft --> posted : 등록(body) [body ≠ ""] / 저장
  posted --> hidden : 신고(n) [n ≥ 5] / 숨김 처리
  hidden --> deleted : 삭제() / 기록

6. 묶음과 물리 구성.

다이어그램단위작성 순서메모
컴포넌트실행 가능한 단위컴포넌트 식별 → 인터페이스 정의 → 관계 정의제공 인터페이스(롤리팝)와 요구 인터페이스(소켓)로 잇는다. 유스케이스별 클래스 묶음이 컴포넌트 후보가 된다
패키지유스케이스·클래스 등 요소의 묶음패키지 식별 → 포함·의존 관계 정의<<import>>는 가져온 요소를 공개로, <<access>>는 비공개로 추가한다
배치물리 노드노드 식별 → 통신 경로 설정 → 아티팩트 배치장치 노드(서버 하드웨어)와 실행 환경(OS, 웹 서버, 컨테이너)을 구분한다. 통신 경로에 HTTP, TCP/IP, RPC 등을 적는다

프로젝트 관리

프로젝트는 유일한 결과물을 만드는 일시적인 활동이고, 프로젝트 관리는 품질·비용·일정 목표를 지키려고 범위를 WBS로 쪼개고, 비용을 산정하고, 일정을 잡고, 진척을 수치로 통제하고, 위험을 미리 다루는 일이다. 참고 틀(PMBOK, ISO/IEC/IEEE 12207)과 CMMI 성숙도 5단계, 비용 산정 기법(델파이, LOC, COCOMO 모드별 계수, Putnam, 기능 점수 가중치와 보정 공식), PERT 주 경로 계산, EVM 지표, 위험의 식별·분석·대응(회피·완화·전가·수용)은 소프트웨어 프로젝트 관리: 비용 산정·일정·위험에 따로 정리했다.

유지보수와 재공학

소프트웨어는 배포 뒤에도 환경·법규·사용자 요구가 바뀌어 계속 고쳐야 한다. 여러 조사에서 유지보수 비용은 생명주기 전체 비용의 절반 이상(조사에 따라 60~70%대)으로 나온다. 오래된 하드웨어·언어에 묶인 구조, 기록되지 않은 변경, 땜질식 수정이 이 비용을 키운다.

유지보수의 네 유형

유형계기예
수정(corrective) 유지보수사용자가 보고하거나 운영 중 발견한 결함버그 수정
적응(adaptive) 유지보수운영체제·하드웨어·법규·외부 시스템 같은 환경 변화OS 버전 대응, 세율 변경 반영
완전화(perfective) 유지보수새 기능, 성능·사용성 개선 요구기능 추가, 응답 속도 개선
예방(preventive) 유지보수지금은 문제없지만 나중에 커질 위험리팩터링, 문서 보강, 노후 라이브러리 교체

유지보수 작업도 작은 개발 주기다. ISO/IEC/IEEE 14764(옛 IEEE 1219)는 변경 요청 식별·분류 → 영향·비용 분석 → 설계 → 구현(단위 테스트 병행) → 시스템 테스트(회귀 테스트 포함) → 인수 테스트 → 배포 순서를 두고, 전 과정을 형상 관리로 묶는다.

Lehman의 소프트웨어 진화 법칙

Lehman은 소프트웨어를 셋으로 나눴다.

유형성격예
S형(Static)명세와 풀이가 코딩 전에 완전히 정해진다. 거의 바뀌지 않는다수식 계산기
P형(Practical)명세는 쓸 수 있지만 풀이가 바로 정해지지 않는다게임, 바둑 프로그램
E형(Embedded)실제 세계의 업무·법규에 묻혀 있어 그 변화를 계속 따라가야 한다증권 거래, 세무 시스템

대부분의 업무 시스템인 E형에 대해 여덟 가지 법칙을 제시했다. 자주 인용되는 것은 다음 다섯이다.

법칙내용
지속적 변화환경에 맞춰 계속 고치지 않으면 점점 쓸모가 줄어든다
복잡도 증가진화할수록 복잡해진다. 줄이는 작업을 따로 하지 않으면 계속 늘어난다
지속적 성장사용자를 만족시키려면 기능이 계속 늘어야 한다
품질 저하운영 환경 변화에 맞춰 엄격히 관리하지 않으면 품질이 떨어진다
친숙성 유지개발자와 사용자가 시스템을 이해하는 수준을 유지해야 하므로, 한 번에 바꿀 수 있는 양에 한계가 있다

나머지는 자기 조절(진화 과정의 측정치가 일정한 분포를 따른다), 조직 안정성(평균 작업률이 수명 내내 비슷하다), 피드백 시스템(다단계 피드백 시스템으로 다뤄야 한다)이다. 「복잡도 증가」가 예방 유지보수와 리팩터링이 필요한 이유이고(소프트웨어 복잡성), 「친숙성 유지」가 큰 변경을 작게 나눠 배포하는 이유다.

재공학과 역공학

기능은 그대로인데 기술이 낡아 유지보수가 어려워진 레거시 시스템은 재공학(re-engineering) 으로 되살린다. 어셈블리로 짠 Unix를 C로 다시 쓴 것이 고전적인 예다.

대상 결정 → 역공학(코드에서 설계·명세 복원) → 프로그램 재구조화 → 데이터 재구조화 → 순공학(복원한 명세로 새 시스템 구현)
용어뜻
역공학(reverse engineering)기존 코드를 분석해 설계와 명세를 거꾸로 얻어 낸다. 낮은 추상 수준에서 높은 수준으로 올라간다
재구조화(restructuring)기능은 바꾸지 않고 코드·데이터 구조를 다시 짠다. 같은 언어 안의 리팩터링, 다른 언어로의 이식, 함수 지향을 객체 지향으로 바꾸기가 여기 든다
순공학(forward engineering)명세에서 시스템을 만드는 일반 개발. 재공학에서는 역공학 뒤에 온다

재공학의 다른 축은 재사용이다. 응집도가 높고 결합도가 낮은 컴포넌트는 애플리케이션 전체, 하위 시스템, 모듈 수준에서 다시 쓸 수 있고, 이를 체계화한 것이 컴포넌트 기반 소프트웨어 공학(CBSE)이다. 요구사항 명세 → 설계 → 컴포넌트 명세 → 저장소에서 맞는 컴포넌트 검색 → 조립 순으로 진행하며, 맞는 컴포넌트가 없으면 요구를 컴포넌트에 맞추거나 컴포넌트를 고친다.

형상 관리 (SCM)

형상(configuration) 은 요구사항 명세서, 설계서, 소스 코드, 테스트 명세서처럼 개발 과정에서 나온 산출물들의 구성이다. 형상 관리는 이 산출물(형상 항목)을 식별하고 변경을 통제하며 처리 상태를 지켜보는 활동이고, 목표는 산출물의 무결성(승인된 것만 들어 있다)과 추적성(누가 언제 왜 바꿨는지 안다)이다. 형상 관리가 없으면 실수로 덮어쓴 작업을 되살리기 어렵고, 복사본 중 무엇이 최신인지, 버그가 어느 버전에 있는지, 고객이 어떤 수정이 들어간 버전을 가졌는지 알 수 없다.

베이스라인

베이스라인(baseline, 기준선) 은 공식 검토와 합의를 거쳐 개발의 기초가 된 상태로, 이후에는 형상 통제 절차로만 바꿀 수 있다. 보통 개발 단계나 마일스톤마다 잡는다. 요구 분석이 끝나면 요구사항 명세서가, 설계가 끝나면 아키텍처·상세 설계서가 더해지는 식으로 단계마다 베이스라인에 들어가는 항목이 늘어난다.

형상 관리 활동

활동하는 일
형상 식별관리할 항목(산출물뿐 아니라 컴파일러 같은 개발 도구까지)을 고르고, 유일한 식별자 체계를 정하고(프로젝트_문서종류_버전_초안 식 파일 이름), 베이스라인 시점을 정한다
형상 통제변경 요청을 형상 통제 위원회(CCB) 가 심사해 승인된 것만 베이스라인에 반영한다. 심사 기준은 난이도, 다른 산출물에 미치는 영향, 일정·비용 안에서 가능한지다
형상 상태 보고CCB 활동, 베이스라인 상태와 변경 내역, 변경 요청 처리 현황(요청·승인·기각 건수)을 보고해 개발 상태를 보이게 한다
형상 감사형상 관리 계획을 지켰는지, 베이스라인 설정과 변경 통제가 적절했는지 독립적으로 점검한다

정보처리기사는 여기에 버전 제어(여러 버전의 형상 항목 관리)를 따로 세고, 상태 보고를 형상 기록(식별·통제·감사 결과를 기록·보고)이라고 부르기도 한다. 변경 요청부터 확인까지의 흐름은 위 「변경 관리와 추적」의 요구사항 변경 절차와 같다. 실무에서는 Git·SVN 저장소로 항목과 버전을, Jira·Redmine 같은 이슈 시스템으로 변경 요청과 심사를 관리한다(Git 기초).

버전 관리의 기본 동작

동작뜻
저장소(repository)코드와 모든 변경 이력을 보관하는 곳
체크아웃(checkout)저장소의 코드를 내 작업 공간으로 가져온다
커밋(commit)확인을 마친 변경을 저장소에 기록해 다른 사람이 쓰게 한다
되돌리기(revert, rollback)버그가 들어오면 전체나 일부를 이전의 안정 버전으로 되돌린다
이력 추적누가 언제 무엇을 바꿨는지 남긴다(git log, git blame)

동시 수정을 다루는 방식은 둘이다. 옛 중앙 집중식 도구는 파일을 체크아웃한 사람만 고치도록 잠근다(lock-modify-unlock). Git과 SVN의 기본 방식은 각자 고친 뒤 병합한다(copy-modify-merge). 어느 쪽이든 공유하는 주 브랜치는 늘 빌드되고 돌아가야 하므로 깨진 코드를 커밋하지 않는다.

CASE 도구

CASE(Computer-Aided Software Engineering) 는 생명주기 활동을 자동화하는 도구다. 산출물을 한곳에 모으는 중앙 저장소(명세, 다이어그램, 보고서, 데이터 사전)를 중심에 두고, 어느 단계를 돕느냐로 나눈다.

구분돕는 단계예
상위(Upper) CASE계획, 요구 분석, 설계요구 관리, UML·DFD 모델링, 프로토타이핑 도구
하위(Lower) CASE구현, 테스트, 유지보수IDE, 코드 생성기, 테스트 자동화, 정적 분석
통합(Integrated) CASE요구 수집부터 테스트·문서화까지 전체상·하위를 하나의 저장소로 묶은 도구 모음

용도별로는 다이어그램, 프로세스 모델링, 프로젝트 관리(일정·공수·자원), 문서화(Doxygen 같은 API 문서 생성기), 분석(다이어그램의 불일치·누락 검사), 형상·변경 관리(Git), 프로그래밍(IDE), 프로토타이핑, 웹 개발, 품질 보증 도구가 있다. 도구가 단계 사이의 일관성을 검사해 결함을 다음 단계로 넘기기 전에 잡는 것이 핵심 이점이고, 도입·교육 비용과 도구에 프로세스가 묶이는 것이 비용이다. RAD 모델이 짧은 주기를 낼 수 있는 것도 이런 도구 덕분이다.

정리

  • 생명주기 모델은 빅뱅(폭포수·V)과 진화적(반복적·원형·나선형·RAD·UP·애자일)으로 나뉜다. 시작 전에 기술·경제·운영 타당성을 본다.
  • 요구공학은 추출 → 분석 → 명세 → 검증으로 만들고, 베이스라인 이후의 변경은 영향 분석과 심사, 추적 매트릭스로 지킨다. 비기능 요구는 숫자로, 자연어 명세는 EARS 틀로 쓴다.
  • 구조적 방법은 문맥도 → DFD → 태스크·모듈 → 구조 차트로, 객체지향 방법은 유스케이스 명세 → 클래스(명사·동사, 경계·제어·실체) → 시퀀스·활동·상태 머신으로 내려간다.
  • 모듈은 결합도를 낮추고 응집도를 높이며, 정보 은닉이 그 수단이다. 복잡도는 McCabe 순환 복잡도(E - N + 2, 10 이하 권장)와 Halstead 척도로 잰다.
  • 인터페이스는 일관성, 피드백, 오류 방지, 되돌리기, 기억 부담 줄이기 같은 황금률로 설계한다.
  • 프로젝트 관리는 WBS로 쪼개고 COCOMO·FP로 산정하고 PERT·EVM으로 일정과 비용을 통제하는 일이다. 계산법은 소프트웨어 프로젝트 관리에 있다.
  • 유지보수는 수정·적응·완전화·예방으로 나뉘고 생명주기 비용의 절반 이상을 차지한다. E형 소프트웨어는 계속 변하고 복잡해지므로(Lehman), 낡은 시스템은 역공학 → 재구조화 → 순공학으로 재공학한다.

관련 노트: 소프트웨어 프로젝트 관리: 비용 산정·일정·위험, 추상 클래스와 인터페이스, 객체 지향의 본질: 메시징, 소프트웨어 테스트, 유스케이스 다이어그램, 객체 지향 설계 면접, 소프트웨어 안전, 소프트웨어 복잡성, 새 서비스 개발 프로젝트 진행 순서, Git 기초

원본 기록4

댓글

GitHub 계정으로 댓글을 남길 수 있어요.