다른 사람들이 작성한 코드를 보면서 알게 된 내용을 정리하면서 공부하려고 한다.
회고 과정을 통해 JAVA에 대한 기초적인 지식이 부족하여 사소한 것이라도 꼼꼼히 배우는 게 목표다.
공통 피드백 정리
1. 문자열 혹은 숫자를 static final 키워드를 사용하여, 의미 있는 이름과 함께 사용해야 한다.
2. 클래스 내부에서는 상수/클래스 변수 -> 인스턴스 변수 -> 생성자 -> 메서드 순으로 작성한다.
3. carNameList 처럼 변수명 안에 자료형과 자료 구조를 포함시키지 말아야 한다.
4. 한 메서드에는 하나의 기능만 처리할 수 있도록 한다.
특히, 여러 메서드에서 중복 사용되는 코드가 있다면 별도로 분리하여 사용할 것.
한 메서드 안에서 코드가 15라인 이상이 되지 않도록 메서드를 분리할 것
5. 테스트 코드 작성을 통해 코드의 문제점을 빠르게 발견할 수 있고, 구조와 의도를 이해하는 데 도움이 된다.
6. 작은 단위의 테스트를 만들어야 한다. 큰 단위로 할 경우, 문제를 발견하기 까지 오래 걸린다.
다른 사람들 코드를 통한 회고
1. class를 기능별로 여러 개 나누어 구현하기
나는 racing 이라는 클래스를 하나만 만들어서 그 안에서만 구현 코드를 작성했다. 하지만 하나의 클래스에는 최대 3개의 메서드와 최대 10개의 생성자를 포함하는 것이 적당하다고 한다.
<< 클래스를 나누는 이유 >>
- 단일 책임 원칙을 준수한다. (하나의 클래스는 하나의 책임만 갖도록 한다.)
- 유지보수성이 좋아진다.
- 다른 프로젝트나 기능에서 재사용이 가능하다. (코드 중복 구현을 방지)
- 테스트 코드를 작성할 때 특정 클래스만 독립적으로 테스트하면서 더 쉽고 빠르게 문제점을 발견할 수 있다.
단일 책임 원칙이 가장 중요한데, 창피하게도 메서드에만 적용하는 원칙인 줄 알고 오직 메서드 분리에만 신경썼었다. ㅠㅠ
1-1. MVC 패턴
MVC 패턴이란, model / view / controll 을 말한다.
유지보수성, 재사용성, 확장성을 위해 사용한다.
Model
a. 데이터와 비즈니스 로직을 담당하며, DB 연결, 데이터 처리, 핵심 로직을 포함한다.
b. 데이터의 CRUD를 주로 담당한다.
c. view와 controller에 대한 정보를 알면 안된다.
View
a. UI를 담당하며, 화면에 정보를 표시하거나 사용자의 입력을 받아온다.
b. 모델의 정보를 저장하고 있으면 안된다.
Controller
a. Model과 View를 연결한다. 사용자의 요청은 Model을 호출하여 수행하고, 결과를 View로 전달한다.
여기서 Model은 한번 더 역할별로 나뉜다!
- Entity / Domain : DB의 테이블 구조를 반영하는 클래스며, 데이터의 속성과 데이터 간의 관계를 정의한다.
- DTO : 보통 Controller -> View로 데이터를 전달할 때, 데이터의 형식을 지정해주는 역할을 한다.
- Repository : 데이터에 접근, 조회, 저장 역할을 담당한다.
- Service : 비즈니스 로직을 처리하며, repository와 다른 service를 호출하여 로직을 구현한다.
그럼 이제 다른 사람들은 MVC 패턴을 어떻게 적용했는지 확인해보겠다.
Model
- domain : 자동차 이름 / 자동차 / 여러 자동차
- 변수 / 생성자 / get() / 그외 관련 로직 / 검증 class (검증과 관련된 모든 로직 포함)
View
- 입력
- 문자열 및 정규화식 변수 / 이름 입력 / 횟수 입력 / 검증 class
- 출력
- 콘솔 상 입력
- 콘솔 상 출력
Controller
- 입출력 view 선언 / 생성자 / run()
🤓알게 된 점
- 관련된 로직들끼리만 하나의 클래스에 작성하니 가독성 및 유지보수성이 훨씬 좋아졌다는 걸 알 수 있었다. 반면 지금 내가 작성했던 코드들은 하나의 클래스에 작성되어 뭐가 car에 관련된 코드고, 뭐가 racing에 관련된 코드인지 구분하기 어렵다. 🥹
🤔궁금한 점 + 해결
domain 안에 검증 로직을 작성하는 것보고 service의 역할과 혼동이 왔다. 그리고 찾아보니 service를 사용한 사람과 사용하지 않은 사람들로 나뉘었는데,, 정확히 둘의 역할을 어떻게 구분해야 하는지 생각해봤다.
Domain
- 주로 객체의 상태와 관련된 데이터를 관리한다. 객체의 상태에 대한 유효성 검증 로직(나이, 이름 형식 등)도 포함할 수 있지만, 복잡한 비즈니스 로직을 처리하는 건 좋지 않다.
Service
- 도메인 객체들을 조합하여 실제 애플리케이션 로직을 수행한다. 도메인 계층에만 의존할 수 없는 복잡한 규칙을 검증할 때는 service에서 수행한다. (도메인 객체 간의 관계 / 외부 요청이나 DB 상태에 따른 조건 검사 등)
이번 과제에서는 인풋값 형식 위주로 검증을 진행하기 때문에 굳이 Service에서 검증 로직을 하지 않아도 될 것 같다!
1-2. Util
코드 구경을 하던 중, 폴더 명을 util로 나눈 것을 보고, 무슨 의도인지 궁금하여 찾아봤다.
util은 프로젝트에서 공통 로직을 한 곳에 모아서 재사용성을 높이고, 코드의 중복을 줄이는 역할을 한다.
util은 상속 및 인스턴스화 하지 못하도록 구현해야 한다. 이를 위해 final class로 구현하여 기본 생성자를 pirvate으로 선언한다. 인스턴스화를 방지하기 위해 특정 객체의 상태를 가지면 안되며, 오직 input / output에 대한 연산만 처리하도록 한다. 또한 정적 메서드로만 구성되어 있다.
상태를 가지지 않는다는 것은 객체가 데이터를 저장하지 않는다는 것이다. 유틸리티 클래스는 인스턴스 변수나 필드가 없고, 외부에서 값을 받아 사용한다.
문제는 외부에서 전달받은 인수만을 통해 작업을 처리한다면 자율성이 없어진다. 자율성은 객체가 상태와 행동을 결합하여 스스로 작업을 수행하도록 하는 것이다. 자율성이 없다는 것은 객체지향적인 것이 아니라는 것이다. 다른 클래스가 util 클래스에 의존성이 높아지며, 함부로 설계할 경우 util 클래스가 너무 많은 책임을 가지게 된다.
따라서 Util 클래스를 사용하기 전에 적절한 기준을 정하여 효율적으로 작성해야 한다.
그리고 수정을 최소화하는 것이 좋다. 여러 클래스나 모듈에서 공통적으로 사용하는 기능이기 때문에 잦은 수정이 있다면 여러 코드에 영향을 줄 수 있기 때문이다.
2. 정규화 적용
입력 받은 값이 숫자 형식인지 검증할 때, 나는 try catch 문을 사용하여 만약 문자열이 숫자로 변경되지 않을 경우 예외처리를 하는 방식으로 진행했다. 전체적으로 이번 주차에서는 형식 검증에 정규화 적용을 하지 않았다.
하지만 숫자 형식이나 영어 형식 등 입력값에 대한 형식 검사를 진행할 때는 정규화 식을 사용해서 진행하는 게 더 가독성 및 재사용성이 좋다고 느껴졌다.
추가로 정규화식은 static final 키워드와 유의미한 변수명을 사용하여 가독성을 신경쓰도록 하는 게 좋다!
3. 출력 메시지 Enum 관리
지금까지는 예외처리 Error Code만 Enum으로 관리해오고, 출력문들은 final 키워드를 사용하여 class 내부에서 사용했었는데 출력문들도 모두 Enum으로 관리해도 괜찮을 것 같다는 피드백 글을 봤다.
[Enum 사용 장점]
- 가독성 향상 (의미있는 이름 사용)
- 타입 안정성 보장 (허용된 값만 사용 가능)
- 유지보수 용이
- 상수에 message 같은 속성을 추가하는 등, 메서드 및 속성 추가가 가능하다.
사실 class 내부에서 static final 키워드와 함께 사용하는 것도 가독성 향상과 타입 안정성 보장이 된다고 생각을 해서 큰 필요성은 느껴지지 않았었다.
하지만 출력문들을 한 곳에서 관리하면 한 눈에 쉽게 볼 수 있게 되면서 편리하게 관리할 수 있고, 유지보수성 및 재사용성이 훨씬 좋아진다는 점을 고려한다면 enum을 사용하는 게 좋을 것 같다.
4. record
불변(immutable) 데이터 객체를 간단히 정의할 수 있는 클래스 유형이다.
Java 16부터 정식 기능으로 채택된 새로운 클래스인데 이번 과제에서 꽤 많은 분들이 적용한 것을 봤다.
[record 특징]
- 데이터 저장 및 전달을 위한 객체 생성 시, 필요한 코드를 줄여준다.
- getter / equals() / hasCode() / toString() 메서드가 자동 생성된다.
- 각 필드는 final로 선언되어 값을 변경할 수 없으며, 상속할 수 없다.
- 코드의 가독성이 좋아진다.
public record Person(String name, int age) { }
위 코드가 아래 코드를 대체하는 것이다.
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return name;
}
public int age() {
return age;
}
@Override
public boolean equals(Object o) {
// equals() 자동 생성
}
@Override
public int hashCode() {
// hashCode() 자동 생성
}
@Override
public String toString() {
// toString() 자동 생성
}
}
확실히 가독성이 좋아져서 적절하게 사용하기 좋다고 느껴졌다.
record를 사용할 경우, 불변 데이터 객체를 관리하는 것이 목적임을 고려하며 로직을 작성하는 게 중요할 것 같다.
2주차에서는 전체적으로 많이 아쉬운 코드를 작성한 것 같다. 🥹
mvc 패턴을 알고 있기는 했지만, 여기서 적용해야 된다는 걸 이제서야 알았다. .. 기초적인 부분이 정말 많이 부족하다는 걸 알았다. 다시 천천히 강의를 들으면서 기초를 탄탄히 다지고 싶다.
그리고 위에 쓴 것 외에도 java에서 제공하는 함수에 대해 더 공부해봐야 할 것 같다. filter() 나 stream().map().tolist()나 이런 부분이 다른 사람들에 비해 많이 부족하게 느껴진다.
[출처]
https://seoarc.tistory.com/122
https://velog.io/@ikjo39/%EC%9C%A0%ED%8B%B8-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%9E%80
https://velog.io/@ozragwort/Util-%ED%81%B4%EB%9E%98%EC%8A%A4%ED%8C%A8%ED%82%A4%EC%A7%80