❐ Description
2주차 작업 과정에서 느꼈던 점과 과제 후기를 작성한다.
❐ Source code
❐ Self - Review
1. [문자열 덧셈 계산기]에서 받은 피드백을 적용해보았는가?
최초 개발 과정에서는 저번 피드백을 최대한 반영하면서 개발을 해보았다.
- 모든 상수를 하나의 클래스에서 관리
- 패키지명에 대한 피드백 (Infra)
- 책임이 많은 도메인
- [Error]를 Prefix로 설정해보기
- 부정어를 사용한 boolean 네이밍
- 등등..
이걸 최대한 다 적용해보면서 1차 개발을 했는데, 개발이 답답하고 굳이 이렇게 까지? 해야하나? 싶었다.
그래서 최종적으로 적용한 피드백은 아래와 같다.
- Infra라는 패키지 네이밍에 대한 피드백
- 책임이 많은 도메인 설계
- 너무 많은 Enum 사용
- 팩토리 메서드 패턴의 네이밍 관련 피드백
- 싱글톤 동시성 문제 관련 피드백
- AppConfig를 활용해보기
- 상수를 한 곳에서 꼭 관리해야 할까?(self-feedback)
- BigInteger를 꼭 사용해야 할까?(self-feedback)
#️⃣ Infra라는 패키지 네이밍 & 책임이 많은 도메인
같이 스터디하는 동훈님, 영철님의 피드백이였다.
사실 Infra라는 패키지 명은 애플리케이션과 외부 서비스간에 필요한 로직이 들어가는 것이 맞다.
그래서 Controller와 View를 하나의 패키지를 묶고 싶었고, 여러 네이밍을 고민하다가 Infra라는
패키지 명을 쓰게 된 것이다.
하지만 이게 통상적이지는 않아서 2주차 과제에는 이를 걷어내고 Controller와 View를 각각의
패키지로 분리해주었다. 이렇게 하니깐 MVC 패턴을 사용했음이 눈에 쉽게 띄였다.
그리고 model 패키지를 보면 다음과 같이 구성되어 있다.
이번 과제는 자동차 경주에 관한 것이기 때문에 실제 F1 경기를 모티브 삼아 너무 작지도 않게, 그렇다고 너무
크지도 않게 최대한 설계해봤다.
- Car : 경주하는 차와 관련된 정보
- DashBoard : 결과를 표출해주는 전광판에서 연상
- Position : 트랙위에 있는 차의 위치관련 정보
- Race : 경주가 관리하는 정보
#️⃣ 너무 많은 Enum 사용
사실 Enum을 많이 사용하는 건 문제 없는데 적절한 곳에 사용하냐에 대해서 생각해봤다.
예를 들어 에러 메시지를 관리하는 클래스가 꼭 Enum일 필요가 있을까? 이 부분에 대해서
고민한 결과 내 대답은 NO로 판결났다.
public enum ErrorMessage {
public static String SHOULD_NOT_BE_NULL = "[ERROR] NULL이여서는 안됩니다.";
// getter 필요함
}
enum을 사용하면 불필요한 Getter를 정의해줘야 하기 때문이다.
#️⃣ 팩토리 메서드 패턴의 네이밍 관련 피드백
이 부분은 통상적인 규칙을 따라가는게 무난할 것 같아서, 이펙티브 자바에 적힌대로 적용했다.
단, 아래와 같이 최초에 값을 할당해줘야 할 경우에는 initiate라는 네이밍을 사용하기로 정했다.
public static Position initiate() {
return new Position(EMPTY_STRING);
}
#️⃣ 싱글톤 패턴 사용시 동시성 문제
이 부분은 별도의 포스팅에서 다룰 예정이다. 그 내용이 많기도 하고 제대로 이해하고 넘어가야 하기 때문이다.
TODO : 포스팅하고 링크 변경하기
#️⃣ AppConfig를 통한 DI
애플리케이션 설정과 객체 생성을 분리하기 위해 별도의 설정 클래스를 만들어 의존성을 주입하는 방법이다.
옛날에 김영한 쌤의 인강을 들을 때 들어봤던 기억도 난다.
public class AppConfig {
private AppConfig() {
}
public static class AppConfigHolder {
private static final AppConfig INSTANCE = new AppConfig();
}
public static AppConfig getInstance() {
return AppConfigHolder.INSTANCE;
}
public MainController initializeMainController() {
return new MainController(initializeRaceService());
}
private RaceService initializeRaceService() {
return new RaceService();
}
}
#️⃣ 상수를 한 곳에서 꼭 관리해야 할까?(self-feedback)
상수 관련해서 사람들이 굉장히 많은 이야기를 하는 것 같아서 혼자 한 번생각해 봤는다.
나의 결론은 다음과 같다. "도메인과 연결된 상수라면 해당 도메인 클래스에서 관리한다."
아래는 Constant를 한 클래스에 모아서 작업한 부분이다.
public class RaceConstant {
public static final int MIN_SPEED_TO_MOVE = 4;
public static final long DEFAULT_LAP_COUNTING_POLICY = 1;
public static final String POSITION_ICON = "-";
private RaceConstant() {
}
}
하나 씩 보면
- MIN_SPEED_TO_MOVE : Speed 도메인과 연결 됨
- DEFAULT_LAP_COUNTING_POLICY : Lap 도메인과 연결 됨
- POSITION_ICON : Position 도메인과 연결 됨
도메인의 메타 데이터가 파편화되어 있다. 1주차에 다른 분 코드 리뷰하면서 파편화라는 피드백을 드린거 같은데
나 또한 메타 데이터가 파편화된 설계를 했던 것이다.
그래서 각 상수를 연관된 도메인으로 옮겨 주었더니 Constant 클래스가 사라졌다. (갠 적으로 맘에듬)
그럼 이제 Constant를 언제 사용할까에 대한 규칙도 정해야 한다. 예를 들면 휴면 계정 전환 시기(300일)와
같은 정말 System 전반에 걸쳐서 사용되는 값 같은 경우는 Constant로 빼도 될 것 같다.
# Example 1
public List<String> sample(String source) {
return String.split(SPLIT_DELIMITER, source);
}
# Example 2
public void showWinners(final DashBoardView view) {
OutputView instance = OutputView.getInstance();
String resultMessage = String.format(RACE_WINNERS, view.getWinners());
instance.simplePrint(resultMessage);
instance.closeConsole();
}
위 코드에서 SPLIT_DELIMITER에 어떤 값이 할당 됐는지 바로 알 수 있을까? 무조건 SPLIT_DELIMITER가
정의된 클래스로 이동하고 해야 할 것이다. 결과적으로 무조건적인 Constant화는 코드를 읽음에 있어서 걸림돌이 된다.
Example2에서도 똑같은 문제가 있지만 RACE_WINNERS는 출력 문자("최종 우승자 : %s")이다.
그러나 이거는 출력이라는 도메인에 속하게 된다. 따라서 이 부분은 괜찮을 것 같다.
빈 문자열("")의 경우, 프로젝트 전반에 걸쳐 많이 사용된다. 또한 종속되는 도메인이 없다.
이런 경우는 상수를 하는게 코드의 중복도와 파편화는 낮추고, 재사용성은 높여주는 장점을 얻을 수 있다.
결과적으로 2주차 과제를 하면서 내린 결론은 아래와 같다.
"어떤 도메인에도 종속되지 않는 데이터" 외에는
도메인과 연결된 곳에 정의한다.
무조건 적인 상수화는 오히려 가독성을 헤친다.
#️⃣ BigInteger를 꼭 사용해야 할까?(self-feedback)
처음에 Lap 클래스를 BigInteger로 설계했다. 왜냐면 저번에 큰수 관련해서 사람들끼리 이야기하는 것을
흘겨봐서 진짜 BigInteger를 왜 쓰는걸까?에 대한 호기심이 있었다.
그래서 처음에는 BigInteger로 작업을 했다. 나의 결론은 "굳이 BigInteger까지...?"다.
- long도 엄청난 범위의 수를 커버한다.
- 하나를 BigInteger로 쓰면 다른 클래스를 만들 때도 이를 계속 염두해야 한다.(작업성 별로...)
- BigInteger로 어마어마하게 큰 수를 넣어서 application을 구동하면 안 끝난다..
물론 저번 과제는 반복적으로 하는 로직이 없어서 적합할 수 있는데, 적어도 이번 과제에서는 아닌거 같다.
그리고 실무에서도 long으로 충분히 무리 없이 개발을 진행했다. (금융 쪽은 많이 사용할 것 같다.)
아무튼, 담 부터 BigInteger는 우선 고려 대상에서 제외 할 것이다.
2. 설정 목표를 성실히 수행했는가?
#️⃣ 최적화된 요구사항 분석 방법 찾기
이번에도 1주차와 똑같이 User와 System으로 분리하여 진행했다. 이번에는 Exceptions라는 파트를
만들어서 과제하면서 생각나는 예외나 특이사항들을 정리해봤다. 예외 같은 경우에는 시간을 잡아놓고
생각한다고 해서 다 찾을 수 있는게 아니라서, 생각날 때마다 업데이트하는 식으로 작성해주었다.
이제 슬슬 윤곽이 잡힌다. User,System 그리고 Exceptions. 이렇게 구조를 잡으면 실수만 안하면
요구사항을 정확히 파악하여 작업을 두번 하는 일은 없을 것 같다.
#️⃣ 성장에 대한 욕구가 강한 개발자 네트워크 형성하기
스터디에 들어갔다!! 안그래도 클린코드, Readable한 코드에 대해서 관심도 많고 관련 능력도 키우 싶었는데
동훈님이 스터디를 오픈해주셔서 들어가게 됐다. 스터디 준비 뿐만 아니라 하루 일과를 카톡에 올리는게 굉장히
맘에 든다. 이게 자극도 되고 목표라는게 생겨서 그런 듯 싶다. 내일부터 1회차 진행하는데 적극적으로 임할 생각이다.
#️⃣ 아는 것과 모르는 것을 명확하게 구분하기
이번 과제에서는 Java Functional Interface를 쓰기를 목표로 삼아봤다.
물론 Java Functional Interface 무엇인지 왜 쓰는지는 알고 있긴한데, 직접 내가 사용해 본 경험은 적다.
그래서 이번 과제에서는 `BiPredicate`를 사용해서 요구사항을 해결해 봤다.
물론 한 번 이렇게 사용했다 해서 100% 체득화 됐다고 생각하지는 않는다.
다만, 이제 언제 어디서 Java Functional Interface를 쓰면 되겠구나 하는 감은 좀 생긴 것 같다.
다음 과제를 설계하는 과정에서 Java Functional Interface를 사용할 수 있다면 적극 활용해 볼 것이다.
2. 과제 후기
확실히 1주차 보다 더 재밌엇다. 왜냐면 다른 사람들의 피드백을 적용하는 그런 제약이 걸려서 그런거 같다.
1주차 과제를 한 나와 2 주차 과제를 한 나를 비교해보면, 코드를 보는 시야, 설계할 때 고려하는 항목들,
체계적인 요구사항 파악 능력이 조금은 나아진 것 같다. 물론 아직 이를 체득화하는데 더 많은 노력과 시간을
쏟아야 한다.
2주차 과제 [자동차 경주]에서는 문서화에도 굉장히 많은 신경을 썼다. 문서화를 소홀히하게 되면 과거 히스토리
파악하는데 불필요한 리소스가 낭비되기 때문이다. 실무에서도 과거 커밋이력을 찾으려고 시간을 쫌 많이 쓴
경험이 있어서 더더욱 필요하다고 생각했다.
그리고 이번에는 현실 세계의 "자동차 경주"에 대해서 알아보고 과제를 시작했다. 결국에 개발을 하기 위해서는
현실 세계의 도메인에 대해서도 충분히 이해를 하고 있어야 한다고 생각한다. 그래서 실제 자동차 경주에서
사용하는 용어를 최대한 사용하려고 노력했다.
물론 문제도 없진 않았다. 경기가 진행 중임을 표현하는 "is underway"라는 표현을 사용했는데, 해당 단어를
처음 접해보는 사람 입장에서는 이해하기 어려운 네이밍이 될 수도 있을 것 같다.
그래도 이번 과제를 하면서 우리가 현재 살고 있는 실제 세상을 코드로 구현하는 과정이 굉장히 흥미로웠고,
새로운 시각으로 개발을 바라 볼 수 있어서 또 다른 재미를 느낄 수 있었다.
'우테코 7기 > 2주차' 카테고리의 다른 글
일급 객체 ,일급 컬렉션, Value Object (0) | 2024.10.30 |
---|---|
[Refactoring] 제어할 수 있는 코드를 작성하자! (0) | 2024.10.27 |
[Refactoring] 패키지가 무엇을 제공하는지 전달하자! (0) | 2024.10.27 |
자동차 경주 (0) | 2024.10.21 |