이어서...
오늘 한 것은
1. 리팩토링
2. TDD(구현)
였다. 사실 TDD 과정 속에 리팩토링이 있으니 동시에 진행했다고 봐도 무방하다.
오늘은 검증과 관련된 코드를 작성했는데, 생각보다 검증 부분에서 테스트하고 리팩토링 할게 많았다.
특히 enum 클래스에 검증에 필요한 값들을 옮기고, 또 테스트를 진행하며 처리하지 못한 에러들을 잡아내는 게 좀 오래 걸렸다.
가변길이 매개변수
테스트 코드에서 이렇게 공통되는 테스트는 assertExceptionTest()
메서드로 분리해서 사용하고 있었다.
자동차 이름을 하나만 넣어서 테스트할 부분이 있었고, 여러개를 넣어서 테스트 할 부분이 있었는데 동시에 진행하고 싶어서 가변길이 매개변수
를 사용했다. 이건 까먹고 있었던 건데, 어떻게 하면 매개변수의 개수를 유연하게 줄 수 있을까 생각하다가, 예전에 배웠던 가변길이 매개변수를 떠올렸다.
| ⬆️ 가변길이 매개변수를 공부했던 기록
이렇게 사용하니 깔끔하게 여러 값을 받을 수 있게 됐다! 자바를 배우것 중 잊은게 많다는 생각이 들었다.
인터페이스와 제네릭
Validator 라는 인터페이스를 만들어 검증 클래스에서 구현하게 해서 사용하고 싶었다. 그런데 한 클래스에서는 List<String>
타입을 받고 반환하고, 다른 한 클래스는 String
타입을 받고 반환한다.
그렇다고 각각을 만족하는 다른 추상 메서드를 만들자니, 한 클래스에서 필요없는 메서드까지 구현해줘야 한다.
이 문제를 해결하기 위해 고민을 많이 했는데, 결론은 제네릭을 사용하는 거였다.
제네릭도 자바 학습 당시 인상 깊은 내용이었는데, 실전에서 쓸려니 기억이 안 났다.
우선적으로 내가 아는 지식을 먼저 찾아 쓰려는 습관 아닌 습관이 있는 것 같았다.
| ⬆️ 제네릭 타입을 공부했던 기록
이렇게 깔끔하게 해결이 돼었다.
적극적인 enum 사용
저번 1주차 과제 때 Enum
사용에 대해 많은 분들이 좋게 리뷰해주셔서 Enum
을 이번에는 조금 더 적극적으로 사용했다.
검증할 것이 '최초 입력값 검증' -> '문자열 분리 검증' -> '분리된 자동차 이름 검증 -> '진행 시도 횟수 검증' 이었는데, 이전 과제에 비해 검증할 것이 많은 만큼 고려해야 할 에러 상황이 많았고, 자연스래 enum 에 이전보다 더 많은 값들을 담게 돼었다.
ValidationConfig
ValidationConfig
에는 검증에 필요한 단순 값들이 포함되어 있다.
최초 입력값의 최소-최대 길이, 자동차 하나의 이름의 최소-최대 길이, 시도 횟수의 최소-최대 길이, 그리고 문자열 분리에 필요한 기준값(delim) 이다.
이 delim을 다른 ErrorMessage
쪽으로 줘야 하나 생각했는데, 맥락을 생각하면 오히려 이쪽이 더 맞을 것 같았다.
InputPattern
InputPattern
은 검증에 필요한 정규 패턴식을 담게 했다. 특이한 점이 최소-최대 길이에 문자열 형식을 사용했는데, 결합도가 높아지는 단점이 있지만, 덕분에 최소-최대 길이가 변경되면 ValidationConfig
의 값들만 변경해줘도 검증할 최소-최대 길이, 에러 메시지 내용이 자동으로 바뀐다!
저번 과제때
이렇게 한번 써먹어 봤는데, 리뷰를 좋게 받았다!
ErrorMessage
ErrorMessage
에는 에러시 출력할 내용이 담겨있다. 여기서도 문자열 형식을 사용했다.
검증 클래스
사실 오늘 하루 종일 검증 기능 구현에만 시간을 들인 이유는 enum
과 리팩토링 말고도, 욕심낸 기능 구현이 있었기 때문이다.
바로 잘못된 자동차 이름 입력 시 해당 자동차 이름 출력
기능이었다.
원래 생각한 기능은 아니었는데, 검증 부분을 구현하다가 생각하게 되었다.
자동차 이름을 많이 입력했을 때, 그 중 형식에 맞지 않는 자동차가 있다면 단순히 틀렸다- 는 메시지 보다는 '~~에서 틀렸다-' 라고 말해주는게 사용자 관점에서 훨씬 편할 것 같았다.
만약 1000개의 자동차 값을 힘들게 적었는데 틀린 값이라서 실행 못한다 하면 1,000 개의 자동차 이름을 사용자가 직접 하나하나 찾아야 하지 않는가? 사용자의 안구 건강이 염려됐다.
그래서 구현할 방법을 고민하고 고민하다가 스트림을 사용했다.
주어진 정규식과 맞지 않는 이름들을 리스트에 담은 후, 만약 리스트가 비어있지 않다면, 즉 틀린 이름이 하나라도 존재한다면 에러를 띄우고 해당 값들을 ', '
를 기준으로 붙여서 출력하게 한다.
이 기능은 구현 중 추가된 기능이지만, 되게 마음에 드는 기능이다!
사실 원래는 '영어 소문자, 숫자, ',' 이외에 다른 문자가 포함되어서는 안됨' 이라는 정규식으로 맨 처음 인풋 단계에서 검증하려 했지만, 이렇게 하면 어떤 자동차가 틀렸는지 알려줄 수 가 없었다. 효율성을 생각하면 문자열 분리까지 가기 전에 오류를 처리하는게 좋지만, 기능성으로는 이 방법이 더 좋지 않을까?
그 밖에...
나머지도 다 검증 기능이다. 똑같이 표현식에 일치하는지 검증하게 했고, 틀리면 똑같이 enum
의 에러 메시지들을 가져와 출력하게 했다.
의외로 검증 부분에서 시간이 많이 흘렀다. 시작할 때 생각했던 것 보다 테스트 케이스가 더 많았다. 이 부분에서 추가적으로 구현한다고 시간이 걸렸고, 처음에는 enum
이 아닌 상수로 구현했다가 enum
으로 리팩토링하는데 시간이 많이 걸렸다.
마무리
사실 2일차에 검증 부분 이외의 것들(옵저버 패턴을 이용한 게임 진행)을 어느 정도 구현을 해놔서 (이 날 19시간을 작업했다...)
다만 TDD로 모든 부분을 테스트 하는게 시간이 좀 걸리지 않을 까 싶다. 다른 객체를 의존하지 않은 상태에서 테스트를 진행해야 하는게 조금 난이도가 있어 보였다.
그 밖에 시간이 된다면 리팩토링 시 저번 과제에서 리뷰 해주신 것들을 최대한 고쳐보려고 한다.
1. web 환경에서의 출력 고려
2. DTO는 데이터 전송만 담당한다. 저장 노노!
3. List의 contains() 사용 지양
4. 싱글톤 패턴의 동시성 문제
그리고 다른 분들의 코드를 리뷰하며 배웠던 점들도 사용해보려 한다.
1. enum 클래스의 인스턴스화 방지
2. 매번 matches() 사용 대신 정규 표현식 캐싱 고려
3. 생성자 주입(객체 내부에서 객체 생성하지 않기) -> 테스트의 어려움, SRP 위반 가능성, 유연성 감소
그리고 객체지향 체조 원칙에서,
1. 일급 컬렉션 사용
2. 모든 원시값 포장
도 시간이 가능하다면 리팩토링을 통해 적용하고 싶다.
이렇게 보니 적용할 게 참 많다. 그래도 항상 더 나은 애플리케이션을 만들고 싶다.
새로 알게된 점
1. 가변길이 매개변수
이건 다시 알게된 점인데, 까먹고 있다가 기억해서 사용할 수 있게 됐으니 새로 알게된 점이나 다름없다. 기억하지 못한건 몰랐던 것과 결과
에서 차이가 없으니까...
2. 제네릭
이것도 1번과 마찬가지! 제네릭이 코드의 유연성을 매우 높여주는 도구인데, 생각도 못하고 있었다.
3. 패키지명 != 계층
mvc 패턴을 학습할 때 패키지명이 controller, view, model 이렇게 되어 있는걸 보고 '각 계층에 해당하는 클래스들을 다 넣어놓은 건가?' 라고 생각해서 'service', 'dto', 'util' 등의 패키지를 mvc 패턴에서 사용을 지양해야하나? 라는 황당한 의문을 가졌었다.
공부한 바, 패키지명 != 계층이라는 것. util 패키지를 추가한다고 util 계층이 생기는 건 아니라는 것... 😅
좋았던 점
1. 적극적인 enum 사용
상수보다는 분명히 enum
으로 사용하면 좋을 것들을 분리해서 사용할 수 있어서 좋았다. 1주차에서는 이게 enum
으로 써야 하는 것인지 상수로 써야 하는 것인지 고민을 오래 했는데, 이번에는 고민이 훨씬 짧았다. 한번 감을 잡으니 빠르게 사용을 결정할 수 있었다.
2. 제네릭을 통한 문제 해결
인터페이스에 제네릭을 사용할 생각을 못했다는게 아쉬우면서도, 결국 이전에 학습했던 내용을 꺼내보면서 결국 실전에 적용할 수 있었던 점이 좋았다. 앞으로는 해결 방법이 고민될 때마다 배웠던 기록들을 둘러 봐야겠다.
아쉬웠던 점
1. 검증 구현에 시간이 많이 들어간 것
그렇게 많이 걸릴 것은 아니었다고 생각했는데, 하루를 다 사용하게 됐다. 일이 있어서 원래 학습에 쓰던 시간보다 조금 적게 시간을 쓰긴 했지만, 그래도 좀 많이 아쉬웠다.
2. 이 코드, 저 코드 왔다갔다 하는 것
이 문제 때문에 생각보다 시간을 많이 소모했다고 생각한다.
한 코드에서 구현하다가 '잠깐, 이걸 이렇게 하면 저쪽 코드는 어쩌지?' 라는 생각에 다른 코드 가서 훑어보고, 그러다가 리팩토링 할거 만나서 수정하고, 그러다 원래 코드로 돌아가고... 수정해야 할 걸 까먹을것 같은 두려움에 이렇게 했던 것 같다. 내일부터는 인텔리제이의 TODO를 적극적으로 활용해야겠다.
3. 내가 아는 지식을 먼저 찾아 쓰려는 습관
해결에 어려움이 있는 문제를 만날 때만 새로운 방법을 찾으려 시도하는 것 같다.
더 나은 방법이 있을까? 라고 매번 고민하면 좋을 것 같은데, 근데 그러면 또 구현에 시간이 너무 많이 걸릴 것 같고... 그러면 그냥 마지막에 리팩토링 하면서 고민해보는것도 괜찮을 것 같고... 다음 과제 때는 코드 작성 시 미리미리 새롭고 더 나은 방법을 고민해보는 방법으로 구현해야 겠다.
도움이 된 자료들
| PHPStorm 이나 IntelliJ 에서 한글 변수명 사용시 "Non-ASCII characters in an identifier" 경고 끄기
'우아한테크코스' 카테고리의 다른 글
[우테코 6기 프리코스] 2주차 - 5일차 회고 (0) | 2023.12.20 |
---|---|
[우테코 6기 프리코스] 2주차 - 4일차 회고 (0) | 2023.12.20 |
[우테코 6기 프리코스] 2주차 - 2일차 회고 (0) | 2023.12.20 |
[우테코 6기 프리코스] 2주차 - 1일차 회고 (1) | 2023.12.20 |
[우테코 6기 프리코스] 1주차 - 7일차 + 1주 전체 회고 (1) | 2023.12.20 |