안녕하세요 제가 공부하고 있는 리액터킷의 기본 개념을 꼼꼼히 공부하고 이해해보도록해요
아래 내용은 리액터킷 깃의 내용을 한글로 제맘대로 적어놓은 것 입니다~
이해가 안되는 여러분들에게도 도움이 되면 좋겠네요...
모든 내용은 https://github.com/ReactorKit/ReactorKit/blob/master/README.md
ReactorKit/README.md at master · ReactorKit/ReactorKit
A library for reactive and unidirectional Swift applications - ReactorKit/ReactorKit
github.com
을 기본으로 합니다!
그럼 시작
기본 개념
ReactorKit은 Flux와 Reactive Programming의 조합
사용자 액션과 뷰 상태는 Observable 스트림으로 각 레이어에 전달됨
이라는데
여기서 Observable 스트림이란?
어떤 값이 흘러가는 파이브 같은 개념
파이프에 값이 들어가면, 그 값을 구독한 애들이 그 변화를 감지해서 동작함
이 스트림은 단방향임:
- View는 오직 Action만 발생시킬 수 있음
- Reactor는 오직 State만 방출할 수 있음

여기까지 느낀바는
아 그럼 ReactorKit은
- 구독 관계가 있고
- 뭔가 이벤트가 발생하면
- 단방향 스트림에 의해
- 변화를 감지하고
- 어떠한 동작을 하는거구나!!!
좀 더 정확히 하자면 "사용자 이벤트가 단방향 스트림을 따라 흘러가면서 상태를 바꾸고, 그 변화를 뷰가 자동으로 반응하는 구조" 인 것
설계 목표
테스트 용이성
: 비즈니스 로직을 뷰와 분리하는 게 가장 큰 목적
Reactor는 View에 의존하지 않음
Reactor와 뷰 바인딩만 테스트하면 됨
-> 덕분에 테스트가 쉬워짐
-> 테스트하기 좋은 코드는 좋은 코드~
작게 시작하기
: 앱 전체가 ReactorKit 아키텍처를 따라야 하는 건 아님
일부 화면에만 적용해도 됨
기존 프로젝트도 전체 리팩토링 필요 없음
코드 최소화
: 단순한 기능에 복잡한 코드 필요 없음
ReactorKit은 다른 아키텍처보다 적은 코드로 구현 가능
아 글쿤요
View
- View는 데이터를 보여주는 역할만 함 (예: 뷰컨트롤러, 셀)
- View는 사용자 입력을 Action 스트림에 바인딩하고, Reactor의 State를 UI 컴포넌트에 바인딩함
-> 즉, 입력은 Action으로 보내고, 출력은 State에서 받아서 UI에 붙인다
위에서 봤던 흐름 그림 생각하면 됨 - 비즈니스 로직 없음 → 오직 Action ↔ State 매핑 정의만 함
뷰 정의 방법
: View 프로토콜 채택 → reactor 프로퍼티 자동 추가
class ProfileViewController: UIViewController, View {
var disposeBag = DisposeBag()
}
profileViewController.reactor = UserViewReactor() // 이렇게 Reactor 주입
reactor가 설정되면 자동으로 bind(reactor:) 호출됨 → 여기서 액션/상태 바인딩 정의하면 됨
func bind(reactor: ProfileViewReactor) {
// action (View -> Reactor)
refreshButton.rx.tap.map { Reactor.Action.refresh }
.bind(to: reactor.action)
.disposed(by: self.disposeBag)
// state (Reactor -> View)
reactor.state.map { $0.isFollowing }
.bind(to: followButton.rx.isSelected)
.disposed(by: self.disposeBag)
}
이런 코드가 불린다는데...이렇게 정의할 수 있다는데,,,
이게 뭔소리냐면
func bind(reactor: ProfileViewReactor) {
// 1️⃣ Action 바인딩 (View → Reactor)
// refreshButton이 눌리면 tap 이벤트가 발생
refreshButton.rx.tap
// 버튼 탭 이벤트를 Reactor가 처리할 Action.refresh로 변환
.map { Reactor.Action.refresh }
// 변환된 Action을 Reactor의 action 스트림으로 전달
.bind(to: reactor.action)
// disposeBag에 담아 구독 해제 시 메모리 관리
.disposed(by: self.disposeBag)
// 2️⃣ State 바인딩 (Reactor → View)
// Reactor의 상태(State) 스트림 구독
reactor.state
// 상태 중 isFollowing 값만 추출
.map { $0.isFollowing }
// 추출한 값을 followButton의 isSelected 속성과 연결
.bind(to: followButton.rx.isSelected)
// disposeBag에 담아 구독 해제 시 메모리 관리
.disposed(by: self.disposeBag)
}
이렇게 정의할 수 있다 이거임
스토리 보드 지원
은 스토리 보드 안쓸거라 생략
Reactor
- Reactor는 뷰 상태를 관리하는 UI 비의존 레이어
- 모든 로직을 Reactor에 위임하고, View와 분리됨 → 테스트 용이
- Reactor 정의 시 Reactor 프로토콜 채택 → Action, Mutation, State, initialState 정의 필요
Action → Mutation → State 흐름으로 동작
- mutate() : Action을 받아서 Mutation 스트림 생성 (비동기 작업, API 호출 수행)
- reduce() : Mutation + 이전 State → 새로운 State 생성 (순수 함수)
- transform() : Action, Mutation, State 스트림 변환/결합 가능
그냥 아 글쿤 하세요
class ProfileViewReactor: Reactor {
// 사용자의 입력/행동을 표현하는 이벤트
// 유저가 뭔가 했다!!!
enum Action {
case refreshFollowingStatus(Int)
case follow(Int)
}
// Action과 State 사이의 다리 (중간 단계)
// State에 어떤 변화가 일어날지 정의
enum Mutation {
case setFollowing(Bool)
}
// 화면(View)이 보여줘야 하는 상태 값
// 지금 화면이 어떤 상태여야 하는가!
struct State {
var isFollowing: Bool = false
}
let initialState: State = State()
}
Reactor는 Action을 받아서 Mutation → State로 바꿔주는 역할을 함
사용자 입력 (Action)
↓ mutate()
중간 변화 (Mutation)
↓ reduce()
화면 상태 (State)
잠만 reduce가 어디서 나왔냐면요
이사진 많이 보셨쥬 ㅋ

리액터라는 프로토콜에
mutate()라는 메서드와 reduce()가 있는거임!!!
- mutate
-> 유저 액션(Action)이 들어오면 → "이 액션 때문에 어떤 변화(Mutation)가 생겨야 하나?"를 결정
-> 예: .refresh → "로딩 시작" + "유저 목록 불러오기" - reduce
-> Mutation이 생겼으니 → "State를 어떻게 바꿔야 하나?"를 적용
-> 예: .setLoading(true) → state.isLoading = true
그럼 여기까지 내용 코드로 봅시다.
// Action: 사용자가 할 수 있는 입력
enum Action {
case refresh
}
// Mutation: 상태에 가해질 변화
enum Mutation {
case setLoading(Bool)
}
// State: 화면이 표현할 상태
struct State {
var isLoading: Bool = false
}
final class MyReactor: Reactor {
let initialState = State()
// Action -> Mutation
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .refresh:
return Observable.just(.setLoading(true))
}
}
// Mutation -> State
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .setLoading(let isLoading):
newState.isLoading = isLoading
}
return newState
}
}
이제 왜인지는 묻지마셈
왜냐고???
이게 ReactorKit의 기본 틀인 것임
왜가 아니라 걍 일케 쓰는거래요
여기에 transform() 까지 해야하는데 그건 패쓰하겠삼
왜냐면 곧 자리 떠야해서 ㅎㅎ
'iOS > ReactorKit' 카테고리의 다른 글
| [ReactorKit] 버튼 누르면 숫자가 증가하는 리액터 만들기 (0) | 2025.09.11 |
|---|---|
| [ReactorKit] 기본적인 ReactorKit 흐름 만들어보기 (0) | 2025.09.11 |
| [ReactorKit] Swift Storyboard 없이 코드로만 앱 만들기 (0) | 2025.09.09 |
댓글