원문: Software Architecture - The Difference Between Architecture and Design
많은 사람들이 소프트웨어 아키텍처와 디자인의 차이를 잘 알지 못합니다. 개발자들에게 또한 그 차이가 종종 분명하지 않기 때문에, 소프트웨어 아키텍처 패턴과 디자인 패턴을 혼동할 수 있습니다. 개발자로서 저는 이 개념들을 간단히 정리하고 소프트웨어 디자인과 소프트웨어 아키텍처의 차이를 설명해보려고 합니다. 또한 개발자가 소프트웨어 아키텍처에 대한 지식 조금과 소프트웨어 디자인에 대한 많은 지식을 아는 것이 왜 중요한지도 설명하겠습니다. 시작해보죠.
The Definition of Software Architecture
간단하게는, 소프트웨어 아키텍처란 유연성, 확장성, 실행가능성, 재사용성, 보안성과 같은 소프트웨어의 특성들을 기술적, 사업적 기대에 부응하는 구조화된 해법으로 바꾸는 과정입니다. 이 정의를 보면 소프트웨어 아키텍처 디자인에 영향을 줄 수 있는 소프트웨어의 특성들에는 무엇이 있는지 궁금해질 것입니다. 사업적, 기능적, 기술적 요구사항들을 대표하는 이러한 특성들은 굉장히 많습니다.
The Characteristics of Software Architecture
앞서 설명한대로, 소프트웨어의 특성들은 소프트웨어의 요구_기대 사항을 기능적_기술적인 수준에서 설명하는 것입니다. 따라서, 제품의 소유자가 급변하는 시장에서 경쟁하고 있을 때, 비지니스 모델을 신속하게 적용해야 합니다. 만약 일정 시간 내에 성공적으로 완수되어야 하는 급한 요청을 처리해야 하는 사업의 경우, 그 소프트웨어는 “확장성 있고, 모듈화되어 있으며, 유지보수성이 높아야” 합니다. 소프트웨어 아키텍트로서 당신은 성능, 낮은 장애 허용, 확장성, 신뢰성이 주요 특성이라는 것을 알고 있어야 합니다. 이제, 이러한 특성들을 정의한 후, 소유자가 이 프로젝트에 대한 예산이 제한적이라고 얘기해줍니다. 또 다른 특성이 여기서 나타나는데 이는 바로 “실행가능성”입니다.
앞서 나열한 것들이 여기서 “품질 특성(quality attributes)” 으로도 알려진 하는 소프트웨어의 특성들입니다.
Software Architecture Patterns
대부분의 사람들이 “마이크로서비스(MicroServices)”라는 용어를 들어봤을 겁니다. 마이크로서비스는 레이어드 패턴, 이벤트 드리븐 패턴, 서버리스 패턴 등과 같은 소프트웨어 아키텍처 패턴 중 하나입니다. 이들 중 몇몇은 후술하도록 하겠습니다. 마이크로서비스는 아마존과 넷플리스에 적용되어, 큰 파장을 일으키며 그 명성을 얻었습니다. 이제 아키텍처 패턴들에 대해 자세히 다루어 보도록 하겠습니다.
팩토리 패턴이나 어댑터 패턴과 같은 디자인 패턴과 아키텍처 패턴을 혼동하지 마세요. 디자인 패턴에 대해서는 이후에 다루겠습니다.
Serverless Architecture
이는 서버와 백엔드 관리의 복잡성을 서드파티 서비스를 사용하여 해소하는 어플리케이션 서비스를 이르는 말입니다. 서버리스 아키텍처는 크게 두가지 카테고리로 나누어집니다. 첫번째는 “Backend as a service (BaaS)”이고 두번째는 “Function as a Service (FaaS)”입니다. 서버리스 아키텍처는 배포와 서버의 일반적인 작업에서 발생하는 버그를 처리하고 고치는 시간을 많이 아낄 수 있게 해줍니다. 가장 유명한 서버리스 API를 제공하는 기관은 Amazon AWS의 “Lambda”입니다.
더 자세한 것들은 여기서 확인해보세요
Event-Driven Architecture
이 아키텍처는 이벤트 생산자(event producer)와 이벤트 소비자(event consumer)에 의존하는 아키텍처입니다. 시스템을 분리(decouple)하여 관심을 가지고 있는 이벤트가 다른 파트로부터 발생하였을 때, 각각의 파트가 작동됩니다. 복잡한가요? 간단하게 해봅시다. 온라인 상점 시스템을 디자인한다고 생각해보면, 시스템은 2개의 파트로 나뉘어집니다. 구매 모듈과 벤더 모듈이죠. 만약 고객이 구매한다면, “주문처리(orderPending)”이라는 이밴트가 구매 모듈로부터 발생됩니다. 벤더 모듈은 “주문처리”라는 이벤트에 관심을 가지고 있기때문에, 이 이벤트가 발생하는지 귀기울이고 있을 것입니다. 벤더 모듈이 이 이벤트를 받게 되면, 몇가지 작업을 수행하거나 또는, 특정 벤더로부터 상품을 더 주문하기 위해서 또 다른 이벤트를 발생시킬 수도 있습니다.
이벤트 생산자의 입장에서는 어떤 이벤트 소비자가 어떤 이벤트에 관심을 가지고 있는지 모른다는 것을 기억하기 바랍니다. 또한, 소비자들도 어떤 소비자가 어떤 이벤트를 귀기울이고 있는지 모릅니다. 그러므로, 이 아키텍처의 주요 아이디어는 시스템의 파트를 분리시키는 것입니다.
Microservice Architecture
마이크로서비스 아키텍처는 최근 몇년사이 가장 인기있는 아키텍처가 되었습니다. 이 아키텍처는 작고 독립적인 모듈 서비스 개발하는데 초점을 두고 있습니다. 각각의 서비스는 특정 문제를 해결하거나 특유의 작업을 수행하고 이러한 모듈들이 사업목적을 달성하기 위해서 잘 정의된 API를 통해 소통하게 됩니다. 다음 이미지를 보시면 더 이상 설명할 필요가 없을 것 같습니다.
Software Design
소프트웨어 아키텍처가 한 소프트웨어의 뼈대나 고수준의 기반을 담당하는데에 반해, 소프트웨어 디자인은 각각의 모듈들이 어떤 것을 하는지, 클래스의 범위, 함수의 목적 등 코드 수준의 디자인을 담당하는 것입니다.
만약 당신이 개발자라면, SOLID 법칙이 무엇인지, 디자인 패턴이 어떻게 일반적인 문제들을 해결하는지 아는 것이 중요합니다.
SOLID란, 단일책임(Single Responsibility), 개방폐쇄(Open Closed), 리스코프 치환(Liskov substitution), 인터페이스 분리(Interface Segregation), 의존관계 역전(Dependency Inversion) 원칙을 의미하는 것입니다.
- 단일책인 원칙은 각각의 클래스가 단 하나의 목적, 즉 하나의 책임 그리고 하나의 변경 사유를 가져야 한다는 것을 의미합니다.
- 개방폐쇄 원칙: 클래스는 확장에는 열려있어야 하지만, 수정에는 닫혀있어야 한다는 것입니다. 간단하게 말하면, 클래스에 새로운 기능을 더하는 것은 가능해야 하지만, 이미 존재하는 기능을 사용하는 코드를 고장내는 방식으로 고쳐서는 안되다는 것을 의미합니다.
- 리스코프 치환 원칙: 이 원칙은 개발자가 상속을 사용할 때, 어플리케이션 로직을 어느 지점에서도 망가뜨리지 않도록 사용해야 한다는 지침입니다. 따라서, 만약 자식 클래스 “XyClass”가 부모클래스 “AbClass”를 상속하였다면, 자식 클래스는 부모 클래스의 행동을 바꾸는 방향으로 부모 클래스의 기능을 복제(replicate)해서는 안됩니다.
- 인터페이스 분리 원칙: 간단하게는, 클래스는 여러개의 인터페이스를 구현할 수 있기 때문에, 한 클래스의 목적에 맞지 않는, 중요하지 않은 함수를 구현하도록 강요하지 않는 방식으로 코드를 구성해야합니다. 즉, 인터페이스들을 분리하세요.
- 의존관계 역전 원칙: 만약 당신이 TDD 방식에 따라 어플리케이션을 개발해봤다면, 테스트 가능성(testability)와 모듈성(modularity)를 위해서 코드를 분리(decouple)시키는 것이 얼마나 중요한지 알고 있을 것입니다. 다시 말해서, 특정 클래스 “예를 들어: Purchase”는 “User” 클래스에 의존적이므로, User 오브젝트의 인스턴스화는 “Purchase” 클래스 밖에서 이뤄져야 합니다.
Design Patterns
- 팩토리 패턴: 이는 OOP에서 가장 많이 사용되는 디자인 패턴인데, 이전에 사용하던 클래스를 이후에 수정해야 하는 경우 많은 시간을 절약해주기 때문입니다.
User( )라는 모델 클래스를 인스턴스화하고 싶다고 하면, 두가지 방법이 존재합니다.
1. $users = new Users( );
2. $users = DataFactory::get('Users');
저는 여러 이유 중 두가지 이유 때문에 두번째 방법을 선호합니다. 첫째로, 클래스 이름을 “Users”에서 “UsersData”로 바꿀 때, “data factory 내부” 한 곳에서만 변경하고 나머지 코드는 그대로 두어도 됩니다. 두번째, 만약 Users 클래스가 Users($connection); 과 같이 파라미터를 받기 시작하면, 역시 이 경우에도 모든 Users 객체를 사용하는 곳이 아닌 한 곳에서만 수정하면 됩니다.
- 어댑터 패턴: 어댑터 패턴은 구조적(structural) 디자인 패턴들 중 하나입니다. 이름으로부터 예상할 수 있듯, 이 패턴은 클래스의 예상치 못한 사용을 예상 가능한 형태로 바꾸어줍니다.
당신이 만드는 어플리케이션이 Youtube API를 사용하여 액세스 토큰을 얻어온다고 생각해보면, getYoutubeToken( );이라는 함수를 호출해야 합니다.
따라서 어플리케이션 내 20개의 서로 다른 곳에서 이 함수를 호출하겠죠.
그 후, Google이 새로운 버전의 Youtube API를 릴리스하고, getYoutubeToken( )의 이름을 getAccessToken( )로 변경합니다.
이제, 당신은 어플리케이션 내부의 모든 getYoutubeToken( )을 찾아 고치거나, 어댑터를 다음과 같이 만들 수도 있습니다.
이 경우에서, 한 줄의 코드만 변경하면 어플리케이션의 나머지는 평소와 같이 잘 동작할 것입니다.
이 글에서 디자인 패턴에 대해서는 자세하게 다루지 않기때문에, 더 배우고 싶은 사람들을 위한 도움이 될 만한 링크를 남깁니다.
https://code.tutsplus.com/series/design-patterns-in-php–cms-747 http://www.phptherightway.com/pages/Design-Patterns.html
소프트웨어 아키텍트와 소프트웨어 개발자 사이의 차이를 기억하세요. 소프트웨어 아키텍트는 주로 팀 리더의 경험을 가지고, 기획 단계에서 팀이 올바른 결정을 내리도록 돕는 이미 존재하는 해법에 대한 지식들을 갖고 있는 사람입니다. 소프트웨어 개발자는 소프트웨어 디자인에 대하여 알고 있어야 하며, 팀 내의 더 쉬운 소통을 위한 충분한 소프트웨어 아키텍처 지식을 가지고 있어야 합니다.
더 읽을 거리
- Advanced Coding Skills, Techniques and Ideas
- Become An Expert Developer with These Advanced Coding Tips (part 1)
- Software Architecture: Architect Your Application with AWS