Coderoad

우아한테크코스 레벨1 - 자동차 경주

2024-02-23 at 우아한테크코스 category

Welcome On Board!

길고 긴 우아한테크코스 선발 과정 이후, 저는 리건이라는 닉네임으로 우아한테크코스 6기 웹 백엔드 과정에 합류했습니다. 2024년 2월, 오리엔테이션에서 처음 받아본 온보딩 미션은 프리코스에서도 이미 한번 해결해봤던 자동차 경주였습니다.(또 하나 어마무시한 미션이 주어졌지만 혹시 이 글을 읽을지도 모를 미래의 크루를 위해 비밀로 하겠습니다.) 미리 경험해봤던 미션이라 그런지 요구사항도 크게 어렵지 않게 분석할 수 있었고, 금방 끝낼 수 있을 것 같다는 생각이 들었습니다. 그러나, 생각과는 다르게 이번 미션이 단순히 기능을 구현하는게 전부가 아니었습니다.

페어 프로그래밍, 우아한테크코스에서 맺어준 페어와 함께 이번 미션을 해결해야하는게 진정한 도전이었습니다. 지금까지 한번도 해본 적 없는 방식으로 개발해야한다는 것이 상당히 어색했습니다. 페어 프로그래밍은 드라이버(Driver)와 네비게이터(Navigator)가 한 팀으로 개발하는 방식을 말하는데, 드라이버는 직접 코드를 작성하는 사람, 네비게이터는 방향성을 제시하고 드라이버가 놓치는 부분을 찾아주는 사람입니다.

페어와 의견이 맞지 않으면 어떡하지, 혹시나 서로 감정이 상해 미션 시작부터 망쳐버리면 어떡하지라는 생각에 조금 긴장되기도 했습니다. 그러나, 제 첫 페어인 러쉬는 제 걱정이 전혀 쓸데없었다는 듯, 소통도 잘되고, 협업도 잘 됐던 페어였습니다. 서로의 개발 스타일을 고집하지 않고, 토론을 통해 서로를 설득하여 개발 방식을 결정했습니다. 메소드와 변수의 이름, 객체 설계, 문서 작성 등, 다양한 부분에서 정말 많은 대화를 나눴고, 예상보다 빠른 속도로 1단계를 완료할 수 있었습니다.

긴장됐던 코드 리뷰!

우아한테크코스의 미션 진행 방식을 간단하게 설명하자면 다음과 같습니다.

  1. 페어와 함께 1단계 미션을 해결한다.
  2. 리뷰어에게 코드 리뷰를 요청한다.
  3. 리뷰어의 리뷰를 바탕으로 리팩토링한다.
  4. 리뷰어가 미션 통과를 알리면 다음 단계 미션을 진행한다.

우아한테크코스를 수료한 선배들에게 내 코드를 리뷰 받는다니! 우아한테크코스에 지원하길 잘했다고 생각한 순간이기도 합니다. 아무리 페어와 함께 개발하면서 최대한 더 나은 방법이 무엇이 있을까 고민했어도, 훨씬 경험이 많은 선배들의 의견을 듣는 것은 차원이 다른 경험이라고 생각합니다. 실제로, 리뷰를 받고 그 리뷰를 바탕으로 코드를 리팩토링 해본 소감도 이 코드 리뷰는 둘도 없는 경험이라는 것입니다.

저와 페어 모두 완전히 놓치고 있었던 부분이나, 아예 몰랐던 개념, 잘못 생각하고 있었던 것들까지 배울 수 있는 것이 정말 많은 한 주였습니다. 주어진 시간 자체도 그렇게 길지 않았음에도 제 첫 리뷰어셨던 웨지는 DM으로 친절하게 리뷰를 요청하는 방법에 대해 설명해주시고, 아무리 긴 질문이나 간단한 질문이라도 자세하게 답변해주셨습니다. 궁금했던 점이 진짜 시원하게 해결되는 느낌이었고, 내가 고민한 내용들이 선배 크루들도 다 겪고 지나간 어려움이었구나라는 생각도 들었습니다.

다르게 보자면, 우아한테크코스에 열심히 몰입했을 때, 웨지와 같이 멋진 선배 개발자가, 누군가에게 좋은 영향을 줄 수 있는 개발자가 될 수 있겠다는 생각도 들었습니다. 첫 번째 미션을 무사히 끝내고나니 앞으로 무엇이든 해낼 수 있겠다는 자신감도 생겼습니다.

생각해볼 것들

이번 주 미션에서 학습한 내용에 대한 우아한테크코스의 질문과, 제 답변을 간략하게 정리해봤습니다.

내가 단위 테스트를 작성하는 이유는 무엇인가?

이번 자동차 미션의 부제는 단위 테스트였습니다. 그동안 TDD나 테스트 작성의 중요성에 대해서 들어는 봤지만, 실제로 개발에 적극적으로 테스트를 도입해본 적은 없었습니다. 항상 기능을 구현하는데 급급했고, 직접 프로그램을 실행해 하나하나 확인하면서 모든 예외 케이스를 체크했습니다. 당연히 예외가 발생하면 프로그램이 중지되니 매번 다시 프로그램을 시작해야했습니다. 개발 능률은 떨어지고, 놓친 예외는 없는지 확인하기도 힘들었습니다.

예외는 물론이거니와 잘못 구현된 기능이 존재하는지 찾는 것도 일이었습니다. 제 생각에는 제대로 구현된 것 같은데 알고보니 완전 잘못된 구현이었다는 걸 알아채는 것도 오랜 시간이 걸렸습니다. 심지어는 끝까지 찾아내지 못하다가 나중에서야 발견하기도 했습니다. 저는 제 코드를 전혀 믿을 수 없고, 항상 불안함에 살게 됐습니다.

버그 없는 코드는 없다지만, 충분히 예방할 수 있는 버그는 막는게 좋을 것입니다. 저는 버그를 예방할 수 있는 가장 좋은 방법이 단위 테스트라고 생각합니다. 즉, 제가 단위 테스트를 작성하는 이유는 제 코드를 믿을 수 있고, 안전하게 만들기 위해서입니다.

내가 작성한 좋은 단위 테스트는 어떠한 부분에서 좋은가?

저는 '좋은' 단위 테스트는 읽기 좋고 어떤 기능을 테스트하는지 한눈에 파악하기 쉬워야 한다고 생각합니다. 단위 테스트의 이름에서부터 알 수 있듯, 최소 개체를 테스트하는 것이 단위 테스트입니다. 보통은 한 메소드가 하나의 기능만 담당하는 것이 좋은 코드라고 여겨지니, 좋은 단위 테스트는 한 테스트에 하나의 기능만 테스트하는 것이 좋은 것 같습니다.

한번에 하나의 기능만 테스트하면 자연스레 읽기 편하고 어떤 기능을 테스트하는지 알기 쉬워질 것입니다. 물론, 저는 예외 상황에 대한 검증도 기능이라고 생각해, 각 예외 상황에서 제대로 예외를 발생시키는지 테스트하는 것도 단위 테스트라고 생각합니다. 이러한 기준을 바탕으로 이번 미션에선 최대한 한 테스트 메소드가 하나의 기능만 테스트하도록 작성했습니다.

이렇게 작성하고나니 테스트 코드만 보더라도 대략적으로 각 객체가 어떤 기능을 가지고 있고, 어떤 상황에서 예외가 발생할지 알 수 있게 되었습니다. 코드가 읽기 좋다는 건 그만큼 유지보수하기 쉽다는 뜻이기도 하니, 제가 작성한 테스트는 제 프로그램의 일종의 설명서 역할을 하는 점에서 좋은 것 같습니다.

좋은 단위 테스트를 작성하기 위해 어떠한 시도를 해볼 수 있는가?

좋은 단위 테스트를 작성하기 위해서는 일단, 구현 요구사항을 정확하게 이해하는 것이 중요하다고 생각합니다. 결국 단위 테스트는 구현한 기능이 의도한대로 정확하게 동작하는지 확인하기 위한 것이고, 개발자가 요구사항을 제대로 이해하지 못했다면 단위 테스트를 작성하는 의미가 없기 때문입니다. 시작부터 잘못 동작하는 기능을 구현하게 될텐데, 기능이 잘못 동작하는지 확인하는 테스트는 결코 좋은 테스트라고 볼 수 없습니다.

다음으로, 테스트 주도 개발(TDD)을 시도해 보는 것도 좋은 단위 테스트를 작성하기 위한 훌륭한 시도라고 생각합니다. 테스트 주도 개발은 먼저 실패하는 테스트를 작성하고, 실패한 테스트가 성공하도록 기능을 구현하고, 테스트가 성공하면 구현 코드가 깔끔해지도록 리팩토링을 시도하는 개발 방법론입니다. TDD를 도입하면, 자연스럽게 구현 요구사항을 상세하게 분석하여, 프로그램에서 가장 작은 단위인 기능들을 명확하게 정의하게 될 것입니다.

어떤 기능을 구현해야할지 명확해지면, 자연스럽게 객체의 역할과 책임이 명확해질 수 있고, 기능 구현 속도도 빨라질 수 있다고 생각합니다. 결국 좋은 테스트는 내가 작성한 코드를 완전히 신뢰할 수 있게 만들어주고, 새로 구현된 기능들이 제대로 동작하는지 빠르게 확인할 수 있어야 좋은 테스트입니다. 구현 요구사항의 정확한 이해와 TDD와 같은 방법론의 도입을 통해 테스트 작성의 효율을 극대화한다면 자연스레 좋은 테스트가 될 수 있을 것입니다.

자동차 경주에 단위 테스트를 얼마나 추가하였는가? 어려웠던 점은 없었는가?

이번 미션에서는 11개 단위 테스트를 작성하여 핵심 도메인 클래스 기준, 66%의 테스트 커버리지(Test Coverage)를 확보했습니다. 어려웠던 점은 프리코스에서부터 꾸준히 겪고 있는 랜덤 값과 같은 테스트하기 어려운 코드에 대한 테스트 작성이었습니다. 자동차 경주에서는 인터페이스를 활용해 자동차의 전진을 위한 숫자 생성 객체를 추상화하여 해결했지만, 피드백을 보니 메소드만 단순하게 분리해 더 간단하게 해결하는 방법도 있을 것 같다는 생각이 들었습니다.

예를 들어, 자동차의 전진에 필요한 숫자의 요구사항이 변경되지 않는다면, 너무 과한 추상화는 아니었는가라는 생각도 들었습니다. 확실히 인터페이스를 통해 테스트할 수 있게 된 것은 좋았지만, 테스트하기 위한 코드가 추가될 수 밖에 없었습니다. 테스트 코드 내부에 난수가 아닌 지정된 숫자를 생성하도록 하는 클래스를 새로 정의해야했고, 이 자체만으로 개발자의 에너지를 소모하는 일이었습니다. 조금 더 생각해볼 여지가 있는 부분이었습니다.

코드 품질을 위해 노력한 부분은 무엇인가?

코드 품질을 높이기 위해 그동안 알고 있었던 여러 지식과 Google Java Convention을 준수하면서 코드를 작성하려고 노력했습니다. 변수 이름을 지을 때는 자료형의 이름을 최대한 제외한다든지, 메소드 이름을 축약하지 않고 읽었을 때 어떤 기능을 할지 알 수 있도록 했다든지, public 메소드와 연관된 private 메소드들을 바로 아래 두어서 메소드가 어떻게 동작하는지 쉽게 파악하도록 했습니다.

코드의 품질은 결국 협업하는 동료 개발자들이 얼마나 읽기 쉬운가에서 온다고 생각합니다. 평생 혼자 개발하겠다는 사람이 아니라면, 필연적으로 다른 동료 개발자들과 소통하게 될텐데, 내가 읽어도 어렵고, 동료는 읽을 수 조차 없는 코드는 재사용은 커녕 유지보수하는 것도 힘들어질 것입니다. 그래서 여러 컨벤션들이 정해지고, 개발 방법론들이 생겨났다고 생각합니다.

선배 개발자들의 시행착오에서 탄생한 여러 컨벤션과 개발 방법론을 공부하는 것만으로도 이미 누군가 지나온 시행착오들을 가뿐하게 건너 뛰어 좋은 코드를 작성할 수 있을 것입니다. 그래서 저는 코드 품질을 높이기 위해 Google Java Convention을 준수하려고 노력했습니다. 이런 컨벤션을 작성한 선배 개발자들도 이런 후배 개발자들이 많아지길 원하지 않았을까요?

본인의 코드가 만족스러운가? 다음 미션에선 어떠한 목표로 코드를 작성할 예정인가?

이번 미션에서의 코드는 당연히 만족스럽지 않습니다.(아마 평생을 만족하지 못할 것 같습니다.) 리뷰어 웨지의 피드백이나, 다른 크루들의 코드를 읽어보니 제가 놓치고 있는 부분이 너무 많다는 생각이 들었습니다. 사소한 부분부터, 기능의 구현 방법까지 저보다 잘하는 사람은 정말 많은 것 같습니다. 그래도 정말 긍정적인 것은, 제가 앞으로 성장할 수 있는 부분이 아직 엄청 많다는 것입니다.

그래서 저는, 다른 크루들의 장점을 배우고, 리뷰어의 피드백을 적극 수용하면서 저만의 코드 스타일을 정립하고자 합니다. 크루들과 대화해보면 리뷰어들마다도 스타일이 달라 페어끼리 어떤 방식이 더 나은지 고민이 많은 것 같았습니다. 저 역시 계속해서 더 나은 방식은 무엇일까, 왜 내 리뷰어와 페어의 리뷰어는 의견이 달랐는지 많은 고민을 했습니다.

다음 미션에서는 이번 미션에서의 여러 피드백들을 적극 반영해볼 생각입니다. 또, 다른 방식으로 이번 미션에서 겪었던 문제를 해결하며 어떤 방식으로 해결하는게 더 나을지 리뷰어의 의견을 들으려합니다. 코치들끼리도 모두 스타일이 다르다고하니, 저는 정말 다양한 사람들의, 다양한 방법에 대한 의견을 듣고 제 스타일, 저만의 기준을 찾는데 더 집중해볼 생각입니다.

공부할 개념들

  • Fluent API
  • final
  • Google Java Convention
  • MVC 패턴
  • static
  • 객체지향 프로그래밍
  • 테스트 주도 개발
hangillee

Personal blog by hangillee.

Road to good developer.