반응형


클린코드 책에서 읽은 '깨끗한 테스트코드 5가지 규칙(FIRST)'에 대해 포스팅 남겨보겠습니다.


프로그래밍을 할 때 어떻게 보면 테스트 코드의 작성은 가장 기본이면서 중요하다고 할 수 있는데요.


TDD방식의 프로그래밍까지는 아니더라도 테스트코드를 작성해주면 서비스에서의 버그를 확실히 줄여줄 수 있고


기능 추가시에도 빠르게 테스트해보고 적용하는 등 이점을 많이 가지고 있는데요. 


그렇다면 어떠한 기준과 방식으로 테스트코드를 작성해야하는지 5가지 규칙을 보시도록 하겠습니다.


[클린코드] 깨끗한 테스트코드 5가지 규칙(FIRST)

빠르게(Fast)
테스트는 빨라야 한다. 테스트는 빨리 돌아야 한다는 말이다. 테스트가 느리면 자주 돌릴 엄두를 못 낸다. 자주 돌리지 않으면 초반에 문제를 찾아내 고치지 못한다. 코드를 마음껏 정리하지도 못한다. 결국 코드 품질이 망가지기 시작한다.


독립적으로(Independent)
각 테스트를 서로 의존하면 안 된다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안 된다. 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야 한다. 테스트가 서로에게 의존하면 하나가 실패할 때 나머지도 잇달아 실패하므로 원인을 진단하기 어려워지며 후반 테스트가 찾아내야 할 결함이 숨겨진다.


반복가능하게(Repeatable)
테스트는 어떤 환경에서도 반복 가능해야 한다. 실제 환경, QA 환경, 버스를 타고 집으로 가는 길에 사용하는 노트북 환경(네트워크가 연결되지 않은)에서도 실행할 수 있어야 한다. 테스트가 돌아가지 않는 환경이 하나라도 있다면 테스트가 실패한 이유를 둘러댈 변명이 생긴다. 게다가 환경이 지원되지 않기에 테스트를 수행하지 못하는 상황에 직면한다.


자가검증하는(Self-Validating)
테스트는 bool값으로 결과를 내야 한다. 성공 아니면 실패다. 통과 여부를 알리고 로그 파일을 읽게 만들어서는 안 된다. 통과 여부를 보려고 텍스트 파일 두 개를 수작업으로 비교하게 만들어서도 안 된다. 테스트가 스스로 성공과 실패를 가늠하지 않는다면 판단은 주관적이 되며 지루한 수작업 평가가 필요하게 된다.


적시에(Timely)
테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다. 실제 코드를 구현한 다음에 테스트 코드를 만들면 실제 코드가 테스트하기 어렵다는 사실을 발견할지도 모른다. 어떤 실제 코드는 테스트하기 너무 어렵다고 판명날지 모른다. 테스트가 불가능하도록 실제 코드를 설계할지도 모른다.


규칙 하나하나 너무나도 중요한 얘기들인 것 같습니다. 이 규칙을 염두해두시고 프로그래밍을 하신다면

좀 더 안정적인 서비스를 하시는데, 작성한 코드를 테스트하는데 드는 시간을 많이 줄일 수 있을 것 같습니다!

반응형
반응형



 부서를 처음 배정받고 선배님들이 'Legacy 코드'라는 말을 자주 사용하는 것을 들을 수 있었다. 그 당시에는 Legacy코드가 뭔지에 대해서 감도 전혀 없었고 Legacy 코드에서 작업하는 것이 새롭게 프로젝트를 진행하는 것보다 어려운지 또한 알지 못했다. 하지만 요즘 Legacy 코드 위에서 작업을 하면서 몸소 그 어려움에 대해 체험하는 중이다. 하지만 반면으론 현재 서비스되고 있는 거대한 양의 코드들을 다루며 내가 그동안 프로그래밍을 하면서 알지 못했던 방식이나 방법들에 대해서도 프로그래밍을 함에 있어 지향해야 할 부분과 지양해야 할 부분에 대해서도 많이 생각해보고 적용해보며 견문을 넓혀 가고 있는 중이다.그렇다면 Legacy코드가 무엇인지에 대해서 알아보고 그 위에서 어떻게 작업해가면 좋을지에 대해서 살펴보도록하자

 Legacy란???먼저 Legacy의 사전적인 의미로는 "(남이 남긴)유산"으로 해석할 수 있다. 그럼 Legacy코드란? 많은 사람들이 이에 대해 다양한 정의를 내리고 있지만 간단히 말해 '다른 사람에게 넘겨받은 읽기 어렵고 수정하기 어려운 오래된 코드'를 말한다. 특히 기술의 변화가 많은 웹 프로젝트들에 있어서는 시간이 지나면서 기술이 발전할 수록 Legacy 코드들이 점점 쌓여저만 간다. 그렇다면 Legacy 코드 위에서 작업할 때 부딪히는 문제들에는 어떠한 것들이 있을까?


 Legacy Code에서 작업할 때 부딪히는 문제점은??? 첫번 째, 해당 코드를 이해하기 어려운 문제점이 있다. (변수명, 메소드명이 명확하지 않거나 매우 복잡함)

 두번 째, 해당 코드에 대해 완벽하기 이해하기가 어렵기 때문에 수정이 필요한 곳도 쉽게 수정하기 힘들어 진다. 또한 해당 부분을 수정한다고 했을 때 그에 따른 사이드 이펙트(기능 수정으로 인해 예상치 못한 부분에서 에러 발생)가 어디에서 발생할지에 대해서도 예측하기가 힘들다.

 세번 째, 코드에 대해 이해가 힘들다보니 해당 프로젝트에 대한 유지보수 작업을 진행함에 있어 자신감이 하락하게 된다.

 그렇다면 Legacy 코드를 만들지 않기 위해서는 어떻게 해야할까??? 1. Magic Number를 사용하지 말자.

 ①번 방식처럼 사용하게 될 경우 숫자1과 숫자2과 무엇을 의미하는 코드인지 알기 힘들다 ②번 방식처럼 Magic Number를 제거하고 사용하게 되면 좀 더 이해하기 쉬운 코드를 짤 수 있다.


 2. 의도를 나타내는 이름을 사용하자. 예를 들어 다음과 같은 메소드가 있다고 하자. (밑의 코드를 예를 들기 위해 짠 코드이기 때문에 메서드 명에 집중해주기 바란다)

 위 메서드가 어떠한 작업을 하는지 메서드명만 봐서는 도무지 감을 잡을 수가 없다. 반면 밑의 코드를 보면

 해당 메소드가 무엇을 하는 메소드인지 단번에 알 수 있다. 이렇든 명확한 이름을 사용하여 코드를 이해하는데 드는 시간을 줄일 수 있다.


 3. 가급적 프로그래밍을 할 때 부분만 옳은 것보다 전체적 대칭을 지키자. 예를 들어,district.setDong("정자")district.setGu("분당")district.setMetroPolitanOrSi("성남") 와 같이 부분만 옳은 동떨어진 메서드를 사용하기 보다는district.setDong("정자")district.setGu("분당")district.setSi("성남") 와 같이 전체적 대칭을 지키는게 나을 때가 많다. 따라서 기존의 코드와 대칭을 이루는 메서드명을 사용하는게 명칭이 잘못됬다 할지라도 나을 경우가 있다.


 4. if문이 덕지덕지 붙어있는 복잡한 조건식을 피하자.될 수 있으면 조건문을 사용하기 보단 다양한 패턴들을 활용해 복잡한 조건식을 피하고 메서드를 세분화 시켜주자.


 5. 사용하면 안 되는 클래스/메서드가 있을 경우

위와 같이 deprecated를 해놓으면 이클립스 등에서 코딩할 때 메서드에 취소선이 나와, 개발자의 주의를 환기시킨다. 하지만 아무런 설명이 없기 때문에 왜 저 메서드를 사용하면 안되는지에 대해 알 수 없다. 그렇기 때문에 무책임하게 @deprecated만 선언하지 말고 왜 @deprecated 되었는지 설명 또한 달아주도록 하자.




 6. 뻔한 중복 주석은 피하자.메서드 명이나 변수명만 봐서도 충분히 파악할 수 있는 부분에 대해서는 추가적인 주석을 달 필요가 없다. 특별한 경우나 이름 만으로만 이해하기 힘든 부분이 있을 경우 꼭 필요한 부분에 있어서 주석을 사용해주자. (의미없는 주석 사용 nono)


 7. 기능에 해당하는 테스트 코드를 작성하자.


 8. 리팩토링 작업을 지속적으로 하자. 리팩토링 - 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다. 리팩토링을 별도의 작업이 아니라 개발의 한 부분으로 생각하도록 하자.


 현장 TIP1. Q) 얼마 전 중복이 많고 복잡한 코드를 GOF 디자인 패턴을 적용하여 리팩토링 했다. 그런데 코드리뷰 때 다른 개발자가 더 코드가 더 보기 어려워졌다고 한다.

 A) Communicative Code의 요건 중 배려가 있다. 따라서 분명히 구조적으로 좋아졌다고 하더라도 팀 내 다른 개발자가 모두 코드 읽기를 어려워 한다면 한번 더 생각해볼 만한 부분이라고 생각한다. 가치의 상충이 일어나는 것인데 '구조적 개선 vs 읽기 어려움'이다. 각각을 통해 얻게 될 가치를 종합적으로 잘 비교해보고 취사선택하는 것이 좋을 것이라 본다. 또한 GOF 디자인 패턴 적용의 초점이 문제해결이 아닌 적용 그 자체에 있을 때가 종종 있다. 이런 형태의 적용은 득보단 실이 많기 때문에 가급적 피하는 것이 좋다.


2. Q) Legacy 코드를 수정 중인데 너무 읽고 이해하기 어려운 클래스 xxx가 있다. 아무래도 안될 것 같아 New XXX를 만들었다. 하지만 예전 클래스를 사용하는 곳 까지 모두 수정하기는 어려워 이번에 작업하는 범위의 클래스에서만 New XXX를 사용하게 바꾸었다. 이렇게 해도 괜찮은 건가?

 A) 경험이 비추어 볼 때 절대 하지 말아야 할 형태의 개선이다. 이런 형태의 개선을 할 때 New, New2, New3가 계속 생기며, 기존 클래스는 정리가 안 되어 이후 복잡도가 높아지고 혼란을 가중시키는 사례를 봐왔다. 따라서 가급적 신규 클래스를 도입하는 형태의 개선은 트랜잭션과 마찬가지로 'All or Noting' 원칙이 적용되어야 한다고 본다. 신규 클래스를 도입하려면 반드시 기존 클래스를 제거하고, 그게 힘들다면 신규 클래스를 만들지 말고 예전 클래스 기반에서 수정하는 것이 올바른 방향이락 본다.


3. Q) Legacy 코드를 수정하기에 앞서 Cover & Modify를 하려고 하고 있다. 그런데 의존하는 객체가 너무 많았다. 할 수 없이 하나하나 만들어가며 테스트를 만들었다. 그런데 새로운 객체를 생성하는 등의 리팩토링을 하다 보니 기존에 만든 테스트가 다 실패한다. 도움을 주기보다는 거추장스러운 느낌이 나는데 왜 그런가?

 A) BO나 Service를 대상으로 단위 테스트를 만들면 대게 객체간의 인터랙션을 테스트하는 코드를 만들게 된다. 하지만 이런 테스트는 객체 간의 인터랙션을 조정하는 등의 리팩토링을 하게 되면 테스트를 고쳐줘야 하고 따라서 위와 같이 의미가 퇴색 될 수 있다. 따라서 이런 때는 통합 테스트를 활용하여 상태를 기반으로 검증하는 것이 좋다. BO의 예를 들면 실제 BO나 Service를 호출하고 데이터베이스 등에 값이 제대로 들어갔는지 등을 검사하는 것이다. 이렇게 만든 테스트는 객체의 관계를 조정하는 등의 리팩토링을 해도 달성되는 결과는 같기 때문에 테스트를 수정할 필요가 없고 안전망으로써 역할을 잘 수행한다.

 지금까지 Legacy코드와 Legacy 코드를 만들지 않기 위한 방법, 현장 Tip등 에대해 알아보았다. 많은 내용은 NHN에서 교육했던 'Legacy 코드에서 작업하기'교재에서 참고하여 정리하였다.


반응형

+ Recent posts