3주차 공통 피드백 리뷰에 들어가기 앞서 설레는 마음 한 가득이다. 피드백이 왜 이렇게 날 설레게 할까?🤭
3주차 미션의 학습 목표 클래스 분리, 단위 테스트를 시작해 보는 것
해야할 일
공백 라인도 한 라인으로 간주하는 것을 기억하자. 이 규칙의 의도에 대해서 생각해보자. 내 생각에는 단일 책임이 중요하기 때문이라고 생각한다. 하나의 메소드는 하나의 동사와 목적어를 사용하도록 하자.
코드를 작성할 때는 예상되는 예외를 미리 고려를 한다.
테스트 작성을 할 때에 작게 나눠진 단위테스트를 하는 것에 집중을 하자. 그리고 그 기능에 대한 테스트를 진행할 때 미리 예외까지 생각해보는 것으로 하자.
toString()은 로그나 디버깅을 위한 상태 표시용. getter는 UI 계층에서 필요한 데이터만 가져올 때 사용.
ex) toString
public class User {
private String name;
private String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
@Override
public String toString() {
return "User{name='" + name + "', email='" + email + "'}";
}
}
콘솔은 익숙한데 로그는 아직 잘 모르겠다. 로그를 분석하는 것도 배워보자. View에서 getter 사용하는 것과 Controller에서 getter 사용해서 View로 전달하는 것 중 어떤 것이 좋을지 고민이 된다. 상황에 따라 둘 다 사용되는 건지 어떠한지 생각해볼 필요가 있는 것 같다.
그룹화하여 한 곳에서 연관된 값들을 관리할 수 있는 장점이 돋보인다. 잘못된 값의 사용도 방지할 수 있고 각 상수가 무엇을 의미하는지 명확히 알 수 있어 좋은 것 같다.
예기치 않은 값의 변경으로 인한 오류를 방지할 수 있다. 불변성에 대해서 강조되는 것이 많이 보이므로 한 번 알아보자.
- Thread Safety: 값이 변경되지 않으므로 동기화 없이도 안전하게 사용 가능
- 예측 가능성과 디버깅 용이성: 불변 객체는 생성 이후 값이 변하지 않기 때문에 코드의 흐름을 예측하기 쉽다.
- 안전성과 보안성: 안전한 객체 전달을 가능하게 한다. 메서드나 다른 클래스에서 값을 변경할 위험이 없다.
- 성능 최적화 가능성: 불변 객체는 캐싱에 유리하다. 한 번 계산된 결과를 재사용할 수 있다. 자주 사용되는 값이나 메서드의 반환 결과를 불변 객체로 관리하면 메모리 사용 효율이 높아진다.
- 함수형 프로그래밍에 적합: 함수형 프로그래밍은 부작용(side effect)를 피하는 것을 지향하는데 이러한 부작용을 줄여준다.
인스턴스 변수의 접근 제어자를 private으로 설정한다. 캡슐화의 중요한 원칙, 객체의 상태는 외부에서 통제되지 않도록 주의하자. (객체의 자율성)
자기 역할에 책임을 다 할 수 있게 하는 것이 중요하다고 얘기하는 듯 싶다. 상태를 가지고 있다면 그 데이터를 활용하는 로직이 필요하다는 것 같다. 또한 내부 구현이 변경되어도 메서드의 인터페이스가 동일하다면 외부 코드에 영향을 미치지 않는 점도 중요하다고 생각된다.
이번 로또 미션에서 Lotto 객체를 전혀 Lotto 객체답게 사용 못 한 것 같아 아쉽다. 레이싱카를 진행할 때에는 이러한 점에 신경을 쓴 것 같은데 마음이 급했나보다.
이번 피드백이 나에게 아주 강렬한 인상을 주었다. 감사합니다😊
자율적, 책임 주도 설계, 메세지와 협력에 대해서 고민하자.
코드를 깔끔하게 하고 싶어서 lottoController 클래스에서 인스턴스 변수의 수를 늘렸다. 하지만 이 피드백에서 말한 것처럼 잠시 필요한 정보라고 판단됐다. 즉 인스턴스 변수로 불필요하다고 생각되어 지역 변수로 사용을 했는데 좋은 선택이었던 것 같다. **필드에 중복과 필요 유무에 대해서 확인하자. **
나는 오히려 예외 케이스만 테스트 작성하려고 했다. 그래서 테스트 작성을 어렵게 생각한 것 같다. 성공하는 케이스를 선 작성하는 것이 좋은 것 같고 예외 케이스 작성도 꾸준하게 해보자. 즉, 기본을 준수하되 예외(변동)에 대비를 하자.
테스트 코드 또한 리팩터링을 할 필요가 있다. 효율적인 테스트 코드 작성을 위해 지속적으로 노력하자.
테스트를 위해 구현 코드를 변경하는 것은 좋지 않은 습관이다.
무엇이 먼저인가? 본질에 대해서 생각하자.
메서드 시그니처란? 메서드를 구별하는 핵심 정보, 예를 들어서 메서드의 이름, 매개변수의 타입 및 순서 이를 통해 유일하게 식별할 수 있다.
제어할 수 없는 요소를 분리나 주입같은 기법을 통해서 조정 가능하게 만들어 예측 가능한 환경을 만든다.
이 주제는 단일 책임 원칙과 테스트 가능성, 그리고 코드 응집도에 대한 깊이 있는 논의로 이어질 수 있는 흥미로운 부분이다.
핵심은, private 메서드가 단순히 구현 세부사항이 아니라 중요한 역할을 수행할 때, 이를 분리하여 독립된 클래스나 객체로 만드는 것이 좋은 설계가 될 수 있다는 점이다.
왜 private 메서드를 테스트하기 어려운가?
중요한 역할을 수행하는 private 메서드란?
주요 로직을 처리하는 메서드가 private으로 정의된 경우, 이 로직이 얼마나 잘 작동하는지를 확인하기 위해서는 세밀하게 검증할 필요가 있다. 단순히 가독성을 위해 코드를 분리한 것이라면 문제가 없겠지만, 중요한 계산 로직이나 데이터 처리를 담당한다면 단순한 구현 세부사항 이상의 역할을 수행하고 있다고 볼 수 있다.
이럴 때 클래스 분리가 왜 중요한가?
SRP를 적용하여 각 클래스가 하나의 책임만 가지도록 하면, private 메서드에서 수행하던 중요한 로직을 독립된 클래스의 public 메서드로 분리할 수 있다. 이렇게 하면 테스트 가능성이 높아지며, 독립된 역할을 부여받은 클래스를 직접적으로 테스트할 수 있게 된다.
예시로 이해해 보기
주문 처리 시스템에서 할인을 계산하는 로직이 있다고 가정하자. 이 할인 계산이 Order 클래스의 private 메서드로 구현되어 있다면, 할인 로직 자체를 테스트하기가 어려워진다. 이럴 때, 할인 계산을 담당하는 DiscountCalculator라는 클래스를 따로 분리하면 DiscountCalculator는 public 메서드로 할인 계산을 수행하므로 독립적으로 테스트할 수 있고 Order 클래스는 주문과 관련된 책임만 가지므로 응집도가 높아진다.
중요한 역할을 수행하는 private 메서드를 분리하는 것은 단순히 테스트 가능성을 높이는 것뿐 아니라, 클래스가 하나의 책임을 가지도록 설계하여 코드의 구조를 개선하고 유지보수성을 높이는 데 기여한다.
테스트 주도 개발과 객체지향적 사고의 중요성을 느끼게 된 피드백이었다. 앞으로 테스트가 필요한 이유에 대한 주관적 견해를 쌓자. 객체지향 프로그래밍을 할 때에 객체의 자율적, 책임과 메세지를 통한 협력, 역할에 대해 고려하자.