🎯 목표 설정
- 우아한테크코스에서의 목표: 개발자로서 추구할 가치를 설정하기 위해 끊임없이 도전하며 나 자신을 깊이 알아가는 것이다. 이를 위해 다양한 문제에 부딪히고, 프로젝트에 애정을 가지며 단순한 구현을 넘어 집요하게 리팩토링하는 경험이 필요하다고 느꼈다.
- 프리코스에서의 목표: 따라서 이번 프리코스에서의 목표는 효율적인 리팩토링을 고민하며 이를 코드에 녹여내는 것이다. 이 과정에서 새로운 기술을 적극적으로 도입해 보고, 배운 점을 체계적으로 정리하려고 한다. 또한, 이러한 배움에 우선으로 집중하기 위해 처음부터 클린 코드 원칙을 준수하여 유지보수가 용이한 코드를 작성하고자 한다.
✏️ 주차 별 회고 기록 방법
- 목표 설정: 요구사항을 분석하고 명확한 학습 목표를 세운다.
- 진행 과정: 고민의 흔적이 드러나는 시도와 과정을 기록한다.
- 기술 고도화 및 리팩토링: 새로운 기술을 도입하고, 리팩토링을 통해 코드를 개선한다.
- 클린 코드 원칙 준수 점검 및 코드 개선: 작성한 코드가 클린 코드 원칙을 잘 따르고 있는지 점검하고 개선한다.
- 배운 점 정리: 배운 내용을 정리하고 복습한다.
- 고찰: 느낀 점과 개선할 부분을 기록한다.
- 참고 자료: 참고한 자료의 출처를 남긴다.
위 과정을 매주 블로그에 정리할 계획이다.
👔 자바 스타일 가이드
Google Java Style Guide 및 [코딩규칙] 자바 코딩 규칙(Java Code Conventions)을 참고하여 정리한 내용이다.
- 파일 이름: 클래스의 이름과 동일하게 작성하며, 확장자는 .java로 끝난다.
- 들여쓰기:
2칸 들여쓰기를 기본으로 한다. - 한 줄 길이:
80자 이하로 제한하며, 가독성을 위해 줄 바꿈을 사용한다. - 중괄호 사용: if, for, while 등의 조건문에서 중괄호를 생략하지 않는다.
- 변수 이름: lowerCamelCase로 작성하고, 명확한 의미를 갖도록 한다.
- 상수 이름: UPPER_SNAKE_CASE로 작성한다.
- 구현 주석: 코드의 로직을 설명하며, 코드를 이해하는 데 필요한 정보만 포함한다. (형태: //)
- 문서화 주석: Javadoc을 활용하여 클래스나 메서드의 기능을 설명한다. (형태: /** ... */)
- 메서드 이름: lowerCamelCase로 작성하며, 동작을 나타내는 동사 또는 동사구로 사용한다.
- 클래스 이름: UpperCamelCase로 작성한다.
- 메서드 인자 제한: 한 메서드에서 인자는 4개 이상을받지 않도록 하고, 가급적 3개 이하로 줄인다.
- else 예약어 사용 금지: else는 피하고, 명확한 조건을 사용한다.
- 디미터 법칙: 한 줄에 점(.)은 하나만 사용하며, 내부 객체에 직접 접근하지 않는다.
- Getter/Setter 사용 자제: 도메인 객체에서는 Getter/Setter를 지양하고, 객체의 상태는 행동을 통해 변경한다.
- 메서드의 책임 분리: 메서드는 하나의 역할만 담당하도록 설계한다.
➕ 우아한테크코스 스타일 가이드
우아한테크코스의 자바 스타일 가이드는 Google Java Style Guide를 기반으로 하며, 몇 가지 차이점을 반영하여 정리한 내용이다. Google Java Style Guide와 다른 부분은 아래와 같다.
- 들여쓰기: 4칸 들여쓰기를 기본으로 한다.
- 한 줄 길이: 최대 120자로 제한된다.
- 들여쓰기 지속: 줄이 너무 길어져 줄 바꿈이 필요할 경우, 그 다음 줄은 원래 줄에서 최소 +8칸 이상 들여쓰기를 해야 한다.
- 수직 빈 줄: 빈 줄은 가독성을 높이기 위해 적절한 위치에 사용할 수 있다. 클래스의 첫 번째 멤버나 초기화 앞에 있는 빈 줄을 강제하지 않는다.
📚 클린 코드 원칙 정리
우아한테크코스 문서 저장소에 있는 우아한테크코스 클린코드 원칙을 참고하여 정리한 내용이다.
▶ 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
- 이유: 들여쓰기가 많아지면 코드의 복잡해지고 가독성이 떨어진다.
- 방법: 메서드 내에서 중첩된 로직을 피하고, 한 단계의 들여쓰기로 제한한다.
🔽 좋은 예시: 한 단계의 들여쓰기
public void printName(User user) {
if (user == null || !user.isActive()) {
return;
}
System.out.println(user.getName());
}
🔽 나쁜 예시: 여러 단계의 들여쓰기
public void printName(User user) {
if (user != null) {
if (user.isActive()) {
System.out.println(user.getName());
}
}
}
▶ else 예약어를 쓰지 않았는가?
- 이유: else는 코드의 흐름을 복잡하게 만든다.
- 방법: early return을 사용하여 else 구문을 제거한다.
🔽 좋은 예시: early return 사용
public void login(User user) {
if (!user.isActive()) {
System.out.println("유저가 활성화되지 않았습니다.");
return;
}
authenticate(user);
}
🔽 나쁜 예시: else 사용
public void login(User user) {
if (user.isActive()) {
authenticate(user);
} else {
System.out.println("유저가 활성화되지 않았습니다.");
}
}
▶ 모든 원시값과 문자열을 포장했는가?
- 이유: 원시값이나 문자열을 직접 사용하는 것은 편리해 보이지만, 의미가 불분명해지고 코드가 복잡해진다.
- 방법: 값을 객체로 포장하는 클래스를 사용하면 해당 값의 의미를 명확히 하고, 관련 로직을 클래스 내부에 캡슐화할 수 있다.
🔽 좋은 예시: 원시값을 객체로 포장
public class Age {
private final int age;
public Age(int age) {
if (age < 0) {
throw new IllegalArgumentException("나이는 음수일 수 없습니다.");
}
this.age = age;
}
}
🔽 나쁜 예시: 단순한 int 값 사용
public class Person {
private int age;
}
▶ 컬렉션에 대해 일급 컬렉션을 적용했는가?
- 이유: 일급 컬렉션이란, 컬렉션을 감싸는 클래스를 만들어 해당 컬렉션을 처리하는 로직을 한 곳에 모으는 방법이다. 이를 통해 데이터 무결성을 유지하고, 관련 비즈니스 로직을 캡슐화할 수 있다. 또한, 불변성을 보장하고, 코드의 응집도가 높인다.
🔽 좋은 예시: 리스트를 일급 컬렉션으로 포장
public class Members {
private final List<Member> members;
public void add(Member member) {
members.add(member);
}
public List<Member> getMembers() {
return Collections.unmodifiableList(members);
}
}
public class Team {
private Members members;
public void addMember(Member member) {
members.add(member);
}
}
🔽 나쁜 예시: 리스트를 직접 사용
public class Team {
private List<Member> members;
public void addMember(Member member) {
members.add(member);
}
}
▶ 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
- 이유: 클래스가 3개 이상의 인스턴스 변수를 가지면, 단일 책임 원칙을 위반할 가능성이 높다. 이는 응집도를 낮추고 유지보수를 어렵게 만든다.
- 방법: 관련 변수를 하나의 클래스로 묶어 새로운 객체로 분리한다.
🔽 좋은 예시: 객체 분리
public class Person {
private String name;
private int age;
private ContactInfo contactInfo; // ContactInfo로 묶음
}
public class ContactInfo {
private String address;
private String phoneNumber;
}
🔽 나쁜 예시: 너무 많은 인스턴스 변수
public class Person {
private String name;
private int age;
private String address;
private String phoneNumber;
}
▶ getter/setter 없이 구현했는가?
- 이유: getter/setter는 객체의 상태를 외부에서 쉽게 변경하거나 접근할 수 있게 하므로 캡슐화를 위반할 수 있고, 데이터 무결성이 깨질 수 있다.
- 방법: 객체는 스스로 상태를 관리하는 방식으로 설계한다.
단, DTO는 예외적으로 getter/setter 사용이 허용된다.
🔽 좋은 예시: 메서드를 통해 상태 관리
public class Account {
private int balance;
public void deposit(int amount) {
if (amount <= 0) {
throw new IllegalArgumentException("유효하지 않은 금액입니다.");
}
this.balance += amount;
}
public void withdraw(int amount) {
if (amount > balance) {
throw new IllegalArgumentException("잔고가 부족합니다.");
}
this.balance -= amount;
}
public int getCurrentBalance() {
return balance;
}
}
🔽 나쁜 예시: getter/setter 사용
public class Account {
private int balance;
// getter
public int getBalance() {
return balance;
}
// setter
public void setBalance(int balance) {
this.balance = balance;
}
}
▶ 메서드의 인자 수를 제한했는가?
- 이유: 메서드에 많은 인자를 전달하면 코드가 복잡해지고 가독성이 떨어진다. 또한, 인자의 순서나 역할을 헷갈릴 수 있어 유지보수와 테스트가 어렵다.
- 방법: 4개 이상의 인자를 넘기지 않도록 하고, 필요하면 DTO나 객체로 묶어서 사용한다.
🔽 좋은 예시: 객체로 묶기
public class UserInfo {
private String name;
private int age;
private String address;
private String phoneNumber;
}
public void createUser(UserInfo userInfo) {
// 유저 생성 로직
}
🔽 나쁜 예시: 4개 이상의 인자 전달
public void createUser(String name, int age, String address, String phoneNumber) {
// 유저 생성 로직
}
▶ 코드 한 줄에 점(.)을 하나만 허용했는가?
- 이유: 코드 한 줄에 여러 개의 점(.)을 사용하면 메서드 체이닝이 발생해 코드가 복잡해지고 가독성이 떨어진다. 객체 간 결합도가 높아져 유지보수가 어려워진다.
- 방법: 디미터 법칙을 지키기 위해 중간 객체에 관련된 로직을 처리하도록 메서드를 추가해 체이닝을 분리한다.
디미터 법칙(Law of Demeter): "친구하고만 대화해라"는 원칙으로, 객체는 직접 관계가 있는 객체와만 상호작용해야 하며, 그 객체의 내부 객체에 직접 접근해서는 안 된다는 원칙이다.
🔽 좋은 예시: 중간 객체 분리
public void printUserAddress(User user) {
ContactInfo contactInfo = user.getContactInfo();
Address address = contactInfo.getAddress();
String street = address.getStreet();
System.out.println(street);
}
🔽 나쁜 예시: 점(.) 여러 개 사용
public void printUserAddress(User user) {
System.out.println(user.getContactInfo().getAddress().getStreet());
}
▶ 메서드가 한 가지 일만 담당하도록 구현했는가?
- 이유: 메서드가 여러 가지 일을 동시에 처리하면, 응집도가 낮아지고, 가독성이 떨어진다. 이는 코드의 유지보수성과 테스트 용이성을 떨어뜨린다.
- 방법: 단일 책임 원칙을 지키기 위해, 메서드는 하나의 역할만 담당하도록 하고, 여러 작업을 처리하려면 작은 메서드로 분리한다.
🔽 좋은 예시: 메서드 분리
public void saveUserData(User user) {
validateUser(user);
saveToDatabase(user);
}
public void sendWelcomeEmail(User user) {
// 이메일 전송 로직
}
🔽 나쁜 예시: 여러 가지 일을 하는 메서드
public void saveUserData(User user) {
validateUser(user);
saveToDatabase(user);
sendWelcomeEmail(user);
}
▶ 클래스를 작게 유지하기 위해 노력했는가?
- 이유: 클래스가 크면 여러 책임을 동시에 지게 되어 응집도가 낮아지고, 유지보수가 어려워진다..
- 방법: 단일 책임 원칙을 지키기 위해, 클래스가 너무 많은 책임을 가지지 않도록 작고 응집력 있는 클래스로 분리한다.
🔽 좋은 예시: 클래스를 역할에 따라 분리
public class OrderService {
public void createOrder(Order order) {
// 주문 생성 로직
}
}
public class DiscountService {
public void calculateDiscount(Order order) {
// 할인 계산 로직
}
}
public class InvoiceService {
public void printInvoice(Order order) {
// 송장 출력 로직
}
}
🔽 나쁜 예시: 여러 책임을 지는 큰 클래스
public class OrderService {
public void createOrder(Order order) {
// 주문 생성 로직
}
public void calculateDiscount(Order order) {
// 할인 계산 로직
}
public void printInvoice(Order order) {
// 송장 출력 로직
}
}
📍 참고 자료
'우아한테크코스' 카테고리의 다른 글
[우아한테크코스] Java Enum 활용기 (4) | 2024.11.05 |
---|---|
[우아한테크코스] 프리코스 3주차 기록 (8) | 2024.11.05 |
[우아한테크코스] 프리코스 2주차 기록 (2) | 2024.10.29 |
[우아한테크코스] 프리코스 1주차 기록 (4) | 2024.10.22 |
[우아한테크코스] 우테코 코드 스타일 포매터 적용 (0) | 2024.10.15 |