한 장의 만능 카드보다 강한 설계: 바꿔 끼울 수 있는 시스템의 원리
Hatched by min dulle
Aug 07, 2026
7 min read
2 views
93%
좋은 시스템은 하나를 고르는 대신 조합하는 법을 설계한다
왜 어떤 서비스는 외부 제공자가 바뀌어도 멀쩡히 작동하고, 어떤 서비스는 카드사 하나의 정책 변경이나 API 장애만으로 전체가 흔들릴까? 두 시스템의 차이는 대개 기술 수준보다 무엇을 하나의 덩어리로 묶고, 무엇을 분리해 조합할 수 있게 만들었는가에 있다.
우리는 선택을 단순하게 만들기 위해 하나의 제품, 하나의 플랫폼, 하나의 공급자를 찾는다. 그러나 실제 세계의 요구는 단일 제품이 잘 충족하지 못한다. 여행자는 항공권은 한 회사에서 사고, 숙소는 다른 서비스에서 예약하며, 환전과 보험도 별도로 고른다. 기업의 소프트웨어도 마찬가지다. 결제, 인증, 메시지 발송, 검색, 분석을 각각 가장 적합한 제공자에게 맡기는 편이 합리적일 수 있다.
문제는 여러 요소를 함께 쓰는 것 자체가 아니다. 문제는 그 요소들이 서로의 내부 사정에 지나치게 의존하는 방식으로 결합되는 것이다. 조합은 유연성을 만들지만, 무질서한 조합은 복잡성만 만든다. 따라서 진짜 설계 과제는 선택지를 늘리는 것이 아니라, 선택지를 바꿔도 전체가 무너지지 않는 구조를 만드는 데 있다.
좋은 시스템의 핵심은 최고의 부품을 고르는 능력이 아니라, 부품이 바뀌어도 목적을 유지하는 조합의 규칙을 만드는 능력이다.
카드 여러 장과 API 여러 개가 만나는 지점
카드 한 장으로 모든 상황을 해결하려는 발상은 직관적이다. 지갑은 가벼워지고 관리도 쉬워 보인다. 하지만 특정 카드가 교통에 강하고, 다른 카드가 해외 결제에 유리하며, 또 다른 카드가 생활 할인에 특화되어 있다면 한 장에 모든 기능을 담으려는 시도는 결국 타협의 목록이 된다. 모든 곳에서 평균적으로 괜찮지만, 어느 곳에서도 가장 적합하지 않은 제품이 되는 것이다.
여러 카드를 조합하는 전략은 이 타협을 인정한다. 각각의 카드를 독립된 능력으로 보고, 사용자의 목적에 따라 배열한다. 중요한 것은 카드의 개수가 아니라 역할의 분리다. 생활비를 처리하는 카드, 여행 비용을 처리하는 카드, 특정 가맹점의 혜택을 받는 카드는 서로 경쟁하는 제품이 아니라 하나의 사용 체계 안에서 서로 다른 일을 맡는다.
외부 API 통합도 정확히 같은 문제를 갖는다. 결제 업체, 문자 발송 업체, 지도 서비스, 인공지능 모델은 각각 다른 강점을 가진다. 그런데 애플리케이션의 핵심 코드가 특정 업체의 요청 형식, 오류 코드, 응답 구조를 곳곳에서 직접 다루기 시작하면 그 업체는 단순한 부품이 아니라 시스템의 일부가 된다. 가격이 오르거나 서비스가 중단되어도 바꾸기 어려운 이유는 기술적으로 교체가 불가능해서가 아니라, 교체의 비용이 시스템 전체로 퍼져 있기 때문이다.
이때 포트와 어댑터 구조가 중요한 이유가 드러난다. 애플리케이션은 먼저 자신이 필요로 하는 능력을 정의한다. 예를 들어 결제라는 포트는 다음과 같이 말할 수 있다. 주문 금액과 결제 수단을 받아 결제를 시도하고, 성공 여부와 거래 식별자를 돌려준다. 특정 결제 업체가 어떤 인증 절차를 쓰는지, 어떤 JSON 형식을 요구하는지는 애플리케이션의 핵심 관심사가 아니다.
어댑터는 이 추상적인 요구를 특정 업체의 언어로 번역한다. 한 업체는 amount를 요구하고 다른 업체는 total_price를 요구할 수 있다. 한 업체는 실패를 예외로 알리고 다른 업체는 응답 안에 오류 코드를 담을 수 있다. 이 차이는 어댑터 안에서 흡수된다. 핵심 영역은 결제라는 업무 개념만 알고, 외부 세계의 변덕은 경계 바깥에 머문다.
카드의 비유로 말하면 포트는 지갑 사용자가 정한 규칙이다. 여행 경비에는 해외 수수료가 낮은 카드를 쓰고, 일상 소비에는 적립률이 높은 카드를 쓴다는 규칙이다. 각 카드는 그 규칙에 맞게 작동하는 어댑터다. 카드의 혜택 구조가 바뀌어도 사용 규칙 자체가 유지된다면, 사용자는 카드를 교체할 수 있다.
조합의 자유는 경계에서 생긴다
많은 팀이 모듈화를 파일과 클래스의 분리로 오해한다. 폴더를 나누고 인터페이스를 추가하면 시스템이 유연해졌다고 생각한다. 하지만 진짜 경계는 코드의 위치가 아니라 변경이 어디까지 번지는가로 판단해야 한다.
예를 들어 주문 서비스가 결제를 요청할 때 외부 업체의 응답 객체를 그대로 주문 도메인에 전달한다고 하자. 처음에는 간단하다. 그러나 외부 업체가 응답 필드 이름을 바꾸면 주문 모델, 컨트롤러, 테스트, 관리자 화면이 연쇄적으로 영향을 받는다. 결제 업체의 개념이 주문 서비스 전체에 침투한 것이다.
반대로 주문 서비스가 자신의 언어로 결제 성공, 결제 거절, 추가 인증 필요 같은 결과만 다룬다면, 외부 API의 구체적인 변화는 번역 계층에 갇힌다. 이 구조에서 경계는 코드의 장식이 아니라 변화의 격리 장치가 된다.
이를 판단하기 위한 간단한 질문이 있다.
- 이 공급자를 내일 다른 공급자로 바꾼다면 수정해야 하는 파일은 몇 개인가?
- 외부 서비스의 오류 코드가 내부 비즈니스 규칙에 직접 등장하는가?
- 핵심 로직을 외부 네트워크 없이도 테스트할 수 있는가?
- 특정 공급자의 이름이 함수명, 데이터베이스 열, 도메인 모델에 남아 있는가?
첫 번째 질문의 답이 넓게 퍼져 있고, 두 번째와 네 번째 질문에 그렇다고 답한다면 시스템은 이미 조합이 아니라 종속을 택한 상태다.
여기서 중요한 것은 모든 차이를 억지로 하나의 공통 인터페이스에 끼워 넣지 않는 것이다. 카드마다 고유한 혜택이 있듯, API마다 고유한 능력이 있다. 모든 결제 업체를 완전히 똑같이 보이게 만들면 가장 약한 공통분모만 남을 수 있다. 어떤 업체가 분할 결제나 구독 결제를 지원한다면, 그 기능을 버리지 않으면서도 핵심 계약을 훼손하지 않는 별도 능력으로 표현해야 한다.
즉, 좋은 추상화는 차이를 삭제하지 않는다. 자주 바뀌는 차이는 가리고, 실제로 중요한 차이는 이름 붙여 보존한다. 이것이 과도한 추상화와 견고한 추상화를 가르는 기준이다.
한 장의 만능 제품이 실패하는 이유
하나의 카드에 모든 혜택을 넣으려는 시도와 하나의 API 공급자에 모든 책임을 맡기는 시도에는 공통된 심리적 함정이 있다. 관리의 편리함을 안정성으로 착각하는 것이다.
통합 공급자는 처음에 큰 편리함을 준다. 계약 한 번, 대시보드 하나, 청구서 하나로 여러 기능을 해결할 수 있다. 하지만 시간이 지나면 서비스의 가격 정책, 장애 이력, 데이터 보관 방식, 출시 일정이 모두 하나의 공급자에게 묶인다. 공급자는 단순한 도구가 아니라 조직의 의사결정 속도를 결정하는 외부 권력이 된다.
반대로 여러 공급자를 사용하면 운영 부담이 증가한다. 서로 다른 계약을 관리해야 하고, 장애 대응 방식도 달라지며, 데이터 형식도 통일해야 한다. 여기서 흔히 나오는 결론은 둘 중 하나를 택하라는 것이다. 그러나 더 정확한 질문은 이것이다. 어떤 부분은 공유하고, 어떤 부분은 분리할 것인가?
조합 가능한 시스템은 모든 것을 독립시키지 않는다. 인증 방식, 관측 도구, 보안 정책, 재시도 규칙처럼 여러 모듈에 공통으로 필요한 기반은 공유할 수 있다. 반면 결제 업체의 응답 형식이나 특정 카드의 혜택 계산처럼 변화 가능성이 높은 내용은 경계 안에 가둬야 한다.
이 판단을 위해 각 외부 의존성을 두 축으로 평가할 수 있다.
첫째는 변경 가능성이다. 가격, 정책, API 형식, 기능이 자주 바뀌는가? 둘째는 실패의 파급 범위다. 그 의존성이 멈추면 특정 기능만 멈추는가, 아니면 주문과 정산과 고객 지원 전체가 멈추는가?
변경 가능성과 파급 범위가 모두 높다면 가장 강한 격리가 필요하다. 결제나 인증이 여기에 해당한다. 변경 가능성은 높지만 파급 범위가 낮다면 교체 가능한 플러그인으로 설계할 수 있다. 변경 가능성은 낮지만 파급 범위가 높다면 장애를 대비한 대체 경로와 캐시가 중요하다. 두 요소가 모두 낮다면 단순한 직접 통합도 충분할 수 있다.
이렇게 보면 포트와 어댑터는 단순한 객체 지향 기법이 아니다. 그것은 조직이 외부 권력에 얼마나 끌려다닐 것인지 결정하는 경제적 설계다. 교체 비용을 낮추면 협상력이 생기고, 협상력이 생기면 공급자를 더 신중하게 선택할 수 있다.
조합은 선택이 아니라 운영 규칙이다
조합 가능한 구조를 만들었다고 해서 자동으로 유연해지는 것은 아니다. 카드 여러 장을 가진 사람이 매달 혜택을 확인하지 않으면 오히려 손해를 볼 수 있다. 마찬가지로 API를 여러 개 연결한 시스템은 각각의 상태와 비용을 관찰하지 않으면 복잡성에 잠식된다.
따라서 조합에는 세 가지 운영 규칙이 필요하다.
첫째, 교체 절차를 실제로 연습해야 한다. 대체 결제 업체를 코드에 연결해 두기만 하고 한 번도 실행하지 않았다면 그것은 보험이 아니라 희망이다. 테스트 환경에서 주기적으로 기본 공급자를 끄고 대체 경로가 작동하는지 확인해야 한다.
둘째, 계약을 문서화하고 자동 검증해야 한다. 포트가 어떤 입력과 출력을 보장하는지, 실패를 어떻게 표현하는지, 재시도가 가능한지 명확해야 한다. 이 계약을 테스트로 고정하면 외부 어댑터가 바뀌어도 핵심 업무가 기대하는 행동을 지킬 수 있다.
셋째, 선택 로직을 한곳에 모아야 한다. 결제 수단을 고르는 규칙이 컨트롤러, 모델, 배치 작업에 흩어지면 카드를 여러 장 가진 것이 아니라 규칙을 여러 벌 가진 셈이 된다. 사용 목적, 비용, 신뢰성, 장애 상태를 고려하는 결정자는 하나의 명시적인 정책으로 관리해야 한다.
여기서 한 걸음 더 나아가면 시스템은 고정된 조합이 아니라 상황에 따라 재구성되는 포트폴리오가 된다. 정상 상황에서는 비용이 낮은 공급자를 쓰고, 장애가 감지되면 안정적인 대체 공급자로 전환할 수 있다. 특정 국가에서는 현지 결제 수단을 사용하고, 다른 지역에서는 국제 결제 수단을 사용할 수 있다. 중요한 것은 이 선택이 핵심 업무 코드에 흩어져 있지 않고 정책으로 표현되는 것이다.
물론 모든 것을 동적으로 바꿀 필요는 없다. 실시간 전환은 관측과 검증이 부족할 때 더 큰 장애를 만들 수 있다. 핵심은 자동화의 화려함이 아니라 변경의 책임이 어디에 있는지 명확히 하는 것이다.
Key Takeaways
- 외부 서비스를 기능이 아니라 역할로 정의하라. 특정 업체 이름부터 코드에 넣지 말고, 애플리케이션이 실제로 필요한 능력과 결과를 먼저 적는다.
- 변경의 파급 범위를 줄여라. 외부 응답 형식과 오류 코드는 어댑터 경계에서 내부의 업무 개념으로 번역한다.
- 공통분모를 너무 좁게 만들지 말라. 모든 공급자를 똑같이 보이게 하기보다, 안정적인 핵심 계약과 공급자별 확장 능력을 함께 설계한다.
- 교체 가능성을 실행으로 검증하라. 대체 공급자를 연결할 수 있다는 문서상의 약속보다, 실제 장애 훈련과 계약 테스트가 더 중요하다.
- 조합을 포트폴리오처럼 관리하라. 비용, 품질, 지역, 장애 위험을 기준으로 각 부품의 역할을 정하고, 선택 규칙을 한곳에서 관리한다.
가장 강한 제품은 가장 많은 기능을 가진 제품이 아니다
여러 카드를 조합한다는 생각과 여러 API를 어댑터 뒤에 배치한다는 생각은 처음에는 전혀 다른 분야의 이야기처럼 보인다. 하나는 소비자의 금융 생활이고, 다른 하나는 소프트웨어 구조다. 그러나 둘은 같은 설계 원리를 드러낸다. 복잡한 목적은 하나의 만능 부품보다, 역할이 분명한 부품들의 조합으로 더 잘 달성된다.
다만 조합의 힘은 부품 수에서 나오지 않는다. 경계, 계약, 선택 규칙이 있을 때만 조합은 자유가 된다. 그렇지 않으면 여러 카드는 관리되지 않는 지갑이 되고, 여러 API는 서로의 고장과 변경을 전파하는 거대한 종속망이 된다.
결국 좋은 설계는 무엇을 하나로 통합할지보다 무엇을 분리해 둘지에서 시작한다. 오늘의 최적 공급자를 고르는 일보다 더 중요한 것은, 내일 더 나은 선택지가 나타났을 때 갈아탈 수 있는 구조를 남겨 두는 일이다.
유연한 시스템은 선택지가 많아서 강한 것이 아니다. 선택지를 바꿔도 자신의 목적을 잃지 않기 때문에 강하다.
이 관점에서 카드도 API도 단순한 상품이나 기술이 아니다. 그것들은 우리가 목적과 수단을 얼마나 잘 분리하는지 시험하는 장치다. 하나를 영원히 믿는 시스템보다, 여러 가능성을 질서 있게 조합하고 필요할 때 다시 배열할 수 있는 시스템이 더 오래 살아남는다.
Sources
Hatch New Ideas with Glasp AI 🐣
Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)
Start Hatching 🐣