728x90

1. 클린 코드란 무엇인가

1.1 정의

클린 코드는 단순히 잘 동작하는 코드가 아니라, 가독성, 유지보수성, 확장성이 뛰어난 코드를 의미합니다.
이는 협업과 장기적인 코드 품질을 유지하기 위한 필수적인 개발 철학 입니다.

1.2 클린 코드를 왜 해야 할까?

  1. "이걸 내가 짰다고?"
    • 클린 코드는 미래의 나 혹은 동료에게 친절한 코드
  2. Dirty Code는 폭탄
    • A를 수정을 하니 B에서 터지네?
  3. 협업의 필수 조건
    • 의도가 명확한 코드는 팀워크를 원활하게 만듭니다.
  4. 기술 부채는 무섭다
    • Dirty Code가 쌓이면 나중에 리팩토링이 아니라 재개발이 더 좋을수도….

1.3 리팩터링은 언제 해야 할까?

  1. "이 코드, 이해가 안 돼!"
    • 코드가 읽기 어려운 순간
  2. "여기 고치면 저기 터지네?"
    • 수정할 때마다 오류가 발생한다면
  3. "이거 너무 반복되는 것 같은데?"
    • 같은 코드가 여기저기 복붙되어 있다면?
  4. "새로운 기능 추가가 너무 힘들어!"
    • 확장하려는데 코드 구조가 방해된다면?
  5. "테스트가 너무 어려워!"
    • 단위 테스트를 작성하기 힘들다면?

2. 클린 코드의 기본 원칙

2.1 의미 있는 이름 짓기

// 나쁜 예
public void processData(List<String> data) {
    for (String item : data) {
        if (item.length() > 5) {
            System.out.println(item);
        }
    }
}

// 좋은 예
public void printLongUserNames(List<String> userNames) {
    final int MIN_NAME_LENGTH = 5;
    for (String userName : userNames) {
        if (userName.length() > MIN_NAME_LENGTH) {
            System.out.println(userName);
        }
    }
}
  • 정리
    1. 구체적이고 의도를 담은 이름을 사용
    2. 매직 넘버를 피하라
    3. 데이터의 의미를 이름에 반영

2.2 함수 분리 하기

// 나쁜 예
public void sendEmail(String recipient, String subject, String body) {
    if (recipient == null || recipient.isEmpty()) {
        throw new IllegalArgumentException("Recipient cannot be null or empty");
    }
    System.out.println("Connecting to SMTP server...");
    System.out.println("Authenticating...");
    System.out.println("Sending email to: " + recipient);
    System.out.println("Subject: " + subject);
    System.out.println("Body: " + body);
    System.out.println("Email sent successfully.");
}

// 좋은 예
public void sendEmail(String recipient, String subject, String body) {
    validateRecipient(recipient);
    connectToSmtpServer();
    authenticate();
    deliverEmail(recipient, subject, body);
}

private void validateRecipient(String recipient) {
    if (recipient == null || recipient.isEmpty()) {
        throw new IllegalArgumentException("Recipient cannot be null or empty");
    }
}

private void connectToSmtpServer() {
    System.out.println("Connecting to SMTP server...");
}

private void authenticate() {
    System.out.println("Authenticating...");
}

private void deliverEmail(String recipient, String subject, String body) {
    System.out.println("Sending email to: " + recipient);
    System.out.println("Subject: " + subject);
    System.out.println("Body: " + body);
    System.out.println("Email sent successfully.");
}
  • 정리
    1. 하나의 함수는 하나의 역할만 수행 (sendEmail은 이메일 발송의 흐름만 관리)
    2. 복잡한 작업은 작은 함수로 분리
    3. 함수 이름은 동작과 목적을 명확히 표현

2.3 불필요한 주석 제거

// 나쁜 예
public void processTransaction(Account fromAccount, Account toAccount, double amount) {
    // 송금 금액이 0보다 커야 합니다.
    if (amount <= 0) {
        throw new IllegalArgumentException("송금 금액은 0보다 커야 합니다.");
    }

    // 잔액 확인
    if (fromAccount.getBalance() < amount) {
        throw new IllegalStateException("계좌 잔액이 부족합니다.");
    }

    // 같은 계좌인지 확인
    if (fromAccount.equals(toAccount)) {
        throw new IllegalArgumentException("같은 계좌로 송금할 수 없습니다.");
    }

    // 송금 실행
    fromAccount.withdraw(amount); // 돈을 출금합니다.
    toAccount.deposit(amount);    // 돈을 입금합니다.

    // 송금 로그
    System.out.println("송금 성공: " + amount + "원 전송됨.");
}

// 좋은 예
public void processTransaction(Account fromAccount, Account toAccount, double amount) {
    // 비즈니스 규칙: 송금 금액은 0보다 커야 함
    if (amount <= 0) {
        throw new IllegalArgumentException("송금 금액은 0보다 커야 합니다.");
    }

    // 비즈니스 규칙: 송금 계좌 잔액이 부족하면 송금 불가
    if (fromAccount.getBalance() < amount) {
        throw new IllegalStateException("계좌 잔액이 부족합니다.");
    }

    // 비즈니스 규칙: 동일 계좌 간 송금 금지 (실수 방지 목적)
    if (fromAccount.equals(toAccount)) {
        throw new IllegalArgumentException("같은 계좌로 송금할 수 없습니다.");
    }

    // 송금 실행
    fromAccount.withdraw(amount);
    toAccount.deposit(amount);

    // 로그 기록: 성공적인 송금을 기록 (보안 및 추적 목적)
    System.out.println("송금 성공: " + amount + "원 전송됨.");
}
  • 정리
    1. 주석은 코드가 아닌 의도를 설명
      • “어떻게”가 아닌 “왜”를 설명
    2. 주석 대신 명확한 변수와 함수 이름으로 의도를 드러냄
    3. 불필요한 주석은 제거하고, 코드는 가능한 자체적으로 읽히게 작성

2.4 코드 중복 제거

// 나쁜 예
public void printUserName(String name) {
    System.out.println("User: " + name);
}

public void printAdminName(String name) {
    System.out.println("Admin: " + name);
}

// 좋은 예
public void printName(String role, String name) {
    System.out.println(role + ": " + name);
}
  • 정리
    1. DRY (Don’t Repeat Yourself) 원칙을 준수

2.5 복잡한 코드를 단순화하기

// 나쁜 예
if (user != null && user.getAge() > 18 && user.isActive()) {
    // do something
}

// 좋은 예
if (isActiveAdultUser(user)) {
    // do something
}

private boolean isActiveAdultUser(User user) {
    return user != null && user.getAge() > 18 && user.isActive();
}
  • 정리
    1. 조건문이 복잡하거나 여러 논리를 포함한다면 메서드로 분리

2.6 부정 표현을 긍정 표현으로 바꾸기

// 나쁜 예
if (!user.isInActive()) {
    return "Inactive User";
}
return "Active User";

// 좋은 예
if (user.isActive()) {
    return "Active User";
}
return "Inactive User";
  • 정리
    1. 긍정적 변수명 사용
    2. 긍정적 조건문 작성
    3. 이중 부정 지양

2.7 else 문 사용 지양하기

// 나쁜 예
public void login(User user) {
    if (user != null) {
        if (user.isActive()) {
            if (user.isVerified()) {
                System.out.println("Login successful");
            } else {
                System.out.println("User is not verified");
            }
        } else {
            System.out.println("User is inactive");
        }
    } else {
        System.out.println("Invalid user");
    }
}

// 좋은 예
public void login(User user) {
    if (user == null) {
        System.out.println("Invalid user");
        return;
    }
    if (!user.isActive()) {
        System.out.println("User is inactive");
        return;
    }
    if (!user.isVerified()) {
        System.out.println("User is not verified");
        return;
    }

    // 모든 조건을 통과한 경우
    System.out.println("Login successful");
}

// 더 좋은 예
public void login(User user) {
    String validationResult = validateUser(user);

    if (!validationResult.equals("Valid")) {
        System.out.println(validationResult);
        return;
    }

    // 모든 조건을 통과한 경우
    System.out.println("Login successful");
}

private String validateUser(User user) {
    if (user == null) {
        return "Invalid user";
    }
    if (!user.isActive()) {
        return "User is inactive";
    }
    if (!user.isVerified()) {
        return "User is not verified";
    }

    return "Valid";
}
  • 정리
    1. else 문을 피하고 기본 동작을 명시
    2. 전처리와 핵심 로직을 분리
    3. 각 조건은 독립적으로 처리

참고

반응형

'Study > etc...' 카테고리의 다른 글

소프트웨어 아키텍처  (0) 2025.01.03
TDD(Test-Driven Development)  (1) 2024.12.18

+ Recent posts