[Android] 안드로이드 앱 구조 정리

2025. 8. 19. 21:47·CS 기초부터 한 걸음씩

1. Empty Views Activity 템플릿으로 생성한 프로젝트 폴더 구조

  • UI는 XML로 구현

프로젝트 구조 (Project View):

MyApp/                                    // 프로젝트 루트 디렉토리
├── app/                                  // 메인 앱 모듈
│   ├── src/                             // 소스코드 루트
│   │   ├── main/                        // 메인 소스셋
│   │   │   ├── java/com.example.myapp/  // Kotlin/Java 소스 파일들
│   │   │   │   └── MainActivity.kt      // 메인 액티비티 클래스
│   │   │   ├── res/                     // 리소스 폴더
│   │   │   │   ├── layout/              // UI 레이아웃 XML 파일들
│   │   │   │   │   └── activity_main.xml // 메인 액티비티 레이아웃
│   │   │   │   ├── values/              // 기본 값 리소스들
│   │   │   │   │   ├── strings.xml      // 문자열 리소스
│   │   │   │   │   ├── colors.xml       // 색상 리소스
│   │   │   │   │   └── themes.xml       // 테마/스타일 리소스
│   │   │   │   ├── values-night/        // 다크 테마용 리소스들
│   │   │   │   │   └── themes.xml       // 다크 테마 스타일
│   │   │   │   ├── xml/                 // 기타 XML 설정 파일들
│   │   │   │   │   └── backup_rules.xml // 앱 백업 규칙 설정
│   │   │   │   ├── drawable/            // 이미지, 벡터, 셰이프 드로어블
│   │   │   │   └── mipmap/              // 앱 아이콘 (다양한 해상도)
│   │   │   └── AndroidManifest.xml      // 앱 설정 및 컴포넌트 선언
│   │   ├── androidTest/                 // 계측 테스트 (실제 기기용)
│   │   │   └── java/com.example.myapp/  // 안드로이드 테스트 소스
│   │   └── test/                        // 단위 테스트 (로컬 JVM용)
│   │       └── java/com.example.myapp/  // 유닛 테스트 소스
│   └── build.gradle                     // 모듈 레벨 빌드 설정
├── gradle/                              // Gradle 래퍼 파일들
└── build.gradle                         // 프로젝트 레벨 빌드 설정

각 폴더 구조 설명:

📁 app/src/main/

모든 메인 안드로이드 컴포넌트를 포함하는 폴더 - Java/Kotlin 파일과 리소스 파일들을 분리해서 관리

📁 res/ 폴더 내부

기본 폴더들:

  • layout/ - UI 레이아웃 XML 파일들
  • values/ - 문자열, 색상, 스타일 등의 값들
  • drawable/ - 이미지, 벡터 드로잉 리소스
  • mipmap/ - 앱 아이콘 (다양한 해상도)

추가 폴더들:

  • values-night/ - 다크 테마용 별도 값들 (Android 10+ 지원)
  • xml/ - 앱 백업 규칙, 네트워크 보안 설정 등의 구성 파일들

📁 테스트 폴더 test/ vs androidTest/ 차이

구분 test/ androidTest/
실행 환경 로컬 JVM (Java Virtual Machine) 실제 Android 기기나 에뮬레이터
테스트 종류 순수 단위 테스트 (Unit Test) 계측 테스트 (Instrumented Test)
Android API 접근 불가능 (Mock 객체 사용) 가능 (실제 Context, View 등)
실행 속도 빠름 느림 (APK 설치 필요)
사용 예시 비즈니스 로직, 계산 함수 UI 테스트, 데이터베이스, 권한

언제 어떤 테스트를 사용할까?

  • test/ - Android 프레임워크를 사용하지 않는 순수 Java/Kotlin 코드
  • androidTest/ - Android 종속성이 있거나 Mock 객체로 만족할 수 없는 코드

2. MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) // 부모 클래스인 AppCompatActivity의 onCreate()를 호출
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            // 시스템 바가 차지하는 영역의 크기(패딩 값)를 추출
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            // 가져온 시스템 바 크기만큼 뷰에 패딩을 설정
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            // insets 반환
            insets
        }
    }
}

 

super.onCreate(savedInstanceState)

  • 목적: 액티비티를 생성하고 초기화
  • 동작: AppCompatActivity와 같은 부모 클래스의 onCreate() 메서드를 호출하여 액티비티의 생명주기를 올바르게 시작하고 필요한 초기 설정을 진행

savedInstanceState: Bundle?

  • 목적: 액티비티가 비정상적으로 종료되기 전의 상태를 저장하고, 다시 시작될 때 해당 상태를 복원하는 데 사용
    예를 들어, 기기 회전 시 화면이 재구성될 때 이전 데이터를 잃지 않도록 도와줍니다.
  • 동작: Bundle 객체는 키-값(key-value) 쌍 형태로 데이터를 저장
    안드로이드 시스템은 onSaveInstanceState() 콜백 메서드에서 이 Bundle에 데이터를 저장하고,
    onCreate()로 다시 전달하여 이전 상태를 그대로 복원할 수 있게 해줍니다.

setContentView()

  • 목적: 액티비티(화면)에 표시할 UI 레이아웃을 연결
  • 동작: res/layout/ 폴더에 있는 activity_main.xml 파일을 읽어 화면에 뷰들을 생성하고 배치

Edge-to-Edge 코드 : enableEdgeToEdge(), ViewCompat.setOnApplyWindowInsetsListener()

  • enableEdgeToEdge()
    • 목적: 시스템 바(상태바, 네비게이션바)를 투명하게 만들어 앱 화면이 전체 디스플레이를 사용
    • 효과: 앱이 상태바와 네비게이션바 뒤까지 그려짐
    • 도입 시기: Android 15부터 기본 적용
  • ViewCompat.setOnApplyWindowInsetsListener()
    • 목적: 시스템 바와 겹치는 부분에 패딩을 추가하여 콘텐츠가 시스템 바에 가려지지 않게 함
    • 동작: 시스템 바 크기만큼 뷰에 패딩을 자동으로 적용
    • 필요성: enableEdgeToEdge() 사용 시 필수

Edge-to-Edge 없이는 앱 콘텐츠가 시스템 바 아래 영역에서만 표시되지만,
Edge-to-Edge 적용 시 전체 화면을 사용하므로 시스템 바와 겹치는 문제를 해결해야 합니다.

Activity 상속 계층 구조

java.lang.Object // Java 기반 | kotlin.Any  (= Java의 Object) // 코틀린 기반
    ↓
android.content.Context (추상클래스)
    ↓
android.content.ContextWrapper
    ↓
android.content.ContextThemeWrapper
    ↓
android.app.Activity
    ↓
androidx.fragment.app.FragmentActivity
    ↓
androidx.appcompat.app.AppCompatActivity

차이점: Kotlin에서는 kotlin.Any가 최상위 클래스이고, Java의 Object와 동일한 역할을 합니다.

각 클래스 역할

클래스 역할
Context 앱 환경 정보에 접근하는 추상 클래스 - 리소스, 파일, 데이터베이스 접근
ContextWrapper Context를 래핑하여 기능을 확장할 수 있게 하는 클래스
ContextThemeWrapper 테마 지원을 추가한 Context
Activity 사용자와 상호작용하는 단일 화면을 제공하는 기본 클래스
FragmentActivity Fragment 지원을 추가한 Activity
AppCompatActivity 하위 버전 호환성 지원 (Material Design, ActionBar 등)

3. AndroidManifest.xml과 앱의 첫 시작점

MainActivity가 main 함수 같은 역할인가? : 비슷하지만 다릅니다.

유사점:

  • 앱 진입점 역할
  • 가장 먼저 실행되는 코드

차이점:

  • Java/Kotlin의 main()은 프로그램 시작점
  • MainActivity는 여러 Activity 중 첫 번째로 표시되는 화면

앱 실행 시작점 결정 방법

AndroidManifest.xml에서 결정:

안드로이드 앱은 다음 4가지 주요 컴포넌트(4대 컴포넌트)로 구성됩니다:

  1. Activity - 사용자 인터페이스가 있는 하나의 화면
  2. Service - 백그라운드에서 실행되는 컴포넌트
  3. Content Provider - 데이터 공유를 위한 컴포넌트
  4. Broadcast Receiver - 시스템 이벤트를 수신하는 컴포넌트
<activity android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

핵심 요소:

  • android.intent.action.MAIN - 앱의 주요 진입점임을 알림
  • android.intent.category.LAUNCHER - 런처(홈화면)에서 실행 가능함을 알림

실제 앱 아이콘 위치:
LAUNCHER 카테고리는 앱 서랍(App Drawer) 또는 모든 앱 메뉴에 아이콘을 생성합니다.
홈화면에 바로 나타나지는 않고, 사용자가 직접 드래그하거나 설치 후 홈화면 추가를 선택해야 홈화면에 표시됩니다.

여러 Activity와 XML 연결

Activity-XML 연결 과정:

  1. 각 Activity마다 별도 XML 레이아웃 파일
  2. setContentView()로 연결
  3. findViewById() / ViewBinding 등로 개별 뷰 참조
// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)  // activity_main.xml 연결
    }
}

// SecondActivity.kt  
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)  // activity_second.xml 연결
    }
}

파일 구조 예시:

res/layout/
├── activity_main.xml     (MainActivity용)
├── activity_second.xml   (SecondActivity용)
└── activity_settings.xml (SettingsActivity용)

4. R 클래스 개념

R은 AAPT(Android Asset Packaging Tool)가 자동 생성하는 클래스로, res/ 디렉토리의 모든 리소스에 대한 ID를 포함합니다.
이러한 R 클래스는 컴파일 시점에 생성됩니다.

R 클래스의 역할:

  • 모든 리소스에 고유한 정수 ID 할당
  • XML에서 문자열로 참조하던 것을 컴파일 타임에 정수로 변환
  • 타입 안전성 제공 (잘못된 리소스 참조 시 컴파일 에러)

R 클래스 구조 예시:

public final class R {
    public static final class layout {
        public static final int activity_main = 0x7f0c001a;
        public static final int activity_second = 0x7f0c001b;
    }
    public static final class id {
        public static final int textView = 0x7f080045;
        public static final int button = 0x7f080046;
    }
    public static final class string {
        public static final int app_name = 0x7f0f0001;
    }
}

사용 예시:

setContentView(R.layout.activity_main)  // 레이아웃 리소스
findViewById<TextView>(R.id.textView)   // ID 리소스
getString(R.string.app_name)            // 문자열 리소스

5. 유용한 Activity 함수들

View 참조 방법들

1. findViewById() (전통적 방법)

val textView: TextView = findViewById(R.id.textView)
val button: Button = findViewById(R.id.button)

2. View Binding (권장) - 추가 설정 필요

build.gradle(Module: app) 설정:

android {
    buildFeatures { // 안드로이드 스튜디오 4.0 이상 시
        viewBinding = true
    }
    // 이하라면 아래처럼 작성 3.6 보다 낮으면 안 됨
    //  viewBinding {
    //    enabled true
    //}
}

적용:

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 직접 접근 가능
        binding.textView.text = "Hello"
        binding.button.setOnClickListener { }
    }
}

View Binding이 권장되는 이유:
findViewById() 을 대체할 수 있고, 다음과 같은 장점이 있습니다:

  • Null 안전성: 존재하지 않는 뷰 참조 방지
  • 타입 안전성: 잘못된 타입 캐스팅 방지
  • 빠른 컴파일: 어노테이션 처리 불필요

3. Data Binding

// XML에서 직접 데이터 바인딩
<TextView
    android:text="@{user.name}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

ID를 기반으로 특정 View 찾기

<TextView
    android:id="@+id/tv_title"
    android:text="제목"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/tv_description"
    android:text="설명"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/tv_time"
    android:text="시간"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

findViewById 사용 시:

val titleView = findViewById<TextView>(R.id.tv_title)
val descView = findViewById<TextView>(R.id.tv_description)  
val timeView = findViewById<TextView>(R.id.tv_time)

View Binding 사용 시:

binding.tvTitle.text = "제목"        // tv_title → tvTitle (camelCase 변환)
binding.tvDescription.text = "설명"   // tv_description → tvDescription
binding.tvTime.text = "시간"         // tv_time → tvTime

정리:

  • XML ID: @+id/tv_title → R 클래스: R.id.tv_title
  • View Binding: snake_case → camelCase 자동 변환
  • 같은 타입의 뷰라도 각각 다른 고유 ID를 부여하여 구분

리소스 접근 함수들

함수 역할 예시
getString() 문자열 리소스 가져오기 getString(R.string.app_name)
getColor() 색상 리소스 가져오기 getColor(R.color.primary)
getDrawable() 드로어블 리소스 가져오기 getDrawable(R.drawable.icon)

액티비티의 생명주기 관련 및 스레드 관련 기타 함수들

함수 역할 예시
startActivity() 다른 Activity 시작 startActivity(Intent(this, SecondActivity::class.java))
finish() 현재 Activity 종료 finish()
runOnUiThread() UI 스레드에서 코드 실행 runOnUiThread { textView.text = "Updated" }
getSystemService() 시스템 서비스 접근 getSystemService(Context.WIFI_SERVICE)

참고자료

  • Android 공식 문서 - Application Fundamentals
  • Android 공식 문서 - 계측 테스트
  • Android 공식 문서 - 로컬 단위 테스트
  • Android 공식 문서 - Activity 클래스
  • Stack Overflow - Activity 상속 구조
  • Android 공식 문서 - Edge-to-Edge
  • Medium - Android 15 Edge-to-Edge 가이드
  • Android 공식 문서 - AndroidManifest.xml
  • GeeksforGeeks - Android Manifest
  • Android 공식 문서 - View Binding
  • Android 공식 문서 - R 클래스와 리소스 접근
  • todaycode.tistory - 안드로이드 뷰 바인딩(view binding)

'CS 기초부터 한 걸음씩' 카테고리의 다른 글

[Android] Fragment 개념 정리  (4) 2025.08.21
[Android] XML 레이아웃과 View 기본 개념  (0) 2025.08.20
[Android] Android Studio Logcat과 로그 레벨  (3) 2025.08.18
[Android] Activity와 Activity Lifecycle 기초 개념  (5) 2025.08.18
word2vec 이란? - 분포 가설부터 학습 알고리즘까지  (4) 2025.08.04
'CS 기초부터 한 걸음씩' 카테고리의 다른 글
  • [Android] Fragment 개념 정리
  • [Android] XML 레이아웃과 View 기본 개념
  • [Android] Android Studio Logcat과 로그 레벨
  • [Android] Activity와 Activity Lifecycle 기초 개념
Celion
Celion
오늘도 평소처럼 화이팅!
  • Celion
    Orion Log
    Celion
  • 전체
    오늘
    어제
    • 전체 글 (144)
      • Uncompiled Thoughts (8)
        • 네이버 부스트캠프 10기 (5)
      • CS 기초부터 한 걸음씩 (34)
      • Code Odyssey (22)
      • Algorithm (77)
        • Coding Test Records (63)
      • Git (3)
      • reference (0)
  • 블로그 메뉴

    • 태그
    • 방명록
  • 태그

    알고리즘고득점kit
    greedy
    프로그래머스
    백준
    Level3
    시뮬레이션
    문법정리
    Level2
    java
    코테
    boostcamp
    Kotlin
  • 최근 글

  • 인기 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.4
Celion
[Android] 안드로이드 앱 구조 정리
상단으로

티스토리툴바