조종 다음은 개발
article thumbnail

⚾️ 1 주차 미션은 숫자 야구!!

내가 좋아하는 무도에서도 했었다 ㅎㅎ

이번 6기 1주 차 미션은 온보딩 미션이 아닌 숫자 야구가 주어졌다!

https://github.com/woowacourse-precourse/java-baseball-6

 

GitHub - woowacourse-precourse/java-baseball-6

Contribute to woowacourse-precourse/java-baseball-6 development by creating an account on GitHub.

github.com

 

내 코드는 다음 링크에서 볼 수 있다. (열리는데 시간이 조금 걸릴 수 있다.)

https://github.com/woowacourse-precourse/java-baseball-6/pull/319

 

[숫자 야구 게임] 최원준 미션 제출합니다. by jhon3242 · Pull Request #319 · woowacourse-precourse/java-baseball

 

github.com

⭐️ 코드 리뷰

나는 1 주차 미션이 끝나자마자 바로 코드 리뷰를 받고자 하였다.

다른 사람들도 마찬가지인 듯하였다. 미션 종료 1시간 전부터 디스코드에서는 리뷰를 대기하는 사람들이 모여있었다.

그래서 나는 전략을 바꿔서 피드백을 드리겠다고 글을 올렸다.

너무나 많은 분들이 댓글을 달아주셔서 빠르게 마감하였다.

내가 피드백을 주었으면 그 사람도 나한테 피드백을 줄 가능성이 높다고 생각하였다 ㅎㅎ 그래서 위와 같이 글을 작성하였다.

그러자 30분도 안돼서 19 명의 동료들이 댓글을 달아주셨다.. 그래서 허겁지겁 더 이상 받지 않겠다고 글을 올렸다.

너무 많이 받으면 피드백을 다 못 드릴 것 같았다...😂

 

하지만 나는 총 30명의 동료들에게 리뷰를 드렸다 ㅎㅎ

리뷰를 통해 정말 많은 것을 배울 수 있었다.

 

생각보다 피드백을 드리는 것은 어려웠다.

물론 다른 사람의 코드를 읽는 것에 어려움이 있었지만, 무엇보다 피드백을 드릴 때 어떻게 말해야 내 의도를 잘 전달할 수 있을지 고민되었다.

"이렇게 말하면 너무 싹수없어 보이지 않나??", "이렇게 말하면 상대방이 기분이 나쁘지 않을까??"라는 생각이 계속 들어서 문장을 계속해서 수정하면서 피드백을 작성하였다.

역시 대면으로 피드백 주는 게 최고일 것 같다.ㅎㅎ (그래서 이후에 스터디에 참여해볼까 한다.)

 

그리고 나는 총 17 명의 동료들에게 피드백을 받았다.

(다시 한번 코드 리뷰를 해주신 17 명의 동료들에게 감사의 말씀을 전합니다!🙇‍♂️)

정말 많은 분들에게 피드백을 받다 보니 내 코드는 피드백으로 덮여 있어서 코드를 읽는데 어려움이 생길 정도였다.

심지어 내 PR 링크를 열려고 하면 너무 오래 걸려서 페이지가 열리지 않았다..

 

정말 정말 많은 것을 배울 수 있었다. 

배운 내용들은 2 주차에서 활용해 볼 생각이다!

 

 

🤔1주 차 미션동안 고민했던 것

나는 우테코를 준비하면서 이전에 풀어보았기 때문에 큰 무리 없이 요구사항을 파악하고 구현할 수 있었다.

그럼에도 여전히 고민하고 배운 것들이 있기에 여기서 한 번 정리해보려고 한다!

 

🧐 검증의 위치?

1주 차 미션을 진행하면서 가장 고민했던 것은 검증 클래스를 어디에 위치할지에 관한 것이었다.

기능 요구사항에서 사용자는 컴퓨터의 숫자를 맞추기 위해 숫자를 입력해야 하는데 이 부분에서 검증이 필요했다. 확인해야 할 검증은 다음과 같았다.

  • 빈 값인 경우
  • 숫자가 아닌 문자가 포함된 경우
  • 3 자리가 아닌 경우
  • 같은 숫자가 반복되는 경우

이러한 검증을 어디에서 하는 게 맞을지에 대해 많은 고민을 하였고 다양한 방법을 시도하였다.

1. View에서 검증

이 부분에 대해 고민하던 중 가장 처음 생각한 방법은 InputView 에서 검증하는 것이었다.

위 코드에 빠진 예외 처리(같은 값 반복 여부 등)는 추후 추가하였다.

위와 같이 처리해 주면 Controller 는 검증된 값을 받기 때문에 따로 추가로 검증해 줄 필요가 없어져 매우 깔끔해졌다.

그러나 위 코드에서 확인할 수 있듯이 InputView 가 “입력받기” + “검증하기” 두 가지의 책임을 가지게 되면서 코드가 복잡해졌다. 이 두 가지의 책임을 분리해 줄 필요성을 느끼게 되었고 이를 분리하였다.

2. 책임의 분리

예외 검증 로직은 Validator 라는 별도에 클래스를 만들어서 책임을 분리하였다. 이렇게 하니까 InputView 에 검증에 대한 메서드가 정의되어 있지 않기 때문에 깔끔해 보였다.

그러나 InputView 에 검증 메서드가 정의되어 있지 않다고 하더라도 InputView 내부에서 검증 메서드를 알고 가져와 사용하고 있기 때문에 여전히 검증에 대한 책임에서 벗어났다고 보기에는 힘든 것 같았다.

3. 검증 로직을 Controller로 옮기기

따라서 이를 해결하기 위해 InputView 에서 검증 로직을 실행하는 것이 아닌 Controller 에서 검증 로직이 실행되도록 리팩터링 해주었다.

컨트롤러 생성시 검증 인스턴스를 받기는 방식으로 하여 최대한 확장 가능하게 설계하려고 했다.
모든 검증에 대한 책임은 이제  Validator  에만 있다.
단순히 ‘입력 받기’에 대한 책임만 받도록 리팩터링 하였다.

이와 같이 리팩터링 하게 되면서 InputView 는 매우 단순하게 바뀌었고 ‘입력받기’에 대한 하나의 책임만 가지게 되었다. 그럼에도 여전히 생각해 볼 문제가 있는 것 같다.

  1. InputView존재가 무의미해진 것 같다. 이 상태라면 굳이 InputView 가 필요할까?
  2. 입력값 오류 시 다시 입력받으려면 로직이 매우 복잡해진다.

이와 같은 문제를 해결하기 위해 어떠한 방법을 사용하면 좋을지 계속 고민하였다.

4. 근본적인 문제에 대한 고민, 그리고 검증 구분

그동안은왜 책임을 분리하려고 하는가?에 대해 고민해 보지 않고 단순히 책임 분리에만 집중하여 최대한 클래스의 책임을 나누어서 분리하였다. 그러다 문득 왜 책임을 분리해야 하는지에 대해 집중해서 고민하게 되었다.

이에 대한 나의 답은 “변경의 영향을 최소화하기 위해서”였다. 그렇다면 비즈니스 로직과 상관이 없어서 변경할 가능성이 거의 없다면 굳이 책임을 분리해 줄 필요 없다고 판단하였다.. 그래서 나는 우선 검증을 두 가지로 분리하였다.

  1. 비즈니스 로직 검증
  2. 기초적인 검증

요구사항이 자주 변경되는 비즈니스 로직과 관련된 검증, 그리고 크게 변경 가능성이 없는 기초적인 검증으로 나누었다. 그리고 InputView 에 기초적인 검증은 남겨주었고, 비즈니스 검증은 기존대로 Validator 라는 별도의 클래스에서 관리해 주었다.

기초적인 검증이 많지 않기 때문에 별도의 클래스하면 오히려 복잡성만 늘어난다고 판단하여 InputView 내부에 구현해 두었다.

또한 메서드가 출력할 메시지를 받는 방식으로 리팩터링 하여 공용적으로 사용하도록 바꿨다. 이로써 두 가지 예외로 구분하면서 예외 발생 시 어느 클래스에서 발생했는지 파악하는 것만으로도 단순히 일반적인 예외인지, 비즈니스 예외인지를 구분할 수 있게 되었다. 또한 요구사항이 변경하더라도 InputView 를 수정할 필요가 없어졌다.

이번 경험을 통해서 근본적인 문제 해결에 집중해야 한다는 것을 깨달았다. 이러한 깨달음을 가지고 단순한 규칙에 사로잡히지 않고 더 가독성 있고, 더 유지보수하기 쉬운 코드에 대해 고민해 나가야겠다!!

👀 어떻게 하면 더 가독성 있게 작성할 수 있을까?

이번 프리코스를 기회로 가독성 있는 코드를 작성하는 능력을 많이 키워보고 싶었다. 그래서 최대한 코드를 가독성 있게 작성하려고 노력하였다. 아래 내용은 어떻게 하면 더 가독성 좋은 코드를 만들 수 있을지 스스로 고민하고 적용했던 내용이다.

1. 함수 분리를 통해 가독성 높이기

기능을 구현하고 난 뒤 Controller 를 살펴보는데 한눈에 어떤 작업을 하는지 보이지 않았다. 그래서 이에 대해 고민하던 중 게임 진행 메서드인 proceed 를 다음과 같은 단계로 나누어서 가독성을 높이는 방법을 생각하였다.

  1. 게임 초기화
  2. 게임 진행
  3. 게임 종료

그러나 리팩터링 과정에서 문제점이 발생한 것을 알았다.

게임 초기화 단계에서 초기화했던 computer게임 진행 단계에서 사용해야 하기 때문에 위와 같이 파라미터로 computer 를 받아야 했다. 지금 단계에서는 별로 큰 영향이 없어 보이지만, 만약 프로그램 요구사항이 증가하고 기능들이 추가된다면 매번 새로운 값이 파라미터가 추가될 상황이 발생할 가능성이 높아 보였다. 그래서 이 부분을 다음과 같이 개선해 주었다.

GameData 라는 모델을 생성하여 게임 초기화 , 게임 진행 에서 각각 접근하도록 리팩터링 해주었다. 이를 통해 새롭게 접근할 값이 필요한 경우 파라미터를 추가할 필요 없이 GameData 에 필드값만 추가해 줄 수 있다. 또한 미학적으로도 통일성 있게 바뀌어져 가독성이 올라갔다고 느껴졌다.

그러나 이 방법이 100% 정답이라고 생각되지는 않았다. 코드를 읽는 사람 입장에서는 어떠한 값이 넘겨지는지 한눈에 파악되지 않다. GameData 내부를 탐색해 봐야 알 수 있기 때문에 코드의 복잡성이 올라가고, GameData 의 필드 개수가 증가한다면 오히려 가독성을 해칠 수도 있겠다고 생각하였다. 어떠한 방법이 최선인지 계속 탐색해 봐야겠다.

 

이후에 JDK14부터 지원하는 record 를 사용해 보았다.

2. 함수화를 통해 가독성 높이기

코드를 읽다 보면 위와 같이 비교문에서 코드를 해석하는데 시간이 걸리면서 가독성이 떨어진다고 느껴졌다.

그래서 한눈에 어떤 작업인지를 파악할 수 있게 아래와 같이 따로 함수로 추출하였다.

또한 함수명을 최대한 어떤 작업을 하는지 알 수 있도록 명명하였다.

그러나 이러한 방식을 너무 많이 사용하면 오히려 함수 개수가 너무 많아져서 큰 구조로 볼 때는 가독성을 해칠 수 있다는 생각이 들었다. 이를 방지하기 위해서 함수로 추출할지 여부를 판단하는 명확한 기준을 세울 필요성을 느꼈다.

🏗️ 확장 가능한 설계

1주 차 미션의 요구사항은 간단하였다. 그래서 만약 여기서 기능이 확장되거나 변경된다면 어떻게 변할지 생각해 보고 확장 가능하게(변경하기 쉽게) 리팩터링 하기로 결심하였다.

우선 내가 기획자라고 생각하고 프로그램의 요구사항이 변한다면 어떻게 변할지 가정해 보았다.

다음은 내가 스스로 생각한 변경 사항이다.

- 숫자 개수의 변화 (3자리 수에서 5자리나, 2자리로 변경 가능)
- 허용 숫자의 변화 (1~9 에서, 0~5 까지만 허용 등으로 변경 가능)

이와 같이 변경 사항을 작성하고 실제로 변경해 보면서 어떤 점이 불편했고 어떤 식으로 수정하면 편하게 변경할 수 있을지 고민해 보았다.

1. 리팩터링 방식

상대방(컴퓨터)의 객체이다.
입력값을 검증하는 객체이다.

숫자야구 개수(3)는 생각보다 여러 객체에서 사용하고 있었다. 그렇기 때문에 만약 숫자 개수가 변한다면 여러 객체들을 탐색하면서 값을 수정해야 했다. 이렇게 되면 버그 발생 가능성도 높아지고 수정하는데도 많은 시간이 소요될 것 같았다. 그래서 다음과 같이 리팩터링 하였다.

위와 같이 게임 설정과 관련된 값을 필드값으로 가지는 GameOption 객체를 생성하였다.

그리고 위와 같이 숫자야구 개수를 사용하는 곳에서 해당 객체를 통해 접근하도록 리팩터링 하였다.

이렇게 리팩터링 하면서 단순히 GameOption 에서 값만 바꾸면 다른 곳에서 추가로 수정해 줄 필요 없이 요구사항이 변경되었다!

허용 숫자도 이와 동일하게 방법으로 리팩터링 해주었다.

 

 

나의 최종 코드는 다음 링크를 통해 확인 가능하다!

https://github.com/woowacourse-precourse/java-baseball-6/pull/319

 

[숫자 야구 게임] 최원준 미션 제출합니다. by jhon3242 · Pull Request #319 · woowacourse-precourse/java-baseball

 

github.com

 

1주 차 미션에서 배운 것

1주차 미션을 진행하면서 기본적인 개념들에 대해서 정리해 보는 시간을 가졌다. 미션을 시작하기 위해 사용하는 Git부터 미션 중 사용한 Stream, Collection 등등에 대해 정리해 볼 수 있었다.

git

미션을 시작하기 위해서는 git을 사용해야 했다. 물론 이전에 git 을 사용한 적이 있기 때문에 큰 무리 없이 미션을 시작하고, 제출할 수 있었다. 그런데 그동안 git에 대해서는 정리해 본 적이 없었던 것 같아 이번 기회에 정리해 보는 시간을 가졌다. 또한 git clone과 git fork의 차이점에 대해서 잘 몰랐기에 이에 대해서도 학습하고 정리하였다.

Git 의 기본 동작과 Fork vs Clone

 

Git 의 기본 동작과 Fork vs Clone

Git 이 뭐야?? 🧐 Git 은 소스 코드를 관리하기 위한 분산 버전 제어 시스템이다. 버전 제어 시스템은 사용자가 파일을 수정할 때 변경 사항을 기록하고 저장하므로 언제든 이전 버전의 작업을 복

flight-developer-stroy.tistory.com

이와 같이 정리하면서 git의 기본 동작 방식에 대해서 학습할 수 있었고 fork와 clone의 명확한 차이점에 대해서도 알 수 있었다.

record

미션에서는 JDK17을 사용하도록 되어있었다. 이전에는 계속 JDK11 로만 개발을 해왔어서 JDK11과 JDK17 사이에 어떠한 업데이트가 있었는지 알아보았다. 기타 여러 가지 기능들이 업데이트되었는데 그
중에서 JDK16부터 정식 스펙으로 포함된 record 가 눈에 띄었다. 그래서 record 에 대해 집중하여 알아보면서 정리하였다.

레코드(record) 누구냐 넌?

 

레코드(record) 누구냐 넌?

🤨 레코드(record) 누구냐 넌? 레코드(record)란 불변의 데이터를 쉽게 생성할 수 있는 새로운 유형의 클래스이다. JDK14에서 처음 소개되었고, JDK16에서 정식 스펙으로 포함되었다. 백문일불여일견,

flight-developer-stroy.tistory.com

이와 같이 배운 record 를 1주 차 미션에서 적용해보고 싶었다. 그래서

Assertions & NsTest

제공된 테스트 코드에서 이전에 보지 못했던 함수들이 많이 있었다. 나만의 테스트 코드를 만들기 위해서는 해당 함수들에 대한 이해가 필요하다고 생각하였다. 그래서 해당 함수들에 대해서 탐색해 보았고 이를 정리하였다.

https://flight-developer-stroy.tistory.com/41

 

Assetions와 NsTest를 살펴보니 겉으로 보는 것과 다르게 내부에서는 생각보다 여러 작업이 일어난다는 것을 확인할 수 있었고 새로운 개념들을 알 수 있었다. 처음 살펴보았을 때는 복잡해 보여서 겁부터 먹었는데 하나씩 차근차근 살펴보면 이해하는데 큰 어려움이 없었던 코드였다. 긴 코드나 공식 문서를 보면 지레 겁을 먹는 경우가 많았는데 이번을 기회 삼아 천천히 살펴보는 습관을 가지도록 노력해야겠다!

이 외에도 stream, collection, MVC 패턴 등 여러 가지 개념에 대해서 학습하는 기회를 가졌다. 이를 학습하고 정리했던 글은 다음과 같다.

collection : https://flight-developer-stroy.tistory.com/39

 

컬렉션 프레임워크(Collection Framework) 은 뭐지??

🚀 컬렉션 프레임워크(Collection Framework)란? 자바에서 컬렉션 프레임워크란 다수의 데이터를 쉽고 효과적으로 처리할 수 있도록 표준화된 방법을 제공하는 클래스의 집합을 말한다. 즉, 데이터를

flight-developer-stroy.tistory.com

stream : https://flight-developer-stroy.tistory.com/38

 

자바 스트림(Stream)이란 무엇이고 왜 쓰는건가?

자바 스트림(Stream)이란?🧐 스트림은 JDK8부터 추가된 기능 중 하나로, 컬렉션 인스턴스에 함수를 조합하여 원하는 결과를 필터링하고 가공된 결과를 손쉽게 처리할 수 있는 기능을 제공한다. 함

flight-developer-stroy.tistory.com

MVC : https://flight-developer-stroy.tistory.com/37

 

MVC 패턴에 대해, 그리고 이를 지키는 규칙

항상 MVC 패턴에 대해 들어봤는데 제대로 알아본 적이 없던 것 같아서 이번 기회에 제대로 알아보려도 이 글을 작성하게 되었다. MVC 이전 시대 우선 MVC 패턴이 나오게 된 배경에 대해 이해하는 게

flight-developer-stroy.tistory.com

 

🚧 보완해야 할 점

 

1주 차 미션이 주어지자마자 빠르게 구현하고자 구조 설계를 하지 않고 바로 코딩하였다.

그래서 명확한 설계 없이 코드를 작성하다 보니 중간에 문제가 발생하였다.

 

1. 자주 변경되는 구현 방식

우선 구현 방식이 수정되는 일이 자주 발생하였습니다. 이런저런 방법을 시도하면서 구현하니까 시간을 낭비하는 경우가 많았다.

 

2. 테스트 코드 활용 부족

비즈니스 로직을 분리하지 못했습니다. 명확한 설계 없이 코드를 작성하다 보니 중간중간 여러 객체에서 비즈니스 로직을 구현하였고 그렇다 보니 특정 비즈니스 로직에 대한 테스트 코드를 제대로 활용하지 못했다.

 

이러한 문제점들을 보완하기 위해서 다음 주차 미션부터는 코드 작성을 하기 이전에 설계에 대해 깊이 생각해 봐야 할 것 같다. 그리고 명확하게 비즈니스 로직을 구분하여 테스트 코드도 제대로 활용하도록 노력해 봐야겠다!

 

 

profile

조종 다음은 개발

@타칸

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!