[개발 문화] 1. 소프트웨어 엔지니어링이란?

Doccimann·2022년 6월 21일
5

개발문화

목록 보기
1/2

🤔 왜 갑자기 소프트웨어 엔지니어링에 대한 글을 써요?

제가 얼마전에 서점에 들러서 컴퓨터 관련 베스트셀러 판대를 보게 되었는데, 3위에 랭크된 책을 우연찮게 보게되었습니다. 그 책은 구글 엔지니어는 이렇게 일한다 라는 책이었습니다.

저는 이 책을 표지만 보고 이 책이 왜 베스트셀러에 올라온거지? 싶어서 앞부분만 슥 읽어봤었는데요, 11페이지에 언급된 이 문구는 저를 매료시키기에 매우 충분했습니다.

코드가 지닌 가치를 오래도록 가장 잘 지켜내려면 소프트웨어 조직은 어떤 관례를 도입해야하는가? 그리고 엔지니어는 어떻게 해야 코드베이스의 지속 가능성을 높이고 소프트웨어 엔지니어링 규율 자체를 더 엄격하게 만들 수 있을까? 우리도 이런 질문의 근본적인 답을 모르지만, 적어도 구글이 지난 20년간 축적한 경험이 답을 찾는 여정에 한 줄기 빛을 밝혀주리라 믿는다.

저는 제 개발 방향을 백엔드로 잡았기에, 백엔드의 경우 코드를 작성함에 있어서 상대적으로 프론트엔드보다 코드의 생명이 길수밖에 없습니다. 게다가 백엔드의 경우 유지보수, 성능개선이 자주 일어나기 때문에 오랜 생명을 지니는 코드를 작성하는 방법을 찾아나설 수 밖에 없는 입장이기도 합니다.

물론 프로그래밍 실력도 중요하겠으나, 수많은 유지보수에도 생명을 잃지않는 코드를 작성하거나, 혹은 코드가 생명을 잃지않도록 만드는것도 중요하다고 생각하기에 이 책을 구매하게 되었고, 앞으로도 이 책을 저의 벨로그에 리뷰를 해볼 생각입니다. 물론 제 주관을 섞어서 리뷰를 할것이기 때문에 저의 글을 많이 신뢰하지는 말아주셨으면합니다.

이제 Chapter1을 리뷰해보도록 하겠습니다.


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

우선 이 책에서 정의하는, 그리고 구글에서 정의하는 소프트웨어 엔지니어링의 개념부터 소개해볼까 합니다.

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

여기서 두개의 단어가 나왔습니다. 바로 소프트웨어 엔지니어링 이라는 단어와, 프로그래밍 이라는 단어입니다.

프로그래밍은 흔히 말하는, 코딩을 의미하는 것입니다. 그런데 소프트웨어 엔지니어링은 프로그래밍을 포함하여, 프로그래밍을 함에 있어서 시간(time), 확장(scalability), trade-off를 합산한 개념으로 정의되고 있습니다. 따라서 소프트웨어 엔지니어링이 수반되는 개발의 경우 일반적인 프로그래밍에 비해서 시간이라는 지표마저 고려가 되어야하기 때문에 적어도 한 차원이 추가된다고 볼수 있습니다.

위의 말이 조금 어렵게 다가오실 수도 있는데, 아래의 질문을 스스로에게 던져보면 감이 오실겁니다.

현재 본인이 짜고계신 코드는 얼마만에 폐기가 될것같아요?

제 스스로 생각해봐도 제가 짜고있는 코드는 1년은 무슨, 3개월도 못 갈것같다는 생각이 듭니다. 소프트웨어 엔지니어링은 이런 식으로 수명이 짧은 코드를 절대로 지향하지 않습니다. 이는 곧 코드의 지속 가능성(Sustainability)와 연관이 매우 깊을겁니다.

얼마전에 Toss Slash 22 세미나 영상 중에 지속 성장가능한 코드를 만들어가는 방법 이라는 챕터 중에 코드는 계층화 시켜야한다 라는 파트에서 나온 이미지입니다. 저도 이러한 계층 구조를 이용해서 위드마켓의 가게노출 시스템을 작성하고있는 입장이기 때문에 위의 개념에 매우 공감이 되었습니다. 그리고 저러한 명확하고 변화에 열려있는 아키텍처를 선정해야만 코드의 수명이 길어진다는 것도 사실입니다.

다음 단락에서 소프트웨어 엔지니어링에서 말하고자하는 시간 이라는 것에 대해 자세히 설명해보도록 하겠습니다.


🌈 시간, 그리고 변경가능성에 관하여

우선 이 단락을 시작하기에 앞서서 하이럼의 법칙 에 관해서 설명을 하고 시작하겠습니다.


[Hyrum's raw]
👉 특정 API의 사용자가 충분히 많다면 API의 명세의 중요도는 떨어진다. 왜냐하면 해당 API의 사용자는 시스템에서 눈에 보이는 모든 행위를 누군가는 이용하기 때문이다.


나는 힐링하라고 그랬지 횟집 장사를 하라고 한 적은 없는데...

위의 짤이 하이럼의 법칙을 잘 설명해줄 것 같습니다. 동물의 숲 개발자 입장에서 한번 생각해보도록 하겠습니다. 어떤 개발자는 아래와 같이 생각할수도 있을겁니다.

🤔 나는 이용자들이 낚시 컨텐츠를 즐기면서 힐링하기를 바랬는데 횟집 장사를 하네? 이거 사행성으로 걸리는거 아니야?

그래서 낚시컨텐츠를 즐기고 얻어낸 보상을 타인에게 금전을 받고 되파는 행위를 금지하는 패치를 하려고 시도했다고 가정을 해보도록 하겠습니다. 그런데 개발자 입장에서 걸리는게 한가지 있습니다. 낚시 컨텐츠로 얻어낸 보상을 다른 사람에게 순수한 의도로 나눔을 실천하는 유저들은 어떡하지? 라는 생각을 가지게 된겁니다.

현실도 이와 그렇게 다르지 않을거라고 봅니다. 분명 개발자는 API의 모범사례만을 사용자가 사용하기를 바라고 작성을 하였지만, 누군가는 분명히 명세에도 존재하지않는 기능을 하나 찾아내서 활용하기도 할것이며, 만약에 그 기능이 매우 유용한 기능이라면 추후에 이러한 기능이 API의 변경 가능성을 없애버릴지도 모릅니다. 그리고 이러한 점을 방치하였다가 다른 무해한 변경을 하더라도 일부 사용자의 소프트웨어를 망가뜨릴 가능성또한 존재합니다.

이러한 점에서 변경 가능성이 고려되지 않은 코드는 기대수명이 매우 짧을수밖에 없습니다.

해당 저서에서는 이러한 관점에서 프로그래밍 스타일을 두 가지로 분류를 해보았습니다.

  1. 이용하는 API의 명세에 명시되지 않은, 즉 언제든지 변할 수 있는 기능을 사용하는 코드는 Hachy(임시방편적인), or Clever(기발한) 코드이다.
  2. 반대로 모범사례를 따르고 미래에 대비한(즉, 변경가능성에 열려있는) 코드는 Clean하다, 그리고 유지보수가 가능한(Maintainable) 코드이다.

물론 위의 두 스타일은 목적에 따라서 정답이 될수도, 틀릴수도 있습니다. 만약에 첫번째 방법이 칭찬으로 통하는 경우에는 해당 개발 행위는 프로그래밍에 불과할 것이고, 두번째 방법이 칭찬으로 들린다면 당신은 소프트웨어 엔지니어링을 하고있는 것이다 라고 볼수는 있겠습니다.

저희가 지향해야할 점은 분명히 두번째 방법에 해당할 것입니다. 물론 단순하게 코딩테스트를 위한 코드, 시험을 위한 코드의 경우 첫번째 방법을 지향해야함은 틀림이 없습니다. 그러나 저희가 일반적인 기업을 가서 코드를 짠다고 하면 분명히 두번째 스타일을 고수해야하기 때문에 저희는 항상 변화에 열려있는(혹은, 변화에도 흔들리지 않는) 코드를 작성해야함은 분명합니다.

다음 단락에서는 두번째 메트릭인 코드의 확장성에 관해서 다뤄보겠습니다.


🌈 코드의 확장성

이번 단락에서는 코드의 Scalability에 대해서 다뤄보겠습니다.

우선 코드를 확장한다는 의미부터 곱씹어보고 시작하겠습니다. 이전 단락에서 저희는 코드의 변경 가능성에 대해서 다뤄보았습니다. 그런데 모든 변경에는 비용(cost)가 수반됩니다. 이 비용은 비단 인력(man power)뿐만이 아니라 시스템 리소스자원 등도 포함이 됩니다.

그런데 만약에, 아주 만약에 특정 코드를 확장함에 있어서 시간 메트릭보다 비용 메트릭이 급격하게 상승하는 경향을 보인다면 과연 개발자 입장에서 확장이 쉬울까요? 저같아도 이런 경우에는 많은 고민을 하게 될것같습니다.

위와 같은 상황을 이 책에서는 확장 가능하지 않은 시스템 이라고 부르고있습니다. 우린 이걸 스파게티 코드라고 부르지 ㅋ

그리고 아무리 안정적으로 짠 코드가 있더라도 추가 인력을 2배로 투입해서 규모를 2배로 확장한다고 가정을 해보겠습니다. 그런데 과연 추가인력을 2배로 투입한다고 해서 규모를 2배로 늘릴수 있을까요? 그건 아닙니다. 왜냐하면 이전 문단에서도 말했듯이 cost에는 man-power 외에도 resource가 포함되어있기 때문입니다. 게다가 코드의 안정성을 위해서 TDD를 한다고 가정을한다면 Test를 위한 리소스는 선형을 그리지 않고 선형보다 더 가파르게 상승할겁니다. 이럴때는 코드가 확장가능하지 않은 국면에 접어들수밖에 없고, 코드베이스에 변화가 필요한 순간입니다.

그래서 코드의 확장성을 따질때에는 인력 측면에서의 확장성만 따질게 아닌, Compute resource에 관한 확장성도 고려해봐야합니다.

다음 단락에서는 코드의 확장성, 그리고 확장정책에 관련된 내용을 언급해보도록 하겠습니다.


🌈 코드의 확장성, 그리고 확장정책

하나의 대표적인 예시를 들고 시작하겠습니다.

A라는 회사는 안드로이드 개발 회사입니다. 어느날 A 회사는 기존의 XML 방식으로 UI를 구성하는 방법을 포기하고 Android jetpack Compose 라이브러리를 이용해서 선언적인 UI를 구성하기로 결정하였습니다. 그래서 A라는 회사의 사장은 개발직원들에게 다음을 주문하였습니다.
"Compose가 좋다던데....우리도 거 Compose라는거 쓸수없나? 15일 뒤까지 모든 XML 다 폐기시키고, Compose로 다 바꿔놔 ^^"

위와 같은 상황에서는 XML에서 compose로 갈아타기가 매우 힘들겁니다. 왜냐하면 기존의 Android 소스들은 모두 xml에 강하게 의존하고 있을것이 뻔하게 보이기 때문이죠. 나도 안드로이드 프로그래밍을 해봐서 알지...

위와 같은 상황이 벌어지게 된다면, 어느 팀은 분명히 전환속도가 빨라서 빠르게 전환하고 compose가 반영된 채로 빌드를 시작하게 될것이고, 어떤 팀은 전환 속도가 느려서 compose를 반영하지 못한채로 빌드를 시작하게 될겁니다. 그런데 이런 상황에서는 분명히 한 쪽은 빌드 실패를 겪게될것이 또 뻔하게 보이는 상황입니다.

위와 같은 상황을, 확장가능하지 못한 코드라고 부르게 될것입니다.

그리고 저희 위드마켓 팀이 겪은 사례도 하나 소개해보도록 하겠습니다.

🥲 저희 WithMarket 어플리케이션 개발을 맡는 팀원들은 마스터 브랜치(트렁크)를 기준으로 각자 브랜치를 하나씩 만들어서 개발을 이어나가기 시작했습니다. 그런데 각자 정해진 기간동안 개발을 진행하고 통합을 시도하였더니 어플리케이션이 빌드 오류를 터뜨리기 시작했습니다.

저희의 사례또한 확장성에 문제가 생긴 경우 라고 볼수있겠습니다. 그리고 위와 같이 저희가 어플리케이션을 확장해나가는 방법을 전통적인 개발 브랜츠를 활용하는 방법(정책) 이라고 부릅니다.

4명이 개발 브랜치를 나눠 사용해도 문제가 터지는데, 커다란 회사의 경우에는 문제가 엄청 거대할겁니다. 병합 과정에서 build 이전에 테스트를 위해서 자원을 소모할것이고, 또 다른 브랜치에서 병합이 일어나면서 엄청난 컴퓨터 자원을 소모할것이고, 반복일겁니다. 이러한 행위는 분명히 비용을 선형이 아닌 기하급수적인 비용 증가를 불러올겁니다.

이를 위해서 많은 기업에서는 모노리포(단일 레포지토리) 관리 정책, 혹은 레포지토리를 세분화하거나(MSA), 혹은 가상의 트렁크를 두는 정책 등등을 사용하고 있습니다. (물론 관련 논문에서도 모노리포가 확장 비용을 줄이는데 큰 도움이 된다고 소개가 되어있기도합니다.)


🌲 마치며

그러면 과연 소프트웨어 엔지니어링, 그리고 프로그래밍중에 어떤게 나을까요?

단순한 토이 프로젝트라면 분명히 프로그래밍이 좋을겁니다. 그러나 저희가 가고자하는 기업들에서는 코드의 생명이 매우 길기 때문에 소프트웨어 엔지니어링이 고려가 되어야함을 사실입니다. 따라서 주어진 과업을 쉽게 해결할 수 있고, 당장 활용 가능한 방법을 선택하는게 정답이라는 말씀을 드리고싶습니다.

다음 포스트에서는 팀워크 이끌어내기를 주제로 다뤄보겠습니다. 다음 포스트에서 뵙겠습니다.

profile
Hi There 🤗! I'm college student majoring Mathematics, and double majoring CSE. I'm just enjoying studying about good architectures of back-end system(applications) and how to operate the servers efficiently! 🔥

0개의 댓글