본문 바로가기
카테고리 없음

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

by alwaysyoung2 2025. 3. 27.
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
반응형