2024. 11. 10. 00:00ㆍ우아한테크코스
코드에 대한 기준이 점점 높아지면서 고민거리가 늘고, 예상치 못한 문제들이 발생하기도 했다. 하지만 돌아보면 이 과정이 프리코스에서 가장 값진 경험이 아니었나 싶다.
Console.close() 호출 문제
class InputHandlerTest {
...
@BeforeEach
void setUp() {
...
}
@AfterEach
void tearDown() {
Console.close();
...
}
@ParameterizedTest
@ValueSource(...)
@DisplayName(...)
void validLottoPurchaseAmount_isStoredCorrectly(String input) {
// given_
System.setIn(new ByteArrayInputStream(input.getBytes()));
// when_
Money purchaseAmount = inputHandler.getLottoPurchaseAmount();
// then_
assertThat(purchaseAmount.getAmount()).isEqualTo(Long.parseLong(input));
}
}
테스트 메서드를 하나씩 실행하면 문제가 없었지만, @ParameterizedTest로 여러 파라미터를 전달하여 테스트를 수행하니 NoSuchElementException이 발생했다.
camp.nextstep.edu.missionutils.Console 클래스를 살펴보니, 원인을 알 수 있었다. Console 클래스의 Scanner는 static 키워드로 선언되어 전역에서 공유되고 있었다. 즉, Console 클래스 내의 Scanner 객체가 싱글톤으로 존재하기 때문에 모든 테스트 메서드가 동일한 Scanner 인스턴스를 공유하고 있었던 것이다.
첫 번째 테스트에서 System.setIn()으로 입력 스트림을 설정하고, 그 스트림을 통해 입력을 소비한 후에도 Scanner 인스턴스는 그대로 남아 있었다. 두 번째 테스트에서 새로운 입력 스트림을 설정하지만, 기존 Scanner는 여전히 이전 스트림을 참조하고 있어 새로운 입력을 읽지 못한다. 그 결과, 입력 스트림이 비어 있어 NoSuchElementException이 발생하게 된다.
@AfterEach 어노테이션을 활용하여 테스트가 끝날 때마다 Console.close()를 호출함으로써 이 문제를 해결할 수 있었다.
NsTest의 종료 조건 충돌 문제
빈 문자열 입력 테스트를 진행하는 과정에서 NoSuchElementException 예외가 발생했는데, 이 문제를 해결하고자 빈 문자열 입력을 허용하는 로직을 추가했다. 즉, NoSuchElementException 예외를 catch 하고 무시한 것이다. 빈 문자열은 입력에서 처리하는 것이 아니라, 유효성 검사에서 처리해야한다고 생각했기 때문이다.
그러나 문제는 NsTest에서 발생했다. NoSuchElementException 예외가 발생하는 것이 테스트 실행 메서드(run)의 종료 조건이었는데, 빈 문자열을 입력해도 예외가 발생하지 않도록 처리했기 때문에 테스트를 통과하지 못했던 것이다. 결국 추가한 빈 문자열 허용 로직을 지우면서 문제를 해결할 수 있었다.
공통 피드백 리뷰
함수 길이를 15라인으로 제한하니 자연스럽게 각 함수가 수행하는 역할이 명확해졌다. 하지만 라인 수에 맞추다 보면 일부 함수가 의도와 다르게 분리될 수도 있으니, 의미 있는 기능 단위로 구분하는 데 집중해야겠다고 생각했다.
3주차 과제에 비즈니스 로직과 UI 로직을 명확히 분리하기 위해 MVC 패턴을 적용했다. View 계층으로는 getter 메서드를 통해서 데이터를 전달하라고 명시되어있는데, 이를 위해 DTO를 사용했었다.
[우테코 7기 프리코스 3주 차] MVC
프리코스 3주 차, 잘못된 입력에 예외가 발생하면 다시 올바른 입력을 요청해야 한다는 요구 사항이 나를 막아세웠다. 무엇이 잘못된 입력인가?이 경우 예외는 어떤 타입으로 던질 것인가?잘못
mak-ing.tistory.com
경계값 분석을 통해 다양한 예외 케이스를 도출하고, 이에 대한 테스트 코드를 추가했다.
프로그래밍에서 가장 어려운 것...
1주 차 공통 피드백 영상에서 final 키워드를 필드뿐만 아니라 메서드 시그니처의 매개변수에 사용하는 것이 인상 깊어서 기억이 난다. final 키워드로 불변임을 명시적으로 표현하는 것이 코드의 안정성을 높이는 데 중요하다는 점을 다시금 되뇐다.
상수에 대해서는 이전에 자세히 정리해뒀다.
[우테코 7기 프리코스 3주 차] 상수
상수 관리? 그게 꼭 필요해? 물론 자주 쓰게 되는 값을 저장해 두면 편리하다는 것은 알고 있었지만, 한 번 쓸 값까지도 다 정리해둬야 하나? 오히려 코드가 더 지저분해지고 불필요한 선언이 늘
mak-ing.tistory.com
인스턴스 변수를 private으로 설정해 불필요한 변경을 방지했다. 더 나은 캡슐화를 위해, 객체의 상태를 직접 변경하기보다는 객체 스스로가 자신의 메서드를 통해 상태를 변경하도록 연습 중이다. 이를 위해 getter 사용을 줄이고 객체의 책임을 강화하려 노력하고 있다.
계산 가능한 값들은 메서드로 처리해 필드를 최소화한다면, 성능상 문제는 없을까 생각이 의문이 들었다.
테스트 코드를 작성하는 요령이 없어서일까, 테스트 코드는 필연적으로 리팩토링을 지속해야 했다.
테스트를 위한 프로덕션 코드가 잘못된 것임을 알기에 주의했고, 이 부분에서는 큰 문제는 없었다.
Random과 같은 의존성을 외부로 분리하면서 테스트가 수월해졌고 코드 구조도 개선되었다.
중요도가 높은 private 함수를 분리해 테스트 가능성을 높였고, 코드의 응집도와 테스트 효율성도 개선되었다. 단일 책임이라는 키워드는 추상적으로는 이해가 되지만 완벽히 적용하기 참 어려운 것 같다.
마무리
트러블 슈팅과 반복되는 시행착오에 지칠 때도 있었지만, 그만큼 배운 점도 많았다. 단순히 기능을 구현하는 것을 넘어, 튼튼한 구조와 효율을 고려한 설계가 얼마나 중요한지, 그리고 어려운지 깊이 실감한 한 주였다.
이렇게 하나씩 경험해가면서 나만의 기준이 조금씩 자리 잡는 느낌이다. 오류와 시행착오가 주는 ‘반복 학습’을 통해 다음 주엔 조금 더 좋은 코드가 내 손 끝에 찾아오길 기대해본다.
'우아한테크코스' 카테고리의 다른 글
[우테코 7기 프리코스 4주 차] 회고 (0) | 2024.11.14 |
---|---|
[우테코 7기 프리코스 3주 차] 회고 (2) | 2024.11.14 |
[우테코 7기 프리코스 3주 차] 상수 (1) | 2024.11.07 |
[우테코 7기 프리코스 3주 차] MVC (3) | 2024.11.06 |
[우테코 7기 프리코스 2주 차] 회고 (0) | 2024.11.01 |