[우테코 6기 프리코스] 꾸준하기 1주차

새양·2023년 10월 19일
1

우테코 6기 일기장

목록 보기
1/16
post-thumbnail

👋 안녕하세요

우테코 6기 백엔드에 지원한 새양입니다.
기록을 함으로써 미래의 제 자신이 걸어왔던 길을 되돌아보며 얼만큼 잘 걸어왔는지 확인할 수 있도록 남겨봅니다.
제가 우테코에 합격 또는 불합격할지는 모르는 일이며 결과를 떠나 프리코스를 통해 제 자신이 얼마나 몰입을 했고 성장할 수 있는지 일기를 꾸준히 써 볼 생각입니다.

✍️ 꾸준하기

본가에서 제가 어렸을 때 적었던 일기를 보게 되었고 1학기에 한 권씩 매일 적었던 것을 보았습니다.
쉽지 않겠지만 그 때의 그 끈기를 한 번 생각해 보며 앞으로의 프리코스 동안 매일 그 날의 마지막으로 일기를 적으며 어제의 저와 얼마나 달라졌는지 꾸준하게 적어보려 합니다.
그럼 꾸준하기를 시작해 보도록 하겠습니다!


🚀 10월 19일 (목)

오늘은 기다리고 기다리던 우테코 6기의 프리코스가 시작되는 날입니다.
오후 2시에 유튜브에서 오리엔테이션을 1시간 가량 듣고 오후 3시에 이메일로 문제가 출제되었습니다.
첫 날인 만큼, 개발 환경을 세팅하여 프로젝트 실행까지만 할 계획입니다.

1 - IDE 설치

Java로 진행된다는 것을 확인하였으므로 IntelliJ라는 프로그램을 사용할 것입니다.
대학생 시절 학생신분을 이용해서 IntelliJ Ultimate를 사용했었지만 졸업 이후 사용할 수 없었습니다.
그리하여 VSCode를 사용하려 했지만 IntelliJ Community 는 사용할 수 있다고 하여 오랜만에 다시 써보려 합니다.

IDE? IDEA?

개발 관련 프로그램들을 IDE라고 부르던 것을 알았는데 IntelliJ 를 다운받을 때 IDEA라고 되어있어서 확실히 알아 짚고 넘어가려 합니다.
https://chat.openai.com/share/2faf284a-b704-4219-ba90-46f185fc5ca6
IDE는 직역 그대로 통합 개발 환경을 뜻하며, IDEA는 일반적으로 IntelliJ IDEA를 뜻한다고 하네요.


설치가 끝나 프로젝트를 여는 화면이 보이고 저는 아마 나중에 Get from VCS를 누르게 될 것 같아 보이네요!

2 - 과제 프로젝트 가져오기

Github에 있는 우테코 과제를 Fork하여 저의 저장소로 가져온 뒤 컴퓨터에 Clone 해보도록 하겠습니다.

프로젝트를 저장할 폴더를 열어 아래 명령어에서 [Github ID] 부분을 자신의 깃허브 아이디로 바꾼 뒤 실행합니다.

git clone https://github.com/[Github ID]/java-basebal-6.git


잘 가져와졌습니다. 이후 브랜치를 자신의 아이디로 생성해야합니다.

cd .\java-baseball-6\
git checkout -b [Github ID]

3 - IntelliJ에서 프로젝트 열기


VSCode에서 자주 봤던 것입니다. IntelliJ를 방금 설치해서 프로젝트를 열었으므로 프로젝트를 신뢰하여 제한된 모드가 아닌 정상적으로 개발할 수 있게 할건지 결정합니다.
체크를 함으로써 이후 같은 위치의 새로운 프로젝트를 열 때에도 항상 신뢰하도록 해주었습니다.

어머나, IntelliJ가 이렇게 아름다웠다니... 대학생때 보았던 화면과 너무 달랐습니다! 아니면 Android Studio 에서 받은 Theme가 여기서도 적용되는 것 일수도 있겠네요!
어쨋든 이렇게 프로젝트가 잘 불러와 졌고 왼쪽 위에 geoje라고 제 Github ID가 표시된 것을 보니 branch도 잘 생성된 것 같습니다.

인 줄 알았지만 바로 오류를 뿜어내는군요... 제 컴퓨터에 자바가 17버전이 아닌 것 같습니다.

4 - Java 17 설치 및 테스트

JDK(Java Development Kit) 17버전을 설치해줍시다!
https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html

설치가 잘 되었네요. IntelliJ를 재시작 해보도록 하겠습니다.

우측 하단에 진행되던 인덱싱이 끝난 후 자바가 없던 이전과는 다르게 좌측 섹션에 파일들이 프로젝트단위로 감싸진 것을 보게 되었습니다.
그리고 IntelliJ가 신기한 것이 중앙 하단을 보시면 Markdown에 코드 구문을 인식하여 실행할 수 있게 버튼이 생기더군요!

못참고 버튼을 눌러버렸습니다. 바로 오류와 직면 했지요... 하지만 자바가 잘 설치됨을 의미하기도 합니다! 그래야 테스트 결과가 실패했다고 볼 수 있었을 테니깐요! 하하

5 - 돌아보기

오늘은 과제에 적응하기 위해 새로운 개발환경 IntelliJ를 설치해서 시행착오를 살짝 겪으며 실행까지 완료했습니다. 내일부터 본격적으로 구현에 대한 요구 사항들을 확인한 뒤 개발에 임해보도록 하겠습니다!


🏃 10월 20일 (금)

1 - 과제 요구 사항

과제에 주어진 요구 사항들을 정리해보았습니다.
기능 요구 사항

  • 숫자 야구 콘솔 게임 구현
    대학교 1학년 과제 수준으로 구현은 금방 할 수 있어 보입니다.
  • 입력 예외 처리
    대학교 2학년 때 객체지향 프로그래밍을 배울 때의 Exceptionthrow하면 될 것 같습니다.

프로그래밍 요구 사항

  • 주어진 대로 사용
    JDK 17 사용, buidl.gradle 수정 불가, 프로그램 종료 시 System.exit() 사용 불가합니다.
  • 테스트
    테스트는 ApplicationTest 를 전부 통과 해야합니다.
  • Java Code Convention
    Google Java Style Guide를 기준으로 하되, 들여쓰기, 열 제한, 수직 빈 줄 등 몇가지 예외사항을 적용합니다. 디자인 패턴과 비슷한 느낌이네요. 저에겐 새로운 개념이고 앞으로도 계속 쓰일 것 같으니 확실히 짚고 넘어가도록 하겠습니다.

과제 진행 요구 사항

  • 구현할 기능 목록 정리
    기능을 구현하기 전 docs/README.md에 무엇을 구현할지 정리한 후 시작을 합니다. 이를 위해 문제를 읽고 정확히 이해하여 어떻게 설계할지 구상한 뒤 README에 적어보는 식으로 진행할 것 같습니다.

중요하다 시은 것 2개를 별로 표시해두었습니다. 이번 주차는 기능 요구 사항이 어렵지 않으니 별표 되어 있는 것을 중점으로 확실히 공부한다는 마인드를 가져야 할 것 같습니다.

2 - Java Code Convention

Google Java Style Guide를 기반으로 하되 우테코의 자바 스타일을 적용해 정리하였습니다.

  1. Introduction
    • class, member, comment 라는 용어알기
  2. Source file basics
    • 파일 이름은 최상위 클래스의 이름을 대소문자 구분하여 사용, 확장자로 .java 쓰기
    • 인코딩은 UTF-8로 하며, 들여쓰기로 Tab 문자 사용하지 않기
    • 특수한 이스케이프 문자는 8진수나 유니코드 대신 \t, \n 을 사용하기
    • 아스키가 아닌 문자들은 유니코드로 사용하기 "μs" > "\u03bcs"
  3. Source file structure
    • 패키지, 참조, 명시 및 한 파일에 최상위 클래스는 하나로 하기
    • 패키지명은 나중에 나올 한 줄 최대 글자 수 100을 지키지 않아도 되지만 여러줄이 되어서는 안됨
    • 필요 시 라이센스, 저작권 명시
    • 각 섹션 분리 시 정확히 하나의 라인으로 구분
    • 와일드카드 * Import 안됨
  4. Formatting
    • if, else 등을 쓸 때 비어있거나 단일문이여도 중괄호 붙이기
    • 여는 중괄호 { 앞에는 줄바꿈 X, 뒤에는 줄바꿈 O
    • 닫는 중괄호 앞에는 줄바꿈 O, 뒤에는 } else { 처럼 연결되는거 빼고는 다 줄바꿈 O
    • 한 줄에는 하나의 명령문만 사용
    • 한 열에는 120자 제한 (import, URL 등 제외 / Google은 100자이지만 우테코에서는 120자로 제한)
    • 블럭 들여쓰기는 +4 공백, 들여쓰기 다음 라인 지속될 경우 +8 공백
    • 선언 당 하나의 변수 int a, b; > int a;
    • Annotation을 클래스나 메서드에는 각 라인마다, 필드에는 한 라인으로 사용
    • 여러 라인 주석 시작 라인에는 /* 만 끝 라인에는 */ 만 사용하고 사이에 내용을 * 를 제일 앞에 붙이고 작성
  5. Naming
    • 일반적으로 알파벳azAZ, 숫자09, 밑줄_ 만을 사용
    • 클래스명은 UpperCamelCaseImmutableList 와 같이 지정
    • 함수명은 lowerCamelCasesendMessage 와 같이 지정
    • 상수명은 UPPER_SNAKE_CASE와 같이 대문자와 밑줄로 사용 (이 때, 상수에 지정된 타입과 초기화한 것을 확실히 하여 상수인지 잘 판별해야 함)
    • 파라미터명은 lowerCamelCase
    • 지역변수명은 lowerCamelCase
    • 흔히 하는 실수로 XMLHTTPRequest > XmlHttpRequest에서 XML이 약자라고 전부 대문자로 사용하면 안됨 Xml 로 써서 CamelCase를 지켜야 함
  6. Programming Practices
    • @Override 를 항상 써야하며 부모 함수가 @Deprecated일 경우 생략 가능
    • try ~ catch ~ 구문을 사용하여 예외 처리를 할 때 예외가 잡혔으면 보통은 어떠한 행동을 하는 것이 일반적이고 정말 아무런 조치가 필요 없다 판단될 때는 주석을 사용 (Exception이 생겨야만 하는 상황에서 catch 했을 경우 비워도 됨)
    • 정적함수는 인스턴스로 호출하는 것이 아닌 해당 클래스명을 참조하여 호출 aFoo.aStaticMethod() > Foo.aStaticMethod()
    • finalize 는 Override 하지 말 것 (finalize 함수는 객체에 대한 참조가 더 이상 없다고 판단할 때 가비지 컬렉션이 호출하는 것)
  7. Javadoc
    • 여러줄 주석과 비슷한 문구인데 시작에 별표가 하나 더 들어간 /** 로 시작하며 끝은 */ 마무리하는 포맷으로 작성함
    • @param, @return, @throws, @deprecated 4개는 기준이 되는 블럭 태그들은 항상 작성되어 있어야 함
    • 간단한 Javadoc을 작성할 때 흔희 하는 실수의 예로 /** @return the customer ID */ 는 틀린 문구이고 /** Returns the customer ID. */로 작성 되어야 함
    • Javadoc은 모든 public 클래스와 클래스 내의 public 또는 protected 멤버에 존재한다.
    • overrides 하는 함수에는 필수가 아님

코드 컨벤션의 내용을 적다보니 편의를 위해 썻던 와일드카드 import나, 잘 모르고 무작정 사용했던 XMLHTTPRequest 같은 CamelCase 실수에 대해 되짚어 볼 수 있었습니다.
/** 로 시작하는 것이 Javadoc인지 처음 알게 되었으며, 예전에 막 사용했었던 적이 있는데 이번에 처음 형식적으로 사용해 볼 수 있을 것 같습니다.
이 외에도 직정 정리하고 작성하며 학습한 것이 많습니다.

3 - 돌아보기

오늘은 주어진 과제에 대해 확실히 파악하고 앞으로도 적용해서 사용해야할 개념인 코드 컨벤션에 대해서 공부를 해보았습니다.
이 때까지는 제가 공부해서 누군가에게 알려주며 얕은 지식을 제 것으로 만들었다면 이번 기회를 통해 기록을 함으로써 제 것으로 만들 수도 있겠다 라는 느낌을 받을 수 있었습니다.


🏃 10월 21일 (토)

코드를 작성하기에 앞에 프리코스 커뮤니티인 디스코드 채널에 같은 백엔드를 지원한 다른 크루원분께서 올려주신 Commit Convention 에 대해서 공부를 해보려 합니다.
평소 개발은 하고 커밋을 할 때 Git Flow 개념만 사용하여 main, develop, feature 브랜치만 사용했으며 squash 같은 기능도 사용하지 않아 결국 main 몇 백개의 커밋이 들어가곤 했습니다.
이번 기회에 확실히 습관을 바로 잡고 올바른 커밋 방법을 익혀 이 더러운 커밋 들을 더이상 만들지 않도록 다짐하는 시간이 되었으면 합니다!

1 - Conventional Commits

우선, 커밋 컨벤션을 사용하기 위해 아래 사이트에서 Summary 부분을 쭉 이해하고, 코딩을 시작 하도록 할 것입니다.
https://www.conventionalcommits.org

무엇 인가?
커밋 컨벤션은 커밋 메세지에 추가되는 간단한 규칙 이라고 하네요.
몇 가지 규칙을 추가 함으로써 명백한 커밋 기록을 관리할 수 있고, 자동화된 도구를 더욱 쉽도록 사용할 수 있는 듯 합니다.
커밋 메세지에 기능, 수정 사항, 주요 변경 사항을 작성함으로써 시맨틱 버전 관리와 딱 들어맞다고 하는데 시맨틱 버전 종류로 MAJOR, MINOR, PATCH가 있는 것으로 보아하니 정식 출시 전에 알파 베타 테스트와 같은 그런 것 인가 봅니다.
우선 커밋 메세지는 아래와 같다고 합니다.

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

예제로 보는 것이 더 확실히 이해될 것 같습니다.

type에 올 수 있는 것들

[ 'build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test' ]

기본적인 커밋 메세지

feat: allow provided config object to extend other configs

Breaking Changefooter에 작성한 경우

feat: allow provided config object to extend other configs

BREAKING CHANGE: `extends` key in config file is now used for extending other config files

느낌표를 넣어 Breaking Change 표현

feat!: send an email to the customer when a product is shipped

여기서 Breaking Change란
기존 코드와의 호환성을 깨뜨려 잠재적으로 문제가 생길 수도 있는 커밋을 말한다고 하네요.
https://chat.openai.com/share/b8d12d83-fd4d-49dc-b231-c2f84c37a3d8

타입 뒤에 오는 괄호 부분 optional scope 표현

feat(lang): add Polish language

여러 단락을 이용한 커밋 메세지

fix: prevent racing of requests

Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.

Remove timeouts which were used to mitigate the racing issue but are
obsolete now.

Reviewed-by: Z
Refs: #123

유의 사항

  • 처음 개발을 시작할 때에도 이미 출시 된 것 처럼 커밋 메세지를 작성하는 것이 좋음
  • type은 대소문자 구분 없지만 일관적인게 좋음
  • 한 커밋이 2개 이상의 type을 포함한다 생각되면 뒤로 돌아가서 커밋들을 분리시켜 줄 것
  • 이거 쓰다가 개발 시간이 느려진다 생각할 수 있지만 여러 프로젝트 협업에서는 기여자들과 함께 더욱 빠르게 개발할 수 있을 것임 (실제로 이런 것들을 잘 안지키면 나중에 공부하게 될 코드 리뷰 라는 것에 어려움을 겪을 것 같아 보이네요..) ⭐

사용하게 될 것
type으로는 fix, feat, docs 를 많이 사용할 것 같아 보입니다.
Javadoc을 작성해야 하며, 기능을 만들었을 때 feat을 쓰고, 오류가 수정되었을 때 fix를 사용될 것으로 보이네요!

기능 하나하나 만들 때 마다 이를 잘 적용해 commit을 할 것에 유의하며 개발을 시작해 보도록 하겠습니다!

2 - 클린 코드

제일 제일 중요한 클린 코드 작성에 최대한 주의를 기울여야 합니다. 저 또한 막 코드를 작성하고 동작 되기에 급급하며, 지역 변수를 사용하기 위해 콜백 함수를 익명 으로 그냥 처리한다던지의 경우가 많아서 들여쓰기도 많은 복잡한 코드를 만들기 일수였는데요. 이번 기회에 이 부분도 확실히 바로 잡을 필요가 있다 생각됩니다.

이 또한 Discord 나누기 채널에 백엔드에 지원한 다른 크루원 분의 글을 보고 댓글 또한 하나씩 전부 읽어가며 공부를 했습니다! 지식 공유에 힘써주시는 분들 정말 대단하다 느끼며 또한 정말 감사드립니다!

클린코드 규칙

  • 자바 코드 컨벤션을 지키면서 프로그래밍했는가?
    • IntelliJ 또는 Eclipse 통합 개발 도구에서 formatting을 한다.
  • 한 함수(메서드)에 최소한의 들여쓰기(indent)만 허용했는가? 최대 depth : 2 까지만 허용
  • else 예약어를 쓰지 않았는가?
  • 모든 원시값과 문자열을 포장했는가?
  • 콜렉션에 대해 일급 콜렉션을 적용했는가?
  • 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
    • 쉽지 않은 연습일 수 있다. 가능하면 인스턴스 변수의 수를 줄이기 위해 노력한다.
  • getter/setter 없이 구현했는가?
    • 핵심 로직을 구현하는 도메인 객체에 getter/setter를 쓰지 않고 구현했는가?
    • 단, DTO는 허용한다.
  • 메소드의 인자 수를 제한했는가?
    • 4개 이상의 인자는 허용하지 않는다.
    • 3개도 가능하면 줄이기 위해 노력해 본다.
  • 코드 한 줄에 점(.)을 하나만 허용했는가?
    • 디미터(Demeter)의 법칙(“친구하고만 대화하라”)을 지켰는가?
    • 예를 들어 location.current.representation.substring(0, 1)와 같이 여러 개의 점(.)이 등장하면 리팩토링할 부분을 찾아본다.
  • 메소드가 한가지 일만 담당하도록 구현했는가?
  • 클래스를 작게 유지하기 위해 노력했는가?

추천 사항

  • 코드를 작성한 이후에 지키지 않은 컨벤션은 어떤 것이 있는지 천천히 회고하며 수정하기
  • setter는 해당 객체 내부 값을 수정하여 예기치 못한 사이드 이펙트가 많이 발생할 수 있기에 지양하며, getter는 주소로 관리하는 참조 객체의 경우 값을 수정했을 때 실제 객체에도 영햐이 있기 때문에 자제하는 것이 좋음

3 - 기능 목록 정리

평소 같았으면 과제를 받고 문제를 이해하면 즉시 코딩에 들어가 기능 구현할 생각에 들떠 두려움 없이 코딩을 시작했을테지만 오늘은 느낌이 완전 다릅니다.
Java Code Convention, Conventinal Commits, Clean Code 3가지에 대해 쭉 읽어서 이해한 후 이를 전부 지키며 코딩을 하려니 솔직히 좀 막막한 느낌이 들 수 밖에 없는 것 같습니다.
새로움에 도전하는 것에 두려움은 없기 때문에 일단 부딪혀 깨지면서 배워나가도록 하겠습니다!
하지만 아직 코딩에 들어갈 수 없습니다.

기능을 구현하기 전 docs/README.md 에 구현할 기능 목록을 정리하라는 과제 진행 요구 사항이 있습니다!

최소한의 기능 하나만 하는 함수들로 모두 쪼개어 보았으며 아래와 같은 결과가 나왔습니다.

함수명

  • 설명: 블라
  • 인자: 블라블
  • 리턴: 블라블라

4 - 돌아보기

평소와는 다르게 구현할 것 들에 대해 미리 생각하고 문서화 한 뒤 진행한 다는 것이 쉽지 않았습니다.
하지만 이 시간들이 내일 실제 코딩에 있어서 확실히 큰 도움이 될 것이라 생각이 듭니다.
기능 목록을 정리한 것을 내일 코딩 시작 전 바로 커밋 컨벤션을 적용하여 저장소로 푸쉬해보고, 메인 함수를 작성하며 필요한 함수를 하나씩 만들 때 마다 커밋을 또 하는 식으로 진행하보려고 합니다.


🏃 10월 22일 (일)

오늘은 구현할 기능 목록을 정리한 후 이 때 까지 공부했던 것을 적용하여 본격적인 코딩을 시작 할 것입니다.

1 - 구현할 기능 목록 정리

기능 요구 사항들을 읽어본 뒤 게임을 개발하기 위해서는 컴퓨터가 수를 뽑는 기능, 사용자의 입력을 컴퓨터의 수와 비교할 수 있는 다른 자료구조로 바꾸는 등 것들이 당장 떠오릅니다.
그리하여 총 4가지 큰 기능들을 만들어야 겠다 생각이 되었고 이 중 결과가 2개의 수이기 때문에 클래스를 만들고 클래스 내에서도 toStringOverride 하여 결과를 쉽게 출력할 수 있도록 해주는 것도 넣으면 좋을 것 같다고 생각되었습니다.

docs/README.md 를 모두 작성한 뒤 해당 파일만 체크하여 아래 커밋 메세지로 진행해 보았습니다.

docs: 구현할 기능 목록 정리

Github 로그인을 해줍니다.

IntelliJ로 돌아오니 Push가 잘 된 것을 볼 수 있었습니다.

Github 에도 정상적으로 반영된 것을 확인하였습니다.

2 - 코드 작성

우선 정리한 목록 중 첫번째 기능을 만들어 보고 커밋을 해보겠습니다.

1부터 9까지 서로 다른 수 3개를 만들어 주는 기능

    public static void main(String[] args) {
        // 게임 시작 문구 출력
        System.out.println("숫자 야구 게임을 시작합니다.");
        // 컴퓨터 숫자 생성
        List<Integer> computer = createComputerNumberList();
    }

    /**
     * 컴퓨터가 3자리의 수를 만들 때 사용 됩니다.
     * @return 1부터 9까지 서로 다른 수 3개가 담긴 리스트
     */
    public static List<Integer> createComputerNumberList() {
        List<Integer> computer = new ArrayList<>();
        while (computer.size() < 3) {
            int randomNumber = Randoms.pickNumberInRange(1, 9);
            if (!computer.contains(randomNumber)) {
                computer.add(randomNumber);
            }
        }
        return computer;
    }

javadoc을 사용함으로써 함수 위에 마우스를 올리니 설명이 편리하게 나옵니다!

한글 깨짐

게임 시작 문구가 잘 나오는지 실행해보니 한글이 깨지는 것을 확인할 수 있었습니다. 파일 인코딩은 UTF-8로 되어있는 것을 보니 다시 검색을 통해 intelliJ 의 인코딩 세팅을 해줘야 할 것 같습니다.

프로젝트 인코딩이 UTF-8 이 아닌 것을 확인하였고 VM Options까지 수정 후 다시 실행해보겠습니다.


문자열을 숫자 3개가 담긴 리스트로 변환 해주는 기능
우선 생각대로 작성을 해보았지만 이후 실제 실행해 보았을 때 문제가 생기게 되면 fix commit 을 만들어 고쳐보도록 하고 일단 새로운 feat commit 을 하나 생성하도록 하며 계속 진행해 보겠습니다.

    /**
     * 숫자로 이루어진 문자열을 정수 리스트로 변환해 줍니다.
     * @param numbersStr 숫자 3개가 이루어진 문자열
     * @return 1부터 9까지의 서로 다른 수 3개가 담긴 리스트
     * @throws IllegalArgumentException
     *         입력된 문자열 길이가 3이 아닐 경우,
     *         숫자가 1부터 9까지가 아닐 경우,
     *         중복된 숫자가 존재할 경우
     */
    public static List<Integer> getThreeNumbersFromString(String numbersStr) {
        if (numbersStr.length() != 3) {
            throw new IllegalArgumentException();
        }
        List<Integer> result = new ArrayList<>();
        for (char numberChar:numbersStr.toCharArray()) {
            if (numberChar < '1' || numberChar > '9') {
                throw new IllegalArgumentException();
            }
            int number = numberChar - '1' + 1;
            if (result.contains(number)) {
                throw new IllegalArgumentException();
            }
            result.add(number);
        }
        return result;
    }
feat: 문자열을 숫자 3개가 담긴 리스트로 변환 해주는 기능

결과 클래스 생성
ComparisonResult.java

public class ComparisonResult {
    int ball;
    int strike;

    public ComparisonResult(int ball, int strike) {
        this.ball = ball;
        this.strike = strike;
    }

    /**
     * 볼, 스트라이크 개수에 따라 결과 문자열을 만들어줍니다.
     * @return 예시 1: <code>1볼 1스트라이크</code><br>
     *         예시 2: <code>3스트라이크</code><br>
     *         예시 3: <code>낫싱</code>
     */
    @Override
    public String toString() {
        if (ball > 0 && strike > 0) {
            return String.format("%d볼, %d스트라이크", ball, strike);
        }
        else if (ball > 0 && strike == 0) {
            return String.format("%d볼", ball);
        }
        else if (ball == 0 && strike > 0) {
            return String.format("%d스트라이크", strike);
        }
        return "낫싱";
    }
}

숫자 리스트 비교
3번째 기능을 구현하며 새로운 정보를 또 학습하여 기록해보려 합니다.
숫자를 담는 것들을 List<Integer> 로 사용하였는데 Integer 객체의 여러가지 특이한 점이 있었습니다.
아래 사진과 같이 == 연산자를 사용하여 비교하려 하니 eqauls() 함수를 써서 비교하라고 되어있더군요.

자바 상수 캐쉬
Integer 형일 경우 127 까지는 == 연산자로 처리 되지만, 128부터는 처리가 되지 않습니다.
자바에서 -128 ~ 127 사이의 Integer 값을 캐쉬하기 때문입니다.
https://devfoxstar.github.io/java/integer-compare-equals

==char 자료형 범위 내의 값들만 캐쉬처리되어 비교가 가능하지만 그 외의 값들은 안되니 equlas() 를 사용하는 것으로 확인했습니다.

따라서 .intValue() 를 사용하여 == 비교하면 괜찮아 진다고 합니다!

Application.java

    public static void main(String[] args) {
        // 게임 시작 문구 출력
        System.out.println("숫자 야구 게임을 시작합니다.");

        // 컴퓨터 숫자 생성
        List<Integer> computer = createComputerNumberList();

        // 사용자 입력 값 숫자 리스트로 변환
        System.out.print("숫자를 입력해주세요 : ");
        String userInput = Console.readLine();
        List<Integer> user = getThreeNumbersFromString(userInput);

        // 컴퓨터와 사용자의 숫자 비교
        ComparisonResult result = CompareNumberList(computer, user);
        System.out.println(result);
        System.out.println("컴퓨터 숫자 리스트: " +
                computer.stream().map(Object::toString).collect(Collectors.joining()));
    }

    ...

    /**
     * 두 리스트를 비교하여 볼과 스트라이크의 개수를 찾아냅니다.
     * @param computer 컴퓨터의 숫자 리스트
     * @param user 유저의 숫자 리스트
     * @return 볼과 스트라이크 개수를 포함한 결과
     */
    public static ComparisonResult CompareNumberList(List<Integer> computer, List<Integer> user) {
        if (computer.size() != 3 || user.size() != 3) {
            throw new IllegalArgumentException();
        }
        int ball = 0;
        int strike = 0;
        for (int i = 0; i < user.size(); i++) {
            if (user.get(i).intValue() == computer.get(i).intValue()) {
                strike++;
            }
            else if (computer.contains(user.get(i))) {
                ball++;
            }
        }
        return new ComparisonResult(ball, strike);
    }

중간 점검
여기까지 중갖 점검으로 숫자를 한번 입력 받아 볼과 스트라이크 결과, 그리고 컴퓨터의 숫자가 무엇이였는지 알려주는 프로그램을 만들어보았습니다.
실행하여 정상적인 값과 이상한 값을 넣어보도록 하겠습니다.

정상 값

이상한 값



세번째 기능이 구현 완료되었습니다. commit & push 하겠습니다.

feat: 숫자 3개가 담긴 두가지 리스트를 비교 하는 기능

3 - 구현할 기능 목록 수정

구현 막바지일 때 4번째 구현할 기능인 결과와 사용자의 입력 여부에 따라 게임 진행을 결정하는 기능 은 아래와 같이 세분화가 더 필요해 보였습니다.

  • 한 게임 내에 3스트라이크가 나올 때 까지 사용자의 입력을 반복하는 기능
  • 게임 하나를 진행하는 부분을 함수화
  • 게임의 시작, 진행, 재시작, 끝을 분리

옛날 이야기
마지막 분리 같은 경우가 제가 재일 좋아하는 부분인데요, 어렸을 때 게임을 만들 때도 GUI 기반으로 시작 중간 끝을 이벤트를 분리해서 제작하게 해주었고, 그것을 기반으로 계속 코드로 짜다보니 대학 1학년 때 C언어를 끝으로 간단한 게임 개발 과제가 주어졌을 때 시작 중간 끝을 잘 분리하여 체계적으로 개발을 함으로써 엄청난 자신감과 뿌듯함을 느꼈던 기억이 있습니다. 당연히 A+ 성적을 받았었죠!

그래서 docs/README.md 수정을 하여 조금 더 생각 후 세분화 해보았습니다.

## 4️⃣ 3스트라이크가 될 때 까지 사용자의 입력을 반복해서 받는 기능
무한 반복문을 통해 사용자의 입력을 받고 컴퓨터의 숫자 리스트와 비교해 3스트라이크가 나올 때 까지 게속해서 입력을 받고 비교합니다.

이 기능을 함수로 구분한 뒤 메인 함수 에서는 게임이 시작, 진행, 재시작, 종료 될 수 있도록 합니다.

## 5️⃣ 결과와 사용자의 입력 여부에 따라 게임 진행을 결정하는 기능
반복 게임 진행을 위해 구현 했던 것들을 무한 반복문으로 감싼 뒤 사용자의 입력에 따라 게임을 재시작 또는 종료 합니다.

3스트라이크 체크 함수 추가
ComparisionResult.java

    /**
     * 3스트라이크 여부를 확인합니다.
     * @return 3스트라이크 여부
     */
    public boolean isThreeStrike() {
        return strike == 3;
    }

멤버변수 접근 제한
ComparisionResult class의 접근 제한자를 private으로 두지 않아 default 가 적용되어 메인 함수에서 접근이 가능한 것을 알고 fix commit 을 작성했습니다.
또한 생성할 때만 값이 입력되므로 final 연산자까지 주어 한번 초기화 되는 상수 처리 하였습니다.
ComparisionResult.java

    private final int ball;
    private final int strike;

사용자 입력 반복 및 게임 진행 분리
이제 사용자가 입력을 반복할 수 있도록 3스트라이크를 체크 할 것이며, 게임 진행 함수를 메인에서 따로 분리하도록 하겠습니다.
Application.java

    public static void main(String[] args) {
        // 게임 시작 문구 출력
        System.out.println("숫자 야구 게임을 시작합니다.");

        // 게임 진행
        playBaseball();

        // 게임 종료
        System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
    }
    
    /**
     * 야구 게임을 진행합니다.
     */
    private static void playBaseball() {
        // 컴퓨터 숫자 생성
        List<Integer> computer = createComputerNumberList();

        // 사용자 입력 값과 컴퓨터 숫자 비교 후 3스트라이크 까지 반복
        ComparisonResult result = new ComparisonResult(0, 0);
        do {
            // 사용자 입력 값 숫자 리스트로 변환
            System.out.print("숫자를 입력해주세요 : ");
            String userInput = Console.readLine();
            List<Integer> user = getThreeNumbersFromString(userInput);

            // 컴퓨터와 사용자의 숫자 비교
            result = CompareNumberList(computer, user);
            System.out.println(result);
        } while(!result.isThreeStrike());
    }

4 - 게임 진행 점검

여기까지 게임을 1회 플레이 할 수 있도록 구현이 완료되었습니다.
여러가지 상황의 수를 두어 플레이를 해보도록 하겠습니다.
기본 플레이 - 성공!

게임 도중 이상한 값 입력 - 성공!

여기까지 해서 아래 커밋을 작성합니다.

feat: 3스트라이크가 될 때 까지 사용자의 입력을 반복해서 받는 기능

게임을 반복하는 기능
Application.java

    public static void main(String[] args) {
        // 게임 시작 문구 출력
        System.out.println("숫자 야구 게임을 시작합니다.");
        for (String replay = "1"; replay.equals("1");) {
            // 게임 진행
            playBaseball();

            // 게임 종료
            System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");

            // 게임 재시작
            System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
            replay = Console.readLine();
        }
    }

이상으로 마지막 기능 구현까지 끝났으므로 마지막 feat commit 을 작성합니다.

feat: 결과와 사용자의 입력 여부에 따라 게임 진행을 결정하는 기능

5 - 테스트 실패

순조로웠지만 테스트는 결국 실패하였다.

JUnit 테스트를 처음 사용해보는데 직접 실행 후 입력하고 결과 확인 할 필요 없이 입력 값을 정해두고 할 수 있다는게 엄청난 장점 처럼 보였습니다.
이번 과제를 제출 한 뒤에 JUnit 에 대해서도 공부를 해봐야 겠다는 생각이 들었습니다.
또한 랜덤 값을 사용하는데 테스트 때는 어떻게 일정하게 되도록 하였는지 의문도 생기게 되었습니다.
우선 테스트 실패부터 해결해보려고 합니다.

1볼 1스트라이크 가 나와야 하는데 1볼, 1스트라이크 가 나온 것을 확인할 수 있었습니다. , 하나 차이가 절 힘들게 하는군요.

ComparisionResult.java
if 문 내부를 수정해 주었습니다.

    @Override
    public String toString() {
        if (ball > 0 && strike > 0) {
            return String.format("%d볼 %d스트라이크", ball, strike);
        }
        else if (ball > 0 && strike == 0) {
            return String.format("%d볼", ball);
        }
        else if (ball == 0 && strike > 0) {
            return String.format("%d스트라이크", strike);
        }
        return "낫싱";
    }

테스트해 성공하였습니다.

6 - 돌아보기

오늘 이렇게 과제 구현을 마쳤습니다.
1주차인 만큼 간단한 구현으로 문제 풀이에 적응할 수 있도록 해준 것 같습니다.
하지만 끝이 아니며, 남은 시간을 얼마나 잘 쓸 것인가에 따라 저의 성장도가 달라질 것이며 커뮤니티에 올라온 다양한 글들을 통해 학습하고 피드백할 수 있으면 좋겠습니다.
또한 제가 작성한 코드를 되돌아보며 리팩토링이 필요한 부분을 수정해 나갈 것입니다.

그리고 gitmoji 라는 것을 써서 다음 주차에는 이모지를 포함한 커밋을 작성하면 더 가독성이 좋을 것 같네요.
https://gitmoji.dev


🏃 10월 23일 (월)

오늘은 이 때 까지 코딩 했던 것들을 리팩토링 하기 위해 Google Java Style Guide 를 한번 더 살펴보며 Clean Code 에 대해서 공부해볼 예정입니다.

1 - 클린 코드

우선 클린 코드를 작성한다는 것이 Style Guide 에 맞도록 작성하는 것에 중점을 두고 있습니다. 최소한의 들여쓰기, else 없도록, getter/setter 사용 안하기 등등 기본적으로 알고 있던 것들은 그대로 사용을 이어 나갔고, 여러군데에서 알아 본 뒤 아래와 같은 것들을 이번에 또 학습할 수 있었습니다.

  • 클래스 내에 3개 이상의 인스턴스 변수를 두지 않기
  • 메소드의 인자를 최소한으로 줄이기, 되도록 3개 초과 하지 않게
  • 코드 한 줄에 점(.)을 하나만 썻는지 체크, 여러 점이 있을 수록 객체 내부에 깊이 접근하는 것을 의미하므로 이를 최대한 피해야 함
  • 모든 엔티티를 작게 유지하기, 50줄 이하의 클래스 및 10개 이하의 파일을 갖는 패키지

여러 내용이 있고 세부 내용들도 의미있었습니다.
협업에 있어서 가장 중요시 되는 부분들 인 것 같아 추려 보았습니다.
남들이 저의 코드를 보았을 때 이해가 잘 되도록 하는 것에 중점을 두어야 했던 것을 간과하여 개발해온 저 자신한테 되돌아 보는 학습 시간 이였습니다.
대학 시절 당시 과제를 상대적으로 빨리 처리한 후 친구들이 과제에 대하여 힘들어 할 때 필요한 부분을 알려주거나 그래도 힘들어 하면 그 부분만 제 코드를 보여주는 식으로 하였는데 그 친구가 과제를 하는 것 보다 제 코드의 의미를 파악하는 데에 더 어려움을 겪지는 않았을까 생각이 듭니다.

2 - 돌아보기

클린 코드 자체가 Java Style Guide 를 따라가는 것 같아 먼저 공부 했던 내용을 생각하며 코드를 짜서 크게 변경할 부분은 없어보였습니다.
하지만 분명 변경해서 개선할 수 있는 부분이 있을 것이며 내일 Code Review 에 대해서 공부를 하고 2주차에 있을 코드 리뷰 시간에 다른 사람의 코드를 보며 리뷰해줄 건 해주고 좋은 사항이 보이면 습득 하여 나중에 작성하는 제 코드에 녹일 수 있도록 공부해보도록 할 것입니다.


🚩 10월 24일 (화)

1 - 코드 제출

다른 분들은 클래스 분리, 테스트 코드 작성 등의 코딩에 대한 실력들이 이미 좋았던 것을 많이 확인했습니다.
오늘 새로운 커밋이 생기면 오해할 소지가 생길 수 있기 때문에 다음 주차의 과제에서 더 많은 노력과 커밋 정리, 기능 정리, 테스트 코드 작성 등을 하려고 생각합니다.
과제 출시 날에 바로 20개 넘는 커밋을 작성하여 Pull Request 하는 분들도 있는 것을 보아 이밎 Spring Boot 에 충분히 적응을 하여 바로 코드를 뽑아낼 수 있는 것을 보았습니다.
저는 1주차 때 새로운 개념들에 대해 공부 정리 하느라 시간이 좀 걸렸다면, 2주차때는 줄치 때 부터 순서대로 잘 작성하여 더 많고 효율적인 코드 밋 커밋을 작성하도록 하겠습니다.

Pull Request

제출
과제 진행 소감에는 커뮤니티를 통해 얻은 것, 학습하며 느낀 것, 코드 작성 시 자바 스타일을 적용할 당시의 느낀 점에 대해서 읽기 좋도록 작성하였습니다.

2 - 돌아보기

코드를 제출할 때 다른 분들의 제출을 볼 기회가 있습니다.
철저히 형식화된 클래스로 나누신 분들, 테스트코드를 추가로 작성하는 분들 정말 다양하게 잘 하신 분들이 많아 저도 2주차에는 더 멋진 코드를 작성해야겠다고 다짐했습니다.
1주차의 코드리뷰를 보고 2주차에는 코드 리뷰 분석, 구현할 기능 더욱 세분화하여 체크리스트로 정리, 테스트코드 추가로 작성, 필요한 부분이 있다면 적극적으로 공부하여 반영 등을 할 것입니다.


🏃 10월 25일 (수)

오늘은 지피지기를 하기 위해 다른 분들이 제출한 코드를 보며 어떻게 배울 점이 있는지 살펴보며 필요한 부분을 정리해서 빠르게 학습 해보려고 합니다.

1 - Pull Request

다른 분들의 PR을 볼 수 있었기 때문에 1주차 미션 목록에 올라온 PR들을 보며 어떻게 구조화를 했는지 코드를 짯는지 염탐을 조금 하였습니다.

확실히 보길 잘했다고 생각한 것이 제가 작성한 코드가 굉장히 초라하게 보였고 이것은 만회하기 위해 더욱 잘 살펴보고 습득할 것을 최대한 가져가자 생각이 들었기 때문입니다.

아직 코드리뷰 기간이 아니기 때문에 단순 코드를 보고 어떻게 갈피를 잡을지 판단하기는 이르기 때문에 폴더 구조화를 어떻게 하였는지, 문서화를 어떻게 하였는지만 대략적으로 살펴보았습니다.

2 - 돌아보기

제가 작성한 코드는 너무나도 기능 구현에 초점이 맞춰져 있었고 단순 함수화 밖에 되지 않은 것에 비해 다른 분들은 구조화를 정말 잘 하였고, 그것을 본 받아 더 나은 프로젝트를 만들어 보겠습니다.

profile
안녕, 세상!

0개의 댓글