1주차 과제는 문자열 덧셈 계산기였다.
요구사항
요구사항은 과제 진행 요구 사항, 기능 요구 사항, 프로그래밍 요구 사항 세 가지로 구성되어 있었다.
과제 진행 요구 사항
- 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다.
- Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.
- Angular JS Git Commit Message Conventons를 참고해 커밋 메시지를 작성한다.
기능 요구 사항
입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.
- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.
- 예: "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6
- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
- 예를 들어 "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.
- 사용자가 잘못된 값을 입력한 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다.
기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.
구현할 기능 목록 작성
1. 입력 처리
- 기능
- 구분자와 양수로 구분된 문자열 입력받기
- `camp.nextstep.edu.missionutils.Console`의 `readLine()` 활용
- 잘못된 값 입력할 경우, `IllegalArgumentException` 발생시킨 후 애플리케이션 종료
- 구분자와 양수로 구분된 문자열 입력받기
- 유효성 검사 : 양수 또는 문자
2. 로직 구현
- 구분자를 기준으로 분리 후, 각 숫자의 합 반환
- 기본 구준자: 쉼표(,) 또는 콜론(:)
- 커스텀 구분자: "//"와 "\n" 사이에 위치하는 문자
- 정규 표현식 활용
3. 출력 처리
- 출력 형식
- `결과: X`
4. 테스트
- 입출력 예제
- "" -> 0
- "1,2:3" -> 6
- "1,2" -> 6
- "1,2,3" -> 6
- "//;\n1;2;3" -> 6
구현하기
MVC 패턴을 유지하고 TDD와 객체지향 원칙을 준수하며 코드를 작성하려고 노력했다.
전체 코드
당시에는 객체지향 원칙을 지키기 위해 코드와 파일을 잘 나누었다고 생각했다... 불과 2주가 지난 지금 보니 참 엉망인데....
폴더 구조도 참 단순했다.
src
├── main
│ └── java
│ └── calculator
│ ├── Application.java
│ ├── controller
│ │ └── CalculatorController.java
│ ├── domain
│ │ └── CalculatorModel.java
│ ├── util
│ └── CalculatorUtils.java
└── test
└── java
└── calculator
├── ApplicationTest.java
└── CaculatorModelTest.java
TDD를 실천하기 위해 우선 테스트 코드부터 작성했다. 결과적으로, 나중에 추가로 떠오른 조건을 반영해 테스트 코드를 일부 수정하거나 보완한 경우 외에는 큰 수정 없이 테스트 코드가 기능을 충분히 검증했다고 생각한다. 이 점에서 이번 과제에서 TDD를 비교적 성공적으로 수행했다고 판단했다. 다만, 지금 생각해보니 `CalculatorUtils`도 테스트 코드를 만드는게 맞았을 것 같다고 생각한다.
테스트 코드를 작성한 후에는 로직 구현 → 입력 처리 → 출력 처리 순서로 코드를 짰고, 이후 차근차근 리팩토링했다.
기능 요구 사항에 기재되지 않는 부분은 스스로 판단해서 구현했다. 예를 들어, 구분자는 하나의 문자로 제한했는데, 구분자는 단일 문자라고 판단했기 때문이다. 또 양수라고 되어 있어서 실수 값도 포함될 수 있다고 보고 `double`형을 구현했다. 실수 계산을 하는 경우, `2.6+0.4` 같은 연산의 결과로 `4.0`이 나오는데, 이때 `4`로 변환해서 출력하는 게 좋을 것 같아 김영한 강의에서 배운 명시적 형 변환을 적용해봤다. 간단한 내용이지만 배운 내용을 실제로 적용해서 좀 뿌듯했다.
MVC 패턴을 지키려고 했지만, 사실`controller`와 `domain` 파일만 만들었었다. 당시에는 view 파일은 단순히 사용자한테 보이는 화면이라고 생각했고 print문은 여기에 포함되지 않는다고 여겨서 모든 print문을 `controller`파일에 넣었었다. 그리고 코드 리뷰에서 시원하게 지적받았다ㅎㅎ
클래스 명이나 메서드 명이 전반적으로 어색했다. 패키지 이름이 `calculator`라서 `domain` 클래스 이름을 그냥 `calculator`로 하기에는 좀 그래서 뒤에 `model`을 붙였고, `controller`와 `util`에는 `calculator`을 붙였다. 이렇게 모든 클래스에 `caculator`가 붙은 구조가 되었는데 뭔가 이상하다고 느꼈지만, 어떻게 고쳐야할지 몰라 그래도 두었고 이 부분도 피드백을 받았다.
마지막으로 함수 구분을 너무 크게 했고 메서드명도 애매했다. 메서드명이 해당 기능 이상을 수행하는데, 그걸 이름에 잘 반영하지 못한 게 아쉬움으로 남는다. 메서드 분리를 좀 더 세분화하거나 이름을 다듬었어야 했다고 생각한다.
트러블 슈팅
과제를 제출하기 전, `./gradlew clean test`을 실행해보았는데 오류가 발생했다. 그래서 `ApplicationTest` 코드를 확인해보니 `"//;\\n1"`이 테스트 입력으로 들어가 있어서 조금 의아했었다. 결과가 1로 나오는 걸 보면, 오류 없이 제대로 작동하는데, 왜 `\n`이 아니라 `\\n`을 넣는 건지 의아했었다.
한참 고민하다가, `\n`이 특수 문자로 인식된다는 기본적인 사실을 까먹고 있었음을 자각했다. 실제로 줄바꿈 문자로 사용되지 않게 하려면 `\`를 두 번 써야 글자로 인식된다는 걸 다시 깨달은 순간이었다.
또 다른 사람의 코드를 보다가 아래와 같은 코드를 발견했다. 이미 입력 값과 관련된 처리를 다른 곳에서 했는데 왜 이런 코드를 넣었는지 의아해했었다. 그런데 `ApplicationTest`에서 입력으로 `null`이 들어가면 이를 걸러내지 못한다는 것을 깨달았다. 그래서 나도 아래처럼 코드를 추가했다.
try {
input = Console.readLine().trim();
} catch (NoSuchElementException e) {
System.out.println("입력 과정에서 오류가 발생했습니다. 빈 문자열을 처리합니다.");
input = "";
}
학습하고 사용한 것들
객체 지향 프로그래밍(Object-Oriented Programming, OOP)
코드 리뷰 후기
[우아한테크코스] 프리코스 1주차 코드 리뷰 후기
[우아한테크코스] 프리코스 1주차 회고1주차 코드 제출 후, 프리코스 커뮤니티를 통해 코드 리뷰를 해줄 사람을 구해 리뷰를 시작했다.코드 리뷰 피드백 요약클래스와 메서드 네이밍 개선클래스
best11gh.tistory.com
'우아한테크코스' 카테고리의 다른 글
[우아한테크코스] 프리코스 3주차 회고 (0) | 2024.11.06 |
---|---|
[우아한테크코스] 프리코스 2주차 코드 리뷰 후기 (0) | 2024.10.31 |
[우아한테크코스] 프리코스 2주차 회고 (0) | 2024.10.31 |
[우아한테크코스] 프리코스 1주차 코드 리뷰 후기 (1) | 2024.10.30 |
[우아한테크코스] 사용한 라이브러리 (0) | 2024.10.23 |