용로그
article thumbnail

랜덤한 숫자를 어떻게 뽑을 것인가


이번 4주차 미션은 마지막 미션 답게 구현에 필요한 몇몇의 클래스가 제시되었는데, 이 클래스들끼리도 바꿀 수 있는 것과 없는 것이 다 달라서 실제 구현 난이도가 체감상 조금 높았던 것 같다.

 

제시된 클래스들을 파악하며 처음으로 느꼈던 것은 BridgeMaker 클래스에서 어떻게 랜덤한 수를 가져올 것인가에 대해서 고민이 되었는데, 처음에는 Interface의 구현체가 없는 줄 알고 생성자 자체에 오버라이딩을 해서 구현을 했었다.

 

이번 우테코가 BridgeRandomNumberGenerator라는 클래스에서 직접 BridgeNumberGenerator를 구현해놓았었다. 그래서 BridgeMaker에서 생성자를 받을 때는 아래와 같이 받으면 랜덤한 숫자를 뽑을 수 있는 프로퍼티가 완성된다.

 

public class BridgeMaker {

    private final BridgeNumberGenerator bridgeNumberGenerator;

    public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
        this.bridgeNumberGenerator = bridgeNumberGenerator;
    }
}

 

확실히 까다로워진 요구사항


3주차 요구사항보다 훨씬 까다로운 요구사항들이 등장했다. 예를 들면 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다거나, 메서드의 파라미터 개수는 최대 3개까지만 허용한다는 것 등등.. 언뜻보면 그냥 지킬 수 있을 것 같지만 막상 코드를 짜다 보면 쉽지 않은 요구사항이라는 것을 알 수 있다.

 

이 외에도 BridgeGame 클래스에서는 View(InputView, OutputView)와 관련된 기능을 직접적으로 사용할 수 없다는 것을 나중에 알아서 그것대로 리팩터링을 진행하기도 했다.

 

하지만 까다로워진 요구사항만큼 메서드는 하나의 기능만을 담당하고 있고 다른 사람이 봐도 이해하기 어렵지 않은 코드가 된 것 같다. 이번주 요구사항 외에도 3주차 피드백으로 나온 부분들도 적용하려고 노력했다.

 

제일 좋았던 피드백 중 하나만 꼽아보자면 객체는 객체스럽게 사용하라는 피드백이었다. 지난주에 진행했던 로또를 예로 들면 Lotto라는 객체에서 데이터를 꺼내지(get) 말고 메시지를 던지도록 구조를 바꿔 데이터를 가지는 객체가 일하도록 해라는 것이다.

 

public class User {

    private boolean isGameClear = true;
    private boolean isRestartGame = true;

    public User() {
        pastResult = new HashMap<>();
    }

    public boolean changeStatusIsFailed(boolean result) {
        if (!result) {
            isGameClear = false;
            isRestartGame = false;
            return true;
        }
        isGameClear = true;
        isRestartGame = true;
        return false;
    }
}

만약 이러한 User 클래스가 존재할 때 changesStatusIsFailed와 같은 메서드를 서비스단에서 작성하면 User의 프로퍼티들은 은닉된 상태이기에 get을 사용해서 데이터를 가져와 비교해야한다.

 

하지만 위와 같이 User 클래스, 즉 객체 그 자체가 자신의 데이터를 가지고 비교하여 이에 대한 메시지를 던져준다면 더 객체지향적인 코드가 되지 않나 싶다.

 

현재 다리 상태에 대한 출력


유저가 다리를 건넌다면 성공 실패 여부에 따라 알맞은 출력이 되어야한다. 예시에서 다리는 위, 아래 2개로 나뉘어져 있고 성공하면 한 칸에 O, 실패하면 X를 출력한다.

 

여기에서 첫 번째 문제를 맞딱뜨렸다. 이전 다리의 상태에 대한 정보는 어떻게 알아서 View가 열심히 다리를 출력해줄 수 있을까? 내가 생각한 방법은 Map을 사용하는 방법이었다.

 

Key에는 몇 번째 다리(step)인가. Value에는 Key와 같은 다리에 대해서 위(U), 아래(D)에 대해서 정보를 저장했다. Map에 저장된 형태를 보자면 다음과 같다.

 

{ {1, U}, {2, U}, {3, D} }

 

위에 저장된 데이터를 토대로 OutputView가 다리를 한칸 씩 출력한다.

다리 프린트 로직

대략적인 다리를 출력하는 모습이다. 우선 printMap 메서드에서 지금 당장 건넌 다리가 성공인지 실패인지에 대한 파라미터, 유저의 정보, 현재 몇번 째 건너는 다리인지 step을 받는 모습이다.

 

그리고 이러한 정보를 토대로 다리를 출력한다. appendByPastBlock 메서드로 과거에 지나온 블럭들을 출력하고, 뒤에 현재 블럭을 붙인다. 그리고 마지막으로 print 할 String에 "]" 붙여서 출력하는 모습이다.

 

참고로 과거 Block에 대한 정보를 가지고 있는 Map은 User의 프로퍼티로 가지고 있는 상태이다.

 

테스트케이스 작성


나는 정말 테스트케이스 작성을 어려워한다. 그래도 우테코 프리코스를 진행하면서 나름 자바 기능에 대한 유닛테스트는 갈피를 잡을 수 있었다. 이제 마지막 과제인대도 불구하고 아직까지 테스트케이스를 작성하는 것에 대해 부담스러워하는 내가 조금은 실망스럽기도 하다.

 

특히 예외처리에 대한 검증에 대해서 많은 고민을 했는데, 비즈니스 로직에서는 한 번에 2개의 검증을 하나의 메서드에 묶어서 사용했다보니 각각의 예외처리를 진행할 수가 없었다.

 

이런식으로 진행했다.

이렇게 되다보니 자연스럽게 각각의 메서드의 접근제어자는 private으로 설정하고 Input에 대한 검증만을 할 수 있는 메서드만 호출할 수 있게 되었다.

 

따라서 테스트코드도 이렇게 작성할 수 밖에 없었는데

@DisplayName("입력받은 다리 길이가 정수가 아니거나, 입력받은 3이상 20이하의 수가 아니라면 예외가 발생한다.")
    @Test
    void 다리길이가_정수가_아니거나_3이상_20이하의_수가_아니면_예외_발생() {
        assertThrows(IllegalArgumentException.class, () -> {
            validateBridgeSize("10d");
        });

        assertThrows(IllegalArgumentException.class, () -> {
            validateBridgeSize("21");
        });
    }

이건 유닛테스트라고 볼 수 없을 것 같다. 하지만 이 이상의 방법이 떠오르지 않았다. 그렇다고 비즈니스 로직을 테스트케이스 때문에 바꿀 수는 없는 노릇이지 않은가.

 

이 부분에 대해서는 다른 크루들과도 대화를 나눠보고 싶다.

 

우테코 프리코스를 끝마치고, 내가 학교에 회의감이 든 이유 (신세한탄이 보기 싫으신 분들은 뒤로 가기!)


조금은 진지하고 무거운 얘기를 해보려고 한다. 1주 차 회고록에서는 우테코에 가고싶어하는 긍정적인 이유에 대해서 적어봤다. 오늘은 마지막 4주 차가 끝난만큼 내 속에 담아두고만 있었던 이야기를 해보려고 한다.

 

내가 다니는 학교는 부산소프트웨어마이스터고등학교라는 타이틀과는 다르게 극히 내신주의다. 나는 우리 학교가 애들한테 이것저것 바라는게 무책임하다고 생각이 든다. 

 

우리 반에는 전교에서 딱 한명 정시를 준비하는 친구가 있다. 우리는 흔히 그 친구를 정시파이터라고 부른다. 그 친구는 수능 공부 100% 코딩 0%이다. 확실히 대학을 갈 친구다.

 

그렇다면 평범한 다른 친구들은 비율을 어떻게 가져갈까. 내가 생각하기에 내신 50% 코딩 10% 기타 40%이다. 그래도 이 정도면 쌤들이 좋아한다. 내신을 중간 기말 중간 기말 다 챙겨주는 친구들이니까.

 

그러면 나는 시간을 어떤 비율로 사용할까. 솔직히 내신 0% 코딩 100%이다. 애들은 나를 보고 코딩파이터라고 부른다. 그리곤 정신을 놨다고 생각할 수 있다. 나도 1학년 때까지는 내신을 챙겼다. 소프트웨어개발과를 내신순으로 선택할 수 있었기 때문이다. 

 

하지만 지금은 상황이 다르다. 당장 내년에 개발자로 취업해야할 친구들한테 "개발자 직군은 내신이 중요하다.", "취업하려면 인문 공부를 해야한다." 지금 뭘가르치는지 조차 모르겠다.

 

 심지어는 개발자를 양성하는 학교에서 밤에 노트북도 뺏는다. 아직 언어도 못 뗀 친구들도 널렸다. 심지어는 우아한형제들이랑 협약이 맺어져 있으며, 내신 성적 순으로 우형이나 우테캠 등에 보내줄 것이라는 말도 거리낌 없이 한다.

 

학생들을 가르치는 선생님들 조차도 수준이 낮다면 낮을 것이고 근거 없는 바람과 정보처리산업기사와 같은 자격증을 따도록 강요한다. 누군가는 나 같은 뭣도 모르는 애송이가 쌤들을 평가하냐고 말할 수 있는데, 수업 때 가져오는 코드들이 전부 구글에서 쉽게 찾을 수 있으며, 수업 시작 몇 시간 전에 달달 외우고 온 듯한 느낌도 든다.

 

그리고 정처기를 따라는 의미는 무엇인가? 그냥 SI에 취직시켜서 취업률이나 올리려는 무책임한 마음가짐이다. 어쨋든 마이스터고니까 취업률은 높아야한다고 생각하겠지. 아직 애들은 SI가 뭐고 개발자로써 얼마나 좋지 않은 곳인지 모른다.

 

나는 구글에서 누구나 쉽게 찾을 수 있는 정보를 배우러 온 것도 아니고, SI나 가려고 이 학교에 들어온 것도 아니다. 큰 꿈이 있다. 어쩌다가 이런 얘기까지 왔는지는 이 한마디로 정의할 수 있다.

 

지금 있는 곳에 만족하지 못한다. 이 곳을 떠나 배울 수 있는 곳으로 가고 싶다.

 

이 때까지 마음에만 담아두고 있었던 이야기를 해보았는데, 그 누구에게 말해도 바뀌지 않을 시스템이라는건 알고 있고 이런 사정은 누구나 가지고 있다는 것도 알고 있다. 그래도 내 블로그에 당시 내 생각을 남겨보고 싶었다.

 

주변에 배울 곳이 없었던 나에게 많은 것을 배우고 성장해서 행복했던 한 달이라는 시간이 지나갔다. 덕분에 좋은 크루들이랑 대화도 많이 해봤고, 리뷰도 같이 하면서 처음으로 스터디도 참가해봤다.

 

내 스스로 성장했다면 성장했을 것이고 아쉬운게 있었다면 그랬을 것이다. 하지만 최종코테에 못간다고 해서 자책하진 않을 것이다. 그렇다고 아무렇지도 않게 일상으로 돌아오지는 못할 것이고, 그런 척도 별로 하고 싶진 않다.

 

우테코에 합격하지 못하면 당분간은 코딩을 못 할수도, 회의감이 들 수도 있다. 하지만 만약 떨어진다면 그냥 털어버리고 빨리 일어나서 부족한 부분들을 채우며 성장하고 싶다.

 

이 때까지 함께 자라기와 성장이라는 것을 잘 알려 주신 크루분들, 코치분들 모두 다 정말 감사했습니다. 앞으로 원하시는 목표 다 이루셨으면 좋겠습니다.

profile

용로그

@용로그

벨덩보단 용덩 github.com/wonyongChoi05