"깊이"가 다른 게임개발자 허민영

유저에서 게임까지, 철학에서 코딩까지, 본질을 보는 게임개발

★철학으로 보는 코딩

상태와 사이드이펙트는 사실 같다?

허민영 2025. 6. 9. 13:49

상태와 사이드이펙트의 본질적 관계

프로그래밍에서 상태와 사이드이펙트는 본질적으로 같은 현상의 서로 다른 측면이라고 볼 수 있다. 철학적으로 접근하면, 상태 변경 자체가 사이드이펙트의 한 형태이다. 함수형 프로그래밍에서 x = x + 1과 같은 단순한 할당도 기존 상태를 변경하는 '부작용'으로 간주되는 것이 그 증거다.

하지만 실용적 관점에서는 미묘한 차이가 존재한다. 상태는 프로그램이 의도적으로 관리하는 데이터를 의미하는 반면, 사이드이펙트는 주요 목적 외에 발생하는 부수적인 변화를 가리킨다. 또한 로컬 변수 변경은 제한된 범위의 상태 변화인 반면, 전역 상태나 I/O는 더 광범위한 사이드이펙트를 야기한다.

의도하지 않은 상태와 사이드이펙트의 관계

대부분의 사이드이펙트는 의도하지 않은 상태 변화에서 기인한다. 하지만 몇 가지 반례들을 살펴볼 수 있다:

  • 순수한 계산 과정의 부작용: 로깅이나 경고 메시지 출력
  • 시스템 리소스 소모: 알고리즘 특성으로 인한 CPU/메모리 사용량 급증
  • 네트워크/I/O 지연: 외부 시스템과의 상호작용 자체가 갖는 특성
  • 예외 발생: 제어 흐름의 예상치 못한 변경

그러나 깊이 생각해보면 이러한 반례들도 결국 "프로그램이 의도하지 않은 전역적 상태 변화"로 해석할 수 있다. 로그 파일 상태, 시스템 리소스 상태, 네트워크 상태, 예외 스택 상태 등 모든 것이 상태의 범주에 포함되기 때문이다.

함수형 패러다임의 한계와 실용적 접근

함수형 프로그래밍에서 사이드이펙트에 대한 전방위적인 방어적 스탠스는 상태에 대해서도 똑같이 적용된다. 이는 범용적인 기능 개발에 현실적 한계를 만들어낸다.

// 함수형적 접근 - 순수하지만 비효율적
public static GameState UpdateGame(GameState current, Input input)
{
    return new GameState(
        new PlayerState(current.Player.Position + input.Movement),
        new EnemyState(current.Enemies.Select(e => UpdateEnemy(e)).ToArray())
    );
}

// 실용적 접근 - 제어된 상태 변경
public void UpdateGame(Input input)
{
    player.Move(input.Movement);
    enemies.ForEach(e => e.Update());
}

게임이나 UI 같은 상태 중심적인 도메인에서 상태를 완전히 배제하려는 시도는 현실과의 괴리를 만들어낸다. 프로그래밍은 "세상에 대한 객관적 인식을 코드로 작성하는 행위"이므로, 상태가 본질적으로 중요한 도메인에서는 이를 인정하고 잘 관리하는 것이 더 합리적이다.

상태 중심의 해결책

상태와 사이드이펙트의 본질적 관계를 인정한다면, 사이드이펙트를 줄이는 가장 효과적인 방법은 상태를 잘 다루는 것이다. 이를 위한 실용적 전략들은 다음과 같다:

캡슐화된 상태: 상태 변경을 특정 경계 안에서만 허용하여 예측 가능성을 높인다.

예측 가능한 상태 전이: 상태 머신 패턴을 활용하여 상태 변화를 체계적으로 관리한다.

불변성과 제어된 가변성의 조합: 핵심 데이터는 불변으로 유지하되, 필요한 부분만 제어된 방식으로 변경을 허용한다.

패러다임의 실용적 통합

결국 중요한 것은 "상태를 없애려고 하지 말고 상태를 잘 관리하자"는 접근이다. 함수형의 순수성 개념은 유용하지만, 그 자체가 목적이 되어서는 안 된다. 대신 더 나은 상태 관리를 위한 도구로 활용되어야 한다.

이런 관점에서 객체지향의 캡슐화나 최근의 상태 관리 라이브러리들이 추구하는 방향이 더 실용적이다. 패러다임의 독단주의에서 벗어나 문제의 본질에 맞는 도구적 다원주의를 추구할 때, 진정으로 효과적인 소프트웨어 개발이 가능해진다.