옵저버 패턴
옵저버 패턴은 특정 객체를 계속 감시하다가 변화가 일어나면 그 변화에 맞게 행동을 하는 패턴입니다.
간단한 예로
유튜브에 내가 구독한 채널에서 새로운 영상이 업로드되면 각 구독자들에게 새 영상이 올라왔다고 알람이 갑니다.
옵저버 패턴에서는 상속 관계의 단어들로 표현하지 않습니다.
예를 들어 부모 상태가 바뀌면 자식이 바뀐다라는 말도 가능하지만 해당 단어들은 상속관계에 의미하기 때문에
옵저버 패턴에서는 '방송국/구독자' 또는'주체/관찰자'라고 표현합니다.
옵저버 패턴의 2가지 방식 : Push VS Pull
1. Push 방식 (데이터 밀어 넣기)
주체가 알람을 보낼 때 데이터를 함께 넘겨주는 방식입니다.
2. Pull 방식 (데이터 당겨오기)
주체는 특정 변화가 일어났다고 신호만 보내고 구독자가 필요할 때 주체에게 가서 데이터르 직접 가져오는 방식입니다.
간단한 예시 (모든 관찰에게 메시지 보내기)
package com.example.androidpractice.observer_ex
//관찰자들에게 메시지를 받을 수 있는 기능 규칙 정함
interface ObserverWidget {
fun updateText(news : String)
}
package com.example.androidpractice.observer_ex
//주체 클래스 (메시지 발송 시스템)
class NewsSystem {
private val subscribers = mutableListOf<ObserverWidget>()
fun subscribe(widget: ObserverWidget) {
subscribers.add(widget)
}
fun broadcast(news : String) {
for(widget in 0..subscribers.size-1) {
subscribers[widget].updateText("구독자$widget 수신된 메시지: $news")
}
}
}
각 텍스트 위젯이 관찰자입니다.
package com.example.androidpractice.observer_ex
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
//관찰자
class ObserverTextView(context: Context, attrs : AttributeSet) : AppCompatTextView(context, attrs), ObserverWidget {
override fun updateText(news: String) {
this.text = news
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="전송할 메시지를 입력하세요"
android:inputType="text"
android:textSize="18sp" />
<Button
android:id="@+id/buttonSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="구독자들에게 알림 보내기"
android:textSize="16sp"
android:backgroundTint="#6200EE"
android:textColor="#FFFFFF"/>
<TextView
android:id="@+id/textViewResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="아직 받은 메시지가 없습니다."
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#000000"/>
</LinearLayout>
메인 엑티비티
package com.example.androidpractice
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.androidpractice.observer_ex.MessageSubscriber
import com.example.androidpractice.observer_ex.MessageSystem
class MainActivity : AppCompatActivity(), MessageSubscriber {
private val messageSystem = MessageSystem()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText = findViewById<EditText>(R.id.editText)
val button = findViewById<Button>(R.id.buttonSend)
messageSystem.addSubscriber(this)
button.setOnClickListener {
val input = editText.text.toString()
messageSystem.sendMessage(input)
}
}
override fun onMessageArrived(content: String) {
Toast.makeText(this, "받은 메시지: $content", Toast.LENGTH_SHORT).show()
val textView = findViewById<TextView>(R.id.textViewResult)
textView.text = "수신된 내용: $content"
}
}
출력 예)

실무에서 사용되는 옵저버 패턴
백엔드/인프라
1. Apache Kafka / RabbitMQ
대용량 데이터 처리 및 메시지 큐
예로
주체(결제 완료) 발행자(결제 완료 메시지 던짐) 구독자 각 서버 (배송서버, 알림 서버, 정산서버)에 동시에 메시지 받아 각자 할 일 수행
2. RxJava / RxKotlin
비동기 프로그래밍 라이브러리
Observable 객체를 만들고 API 호출, DB 조회, 버튼 클릭 등 모든 것을 데이터 스트림을 보고 변화 생기면 변화에 따른 동작 수행
옵저버 패턴의 장점
느슨한 결합 : 주체가 관찰자는 서로에 대한 자세한 정보를 몰라도 됩니다. 객체 간의 결합도를 낮출 수 있습니다.
확장성 : 새로운 관찰자를 쉽게 추가할 수 있습니다.
옵저버 패턴의 단점
성능 문제 : 관찰자(옵저버)가 많으면 주체의 상태가 변할 때 발생하는 행동에 대한 성능에 영향이 끼칩니다.
의존성 문제 : 관찰자가 주체 변화에 지나치게 의존하면 의존성 문제가 발생합니다.