사전 앱 컴포넌트
개념

앱 컴포넌트

gabury1고친 사람 github-actions[bot]

앱 컴포넌트는 안드로이드 운영체제가 앱에 일을 시킬 때 들어가는 입구 노릇을 합니다. 안드로이드 앱은 main 함수 하나에서 시작하지 않습니다. 운영체제가 앱 안의 여러 부품 가운데 지금 필요한 것 하나를 골라 띄웁니다. 그 부품이 앱 컴포넌트입니다.

쉽고 빠른 이해

무슨 일을 하는 물건인가 — 운영체제가 앱을 깨울 때 들어가는 입구입니다. 사진 앱에서 「공유」를 눌러 메신저를 고르면, 메신저의 첫 화면을 거치지 않고 대화 상대를 고르는 화면이 바로 뜹니다. 그 화면이 컴포넌트 하나입니다.

왜 이렇게 하나 — 폰은 메모리가 작아서 운영체제가 안 보이는 앱을 수시로 끝냅니다. 앱을 처음부터 다 올리지 않고 필요한 부품 하나만 살려야 가볍습니다. 앱끼리 서로의 기능을 빌려 쓰는 것도 이 입구로 합니다.

어떻게 도나

  1. 앱은 컴포넌트 클래스를 짭니다. 그 목록은 설정 파일에 적어 둡니다
  2. 누가 그 컴포넌트를 원하면 운영체제에 요청 메시지를 보냅니다
  3. 운영체제가 앱을 띄워 컴포넌트 객체를 만듭니다. 그 객체의 정해진 메서드를 부릅니다

대가 — 객체를 언제 만들고 없앨지 운영체제가 정합니다. 객체가 예고 없이 사라질 수 있어서, 남겨야 할 값은 객체 밖에 둬야 합니다.

상세

이 절은 앱 컴포넌트가 무엇인지부터 세웁니다. 다음으로 네 종류를 표로 짚습니다. 그 뒤 운영체제가 컴포넌트를 깨우고 치우는 흐름을 백엔드 서버의 main 함수와 견주어 봅니다. 끝에서는 「컴포넌트」라는 말이 겹치는 이웃을 가릅니다.

큰 종합병원에는 문이 여럿입니다. 응급실 문, 외래 접수 문, 주차장에서 곧장 올라가는 문이 따로 있습니다. 환자는 정문 안내 데스크를 거치지 않고 볼일에 맞는 문으로 바로 들어갑니다.

운영체제가 여는 입구

안드로이드는 스마트폰과 태블릿에서 도는 운영체제입니다. 앱 컴포넌트는 안드로이드 앱을 이루는 부품 가운데 운영체제가 알아보고 직접 띄우는 부품입니다. 개발자는 정해진 부모 클래스를 물려받아 컴포넌트 클래스를 짭니다. 그 클래스의 객체를 만드는 쪽은 운영체제입니다.

컴포넌트 하나하나가 앱의 진입점입니다. 진입점은 프로그램 바깥에서 실행이 들어오는 곳입니다. 입구가 여럿이라 운영체제는 앱의 첫 화면을 거치지 않고 필요한 컴포넌트로 바로 들어갈 수 있습니다.

사진 앱에서 「공유」를 눌러 메신저를 고르면, 메신저의 첫 화면이 아니라 대화 상대를 고르는 화면이 바로 뜹니다. 그 화면을 맡은 컴포넌트가 입구가 된 것입니다.

main 함수가 없는 앱

백엔드 서버 프로그램은 main 함수 하나에서 시작합니다. 그 함수가 설정을 읽어 필요한 객체를 만듭니다. 그다음 요청을 기다립니다. 요청이 어디로 들어오든 실행은 늘 그 한 곳에서 출발합니다.

안드로이드 앱에는 개발자가 쓰는 main 이 없습니다. 운영체제는 컴포넌트 하나를 띄워야 할 때, 그 앱의 프로세스가 아직 없으면 프로세스부터 시작합니다. 프로세스는 운영체제가 앱 하나에 내주는 실행 단위입니다. 그다음 필요한 컴포넌트의 객체만 만듭니다.

운영체제가 이렇게 입구를 쥐는 까닭은 폰의 사정에 있습니다. 폰은 메모리가 작아서, 운영체제가 사용자가 안 보는 앱의 프로세스를 수시로 끝냅니다. 그러니 앱을 다시 띄울 때마다 처음부터 다 올리기보다 지금 필요한 부품 하나만 살리는 편이 가볍습니다.

입구가 여럿이면 앱끼리 기능을 빌려 쓰기도 쉽습니다. 메일 앱은 카메라 기능을 직접 짜지 않고 카메라 앱의 촬영 화면을 불러 씁니다. 부르는 쪽은 카메라 앱의 첫 화면이 아니라 촬영을 맡은 컴포넌트로 곧장 들어갑니다.

네 가지 컴포넌트

컴포넌트는 맡은 일에 따라 네 종류로 나뉩니다. 종류마다 물려받는 부모 클래스가 다릅니다. 운영체제가 깨우는 방법도 다릅니다. 아래 표는 각 종류가 물려받는 부모 클래스와 맡는 일을 짚습니다.

컴포넌트 부모 클래스 맡는 일 화면 예
액티비티 Activity 화면 한 장을 띄우고 사용자의 입력을 받는다 ✓ 받은 편지함 화면
서비스 (Android) Service 화면 없이 뒤에서 작업을 이어 간다 ✗ 음악 재생 · 파일 동기화
브로드캐스트 리시버 BroadcastReceiver 시스템이나 다른 앱이 널리 알리는 소식에 반응한다 ✗ 전지 부족 · 충전기 연결
콘텐츠 프로바이더 ContentProvider 앱이 가진 데이터를 다른 앱에 내준다 ✗ 연락처 목록

넷 가운데 화면을 가진 것은 액티비티뿐입니다. 나머지 셋은 사용자 눈에 안 보이는 채로 일합니다. 각 종류가 어떻게 도는지는 그 이름의 항목이 따로 풉니다.

매니페스트에 적어야 보인다

운영체제가 컴포넌트를 띄우려면 앱에 어떤 컴포넌트가 있는지 먼저 알아야 합니다. 그래서 앱은 가진 컴포넌트를 매니페스트에 적어 둡니다. 매니페스트는 앱 꾸러미에 함께 들어가는 설정 파일입니다. 앱을 설치할 때 운영체제가 이 파일을 읽어 목록을 챙깁니다.

매니페스트는 XML(Extensible Markup Language, 태그로 구조를 적는 텍스트 형식)로 씁니다. 아래는 네 종류를 하나씩 적은 모양입니다.

XML
<application>
    <activity android:name=".InboxActivity" />
    <service android:name=".SyncService" />
    <receiver android:name=".ChargeReceiver" />
    <provider
        android:name=".MailProvider"
        android:authorities="com.example.mail" />
</application>

태그 이름이 컴포넌트 종류입니다. android:name 은 그 컴포넌트를 짠 클래스 이름입니다. 콘텐츠 프로바이더에는 android:authorities 가 하나 더 붙습니다. 다른 앱이 이 데이터를 찾을 때 부르는 이름입니다.

코드로 짰어도 매니페스트에 안 적은 컴포넌트는 운영체제가 모릅니다. 모르는 컴포넌트는 띄울 수도 없습니다. 브로드캐스트 리시버만 예외입니다. 앱이 도는 동안 코드에서 등록해도 됩니다.

인텐트로 깨운다

컴포넌트끼리는 서로의 객체를 직접 만들지 않습니다. 같은 앱 안이라도 개발자 코드가 직접 만든 객체는 운영체제가 모릅니다. 운영체제가 모르는 객체는 입구가 되지 못합니다. 언제 깨우고 치울지도 운영체제가 정할 수 없습니다.

다른 앱의 컴포넌트라면 만들 방법부터 없습니다. 안드로이드는 앱마다 프로세스를 따로 두어 서로의 메모리를 못 건드리게 막기 때문입니다. 이렇게 앱을 가둬 두는 울타리를 샌드박스라고 부릅니다.

그래서 컴포넌트를 깨우고 싶은 쪽은 운영체제에 요청 메시지를 보냅니다. 이 메시지를 담는 객체가 인텐트입니다. 인텐트에는 깨울 컴포넌트의 클래스 이름을 적을 수 있습니다. 「사진을 찍고 싶다」처럼 하고 싶은 일만 적을 수도 있습니다.

하고 싶은 일만 적으면 운영체제가 그 일을 받겠다고 나선 컴포넌트를 찾아 줍니다. 컴포넌트는 매니페스트에 인텐트 필터를 달아 어떤 일을 받는지 밝혀 둡니다.

아래 그림은 다른 앱의 컴포넌트를 깨울 때 무엇이 오가는지를 보입니다.

sequenceDiagram
    participant A as 보내는 앱
    participant O as 운영체제
    participant B as 받는 앱
    A->>O: 인텐트를 보낸다
    Note over O: 매니페스트 목록에서 받을 컴포넌트를 찾는다
    O->>B: 프로세스가 없으면 먼저 시작한다
    O->>B: 컴포넌트 객체를 만들고 정해진 메서드를 부른다

그래서 보내는 앱은 받는 앱이 지금 떠 있는지 몰라도 됩니다.

콘텐츠 프로바이더만 인텐트로 깨우지 않습니다. 이 컴포넌트는 일을 시키는 대상이 아니라 데이터를 묻는 대상이기 때문입니다. 데이터를 달라는 요청은 콘텐츠 리졸버를 거쳐 들어갑니다. 콘텐츠 리졸버는 다른 앱의 데이터를 조회할 때 쓰는 창구 객체입니다.

컴포넌트 깨우는 방법
액티비티 인텐트를 startActivity 로 넘긴다
서비스 인텐트를 startService 등으로 넘긴다
브로드캐스트 리시버 인텐트를 sendBroadcast 로 널리 알린다
콘텐츠 프로바이더 콘텐츠 리졸버로 데이터를 요청한다

위 세 줄은 모두 인텐트를 씁니다. 다른 것은 인텐트를 운영체제에 넘기는 메서드뿐입니다.

생명주기는 운영체제가 쥔다

앞에서 보았듯 컴포넌트 객체를 만드는 것은 운영체제입니다. 그러니 없애는 것도 운영체제입니다. 객체는 만들어져서 일하다가 멈추고 없어지는 여러 상태를 거칩니다. 이 상태들이 옮겨 가는 순서가 생명주기입니다.

상태가 바뀔 때마다 운영체제는 컴포넌트의 정해진 메서드를 부릅니다. 개발자가 아니라 운영체제가 불러 주는 이런 메서드를 콜백이라고 부릅니다. 액티비티라면 막 만들어졌을 때 onCreate 가, 없어지기 직전에 onDestroy 가 불립니다.

백엔드 개발자에게 낯선 방식은 아닙니다. 서블릿 컨테이너도 서블릿 객체를 직접 만듭니다. 요청이 올 때마다 그 메서드를 부르는 것도 컨테이너입니다.

이렇게 개발자 코드를 받아 두었다가 대신 불러 주는 바탕 소프트웨어를 프레임워크라고 부릅니다. 여기서는 안드로이드 운영체제와 서블릿 컨테이너가 그 노릇을 합니다.

프레임워크가 객체를 만들고 개발자 코드를 불러 주는 방식을 제어의 역전이라고 합니다. 실행 흐름을 쥐는 쪽이 개발자 코드에서 프레임워크로 넘어갔다는 뜻입니다.

다른 점은 객체가 사라지는 때를 앱이 모른다는 것입니다. 운영체제는 메모리가 모자라면 사용자가 안 보는 앱의 프로세스를 끝냅니다. 그러면 그 안의 컴포넌트 객체도 함께 사라집니다. 그래서 남겨야 할 값은 객체의 필드가 아니라 데이터베이스 같은 저장소에 둡니다.

한 프로세스 · 한 스레드

한 앱의 컴포넌트들은 설정으로 따로 떼지 않으면 같은 프로세스 하나에서 돕니다. 그 앱의 컴포넌트를 처음 깨울 때 프로세스가 뜹니다. 뒤이어 깨운 컴포넌트는 그 프로세스 안으로 들어갑니다.

같은 프로세스 안에서 컴포넌트의 생명주기 콜백은 메인 스레드 하나에서 불립니다. 메인 스레드는 화면을 그리고 터치를 받는 스레드입니다. 서비스라는 이름이 붙었어도 저절로 별도 스레드에서 돌지는 않습니다.

오래 걸리는 일을 콜백 안에서 바로 하면 그동안 화면이 멈춥니다. 네트워크 호출이나 큰 파일 읽기는 따로 스레드를 두거나 코루틴 같은 비동기 수단으로 넘깁니다.

컴포넌트가 아닌 부품

안드로이드 앱에는 이 넷 말고도 부품이 많습니다. 가르는 기준은 하나입니다. 운영체제가 알아보고 직접 띄울 수 있느냐입니다.

프래그먼트는 액티비티 화면의 한 구역을 맡는 화면 조각입니다. 매니페스트에 적지 않습니다. 혼자서는 뜨지도 못합니다. 늘 어떤 액티비티 안에 들어가서 돕니다. 그래서 앱 컴포넌트가 아닙니다.

뷰는 버튼이나 글 상자처럼 화면에 그려지는 요소 하나입니다. 뷰는 늘 액티비티 화면 안에 담겨 그려집니다. 운영체제가 뷰 하나를 따로 띄우지는 못합니다.

뷰모델은 화면이 쓰는 데이터를 들고 있는 객체입니다. 앱 코드가 만들어 쓰는 보통 객체라 운영체제가 따로 띄우지 않습니다.

새 부품을 컴포넌트로 만들지는 한 가지를 물으면 갈립니다. 운영체제나 다른 앱이 이 부품을 바깥에서 불러야 하나입니다. 그렇지 않은 로직은 보통 클래스로 두고 컴포넌트 안에서 부릅니다.

「컴포넌트」가 겹치는 이웃

「컴포넌트」는 소프트웨어에서 흔한 말이라 다른 프레임워크에서도 자주 만납니다. 모양이 비슷해 보여도 누가 만들고 부르는지가 다릅니다.

말 만들고 부르는 쪽 떼어 낸 단위
안드로이드 앱 컴포넌트 운영체제 바깥에서 앱으로 들어오는 입구 하나
스프링의 @Component 스프링 컨테이너 다른 객체에 주입될 부품 하나
React 같은 화면 프레임워크의 컴포넌트 화면 프레임워크 화면의 한 조각

스프링의 @Component 는 스프링 컨테이너에게 이 클래스의 객체를 만들어 관리하라고 붙이는 표시입니다. 그 객체는 앱 안의 다른 객체에 주입되어 쓰입니다. 앱 안에서 서로 엮이는 부품이지, 바깥에서 들어오는 입구가 아닙니다.

React 의 컴포넌트는 화면의 한 조각을 그리는 함수나 클래스입니다. 화면 프레임워크가 이것들을 불러 화면 하나를 짜 맞춥니다.

백엔드 개발자가 스프링의 컴포넌트를 떠올리며 읽으면 방향이 헷갈립니다. 스프링 컴포넌트는 컨테이너 안에서 서로를 부릅니다. 안드로이드 앱 컴포넌트는 운영체제가 바깥에서 앱으로 들어오는 문입니다.

관련 항목

앱 컴포넌트의 하위 종류

액티비티 · 서비스 (Android) · 브로드캐스트 리시버 · 콘텐츠 프로바이더

앱 컴포넌트를 선언하고 깨우는 장치

매니페스트 · 인텐트 · 명시적 인텐트 · 암시적 인텐트 · 인텐트 필터 · 콘텐츠 리졸버 · 딥 링크

앱 컴포넌트가 도는 실행 환경

Android · 운영체제 · 프로세스 · 메인 스레드 · 샌드박스 · 권한 · Binder · IPC

앱 컴포넌트의 생명주기를 다루는 수단

생명주기 · 앱 생명주기 · 콜백 · 구성 변경 · 번들 (Android) · 백 스택

앱 컴포넌트에 들지 않는 안드로이드 부품

프래그먼트 · 뷰 · 뷰 그룹 · 레이아웃 · 뷰모델

앱 컴포넌트와 같은 제어 방식을 쓰는 프레임워크와 개념

서블릿 컨테이너 · 제어의 역전 · 의존성 주입 · 프레임워크 · 진입점

컴포넌트라는 이름이 겹치는 이웃

스프링 · 스프링 컨테이너 · 스프링 빈 · UI 컴포넌트 · React · 컴포넌트 기반 개발

앱 컴포넌트가 속하는 개발 분야

모바일 개발 · iOS · Kotlin · Android Developers · 앱 스토어

다른 이름: app component · app components · 안드로이드 앱 컴포넌트 · 애플리케이션 컴포넌트 · 안드로이드 컴포넌트