아키텍처 특성과 결정
성능·확장성·가용성 같은 아키텍처 특성을 고르고, 트레이드오프를 따져 결정하고, 그 결정을 기록으로 남기는 법이다.
목차 (6)
아키텍처란 무엇인가
소프트웨어 아키텍처를 한 줄로 정의하기는 어렵다. 마틴 파울러는 2003년 글 Who Needs an Architect?에서 랄프 존슨의 말을 빌려, 아키텍처란 결국 "중요한 것", 그리고 나중에 바꾸기 어려운 것에 대한 개발자들의 공통 이해라고 정리했다. 무엇이 중요한지는 시스템마다 다르므로, 정의보다 무엇을 적어야 아키텍처를 설명했다고 할 수 있는지를 보는 편이 쓸모 있다.
- 구조: 시스템을 어떤 모양으로 나눴는가. 계층형인지 마이크로서비스인지 같은 아키텍처 스타일이다(아키텍처 스타일 노트).
- 아키텍처 특성: 기능과 별개로 시스템이 잘해야 하는 것. 가용성, 확장성, 보안 같은 이른바 "-ility"다.
- 아키텍처 결정: 팀이 반드시 지킬 규칙. "화면 계층은 데이터베이스를 직접 부르지 않는다"처럼 어기면 구조가 무너지는 것이다.
- 설계 원칙: 규칙보다 느슨한 지침. "서비스 사이는 되도록 비동기로 통신한다"처럼, 상황에 따라 예외를 둘 수 있다.
이 네 가지 나눔은 리처즈와 포드의 책 Fundamentals of Software Architecture가 쓰는 틀이다. 이 노트와 아래 네 편(모듈성, 스타일, 분해, 진화)은 그 책과 Software Architecture: The Hard Parts, Building Evolutionary Architectures가 다루는 주제를 지도로 삼되, 설명과 그림은 직접 쓰고 근거는 공개된 글로 단다.
아키텍처 특성: 기능이 아니라 "얼마나 잘"
"주문을 받는다"는 기능 요구다. "주문이 몰리는 명절에도 주문을 받는다"는 같은 기능에 붙은 아키텍처 특성(architecture characteristic)이다. 흔히 비기능 요구사항이나 품질 속성이라고도 부른다. 특성이 아키텍처 수준이 되려면 세 가지가 맞아야 한다. 기능과 상관없는 설계 고려이고, 구조에 영향을 주며(특별한 구조 없이는 맞추기 어렵다), 시스템이 성공하는 데 중요해야 한다. 예컨대 결제 시스템의 감사 기록은 구조를 바꿔야 할 만큼 중요하지만, 버튼 색은 아니다.
특성은 대략 셋으로 나눈다. 운영 특성은 시스템이 돌아가는 동안의 모습(가용성, 성능, 확장성, 탄력성, 복구 가능성), 구조 특성은 코드를 고치고 늘리는 쉬움(유지보수성, 확장 가능성, 이식성), 횡단 특성은 어느 한 곳에 속하지 않고 시스템 전체에 걸치는 것(보안, 접근성, 개인정보 보호, 법규 준수)이다. 국제 표준으로는 제품 품질 모델 ISO/IEC 25010이 있다. 2023년 판은 기능 적합성, 성능 효율성, 호환성, 상호작용 능력, 신뢰성, 보안, 유지보수성, 유연성, 안전의 아홉 가지로 나눈다.
특성은 요구사항 문서에 적힌 명시적 특성과, 아무도 말하지 않지만 당연히 기대되는 암묵적 특성으로도 나뉜다. "동시 접속 10만"은 적혀 있지만, "개인정보가 새지 않는다"나 "자주 멈추지 않는다"는 대개 적혀 있지 않다. 암묵적 특성을 놓치면 출시 직전에야 구조를 뜯어고치게 된다.
중요한 습관은 적게 고르는 것이다. 특성은 서로 부딪힌다. 보안을 높이려 암호화와 검사를 늘리면 성능이 떨어지고, 확장성을 위해 서비스를 나누면 단순함과 디버깅 쉬움을 잃는다. 모든 특성을 다 챙기려는 설계는 아무것도 잘하지 못하는 복잡한 설계가 된다. 그래서 이 시스템을 성공하게 만드는 특성 서너 개를 골라 순위를 매기고, 나머지는 "적당하면 된다"로 둔다. 최선을 노리기보다 가장 덜 나쁜 조합을 찾는다고 생각하면 쉽다.
특성을 잴 수 있게 바꾸기
"빨라야 한다"는 요구는 확인할 수 없다. 특성 이름은 사람마다 뜻이 다르고, 잴 수 없으면 지켜졌는지 알 수 없다. 그래서 특성을 고른 다음에는 목표 값과 재는 법으로 바꾼다. 성능이라면 평균이 아니라 백분위(p95, p99) 응답 시간으로 적는다. 평균은 소수의 아주 느린 요청을 가리기 때문이다. 백분위와 지연 숫자를 다루는 법은 규모 확장 노트에 있다.
- 운영 지표: 응답 시간 백분위, 초당 처리량, 가동률(월 99.9%면 한 달에 약 43분까지 멈출 수 있다), 장애 뒤 복구 시간. 운영 중에 모니터링으로 잰다.
- 구조 지표: 순환 복잡도(함수 안 분기 경로 수), 모듈 사이 결합 정도, 테스트 커버리지. 정적 분석으로 잰다. 결합을 재는 수는 모듈성과 결합 노트에 있다.
- 과정 지표: 배포 주기, 변경 실패율, 변경이 배포까지 걸리는 시간. 민첩성이나 배포 용이성 같은 특성은 코드가 아니라 개발 과정에서 드러난다.
이렇게 잰 값을 사람이 가끔 보는 데서 그치지 않고 빌드 파이프라인이 자동으로 검사하게 만들면, 그것이 진화적 아키텍처 노트의 적합성 함수가 된다.
트레이드오프 분석: "상황에 따라 다르다"
아키텍처 질문의 정직한 답은 대개 "상황에 따라 다르다"다. 그 말로 끝내면 쓸모가 없으니, 무엇에 따라 다른지를 적는 것이 아키텍트의 일이다. 아키텍처에는 정답이 없고 트레이드오프만 있다. 그래서 어떤 선택을 할 때 얻는 것만 늘어놓지 말고 내주는 것을 같은 무게로 적는다. 내주는 것이 안 보인다면 아직 분석이 덜 된 것이다.
트레이드오프를 따질 때는 앞에서 고른 특성 순위를 기준으로 쓴다. 캐시 예에서 이 시스템의 최우선 특성이 성능이면 오래된 값이라는 대가를 받아들이고, 정확한 재고 수량이 최우선이면 그 화면만큼은 캐시를 빼는 식이다. 결정을 내리면 판단의 근거와 함께 기록으로 남긴다. 조건은 시간이 지나며 바뀌므로, 근거 없이 결론만 남은 결정은 나중에 아무도 감히 바꾸지 못하거나 이유도 모른 채 뒤집힌다.
아키텍처 결정 기록(ADR)
결정을 남기는 가벼운 방법이 아키텍처 결정 기록(ADR, Architecture Decision Record)이다. 마이클 나이가드가 2011년 글 Documenting Architecture Decisions에서 제안한 형식이 가장 널리 쓰인다. 결정 하나에 짧은 문서 하나를 쓰고, 코드 저장소 안에 번호를 붙여 차례로 쌓는다.
- 제목: 결정을 짧은 명사구로. 번호를 붙인다.
- 상태: 제안됨, 수락됨, 폐기됨, 다른 ADR로 대체됨.
- 맥락: 이 결정을 내리게 만든 힘들. 기술적·정치적·일정 제약을 사실대로 적는다.
- 결정: "우리는 …한다"처럼 능동형 문장으로.
- 결과: 결정 뒤에 생기는 일. 좋은 것과 나쁜 것을 모두 적는다.
핵심은 "왜"가 남는다는 점이다. 새로 온 사람이 이상해 보이는 구조를 만났을 때 맥락을 읽고 그대로 따를지, 상황이 바뀌었으니 새 ADR로 뒤집을지 판단할 수 있다. 템플릿과 도구는 adr.github.io에 모여 있고, 소트웍스 테크놀로지 레이더도 Lightweight Architecture Decision Records를 대부분의 팀에 권하는 기법으로 올렸다. 위키 대신 코드와 같은 저장소에 두면 코드 리뷰처럼 리뷰를 받고 코드와 함께 버전이 남는다.
아키텍트와 개발자
아키텍트의 일은 그림을 그려 넘기는 것이 아니다. 특성을 찾아 고르고, 트레이드오프를 따져 결정하고, 그 결정이 지켜지는지 계속 확인하고, 기술과 업무 환경의 변화를 따라가며 결정을 고친다. 그래서 아키텍트에게는 한 기술을 깊이 아는 것보다 여러 선택지가 있다는 것을 넓게 아는 편이 중요하다. 선택지를 모르면 트레이드오프를 따질 대상조차 없다.
아키텍트와 개발자를 가르는 선도 고정돼 있지 않다. 아키텍트가 정한 경계 안에서 개발자는 설계를 자유롭게 하고, 개발자가 구현하며 마주친 문제는 아키텍처 결정을 다시 보게 만든다. 이 고리가 끊기면 이른바 "상아탑 아키텍트"가 된다. 현실과 맞지 않는 결정이 내려오고, 개발자는 그것을 몰래 우회한다. 아키텍트가 코드를 조금이라도 계속 쓰고(핵심 경로가 아닌 도구, 검증용 시제품, 버그 수정 등), 결정마다 ADR로 이유를 공유하는 것이 이 고리를 지키는 현실적인 방법이다.
파울러의 Software Architecture Guide는 이 관점의 글들을 모아 둔 입구다. 좋은 아키텍처의 가치는 지금 빨리 만드는 데보다, 시간이 지나도 기능을 추가하는 속도가 떨어지지 않게 하는 데 있다는 주장이 그 중심이다.