과제/우테코 6기 프리코스

우테코 6기 프리코스 안드로이드 - 1주차 미션

2023. 10. 25. 11:23
목차
  1. 숫자 야구 게임
  2. 과제 진행 순서
  3. 객체지향 설계
  4. 커밋 컨벤션
  5. 구현 및 테스트
  6. 클린코드로 리팩토링하기
  7. 과제 제출

 


 

숫자 야구 게임

올해 프리코스의 1주차 미션은 숫자 야구 게임 프로그램을 구현하는 것이다. 

리드미의 '기능 요구 사항', '프로그래밍 요구 사항', '과제 진행 요구 사항' 세 카테고리의 요구 사항을 만족하며 과제를 진행하면 된다. 

 

과제의 기능 구현은 간단하다.

하지만 코드 컨벤션, 예외처리, 테스트, 클린코드까지 고민하고 학습하다 보면 일주일이 금방 지나간다. 

그동안 개발을 하면서 기능 구현에만 집중했던 스스로를 반성했다.

 

주도적으로 학습하는 개발자, 협업에 능한 개발자가 되기 위한 기본적인 역량을 체감할 수 있었다.

무엇보다 오랜만에 순수하게 개발에만 몰입할 수 있어 즐거웠다.

'오랫동안 동료들과 즐겁게 일하며 성장하는 개발자'가 우테코가 지향하는 개발자의 모습이지 아닐까 하다.

 

짧은 시간동안 모든 것을 완벽하게 학습할 수는 없었다.

잘하고 있는 것인지 의심하고 고민하면서 내게 부족한 것을 파악할 수 있었다.

과제의 마지막은 스스로 회고하며 이러한 점들을 파악하고 정리하는 것이라고 생각한다.

 

1주차 과제의 기능 구현을 완료하며 진행과정과 배운 점을 정리하려고 한다.

프리코스 과정을 기술블로그에 작성하는 가장 큰 이유는 스스로 부족한 부분을 파악하기 위해서지만

어디서부터 시작해야 할 지 막막한 분들에게도 도움이 되었으면 한다.

 

 

 


 

 

과제 진행 순서

  1. 과제 요구 사항 파악
  2. 환경 설정    
    • 우테코 과제 레포지토리 fork & clone
    • IntelliJ IDEA 환경 설정 ( + 코틀린 코드 컨벤션 )
  3. 빠르게 구현해보기
  4. 설계
    • 객체지향 프로그램 설계
    • 구현 기능 목록 작성
  5. 커밋 컨벤션 정하기
  6. 구현 및 테스트
    • 기능 구현
    • 예외 처리
    • 테스트 코드
    • 기능 단위 커밋
  7. 클린 코드로 리팩토링하기
  8. 과제 제출해서 테스트 통과 확인
  9. 회고, 소감문 작성
  10. 제출 완료

 

과제는 이런 순서로 진행했다.

과정에서 정리가 필요한 부분들만 아래에 작성하려고 한다.

충분히 학습하지 못했던 부분은 추후에 개념별로 따로 작성할 생각이다.

 

 


 

 

객체지향 설계

설계에 대한 감을 잡기 위해 프로그램이 요구하는 기능을 빠르게 구현해보았다.

클래스를 나누고 파일을 분리하지 않고 예외처리 같은 것도 신경쓰지 않았다.

 

어떻게 시작하면 좋을 지 고민하다 객체지향을 공부하면서 읽었던 '객체지향의 사실과 오해' 책을 떠올렸다.

이 책에서 설명한 개념들을 적용해보고 체득하고 싶었다.

"프로그램은 적절한 책임과 역할이 부여된 객체들의 협력을 하는 공동체이다" 을 고려하며

  • 책임을 수행하는 역할을 가진 객체를 구상
  • 객체들이 서로 어떤 메시지를 주고 받으며 협력하는지 관계를 구축

이 두 관점에서 설계를 진행해보았다.

 

 

책임과 역할 부여

객체에게 책임과 역할을 부여하기 전에 사용자가 프로그램에 요구하는 책임들을 정의할 필요가 있다.

  1. 1부터 9까지 서로 다른 수로 이루어진 세 자리수의 숫자를 정한다.
  2. 2개의 숫자를 비교해 볼과 스트라이크 개수를 정한다.
  3. 게임 진행 메세지를 보여준다.
  4. 게임 플레이를 진행한다.
  5. 게임을 시작하거나 종료한다.

이제 정의한 책임들을 수행할 수 있는 역할을 정한다.

 

숫자 야구 게임은 두 플레이어 중 한 플레이어가 결정한 숫자를 다른 플레이어가 맞히는 게임이다.

기본적으로 다른 역할을 하는 두 플레이어가 필요하다는 것을 알 수 있다.

 

현실 세계에서는 두 명이 게임을 진행하기에 충분하다. 

하지만 프로그램에서 모든 책임을 플레이어에게 부여하면 구현 복잡성이 증가할 수 있어 게임 진행 책임을 수행할 역할을 분리한다.

 

따라서 구상한 역할은 아래 3개의 역할이다.

  • 숫자를 결정하는 플레이어
  • 숫자를 맞히는 플레이어
  • 게임 진행자

플레이어는 숫자를 결정하는 쪽과 숫자를 맞히는 쪽으로 구분되어 있다.

그리고 요구사항을 토대로 숫자를 결정하는 플레이어는 컴퓨터이며 숫자를 입력해 맞히는 플레이어는 사용자이다.

따라서 두 플레이어 모두 숫자를 정한다는 동일한 책임을 수행해야 한다.  ⇒ 책임 1

 

게임 진행자는 아래 3개의 책임을 부여했다.

  • 게임 진행 메세지를 보여준다  ⇒ 책임 3
  • 게임 플레이를 진행한다 ⇒ 책임 4
  • 게임을 시작하거나 종료한다  ⇒ 책임 5

게임 진행자는 게임 진행을 위해 볼과 스트라이크 개수를 알아야 한다.

그런데 플레이어들의 숫자 2개를 비교해 볼과 스트라이크 개수를 정하는 역할은 누가 맡아야 할까?

  1. 숫자를 결정하는 플레이어가 맞히는 플레이어의 숫자를 비교한다.
  2. 게임 진행자가 직접 두 플레이어의 숫자를 비교한다.

두 경우 모두 자연스럽지만 이는 요구 사항에 따라 달라질 것 같다. 

게임 진행자의 관점에서 게임 진행을 위해 직접적으로 필요한 것은 볼과 스트라이크 개수이다.

이 관점에서 볼과 스트라이크 개수를 정하는 책임은 숫자를 결정한 플레이어가 가져가는 것이 조금 더 자연스러울 것 같다. ⇒ 책임 2

 

 

클래스 타입, 메서드 정의

구상한 역할들의 클래스 타입을 정의한다.

  • 숫자를 결정하는 플레이어 ⇒ Computer
  • 숫자를 맞히는 플레이어 ⇒ User
  • 게임 진행자 ⇒ BaseballGame

그리고 부여한 책임에 따라 각 클래스 타입의 객체가 가진 기능들을 정의한다.

  • Computer
    • 1 ~ 9까지의 서로 다른 세 자리수 숫자를 자동으로 생성
    • 2개의 숫자를 비교해 볼과 스트라이크 계산
  • User
    • 사용자에게 1 ~ 9 까지의 서로 다른 세 자리수 숫자를 입력받아 숫자를 생성
    • 사용자에게 게임 진행 여부 결정하는 1 또는 2를 입력받기
  • BaseballGame
    • 게임 진행 메세지 출력
    • 게임 플레이 진행
    • 게임 시작 및 종료

 

Computer 객체와 User 객체는 "1부터 9까지 서로 다른 수로 이루어진 세 자리수의 숫자를 정한다"의 동일한 책임을 수행한다.

하지만 Computer 객체는 자동으로 숫자를 생성하며, User 객체는 입력을 받아 숫자를 생성하는 방식이다.

이처럼 동일한 책임이지만 어떻게 수행할 지는 요구사항에 따라 달라질 수 있다.

 

 

협력 관계 설정

마지막으로 객체들이 프로그램으로 동작하기 위해서 메세지를 통한 협력 관계를 설정해준다.

  • BaseballGame → Computer
    • 숫자를 결정해
    • 볼과 스트라이크 개수를 알려줘
  • Computer → User
    • 숫자를 알려줘
  • BaseBallGame → User
    • 게임을 종료할 지 지속할 지 알려줘

 

위와 같은 사고를 거쳐 객체지향 설계를 진행해보았지만 잘 한 것인지는 잘 모르겠다.

이런식으로 설계를 하고 프로그래밍을 한 것은 처음이다.

하지만 확실한 것은 방향이 정해져 개발을 체계적으로 진행할 수 있다는 것이었다.

 

지금은 결과만을 적은 것이지만 프로그래밍을 하는 도중 세부적인 설계 내용은 얼마든지 바뀔 수 있다.

그러니 너무 세세하게 하기 보단 방향성을 정하는 정도로 설계를 하는 것이 좋을 것 같다.  

 

커뮤니티에서 MVC 패턴으로 개발한 분들도 있던데 시간이 부족할 것 같아 디자인패턴은 적용해보지 않았다.

시간이 있으면 디자인 패턴도 고려해서 설계를 해보면 좋을 것 같다.

다음 과제나 시간이 있을 때 학습하고 시도해보려고 한다.

 

 

구현 기능 목록

설계를 토대로 구현해야 하는 핵심 기능들을 docs/README.md 파일에 작성하는 것으로 설계 과정을 마무리했다.

Computer
- 1 ~ 9까지의 서로 다른 세 자리수 숫자를 자동으로 생성
- 2개의 숫자를 비교해 볼과 스트라이크 계산
User
- 사용자에게 1 ~ 9 까지의 서로 다른 세 자리수 숫자를 입력받아 숫자를 생성
- 사용자에게 게임 진행 여부 결정하는 1 또는 2를 입력받기
BaseballGame
- 게임 진행 메세지 출력
- 게임 플레이 진행
- 게임 시작 및 종료

 

 


 

 

 

커밋 컨벤션

개발을 본격적으로 시작하기 전에 커밋 컨벤션을 정하고, 기능 단위 커밋에 대해 정의할 필요가 있다.

작업물의 진행 기록을 약속된 형식을 통해 공유하는 것은 협업에 있어 무엇보다 중요하다.

규칙에 따라 작업을 진행해야 작업의 효율성과 안정성을 높일 수 있다.

 

우테코 공개 레포지토리의 커밋 메세지를 참고해 커밋 컨벤션을 정했다.

 

<type>(<scope>): <subject> - Subject line

<body>
- Message body
  • type : 작업의 타입
  • scope : 작업 파일
  • subject : 구현 기능 또는 내용 한 줄 요약
  • body : 작업 내용에 대한 세부적인 내용 
<type>
feat : 기능 추가
fix : 에러 수정
docs : documentation 작성
style : 코드 스타일 수정
refactor : 코드 개선
test : 테스트 코드 추가
chore : 환경 설정 등 기타사항

<scope>
작업을 진행한 영역 : 작업 파일 이름을 작성

글자 수 제한 등 세부적인 규칙보다는 과제를 수행하는데 무리가 없을 정도로만 규칙을 설정하고 진행한다.

커밋 컨벤션을 위와 같이 정한 뒤 IntelliJ IDEA에 커밋 메세지 자동 포매팅을 설정해준다.

 

아래는 과제를 하면서 커밋 컨벤션을 토대로 커밋을 남긴 예시이다. 

feat(Computer, Player): 1~9까지 서로 다른 3개의 수 자동 생성

- Computer 클래스에서 generateNumbers 메서드 구현
- 1~9까지 서로 다른 3개의 수를 가진 리스트를 생성해 numbers 프로퍼티에 할당

"Computer 와 Player 파일에서 1~9까지 서로 다른 3개의 수 자동 생성 기능에 대한 기능을 추가했다" 의 의미이다.

 

이처럼 자신만의 커밋 컨벤션을 정한 뒤 작업을 진행하면 된다.

 

 


 

 

구현 및 테스트

환경 설정, 설계, 커밋 컨벤션을 정하는 것에 사실 시간이 제일 많이 쓰였다.

이후에 기능 구현은 빠르게 진행할 수 있었다.

구현 기능 목록을 따라 기능 단위 커밋을 하며 하나씩 기능을 완성해나가는 것에 몰입했다.  

 

 

기능 단위  커밋

과제 진행 방식에 '기능 단위 커밋'으로 개발 진행을 하라고 명시되어 있다.

작업을 하면서 변경 사항에 대해서 커밋으로 작업물을 기록해야 한다.

1000줄이 넘는 코드를 수정하고 한 번에 커밋하면 어떤 기능이 어떻게 수정되었는지 알 수 없다.

 

그러므로 최대한 작은 영역으로 변경 사항을 커밋하는 것이 여러 면에서 좋다.

기능 단위 커밋은 하나의 기능이 단위가 되어 추가, 수정, 삭제, 문서 작성 등의 행위를 했을 때 커밋을 남기는 방식이다.

기능 단위 커밋에 대해 제대로 이해한 것인지는 모르겠지만 아래와 같이 기능 단위 커밋을 진행했다.

 

만약 아래 기능을 구현한다고 가정하고 작업을 진행했을 때

Computer
-[ ] 1 ~ 9까지의 서로 다른 세 자리수 숫자를 자동으로 생성

"1 ~ 9까지의 서로 다른 세 자리수 숫자를 자동으로 생성" 하나의 기능을 구현하는동안 기능 추가, 오류 수정, 테스트, 코드 개선 등 다양한 행위가 나온다. 

이처럼 하나의 기능에서도 여러 파일에서 작업을 하거나 다른 행위의 작업을 할 수 있다.

주로 커밋을 남기는 시점은 작업 행위의 타입에 따라서 남기는 것이 자연스러웠다.

 

 

기능이 완전히 구현되었다고 판단되면 마지막으로 구현 완료 체크를 하고 docs 커밋으로 마무리했다.

Computer
-[x] 1 ~ 9까지의 서로 다른 세 자리수 숫자를 자동으로 생성

 

이렇게 작업을 진행하고 나서 커밋 로그를 본다면 위의 사진처럼 feat → ... -> docs → feat → ... → docs → 의 패턴이 보인다.

 

 

 

예외 처리

과제에서 요구하는 예외 처리는 사용자의 잘못된 입력에 대해서 예외를 발생시켜 프로그램을 종료하는 것이다.

Validator 라는 싱글톤 객체가 사용자의 입력과 생성된 숫자에 대한 유효성 검사를 진행하게 했다.

 

하나의 유효성 검사 로직을 담은 메서드를 나눠 validate... 메서드에서 한 번에 검사를 진행해 유효하지 않을 때 예외를 던지는 방식으로 코드를 작성하였다.

object Validator {
    // ...
    fun isInvalidNumericInput(input: String): Boolean {
        return !input.all { it.isDigit() }
    }

    fun isBlankInput(input: String): Boolean {
        return input.isBlank()
    }

    fun validateInput(input: String) {
        if (isBlankInput(input)) {
            throw IllegalArgumentException("입력이 올바르지 않아요.")
        }

        if (isInvalidNumericInput(input)) {
            throw IllegalArgumentException("숫자를 입력해주세요.")
        }
    }
    // ...
}

 

코틀린에서 예외를 발생시키고 예외 처리를 하는 방식에 대해서도 학습해보면 좋을 것 같다.

 

 

테스트 코드

이번 프리코스를 하면서 처음 테스트코드를 작성해보았다.

테스트코드를 작성해야 한다는 것은 알고 있었지만 이런저런 핑계로 미뤄왔는데 이번 기회에 실제로 해볼 수 있어서 좋았다.

 

테스트코드를 작성하면서 어떤 부분을 테스트해야 하는지 어디까지 테스트해봐야 하는지 모르겠다는 점이 난감했다.

그래서 이 부분은 한 번 테스트해보면 좋을 것 같다는 생각이 들 때 테스트코드를 작성하며 연습했다.

 

아래 테스트코드는 컴퓨터 객체가

  • 유효성 검사를 통해 예외를 발생시키지 않고 숫자를 생성하는지
  • 내가 작성한 로직이 볼과 스트라이크 계산을 올바르게 하는지

에 대해 테스트코드를 작성해봤다.

class ComputerTest {
		private lateinit var computer: Computer
    
        @BeforeEach
        fun setUp() {
            computer = Computer()
        }

        @Test
        fun `컴퓨터가 생성한 수에 대해 예외가 발생하지 않아야 함`() {
            assertDoesNotThrow { computer.generateNumbers() }
        }

        private fun runCalculateStrikeAndBallTest(
            userNumbers: List<Int>,
            expectedBall: Int,
            expectedStrike: Int
        ) {
            computer.setNumbersForTesting(listOf(1, 2, 3))
            val ballCount = computer.calculateBallCount(userNumbers)
            val strikeCount = computer.calculateStrikeCount(userNumbers)

            assert(ballCount == expectedBall)
            assert(strikeCount == expectedStrike)
        }

         @Test
        fun `0볼 0스트라이크 테스트`() {
            runCalculateStrikeAndBallTest(
                userNumbers = listOf(4, 5, 6),
                expectedBall = 0,
                expectedStrike = 0
            )
        }
        // ...
}

 

TDD, 단위 테스트, JUnit 등 테스트코드에 대한 공부가 필요할 것 같다.

  • 테스트코드를 왜 작성하는지
  • 테스트코드는 어떻게 작성하는지
  • 테스트는 어디까지 진행해야 하는지

 


 

 

클린코드로 리팩토링하기

클린코드는 소프트웨어 개발과 유지 보수 프로세스에서 중요하다.

프로그램이 잘 돌아가더라도 코드가 난잡하다면 기능을 추가하고 오류를 수정하기 점점 더 힘들어진다.

프로그램의 품질 향상과 협업을 위해 클린코드에 신경쓰며 개발해야 한다.

 

기능 구현을 완료하고 우테코의 클린 코드 원칙을 최대한 따르며 코드를 수정했다.

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

 

이거까지 수정할 필요가 있나 싶은 것도 다시 한 번 개선할 부분이 있는지 고민하였다.

그렇게 원칙을 준수하려고 따라가다보니 코드의 일관성를 개선할 수 있었고

코드를 읽었을 때 한 눈에 들어오는지 점검할 수 있었다.

 

기능 구현을 완료하면 위의 체크 리스트에 따라서 클린코드로 리팩토링 해보면 좋을 것 같다.

 

 


 

 

과제 제출

클린코드로 리팩토링까지 완료하고 과제 제출일이 되어서 테스트가 통과하는지 확인하였다.

 

다행히 잘 실행된 것 같다. 

 


 

 

여기까지 프리코스 1주차 과제에 대해 정리해보았다.

부족하다고 생각한 부분에 대해서 따로 학습하고 정리할 계획이다.

다음 과제부터는 이렇게 길게 정리하지는 않고 부족한 부분을 파악하는 것에 집중해서 작성할 것 같다.

 

1주차 과제를 했을 뿐이지만 많은 것을 느낄 수 있었다.

꼭 우테코에 붙어야지라는 생각보다는 과제 자체에 흥미를 느껴 개발에 몰입할 수 있었다.

그동안 개발을 많이 해왔다고 생각했지만 기본기가 부족하다는 생각을 계속 해왔다.

계속 고민하고 찾아가는 과정을 통해 부족한 점들을 발견할 수 있었고

그것들을 학습하고 정리하는 과정이 누적될수록 더 좋은 개발자가 될 수 있을 거라 생각한다.

 

 

 

GitHub - minuxx/kotlin-baseball-6: 숫자 야구 게임 미션을 진행하는 저장소

숫자 야구 게임 미션을 진행하는 저장소. Contribute to minuxx/kotlin-baseball-6 development by creating an account on GitHub.

github.com

 

728x90

'과제 > 우테코 6기 프리코스' 카테고리의 다른 글

우테코 6기 프리코스 안드로이드 - 3주차 미션  (0) 2023.11.08
우테코 6기 프리코스 안드로이드 - 2주차 미션  (0) 2023.11.01
  1. 숫자 야구 게임
  2. 과제 진행 순서
  3. 객체지향 설계
  4. 커밋 컨벤션
  5. 구현 및 테스트
  6. 클린코드로 리팩토링하기
  7. 과제 제출
'과제/우테코 6기 프리코스' 카테고리의 다른 글
  • 우테코 6기 프리코스 안드로이드 - 3주차 미션
  • 우테코 6기 프리코스 안드로이드 - 2주차 미션
minux.
minux.
minux.
minux.log
minux.
전체
오늘
어제
  • 분류 전체보기 (43)
    • 안드로이드 (5)
      • Jetpack (0)
      • 라이브러리 (2)
    • 프로젝트 (4)
      • 마스크 알라미 (4)
    • CS (12)
      • 운영체제 (6)
      • 자료구조 (3)
      • 네트워크 (3)
    • 코딩테스트 (11)
      • 알고리즘 (6)
      • 자료구조 (3)
    • Kotlin (6)
    • 과제 (3)
      • 우테코 6기 프리코스 (3)
    • 소프트웨어 공학 (2)
      • 아키텍처 (1)
      • 객체 지향 프로그래밍 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Repository 패턴
  • 우아한 테크코스
  • 클린 아키텍처
  • Ditionary
  • 프리코스
  • bfs
  • 네트워크
  • Android
  • greedy
  • Deque
  • SAM 인터페이스
  • 아키텍처
  • Kotlin
  • backtracking
  • generateSequence
  • apply
  • 시간복잡도
  • coroutine
  • brute-force
  • 함수형 인터페이스
  • 마스크 알라미
  • 리팩토링
  • 람다 함수
  • dfs
  • 운영체제
  • Collection
  • MVVM 패턴
  • Lifecycle callback
  • 자료구조
  • 공간복잡도

최근 댓글

최근 글

hELLO · Designed By 정상우.
minux.
우테코 6기 프리코스 안드로이드 - 1주차 미션
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.