안드로이드의 Context 이해하기

aufcl4858·2025년 8월 17일
post-thumbnail

안드로이드 개발을 하다 보면 Context라는 녀석을 정말 자주 만나게 된다. 처음엔 그냥 "아, 이거 넘겨주면 되는구나" 하고 대충 넘어갔는데, 시간이 지날수록 이게 얼마나 중요한 개념인지 깨닫게 되더라. 특히 메모리 누수나 런타임 에러를 겪고 나면 더욱 그렇다.

오늘은 Context가 정확히 뭔지, 어떤 종류가 있는지, 그리고 왜 올바르게 사용해야 하는지에 대해 정리해보려고 한다.

Context란 무엇인가?

Context를 한마디로 정의하면 "지금 내가 어디서 실행되고 있고, 무엇을 할 수 있는가?"에 대한 환경 정보를 담은 객체라고 할 수 있다.

좀 더 기술적으로 말하면, Context는 현재 실행 중인 컴포넌트(Activity, Service, Application 등)의 기본적인 정보와 생명주기를 담고 있는 추상 클래스다. 리소스 접근, 시스템 서비스 호출, 데이터베이스 연결 등 거의 모든 작업에서 필요한 핵심 요소라고 보면 된다.

Context의 주요 유형들

1. Application Context

Context appContext = getApplicationContext();
  • 앱 전체의 생명주기와 동일
  • 싱글톤으로 앱 전체에서 동일한 객체
  • 장기간 유지되는 작업에 적합
  • 단점: UI 관련 작업 불가 (다이얼로그, 토스트 등)

2. Activity Context

Context activityContext = this; // Activity 내에서
  • Activity의 생명주기와 연결
  • UI 관련 작업 가능 (다이얼로그, 토스트 등)
  • 테마 정보 포함
  • Activity가 종료되면 함께 소멸

3. Service Context

// Service 클래스 내에서
Context serviceContext = this;
  • Service의 생명주기와 연결
  • 백그라운드 작업에 적합
  • UI 관련 작업은 제한적

4. BroadcastReceiver Context

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 여기서 받는 context
    }
}
  • 브로드캐스트 수신 시에만 유효
  • 매우 짧은 생명주기
  • bindService() 호출 불가
  • 약 10초 제한

왜 올바른 Context를 사용해야 할까?

1. 런타임 에러 방지

각 Context마다 할 수 있는 일이 다르다. 예를 들어 Application Context로 다이얼로그를 띄우려고 하면 런타임에서 에러가 발생한다. 이런 문제들을 미리 방지하려면 상황에 맞는 Context를 선택해야 한다.

2. 메모리 누수 방지

가장 심각한 문제가 바로 이것이다. 특히 Activity Context를 장기간 참조하는 경우:

// 나쁜 예시 - 메모리 누수 발생 가능
public class BadSingleton {
    private static Context context;  // 💥 위험!
    
    public static void init(Context ctx) {
        context = ctx;  // Activity Context를 오래 보관
    }
}

이렇게 되면 Activity가 종료되어야 하는데 다른 객체가 계속 참조하고 있어서 가비지 컬렉터가 메모리에서 해제하지 못한다. 결과적으로

  • 메모리 사용량 점진적 증가
  • OutOfMemoryError 발생 가능
  • 앱 성능 저하
// 좋은 예시 - Application Context 사용
public class GoodSingleton {
    private static Context context;
    
    public static void init(Context ctx) {
        context = ctx.getApplicationContext();  // ✅ 안전!
    }
}

ContextWrapper는 뭐지?

개발하다 보면 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의 기능을 그대로 유지하면서 필요한 부분만 커스터마이징할 수 있다.

ContextWrapper 활용 예시

  • 커스텀 테마 적용
  • 언어 변경
  • 로깅 추가
  • 의존성 주입

Activity에서 this vs getBaseContext()

Activity 내에서 Context를 사용할 때 thisgetBaseContext()의 차이를 이해하는 것도 중요하다.

Activity (this) ← 커스터마이징된 ContextWrapper
├── 재정의된 메서드들 (테마, 커스텀 동작 등)
├── 추가 기능들
└── mBase (원본)  ──────┐
                       │
getBaseContext() ←─────┘  원본 Context 객체
  • this: ContextWrapper에서 커스터마이징된 Context (Activity의 모든 기능)
  • getBaseContext(): 커스터마이징 전의 원본 Context (순수 시스템 Context)

거의 모든 경우에 this를 사용하는 것이 올바른 선택이다. getBaseContext()는 정말 특별한 경우가 아니면 사용하지 않는 게 좋다.

마무리하며

Context는 안드로이드 개발의 기본 중의 기본이지만, 제대로 이해하지 못하면 예상치 못한 문제들을 만나게 된다. 특히 메모리 누수는 정말 골치 아픈 문제니까 항상 신경 써서 사용해야 한다.

핵심 원칙을 정리하면:

  • 단기 사용: Activity Context → Activity 내에서만
  • 장기 사용: Application Context → 앱 전체 생명주기 동안
  • UI 작업: Activity Context 필수
  • 백그라운드 작업: Application Context 권장

이 정도만 기억해도 Context 관련 문제의 90%는 해결할 수 있을 거다. 남은 10%는... 경험으로 채우는 수밖에 😅

profile
데브누누

0개의 댓글