Generated Code
R3S 의 가장 큰 장점은 보일러플레이트 제거입니다.
핵심 관점
-
개발자는 선언과 로직에 집중
-
반복 연결/노출/정리 코드는 생성기로 이동
-
코드 리뷰가 “반복 구현 확인”에서 “비즈니스 로직 검증”으로 이동
Before (직접 작성 방식)
private readonly ReactiveProperty<int> _hp = new(100);
public ReadOnlyReactiveProperty<int> Hp => _hp;
private readonly ReactiveCommand<Unit> _attack = new();
public Observable<Unit> Attack => _attack;
public void ExecuteAttack() => _attack.Execute(Unit.Default);
private CompositeDisposable _disposable = new();
private void Awake() => _hp.Subscribe(OnHpChanged).AddTo(_disposable);
private void OnDestroy() => _disposable.Dispose();
R3 방식개발자가 직접 작성하면 다음 코드가 반복됩니다.
-
공개용 프로퍼티 래퍼
-
Subscribe(...).AddTo(...)연결 -
CompositeDisposable생성/정리 -
커맨드 실행 메서드
After (R3S 사용)
[AutoDispose]
public partial class PlayerViewModel : MonoBehaviour
{
[ReactiveProperty]
private ReactiveProperty<int> _hp = new(100);
[ReactiveCommand]
private ReactiveCommand<Unit> _attack = new();
[AutoSubscribe(nameof(_hp))]
private void OnHpChanged(int hp) => Debug.Log(hp);
private void Awake() => R3Awake();
private void OnDestroy() => R3OnDestroy();
}
R3S의 방식개발자가 유지하는 코드는 다음처럼 짧아집니다.
-
Attribute 선언
-
비즈니스 로직 메서드
-
Awake/OnDestroy에서R3Awake,R3OnDestroy오토 멤버 호출
실제 생성 코드 예시
아래는 위 사용자 클래스를 기반으로 R3S 에서 자동으로 생성된 코드 형태입니다.
public partial class PlayerViewModel
{
private CompositeDisposable _disposable = new();
public ReadOnlyReactiveProperty<int> Hp => _hp;
public Observable<Unit> Attack => _attack;
public void ExecuteAttack() => _attack.Execute(Unit.Default);
private void R3Awake()
{
_hp.Subscribe(OnHpChanged).AddTo(_disposable);
}
private void R3OnDestroy() => _disposable.Dispose();
}
해당 스크립트는 R3S 의 Roslyn Source Generator가 "자동"으로 생성합니다.
사용자는 볼 수도, 조작할 수도 없습니다.
튜토리얼 1 - MonoBehaviour 클래스 기준
작성 코드
using R3;
using R3.Attributes;
using UnityEngine;
[AutoDispose]
public partial class LobbyViewModel : MonoBehaviour
{
[ReactiveProperty] private ReactiveProperty<int> _playerCount = new(1);
[ReactiveCommand] private ReactiveCommand<Unit> _startGame = new();
[AutoSubscribe(nameof(_playerCount))]
private void OnPlayerCountChanged(int count)
{
Debug.Log($"players: {count}");
}
private void Awake() => R3Awake();
private void OnDestroy() => R3OnDestroy();
}
-
PlayerCount노출 프로퍼티 -
StartGameObservable 노출 +ExecuteStartGame()생성 -
R3Awake()내부 구독 연결 -
R3OnDestroy()내부 dispose
튜토리얼 2 - 일반 클래스 기준
일반 클래스는 생성자에서 R3Initialize() 진입 패턴을 사용합니다.
using R3;
using R3.Attributes;
[AutoDispose]
public partial class QuestService
{
[ReactiveProperty]
private ReactiveProperty<int> _activeQuestCount = new(0);
[AutoSubscribe(nameof(_activeQuestCount))]
private void OnQuestCountChanged(int count)
{
// logging
}
public QuestService()
{
R3Initialize();
}
}
-
IDisposable구현 및Dispose()생성 여부 -
생성자에서
R3Initialize()호출 여부 -
AddTo 모드가
Disposable인지 확인
튜토리얼 3 - 리뷰/리팩토링 루틴
아래는 간단한 예시이며, 필요에 따라 설계 후 진행하시면 됩니다.
-
수동 프로퍼티 래퍼/수동 Execute 메서드 제거 가능성 확인
-
수동 Subscribe 연결을
AutoSubscribe로 치환 -
수동 Dispose 로직을
AutoDispose로 치환 -
클래스에
partial선언 추가 -
진입점(
Awake/OnDestroy또는 생성자) 확인
결과
-
클래스당 수십 줄의 반복 코드 감소
-
코드 리뷰 포인트가 “반복 구현”에서 “도메인 로직”으로 이동
-
실수하기 쉬운 수동 연결 코드 제거