← 기술 지식

기술 지식 / 소프트웨어 설계

소프트웨어 설계 기초

요구사항, 불변식, 책임과 경계를 주문 취소 기능의 구현 과정으로 설명합니다.

설계 · 원리·구현·운영
기술 지식 트리

주문 취소 버튼을 추가한다고 해 보겠습니다. 버튼을 누르면 DB의 상태를 CANCELLED로 바꾸는 것만으로 충분할까요? 이미 배송된 주문, 다른 사람의 주문, 결제 환불이 필요한 주문은 다르게 처리해야 합니다. 설계는 클래스를 많이 만드는 일이 아니라 이런 규칙을 누가 어떤 경계에서 지킬지 정하는 일입니다.

요구사항과 불변식

요구사항은 사용자가 얻어야 할 결과입니다. ‘내 주문을 취소할 수 있다’에는 취소 가능한 상태, 권한, 실패 안내 같은 조건이 숨어 있습니다. 이를 예제로 구체화하면 구현 전에 모호함을 발견할 수 있습니다.

불변식은 허용된 상태에서 항상 지켜야 하는 관계입니다. 예를 들어 취소된 주문이 배송 대기열에 다시 들어가지 않아야 한다거나, 이미 반환한 금액을 다시 환불하지 않아야 한다는 규칙입니다. 필드를 private으로 만드는 것만으로는 이 관계가 보장되지 않습니다. 변경 명령이 현재 상태와 입력을 함께 검사해야 합니다.

책임과 변경 이유

HTTP 요청 파싱, 취소 가능 여부 판단, DB 저장, JSON 응답 생성은 서로 다른 이유로 바뀝니다. 모든 일을 controller 하나에 넣으면 응답 형식을 바꾸다가 취소 규칙까지 건드리기 쉽습니다.

요청을 업무 명령으로 바꾸는 어댑터, 취소 규칙을 수행하는 서비스, 저장 방식을 담당하는 구현으로 나눌 수 있습니다. 이렇게 하면 같은 취소 규칙을 HTTP뿐 아니라 메시지 소비자에서도 사용할 수 있습니다. 다만 계층 이름을 붙였다는 이유만으로 책임이 분리되는 것은 아닙니다. 서비스가 HTTP 객체나 특정 DB 드라이버의 세부사항을 그대로 요구한다면 결합이 남습니다.

주문 취소의 책임 분리HTTP 요청 · 주체·주문 ID. 취소 서비스 · 권한·상태 규칙. 저장소 · 조건부 변경. 응답 · 결과 표현명령 변환검증된 변경확정 결과HTTP 요청 · 주체·주문 ID취소 서비스 · 권한·상태 규칙저장소 · 조건부 변경응답 · 결과 표현
주문 취소의 책임 분리 화살표는 호출 흐름입니다. 각 단계는 다른 변경 이유를 가지며, 업무 규칙은 입력 형식이나 화면 표현과 분리합니다.
그림을 글로 읽기
  • HTTP 요청 · 주체·주문 ID
  • 취소 서비스 · 권한·상태 규칙
  • 저장소 · 조건부 변경
  • 응답 · 결과 표현
  1. HTTP 요청 · 주체·주문 ID → 취소 서비스 · 권한·상태 규칙: 명령 변환
  2. 취소 서비스 · 권한·상태 규칙 → 저장소 · 조건부 변경: 검증된 변경
  3. 저장소 · 조건부 변경 → 응답 · 결과 표현: 확정 결과

조회와 변경 사이의 경합

화면을 열 때 취소 가능하다고 확인해도 버튼을 누르기 전에 배송이 시작될 수 있습니다. 따라서 화면의 판단은 안내이고, 실제 변경 시점의 검사가 최종 책임입니다.

주문을 읽을 때 버전이 7이었다면 저장할 때도 버전 7인지 조건부로 확인할 수 있습니다. 배송 작업이 먼저 버전을 8로 바꿨다면 취소 저장은 충돌로 끝납니다. 이때 버전 숫자만 8로 바꾸어 옛 상태를 다시 쓰는 것은 해결이 아닙니다. 현재 상태를 다시 읽고 취소가 여전히 가능한지 판단해야 합니다.

상태를 조회(version=7) → 배송 변경(version=8) → 취소의 조건부 저장 실패로 적어 보면 조회 결과는 권고이고 저장 조건이 최종 보호 경계라는 점이 드러납니다. 취소 서비스는 실패를 단순한 HTTP 오류로 뭉개지 말고 ‘이미 배송됨’, ‘권한 없음’, ‘환불 불확정’처럼 후속 행동이 다른 업무 결과로 분류해야 합니다.

의존성과 테스트

서비스가 시간을 직접 읽고 외부 결제 API를 직접 만들면 테스트가 실제 시각과 네트워크에 묶입니다. 시계와 결제 동작을 좁은 계약으로 전달받으면 ‘환불 성공 뒤 응답 유실’ 같은 상황을 통제할 수 있습니다.

가짜 결제 구현이 테스트를 통과해도 실제 API의 취소·멱등·오류 계약을 검증한 것은 아닙니다. 순수 규칙은 빠른 단위 테스트로, 외부 경계는 실제 구현의 계약 테스트로 나눠 확인합니다. 모든 내부 함수에 인터페이스를 붙일 필요는 없습니다. 실패와 변화가 드나드는 경계부터 분리합니다.

작은 실습에서는 가짜 저장소가 version 7 조건을 만족할 때만 취소를 저장하도록 만들고, 두 번째 작업이 먼저 version 8을 기록하게 합니다. 예상 결과는 취소 서비스가 옛 주문을 덮어쓰지 않고 충돌을 반환하며, 환불 API의 응답을 일부러 잃은 경우에는 주문 상태를 성공으로 단정하지 않고 재조회 가능한 불확정 결과를 남기는 것입니다.

결정 주문 취소의 예
규칙 어느 상태에서 누가 취소 가능한지
원자 경계 주문 상태와 취소 기록을 함께 저장할지
외부 효과 환불 실패·응답 유실을 어떻게 복구할지
표현 성공·거절·불확정 결과를 어떻게 보여 줄지

세부 학습 경로

객체 불변식, 의존성 주입과 포트, 상태 전이와 정정, 테스트 검출력은 위 결정을 각각 더 깊게 다룹니다. 서비스 분할은 이 책임과 원자 경계를 이해한 뒤 서비스 모델에서 살펴볼 수 있습니다.

후속·관련 개념

이 개념의 확장

분야별 학습 경로

전체 기술 지식
선택 연습 · 관련 질문 2개

질문은 이해를 확인하는 보조 자료입니다. 학습 경로와 문서의 범위는 질문 수와 독립적입니다.