우리 회사 " 개발의 정석 "


본 컬럼은 회사 구성원들, 특별히 사내 신입 사원들에게 바라는 개발자의 모습을 정리한 글입니다. 우리 회사의 상황과 경험에서 우러나온 조언이므로 개개인 별로 다르게 생각하는 부분들도 있겠지만 회사가 지향하는 연구소의 모습을 살펴보고 기존 멤버들도 다시금 우리 문화를 돌아봤으면 합니다.


주의 
사내에서 진행된 기술 세미나의 발표 자료를 컬럼 형태로 수정해서 공유합니다.
모든 내용에 관한 저작권은 발표자와 (주)데브구루에 있습니다.
수정이 필요한 부분이나 새로운 정보는 언제라도 아래 메일로 연락 부탁 드립니다.

발표 : 최정현 (cjhnim@devguru.co.kr
 정리 : 송지호 (songjiho@devguru.co.kr)


1. 개발자는 분석가이다

개발자들이 일하는 것을 보면 종종 자신의 일의 범위를 좁혀서 생각하는 경우가 있다. 소프트웨어 개발은 포괄적인 일이다. 너무 시각이 좁아져서 일의 범위를 코드만으로 한정 지어 생각하는 잘못을 범하지 말자.

Software Development Life Cycle은 일반적으로 ‘요구 사항 수집 및 분석 → 설계 → 구현 → 테스트 → 배포 → 운영 및 유지 보수’과정을 통해서 성장하고 유지되고 사라지게 된다.

이러한 라이프 사이클에서 맨 처음 나오는 ‘요구 사항 수집 및 분석’은 다른 단계들에 지속적인 영향을 끼친다.

최근 개발한 Teleport USB 제품을 개발 하면서 기획팀으로부터 ‘USB 3.0을 지원합니까?'라는 질문을 받았다. 이 경우 기획팀(고객)이 말하는 ‘지원’과 개발자가 생각하는 ‘지원’ 이라는 단어의 의미에 차이가 있을 수 있다.

디바이스 드라이버라는 계층에서는 USB 2.0 이나 USB 3.0이 거의 차이가 없다. 그래서 드라이버 개발자는 OS가 지원한다면 별다른 문제 없이 지원한다고 생각한다. 하지만 ‘왜 묻죠?’ 라고 한마디만 반문해 보면 지원의 의미가 다르다는 것을 파악할 수 있다.

기획팀에서 문의하게 된 이유는 직접 측정해 보니 우리 프로그램을 사용할 때 USB 2.0에서 파일 복사 속도와 USB 3.0에서의 파일 복사 속도가 동일하기 때문이었다. 기획팀에서는 기술적으로 USB 3.0스팩을 지원하는지(기능적 요소)에 대한 질문이 아니라 품질, 성능 적인 요소에 대한 질문이었던 것이다. 이처럼 왜 물어 봤는지를 알아야 정확한 대답을 해줄 수 있는 경우들이 많다. 질문 의도를 정확히 모르고 USB 3.0을 지원한다고 했으면 서로 다른 결과를 상상했을 수 있다.


계속 이어서 기획팀에서는 ‘그럼 빠르게 해줄 수 있는가?’ 라고 문의를 했다. 이 경우도

  1. 얼마나 빠르게 해드려요? 라고 재 질문을 한다면 아마도 최대한 빠르게 해주세요 라는 답변을 듣게 될 것이다. 대화의 대상이 엔지니어가 아니면 이런 접근은 잘못되었을 수 있다.

  2. USB 데이터를 리다이렉션 하는 프로그램 이므로 당연히 USB 스팩에서 말하는 전송 속도는 의미가 줄어 든다. 이미 만들어진 타 사 제품을 벤치 마킹 해 보고 이들의 최대 전송속도, 최저 전송 속도, 평균 전송 속도 등을 확인하고 우리는 후발 이므로 어디에서부터 어디까지의 전송 속도를 목표로 하겠다 정도로 이야기하는 것이 상호 의견 교환에 합리적이다.

이처럼 상황을 잘 분석하고 대응하는 모습이 필요하겠다.



2. 코딩도 빌드업이 필요하다

게임 중계를 통해서 빌드업이라는 단어를 처음 들었다. 스타크레프트라는 게임을 해보면 적절한 단계를 거치면서 성장해 가는 것을 볼 수 있다. 같은 룰에서 진행하는 게임이지만 운영하는 게이머의 목적에 따라서 진행 과정이 달라지게 된다. 하지만 어떤 게이머도 과정을 거치지 않고 최종까지 갈 수는 없다. 코딩도 유사하다. 목적에 따라서 과정에서 세세한 차이는 있겠지만 공학적으로 말하는 과정을 잘 거쳐야 한다.

플랫폼 종속적인 것인가, 어느 OS까지 지원할 것인가, 추후 확장은 어느 정도 고려할 것인가? 등등에 따라서 코딩 차이는 발생하지만 빌드업 순서는 잘 지켜주면 좋다.

과거에는 이러한 부분에 소홀해서 구현을 일찍 시작하고 디버깅에서 많은 시간을 보냈던 것 같다.

하지만 빌드업 순서를 지켜보면 QA팀과 TC를 만들 때 요구 사항이 명확해진다. 명확해진 전체 요구 사항을 가져와서 작은 요구 사항으로 쪼개서 각각을 개발하고 테스트하다 보면 전체 테스트 케이스가 완성되게 된다. 그리고 개발자가 만든 단위 테스트에 대한 테스트 방법도 생기게 된다.



3. 개발 환경과 도구도 계속 진화해야 한다

개발 도구나 환경은 최신일수록 좋으나 최신 트렌드를 너무 빨리 따라 가면 문제가 발생하기도 한다. 안정화된 버전의 최신 도구들을 계속 도입하자. 리눅스 용 개발 환경에서의 makefile을 Visual C에서 통합해서 사용하는 것을 고려하는 등 도구와 환경의 진화에 신경을 잘 쓰면 도구의 도움을 많이 받을 수 있다. 여러분들은 어떤 환경에서 어떤 툴 들을 사용하고 있는가? 가장 효율적인 방법을 사용하고 있는가? 질문해 보자.

친숙한 환경에서 샘플 프로그램을 만들고 이 프로그램을 돌리는 것으로만 만족하는가?



4. 반복 작업은 최대한 자동화한다

다음과 같은 일들이 자동화하기 좋은 일들이다. 반복되는 것들을 자동화하고 코드 작성에 더 많은 시간과 관심을 쏟자

  • 개발PC에서 테스트 PC로 드라이버 파일(sys)을 복사
  • 테스트 PC에 Windows Service를 배포
  • 배포 패키지 만들기
  • 개발자 테스트
  • API 레퍼런스 설명서 문서 작성
  • 릴리즈 노트 작성



5. 개발 목표를 세운다

프로젝트 목표는 고객이 원하는 목표이고 그 목표를 달성하는 과정에서 개발자들은 어떠한 것을 얻게 될지 목표를 세워 보자. 그렇지 않으면 똑 같은 일을 반복하는 듯한 느낌을 가질 수도 있다.

새로운 목표를 세워서 작업하고, 이 결과가 우리에게 맞으면 이를 업무 프로세스에 적용하는 과정들을 지속적으로 거치면 스스로가 발전하게 된다. 필자는 다음과 같은 것들을 목표로 삼아서 개발해 보고 좋은 것들을 업무 표준으로 적용해 왔다.

  • 문서 기반 개발을 해보자
  • 유닛 테스트를 사용해 보자
  • 테스트 자동화를 해보자
  • TDD를 사용해 보자
  • Scrum으로 개발해 보자
  • CrossPlatform을 제공해보자
  • 최대한 빨리 개발해보자
  • 문서 생성 자동화를 해보자



6. 개발자도 과학자이다

개발자도 과학자처럼 가설을 세우고 이를 증명하는 과정으로 개발을 수행할 때가 많다. 이 때 다음과 같은 태도로 업무에 임해야 한다.

  • 가설 조작 변인 및 통제 변인에 대한 명확한 고려를 기반으로 검증
  • 직감보단 논리에 의거한 판단
  • 경험보단 근거에 의거한 분석

상황에 휩쓸려 잘못된 판단을 하거나, 지나치게 경험에 의존해 성급한 결론을 내는 등 비논리적인 이유로 미리 답을 정해 놓고 이슈를 바라보는 잘못을 하지 않기 바란다.



7. S/W는 추적 가능해야 한다

Svn이나 Git, Jira, Github, 빌드 시스템, wiki 같은 시스템을 사용하는 이유 중 하나는 바로 ‘추적 가능’이다. 독자들의 개발 환경에서는 다음과 같은 것들이 가능한가 답해보자.

  • 특정 버전의 바이너리와 소스 코드가 보관되어 있는가?
  • 특정 버전의 수정 이력을 파악할 수 있는가?
  • 수정 이력 각각에 대한 코드 확인이 되는가?
  • 그 코드와 관련된 설계 문서를 찾을 수 있는가?
  • 설계 문서가 기초한 Spec을 찾을 수 있는가?
  • Spec 문서가 기초한 요구 사항 문서를 찾을 수 있는가?
  • 요구 사항은 누가 요청한 것인가? 요청 배경은 무엇인가?

새로운 팀에 합류하기로 하였다. 첫날부터 버그를 해결해야 한다. 재현 경로도 있고 재현 환경도 있고 이슈 해결에 필요한 기본 정보가 있다. 허나 S/W가 추적이 불가능하다면, 과연 버그를 해결할 수 있을까?

설계 문서를 작성하는 것에는 약간의 논란이 있을 수 있다. 여기서 설계 문서의 의미는 워드 문서와 같은 것들만 이야기하는 것은 아니다. 잘 관리된 코드의 주석도 문서가 될 수 있다. 주석이 없더라도 잘 정제된 코드 자체도 훌륭한 문서가 되는 경우도 수 없이 보아왔다. 워드 문서가 있는지 없는지 보다는 어떤 수단을 사용하든지 간에 코드의 작성 의도를 알 수 있고 추적이 가능한지가 중요하다.



8. S/W는 진화 가능해야 한다

개발 처음부터 완벽하게 설계하고 구현하여 한번에 잘 만들려고 욕심을 부릴 때가 있다. 어떤 경우에도 대응할 수 있는 코드를 한번에 만들기는 어렵다. 발생할 문제를 예측해서 미리 잘 만드는 것도 중요하지만 문제가 발생했을 때 잘 대응하는 것이 더 중요하다. 다시 말해 잘 대응할 수 있는 구조(design)를 유지해 나가는 것이 중요하다.

예를 들면 어떤 버그 해결을 위해 인터페이스 변경이 꼭 필요한 경우가 있다. 하지만 어떤 결과를 가져올지 예측이 어려워 이를 회피하고 주먹구구 코드를 작성하는 경우도 종종 있다. 이럴 땐 현재 SW의 구조(design)가 이러한 변화를 수용할 수준이 안된다는 뜻이다. 수용할 수 있도록 구조를 먼저 변경해야 한다. 다른 말로 리팩터링이 필요하다. 리팩터링을 성공하기 위해 구조 변경 전 정상 동작함을 알 수 있는 테스트를 만들고 구조 변경 후 테스트를 수행하여 문제가 없음을 확인해야 한다. 구조가 유연하게 바뀌면, 인터페이스 변경도 쉬워진다. 이렇게 S/W는 진화하게 되고 그 반복되는 과정 속에 축적된 테스트는 추후에 회귀 테스트로 활용할 수 있고 품질 확인에 도움이 된다. 이러한 선순환 구조로 SW는 계속 진화한다.



9. S/W는 진단 가능해야 한다

문제가 발생했을 때 진단을 못하는 경우 대응이 곤란하고 이를 수정하는데 많은 비용이 발생한다.

소프트웨어의 진단 기능은 개발 일정에 반영하여야 하고 이 또한 점차적으로 고도화 해 나가야 한다. 다른 프로젝트에서도 사용 가능하게 라이브러리를 구축해 나가는 것도 중요하다.

이런 맥락에서 개발 초기부터 로깅은 셋업 되어 있어야 하고 구현이 늘어날 수록 로그도 함께 늘어나야 한다. 로그 확인도 테스트의 일종일 수 있다. 로그를 통해 개발하는 습관을 갖자.

로그들은 다음과 같은 구성 요소들을 포함하게 만들어야 사용 상에 무리가 없다.

  • 다양한 방식 제공
      • 디버그 메세지 로깅
      • 파일 로깅
      • 이벤트 로깅
  • 로그 레벨(Level)
  • 로그 영역(Area)
  • Crash dump 수집기
  • 로그 수집기



10. 코드의 중복은 악이다

코드의 중복 사례로는 코드의 중복, 함수의 중복, 클래스의 중복, 로직의 중복, Log 라이브러리의 중복, Crossplatform 지원으로 발생하는 중복과 같은 다양한 부분에서 중복된 코드를 볼 수 있다.

이러한 중복 문제를 잘 다루기 위해서는 첫 번째로는 결합도(Coupling)와 응집도(Cohension)를 고려해야 하겠다. 다들 아는 바와 같이 결합도는 낮추고 응집도는 높이는 설계와 코딩을 하자.

두 번째는 리팩터링과 테스트이다. 힘들더라도 리팩터링 시점을 잘 확보해서 코드를 정리하고 테스트하는 노력을 게을리 하지 말아야 하겠다. 작업 되어 있는 코드나 라이브러리는 다시 가져다 사용할 수 있게 정리되어야 한다. 이는 세 번째 방법인 기술적 부채 와도 이어지는 부분이다.

마지막으로 기술적 부채를 인지하고 잘 관리하려고 노력하자. 부채가 계속 쌓이면 이자가 쌓이고 심한 경우는 어떤 순간 원금을 넘어서서 파산하는 지경에 이를 수 있다. 적절한 시점엔 부채를 갚아 이자를 줄여 나가야 한다. 적절한 부채는 SW의 효율적 운영에 도움이 되지만 과도하면 화를 불러오므로 조심하자.

기술적 부채(Technical debt)
소프트웨어 개발 과정에서 장기적으로 바람직한 접근법 대신 당장 편한 해법을 택해 발생하는 추가적 작업 비용을 가리킵니다. '디자인 부채(design debt)', '코드 부채(code debt)'라고도 불립니다. 예를 들어 어떤 코드를 수정하면 연관된 코드와 문서화 작업을 함께 해야 하는데, 여러 이유로 이 작업을 미뤄 두면 결국 언젠 가는 해야 할 작업, 즉 '빚'이 됩니다.

기술적 부채의 예(Technical debt)
- 개발 환경 세팅의 부재(새 노트북을 받았다. 언제 개발 환경을 세팅 해야 할까)
- 여기저기 흩어진 동일 기능의 다른 코드들



11. 코드는 테스트 가능해야 한다

코드 테스트와 관련해서는 특별히 예외나 에러 핸들링의 경우는 테스트를 한번도 하지 않은 코드가 포함되어 출시되는 경우가 발생할 수 있다는 점을 유념하라. 결국 한번도 테스트 되지 않은 코드들이 출시되고, 이 코드들은 실제로 문제가 발생했을 때 호출되는데 발생한 문제를 해결 할 수 있는 코드인지 제대로 검증 되지 않아서 또 다른 문제를 유발 할 수 있다. 그리고 테스트와 관련 해서는 테스트를 남발하지 않게 주의 해야 함도 함께 기억해야 한다.

코드를 작성하면서 다음 사항 정도는 반드시 테스트를 하기를 당부한다.

  • 파라미터 체크 구문
  • 메모리 할당 실패 체크 구문
  • 메모리 해제가 잘되는지
  • 버퍼의 경계 테스트




12. Side Effect를 이해한다

ISO/IEC 에서는 Side Effect를 다음과 같이 정의한다.

Side Effect
  • Accessing an object designated by a volatile lvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. [ISO/IEC 14882]

다음은 몇 개의 Side Effect가 존재할까?
첫 번째 구문은 Side Effect가 1개이다. 4+3의 연산 결과인 7이 Y라는 변수에 대입 되며 Y의 값이 변화가 발생된다. 두 번째 구분은 Side Effect가 2개이다. X의 값도 증가하면서 Y에 대입이 일어난다.

(1) Y = 4 + 3
(2) Y = X++

Side Effect에 따른 문제점 발생을 최소화하려면 다음을 고려해야 한다.

  • 수정한 코드의 Side Effect를 파악하여 코드의 동작에 어떤 영향을 끼치는 지 영향 범위를 정확히 파악하자. 
  • 영향 범위에 따른 테스트 케이스들을 선별하고, 테스트를 시행한다.
  • Side Effect 파악이 어렵다면, 적절한 회귀 테스트로 파악하지 못한 Side Effect를 극복하자.
  • Side Effect를 최소화하려면, 제품 코드에 중복이 없도록 노력을 한다. 중복을 허용하자면 향후 개선에 대한 계획 정도는 생각하여 대비한다.



13. 개발 방정식 (전체 개발 시간 = 범위 X 시간)

개발 일정을 단축하려면 어떻게 해야 할까? 일의 범위가 고정되면 사람을 더 투입하거나, 일을 오버 해서 더 하거나 하는 방법을 사용해서 일정을 조정할 수 있다. 하지만 일의 범위를 조정할 수 있다면 무리하지 않게 일정을 유지할 수 있다.

많은 프로젝트들이 목표 범위 내에서 해야 할 일의 범위를 조정하는 것이 가능한 경우가 많다. 범위를 고정 시켜 놓고 생각하지 말자. 예를 들어 특정 시기에 서비스를 반드시 런칭 해야 하는데 정해진 시간 안에 완료가 어렵다 판단된다면, 범위를 조정할 수 있을지 살펴보도록 하자. 방도는 항상 존재할 것이다.

반대로 물리적으로 정해진 시간 내에 더 많은 시간을 투입하지 않고 시간을 확보할 수 있는 방법은 없을까? 이게 무슨 말인가? 쉽게 말하면 평소 일주일 걸리던 일을 3일 안에 완수하면 시간을 확보하게 된다. 이를 위한 방법은 무엇일까? 자 그렇다. 지금까지 이야기했던 것들, 앞으로 남은 몇 가지 이야기가 모두 해당한다. 일을 좀 더 효과적으로 할 수 있는 방법을 항상 고민하고 개발 인프라를 개선하고, 개개인의 성장에 관심을 두며 조직과 시스템이 이를 뒷받침해야 한다.



14. 품질에 대한 생각

품질 확보의 척도는 상대적이다. 프로젝트, 서비스, 또는 SW의 목적에 따라 요구하는 품질의 수준은 달라져야 한다. 우리 회사 업무에서 상대성이 발생하는 요소들은 다음과 같은 것들이 있다.

  • 외주 개발
  • B2C 자사 서비스
  • B2B 외주 솔루션
  • 연구 개발

외주 개발은 고객이 요청한 요구 사항을 정확히 구현해주는 것이 일 순위로 중요하다. 개발사 입장의 시선에서는 그 외의 비기능적인 요소들이 눈에 많이 보이긴 할 것이다. 하지만 이들 모두를 주어진 시간 내에 모두 다 진행하기에 어려운 경우가 많다. 이럴 땐 고객의 요구 사항과 관련된 필수적인 것들만 반영하고, 그 외의 것들은 별도의 프로젝트 계약을 성사 하여 일을 진행하는 편이 발주사나 수행 사 입장에서 모두 이득이다.

B2C 자사 서비스의 경우 기능 요구 사항보다 비 기능 요구 사항에 대한 품질을 확보하는 것이 더 중요할 수 있다. 비슷한 동종 서비스 간의 차이는 바로 비 기능 요구 사항에 대한 품질 차이 때문이다. 비 기능 요구 사항이란 얼마나 빨리 수행 되는지와 연관된 성능, 얼마나 사용하기 편한지에 대한 UX, 어느 언어를 사용하는 사람이 사용 하느냐의 다국어 지원 등 기능에 대한 정도, 서비스를 사용하는 환경과 관련된 것들을 일컫는 말이다.

B2B 외주 솔루션에서 가장 중요하게 봐야 할 품질 요소는 고객이 필요로 하는 기능을 정확하게 구현했는가 이다. 또한, 고객이 미쳐 파악하지 못한 요소에 대해 제안도 할 줄 알아야 한다.

연구 개발의 경우 로직이나 개념 증명(PoC)에 포커스가 맞춰진다. 해당 기술이 사용할 수 있는 가에 초점이 더욱 맞춰지며 품질에 대한 시각도 이에 맞춰 달라져야 할 것이다.



15. 일이 많다는 것에 대한 생각

개발자들의 삶이란 항상 시간에 쫓기며 바쁜 일상을 살아간다. 개발자들이 삶과 일의 균형을 가질 수 있도록 조직 차원에서 일의 분배가 균형적으로 이루어진다면 얼마나 좋을까? 회사에서 충분한 개발자를 고용하여 팀에 일의 부하가 가지 않도록 하면 해결할 수 있지 않을까? 분명 필요한 작업일 것이다.

하지만, 개발자의 입장에서 바라본다면, 이는 내가 스스로 바꿀 수 있는 것들은 아니다. 보통 주어지는 것이다. 그럼 이 상황을 내가 능동적으로 해결할 방법은 없을까?

그에 대한 답 대신 그림 한 장으로 대신하고자 한다.


여러 분들의 사용하는 수레는 혹시 네모난 바퀴를 달고 있지는 않을까? 때론 여유를 갖고 동그란 바퀴를 장착하는데 시간을 들이는 것이 결국은 여러분의 일을 줄일 수 있는 방법일 수도 있다.