해당 포스팅은 박재성(javajigi)님의 "자바 웹 프로그래밍 NEXT-STEP" 책을 읽고 실습한 내용을 정리한 것 입니다!
🤔 요구사항
기본 기능 : 전달하는 문자를 구분자로 분리한 후 각 숫자의 합을 구한 후 반환
- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다
- 예) "" → 0 / "1,2" → 3 / "1,2,3" → 6 / "1,2:3" → 6
- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
- 예) "//;\n1;2;3" → 6 (커스텀 구분자는 ;)
- 문자열 계산기에 음수를 전달하는 경우 RuntimeException으로 예외 처리해야 한다
- 예) 0 → RuntimeException
🧑🏻💻 힌트 없는 구현
요구사항 1. 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다
- 간단하게 생각해서 String class의 split() 메서드를 사용해 구현할 수 있다고 생각
// 구현 코드
public int requirement1(String input) {
return Arrays.stream(input.split("[,;]"))
.mapToInt(Integer::parseInt)
.sum();
}
// 테스트 코드
@Test
@DisplayName("요구사항 1에 대한 첫 번째 테스트")
void requirements1Test1() {
assertEquals(0, calculator.requirement1(""));
}
@Test
@DisplayName("요구사항 1에 대한 두 번째 테스트")
void requirements1Test2() {
assertEquals(6, calculator.requirement1("1,2,3"));
}
@Test
@DisplayName("요구사항 1에 대한 세 번째 테스트")
void requirements1Test3() {
assertEquals(6, calculator.requirement1("1,2;3"));
}
@Test
@DisplayName("요구사항 1에 대한 예외 테스트")
void requirements1TestException() {
assertEquals(0, calculator.requirement1("Not,A;Number"));
}
// 수정된 구현 코드
public int requirement1(String input) {
return Arrays.stream(input.split("[,;]"))
.mapToInt(this::stringToInt)
.sum();
}
private int stringToInt(String string) {
try {
return Integer.parseInt(string);
} catch (NumberFormatException e) {
return 0;
}
}
❗️ 풀이 과정에서 든 생각 (다음 요구 사항에서 적용할 부분)
- "" 는 예외가 아니다! → 예외를 던지지 않고 값을 0으로 처리해 주는 게 맞다!
- 숫자가 아닌 게 들어오거나 너무 큰 값이 들어오는 것은 입력이 잘못된 것인가? 그렇다면 어떤 방식으로 클라이언트에 전달해야지?
요구사항 2. 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
- 먼저 // 과 \n 사이의 문자를 구해야 한다고 생각했다
- 구한 구분자를 가지고 문자열을 분리해 숫자의 합을 구한다!
- 이 때, 위에서 생각난 부분을 "문자열 → 숫자"를 처리하는 부분에 적용한다!!
// 구현 코드
public int requirement2(String input, String open, String close) {
return Arrays.stream(input.split(getSeparatorBetween(input, open, close)))
.map(str -> this.removeSeparator(str, open))
.map(str -> this.removeSeparator(str, close))
.mapToInt(this::stringToInt)
.sum();
}
private String getSeparatorBetween(String input, String open, String close) {
int startWordIndex = input.indexOf(open);
if (startWordIndex == -1) {
throw new IllegalArgumentException("input: " + input + " open: " + open + " 여는 구분 문자열을 찾을 수 없습니다.");
}
int endWordIndex = input.indexOf(close, startWordIndex + open.length());
if (endWordIndex == -1) {
throw new IllegalArgumentException("input: " + input + " close: " + close + " 닫는 구분 문자열을 찾을 수 없습니다.");
}
return input.substring(startWordIndex + open.length(), endWordIndex);
}
private String removeSeparator(String input, String separator) {
return input.replaceFirst(separator, "");
}
private int stringToInt(String string) {
try {
if (string.equals("")) {
return 0;
}
return Integer.parseInt(string);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("string: " + string + " 올바른 문자열이 아니거나 수의 값이 너무 큽니다!");
}
}
// 테스트 코드
@Test
@DisplayName("요구사항 2에 대한 첫 번째 테스트")
void requirements2Test1() {
assertEquals(6, calculator.requirement2("//;\n1;2;3", "//", "\n"));
}
@Test
@DisplayName("요구사항 2에 대한 예외 테스트: 여는 구분 문자열이 입력 문자열에 없을 때")
void requirements2Test2() {
assertThrows(IllegalArgumentException.class, () ->
calculator.requirement2("//;\n1;2;3", "WW", "\n"));
}
@Test
@DisplayName("요구사항 2에 대한 예외 테스트: 닫는 구분 문자열이 입력 문자열에 없을 때")
void requirements2Test3() {
assertThrows(IllegalArgumentException.class, () ->
calculator.requirement2("//;\n1;2;3", "//", "/n"));
}
@Test
@DisplayName("요구사항 2에 대한 예외 테스트: 입력 문자열에 숫자가 없을 때")
void requirements2Test4() {
assertThrows(IllegalArgumentException.class, () ->
calculator.requirement2("//;\nONE;TWO;THREE", "WW", "\n"));
}
@Test
@DisplayName("요구사항 2에 대한 예외 테스트: 입력 문자열의 숫자가 너무 클 때")
void requirements2Test5() {
assertThrows(IllegalArgumentException.class, () ->
calculator.requirement2("//;\n1000000000000;2;3", "//", "\n"));
}
요구사항 3. 문자열 계산기에 음수를 전달하는 경우 RuntimeException으로 예외 처리해야 한다
- 해당 요구사항에서는 예외 처리만 해주면 될 것 같은 데, "-" 문자가 구분자일 경우를 예외처리하는 부분에 대해 고민이 된다!
- 구분자가 "-"인 경우를 사전 차단 하여 예외처리하는 방식으로 코드를 작성하면 될 것 같다!
// 구현코드
public int requirement3(String input, String open, String close) {
return Arrays.stream(input.split(getSeparatorBetween(input, open, close)))
.map(str -> this.removeSeparator(str, open))
.map(str -> this.removeSeparator(str, close))
.mapToInt(this::stringToInt)
.sum();
}
private int stringToInt(String string) {
try {
if (string.equals("")) {
return 0;
}
int number = Integer.parseInt(string);
if (number < 0) {
throw new RuntimeException("number: " + number + " 음수는 입력될 수 없습니다.");
}
return number;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("string: " + string + " 올바른 문자열이 아니거나 수의 값이 너무 큽니다!");
}
}
private String getSeparatorBetween(String input, String open, String close) {
int startWordIndex = input.indexOf(open);
if (startWordIndex == -1) {
throw new IllegalArgumentException("input: " + input + " open: " + open + " 여는 구분 문자열을 찾을 수 없습니다.");
}
int endWordIndex = input.indexOf(close, startWordIndex + open.length());
if (endWordIndex == -1) {
throw new IllegalArgumentException("input: " + input + " close: " + close + " 닫는 구분 문자열을 찾을 수 없습니다.");
}
String separator = input.substring(startWordIndex + open.length(), endWordIndex);
if (separator.equals("-")) {
throw new IllegalArgumentException("- 는 음수 사용을 위해 구분자가 될 수 없습니다.");
}
return separator;
}
/*이외의 코드는 위에 작성한 것을 그대로 재사용했습니다!*/
// 테스트 코드
@Test
@DisplayName("요구사항 3에 대한 예외 테스트: 입력 문자열의 숫자가 음수일 때")
void requirements3Test1() {
assertThrows(RuntimeException.class, () ->
calculator.requirement3("//;\n1;-2;3", "//", "\n"));
}
@Test
@DisplayName("요구사항 3에 대한 예외 테스트: 입력 문자열의 구분자가 \"-\"일 때")
void requirements3Test2() {
assertThrows(RuntimeException.class, () ->
calculator.requirement3("//-\n1;-2;3", "//", "\n"));
}
이번 실습은 최대한 저의 생각의 과정을 반영해 작성했습니다!
다음 포스팅에서는 요구사항 이후에 나와 있는 힌트를 참고하여 코드를 리팩토링해 보겠습니다!
🔥 회고
🧳 Keep
- 요구사항을 잘 반영해서 최대한 한 가지 역할을 가진 함수로 분리하도록 노력하기
- 자신만의 생각의 흐름을 잘 반영해 코드를 작성하기
- 여러 가지의 예외 사항에 대해 고민하기
- 고민했던 부분을 항상 기록으로 작성하기
👿 Problem
- 읽기 좋은 코드를 작성하는 데에 더 노력해야 한다
- 다양한 상황에 대한 예외 처리가 부족하다
- 변수 이름을 작성할 때 좀 더 신경써야 한다
- 성능에 대한 고민도 해야 한다
🚵🏻 Try
- 문자열 처리에 대한 다양한 상황에 대한 예외처리를 고민해보자
- 적절한 띄어쓰기의 활용과 적절한 작명으로 읽기 좋은 코드를 만들도록 노력하자
- 문자열 연산에 대한 성능도 고민해 코드를 작성해보자