추상화·인터페이스·계층·의존성
자동차를 운전할 때 엔진의 모든 폭발 과정을 계산하지 않는다. 운전자는 핸들·페달이 어떤 입력을 받고 어떤 반응을 내는지만 알면 된다. 소프트웨어도 내부 세부를 모두 공개하면 사용자는 복잡성에 눌리고, 내부를 완전히 숨기기만 하면 올바르게 사용할 수 없다. 필요한 것은 목적에 맞는 모습만 드러내고, 경계를 넘는 약속…
내부를 몰라도 쓸 수 있으려면 무엇을 알아야 하는가
자동차를 운전할 때 엔진의 모든 폭발 과정을 계산하지 않는다. 운전자는 핸들·페달이 어떤 입력을 받고 어떤 반응을 내는지만 알면 된다. 소프트웨어도 내부 세부를 모두 공개하면 사용자는 복잡성에 눌리고, 내부를 완전히 숨기기만 하면 올바르게 사용할 수 없다. 필요한 것은 목적에 맞는 모습만 드러내고, 경계를 넘는 약속을 정확히 밝히는 것이다.
이 절의 네 개념은 한 줄로 연결된다. 추상화가 중요한 성질만 남기고, 인터페이스가 경계의 약속을 정하며, 계층이 이 약속을 따라 책임을 쌓는다. 의존성은 한 요소가 다른 요소의 약속에 기대는 방향이다.
먼저 되짚기: 숨김은 경계가 있어야 가능하다
앞 절의 시스템 경계는 안과 밖의 책임을 가르는 분석 결정이었다. 경계 안의 구현을 숨기려면 밖에서 무엇을 볼 수 있는지 정해야 하고, 경계를 넘는 입력·출력·오류 약속을 남겨야 한다. 추상화와 인터페이스는 바로 이 경계를 사용할 수 있는 형태로 만든다.
추상화
추상화(abstraction)는 목적에 필요하지 않은 세부를 숨기고 중요한 성질만 드러내는 방법이다. 지도는 실제 도시의 모든 돌과 나무를 복제하지 않고 길 찾기에 필요한 도로·방향·거리만 남긴다. 좋은 추상화는 세부가 적은 것이 아니라 특정 판단에 필요한 세부가 충분한 것이다.
인터페이스
인터페이스(interface)는 두 요소가 교환할 입력·출력·규칙·오류의 약속이다. 함수 이름이나 화면 버튼만이 아니다. 허용 입력, 결과의 의미, 호출 순서, 시간 제한, 실패 표현, 권한과 버전까지 상대가 올바르게 사용하기 위해 알아야 할 계약이 포함된다.
계층
계층은 인접 책임과 인터페이스를 따라 복잡성을 나눈 구조다. 위 계층은 아래 계층이 제공하는 능력을 사용하고, 아래 계층은 내부 구현을 바꿔도 약속한 결과를 유지한다. 계층은 단순한 위아래 그림이 아니라 허용된 사용 방향과 책임 경계를 가진다.
의존성
의존성은 한 요소의 동작이나 변경이 다른 요소의 계약에 기대는 관계다. 주문 서비스 → 결제 인터페이스라면 주문 서비스가 결제 인터페이스에 의존한다. 화살표 방향은 데이터가 오가는 양방향과 다를 수 있다. 의존 방향은 “누구의 약속이 바뀌면 내가 영향을 받는가”로 판단한다.
결제 기능을 바꿔 끼우는 작은 예
상자 안의 대상과 화살표의 조건을 함께 읽으세요. 좁은 화면에서는 같은 관계를 세로로 재배치한 도식을 보여 줍니다.
주문 계층은 pay(금액, 주문 식별값)의 입력·출력·오류 계약에 의존한다. 카드 결제 모듈과 계좌 결제 모듈은 서로 다른 내부 절차로 같은 계약을 구현한다. 구현에서 계약으로 향하는 선은 구현 관계이며 실제 데이터 요청 방향이 아니다. 금액 단위나 오류·재시도 의미를 바꾸면 같은 이름이어도 계약이 달라진다.
주문 계층은 pay가 성공 결과나 명시된 오류를 돌려준다는 추상적인 계약만 안다. 카드사 통신과 계좌 확인은 구현 안에 숨는다. 이 때문에 구현을 바꿔도 계약이 같으면 주문 코드를 고칠 필요가 줄어든다. 반대로 오류 코드의 의미나 금액 단위가 바뀌면 인터페이스 계약이 바뀐 것이므로 의존하는 주문 계층도 검토해야 한다.
pay(금액, 주문 식별값)는 실행할 실제 프로그램 문장이 아니라 “pay라는 결제 동작에 괄호 안 두 값을 전달한다”는 읽기용 표기다. 금액을 원 단위 정수로 보내고 같은 금액과 같은 주문 식별값을 다시 보내면 새 승인 없이 기존 결과를 돌려준다는 교육용 계약을 두자. 같은 식별값으로 다른 금액을 보내면 입력 불일치로 거부하고 기존 승인 기록을 바꾸지 않는다고 정한다. 주문 계층이 pay(10000, 주문42)를 요청하면 결제 모듈은 주문42가 이미 승인되었는지 확인하고, 없으면 10,000원 승인을 진행해 승인번호를 남긴 뒤 성공을 반환한다. 응답을 받지 못해 같은 값을 다시 보낸 경우에도 저장한 승인번호를 반환한다. 구현을 카드에서 계좌로 바꿔도 이 입력·출력·중복 처리 약속을 유지하는지가 교체 판단의 기준이다.
금액 단위를 원에서 100분의 1원으로 바꾸고 함수 이름만 같게 두면 어떨까? 주문 계층이 보낸 10000의 의미가 달라져 계약이 깨진다. “이름이 같으면 교체 가능”은 이 오답의 원인이다. 위의 인터페이스로 돌아가 단위와 실패·재시도 규칙까지 확인하면 고칠 수 있다. 반대로 내부 카드 통신 순서만 바뀌고 계약 결과는 그대로라면 주문 계층이 그 순서를 새로 배울 필요는 없다.
위의 승인 확인·저장 순서는 한 요청씩 진행하는 읽기용 추적이다. 실제로 두 요청이 동시에 오거나 카드에서 계좌 구현으로 교체되면, 같은 주문의 처리 담당을 하나만 확보하고 승인 기록을 함께 확인할 수 있어야 이 중복 처리 약속이 유지된다. 응답이 없다고 다른 구현에서 새로 승인하지 않고, 기존 요청의 결과를 먼저 확인한다. 함수 이름과 자료형이 같다는 사실만으로 이 행동 계약이 구현되는 것은 아니다. 구체적인 동시 처리와 효과·기록 경계는 동시성과 API의 중복 요청 사례로 이어진다.
이 예에서는 추상화 → 인터페이스 → 계층 → 의존성 순서로 설계를 읽을 수 있다. 무엇을 숨길지 정하고, 숨긴 대신 약속할 것을 적고, 약속을 기준으로 책임을 나눈 뒤, 변경 영향이 흐르는 방향을 관리한다. 네 개념 자체가 시간 순서대로만 생기는 것은 아니다. 오류 계약을 정하다가 숨길 수 없는 내부 제약을 발견하면 추상화와 책임 경계를 다시 조정한다.
비슷해 보이는 말을 구분한다
| 개념 | 남기는 것 | 주된 판단 | 지나치면 생기는 문제 |
|---|---|---|---|
| 추상화 | 목적에 중요한 성질 | 무엇을 보이고 숨길까 | 필요한 차이까지 지움 |
| 인터페이스 | 경계 통과 계약 | 무엇을 어떤 조건으로 주고받나 | 계약이 비대해짐 |
| 계층 | 책임과 사용 방향 | 어느 수준이 무엇을 맡나 | 단순 전달층만 늘어남 |
| 의존성 | 변경 영향 방향 | 누구의 약속에 기대나 | 순환 의존·변경 확산 |
추상화 수준은 사용자에 맞춰야 한다. 업무 담당자에게는 주문 취소가 적절하지만 통신 장애를 분석하는 개발자에게는 요청 식별자와 시간초과가 필요하다. 인터페이스에서 안정적으로 유지할 핵심을 작게 두되 오류와 경계조건을 숨기지 않는다. 계층은 독립적인 책임이 있을 때 만들고, 단순히 함수를 전달하기만 한다면 제거를 검토한다.
실패조건과 교정 질문
새는 추상화는 사용자가 숨긴 내부 순서나 특정 제품 이름을 알아야만 시스템을 정상적으로 사용할 수 있는 경우다. “내부가 바뀌어도 이 설명은 유지되는가?”를 묻는다.
불완전한 인터페이스에는 성공 입력만 있고 시간초과·부분성공·재시도 규칙은 빠져 있다. 정상·경계·오류 입력을 각각 계약에 넣는다.
계층 건너뛰기는 상위 계층이 하위 계층의 내부 저장소를 직접 바꾸는 경우다. 당장은 빠르지만 내부 변경이 전체로 번진다. 예외 경로가 정말 필요한지, 공식 인터페이스로 올릴 수 있는지 판단한다.
순환 의존은 A가 B 없이는 바뀌지 못하고 B도 A 없이는 바뀌지 못하는 구조다. 더 안정적인 추상 계약을 별도로 두거나 함께 변하는 책임을 한 경계로 합친다.
직접 판단하기: 내부 변경과 계약 변경을 가른다
앞 예의 원 단위 정수·주문 식별값·중복 처리 계약을 그대로 사용한다. 다음 세 제안을 각각 받아들일 수 있는지, 주문 담당과 결제 담당이 무엇을 확인해야 하는지 먼저 적어 보자.
- 카드사 통신 라이브러리를 바꾸되 같은 입력의 성공·오류·중복 처리 결과를 유지한다.
- 같은 이름의
pay가 10000을 10,000원 대신 100원으로 해석하도록 바꾼다. - 응답이 늦으면 주문 담당이 결제 모듈의 승인 저장소를 직접 고치고 다른 결제수단으로 다시 승인한다.
변경 영향과 검증 책임 비교하기
1번은 내부 구현 변경 후보이므로 결제 담당이 정상·오류·동일 요청 재시도 결과를 대조한다. 주문 담당은 공개 계약을 통한 통합 결과를 확인하며 새 라이브러리의 내부 호출 순서를 알 필요는 없다. 단순히 이름이 같다는 사실만으로 대체 가능하다고 판정하지 않는다.
2번은 금액의 의미가 달라지는 계약 변경이다. 입력 10000의 단위가 기존과 다르므로 주문 담당과 결제 담당이 함께 호환 방식과 변경 영향을 정해야 한다.
3번은 책임 경계를 우회한다. 시간초과는 승인되지 않았다는 증거가 아니므로 원래 요청 결과를 확인하고 정한 재시도 계약을 따라야 한다. 내부 기록을 직접 고치거나 다른 수단으로 새 승인을 내면 중복 승인 방지 약속을 깨뜨릴 수 있다.
문제: 추상화·인터페이스·계층·의존성의 관계와 설계 판단기준을 설명하시오.
변경을 경계 안에 가두는 모범답안 보기
모범답안 축약본: 변경을 경계 안에 가두는 네 개념
추상화는 목적에 필요한 성질만 드러내 복잡성을 줄이고, 인터페이스는 그 추상 경계를 사용하는 입력·출력·오류의 계약이다. 계층은 인접 책임과 허용된 사용 방향을 정하며, 의존성은 한 요소가 다른 요소의 계약 변경에 영향을 받는 관계다. 주문이 pay(금액, 주문 식별자) 계약만 사용하면 카드·계좌 구현의 내부 변경을 숨길 수 있다. 판단할 때는 추상 수준이 사용자의 질문에 맞는지, 계약이 정상·경계·오류와 시간초과를 포함하는지, 계층마다 독립 책임이 있는지, 의존 화살표가 안정적인 계약을 향하는지 본다. 내부 세부사항이 새어 나오거나 상위층이 하위 저장소를 직접 고치고 순환 의존이 생기면 변경이 전파된다. 함께 변하는 책임을 합치거나 더 안정적인 계약을 분리해 이를 교정한다.
출처와 예의 범위
공개 인터페이스가 구현을 몰라도 협력할 계약을 제공한다는 구체 언어 예는 Oracle Java Tutorial의 Interfaces(JDK 8용 자료)에서 확인할 수 있다. 이 절의 계층과 결제 예는 설계 설명을 위한 교육용 모델이다. Java의 interface 선언이 금액 단위·재시도·외부 승인 효과까지 자동으로 보장한다는 뜻은 아니다. 그런 행동 약속은 구현과 계약시험으로 따로 확인한다.
30초 정리
- 추상화는 목적에 필요한 성질만 드러내고, 인터페이스는 숨긴 경계를 사용할 계약을 적는다.
- 계층은 책임과 인터페이스를 따라 복잡성을 나누며, 의존성은 상대 계약 변경의 영향 방향이다.
- 좋은 분리는 내부 변경을 가두지만, 새는 추상화·불완전한 오류계약·건너뛰기·순환 의존은 변경을 다시 퍼뜨린다.