클린코딩

언리얼 거토
|2025. 3. 27. 21:43

제가 평소에 코드를 짜면서 유의하면 좋을 것 같은 코딩 유의사항을 정리해보았습니다.

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

문제점: 너무 많은 매개변수가 있을 경우 함수의 가독성이 떨어지고, 관리가 어려워진다.

나쁜 예시:

void InitWeapon(FString Name, float Damage, float FireRate, int32 AmmoCount, float ReloadTime, USkeletalMesh* Mesh, USoundBase* Sound)
{
    // 와, 많다 ...
}

좋은 예시: 구조체를 사용해 매개변수를 그룹화한다.

struct FWeaponData
{
    FString Name;
    float Damage;
    float FireRate;
    int32 AmmoCount;
};

struct FWeaponAssets
{
    USkeletalMesh* Mesh;
    USoundBase* Sound;
};

void InitWeapon(const FWeaponData& InData, const FWeaponAssets& InAssets)
{
    // 훨씬 깔끔!
}

2. 전역 데이터 (Global Data)

문제점: 전역 데이터를 사용하면 예상치 못한 값 변경이 발생할 수 있으며, 디버깅이 어려워진다.

나쁜 예시:

UGameManager* GGameManager; // 전역!

void IncreaseScore()
{
    GGameManager->Score += 10;
}

좋은 예시: Subsystem을 활용해 데이터의 접근을 제한한다.

UCLASS()
class UScoreSystem : public UGameInstanceSubsystem
{
    GENERATED_BODY()

private:
    int32 Score;

public:
    void AddScore(int32 Amount)
    {
        Score += Amount;
    }

    int32 GetScore() const { return Score; }
};

3. 샷건 수술 (Shotgun Surgery)

문제점: 관련된 기능이 여러 클래스에 분산되어 있어 수정 시 여러 곳을 변경해야 한다.

나쁜 예시:

class APlayerCharacter : public ACharacter
{
public:
    void TakeDamage(float Amount);
};

class AWeapon : public AActor
{
public:
    float CalculateDamage();
};

class AMyGameMode : public AGameModeBase
{
public:
    void UpdateDamageLeaderboard();
};

좋은 예시: 데미지 로직을 하나의 시스템으로 모은다.

class UDamageSystem : public UObject
{
public:
    float CalculateDamage(AWeapon* Weapon, ACharacter* Target);
    void ApplyDamage(AWeapon* Weapon, ACharacter* Target);
    void UpdateDamageLeaderboard(ACharacter* Damager, ACharacter* Target, float Amount);
};

4. 기능 편애 (Feature Envy)

문제점: 특정 클래스가 다른 클래스의 데이터를 과도하게 사용하면 유지보수가 어려워진다.

나쁜 예시:

class UDamageCalculator : public UObject
{
public:
    float CalculateDamageReduction(AMyCharacter* Character, float Damage);
};

좋은 예시: 필요한 연산을 해당 클래스 내부에서 처리하도록 한다.

class AMyCharacter : public ACharacter
{
public:
    float CalculateDamageReduction(float Damage) const;
};

5. 데이터 뭉치 (Data Clumps)

문제점: 비슷한 데이터가 여러 곳에서 반복되면 유지보수가 어려워진다.

나쁜 예시:

void FireWeapon(float Damage, float Range, float Accuracy);
void ShowWeaponStats(float Damage, float Range, float Accuracy);
void UpgradeWeapon(float& Damage, float& Range, float& Accuracy);

좋은 예시: 공통 데이터를 구조체로 묶는다.

USTRUCT(BlueprintType)
struct FWeaponStats
{
    GENERATED_BODY()

    float Damage;
    float Range;
    float Accuracy;
};

6. 기본형 집착 (Primitive Obsession)

문제점: 단순한 데이터 타입을 과도하게 사용하면 데이터 무결성이 깨질 위험이 있다.

나쁜 예시:

float Health;
float MaxHealth;

좋은 예시: 전용 클래스를 만들어 로직을 포함시킨다.

class FHealth
{
public:
    void ApplyDamage(float Amount);
private:
    float Current;
    float Max;
};

7. 게으른 요소 (Lazy Element)

문제점: 불필요한 중간 함수는 코드 복잡성을 증가시킨다.

나쁜 예시:

void FireProjectile(AProjectile* Projectile, FVector Direction, float Speed);

좋은 예시: 직접 핵심 로직을 실행한다.

class AProjectile : public AActor
{
public:
    void Launch(const FVector& Dir, float Speed);
};

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

문제점: 필요하지 않은 확장성을 고려하면 코드가 불필요하게 복잡해진다.

나쁜 예시:

class AWeaponBase : public AActor
{
    virtual void SpecialAttack() = 0;
};

좋은 예시: 필요한 기능만 구현하고, 필요 시 확장한다.

class AWeapon : public AActor
{
public:
    void Attack();
};

9. 메시지 체인 (Message Chains)

문제점: 객체 참조가 깊어질수록 유지보수가 어렵다.

나쁜 예시:

Player->GetInventory()->GetEquippedWeapon()->GetSound();

좋은 예시: 중간 객체가 직접 필요한 데이터를 제공하도록 한다.

class APlayer
{
public:
    USoundBase* GetEquippedWeaponSound();
};

이와 같은 클린 코딩 원칙을 적용하면 유지보수성과 가독성이 높은 코드를 작성할 수 있다!

'C++' 카테고리의 다른 글

브루트포스 기법  (0) 2025.03.11
STL 기본 구조  (0) 2025.02.18