
안드로이드 개발을 하다 보면 Context라는 녀석을 정말 자주 만나게 된다. 처음엔 그냥 "아, 이거 넘겨주면 되는구나" 하고 대충 넘어갔는데, 시간이 지날수록 이게 얼마나 중요한 개념인지 깨닫게 되더라. 특히 메모리 누수나 런타임 에러를 겪고 나면 더욱 그렇다.
오늘은 Context가 정확히 뭔지, 어떤 종류가 있는지, 그리고 왜 올바르게 사용해야 하는지에 대해 정리해보려고 한다.
Context를 한마디로 정의하면 "지금 내가 어디서 실행되고 있고, 무엇을 할 수 있는가?"에 대한 환경 정보를 담은 객체라고 할 수 있다.
좀 더 기술적으로 말하면, Context는 현재 실행 중인 컴포넌트(Activity, Service, Application 등)의 기본적인 정보와 생명주기를 담고 있는 추상 클래스다. 리소스 접근, 시스템 서비스 호출, 데이터베이스 연결 등 거의 모든 작업에서 필요한 핵심 요소라고 보면 된다.
Context appContext = getApplicationContext();
Context activityContext = this; // Activity 내에서
// Service 클래스 내에서
Context serviceContext = this;
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 여기서 받는 context
}
}
bindService() 호출 불가각 Context마다 할 수 있는 일이 다르다. 예를 들어 Application Context로 다이얼로그를 띄우려고 하면 런타임에서 에러가 발생한다. 이런 문제들을 미리 방지하려면 상황에 맞는 Context를 선택해야 한다.
가장 심각한 문제가 바로 이것이다. 특히 Activity Context를 장기간 참조하는 경우:
// 나쁜 예시 - 메모리 누수 발생 가능
public class BadSingleton {
private static Context context; // 💥 위험!
public static void init(Context ctx) {
context = ctx; // Activity Context를 오래 보관
}
}
이렇게 되면 Activity가 종료되어야 하는데 다른 객체가 계속 참조하고 있어서 가비지 컬렉터가 메모리에서 해제하지 못한다. 결과적으로
// 좋은 예시 - Application Context 사용
public class GoodSingleton {
private static Context context;
public static void init(Context ctx) {
context = ctx.getApplicationContext(); // ✅ 안전!
}
}
개발하다 보면 ContextWrapper라는 것도 만나게 된다. 이건 기존 Context를 "감싸는(wrapping)" 클래스로, 데코레이터 패턴의 구현체라고 보면 된다.
public class ContextWrapper extends Context {
Context mBase; // 실제 Context 객체를 감쌈
public ContextWrapper(Context base) {
mBase = base;
}
// 모든 메서드를 base에 위임
@Override
public String getString(int resId) {
return mBase.getString(resId);
}
}
실제로 Activity, Service, Application 모두 ContextWrapper를 상속받는다. 이를 통해 기존 Context의 기능을 그대로 유지하면서 필요한 부분만 커스터마이징할 수 있다.
Activity 내에서 Context를 사용할 때 this와 getBaseContext()의 차이를 이해하는 것도 중요하다.
Activity (this) ← 커스터마이징된 ContextWrapper
├── 재정의된 메서드들 (테마, 커스텀 동작 등)
├── 추가 기능들
└── mBase (원본) ──────┐
│
getBaseContext() ←─────┘ 원본 Context 객체
this: ContextWrapper에서 커스터마이징된 Context (Activity의 모든 기능)getBaseContext(): 커스터마이징 전의 원본 Context (순수 시스템 Context)거의 모든 경우에 this를 사용하는 것이 올바른 선택이다. getBaseContext()는 정말 특별한 경우가 아니면 사용하지 않는 게 좋다.
Context는 안드로이드 개발의 기본 중의 기본이지만, 제대로 이해하지 못하면 예상치 못한 문제들을 만나게 된다. 특히 메모리 누수는 정말 골치 아픈 문제니까 항상 신경 써서 사용해야 한다.
핵심 원칙을 정리하면:
이 정도만 기억해도 Context 관련 문제의 90%는 해결할 수 있을 거다. 남은 10%는... 경험으로 채우는 수밖에 😅