카테고리 없음

23.03.27 Unreal 엔진 코드 악취 24가지: 심층 분석과 리팩토링 전략

alwaysyoung2 2025. 3. 27. 20:24
728x90
반응형

프로그래밍을 하다 보면 유지보수가 어렵고 가독성이 떨어지는 코드를 마주할 때가 있습니다. 이러한 문제를 "코드 악취"라고 부르며, Unreal 엔진 프로젝트에서도 흔히 발견됩니다. 이 글에서는 24가지 코드 악취의 예시와 이를 개선하는 방법을 소개합니다.


1. 기이한 이름 (Mysterious Name)

  • 문제점: 변수나 함수의 이름이 모호해 의도를 파악하기 어려움.
  • 해결책: 역할이 명확한 이름 사용.
  • cpp
    Copy
    // 나쁜 예
    float AAA;
    void DoIt(int x);
    
    // 좋은 예
    float CurrentHealth;
    void AttackEnemy(int DamageAmount);

2. 중복 코드 (Duplicated Code)

  • 문제점: 유사한 로직이 반복되어 수정이 어려움.
  • 해결책: 상속 또는 컴포넌트로 통합.
  • cpp
    Copy
    // 공통 부모 클래스에서 데미지 처리 로직 통합
    class ACharacterBase {
      void TakeDamage(float Amount) {
        Health -= Amount;
        if (Health <= 0) OnDeath();
      }
      virtual void OnDeath() {}
    };

3. 긴 함수 (Long Function)

  • 문제점: 한 함수가 너무 많은 역할을 수행.
  • 해결책: 작은 단위의 함수로 분리.
  • cpp
    Copy
    void AMyCharacter::Tick(float DeltaTime) {
      HandleMovement(DeltaTime);
      HandleJump();
      HandleAttack();
    }

4. 긴 매개변수 목록 (Long Parameter List)

  • 문제점: 매개변수가 많아 가독성 저하.
  • 해결책: 구조체로 그룹화.
  • cpp
    Copy
    struct FWeaponData { float Damage, FireRate; };
    void InitWeapon(const FWeaponData& Data);

5. 전역 데이터 (Global Data)

  • 문제점: 전역 변수로 인한 부작용 발생.
  • 해결책: 서브시스템 활용.
  • cpp
    Copy
    class UScoreSystem : public UGameInstanceSubsystem {
      void AddScore(int32 Amount);
    };

6. 가변 데이터 (Mutable Data)

  • 문제점: 외부에서 직접 필드 수정.
  • 해결책: 캡슐화와 Getter/Setter 사용.
  • cpp
    Copy
    class APlayerCharacter {
    private:
      float Health;
    public:
      void TakeDamage(float Amount);
    };

7. 뒤엉킨 변경 (Divergent Change)

  • 문제점: 한 클래스가 여러 이유로 자주 수정됨.
  • 해결책: 단일 책임 원칙 적용.
  • cpp
    Copy
    class UPlayerDataManager; // 데이터 처리
    class UGameplayManager;   // 게임플레이 처리

8. 샷건 수술 (Shotgun Surgery)

  • 문제점: 한 기능 변경에 여러 클래스 수정 필요.
  • 해결책: 관련 로직을 한 시스템으로 중앙화.
  • cpp
    Copy
    class UDamageSystem {
      void ApplyDamage(AWeapon* Weapon, ACharacter* Target);
    };

9. 기능 편애 (Feature Envy)

  • 문제점: 다른 클래스의 데이터를 과도하게 참조.
  • 해결책: 해당 기능을 데이터 소유 클래스로 이전.
  • cpp
    Copy
    float AMyCharacter::CalculateDamageReduction(float Damage);

10. 데이터 뭉치 (Data Clumps)

  • 문제점: 연관된 데이터가 흩어져 전달.
  • 해결책: 구조체로 묶어 관리.
  • cpp
    Copy
    struct FWeaponStats { float Damage, Range, Accuracy; };

11. 기본형 집착 (Primitive Obsession)

  • 문제점: 기본 타입으로만 데이터 표현.
  • 해결책: 커스텀 타입 생성.
  • cpp
    Copy
    class FHealth { void ApplyDamage(float Amount); };

12. 반복되는 스위치문 (Repeated Switches)

  • 문제점: 타입 확인을 위한 조건문 반복.
  • 해결책: 다형성 활용.
  • cpp
    Copy
    class AWeapon { virtual void Attack(); };
    class ASword : public AWeapon { void Attack() override; };

13. 반복문 (Loops)

  • 문제점: 중첩 루프와 복잡한 조건.
  • 해결책: 알고리즘 함수 활용.
  • cpp
    Copy
    auto HeavyItems = Items.FilterByPredicate(...);

14. 게으른 요소 (Lazy Element)

  • 문제점: 불필요한 중간 계층 함수.
  • 해결책: 직접적인 구현으로 단순화.
  • cpp
    Copy
    void AProjectile::Launch() { /* 직접 구현 */ }

15. 추측성 일반화 (Speculative Generality)

  • 문제점: 사용되지 않는 확장성 코드.
  • 해결책: 실제 필요에 맞춰 최소화.
  • cpp
    Copy
    class AWeapon { void Attack(); }; // 기본 기능만

16. 임시 필드 (Temporary Field)

  • 문제점: 특정 상황에서만 사용되는 필드.
  • 해결책: 컴포넌트로 분리.
  • cpp
    Copy
    class URangedAttackComponent : public UActorComponent;

17. 메시지 체인 (Message Chains)

  • 문제점: 과도한 점 표기법으로 객체 탐색.
  • 해결책: 캡슐화로 체인 단축.
  • cpp
    Copy
    USoundBase* AWeapon::GetAttackSound();

18. 중재자 (Middle Man)

  • 문제점: 중개자가 불필요한 호출만 전달.
  • 해결책: 직접 바인딩으로 우회.
  • cpp
    Copy
    MyChar->SetupPlayerInput(InputComponent);

19. 내부자 거래 (Insider Trading)

  • 문제점: 다른 객체의 내부 상태 직접 조작.
  • 해결책: 공개 인터페이스만 사용.
  • cpp
    Copy
    Player->ReceiveDamage(AttackDamage);

20. 거대한 클래스 (Large Class)

  • 문제점: 하나의 클래스가 너무 많은 책임.
  • 해결책: 컴포넌트 기반 설계.
  • cpp
    Copy
    UPROPERTY() UMovementComponent* MovementComp;

21. 서로 다른 인터페이스의 대안 클래스들

  • 문제점: 유사 기능이지만 인터페이스 불일치.
  • 해결책: 공통 인터페이스 정의.
  • cpp
    Copy
    class AWeapon { virtual void Attack(); };

22. 데이터 클래스 (Data Class)

  • 문제점: 데이터 저장만 하는 수동적 클래스.
  • 해결책: 로직을 포함한 능동적 클래스로 변경.
  • cpp
    Copy
    class FPlayerStats { void ApplyDamage(float Damage); };

23. 상속 포기 (Refused Bequest)

  • 문제점: 부모 클래스의 메서드를 사용하지 않음.
  • 해결책: 인터페이스 분리.
  • cpp
    Copy
    class ABaseWeapon { virtual void Attack(); };

24. 주석 (Comments)

  • 문제점: 복잡한 코드를 설명하는 과도한 주석.
  • 해결책: 함수 분리와 의미 있는 이름 사용.
  • cpp
    Copy
    bool AEnemy::CanSeePlayer();

결론

코드 악취는 프로젝트의 유지보수성을 크게 저하시킵니다. Unreal 엔진의 기능(서브시스템, 컴포넌트, 다형성 등)을 활용해 가독성과 확장성이 높은 코드를 작성합시다. 리팩토링은 지속적인 과정이므로, 코드 리뷰와 리팩토링을 습관화하는 것이 중요합니다

728x90
반응형