티스토리 뷰

etc/프로젝트

테스트 코드와 리팩토링

기억용블로그 2022. 6. 22. 21:53
728x90

진행 중인 개인 프로젝트에서 기능을 더 이상 추가하지 않고 작성하지 않고 있었던 테스트 코드를 몰아서 작성하고 있는 중이다.

 

테스트 코드를 작성해야겠다라고 느낀건 기능을 작성한 이후 UI단에서 기능이 동작하는지 확인하는데까지 오랜 시간이 소요됐기 때문이다.

처음은 그냥 데이터를 꺼내어 단순히 열거만 하는 형식이었기에 테스트 자체가 필요없다고 생각했다.

 

하지만 문제가 생긴건 프로젝트가 조금씩 복잡해지면서 한 화면에서 필요한 기능이 여러개가 되고 또 여러 테이블을 불러와야 하고 또 WAS단에서 여러 복잡한 로직이 조금씩 추가되기 시작하면서였다.

 

'단순히 DB에서 화면까지 잘 연결되었네'만 확인하는 수준과 여기서 이런 저런 로직이 필요해지는 수준은 또 다른 레벨이었고 이전처럼 로직이 잘 작성되었는지 UI로 확인하는건 생산성의 급격한 저하를 불러왔고 생산성의 향상을 위해서는 이전까지 몰아두었던 테스트 코드를 작성해야 할 시간이 다가왔다.


테스트를 짜는 이유

가장 먼저 테스트를 작성하지 못 하고 있었던건 Test Doubles라는 존재의 난해함때문이었다. 

'여기서는 이런 데이터가 온다고 가정' '저기서는 저런 데이터가 온다고 가정'해서 테스트 코드를 작성하는 것에 어떤 의미가 있는거지? 라는 생각에 다가가기 힘든 존재였다.

 

이 의문점은 공부하다 보면서 자연스레 해소되었는데, 그것은 많은 사람들이 말하던 '관심사의 분리 문제'였다.

관심사를 정확히 분리해서 이 객체에서 내가 하고싶은게 정확히 무엇인지를 알고나면 다른 모든 값들은 전부 mocking하는 것이 가능해진다.


예를 들어서 Service 클래스 내에 실제 로직을 수행하는 여러 메서드들이 있고 그 메서드들을 모아서 하나의 흐름으로 통제해주는 메서드가 있다고 가정해보자.

 

이때 나는 각 로직을 수행하는 여러 메서드들을 각각 테스트를 진행할 수 있고 하나로 뭉쳐주는 메서드의 테스트를 진행할 수 있는데

만약 실제 로직을 수행하는 메서드들을 '이미 테스트가 완료되었다'라고 가정한다면 나는 이 메서드가 어떤 값을 넣으면 어떤 리턴을 해줄지 명확하게 알고있다.

 

물론 규모가 커지고 협업을 하게 되면 남의 코드가 전체의 대부분일 것이므로 어떤 동작을 할지 명확하게 모르게 되는 것은 당연한 수순일 것이라 생각한다.

그래서 내가 아닌 누가봐도 바로 알 수 있을 정도로 Java Docs로 문서화하는 것과 명확한 테스트 이름과 각 메서드마다 꼼꼼하게 여러 테스트를 진행하는 것이 매우 중요할 것이라 생각된다!

이것은 '현재의 나'를 위해서가 아닌 '미래의 나', '현재의 남', '미래의 남'에 대한 존중이다.
공리주의에 의해 '현재의 나'는 많은 것을 희생할 수 있어야 한다.

나의 코드가 플랫폼이 될 수 있도록 짜야한다.

 

흐름을 컨트롤하는 메서드에서는 흐름대로 잘 작동되는지 확인 하는 것만이 오롯한 목표이다. 각 메서드에서 어떤 로직이 수행되었는지는 알 필요도 없고 알아서도 안 된다. 

(각 테스트는 한 가지 이유에 의해서 성공하거나 실패해야 한다!)


이것이 모킹하는 이유이다. 이 이유를 이해하고나니 테스트 코드가 친숙해졌다.

이 이전에는 테스트 코드를 짜고 싶지 않았다. 작성하는 이유도 몰랐다. 

테스트 코드의 중요성과 이유를 깨닫고 난 지금은

 

"테스트 코드를 먼저 짜지 않은 것을 후회 중이다. 그리고 테스트 코드를 짜는 것이 재미있다."

 

뒤늦게 테스트 코드를 짜면서 본인의 코드를 보며 느끼는 것이

'코드를 이렇게 짜놓은 놈 누구야?' ==> 본인이었다.

테스트 코드를 미리 작성하여 이 메서드에서는 어떤 동작으로 동작해야 하는지 로직적인 문제가 어디서 어떻게 발생할 수 있는지 등을 미리 계산하여 작성하였다면 

'코드를 이렇게 짜놓은 놈' 정도까지 가지 않고 '코드를 짠 사람' 정도에서 끝났을 것으로 생각한다.

 

그리고 테스트 코드를 짜는 것이 재미있다.

이 메서드에서는 빠져나갈 구멍이 어디지? 어디서 런타임 에러가 날 수 있을까? 최악의 유저가 온다면? 등의 시나리오를 생각해서 when then을 짜는 것은 즐거웠다.

(아마 직전까지 하던게 프론트의 UI단이었던지라 더욱 재밌게 느껴졌던 것같다.)

 

리팩토링의 필요성

'코드를 이렇게 짜놓은 놈'으로서 코드를 차근차근 보고 있으면

 

1. 이 메서드 이름은 왜 이렇지?

2. 이 메서드는 왜 모든걸 드러내고 있지?

3. 비슷한 내용의 DB I/O를 왜 여러번 하고 있을까?

 

등등의 생각이 절로 들었다. 성능 개선 및 리팩토링의 필요성이 생기는 순간이었다.

 

1. 이 메서드 이름은 왜 이렇지?

메서드가 무엇을 하는지 행위에 대해 명확한 이름을 갖지 못 한다면 길게 작성해서라도 명확히 하려고 시도했고 

한 메서드 내에서 이것도 하고 저것도 하는데 이름을 뭘로 하지?라는 생각을 오래 하게되면 아예 다른 메서드로 분리시켰다. (감사합니다 엉클 밥)

 

2. 이 메서드는 왜 모든걸 드러내고 있지?

모든 것을 다 드러내놓고 내부 로직을 전혀 감춰두지 않은 메서드들이 몇 개 있었다.

본인이 보기에도 그 코드는 타인에 대한 배려가 느껴지지 않았다.

 

다른 프로그래머들로 하여금 이 코드가 어떤 식으로 동작하는지 궁금해하게 만드는 코드가 최악의 코드라고 생각한다.
내 코드는 다른 사람에게 단 두 가지만 주면 된다. 리턴값과 리턴값에 대한 확신.

 

조치한 방법은 모든 내부 로직들을 관심사에 맞게 나누어 private으로 분리시켰다.

return의 값이 여러 개가 나오는 경우가 몇몇 있었는데 이런 경우에 어떻게 분리해야 하나 고민하다가 Object를 생성해서 받게 만들었다.

 

3. 비슷한 내용의 DB I/O를 왜 여러번 하고 있을까?

캐시의 지역성을 고려하지 않았으며 비싼 DB I/O를 낭비하고 있었다. 지금 당장은 DB에 실제 데이터가 없으니 문제되진 않았지만 만약 수백 수천만건의 row가 있었다면 문제가 생길 여지가 큰 코드였다.

 

해결 방법은 간단했는데 비슷한 값을 따로 가져오는게 아니라 한번의 I/O로 다 가져오고 Service에서 분리시켜 이용하는 방법이었다. 분리시킬 때는 람다식을 이용해서 분리했는데 람다식을 명확히 이해하고나니 람다식도 사용하기에 참 편리했다.

 

마무리

테스트 코드와 리팩토링, 클린 코드에 한 발자국 내딛게 된 후기로

선배 개발자들이 그렇게 강조하는 것들에 대해 아주 조금 공통점이 생긴 것같아 기쁘다.

 

요즘 로버트 마틴 옹의 강연을 듣는 중인데 클린 코드, 클린 아키텍처에 대한 많은 정보를 얻고 있다.

10시간도 안 되는 6개의 강연이 기본적인 개념을 잡는데 많은 도움이 되는 것같다.

 

클린함을 코드에 녹여내는데까지 많은 시간과 경험이 필요하겠지만 꾸준하게 노력하자!

'etc > 프로젝트' 카테고리의 다른 글

쇼핑몰 개인 프로젝트 회고  (0) 2022.07.31
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함