성장일기

과제 진행 소감에는 미션을 진행하면서 느끼고 배운 점, 많은 시간을 투자한 부분 등도 포함하면 더 좋을 것 같습니다. 🙂

 

그리고 과제를 제출할 때 'git’'과정별 언어’를 학습하면서 느낀 점을 소감문으로 작성해 주세요. 이때 학습한 '과정’을 잘 드러내 주세요.

 


 

이번 우아한 테크 코스 5기는 이전 기수들과 달리 1차 코딩 테스트가 없어지고, 기존의 프리코스 3주 차에서 4주 차로 바뀌면서 1주 차에 온보딩 주차가 추가되었다.

 

온보딩에 대한 자세한 내용은 해당 우테코 깃허브를 통해 공개해놓고 있기 때문에 들어가서 확인하면 된다.

 

 

온보딩 과제는 미션 제목처럼 온보딩이여서 그런지 엄청 어려운 요구사항은 없었다. 그냥 기존의 코딩 테스트를 보는 대신 코딩 테스트 문제들을 온보딩 주차에 미션으로 추가한 것 같다.

 

나는 프로그래밍을 독학하면서 NextStep의 JavaPlayground를 통해서 우테코 프리코스와 비슷한 과정을 의식적인 연습을 통해 학습한 경험이 있다. JavaPlayground도 우테코 캡틴인 박재성님이 하는 교육이기에 과정이 비슷하다. 물론 JavaPlayground를 진행했다고 해서 프리코스에서 얻는 게 없고, 배움이 없는 건 아니다. 그 당시의 나와 지금의 나는 다르기 때문이다.

프리코스를 하면서 github pull request 하는 것도 처음이었고, Java언어와 Git에 대해서 배우고 얻는 것도 많았지만, 이번 프리코스 4주차를 통해 새로운 인사이트를 얻고 싶었다. 바로 TDD(Test Driven Development)이다.

 

테스트 코드를 공부하다가 TDD에 대해서 알게 되었고, 테스트 코드의 중요성을 느꼈기에 관심이 생겨서 '테스트 주도 개발'이라는 책을 사서 읽었다. 앞의 화폐 예제는 볼만해서 보고, 뒤로 갈수록 내용이 추상적이라 보는 것을 멈췄지만, TDD를 이해하기에는 충분했다.(TDD에 대해선 설명하지 않는다.)

 

책을 보고 다음과 같은 생각이 들었다.

 

  1. 그래서 TDD의 장점이 뭐지? 굳이 이렇게 까지 해야하나?
  2. 그냥 프로덕션 코드 먼저 작성하고, 단위 테스트를 작성하면 되는 거 아닌가?
  3. TDD를 Spring을 통한 웹 애플리케이션 개발에서도 할 수 있을까?

 

이후 나는  Spring 프로젝트에서 TDD를 시도해보았지만, 당연히 포기했다. TDD가 연습되어 있지 않을뿐더러, 많은 프레임워크와 라이브러리가 의존되어 있을수록, 해당 문법에 익숙지 않을수록, 테스트 코드를 먼저 작성하는 일은 쉽지 않았다. 이후 TDD의 장단점에 대해서 구글링을 해보았지만, 다양한 상황에서 장단점이 있었고, 사람들마다 의견이 다양했다. 

 

JavaPlayground 피드백 강의에서 박재성님이 하신 말이 떠올랐다. "TDD 한번 해보고 좋은 거 같으면 계속하고, 아니면 하지 마세요." 맞다. 나는 제대로 해보지도 않고 TDD의 장단점이 뭔지에 대해서만 꽂혀있었다. 프로그래밍은 잘못된 것은 있지만, 정답은 없다고 생각한다. 나에게 맞을 수도 안 맞을 수도 있다.

 

따라서 이번 프리코스를 통해 TDD에 대한 인사이트를 얻고자, 모든 미션부터 최종 코딩 테스트 까지(1차가 붙는다면ㅠ) TDD로 개발하기로 도전했다. 프리코스 과정이 우테코 합격에 영향을 미치기에 쉽지 않은 결정이었지만, 그만큼 많은 성장이 있을 것으로 기대하고 있다.

 

 

1. 1주차 - TDD에 대한 인사이트

내가 지금까지 개발을 진행해왔던 방법이다.

 

  1. 기능 요구사항을 정리한다.
  2. 구체적인 클래스 설계를 한다.
  3. 하나의 기능을 구현한다.
  4. 기능에 대한 테스트 코드를 작성한다.(성공할 때까지 코드 수정)
  5. 리팩토링한다.
  6. 3~5 과정을 반복한다.

 

하지만 TDD로 도전하면서 다음과 같은 싸이클로 개발을 진행했다.

 

  1. 기능 요구사항을 정리한다.
  2. 대략적인 클래스 설계를 한다.(구현 중 유동적으로 바뀔 수 있음)
  3. 하나의 기능에 대한 실패하는 테스트 코드를 작성한다.
  4. 작성한 테스트 코드를 성공시키기 위한 범위 내 프로덕션 코드를 작성한다.
  5. 리팩토링한다.
  6. 3~5 과정을 반복한다.

 

첫 번째로 메소드의 접근제어자 의문이 들었다. 기존에 개발 방식은 주요 기능에 대한 메소드를 public으로 열어주고 해당 메소드에서 파생되는 메소드는 private으로 클래스 내에서만 접근 가능하게끔 했다. 테스트 코드를 짤 때에도 public 메소드만 테스트하여 private 메소드는 public 메소드를 통한 간접 테스트를 진행했다. 물론 private 메소드들도 테스트를 위해 public으로 열어줘도 된다고 생각하지만(테스트는 그만큼 중요하다.), 나는 private 메소드가 엄청 복잡한 로직이여서 내가 불안한 경우를 제외하고는 굳이 테스트 하지 않는다. 왜냐하면 서비스 규모가 커지면 커질수록 해당 클래스 내에서만 사용하는 메소드들은 private으로 두어서 주는 이점이 더 많다고 생각하기 때문이다.

 

즉 (현재 상황이 메소드가 하나의 기능을 담당한다고 가정했을 때) 정리해보자면, 하나의 기능을 구현하기 위해 methodA를 public으로 열어두고 구현하였고, 그 과정에서 methodB, methodC가 파생되었으며 methodA만 테스트 코드를 작성했다.

public methodA(...){
    methodB(...);
    methodC(...);
    ...
}

private methodB(...){};
private methodC(...){};

 

 

이 생각은 변함이 없고, 이 생각으로 TDD를 진행하는데 어려움이 있었다. TDD는 더 작은 기능 단위로 접근을 하게 되었고, 위의 예에서 methodB, methodC를 먼저 TDD로 개발하다 보니 테스트 코드 작성을 위해 public으로 열어둬야 했다. 따라서 결국 public으로 열어두지 않아도 될 메소드들이 테스트를 위해 public인 상태로 개발을 진행하게 되었다.

 

이에 대한 나의 생각은 다음과 같다. methodB, methodC를 위한 테스트 코드를 작성하기 위해 public으로 열어두지만, 진행을 하다 보면 결국 methodB, methodC가 methodA 내부에서만 쓰이기 때문에 이 점을 인지했을 때, private으로 수정하고 기존의 methodB, methodC의 테스트 코드를 삭제하면 TDD개발의 장점을 가져가면서 public을 남발하지 않을 수 있다고 생각한다. 결과만 놓고 보자면 의미 있는 기능의 methodA의 단위 테스트만 존재하는 것이다.

 

하지만 작성한 테스트 코드 삭제에 대해서 의문이 들었고, 이에 대한 생각을 정리하고, 다른 사람들의 생각이 궁금하여 구글링과 책을 통해서 찾아봤다.

 

다양한 사람들의 의견이 존재했다.

  1. public이 싫다면 default나 protected 접근제어자를 이용하여 해당 패키지까지로 까지 접근 범위를 줄인다. -> https://groups.google.com/g/xper/c/1p6V8kB7EIQ
  2. methodB, methodC가 다른 책임을 가지는 클래스로 쪼개질 수 있는 경우, public으로 열어두어 모두 테스트가 가능한 구조가 된다. 즉 private 메소드에 대한 단위 테스트가 필요하다고 느끼는 순간, 클래스 설계에 대해 다시 고민해 볼 필요가 있다. -> https://shanepark.tistory.com/367
  3. 꼭 private으로 두어야 하지만, test가 필요할 경우 Reflection을 이용한다. -> https://shanepark.tistory.com/367
  4. 모든 메소드는 public 이어야 한다. -> http://wiki.c2.com/?MethodsShouldBePublic, https://ohgyun.com/243

 

다양한 사람들의 의견에 대한 내 생각은 이렇다,

  1. 접근 제어자를 통해 열어주는 방법도 고려해 볼만 하나, 서비스 규모가 커지면 커질수록 해당 클래스 내에서만 사용하는 메소드들은 private으로 두어서 주는 이점이 더 많다고 생각하기 때문에 신중할 것 같다.
  2. 이 경우에는 클래스로 쪼개질 수 있는 경우 쪼개져도 좋다고 생각하나, 잘못하면 오버프로그래밍을 할 수도 있다고 생각한다. 다른 클래스로 리팩토링 필요성을 느끼는 경우 그러면 좋겠지만, 해당 private 메소드가 해당 시점까지는 해당 클래스에 존재해야 할 수도 있다고 생각한다.
  3. 굳이 Reflection의 비용을 들이면서 까지 테스트를 해야하나? 라고 생각한다. 또한 private 메소드가 엄청 복잡한 로직이어서 내가 불안한 경우를 제외하고는 굳이 테스트 하지 않고, public을 통한 간접 테스트만을 진행할 것 같다. 또한 Reflection을 이용해서 private 메소드를 테스트 하게 되면, 테스트 코드도 역시 유지보수 해야하는 코드이기 때문에 이 점에서 단점이 존재한다는 점을 알게 되었다. -> https://6161990src.tistory.com/125
  4. 해당 의견도 존재한다는 것을 찾아보면서 처음 알게되었고, 무슨 내용인지는 알겠으나 나한테는 와닿지 않다.

 

다양한 상황에서 다양한 의견들이 존재하고, 정답은 없다고 생각한다. 같이 개발하는 팀원들과 토론을 통해 규칙을 정하는 게 맞다고 생각한다.

 

하지만 private 메소드의 유닛 테스트 여부에 대한 답변은 많은데, 나의 궁금증인 TDD로 개발하면서 이전의 public 메소드가 어느 한 메소드에서만 포함되고 해당 클래스 내에서만 쓰일 때, private으로 바꾸고 해당 테스트 코드를 삭제해도 되는지에 대한 의견은 찾을 수 없었다. 따라서 '테스트 주도 개발' 책에서 테스트 삭제 관련 내용을 찾아보았다.

 

책에는 다음과 같은 질문과 답이 있었다.

 

테스트를 지워야 할 때는 언제인가?

  1. 첫째 기준은 자신감이다, 테스트를 삭제할 경우 자신감이 줄어들 것 같으면 절대 테스트를 지우지 말아야 한다.
  2. 둘째 기준은 커뮤니케이션이다. 두 개의 테스트가 코드의 동일한 부분을 실행하더라도, 이 둘이 서로 다른 시나리오를 말한다면 그대로 남겨두어야 한다.
  3. 추가로 별 부가적인 이득이 없는 중복된 테스트 두 개가 있다면 덜 유용한 것을 삭제하라.

 

테스트 코드를 지워야 할 때의 조언을 찾을 수 있었다. 온보딩을 진행할 때는 public으로 열어두지 않아도 될 메소드들을 열어두 었지만, 앞으로는 이럴 경우 테스트 코드를 지우고 해당 메소드를 private으로 리팩토링하는식으로 진행하고, private 메소드 테스트는 해당 메소드를 호출하는 public 메소드를 통한 간접 테스트만을 진행하려고 한다.

 

결과적으로 1주 차를 TDD로 개발을 진행해본 결과 느낀 점은 다음과 같다.

  1. 디버깅 시간이 줄어드는 것은 확실하다.
  2. 테스트가 실패했을 때, 버그를 보다 빠르게 찾고, 효과적으로 개선할 수 있다.
  3. 버그 발생 가능성이 낮아, 좀 더 견고한 프로그래밍이 가능하다.
  4. 해당 도메인을 잘 이해할수록 TDD가 더 수월하다.
  5. 객체지향적인 사고, 문제 이해 역량에 따라 TDD 난이도가 달라질 수 있다.
  6. 기존의 개발 방법보다 시간이 더 오래 걸린다.(하지만 오히려 버그 발생 가능성이 줄어들어서 장기적으로 볼 땐 시간적인 면에도 장점이 있을 거라고 생각한다.)

 

1주 차를 통해 얻은 인사이트로 나머지 프리코스도 TDD로 계속 도전하려고 한다.

 

 

2. 클래스 내 메소드 위치 컨벤션?

이 생각은 기존의 내가 개발을 진행해왔던 방법과, 프리코스에서 TDD를 진행하면서 클래스 내에 메소드 위치가 다르게 구성되어 의문이 생겨서 구글링을 해보았다. 클래스 변수 -> 인스턴스 변수 -> 생성자 -> 메소드 이러한 컨벤션은 이미 알고 있었고, 클래스 내 여러 메소드들 사이의 배치 순서에 대한 내용을 찾아보았다.

 

나는 메소드들을 접근 제어자 기준으로 작성하는 것보단 기능 및 역할별로 분류하여 다른 사람이 봤을 때 책을 위에서 아래로 읽는 것처럼 자연스럽게 읽히는 순서가 가장 이상적이라고 생각했다. 구글링을 해보니 내 생각과 동일했다.

 

전체 플로우를 정리해보자면,

  1.  public 주석 
  2.  클래스 
  3.  정적 변수 : public -> protected -> private 
  4.  인스턴스 변수 : public -> protected -> private 
  5. 생성자 
  6.  정적 메소드 : static 메소드 (main 메소드가 있다면 static 메소드 이전에 작성)
  7.  메소드 : 접근자 기준으로 작성하지 않고, 기능 및 역할별로 분류하여 기능을 구현하는 그룹별로 작성이 이루어지도록 해야 한다.  (public 사이에 private 메소드가 존재할 수 있다)
  8.  스탠다드 메소드 : toString, equals, hashcode 와 같은 메소드
  9.  getter, setter 메소드 : 클래스의 밑 부분에 위치 

 

7번이 나의 궁금증이었는데 해결이 되었다.

 

8번의 경우는 몰랐기 때문에 이번 온보딩에서는 신경 쓰지 못했는데, 다음 프리코스부터는 신경 써서 고려해봐야 할 부분이었고, 9번 경우 나는 개발할 때 setter를 아예 쓰지 않으며 getter 또한 dto에서만 사용하기 때문에 큰 연관은 없는듯하다.

 

https://stackoverflow.com/questions/4668218/are-there-any-java-method-ordering-conventions

http://daplus.net/java-java-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%88%9C%EC%84%9C-%EA%B7%9C%EC%B9%99%EC%9D%B4-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C-%EB%8B%AB%EC%9D%80/

 

 

3. Stream을 쓰는 이유?

프리코스 1주 차를 진행하면서 람다와 스트림을 적극 사용하기로 했다. 하지만 나는 모든 경우를 스트림으로 해결하기로 노력했고, 가독성에 장점이 있는 스트림이 오히려 가독성을 해치는 경우가 종종 발생했다. 무조건 스트림이 좋다고 생각한 내 생각에 의구심이 들어서 구글링을 통해 자료를 찾아보았다.

 

정리해보자면 아래와 같다.

  1. 스트림은 오히려 가독성을 해칠 수 있다.
  2. 스트림 내부에서 사용하는 함수 객체나 람다에서는 할 수 없는 일이 존재한다.(지역변수 수정, 반복문 도중 return, break, continue)
  3. 종료 조건이 있는 로직을 구현할 때 주의해야 한다.
  4. Stream의 forEach를 사용할 때, 기능적인 로직이 들어가 있는 경우 Stream을 잘 활용하고 있는 건지 의심해봐야 한다.
이펙티브 자바 아이템 46
에 따르면, 
forEach 연산은 최종 연산 중 기능이 가장 적고 가장 ‘덜’ 스트림답기 때문에, forEach 연산은 스트림 계산 결과를 보고할 때(주로 print 기능)만 사용하고 계산하는 데는 쓰지 말자...

 

이와 같이 내 생각이 틀렸음을 깨달았다. 온보딩 과정에서 어떠한 상황인지를 고려하지 않고, 무조건 스트림으로 해결하려고 해서 가독성을 해치는 경우가 있었다. 다음 프리코스부터는 무조건 스트림을 사용하기보다는 각 상황을 이해하여 스트림을 사용해야겠다.

 

https://stalker5217.netlify.app/java/stream-deeping/

https://tecoble.techcourse.co.kr/post/2020-05-14-foreach-vs-forloop/

 

 

4. Git 커밋에 대한 범위

기존에는 커밋을 할 때 클래스, 메소드 단위로 커밋을 주로 해왔다. 하지만 우테코 프리코스에서는 의미 있는 기능별 커밋에 대해 요구사항을 주고 있다. 하나의 기능을 구현하는데 3개의 메소드가 구현되었다고 하면, 기존에 했던 방법은 3번의 커밋이 발생했을 것이다. 또한 클래스 단위로 커밋을 했다면 여러 기능(메소드)들이 한 번에 커밋이 발생했을 것이다.

 

온보딩 과정을 통해 의미 있는 기능별로 커밋을 구성하다 보니깐 나중에 커밋 내역을 확인할 때 좀 더 직관적인 장점이 있었고, 버전을 되돌리기에도 더 유연했다. 그리고 프리코스 과정을 통해 Pull Request를 처음 경험하게 되었다. 우테코에 합격해서 백엔드 협업 과정을 겪는다면, 깃허브를 활용한 협업에 대한 경험을 더 할 수 있을 것으로 기대하고 있다.

 

 

5. 마무리

프로그래밍을 독학해오며 혼자 무엇을 공부해야 하는지 공부하는 것 조차 힘들었는데, 프리코스 미션을 진행하면서 자연스럽게 느낌 점과 궁금한 점, 부족한 점을 통해서 무엇을 공부해야하는지 깨닫게 되고, 이것들을 하나씩 채워갈 때마다 성장하고 있음을 느꼈으며 앞으로의 프리코스 과정도 기대가 된다.😊

공유하기

facebook twitter kakaoTalk kakaostory naver band