소프트웨어 개발에서 '클린 코드(Clean Code)'는 코드의 가독성, 유지보수성, 확장성을 높이기 위한 방법론을 의미합니다. 클린 코드를 작성하는 것은 단순히 기능을 구현하는 것 이상으로, 코드의 품질과 팀 협업의 효율성을 높일 수 있습니다.
변수와 함수의 이름은 코드의 가독성에 큰 영향을 미칩니다. 이름은 코드의 의도를 명확하게 전달해야 하며, 이름만으로도 해당 변수나 함수의 역할을 알 수 있어야 합니다. 뿐만아니라 독특한 어휘를 사용하지 않고 일관성있는 어휘를 사용하여 그 의미를 알 수 있도록 합니다.
// 나쁜 예시
int day; // 날짜를 의미하지만 명확하지 않음
// 좋은 예시
int daysSinceLastUpdate; // 날짜를 명확하게 표현
함수 이름 역시 마찬가지로, 함수가 수행하는 작업을 명확히 설명해야 합니다.
// 나쁜 예시
public void getData() {
// 데이터베이스에서 데이터를 가져옴
}
// 좋은 예시
public void fetchCustomerData() {
// 데이터베이스에서 고객 데이터를 가져옴
}
함수는 단일 책임 원칙(Single Responsibility Principle, SRP)을 따라 하나의 작업만 수행해야 합니다. 함수가 여러 작업을 수행하면 코드의 가독성과 유지보수성이 떨어집니다.
// 나쁜 예시
public void saveAndPrintCustomerData(Customer customer) {
saveCustomerData(customer);
printCustomerData(customer);
}
// 좋은 예시
public void saveCustomerData(Customer customer) {
// 고객 데이터를 저장
}
public void printCustomerData(Customer customer) {
// 고객 데이터를 출력
}
작은 함수와 클래스를 사용하면 코드의 가독성과 재사용성이 높아집니다. 큰 함수나 클래스는 이해하기 어렵고, 재사용하기도 어렵습니다.
// 나쁜 예시
public class CustomerService {
public void handleCustomerData() {
// 고객 데이터 처리
}
}
// 좋은 예시
public class CustomerDataSaver {
public void save(Customer customer) {
// 고객 데이터 저장
}
}
public class CustomerDataPrinter {
public void print(Customer customer) {
// 고객 데이터 출력
}
}
주석은 코드의 가독성을 높이는 데 도움이 될 수 있지만, 불필요한 주석은 오히려 방해가 될 수 있습니다. 따라서 주석은 코드 자체로 충분히 이해되지 않는 부분에만 사용합니다. 좋은 주석을 다는 방법은 다음과 같습니다.
// 나쁜 예시
int i = 0; // 변수 i를 0으로 초기화
// 좋은 예시
// 고객 목록을 반복하여 각각의 주문을 처리
for (Customer customer : customers) {
processOrder(customer.getOrder());
}
// 특정 할인 규칙을 적용해야 하는 이유 설명
public double calculateDiscount(double totalPrice) {
// 정액 할인 규칙 적용: 고객이 VIP 회원일 경우 10% 할인
if (isVIPCustomer()) {
return totalPrice * 0.9;
}
return totalPrice;
}
// 복잡한 로직에 대한 설명
// 이 메서드는 고객의 주문 내역을 분석하여 추천 상품을 반환합니다.
// 분석 과정에서 머신러닝 모델을 사용하며, 성능 최적화를 위해 배치 처리 방식을 채택했습니다.
public List<Product> recommendProducts(Customer customer) {
// 추천 로직 구현
}
중복 코드는 유지보수를 어렵게 만들기 때문에, 공통된 로직은 별도의 메서드나 클래스로 분리하여 재사용할 수 있도록 합니다.
// 나쁜 예시
public void sendWelcomeEmail(Customer customer) {
String email = customer.getEmail();
// 이메일 전송 로직
}
public void sendPasswordResetEmail(Customer customer) {
String email = customer.getEmail();
// 이메일 전송 로직
}
// 좋은 예시
public void sendEmail(String email, String message) {
// 이메일 전송 로직
}
public void sendWelcomeEmail(Customer customer) {
sendEmail(customer.getEmail(), "Welcome!");
}
public void sendPasswordResetEmail(Customer customer) {
sendEmail(customer.getEmail(), "Reset your password");
}
객체 지향 프로그래밍의 원칙을 따르는 것도 클린 코드를 작성하는 데 중요합니다. 특히 단일 책임 원칙(Single Responsibility Principle, SRP)을 준수하여 각 클래스가 하나의 책임만 가지도록 설계해야 합니다.
// 나쁜 예시
public class OrderProcessor {
public void processOrder(Order order) {
if (isValidOrder(order)) {
calculateTotal(order);
saveOrder(order);
sendConfirmationEmail(order);
}
}
private boolean isValidOrder(Order order) {
// 유효성 검사 로직
return true;
}
private void calculateTotal(Order order) {
// 총액 계산 로직
}
private void saveOrder(Order order) {
// 주문 저장 로직
}
private void sendConfirmationEmail(Order order) {
// 확인 이메일 발송 로직
}
}
// 좋은 예시
public class OrderProcessor {
private OrderValidator validator;
private OrderCalculator calculator;
private OrderRepository repository;
private EmailService emailService;
public OrderProcessor(OrderValidator validator, OrderCalculator calculator, OrderRepository repository, EmailService emailService) {
this.validator = validator;
this.calculator = calculator;
this.repository = repository;
this.emailService = emailService;
}
public void processOrder(Order order) {
if (validator.isValid(order)) {
calculator.calculate(order);
repository.save(order);
emailService.sendConfirmation(order);
}
}
}
테스트 코드는 코드의 정확성을 보장하고, 리팩토링 시 코드의 기능이 유지되는지 확인하는 데 중요합니다. 자바에서는 JUnit과 같은 테스트 프레임워크를 사용해 테스트 코드를 작성할 수 있습니다.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class CustomerServiceTest {
@Test
public void testSaveCustomerData() {
CustomerService service = new CustomerService();
Customer customer = new Customer("John", "Doe");
service.saveCustomerData(customer);
// 데이터베이스에 고객 데이터가 저장되었는지 확인
assertNotNull(service.findCustomerById(customer.getId()));
}
}
코드 스타일을 일관되게 유지하는 것은 팀 내 협업을 원활하게 하고, 코드를 읽고 이해하는 시간을 줄이는 데 도움이 됩니다. 코드 스타일 가이드를 정하고, 이를 준수하도록 합니다.
// 나쁜 예시
public class CustomerService {
public void saveCustomerData(Customer customer) {
DatabaseConnection connection = new DatabaseConnection();
connection.save(customer);
}
}
// 좋은 예시
public class CustomerService {
public void saveCustomerData(Customer customer) {
DatabaseConnection connection = new DatabaseConnection();
connection.save(customer);
}
}
적절한 예외 처리는 코드의 안정성을 높이고, 오류가 발생했을 때의 대처 방안을 명확히 합니다. 자바에서는 try-catch 블록을 사용해 예외를 처리할 수 있습니다.
// 나쁜 예시
public void saveCustomerData(Customer customer) {
DatabaseConnection connection = new DatabaseConnection();
connection.save(customer);
}
// 좋은 예시
public void saveCustomerData(Customer customer) {
try {
DatabaseConnection connection = new DatabaseConnection();
connection.save(customer);
} catch (DatabaseException e) {
// 로그 기록 및 사용자에게 오류 메시지 전달
System.err.println("Error saving customer data: " + e.getMessage());
}
}