Home 01. 소프트웨어 엔지니어링이란?
Post
Cancel

01. 소프트웨어 엔지니어링이란?

프로그래밍과 소프트웨어 엔지니어링의 가장 큰 차이는 시간, (규모)확장, 실전에서의 트레이드오프 이다. 소프트웨어 엔지니어링 프로젝트에서 엔지니어는 시간의 흐름과 언젠가 변경될 가능성에 더 신경 써야 한다. 만들어낼 소프트웨어 자체뿐 아니라, 제작하는 조직까지 양 측면 모두에서의 확장과 효율에 더 집중해야 한다.

소프트웨어 엔지니어링은 흐르는 시간 위에서 순간순간의 프로그래밍을 모두 합산한 것이다.

  • 시간이 프로그램에 미치는 영향 : ‘이 코드의 예상 수명은?’ 수명이 길어질수록 변경이라는 요소가 점점 중요해진다.
  • 규모 관점에서 바라본다 : 몇 명이 참여하는가, 시간의 흐름에 따라 엔지니어들은 개발과 유지보수의 어느 부분에 관여하는가? 소프트웨어 엔지니어링은 팀 업무이다. 소프트웨어 엔지니어링과 프로그래밍은 시간과 참여 인원 면에서 차이가 난다. 협업은 그 자체로 새로운 문제를 유발하지만, 한 명이 개발하는 것보다 가치 있는 시스템을 만들어낼 잠재력 또한 가진다. 프로젝트나 조직 규모가 확장되며 딸려오는 문제는 흔히 정책에 영향을 주며 소프트웨어의 지속 가능성을 묻는 질문에 답하는 기초가 된다.
  • 트레이드 오프 평가 : 소프트웨어 엔지니어링에서는 주기적으로 여러 선택지 사이(유지보수에 도움이 되는 변경을 연기하거나 확장성이 떨어지는 정책을 받아들여야 하는 경우 등등)의 트레이드 오프를 평가해야 하고, 합리적인 결정을 내려야 한다.

1.1 시간과 변경

초심자가 프로그래밍을 배우는 과정에서 작성한 코드의 수명은 보통 몇 시간에서 며칠 정도일 것이다. 또 초기 스타트업에서 일하는 엔지니어들도 바로 눈앞의 목표에 집중하려 할 것이다. 그래서 줄곧 스타트업들에서만 일해왔다면 10년차 개발자라도 소프트웨어를 1~2년 이상 유지보수해본 경험이 전무할 수 있다.

반대편에서는 반 영구적으로 살아남은 프로젝트도 있다. 구글 검색, 리눅스 커널, 아파치 HTTP 서버의 수명은 언제 다할지 예측할 수 없다.

기대 수명이 수십년이 될 수록 업그레이드의 중요도도 높아진다. 이때 기대 수명의 최저점, 최고점 사이의 어디에서 전환이 일어나는데 이 기점으로 프로젝트는 외부 환경의 변화에 대비하기 시작해야 한다. 초기부터 업그레이드를 계획하지 않은 프로젝트라면 이 전환이 아주 고통스러울 것이다.

  • 해당 프로젝트에서 수행해본 적 없는 작업을 진행해야 한다.
  • 업그레이드 담당 엔지니어들이 이런 종류의 작업을 경험해보지 못했을 가능성이 크다.
  • 일반적인 업그레이드보다 작업 규모가 큰 경우가 많다.(점진적X, 수년치 업그레이드를 한 번에 진행)

이러한 고통 때문에 기존 코드를 버리고 새로 작성하거나 업그레이드를 완전히 포기해버릴 수 있다.

첫 대규모 업그레이드를 성공적으로 마치는 일뿐 아니라 현재 상태를 안정되게 유지할 수도 있어야만 프로젝트가 오래 지속 가능할 확률이 높다. 지속 가능하게 하려면 요구되는 변경들의 영향을 계획하고 관리해야 한다.

1.1.1 하이럼의 법칙

다른 엔지니어들이 사용 중인 프로젝트를 유지보수하고 있다면 동작한다 / 유지보수 가능하다 를 구분 짓는 가장 중요한 요인은 하이럼의 법칙일 것이다.

하이럼의 법칙 : API 사용자가 충분히 많다면 API 명세에 적힌 내용은 중요하지 않다. 시스템에서 눈에 보이는 모든 행위를 누군가는 이용하게 될 것이기 때문이다.

시간의 흐름에 따른 변경과 유지보수를 논하려면 하이럼의 법칙을 알아야 한다. 하이럼의 법칙은 최선의 의도, 최고의 엔지니어, 꼼꼼한 코드 리뷰가 뒷받침되더라도 공표한 계약(명세)이나 모범 사례를 완벽하게 구현해냈다고 단정할 수 없다는 현실을 표현한 말이다.

현실에서는 API 사용자가 명세에는 없는 기능을 찾아서 활용하기도 하며 그 기능이 유용해 널리 쓰이게 되면 추후 API를 변경하기 어렵게 된다. 따라서 변경이 얼마나 유용한지를 분석할 때는 충돌을 조사, 식별, 해결하는 데 따르는 비용도 고려해야 한다.

1.1.2 사례:해시 순서

해시 테이블의 원소 순서는 나름의 알고리즘에 의해 정해진다. 그로인해 해시 테이블을 사용하면 다음과 같은 일이 발생하는 것을 경험했다.

  • 해시 플러딩 공격(DoS의 하나로 해시 함수의 구조를 아는 사용자가 일부러 해시 테이블이 느려지도록 구성한 데이터를 제공하는 공격) 때문에 해시 반복 순서가 비결정적이어야 할 필요가 커졌다.
  • 개선된 해시 알고리즘과 해시 컨테이너 연구로 얻은 효율 개선 효과를 보려면 해시 반복 순서에 변화를 줘야 한다.
  • 하이럼의 법칙에 따라 해시 테이블의 순회 순서에 의존하는 프로그램을 작성하는 프로그래머가 나타날 것이다.

해시 컨테이너의 반복 순서에 의존하더라도 프로그램의 수명이 짧다면 기술적 문제를 전혀 겪지 않을 것이다. 소프트웨어 엔지니어링 프로젝트에서는 이런 의존성은 위험 요인에 해당한다.

프로그래밍 스타일 분류

  • 이용하는 API의 명세에 명시되지 않은, 즉 언제든 변할 수 있는 기능을 사용하는 코드는 임시방편적인 혹은 기발한 코드이다. ===> 프로그래밍
  • 모범 사례를 따르고 미래에 대비한 코드는 클린하고, 유지보수 가능한 코드이다. ===> 소프트웨어 엔지니어링

1.1.3 변하지 않기를 목표로 하지 않는 이유

시간과 변경에 대처해야 하는 이유의 밑바탕은 변경은 피할 수 없다라는 가정이 깔려 있다. 오래전에 만들어둔 알고리즘과 데이터 구조는 최신 장비에서 효율이 떨어지기도 한다. 그래서 소프트웨어 설계도 제때 변경해주지 않으면 최신 하드웨어를 도입하는 효과가 퇴색된다. 하위 호환되는 하드웨어라서 오래된 시스템도 여전히 구동되지만 그 시절 최적화 기법이 지금도 여전히 유효할지는 불확실하다. 그래서 최신 장비의 잠재력을 최대한 끌어 쓸 의지와 역량이 없다면 지불한 비용만큼의 효과를 얻지 못할지도 모른다. (적절한 변경이 뒷받침되어야 새로운 선택지의 도입 효과가 극대화된다.)

변경은 본질적으로 좋지 않으므로 변경을 위한 변경은 삼가야 하지만 변화에 대응할 수는 있어야 한다.

1.2 규모 확장과 효율성

코드베이스의 지속 가능성은 ‘코드 베이스의 수명이 다할 때까지 직면하는 변화가 몰고 오는 모든 변경을 안전하게 처리할 수 있다면 그 코드베이스는 지속 가능하다.’라는 의미이다. 여기에는 비용이라는 요소가 숨어 있다. 비용이 너무 많이 드는 변경은 지연되기 쉽다. 변경 비용이 시간 흐름보다 가파르게 상승하는 시스템은 분명 확장 가능하지 않다.

인건비 외에도 확장 가능해야하는 것은 더 있다. 소프트웨어 자체는 연산, 메모리, 스토리지, 대역폭 같은 전통적인 자원을 더 추가할수록 비례하여 속도나 처리량이 확장되어야 한다. 마지막으로 소프트웨어 조직에서 가장 중요한 자산인 코드베이스 자체도 확장 가능해야 한다. 코드가 많아지고 변경 이력이 쌓이는 등의 이유로 빌드 시스템이나 버전 관리 시스템이 점점 느려진다면 어느 순간 더는 정상 운영할 수 없는 시점이 온다.

전체 빌드에 걸리는 시간, 리포지토리에서 전체를 새로 내려받는 시간, 프로그래밍 언어 버전을 업그레이드 하는 비용 같은 지표는 적극적으로 관리하지 않으면 천천히 악화된다.

조직에서 코드를 작성하고 관리하는 데 활용하는 모든 것이 총비용과 자원 소비 측면에서 확장 가능해야 한다. 특히 반복적으로 수행하는 일이라면 모두 인적 비용 측면에서 확장 가능해야 한다.

1.2.1 확장하기 어려운 정책들

시스템을 폐기시키는 전통적인 방법이 확장성 문제의 예로 적합하다. 예) 새로운 위젯을 개발했다고 할때 모두가 기존 것을 버리고 새 위젯을 쓰기로 결정했다. 전환을 독려하기 위해 기존 위젝은 며칠뒤에 삭제할 것이라고 공지를 내렸다. ==> 이런 방식은 작은 조직에서는 통할지 몰라도 의존성 그래프가 조금만 깊고 넓어지면 곧바로 실패하고 만다.

‘갈아타기 규칙 정책’ - 마이그레이션 책임 소재를 달리하는 새 규칙으로 인프라 팀은 사내 사용자들이 새 버전으로 옮기도록 돕거나 직접 업데이트를 하되, 하위 호환성을 유지해야 한다. 사용자 입장에서는 새 인프라로 바꿔서 당장 얻는 이익이 눈에 보이지 않음에도 자신들 보고 바꾸라고 하면 의욕이 떨어져서 적극적으로 움직이지 않기 때문이다.

1.2.2 확장 가능한 정책들

구글 내부 정책 중 인프라 팀이 인프라 변경을 안전하게 진행하게끔 보호해주는 정책이 하나 있다. 바로 ‘인프라를 변경하여 서비스가 중단되는 등의 문제가 발생하더라도, 같은 문제가 지속적 통합(CI) 시스템의 자동 테스트에서 발견되지 않는다면 인프라팀의 책임이 아니다’ 라는 정책이다. ==> 비욘세 규칙(코드를 짰으면 자기 코드에 대한 테스트도 자기가 제대로 만들었어야지 란 뜻으로 해석)

전문성과 공유 포럼이 조직 확장에 기여하는 바가 크다는 사실을 깨달았다. 엔지니어들이 포럼에 질문하고 답하는 과정에서 지식이 전파되고 새로운 전문가가 성장한다.

1.2.3 사례: 컴파일러 업그레이드

하위 호환성이 좋다고 알려진 경우라도 언어와 컴파일러 업그레이드는 섬세하고 어려운 과제이다. 컴파일러가 업그레이드될 때는 거의 항상 어딘가 미묘하게 다르게 동작하는 곳이 있기 마련이다. 구글에서도 컴파일러를 5년간 업그레이드 하지 않아 대부분의 엔지니어가 컴파일러를 변경해본 경험이 없고, 작성된 코드들도 단 하나의 컴파일러 버전만 겪어본 상태였다. 이 때 굉장히 고통스러운 업그레이드 경험을 하게 된다.

인프라는 더 자주 변경할수록 변경하기가 오히려 쉬워진다. 컴파일러 업그레이드등의 목적으로 코드를 한 번 수정해두면 거의 예외 없이 코드가 더 견고해지고 다음번 업그레이드를 하기도 쉬워진다는 것을 깨달았다. 이런 수정을 여러 차례 거치고 나면 하부 구현의 미묘한 차이에 의존하는 일이 없어지고, 대신 언어나 OS 차원에서 보장하는 추상 개념을 활용하도록 바뀐다.

[코드베이스의 유연성에 영향을 주는 여러 요인]

  • 전문성 : 여러 방법에 대한 충분한 지식과 경험
  • 안정성 : 규칙적으로 릴리스 하여 릴리스 사이의 변경량을 줄였다.
  • 순응 : 규칙적인 업그레이드
  • 익숙함 : 업그레이드를 정기적으로 수행하여 중복되는 작업을 찾고 자동화 하도록 노력
  • 정책 : 유용한 정책과 절차를 갖춘다.

1.2.4 원점 회귀(왼쪽으로 옮기기)

위 그림에서 보듯 문제 발견 시점을 왼쪽으로 이동시킬수록 수정 비용이 줄어든다. 왼쪽으로 옮기는 행위를 원점 회귀라고 하는데 보안성 점검을 개발 프로세스의 마지막으로 연기하면 안 된다고 호소하며 보안을 고려하는 시점을 왼쪽으로 이동시켜라 라고 한 말에서 유래되었다.

제품을 고객에게 배포한 후에야 취약점이 발견되면 해결하는 데 막대한 비용이 들 것이다. 코드 커밋 전에 정적 검사나 코드 리뷰로 찾아낸 버그는 프로덕션 이후에 발견한 버그보다 훨씬 싸게 고칠 수 있다.

1.3 트레이드오프와 비용

소프트웨어 엔지니어링에서도 좋은 선택이 대체로 좋은 결과로 이어진다.

[비용의 종류]

  • 금융 비용 (돈)
  • 리소스 비용 (CPU시간)
  • 인적 비용 (엔지니어링 노력)
  • 거래 비용 (조치를 취하는 비용)
  • 기회 비용 (조치를 취하지 않는 비용)
  • 사회적 비용 (선택이 사회 전체에 미치는 영향)

위에 나열된 비용 외에도 현상 유지 편향과 손실 회피같은 치우침도 존재한다. 비용을 평가할 때는 모든 비용을 염두해 두어야 한다. 은행 잔고뿐 아니라 구성원들이 스스로의 가치를 느끼고 생산적인 일을 하고 있다고 생각하는지까지 포함된다.

1.3.1 사례:화이트보드 마커

회의때 제대로 나오는 마커가 부족해서 방해된 적이 몇 번인가? 마커가 나오다 말아서 사고의 흐름이 끊겨본 경험은?

구글에서는 사무용품과 일상적인 개발시 드는 경비부터 글로벌 규모의 서비스를 준비하고 운영하는 방법에 이르기까지 모든 일과 관련된 비용/이윤 트레이트 오프에 동일한 수준의 관심을 두고 명확이 계량하려고 한다.

종종 구글의 문화는 데이터 주도적이다라고 하는데 데이터가 없을 때조차 근거, 선례, 논증을 거쳐 결정을 내리곤 한다. 좋은 엔지니어링적 결정이란 결국 가용한 모든 근거 자료에 적절한 가중치를 부여하고, 이러한 풍부한 지식을 바탕으로 균형점을 잡는 일이다.

  • 반드시 해야 하는일(법적 요구사항, 고객 요구사항)
  • 근거에 기반하여 당시 내릴 수 있는 최선의 선택(적절한 결정권자가 확정)

1.3.2 의사결정을 위한 근거 자료

근거의 가중치를 정하는 시나리오

  • 관련한 정량적 데이터를 모두 측정할 수 있거나 최소한 추정이라도 할 수 있는 경우 : 결함이 파고들 여지가 적다
  • 측정하기 어렵거나 측정 방법을 모르는 정량적 데이터 : 쉬운 답이 나오기 어렵다. 그래서 절충안을 찾기 위해 경험, 리더십, 선례에 기대야 한다.

1.3.3 사례: 분산 빌드

소프트웨어가 거대해진 오늘날에도 약 60~70%의 개발자가 빌드를 로컬에서 실행한다고 한다. 구글에서도 2000년대 중반까지 로컬 빌드에 의존했다. 그리고 코드베이스가 커져가면서 컴파일 시간도 꾸준히 늘어났다. 그로인해 더 크고 강력한 로컬 머신을 구입하는 지출도 커졌다.

결국 구글은 자체 분산 빌드 시스템을 개발했다. 비용이 많이 들고 많은 엔지니어 시간이 투입됐다. 그래도 전체적으로는 절약되는 비용이 훨씬 컸음이 명백했다.

시간이 흐르면서 분산 빌드 자체가 폭발적으로 커졌고, 비대해진 빌드에는 불필요한 의존성이 넘쳐나게 됐다. 그래도 종합적으로 분산 빌드 시스템을 도입하여 절약한 비용이 시스템을 구축하고 유지보수하는데 드는 부정적인 비용보다 훨씬 컸다.

1.3.4 사례: 시간과 규모 확장 사이에서 결정하기

“우리 팀에만 해당하는 문제가 생겼어 해결하려면 의존성을 추가하는 게 좋을까, 아니면 포크하거나 다시 구현하는게 좋을까?”

이 질문은 소프트웨어 스택의 여러 계층에서 등장한다. 당면한 문제에만 최적화한 해법이 모든 가능성에 대비하는 범용적인 해법보다 성능이 뛰어날 가능성이 크기 때문이다. 그래서 유틸리티 코드를 포크하거나 다시 구현하여 주어진 문제만을 풀도록 하면 사용성을 개선하거나 더 확실하게 최적화할 수 있다.

그렇다고 모든 개발자가 기존 솔루션을 재활용하는 대신 전부 포크해버린다면 확장성과 지속 가능성이 위협 받는다. 프로젝트의 수명이 짧거나 포크한 코드의 영향 범위가 제한적인게 확실할 때 포크하는 게 유리하다. 반면 데이터 구조, 직렬화 포맷, 네트워크 프로토콜처럼 프로젝트의 수명에 종속되지 않는 인터페이스라면 포크를 피하는 게 좋다.

1.3.5 결정 재고하기와 잘못 인정하기

잘 드러나지는 않지만 잘못했음을 인정할 수 있게 해주는 능력 역시 데이터 중심 문화가 주는 커다란 장점이다. 제대로된 데이터이고 가정의 수는 가능한 한 적어야 좋겠지만 결정 시점에 가용한 데이터만을 활용해야 한다는 한계가 있다. 그래서 새로운 데이터를 얻어 상황이 바뀌거나 가정이 무너진다면 기존 결정에 오류가 있었음이 밝혀질 수 있다.

데이터 자체도 시간이 지나면 변하고 새로운 데이터가 나타날 수 있음을 안다. 그러니 시스템의 생애 동안 과거에 내린 결정을 수시로 재고해봐야 한다.

1.4 소프트웨어 엔지니어링 VS 프로그래밍

소프트웨어 엔지니어링이 프로그래밍보다 우수하다는 게 아니다. 단지 이 둘에게 적용되는 제약 사항, 가치, 모범 사례가 서로 다르다는 뜻이다.

서로 관련은 깊지만 상이한 두 용어인 ‘프로그래밍’과 ‘소프트웨어 엔지니어링’을 구별해야 한다. 그 차이 대부분은 시간 흐름에 따른 코드관리, 시간 흐름에 따른 규모 확장의 영향, 이런 관점에서의 의사결정 방식에 있다. 프로그래밍은 코드를 생산하는 즉각적인 행위이다. 소프트웨어 엔지니어링은 활용 가치가 남아 있는 한 오랫동안 코드를 유용하게 관리하고 팀 간 협업을 가능케 하는 정책, 관례, 도구 모두를 아우르는 종합적인 개념이다.

1.5 마치며

1.6 핵심 정리

  • 프로그래밍은 코드 생산에 관한 것이다. 반면 소프트웨어 엔지니어링은 프로그래밍을 확장하여 소프트웨어의 수명이 다할 때까지 코드를 유지보수하는 문제까지 고려한다.
  • 단명하는 코드와 장수하는 코드의 수명은 적어도 10만 배는 될 것이다. 두 프로젝트에 똑같이 적용하려는 건 너무 안일한 생각이다.
  • 코드의 기대 수명 동안 의존성, 기술, 제품 요구사항 변경에 대응할 역량이 갖춰져야 지속 가능한 소프트웨어이다. 변경을 받아들이지 않기로 선택하더라도 변경할 수 있는 역량 자체는 필요하다.
  • 하이럼의 법칙
  • 조직에서 반복적으로 수행하는 모든 작업은 필요 인력 측면에서 확장 가능해야 한다.
  • 끓는 물 속의 개구리가 되지 않도록 주의하자
  • 의사결정을 데이터 주도로 하겠다는 생각은 좋은 출발이다.
  • 데이터 주도 방식은 시간이 흘러 데이터가 변하면 프로젝트의 방향도 바뀔 수 있음을 뜻한다.
This post is licensed under CC BY 4.0 by the author.

01.문자찾기, 대소문자 변환, 문장속 단어

02. 팀워크 이끌어내기