우아한테크코스 2주차[BE] 피드백
2주 차 공통 피드백의 내용은 다음과 같았습니다.
- README.md를 상세히 작성한다
미션 저장소의 README.md는 소스코드에 앞서 해당 프로젝트가 어떠한 프로젝트인지 마크다운으로 작성하여 소개하는 문서이다. 해당 프로젝트가 어떠한 프로젝트이며, 어떤 기능을 담고 있는지 기술하기 위해서 마크다운 문법을 검색해서 학습해 보고 적용해 본다.
=> 앞으로 마크다운 문법으로 블로그를 작성해보려고 한다.
- 기능 목록을 재검토한다
기능 목록을 클래스 설계와 구현, 함수(메서드) 설계와 구현과 같이 너무 상세하게 작성하지 않는다. 클래스 이름, 함수(메서드) 시그니처와 반환값은 언제든지 변경될 수 있기 때문이다. 너무 세세한 부분까지 정리하기보다 구현해야 할 기능 목록을 정리하는 데 집중한다. 정상적인 경우도 중요하지만, 예외적인 상황도 기능 목록에 정리한다. 특히 예외 상황은 시작 단계에서 모두 찾기 힘들기 때문에 기능을 구현하면서 계속해서 추가해 나간다.
- 기능 목록을 업데이트한다
README.md 파일에 작성하는 기능 목록은 기능 구현을 하면서 변경될 수 있다. 시작할 때 모든 기능 목록을 완벽하게 정리해야 한다는 부담을 가지기보다 기능을 구현하면서 문서를 계속 업데이트한다. 죽은 문서가 아니라 살아있는 문서를 만들기 위해 노력한다.
=> 기능 목록에 예외 상황 파트도 추가해서 작성해보려고 한다.
- 값을 하드 코딩하지 않는다
문자열, 숫자 등의 값을 하드 코딩하지 마라. 상수(static final)를 만들고 이름을 부여해 이 변수의 역할이 무엇인지 의도를 드러내라. 구글에서 "java 상수"와 같은 키워드로 검색해 상수 구현 방법을 학습하고 적용해 본다.
=> 2주차에서는 상수 처리를 빼먹은 부분들이 존재했었는데 3주차에서는 보다 꼼꼼하게 작성하려고 한다.
- 구현 순서도 코딩 컨벤션이다
클래스는 상수 또는 클래스 변수, 멤버 변수, 생성자, 메서드 순으로 작성한다.
=> 메서드 내에의 순서도 지켜서 작성하려고 한다.
- 정적 변수 : public -> protected -> private
- 인스턴스 변수 : public -> protected -> private
- 생성자
- 정적 메소드 : static 메소드 (main 메소드가 있다면 static 메소드 이전에 작성)
- 메소드 : 접근자 기준으로 작성하지 않고, 기능 및 역할별로 분류하여 기능을 구현하는 그룹별로 작성이 이루어지도록 해야한다. (public 사이에 private 메소드가 존재할 수 있다)
- 스탠다드 메소드 : toString, equals, hashcode 와 같은 메소드
- getter, setter 메소드 : 클래스의 밑 부분에 위치
- 변수 이름에 자료형은 사용하지 않는다
변수 이름에 자료형, 자료 구조 등을 사용하지 마라.
=> 변수 이름에 자료형과 자료구조를 포함한다면 확장성이 떨어지며, 불필요한 내용이 네이밍에 들어가 가독성 또한 떨어진다
- 한 함수가 한 가지 기능만 담당하게 한다
함수 길이가 길어진다면 한 함수에서 여러 일을 하려고 하는 경우일 가능성이 높다. 아래와 같이 한 함수에서 안내 문구 출력, 사용자 입력, 유효값 검증 등 여러 일을 하고 있다면 이를 적절하게 분리한다.
- 함수가 한 가지 기능을 하는지 확인하는 기준을 세운다
만약 여러 함수에서 중복되어 사용되는 코드가 있다면 함수 분리를 고민해 본다. 또한, 함수의 길이를 15라인을 넘어가지 않도록 구현하며 함수를 분리하는 의식적인 연습을 할 수 있다.
- 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
단지 기능을 점검하기 위한 목적으로 테스트를 작성하는 것은 아니다. 테스트를 작성하는 과정을 통해서 나의 코드에 대해 빠르게 피드백을 받을 수 있을 뿐만 아니라 학습 도구(학습테스트를 통해 JUnit 학습하기.pdf)로도 활용할 수 있다. 이런 경험을 통해 테스트에 대해 어떤 유용함을 느꼈는지 알아본다.
- 처음부터 큰 단위의 테스트를 만들지 않는다
테스트의 중요한 목적 중 하나는 내가 작성하는 코드에 대해 빠르게 피드백을 받는 것이다. 시작부터 큰 단위의 테스트를 만들게 된다면 작성한 코드에 대한 피드백을 받기까지 많은 시간이 걸린다. 그래서 문제를 작게 나누고, 그 중 핵심 기능에 가까운 부분부터 작게 테스트를 만들어 나간다.
=> 단위 테스트 진행 후에 통합 테스트를 진행하자. 테스트 작성에 있어 이유를 생각해보자.
개인 PR에 달린 다른 지원자 분들의 피드백
- private 생성자와 final 활용
Constants의 경우 상수들로 이루어진 클래스이다. 따라서 생성자가 필요 없다.
``` private Constants() {} ```
와 같은 방법으로 생성자를 막아 둔다면 객체 생성을 방지할 수 있다. 또한 상속할 클래스가 존재하지 않을 경우, 이러한 상속을 방지하고 싶다면 클래스에 final 키워드를 붙여 원하지 않은 상속을 막을 수 있다.
``` public final class Constants ```
- 일급 컬렉션에서 getter 지양하기
getter를 사용을 지양한다면 일급 컬렉션의 장점 중 하나인 불변성을 보장할 수 있다.
- 상수처리
몇 개 놓친 상수 처리가 존재했다. 예를 들어, 자동차 전진 횟수 초기값인 0의 상수처리를 놓쳤다. 그 외에도 ErrorCode의 메세지인 "자동차 이름은 5글자 초과할 수 없습니다."에서 글자수를 확장한다면 수정해야하는 요소가 늘어난다. 이러한 경우에도 5를 상수처리해서 받는 게 좋아보인다.
- 명확한 의미 전달을 위한 네이밍
자동차 이름의 검증 함수들을 하나로 모은 validate() 메서드가 존재했다. 여러 검증들을 모았기에 validate라는 네이밍을 했지만 차 이름에 대한 검증임을 명확하게 가르키는 validateName()이 더 좋아보인다. 또한 우승자를 가리는 judge()의 경우 판정하다라는 의미도 좋지만 우승자를 선발한다는 getWinners() 라는 이름이 더 적절해 보인다.
- 도메인 검증의 분리
입력에서의 검증과 도메인 엔티티에서의 검증을 분리하여 생각해야 한다. 도메인 엔티티가 입력이 아닌 다른 방법을 통해 생성된다면 말씀하신 것처럼 놓치는 사례도 존재할 수도 있다. 즉 로직의 흐름에 따라 도메인을 검증하는 것이 아닌 도메인 엔티티 별로 검증의 필요성을 따져야 한다.
그 외에 다른 분들 코드를 보며 3주차에 반영하고 싶은 것들
- 인터페이스를 이용한 테스트 작성
자동차내에서 우승과 관련한 코드를 어떻게 테스트할 수 있을 까란 고민이 있었다. 해당 고민의 대부분은 random에 대한 고민이었다. 어떻게 random을 처리할 수 있을 지에 대해 고민을 했고 random을 과정을 포함하도록 코드를 작성했다. (count가 원하는 값이 될때까지 move 시도하는 방법을 통해서...) 하지만 이는 테스트 진행 시간을 오래 잡아먹는 코드가 된다. 테스트는 간결하고 명확할 수록 좋다. 특히 단위테스트라면! 다른 분들의 코드를 보며 이를 interface로 추상화시켜 해결한 코드들을 확인할 수 있었다. random과 관련한 부분을 추상화시켜 `전진 전략`을 통해 랜덤을 이용해서 전진 전략과 무조건 전진 전략으로 나눈 코드를 확인할 수 있었다. 따라서 테스트 진행에 있어 규모가 커지거나 문제가 발생한다면 그것을 추상화해 외부에서 주입받는 구조로 구현할 수는 없는 가에 대해 고민해보는 것도 좋겠다 라는 걸 배울 수 있었다.
- 팩토리 메서드 패턴
다른 지원자의 코드에서 Factory라는 클래스를 확인할 수 있었다. 팩토리 패턴에 대해 공부해 보면서 프리코스 기간동안 관련된 부분이 있다면 사용해보면 좋을 것 같다.