본문 바로가기
프로그래밍이야기/Java

TDD 연습 - 특정 문자열에 하이라이트 표시

by 사랑꾼이야 2022. 6. 23.
반응형

TDD 연습을 하기 위해서 나름 아껴놓고 있다가 진행하게 되었습니다. 유투브 영상의 앞부분 요구사항 나오는 부분만 확인한 후 실습을 진행하였습니다. 그리고 직접 TDD로 연습하면서 개발을 완료한 다음, 영상에서 진행하신 부분과 내가 진행한 코드가 어떤지 비교를 해보는 것도 좋을것 같습니다.

출처
최범균님 유투브

직접 진행한 소스는 github에서 확인할 수 있습니다. - 소스 바로가기

요구사항

특정 문자열에 하이라이트를 표시하는 내용이며, 요구사항은 다음과 같습니다.

문자열 목록에서 note가 포함된 문자열을 강조해서 출력하는 프로그램을 만든다.
단, 정확하게 원하는 알파벳만으로 이루어진 단어만을 확인해야 한다.
예를 들어, note1이나 keynote는 원하는 단어가 아니다.
note의 앞이나 뒤에 숫자나 알파벳이 나오면 다른 단어라고 판단한다.
출력 결과는 note 단어를 강조하기 위해 단어의 앞뒤에 중괄호{ }를 한다.
문자열은 알파벳 소문자, 공백, 숫자로만 구성되어 있다고 가정한다.

변환 예:

* abc -> abc
* note -> {note}
* 1 note -> 1 {note}
* 1 note 2 -> 1 {note} 2
* keynote -> keynote
* ke1note -> ke1note
* yes note1 -> yes note1
* yes notea -> yes notea
* no a note -> no a {note}
* no a note note -> no a {note} {note}
* no a note note anote -> no a {note} {note} anote

구현 방법

정확한 케이스는 빠르게 넘어갔습니다. 바로 다음과 같은 경우입니다.

note 문자열만 존재하는 경우

  • ex) note
if (str.equals("note")) {
    return "{note}";
}

note 가 아닌 경우

  • ex) abc
if (str.equals("note")) {
    return str;
}

위의 경우처럼, 몇 가지 정확한 케이스는 어렵지 않게 해결이 됩니다.

바로 테스트 코드를 작성하기에 어렵지 않으니 바로 작성할 수 있고 테스트 결과를 통해서 녹색불을 볼 수 있게 되면서 기분도 좋고 자신감도 생기게 됩니다.

하지만 문자열 속에 note 가 함께 있는 경우는 좀 고민을 해보게 되었습니다. 정규식을 이용해보면 어떨까 싶어서 진행을 해보게 되었습니다.

1 note, 1 note 2, keynote, ke1note, yes note1, yes notea, no a note, no a note note, no a note note anote

몇 가지 고려해야될 부분들이 있습니다.

  • 문자열 속에서 note 만 비교
  • 문자열 속에 note 가 있으면 note 만 변경해야함
    • 1 notenote 가 존재하기 때문에 note 결과로 나오면 안되고 1 {note} 로 나와야 합니다.
  • 문자열 속에 notenote 가 아닌 문자열이 존재할 경우 처리
    • no a note note anote 에는 notenote 가 아닌 문자열이 존재하기에 no a {note} {note} a{note} 결과로 나오면 안되고 no a {note} {note} anote 로 나와야 합니다.

첫번째 생각한 방법 - 정규식

문자열 속에 note가 함께 있는 경우

해당 케이스를 나열해보면 다음과 같습니다.

  • ex) 1 note, 1 note 2, keynote, ke1note, yes note1, yes notea, no a note, no a note note, no a note note anote
  • 문자열 속에 note 앞뒤로 공백과 문자열이 올 수도 있고 안 올수도 있습니다.
  • 문자열 속에 note 앞에만 공백과 문자열이 올 수 있습니다.
  • 문자열 속에 note 뒤에만 공백과 문자열이 올 수 있습니다.

그래서 가장 먼저 들었던 생각이 정규식이었습니다.

  • note 앞뒤로 공백이 존재하면 문자열 속 중간에 나온 부분으로 체크해볼 수 있습니다. : .*( note ).*
  • note 앞에만 공백이 존재하고 끝에는 존재하지 않으면 문자열 끝으로 체트해볼 수 있습니다. : .*( note)
  • note 뒤에만 공백이 존재하고 앞에는 존재하지 않으면 문자열 끝으로 체트해볼 수 있습니다. : (note ).*

자바에서 정규식 문자열을 찾는 부분은 MatcherPattern 을 이용해서 다음과 같이 작성하였습니다.

Matcher matcher = Pattern.compile(".*( note ).*|.*( note)|(note ).*").matcher(this.name);
boolean matches = matcher.matches();
if (matches == false) {
    return this.name;
}

return str.replace(STRING_NOTE, STRING_CORRECT_NOTE);

문제점

요구사항 중 딱 하나 케이스를 제외하고 모두 통과합니다. 통과하지 않는 케이스는 no a note note anote 바로 이것이었습니다.

정규식만 생각하다보니 오히려 단순한 부분을 생각을 하지 못하다보니 다음과 같은 문제점이 생겼습니다.

  • 정규식이 or 조건이라서 matches 를 true로 결과를 반환하여서 결과가 다음과 같이 나타나게 됩니다.
    • no a {note} {note} a{note}
  • MatcherPattern 을 이용해서 좀 더 세분화하여서 하는 방법도 생각을 하였지만 Effective Java 의 item6에서 불필요한 객체를 생성하지 마라 고 설명을 하고 있습니다.

그래서 정규식은 진행을 하지 않기로 결정을 하게 되었고, 문자열속에 문자 단위별로 비교해서 변경하는 것이 필요하다고 생각을 하게 되었습니다.

그래서, 다음과 같이 진행을 해보았습니다.

두번째 생각한 방법 - Split

바로 공백() 문자 기준으로 문자열을 split 하여서 문자 단위별로 note 를 비교하는 것이었습니다.
마지막 케이스를 자세히 보겠습니다.

구현 방법

  • ex) no a note note anote
    • 공백() 문자 기준으로 문자열 split 처리하면 다음과 같이 나옵니다.
    • no
    • a
    • note
    • note
    • anote
    • 그렇게 나온 각 문자열을 note 와 비교(equals)하여 일치하면 {note} 문자열로 변경하고 일치하지 않으면 그대로 반환합니다.
    • 공백() 문자 기준으로 문자열 split 하였으니, 각 문자열 비교가 끝난 후 반환 시 공백() 문자열 추가합니다.
    • 마지막 문자열에서 반복문이 끝나면 마지막 공백() 문자 제거합니다.

split을 이용해서 문자단위별로 비교한 방법에 대해서 모든 경우에 대해 테스트를 통과할 수 있었습니다.

반응형

댓글