우아한테크코스 3주차 회고

리브리버·2022년 11월 14일
0

우테코회고

목록 보기
3/4
post-thumbnail

2주차에 대해 공통 피드백이 도착하였다.

공통 피드백은 아래와 같았다.

  • README.md를 상세히 작성한다.
  • 기능 목록을 재검토 한다.
  • 기능 목록을 업데이트 한다.
  • 값을 하드 코딩 하지 않는다.
  • 구현 순서도 코딩 컨벤션이다.

2주차의 주요한 피드백은 아래와 같다.

  1. 클래스(객체)를 분리하는 연습
  2. 도메인 로직에 대한 단위 테스트를 작성하는 연습

첫 번째로는

내가 할 수 있는 클래스를 분리하는 연습에 최대한 집중을 해보았다.

제시된 기능들을 구현 시 클래스를 분리하는 연습에 집중하여 클래스별 1 기능만 수행할 수 있도록 작성하니 가독성이 높아지고 이후 수정이 필요한 부분이 생겼을 경우 유지 보수가 편리하여 클래스 분리 연습이 왜 필요한지 이해할 수 있었다.

아래 예시를 보자.

하나의 메소드내에 여러 역할을 준 경우

public static List<Integer> jugementNumberWithBonus(int InputMoney) {
				List<Integer> resultNumber = new ArrayList<>();
        List<List<Integer>> correctNumberList = FinalUserNumberGenerator.FinalUserNumber(InputMoney);
        List<Integer> correctNumber = Lotto.CorrectLottoNumber();

        int bonusNumber = BonusNumberGenerator.createBonusNumber(correctNumber);
				for(int i=0; i<InputMoney; i++) {
            int returnNumber = CompareNumber.jugementNumber(correctNumberList.get(i), correctNumber);
            if(returnNumber == 5) {
                returnNumber = JudgmentBonusNumberGenerator.JudgmentBonusNumber(correctNumberList, bonusNumber, returnNumber, i);
                resultNumber.add(returnNumber);
                continue;
            }
            if(returnNumber == 6) {
                returnNumber = 7;
            }
            resultNumber.add(returnNumber);
        }

        return resultNumber;
    }

코드가 아주 지저분 한 것을 볼 수 있다.

물론 변수명으로 대략적으로 어떤 역할을 하는지 예상은 할 수 있겠지만 가독성이 많이 떨어지는 것을 볼 수 있다.

하지만 아래와 같이 메서드만 두개로 나누어주어도 변수명으로 어떤 역할을 하는지 더욱 명확하게 알 수 있다.

(물론 best practice는 아니며 리팩터링이 더 필요한 코드이다..)

public static List<Integer> jugementNumberWithBonus(int InputMoney) {
        List<List<Integer>> correctNumberList = FinalUserNumberGenerator.FinalUserNumber(InputMoney);
        List<Integer> correctNumber = Lotto.CorrectLottoNumber();

        int bonusNumber = BonusNumberGenerator.createBonusNumber(correctNumber);

        return BonusNumberCalculatorGenerator.BonusNumberCalculator(InputMoney, correctNumberList, correctNumber, bonusNumber);
    }


public static List<Integer> BonusNumberCalculator(int InputMoney, List<List<Integer>> correctNumberList, List<Integer> correctNumber, int bonusNumber) {
        List<Integer> resultNumber = new ArrayList<>();

        for(int i=0; i<InputMoney; i++) {
            int returnNumber = CompareNumber.jugementNumber(correctNumberList.get(i), correctNumber);
            if(returnNumber == 5) {
                returnNumber = JudgmentBonusNumberGenerator.JudgmentBonusNumber(correctNumberList, bonusNumber, returnNumber, i);
                resultNumber.add(returnNumber);
                continue;
            }
            if(returnNumber == 6) {
                returnNumber = 7;
            }
            resultNumber.add(returnNumber);
        }
        return resultNumber;
    }

이렇게 둘로 나눔으로써 이후 유지보수를 할때에도 역할분담을 명확히 하여 쉽게 할 수 있는 장점이 있다.

이번 3주차를 진행하며 가장 크게 신경썼던 부분은 클래스(객체)를 분리하는 연습 이었다.

가능한 작은 단위의 메소드들로 만들려고 노력하였고(=15줄 이내의 코드를 작성하시오) 그 결과 총 17개의 클래스(Application 제외)를 생성하게 되었다.
(클래스, 함수명 짓는것이 정말 어렵다는 것을 다시 한 번 깨달았다..)


두 번째로는

단위 테스트코드를 작성하는 연습이었다.

  • 테스트의 중요한 목적 중 하나는 내가 작성하는 코드에 대해 빠르게 피드백을 받는 것이다.
  • 시작부터 큰 단위의 테스트를 만들게 된다면 작성한 코드에 대한 피드백을 받기까지 많은 시간이 걸린다.
  • 그래서 문제를 작게 나누고, 그 중 핵심 기능에 가까운 부분부터 작게 테스트를 만들어 나간다

테스트코드 작성을 통해 피드백을 받으려다보니 어려움이 많았다.

구현하기에 바쁜 나를 볼 수 있었고 그것은 좋은 구현방식이 아니었다.

뒤돌아보니 결국 다시 틀린부분으로 돌아가게 되어있었고 테스트코드를 처음부터 잘 작성하였으면 되었을 일이었다.


아래 간단한 예시를 함께 보겠다.

@Test
    void InputMoney() {
        assertSimpleTest(() -> {
            runException("1000");
            assertThat(output()).contains("1개를 구매했습니다.");
        });
    }

이는 처음 코드가 실행되고 1000을 입력하였을때 “1개를 구매했습니다.” 라는 출력문이 포함이 되었는지에 대한 테스트 코드이다.

이렇게 테스트코드를 만든 후 입력기능의 코드와 그에 맞는 출력기능을 구현하였다면 구현한 코드에 대한 확인을 할때 매번 눈으로 확인하지 않고 빠르게 어디서 어떤 부분이 에러가 났는지 확인할 수 있었을 것이다. 물론 눈으로 보며 디버깅을 통해서도 할 수 있지만 그것은 아주 좋은 방법만은 아니다.

그리고 테스트코드 실행시 반복적인 실패로 인해 다시 처음부터 요구사항을 아주 자세히 읽어보았다. 그리고는 메일에 도착한

‘위 요구 사항은 예외 발생 시 로그를 남기고, 프로그램이 종료되는 상황을 생각해 보세요.’

문구에서 로그를 남기고 종료된다는 말에 영감을 얻었다.

여기서 예외 처리에 대해 자세히 알아볼 수 있었다.

아래 예시를 보자

public static void main(String[] args) {
		int[] intArray= {10};
		try {
			System.out.println(intArray[2]);   // 예외발생 -> 프로그램 종료
		}catch(ArrayIndexOutOfBoundsException e){
			System.out.println("intArray 배열에는 2번방이 없습니다.");
		}
		System.out.println("프로그램을 종료합니다.");
	}

여기서 try문에서 존재하지 않는 배열을 접근하려하여 ArrayIndexOutOfBoundsException 예외 가 발생되었다. 예외가 발생하였으므로 catch문에서 해당 예외를 잡아 밑에있는 System.out.println();문을 실행하는 것이다.

이 과정을 통해 어떻게 로깅을 남기고, 프로그램을 종료하는지 알 수 있었다.

3 주차 미션 제출은 2/2 로 테스트통과를 하였다.
비록 통과는 하였지만 우테코에서 요구하는 사항(리팩터링, 단위 테스트코드 작성 등)들을 모두 만족하지는 못하였던 것 같다.


이번 과정은 다른 주차에 비해 얻어가는 것이 매우 많은 한 주 였다.

최소단위의 클래스, 메소드 만들기 그리고 단위 테스트 만들어보기 자세한 README 작성 등 앞으로 개인적인 발전에 있어 길잡이가 되어주는 순간들이라고 생각된다.

이번 주차의 내용들을 잊지 않고 앞으로 있을 미션들에 대해 반드시 적용시켜보려 더욱 노력하는 내가 되어야 겠다.

0개의 댓글