요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

지난 글 <소프트웨어 설계 20년 해보고 깨달은 ‘좋은 설계’의 조건>의 독자 중 한 분이 저에게 기술적인 노하우를 더 공유할 수 있는지 물었습니다.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

느슨한 결합(loosely coupled) 원칙을 활용한 소프트웨어 설계

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

 

지난 글 <소프트웨어 설계 20년 해보고 깨달은 ‘좋은 설계’의 조건>의 독자 중 한 분이 저에게 기술적인 노하우를 더 공유할 수 있는지 물었습니다.

 

조금 주저되었습니다. 개발자 입장에서 설계를 다룰 때, 당연히 프로그램을 잘 만드는 것에 집중하기 마련입니다. 이는 당연한 요구입니다. 그러나 ‘모든 상황에서 통하는 기술적인 노하우가 과연 있을까’ 하는 생각을 지울 수 없었습니다. 개발자들의 세계에는 하나의 방법으로 모든 것을 해결하려는 어리석음을 묘사하는 ‘은탄환(silver bullet)’이라는 비유가 있는데, 제가 어떤 노하우를 공유하면 그것이 자칫 은탄환으로 쓰이게 될지도 모른다는 걱정도 드는 것이죠. 물론 이는 기우일 수 있습니다.

 

제가 가장 좋아하는 소프트웨어 설계 책 중에 에릭 에반스(Eric Evans)의 <도메인 드리븐 디자인(Domain-Driven Design)>이라는 책이 있습니다. 예전에 모 출판사 편집장이 ‘어떤 책이 번역되면 좋겠느냐’ 묻기에, 이 책을 꼭 해야 한다고 권유 했던 일도 있었을 정도입니다. 이 책을 가장 좋아하게 된 이유는 이 책에 담긴, 함께 일하는 이들이 고민하는 영역(Domain)에 기반해 설계를 풀어가는 사상과 태도 때문입니다. 특히, 이해관계자들이 모두 하나의 언어를 쓰는 비전을 표현한 ‘유비쿼터스 랭귀지(Ubiquitous Language)’는 심미감마저 느껴졌습니다. 기술이 아니라 공동체의 문제에 초점을 맞춘다는 점에서 이 책의 관점이 설계를 제대로 묘사했다고 평가합니다.

 

저는 도메인 드리븐(Domain-Driven)이라는 말을 ‘우리 상황에 맞춰서’라는 의미로 이해합니다. 우리가 다루는 비즈니스의 가치와 양상, 우리가 쓰는 기술, 우리의 여력, 개발 속도, 소통 방식 등을 모두 함축한 말이라 여기기 때문입니다.

 

이러한 가치관을 갖고 있는 제 입장에서 선뜻 자신있게 기술적인 노하우를 꺼내는 일은 어색한 일이 아닐 수 없었습니다. 그래서 스스로에게 질문을 던져 보았습니다. ‘그럼에도 불구하고 하나만 언급해야 한다면 어떤 게 있을까?’라고요.

 

 

느슨한 결합(loosely-coupled): 가장 중요하게 생각하는 하나의 원칙

개인적인 노하우로 질문을 좁히고, ‘하나만’ 이라고 다시 좁혀보니 도리어 쉽게 답할 수 있었습니다. 바로 ‘느슨한 결합(loosely-coupled)’이었습니다. 3년 정도 써 오던 브런치에서 ‘loosely-coupled’를 키워드로 검색해 보니 제가 쓴 글이 23개 있었습니다. 그만큼 제가 이 원칙을 빈번하게 활용하고 있다는 의미입니다. 그리고 글을 따라가 보면 다양한 방면으로 적용하고 있다는 사실도 확인할 수 있었습니다.

 

이번 글에서는 제가 좋아하는 ‘느슨한 결합이라는 개념에 대해 설명하고 어떻게 활용할 수 있는지 예를 드는 것을 목표로 하겠습니다.

 

먼저, 개념을 설명하기 위해 영문 위키피디아를 찾아 보았습니다. ‘Loose coupling’이라는 페이지가 있었고 아래와 같이 설명하고 있었습니다.

 

In computing and systems design, a loosely coupled system is one

  1. in which components are weakly associated (have breakable relationships) with each other, and thus changes in one component least affect existence or performance of another component.
  2. in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services. Loose coupling is the opposite of tight coupling.

 

첫 번째 정의는 “시스템의 구성요소(component)가 서로 약하게 연관돼 관계를 떼어낼 수 있고, 그때문에 한 구성요소에 변화가 생겼을 때 다른 구성요소의 성능이나 존재에 최소한의 영향을 끼치는 상태”라고 요약할 수 있을 듯합니다. 두 번째 정의는 조금 다른데요. “구성요소가 다른 구성요소의 정의에 대해 많은 지식이 없이도 사용할 수 있는 상황”을 칭한다고 말합니다. 전자가 구성요소 간의 결합의 양상을 말했다면, 후자는 그에 따른 효과와 결합의 범주에 대해 말합니다. 클래스, 인터페이스, 데이터, 서비스 등이 구성요소가 될 수 있다는 뜻이죠.

 

이제 이 느슨한 결합을 어떻게 활용할 수 있는지는 크게 아래 세 가지 기준으로 나눠 설명하겠습니다.

 

  1. 프로그래머들에게 익숙한 클래스 단위, 다시 말해서 내가 짠 프로그램 수준에서 활용하기
  2. 네트워크와 하드웨어의 발달로 나눠서 짤 수 있는 상황에 어울리는 분산 프로그래밍 형태에서 활용하기
  3. 복잡한 일을 조직화할 때 일사불란함을 만들기 위한 도구인 ‘단위’ 그리고 자유로운 여지를 두기 위한 장치인 ‘경계’를 구축하는 데 활용하기

 

 

1. 나의 프로그래밍에서의 느슨한 결합 

클래스 사이의 결합과 결합의 시점 문제

UML에서 클래스 사이의 관계를 묘사하기 위한 용도로 쓰이는 클래스도(class diagram)에 관계를 표현하는 방법 중에서 의존 관계(dependency)라는 것이 있습니다. 약한 결합 정도를 나타내기 위해 실선 대신 점선을 쓰죠.

 

 

그런데 ‘약하다’는 것이 실제로 어떻게 구동하는 것일까요? 설계할 시점에서는 구성요소를 결합시키지 않거나 최소한의 결합 상태만 만들어 두었다가, 실행할 시점에 결합을 완성하는 식으로 느슨한 결합을 구현하는 경우가 있습니다.

 

자바 프로그래머 대다수가 사용하는 스프링 프레임워크의 구동은 아예 이를 기반으로 합니다. 이것을 의존성 주입(Dependency Injection)이라고 부릅니다. 스프링을 사용하면 의존 관계를 선언할 때는 타입 수준에서 프로그램 정합성을 확인합니다. 컴파일할 때는 호출하는 클래스 사이에서 타입과 호출하는 함수(메소드)의 존재 여부 정도를 컴파일러가 확인한다고 간단히 요약할 수 있습니다.

 

반면에 실제 구동 시점에는 정의한 의존 관계에 따라 실제 클래스가 구동할 때, 구체적인 결합이 만들어집니다. 이렇게 하면 코드의 연관성을 타입 확인만 하는 설계 시점과 메모리에 올라가는 구동 시점이라는 두 시점에 나누어서 구성하는 유연성을 갖게 됩니다. 이에 대한 구체적인 이야기는 과거에 썼던 <DI 적용도 생각하는 힘과 함께 하자>라는 글로 대체합니다.

 

여기서 강조하고 싶은 내용은 이렇습니다. 우리가 설계할 때 오랜 시간 변치 않을 내용 혹은 시스템 전체에 영향을 끼치는 부분과 미래를 위해 선택지를 열어두고 싶은 부분을 판단할 수 있어야, 이런 유연성을 제대로 활용할 수 있습니다. 비유적으로 설명해보겠습니다. 하나로 쓰던 상자를 둘로 나누려면, 둘로 나눴을 때 각각 용도가 따로 있어야 가치가 있겠죠. 같은 용도로 쓰는데 나눈다면 번거로움만 더할 뿐입니다. 결과적으로 느슨한 결합을 제대로 구현하려면, 타입 정보를 담는 부분과 구체적인 실행 로직을 담는 부분을 나누는 일이 프로그래머 자신의 사고와 작업 방식에 도움을 줄 수 있어야 합니다.

 

 

2. 분산 프로그래밍 형태에서의 느슨한 결합 

인터페이스 결합이 중요하지 않은 경우

위키피디아의 ‘느슨한 결합’ 두 번째 정의에서 인터페이스의 결합이 하위 영역에 포함된다고 되어 있는데요, 사실 인터페이스의 결합은 전제에 따라 너무나 다른 내용으로 느껴질 수 있습니다. 인터페이스에 대한 정의를 두 개로 두고 결합에 대해 설명해보겠습니다. 자바를 처음 공부할 때를 떠올려 보았습니다. 그때는 자바 언어를 만드는 사람들이 제공하는 공용 프로그래밍 요소에 대한 사용법을 API 문서라고 불렀습니다. 지금도 구글링을 해보니 ‘생활코딩’에 비슷한 내용이 보입니다.

 

출처 : 생활코딩

 

자바 언어 사용에 꼭 필요한 클래스들을 이용하기 위해서 프로그래머에게 제공하는 인터페이스라는 뜻이 바로 '애플리케이션 프로그래밍 인터페이스(Application Programming Interface, API)’입니다. 여러분이 자바로 프로그램을 작성한다면 해당 API를 구현한 파일에 의존하게 됩니다. 대부분의 경우 해당 파일이 있어야 돌아가고, 강한 결합으로 묶이는 경우가 많습니다. 하지만 이 경우는 결합의 정도는 크게 중요하지 않습니다.

 

결합의 정도가 중요한 이유는 프로그램이 변해야 하는 경우에 발생합니다. 따라서, 언어를 구성하는 구성요소는 상대적으로 느리게 바뀌기 때문에 결합의 정도가 그리 중요하지 않습니다.

 

결합의 중요성이 크게 발생하는 API의 전형은 API를 쓰는 프로그램과 API를 제공하는 프로그램이 서로 다른 프로그래머 혹은 다른 회사에 의해 빈번하게 수정될 수 있는 상황입니다. 앞서 말한 대로 프로그래밍 언어가 제공하는 API는 수정 요구를 받아들이는 기간이 길고, 바뀌지 않아도 당장 큰 문제가 없는 경우가 많습니다. 자유 경쟁하는 시장 상황에서 정말 불편하면 프로그래머가 언어를 바꾸면 됩니다.

 

제가 아는 개발자들 중에서 몇몇은 자바로 만들어진 스프링 프레임워크는 계속 쓰고 싶지만, 자바 언어의 표현력에 실망하며 스프링 프레임워크를 코틀린이라는 언어와 함께 쓰는 이른바 ‘코프링’ 조합을 쓰기도 합니다. 대체재가 있다면 결합이 큰 문제가 되지 않을 수 있다는 말이죠.

 

이와 달리 매일 바뀌는 기능을 써야 우리 회사의 시스템 혹은 서비스를 만들 수 있는 상황이라면 어떨까요?

 

느슨한 결합의 전형적인 사례 : 오픈 API 혹은 REST API를 통합 결합

인터넷 서비스를 제공하는 대부분의 기업들은 인터넷이 제공하는 네트워크라는 인프라 위에서 구동하는 API를 활용하여 프로그램을 결합합니다. 방대한 구성을 가능하게 하는 웹의 기본 원리에 충실한 형태를 띤 분산 API를 REST API 라고 하여 널리 쓰입니다. 하지만 정확하게 말하면 REST 형태가 아닌 원격 API 호출 방식도 많이 쓰이고, 공개나 개방 정도를 강조하는 표현인 오픈 API 라는 표현도 자주 쓰이는데, 이들 모두는 한 가지 공통점을 갖고 있습니다.

 

바로 서로 다른 서버에서 구동하고 네트워크 통신 규약에 따라 결합한다는 사실입니다. 앞서 언급한 느슨한 결합을 구현하기 좋은 방식입니다. 구체적으로 프로그램을 어떻게 만드느냐에 대해서는 각자 알아서 할 수 있습니다. 서로 다른 회사나 개발팀이 함께 일 하는 데 자유를 제공하는 좋은 방법이고 ‘느슨하다’는 형용사가 잘 어울립니다. 또한, 서로 역할을 분배하는 시점(사업적 제휴 등으로 협력)에서는 주고 받는 데이터의 형식이나 순서, 데이터량에 대해서만 약속하면 됩니다. 앞서 사용한 기술적 표현으로 나타내면 설계 시점의 결합이 강하지 않다고 표현할 수 있습니다.

 

왜냐하면 서로 다른 프로그래밍 언어를 써도 무방하기 때문에 문화적 결합이 필요하지 않습니다. 강력한 팀으로 일해야 할 이유가 없는 것이죠. 그리고, 데이터베이스 결합이 없어서 데이터 이관과 같은 노력이 많이 들어가지도 않습니다. 

API를 매개로 설명한 내용이 바로 분산 프로그래밍 형태에서 ‘느슨한 결합(loosely-coupled)’의 전형적인 쓰임새입니다.

 

 

3. 단위와 경계를 만드는 느슨한 결합

마지막으로 단위와 경계를 만들 때 ‘느슨한 결합’이 어떤 효과를 발휘하는지 설명하겠습니다. 복잡한 일을 하기 위해서는 공통된 단위가 필요합니다. 단위에 대한 배경 지식은 제가 이전에 쓴 ‘1 이라는 수와 경계 그리고 단위의 문제’란 글을 참조하실 수 있습니다. 여기서는 프로그래밍에 한정하여 단위를 말해보겠습니다.

 

몇 년 전에 객체지향 프로그래밍과 함수형 프로그래밍에 대한 논란이 유행한 일이 있습니다. 여기서 간단히 내용을 설명할 수 없을 정도로 복잡한 쟁점을 갖고 있습니다만, 복잡한 프로그래밍을 다룰 때의 핵심 단위에 대한 논쟁이라고 말할 수 있습니다. 객체는 상태 관리의 단위가 되는 이점을 갖고 있는데, 반면에 해당 객체를 쓰는 프로그램(클라이언트)의 관점에서는 상태에 따라 다른 결과가 반환되는 것을 원치 않는 쓰임새가 늘어난 듯합니다. 그래서 안정적으로 반복 처리하고 싶은 업무를 맡기는 새로운 단위(혹은 방식)가 필요했다고도 볼 수 있습니다.

 

이런 단위는 왜 만들까요? 프로그래밍에서는 메모리에 구동하는 단위를 일원화해서 복잡한 연산을 구성하기 위해 명령어 덩어리가 필요합니다. 그것을 해당 도메인에서 사람들이 다루기에 적합한 형태로 만들어야 쉽게 찾아 고칠 수 있겠죠. 이것이 생산성에 절대적인 영향을 끼칩니다.

 

그런 것들이 물리적으로는 ‘파일’이지만, 도메인에 어울리는 연산 방식을 담기 위해 프로시져, 객체, 함수와 같은 식으로 만들어져 쓰이고 있습니다. 그런데 단위를 통일한다고 해서 우리가 다루고 싶은 복잡한 인간의 문제가 단순해지는 것은 아닙니다. 그래서 그런 단위들이 복잡한 의존 관계로 얽히게 됩니다.

 

언젠가는 협업을 위한 덩어리가 필요합니다. 앞서 말한 단위와는 다른 무언가가 필요한데, 그것이 저는 경계(boundary)라고 생각합니다. 경계란 표현을 적극적으로 쓰게 된 계기는 ‘경계 설정은 소프트웨어 설계의 핵심 활동’이란 글에서 다뤘습니다. 이 글에서는 느슨한 결합과 경계의 관련성에 초점을 맞춥니다.

 

경계를 만들면 경계 밖의 문제에 대해서 자유로워질 수 있습니다. 하지만, 복잡한 문제를 풀려면 결국은 연결해야 합니다. 그래서, 네트워크로 분산된 시스템으로 경계를 이룬 후에 경계면에 해당하는 API를 노출하여 필요한 만큼만 결합하는 방식이 바로 경계를 활용하는 예시가 되기도 합니다. 이렇게 하면 연관성에 따라 문제를 표현하는 코드를 나누어 다룰 수 있어 효과적인 협업이 가능합니다.

 

지금까지 세 가지 관점으로 느슨한 결합의 이점을 설명했습니다. 처음 시도하는 설명이라 스스로도 조금 낯설지만 적어도 설계 과정에서 매우 중요하다는 느낌 정도는 전달할 수 있을 것이라는 기대를 합니다. 여러분도 활용해보시기를 권합니다. 저는 소프트웨어 업계에 있는 내내 셀 수도 없이 잘 활용해 왔으니까요.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
베터코드 CEO
46
명 알림 받는 중

작가 홈

베터코드 CEO
46
명 알림 받는 중
프로그래머로 사회 생활을 시작해서 IT 컨설팅과 IT 서비스 회사 경영자로 동종 업계에서 스무 해 이상 일 하고 있습니다. 한 때는 한국 스프링 사용자 모임을 만들어 운영했던 만큼 커뮤니티 활동과 지식 공유를 즐기고 있습니다.

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기