본문 바로가기
우아한테크코스

우아한테크코스6기 프리코스 1주차 미션 후기

by 구본식 2023. 10. 24.

작년에 우아한테크코스를 처음 알게 되었고, 몰입이라는 교육 철학이 너무나 인상 깊어 이번 프리코스에 참여하기로 결정했다.

우아한테크코스의 몇 가지 후기를 보았을 때, 매 기수 미션은 동일한 거 같았다. 그래서 미션과 관련된 내용들이 나오면 창을 닫았었고, 프리코스 미션들을 온전히 내 힘으로 풀고, 부족한 점을 발견하자는 생각으로 프리코스에 참가했다.

 

1주 차 미션은 https://github.com/BonSik-Koo/java-baseball-6 에 적힌 야구 게임을 fork/clone 한 후, 요구사항을 만족해 미션 코드를 작성하는 것이었다.

제가 작성한 코드는 https://github.com/woowacourse-precourse/java-baseball-6/pull/947 를 참고 바랍니다.


1주차 미션 야구 게임

1주차 미션인 야구 게임은 단순한 기능의 미션이었고, 1~9까지 서로 다른 수로 이루어진 3자리 수를 사용자가 입력해서 맞추는 것이 핵심이었다.

 

미션을 읽으면서 어색한 부분이 있었는 데, 바로 기능 구현 목록을 작성하는 것이었다.

이전까지 프로그래밍을 하면서 필요한 기능을 생각만 했지 직접 문서로 적어보지는 않았다. 하지만 학부생 시절 전공과목을 공부할 때 학습 목차를 먼저 정리하는 습관이 있어서 조금 수월하게 진행했던 거 같았다.

또, 미션 프로그래밍 요구 사항 중, Java 코드 컨벤션을 준수해 프로그래밍하라는 규칙이 있었다.

이전까지 코드를 작성하면서 코드 컨벤션 규칙을 지켜 프로그래밍을 하지 않았지만(가장 기본인데..말이죠...), 공유해 준 문서를 정독하면서 지금까지 작성한 코드가 얼마나 부끄러운 코드인지를 스스로 반성할 수 있었다.

그중에서도, 1 depth 들여 쓰기, else 예약어 지양하기 등은 처음 알았었고, 클린코드 작성 방법에 대해 많이 공부할 수 있었다.


객체지향 원칙을 준수하여 프로그래밍해보자

프로코스 미션에 대해 작성한 코드가 작동하지 않으면 0점 처리가 되기 때문에, 돌아가는 쓰레기 코드를 먼저 만들어야 하는지, 처음부터 클래스를 작게 설계해 구현해야 하는지 고민이 많았다.

돌아가지 않는 코드는 아무 의미가 없으니 쓰레기 코드를 먼저 만드는 것도 맞는 거 같다.

하지만, 프리코스 각 미션은 약 일주일 간 진행할 수 있기 때문에, 확실한 역할과 책임이 분리되는 경우 클래스를 사용해서 초기 개발을 하자는 스스로의 기준점을 잡았다.

만약, 정답이 틀리거나 예외가 발생하면 최소한 각 클래스에 집중할 수 있고, 시간 효율이나 버그 발견 및 기능 추가 측면에서 더 효율적일 거라 판단했다.

 

또 무엇보다 아직 객체지향 프로그래밍에 대해 크게 자신감이 없었다. 

학부생 때 자바를 겨울 방학 특강으로 학습했고, 스프링 학습(인강)과 프로젝트를 진행하면서 야행성으로 부딪쳐가며 필요한 내용을 찾아가며 학습한 게 전부였다.

그래도 인턴을 하면서 몸소 느낀 지식을 더해서, 간단한 1주차 미션인 만큼 최대한 더 객체지향 원칙을 준수해 프로그래밍하는 연습에 초점을 맞췄다.

 

아래는 미션을 진행하면서, 중간중간 스스로 생각해 본 생각의 흔적이다.


가독성을 위해 Indent 1 depth를 고려하자

클린 코드 규칙에서 가장 인사이트를 얻은 부분이자 가장 어려웠던 부분이었다.(기본 원칙인데.. 왜 몰랐지..)

 

이전까지는 아무렇지 않게 for-if, for-for, while-if 문을 사용했었다. 프로젝트를 진행하면서, 코드가 한 번에 이해가 되지 않는다 등의 피드백을 받을 때 들여 쓰기 depth를 줄이는데 집중하지 않고, 변수/메서드 네이밍을 수정한다던지, 코드 수를 줄이기 위해 집중했다.

초기에는 이것이 중요한 지를 깨닫지 못했지만, https://tecoble.techcourse.co.kr/post/2020-06-10-one-level-of-indentation-per-method/ 와 미션 코드를 리팩토링 해보며 장점과 필요성을 분명히 배울 수 있었다. 

(우테코 홍보 아닙니당...ㅎ)

리팩토링 전 코드

공 번호를 담은 리스트를 인수로 받아 자신의(BaseBall 클래스) 공 번호들과 비교하는 메서드이다.

내가 봤을 때 충분히 직관적이고 이해가 한 번에 된다고 생각했다. 하지만 이는 코드가 짧을 때나 통하는 말이었다..!

해당 미션의 요구사항은 스트라이크/볼 인지만 판별하면 됐지만, 만약에 그 외의 경우를 판별해야 하는 경우가 생긴다면 if문을 또 추가했을 것이다.

이렇게 if문이 추가되게 되면 가독성도 떨어질뿐더러, 해당 메서드가 정상 동작하지 않을 시 어떤 부분이 잘못된 지 쉽게 알 수 없다고 생각했다.

리팩토링 후 코드

기존의 if문을 메서드로 분리했고, 훨씬 더 가독성이 좋다고 생각했다.

또한, 현재 미션이 아닌, 더 다양한 기능이 존재하는 상황에서는 해당 메서드의 재사용성도 증가할 것이라고 생각했다.

더불어, 코드 상 스트라이크/볼 필드는 Result 클래스 내에 존재하는 필드이므로, 해당 값의 증가를 Result 클래스에 부여하여 조금 더 객체 지향적이라고 생각을 했다.


메서드에 한 가지 일만 담당하도록 구현하자

미션을 진행하면서 나도 모르게 놓쳤던 부분이자 객체지향의 가장 기본적인 원칙이라 생각한다.

리팩토링 전 코드

기존 BaseBall 클래스 생성자에서 랜덤 숫자들을 생성한 후 클래스 필드 값으로 할당하는 방식을 사용했다.

그때 당시에는 아무렇지 않게 넘어갔지만, 리팩토링 단계 중 해당 메서드가 한 가지 일을 담당하는가? 에 대해 의문점을 가졌다.

생성자는 말 그대로 클래스를 생성하는 역할만 담당해야 하고, 랜덤 숫자 생성은 다른 일이라고 생각했다.

아래와 같이 수정함으로써, 생성자는 한 가지 일을 담당할 수 있었다.

리팩토링 후 코드

생성자는 단순히 BaseBall 클래스를 생성하는 역할을 수행한다.

그 후, 랜덤 번호 생성의 일은 어떤 클래스에게 역할을 위임해야 하는 지를 고민했다.

코드 내에 BaseBallGame 클래스는 BaseBall, 입력기, 출력기 클래스 등을 사용해서 야구 게임을 진행하는 역할을 수행하도록 작성했다.

BaseBallGame 클래스 private 메서드로 랜덤 번호 생성을 구현할 수 있지만, 이는 야구 게임을 진행시키는 일과는 또 다른 일이라고 생각했다.

그래서 별도의 생성기 클래스를 두어 랜덤 번호 생성을 분리했고, 아래와 같이 BaseBallGame 메서드 내에서 사용하도록 했다.


상수를 한 곳에 모으자

기존에는 특정 클래스에서 사용되는 상수를 클래스 자체에 추가했고, static 키워드를 통해 여러 객체가 하나의 메모리 영역을 공유하도록 하여 메모리 효율을 높이고자 했었다.

하지만, 기능을 구현하다 보니 여러 클래스에서 같은 상수 값이 사용되는 경우가 있었고, 같은 상수 값이라면 굳이 각각의 변수를 Stack 메모리에 할당할 필요는 없다고 생각했다.



사용되는 상수를 하나로 모아 유틸리티 클래스로 만들었다. 또한, class를 final로 선언하여 상속을 방지했고, 생성자를 private로 선언하여 묵시적인 생성자가 생기는 것을 방지했다.

평소에 상수를 모은 유틸리티 클래스를 무의식적으로 자주 사용했다. 하지만 왜 클래스를 사용하지? 인터페이스는 왜 사용 안 하지?라는 생각을 한 번도 깊게 해보지 않았다.

우아한테크코스 프리코스를 스스로 부족한 부분을 찾고 학습하자는 생각을 가지고 참여했기 때문에, 이번에 둘의 차이점을 공부해 보았다.

아래는 공부한 내용을 개인 노션 페이지에 정리해 본 내용입니다..!!

https://chivalrous-asparagus-831.notion.site/vs-72bda8b89c984dbdbfb33bfd6fdebe57?pvs=4

 

상수 인터페이스 vs 유틸리티 클래스

상수 인터페이스(Constant Interface)

chivalrous-asparagus-831.notion.site


기능 단위로 커밋하자

이때까지는 보통 여러 기능을 구현하거나, API를 완성하고 관련된 기능들을 커밋하는 방식을 사용했다.

이를 대수롭지 않게 생각했지만, 프로코스 내 디스코드 토론하기, 함께 나누기 채널에서 효과적인 커밋 방식에 관한 자료/의견들을 들으며, 기능 단위 커밋의 장점을 배울 수 있었다.

미션을 진행하면서 1기능 1커밋을 최대한 지키고자 노력했다.

하지만, 실제론 2~3 기능 1커밋한 경우도 발생했고, 다음 미션부터는 나아지도록 노력해야겠다...!!!


[+2023.11.1] 피드백을 통한 자아성찰

공통 피드백과 크루원들의 코드를 보면서 많은 것을 배울 수 있었다.

이에 1주차 코드를 스스로 다시 돌아보고, 느낀 점을 남겨보고자 한다. 

Result 클래스

  • 클래스를 캡슐화 목적으로 사용하는데, Getter()로 필드를 그대로 다 리턴해주게 되면 캡슐화를 지켰다고 할 수 있을까?
  • 결과 문자열을 내부 필드들로 만들어서 리턴해주는 게 어땠을까? 그러면 내부 필드 들을 외부로 공개하지 않을 수 있었을 것이다.
    • 스트라이크/볼 개수 따른 결과 문자열 값을 만든다면, 문자열 더하기 로만 만들 수 있을까?
    • enum을 선언하고, enum에서 문자열을 만들어주는 방법도 있을 수 있을 것이다. → 추상 메서드, 함수형 인터페이스 등을 사용해서.

검증

  • 검증 클래스를 통해, 입력 클래스 내 검증 메서드를 분리했으면 어땠을까?
  • 검증 클래스는 야구 게임이 실행되면서 지속적으로 입력을 받는다. 즉, 야구 게임의 라이플사이클이 동일한데 굳이 생성할 필요가 있을까? 유틸리티 클래스로 만들어서 사용하는 것은 어땠을까?
  • 검증 클래스는 단순히 입력한 문자가 숫자인지 등의 기본 값만 비교하고(메서드로 의미 표시하기), 1~9 범위, 중복 숫자 등 특정 클래스에 특화된 제약 조건은 해당 클래스의 책임으로 위임했으면 어땠을까?

입력 & 결과

  • 입력 클래스는 단순히 사용자로부터 입력만 받았으면 어땠을까?
  • 출력 클래스는 단순히 인수로 받은 값 혹은 고정 문자열 값을 콘솔 로그 등으로 출력하는 역할만 있었으면 어땠을까? → Result 클래스를 인수로 받는 것이 아니라
  • 입력/출력 클래스도 검증 클래스와 마찬가지로 야구 게임의 라이플사이클 내 항상 사용될 텐데 유틸리티 클래스가 어땠을까?

상수, 메시지 유틸리티 클래스

  • 유틸리티 클래스 사용 이전에 enum으로는 관리할 수는 없었을까?
  • 특정 클래스에 종속된 상수 값들은, 해당 클래스의 private static final필드로 선언하는 게 더 낫지 않았을까?
  • 상수 유틸리티 클래스로 분리하기 전에, 특정 클래스에 종속된 상수값인지, enum으로 관리할 수 있는지를 먼저 점검하자!

재귀함수

  • playRound()를 재귀함수가 아닌 반복문을 사용했으면 어땠을까
    • 재귀함수를 사용하면 코드 가독성은 좋아진다.
    • 재귀함수는 매개변수, 리턴값, 함수 종료 후 돌아갈 위치 값들이 스택 메모리에 저장되는 오버헤드가 발생하니, 반복문 보다 일반적으로 느리다. → stack overflow 위험.
    • 즉, 재귀를 사용하면 함수, 코드 길이를 줄일 수 있고 코드 가독성을 향상할 수 있지만, 무작정 사용하는 것보다는 stack overflow를 고려하여 재귀 깊이를 예측 가능한 경우 위주로 사용하는 것이 현명할 것이다.

1주차 소감

1주차 소감을 그림 한 장으로 표현할 수 있을 거 같다.

작년부터 관심 있던 교육기관 미션을 직접 해볼 수 있어서 시작할 때 설렘이 가득했다.

하지만, 현재 대학생 4학년이고 빨리 경험을 쌓아보고자 인턴을 병행하고 있어, 퇴근 후 틈틈이 미션을 하다 보니 몸과 마음이 너무 힘들었다.

몸이 힘들었지만, 스스로 클래스/함수를 설계하고 구현하는 과정 속에서 게임을 하듯이 딱딱 맞았을 때 매우 매우 재미있었다.

그리고 평소에 공부했었던 MVC 패턴 등의 디자인 패턴을 적용해 보기에는 1주 차 미션이 너무 간단한 미션이었다고 생각했다. 어쭙찮게 사용하지 않고, 명확한 명분과 이점이 있을 때 사용해야겠다고 생각했다. 

(나름 객체 지향으로 구현하다 보니깐, 자연스레 MVC 구조 느낌으로 구현되긴 했다...)

 

또한, 평소에 회고 블로그를 꾸준히 작성해 보자는 생각을 가지고 있었지만, 마음먹기처럼 쉽게 되지는 않았다.

프리코스 오리엔테이션에서 회고를 꼭 해보라는 추천이 있었고, 이번 기회에 작성하다 보니 글 쓰는 게 점점 재미있어지는 거 같아 스스로 뿌듯했다.ㅎㅎ

1주차 미션은 간단한 야구 게임이지만, 자바 코드 컨벤션, 클린 코드 규칙, 객체지향 설계를 지키면서 진행하다 보니 많이 배운 거 같았다.

회고록을 작성할 때는 다른 크루들의 코드를 볼 수 없어 아쉽지만, 제출 기한이 끝나면 얼른 코드들을 보고 싶다. 다음 주차 미션에도 지금처럼 하나라도 얻고 성장해야겠다.

(참고로 제 코드는 오로지 저의 생각으로 작성했으니 부족하더라도.. 양해.. 부탁드립니다...ㅎㅎ)