컨트롤러가 필요할까
무의식적으로 MVC 구조를 계속 사용하고 있었고, 당연히 Controller 클래스를 항상 정의해왔다.
리뷰어 분이 main에서 정의하지 않고 컨트롤러를 쓰는 이유를 여쭤보셨는데, 나는 다음과 같이 생각했다.
'main은 시스템의 엔트리포인트 역할을 한다. 그렇기에 단순히 컨트롤러 등의 호출 순서를 조정해주는 역할이라고 생각한다'
그렇기 때문에 컨트롤러를 사용한 것인데, 계속 생각해 본 결과 컨트롤러가 MVC 패턴이 아니라면 굳이 필요할까? 싶기도 한다.
도메인 영역이 구성이 잘 되어있다면 컨트롤러는 존재하지 않아도 된다.
도메인이 해결할 수 없는 부분을 해결하기 위해 컨트롤러가 존재하는 것이기 때문이다. 이는 Service Layer의 존재 의의와도 비슷하다.
결국 핵심은 도메인인데, MVC 모델에 빠진 나머지 아무 생각 없이 컨트롤러를 사용하고 있었던 것은 아닌가? 생각이 들었다.
상수의 경우 private static final을 사용한다
리뷰어분이 상수는 private static final로 사용하시라길래, 처음에는 의문이 들었다.
'엥, private final 만으로도 괜찮은 것 아닌가? 굳이 static을 사용해 메모리에 영구박제(?) 시킬 필요가 있나?'
그래서 조금 찾아봤는데, 굳이 static을 안붙일 이유도 없다고 한다.
애초에 상수는 인스턴스마다 달라지지 않기 때문이다.
인스턴스마다 공통된 값을 공유한다라는 키워드에서 static을 사용해야겠구나! 를 짐작할 수 있다.
무심하게 상수를 선언하다가 이번에 처음 알게 된 부분이다 .. ㅎ
왜 자바에서 final 멤버 변수는 관례적으로 static을 붙일까?
유효성 검증을 어디서 할까
자동차 경주 미션에서는 다음과 같은 예외가 존재했다.
1. 자동차 이름이 5자를 넘어가는 경우
2. 시도 횟수가 음수인 경우 등
시스템을 설계하다보면 필연적으로 처리해야 할 예외가 발생한다.
그렇기에 올바른 데이터가 입력되었는지 검증할 필요가 생긴다.
그러면 View 단에서 예외처리를 해야 하는가, Domain 단에서 예외처리를 해야 하는가?
이 문제로 꽤 오랫동안 고민했던 것 같다.
그래서 페어(하디)와 함께 고민해 본 결과 결론이 안나서, 우선은 InputValidator에서만 검증하고 나중에 조언을 받기로 했다.
리뷰어분이 이러한 고민을 보고 해주신 말씀은 다음과 같다!
-> View와 Domain, 둘 중 한 곳에서만 검증을 하는 것은 올바르지 않다. 검증의 유형에 따라 나뉘게 된다.
모든 View에 적용되는 제약조건은 Domain이, 특정 View에만 적용되는 제약조건은 View 단에서 검증해야 한다는 것이다.
예를 들어서, 자동차 경주 게임에서 자동차는 컴마(,)를 기준으로 입력되었다.
이것은 View단에서 처리해야 할 조건이다. 나중에 컴마가 아닌 콜론(:)으로 입력받는다면? 뷰의 검증조건만 바뀌어야 할 것이다.
'자동차 이름이 5자 이하', 이것은 도메인에서 검증해야 한다. 모든 뷰에 공통적으로 적용되는 로직이다.
'변하는 것과 변하지 않는 것' 을 구분해 변하면 안되는 것을 도메인 안으로 넣어두는 것이 중요하다는 것을 느꼈다 🫠
예외 메세지를 도메인 안에 넣었다고?
// CarName 클래스
private static final String CAR_NAME_EMPTY = "자동차 이름이 없구나..";
private void validateCarNameIsNotEmpty(String carName) {
if (carName.isBlank()) {
throw new IllegalArgumentException(CAR_NAME_EMPTY);
...
위와 같은 스타일이 내가 지금까지 사용해왔던 스타일이다.
그런데 리뷰어분이 '다국어 뷰가 생기는 경우 Domain이 변경되어야 하지 않는가?' 라는 질문을 주셨다.
생각해보니 그렇다. 사용자에게 보여줄 메세지를 도메인이 정의하고 있다. 왜 눈치채지 못했을까?
의존성의 방향이 도메인 <- 뷰 가 아니라, 도메인 -> 뷰가 되는 것이다. 즉, 도메인은 뷰에 대해 알게된다.
그렇기에 고민해 본 결과, 커스텀 예외를 써야 할 것 같았다.
따라서 도메인 영역에서는 커스텀 예외만 던지고(내부 로직은 전혀 없는),
View 단에서 예외를 판단하고(instanceof 연산자를 쓴다던지), 메세지를 정의하는 방식이 맞는 것 같다.
그런데 대부분의 코드(크루들이나 심지어는 포비 코드가!) 도메인에서 Message를 정의하고 있는 것 같았는데,
예외 메세지를 밖으로 빼는 것이 맞지 않을까? 라는 생각이다. (반란군이 되고 싶어요)
이것을 자동차 경주 게임에서는 오버엔지니어링으로 생각할 수도 있겠지만, 나는 좋다고 생각한다!
앞서 말한 방식을 조금 더 연구해봐야겠다 .. 다음 미션부터는 적용해보면 좋을 것 같은데 말이다 🧐
놀람 최소화 원칙
이름은 웃기지만 이 원칙을 어길 때가 많았던 것 같다.
메소드명이나 컨벤션이 필요한 이유도 이것 때문이지 않을까, 싶다!
클라이언트가 예측 가능한 기능을 제공하자.
https://ko.wikipedia.org/wiki/%EB%86%80%EB%9E%8C_%EC%B5%9C%EC%86%8C%ED%99%94_%EC%9B%90%EC%B9%99
결합도는 낮추고 응집도는 높여라
응집도와 결합도의 말을 혼용해서 쓰다가 두 개념이 다르다는 것을 알게 되었다.
응집도 -> 모듈 내부가 얼마나 관련되어 있는지를 나타낸다.
결합도 -> 서로 다른 모듈 간에 상호 의존하는 정도를 나타낸다.
https://madplay.github.io/post/coupling-and-cohesion-in-software-engineering
타입 추론
Java 10에서부터 타입 추론을 지원한다.
구체적 타입을 명시하지 않고, 컴파일러에게 타입을 유추하도록 지정할 수 있다.
사용하는 법은 간단! 'var' 키워드를 사용하면 된다.
var randomBlockGenerator = new RandomBlockGenerator();
RandomBlockGenerator randomBlockGenerator = new RandomBlockGenerator();
위 두 문장은 동일하다.
그럼 언제 사용해야 하는가?
1. 타입이 너무 많은 정보를 포함하고 있을 때
2. 타입을 알 필요가 없을 때 (익명 클래스 등)
등이 있을 것이다. 이외에도 타입보다는 '행위'가 중요하거나, 굳이 타입을 작성하지 않아도 되는 경우
필요에 따라 var를 사용할 수 있다.
다만 주의해야 할 점은 var를 남용하면 오히려 가독성을 해칠 수 있으며, 적시에 사용하는 것이 중요하다!
다음은 var 사용에 관한(타입추론 사용에 관한) Java 가이드라인이다.
https://openjdk.org/projects/amber/guides/lvti-style-guide#G3
'우테코 5기' 카테고리의 다른 글
[레벨 1 강의] 좋은 코드 (0) | 2023.03.01 |
---|---|
[레벨 1 미션] 자동차 경주 미션 회고 (0) | 2023.02.18 |
[레벨 1 강의] TDD, 리팩터링, 자바에 대한 이해 (0) | 2023.02.18 |
[레벨1 강의] 단위 테스트, 코드 품질 (0) | 2023.02.10 |
[우테코] 우아한테크코스 백엔드 5기 최종 합격 후기 (24) | 2022.12.31 |