본문 바로가기

데이터 중심 애플리케이션 설계

[데이터 중심 애플리케이션 설계] 유지보수성

 

- 소프트웨어 비용의 대부분은 초기 개발이 아니라 지속해서 이어지는 유지보수에 들어간다는 사실은

  잘 알려져 있다. 이런 유지보수에는 버그 수정, 시스템 운영 유지, 장애 조사, 새로운 플랫폼 적응,

  새 사용 사례를 위한 변경, 기술 채무 상환, 새로운 기능 추가 등이 있다. 

 

- 유감스럽게도 여전히 소프트웨어 시스템상에서 일하는 많은 사람은 소위 레거시 시스템 유지보수 작업을

  좋아하지 않는다. 어쩌면 다른 사람의 실수를 고쳐야 하거나 한물 간 플랫폼에서 작업해야 하거나

  정말 하기 싫은 일을 해야 하는 시스템에 관여해야 하기 때문이다.

  모든 레거시 시스템은 각자 나름대로의 불편함이 있다.

  그래서 이런 레거시 시스템을 다루기 위해 일반적으로 추천할 만한 방법을 제시하는 일은 매우 어렵다. 

 

- 하지만 희망적인 점은 유지보수 중 고통을 최소화하고 레거시 소프트웨어를 직접 만들지 않게끔

  소프트웨어를 설계할 수 있다는 것이다. 아니 꼭 그래야 한다.

  그러기 위해 주의를 기울여야 할 소프트웨어 시스템 설계 원칙은 다음 세 가지다. 

 

1) 운용성(operability)

- 운영팀이 시스템을 원활하게 운영할 수 있게 쉽게 만들어라

 

2) 단순성(simplicity)

- 시스템에서 복잡도를 최대한 제거해 새로운 엔지니어가 시스템을 이해하기 쉽게 만들어라

  (사용자 인터페이스의 단순성과는 다르다는 점에 유의하라)

 

3) 발전성(evolvability)

- 엔지니어가 이후에 시스템을 쉽게 변경할 수 있게 하라.

  그래야 요구사항 변경 같은 예기치 않은 사용 사례를 적용하기가 쉽다.

  이 속성은 유연성(extensibility), 수정 가능성(modifiability), 적응성(plasticity)으로 알려져 있다. 

 

- 이전에 설명했지만 신뢰성, 확장성을 달성하기 위한 쉬운 해결책은 없다.

  그보다 운용성, 단순성, 발전성을 염두에 두고 시스템을 생각하려 노력해야 한다. 

 

 

1) 운용성 : 운영의 편리함 만들기 

- "좋은 운영은 종종 나쁜(또는 불완전한) 소프트웨어의 제약을 피하는 대안이 될 수 있다.

  하지만 좋은 소프트웨어라도 나쁘게 운영할 경우 작동을 신뢰할 수 없다. 는 말이 있다.

  운영 중 일부 측면은 자동화할 수 있고 또 자동화해야 한다.

  그러나 자동화를 처음 설정하고 제대로 동작하는지 확인하는 일은 여전히 사람의 몫이다. 

 

- 시스템이 지속해서 원활하게 작동하려면 운영팀이 필수다.

  좋은 운영팀은 일반적으로 다음과 같은 작업 등을 책임진다. 

 

(1) 시스템 상태를 모니터링하고 상태가 좋지 않다면 빠르게 서비스를 복원

(2) 시스템 장애, 성능 저하 등의 문제의 원인을 추적

(3) 보안 패치를 포함해 소프트웨어와 플랫폼을 최신 상태로 유지

(4) 다른 시스템이 서로 어떻게 영향을 주는지 확인해 문제가 생길 수 있는 변경 사항을

     손상을 입히기 전에 차단

(5) 미래에 발생 가능한 문제를 예측해 문제가 발생하기 전에 해결(예를 들어 용량 계획)

(6) 배포, 설정 관리 등을 위한 모범 사례와 도구를 마련

(7) 애플리케이션을 특정 플랫폼에서 다른 플랫폼으로 이동하는 등 복잡한 유지보수 태스크를 수행

(8) 설정 변경으로 생기는 시스템 보안 유지보수

(9) 예측 가능한 운영과 안정적인 서비스 환경을 유지하기 위한 절차 정의

(10) 개인 인사 이동에도 시스템에 대한 조직의 지식을 보존함

 

- 좋은 운영성이란 동일하게 반복되는 태스크를 쉽게 수행하게끔 만들어

  운영팀이 고부가가치 활동에 노력을 집중한다는 의미다.

-> 데이터 시스템은 동일 반복 태스크를 쉽게 하기 위해 아래 항목 등을 포함해

    다양한 일을 할 수 있다.

 

(1) 좋은 모니터링으로 런타임(runtime) 동작과 시스템의 내부에 대한 가시성 제공

(2) 표준 도구를 이용해 자동화와 통합을 위한 우수한 지원을 제공

(3) 개별 장비 의존성을 회피. 

     유지보수를 위해 장비를 내리더라도 시스템 전체에 영향을 주지 않고

     계속해서 운영 가능해야 함

(4) 좋은 문서와 이해하기 쉬운 운영 모델(ex) X를 하면 Y가 발생한다) 제공

(5) 만족할 만한 기본 동작을 제공하고, 필요할 때 기본값을 다시 정의할 수 있는 자유를 관리자에게 부여

(6) 적절하게 자기 회복(self-healing)이 가능할 뿐 아니라 필요에 따라

      관리자가 시스템 상태를 수동으로 제어할 수 있게 함

(7) 예측 가능하게 동작하고 예기치 않은 상황을 최소화함 

 

 

2) 단순성: 복잡도 관리

- 소규모 소프트웨어 프로젝트에서는 간단하고 표현이 풍부한 코드로 말끔하게 시스템을 작성할 수 있지만,

  프로젝트가 커짐에 따라 시스템은 매우 복잡하고 이해하기 어려워진다.

-> 복잡도는 같은 시스템에서 작업해야 하는 모든 사람의 진행을 느리게 하고 나아가 유지보수 비용이 증가한다.

-> 복잡도 수렁에 빠진 소프트웨어 프로젝트를 때론 커다란 진흙 덩어리로 묘사한다. 

 

- 복잡도는 다양한 증상으로 나타난다. 상태 공간의 급증, 모듈 간 강한 커플링, 복잡한 의존성,

  일관성 없는 명명과 용어, 성능 문제 해결을 목표로 한 해킹, 임시방편으로 문제를 해결한 특수 사례 등이

  이런 증상이다.

-> 이 주제는 이미 많이 회자되고 있다. 

 

- 복잡도 때문에 시스템 유지보수가 어려울 때 예산과 일정이 초과되곤 한다.

  복잡한 소프트웨어에서는 변경이 있을 때 버그가 생길 위험이 더 크다.

  개발자가 시스템을 이해하고 추론하기 어려워지면 시스템에 숨겨진 가정과 의도치 않은 결과 및

  예기치 않은 상호작용을 간과하기 쉽다.

-> 반대로 복잡도를 줄이면 소프트웨어 유지보수성이 크게 향상된다.

-> 따라서 단순성이 구축하려는 시스템의 핵심 목표여야 한다. 

 

- 시스템을 단순하게 만드는 일이 반드시 기능을 줄인다는 의미는 아니다.

-> 우발적 복잡도를 줄인다는 뜻일 수도 있다.

-> 모슬리와 마크스는 우발적 복잡도를 소프트웨어가 풀어야 할 문제에 내재하고 있지 않고

    구현에서만 발생하는 것으로 정의했다. 

 

- 우발적 복잡도를 제거하기 위한 최상의 도군느 추상화다.

  좋은 추상화는 깔끔하고 직관적인 외관 아래로 많은 세부 구현을 숨길 수 있다.

  또한 좋은 추상화는 다른 다양한 애플리케이션에서도 사용 가능하다.

  이러한 재사용은 비슷한 기능을 여러 번 재구현하는 것보다 

  더 효율적일 뿐만 아니라 고품질 소프트웨어로 이어진다.

  추상화된 구성 요소의 품질 향상이 이를 사용하는 모든 애플리케이션에 도움이 되기 때문이다. 

 

- 예를 들어, 고수준 프로그래밍 언어는 기계 언어, CPU 레지스터, 시스템 호출을 숨긴 추상화다.

  SQL은 디스크에 기록하고 메모리에 저장한 복잡한 데이터 구조와 다른 클라이언트의 동시 요청과

  고장 후 불일치를 숨긴 추상화다.

-> 물론 고수준 언어로 프로그래밍해도 여전히 기계어를 사용한다.

-> 단지 직접 사용하지 않을 뿐이다.

-> 프로그래밍 언어의 추상화 덕분에 기계어를 생각할 필요가 없기 때문이다. 

 

- 하지만 좋은 추상화를 찾기는 매우 어렵다. 

  분산 시스템 분야에는 여러 좋은 알고리즘이 있다.

  하지만 관리 가능한 수준에서 시스템 복잡도를 유지하는 데 도움이 되는 추상화로

  이런 알고리즘을 묶는 방법은 명확하지 않다. 

 

 

3) 발전성: 변화를 쉽게 만들기

- 시스템의 요구사항이 영원히 바뀌지 않을 가능성은 매우 적다.

  시스템의 요구사항이 끊임없이 변할 가능성이 훨씬 크다.

  새로운 사실을 배우고 미처 예기치 않은 사용 사례가 나타나고

  비즈니스 우선순위가 바뀌고 사용자가 새로운 기능을 요청하고,

  새로운 플랫폼이 기존 플랫폼을 대체하고 법적 또는 규제 요구사항이 변경되고

  시스템의 성장으로 인한 아키텍처 변화 등이 이런 요구사항에 해당한다.

 

- 조직 프로세스 측면에서 애자일(agile) 작업 패턴은 변화에 적응하기 위한 프레임워크를 제공한다.

  또한 애자일 커뮤니티는 테스트 주도 개발(TDD)과 리팩터링(refactoring) 같이

  자주 변화하는 환경에서 소프트웨어를 개발할 때 도움이 되는 기술 도구와 패턴을 개발하고 있다.

 

- 이런 애자일 기법에 대한 설명은 대부분 매우 작고, 로컬 규모(동일 애플리케이션 내 소스코드 파일이 

  몇 개만 있음)에 초점을 맞추고 있다.

-> 이 책에서는 다양한 애플리케이션이나 다른 특성을 가진 서비스로 구성된 대규모 데이터 시스테 수준에서

    민첩성을 높이는 방법을 찾는다.

-> 예를 들면, 홈 타임라인을 구성하기 위한 트위터의 아키텍처를 접근 방식 1에서 접근 방식 2로

    리팩터링하는 방법을 찾는다.

 

- 데이터 시스템 변경을 쉽게 하고 변화된 요구사항에 시스템을 맞추는 방법은

  시스템의 간단함과 추상화와 밀접한 관련이 있다.

-> 간단하고 이해하기 쉬운 시스템은 대개 복잡한 시스템보다 수정하기 쉽다.

-> 하지만 이것은 매우 중요한 개념이기 때문에 데이터 시스템 수준에서 민첩성을 언급할 때는 

    민첩성 대신 다른 단어로 발전성을 사용하겠다.