프로젝트 구조를 먼저 알아야 하는 이유
안드로이드 앱 개발을 시작하면 처음에는 MainActivity.kt만 눈에 들어옵니다. 하지만 실제 앱은 하나의 Kotlin 파일로만 이루어지지 않습니다. 화면을 담당하는 코드, 앱 설정 파일, 이미지와 문자열 같은 리소스, 빌드 설정, 패키지 구조가 함께 움직입니다.
프로젝트 구조를 이해하면 다음 일이 쉬워집니다.
오류가 발생했을 때 어느 파일을 봐야 하는지 알 수 있습니다.
새 화면, 이미지, 문자열, 권한을 어디에 추가해야 하는지 알 수 있습니다.
앱이 어떤 Android 버전을 지원하는지 판단할 수 있습니다.
코드와 리소스를 역할별로 정리할 수 있습니다.
이번 장에서는 안드로이드 프로젝트의 핵심 파일과 구성 요소를 한 번에 연결해서 살펴봅니다.

안드로이드 프로젝트의 큰 구조
Android Studio에서 프로젝트를 만들면 보통 다음과 같은 구조를 볼 수 있습니다.
MyApplication/
├─ settings.gradle.kts
├─ build.gradle.kts
├─ app/
│ ├─ build.gradle.kts
│ └─ src/
│ └─ main/
│ ├─ AndroidManifest.xml
│ ├─ java/ 또는 kotlin/
│ │ └─ com/example/myapplication/
│ │ └─ MainActivity.kt
│ └─ res/
│ ├─ drawable/
│ ├─ mipmap/
│ ├─ values/
│ │ ├─ strings.xml
│ │ ├─ colors.xml
│ │ └─ themes.xml
│ └─ ...처음에는 파일이 많아 보이지만, 역할은 크게 네 가지로 나눌 수 있습니다.
빌드 설정: 앱을 어떻게 만들고 실행할지 정합니다.
앱 설정: 앱 이름, 시작 화면, 권한 등을 선언합니다.
Kotlin 코드: 실제 동작과 화면 로직을 작성합니다.
리소스: 문자열, 색상, 이미지, 테마 등을 관리합니다.
이 네 가지가 연결되어 하나의 앱이 됩니다.
app 모듈의 역할
안드로이드 프로젝트에서 app은 가장 중요한 모듈입니다. 모듈은 독립적으로 빌드될 수 있는 코드와 리소스의 묶음입니다. 처음 만드는 대부분의 앱은 app 모듈 하나로 시작합니다.
app 모듈 안에는 실제 앱을 구성하는 파일이 들어 있습니다.
app/
├─ build.gradle.kts
└─ src/
└─ main/
├─ AndroidManifest.xml
├─ kotlin/ 또는 java/
└─ res/여기서 기억해야 할 위치는 세 곳입니다.
app/build.gradle.kts: 앱 모듈의 빌드 설정app/src/main/AndroidManifest.xml: 앱의 주요 정보 선언app/src/main/kotlin또는java: Kotlin 코드 위치app/src/main/res: 이미지, 문자열, 색상 같은 리소스 위치
나중에 앱 규모가 커지면 feature, data, domain 같은 모듈을 추가로 만들 수 있습니다. 하지만 지금은 app 모듈이 하나의 앱을 담는 기본 단위라고 이해하면 충분합니다.
Gradle 설정 파일 이해하기
안드로이드 앱은 Kotlin 파일을 작성한다고 바로 실행되는 것이 아닙니다. 작성한 코드와 리소스를 모아 실제 설치 가능한 앱으로 만드는 과정이 필요합니다. 이 과정을 빌드라고 합니다.
Android Studio는 보통 Gradle이라는 빌드 도구를 사용합니다. Gradle 설정 파일에는 앱을 어떤 방식으로 만들지, 어떤 라이브러리를 사용할지, 어떤 Android 버전을 기준으로 컴파일할지 등이 적혀 있습니다.
프로젝트에는 보통 여러 Gradle 파일이 있습니다.
MyApplication/
├─ settings.gradle.kts
├─ build.gradle.kts
└─ app/
└─ build.gradle.kts각 파일의 역할은 다릅니다.
| 파일 | 역할 |
|---|---|
settings.gradle.kts | 프로젝트에 어떤 모듈이 포함되는지 설정합니다. |
루트 build.gradle.kts | 프로젝트 전체에 적용되는 공통 빌드 설정을 관리합니다. |
app/build.gradle.kts | 앱 모듈의 Android 버전, 앱 ID, 의존성 등을 설정합니다. |
우리가 가장 자주 수정하는 파일은 app/build.gradle.kts입니다.
예를 들어 다음과 같은 설정을 볼 수 있습니다.
android {
namespace = "com.example.myapplication"
compileSdk = 35
defaultConfig {
applicationId = "com.example.myapplication"
minSdk = 23
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
}이 설정은 앱의 기본 정보를 담고 있습니다.
namespace: 코드와 리소스를 구분하기 위한 이름 공간입니다.applicationId: 기기에 설치될 앱의 고유 ID입니다.compileSdk: 앱을 컴파일할 때 사용하는 Android SDK 버전입니다.minSdk: 앱이 설치될 수 있는 가장 낮은 Android 버전입니다.targetSdk: 앱이 최적화되어 있다고 선언하는 Android 버전입니다.versionCode: 내부적으로 사용하는 앱 버전 번호입니다.versionName: 사용자에게 보이는 앱 버전 이름입니다.
여기서 namespace와 applicationId는 비슷해 보이지만 역할이 다릅니다. namespace는 코드와 리소스를 위한 이름이고, applicationId는 실제 앱을 구분하는 ID입니다. 보통 처음에는 둘을 같게 두는 경우가 많습니다.
AndroidManifest.xml의 역할
AndroidManifest.xml은 앱의 설명서 같은 파일입니다. Android 시스템은 이 파일을 보고 앱의 핵심 정보를 파악합니다.
위치는 다음과 같습니다.
app/src/main/AndroidManifest.xml예시는 다음과 같습니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:theme="@style/Theme.MyApplication"
android:label="@string/app_name"
android:allowBackup="true">
<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>
</application>
</manifest>핵심은 application과 activity입니다.
application 태그
application 태그는 앱 전체에 적용되는 정보를 담습니다.
<application
android:theme="@style/Theme.MyApplication"
android:label="@string/app_name">여기서 android:theme은 앱의 기본 테마를 지정합니다. @style/Theme.MyApplication은 res/values/themes.xml에 정의된 스타일을 가리킵니다.
android:label은 앱 이름입니다. @string/app_name은 문자열 리소스를 의미하며, 실제 값은 res/values/strings.xml에 들어 있습니다.
즉 Manifest는 값을 직접 모두 적기보다 리소스를 참조하는 경우가 많습니다.
activity 태그
activity 태그는 앱의 화면 단위를 등록합니다.
<activity
android:name=".MainActivity"
android:exported="true">android:name=".MainActivity"는 이 Activity가 현재 패키지 안의 MainActivity라는 뜻입니다. 실제 파일은 보통 다음 위치에 있습니다.
app/src/main/kotlin/com/example/myapplication/MainActivity.ktintent-filter는 이 Activity가 어떤 방식으로 실행될 수 있는지 선언합니다.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>MAIN과 LAUNCHER가 함께 있으면, 이 Activity는 앱 아이콘을 눌렀을 때 처음 실행되는 화면이 됩니다.
따라서 앱 실행 흐름은 이렇게 볼 수 있습니다.
사용자가 앱 아이콘을 누름
→ Android 시스템이 AndroidManifest.xml 확인
→ MAIN + LAUNCHER로 표시된 Activity 찾음
→ MainActivity 실행
→ 화면 표시이 흐름을 이해하면 "앱을 켰을 때 왜 이 화면이 먼저 뜨는가?"라는 질문에 답할 수 있습니다.
패키지 구조의 역할
Kotlin 파일은 패키지 안에 들어 있습니다. 패키지는 코드를 분류하는 주소 같은 역할을 합니다.
MainActivity.kt의 맨 위에는 보통 다음과 같은 코드가 있습니다.
package com.example.myapplication그리고 파일 위치도 패키지 이름과 비슷한 구조를 가집니다.
com/example/myapplication/MainActivity.kt패키지를 쓰는 이유는 코드가 많아졌을 때 충돌을 막고 역할별로 정리하기 위해서입니다.
작은 앱에서는 다음처럼 단순할 수 있습니다.
com.example.myapplication
└─ MainActivity.kt하지만 앱이 커지면 다음처럼 나눌 수 있습니다.
com.example.myapplication
├─ ui/
│ ├─ home/
│ └─ settings/
├─ data/
├─ model/
└─ MainActivity.kt예를 들어 ui에는 화면 코드, data에는 데이터 처리 코드, model에는 데이터 형태를 나타내는 클래스를 둘 수 있습니다.
중요한 것은 처음부터 복잡하게 나누는 것이 아닙니다. 파일이 많아질 때 찾기 쉽게 나누는 것입니다. 좋은 패키지 구조는 "이 코드는 어디에 있어야 하는가?"라는 질문에 쉽게 답하게 해 줍니다.
MainActivity는 무엇을 하는가
MainActivity는 앱의 첫 화면을 담당하는 Activity입니다. Activity는 사용자가 보고 상호작용하는 화면의 기본 단위입니다.
Jetpack Compose 프로젝트에서는 MainActivity.kt가 대략 다음과 같은 형태를 가집니다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Greeting(name = "Android")
}
}
}
}이 코드를 단계별로 보면 어렵지 않습니다.
class MainActivity : ComponentActivity()MainActivity가 ComponentActivity를 상속한다는 뜻입니다. 즉 Android 화면으로 동작하기 위해 필요한 기본 기능을 물려받습니다.
override fun onCreate(savedInstanceState: Bundle?)onCreate는 Activity가 처음 만들어질 때 호출되는 함수입니다. 여기에서 초기 설정을 합니다.
setContent {
...
}setContent는 Compose 화면을 Activity에 연결합니다. 쉽게 말해 "이 Activity에 어떤 화면을 보여줄지" 정하는 부분입니다.
아직 Compose 문법을 모두 이해하지 않아도 괜찮습니다. 지금은 MainActivity가 앱 시작점이고, onCreate에서 화면을 준비하며, setContent로 Compose UI를 표시한다고 이해하면 됩니다.
Activity 생명주기 이해하기
Activity는 한 번 실행되면 계속 같은 상태로 머무르지 않습니다. 사용자가 앱을 켜고, 다른 앱으로 이동하고, 다시 돌아오고, 화면을 회전하고, 앱을 종료하는 동안 Activity의 상태가 바뀝니다.
이 상태 변화의 흐름을 Activity 생명주기라고 합니다.
주요 함수는 다음과 같습니다.
| 함수 | 호출되는 시점 |
|---|---|
onCreate() | Activity가 처음 생성될 때 |
onStart() | 화면이 사용자에게 보이기 시작할 때 |
onResume() | 사용자가 화면과 상호작용할 수 있을 때 |
onPause() | 다른 화면이 앞에 나타나 현재 화면이 잠시 멈출 때 |
onStop() | 화면이 더 이상 보이지 않을 때 |
onDestroy() | Activity가 종료되어 제거될 때 |
onRestart() | 멈췄던 Activity가 다시 시작될 때 |
흐름으로 보면 다음과 같습니다.
앱 실행
→ onCreate()
→ onStart()
→ onResume()
→ 사용자가 앱 사용다른 앱으로 이동하면 다음 흐름이 발생할 수 있습니다.
다른 앱으로 이동
→ onPause()
→ onStop()다시 앱으로 돌아오면 다음처럼 이어집니다.
앱으로 복귀
→ onRestart()
→ onStart()
→ onResume()앱이 완전히 종료되면 다음 함수가 호출될 수 있습니다.
앱 종료
→ onPause()
→ onStop()
→ onDestroy()여기서 중요한 점은 사용자가 앱을 "종료했다"고 느끼는 순간과 시스템이 Activity를 실제로 제거하는 순간이 항상 같지는 않다는 것입니다. Android는 메모리 상황, 화면 전환, 시스템 정책에 따라 Activity를 멈추거나 제거할 수 있습니다.
생명주기를 왜 알아야 할까
생명주기를 이해해야 하는 이유는 앱이 항상 켜진 상태로만 동작하지 않기 때문입니다.
예를 들어 음악 앱을 생각해 봅시다.
화면이 보일 때 재생 버튼 상태를 갱신해야 합니다.
앱이 백그라운드로 가도 음악은 계속 재생될 수 있습니다.
사용자가 다시 돌아오면 현재 재생 중인 곡을 보여줘야 합니다.
또는 카메라 앱에서는 다음이 중요합니다.
화면이 열릴 때 카메라를 준비합니다.
화면을 벗어나면 카메라 자원을 해제합니다.
다시 돌아오면 카메라를 다시 연결합니다.
이처럼 생명주기는 자원을 언제 준비하고 언제 정리할지 판단하는 기준이 됩니다.
초기 학습 단계에서는 모든 생명주기 함수를 외우려고 하지 않아도 됩니다. 우선 다음 세 가지만 기억하세요.
onCreate()에서 초기 설정을 합니다.onResume()은 화면이 실제로 사용 가능한 상태입니다.onPause()나onStop()에서는 잠시 멈추거나 정리할 일을 처리합니다.
Compose를 사용하더라도 Activity 생명주기는 여전히 중요합니다. Compose는 화면을 만드는 방식이고, Activity는 그 화면이 Android 시스템 안에서 살아가는 틀입니다.
리소스 디렉터리의 역할
안드로이드 프로젝트에는 res 디렉터리가 있습니다.
app/src/main/res/res는 resource의 줄임말입니다. 앱에서 사용하는 문자열, 색상, 이미지, 테마 같은 정적 자원을 보관합니다.
대표적인 디렉터리는 다음과 같습니다.
| 디렉터리 | 역할 |
|---|---|
drawable | 이미지, 도형 XML, 벡터 이미지 등을 저장합니다. |
mipmap | 앱 아이콘 이미지를 저장합니다. |
values | 문자열, 색상, 테마 같은 값을 XML로 저장합니다. |
font | 글꼴 파일을 저장합니다. |
raw | 가공하지 않은 원본 파일을 저장합니다. |
xml | 설정용 XML 파일을 저장합니다. |
리소스를 따로 관리하면 코드가 깔끔해집니다. 예를 들어 앱 이름을 Kotlin 코드에 직접 쓰지 않고 strings.xml에 넣으면 여러 곳에서 같은 값을 재사용할 수 있습니다.
문자열 리소스 관리
문자열은 보통 res/values/strings.xml에 저장합니다.
<resources>
<string name="app_name">My Application</string>
<string name="welcome_message">안녕하세요</string>
</resources>Manifest에서는 다음처럼 참조할 수 있습니다.
android:label="@string/app_name"Compose 코드에서도 문자열 리소스를 가져와 사용할 수 있습니다.
val message = stringResource(id = R.string.welcome_message)문자열을 리소스로 관리하는 가장 큰 이유는 다국어 지원입니다. 예를 들어 한국어와 영어를 지원하고 싶다면 언어별 리소스 폴더를 둘 수 있습니다.
res/values/strings.xml // 기본 문자열
res/values-ko/strings.xml // 한국어 문자열
res/values-en/strings.xml // 영어 문자열이렇게 하면 사용자의 기기 언어에 맞춰 적절한 문자열을 보여줄 수 있습니다.
초보자가 자주 하는 실수는 화면 코드 안에 모든 문구를 직접 쓰는 것입니다.
Text("안녕하세요")작은 예제에서는 괜찮지만, 실제 앱에서는 다음 방식이 더 좋습니다.
Text(stringResource(R.string.welcome_message))문자열을 리소스로 분리하면 수정, 재사용, 번역이 쉬워집니다.
색상과 테마 관리
색상은 보통 res/values/colors.xml에 정의합니다.
<resources>
<color name="purple_500">#6200EE</color>
<color name="white">#FFFFFF</color>
</resources>테마는 themes.xml에서 관리합니다.
<resources>
<style name="Theme.MyApplication" parent="android:style/Theme.Material.Light.NoActionBar">
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>Jetpack Compose를 사용하는 프로젝트에서는 Kotlin 코드로 색상과 테마를 정의하는 경우도 많습니다. 예를 들어 ui/theme 패키지 안에 다음과 같은 파일이 있을 수 있습니다.
ui/theme/
├─ Color.kt
├─ Theme.kt
└─ Type.ktXML 기반 리소스와 Compose 테마는 함께 사용될 수 있습니다.
Manifest는
@style/Theme.MyApplication처럼 XML 스타일을 참조합니다.Compose 화면은
MyApplicationTheme { ... }처럼 Kotlin 테마 함수를 사용합니다.
처음에는 이 둘이 분리되어 보여 헷갈릴 수 있습니다. 간단히 말하면, XML 테마는 Android 시스템 수준의 기본 모양을 정하고, Compose 테마는 Compose 화면 안의 색상, 글꼴, 형태를 정한다고 이해하면 됩니다.
이미지 리소스 관리
이미지는 보통 drawable 또는 mipmap에 넣습니다.
res/drawable/
res/mipmap/두 폴더의 용도는 조금 다릅니다.
drawable: 화면 안에서 사용하는 이미지mipmap: 앱 아이콘 이미지
예를 들어 res/drawable/logo.png 파일이 있다면 Compose에서 다음처럼 사용할 수 있습니다.
Image(
painter = painterResource(id = R.drawable.logo),
contentDescription = "앱 로고"
)여기서 R.drawable.logo는 리소스를 코드에서 참조하기 위해 자동으로 만들어지는 이름입니다.
파일 이름에는 주의해야 합니다. 리소스 파일 이름은 보통 소문자, 숫자, 밑줄을 사용합니다.
좋은 예:
ic_home.png
profile_image.png
background_main.xml피해야 할 예:
HomeIcon.png
profile-image.png
main background.png파일 이름 규칙을 지키지 않으면 빌드 오류가 발생할 수 있습니다.
R 클래스는 리소스와 코드를 연결한다
리소스를 추가하면 Android 빌드 도구가 R이라는 클래스를 자동으로 생성합니다. 이 클래스 덕분에 Kotlin 코드에서 리소스를 참조할 수 있습니다.
예를 들어 strings.xml에 다음 문자열이 있다고 합시다.
<string name="app_name">Study App</string>그러면 코드에서는 다음처럼 접근할 수 있습니다.
R.string.app_name이미지는 다음처럼 접근합니다.
R.drawable.logo색상은 다음처럼 접근합니다.
R.color.purple_500초보자가 자주 만나는 오류 중 하나가 R 관련 오류입니다. 보통 원인은 리소스 XML 문법 오류, 잘못된 파일 이름, 중복된 리소스 이름입니다.
예를 들어 strings.xml에서 닫는 태그를 빠뜨리면 전체 리소스 처리가 실패할 수 있습니다. 그러면 아무 문제 없어 보이는 Kotlin 파일에서 R을 찾을 수 없다는 오류가 나타나기도 합니다.
이럴 때는 Kotlin 코드만 보지 말고 최근 수정한 리소스 파일도 함께 확인해야 합니다.
minSdk, targetSdk, compileSdk 이해하기
안드로이드 앱은 다양한 기기와 OS 버전에서 실행됩니다. 그래서 프로젝트 설정에는 SDK 버전 정보가 들어갑니다.
대표적으로 세 가지가 중요합니다.
android {
compileSdk = 35
defaultConfig {
minSdk = 23
targetSdk = 35
}
}이 세 값은 비슷해 보이지만 의미가 다릅니다.
| 항목 | 의미 |
|---|---|
minSdk | 앱이 설치될 수 있는 최소 Android 버전 |
targetSdk | 앱이 대응하도록 설계한 Android 버전 |
compileSdk | 앱을 컴파일할 때 사용하는 SDK 버전 |
각각을 조금 더 자세히 보겠습니다.
minSdk: 어디까지 오래된 기기를 지원할 것인가
minSdk는 앱이 설치될 수 있는 가장 낮은 Android 버전입니다.
minSdk = 23이 설정은 Android 6.0 이상 기기에 앱을 설치할 수 있다는 뜻입니다. 그보다 낮은 버전의 기기에는 설치할 수 없습니다.
minSdk를 낮게 잡으면 더 많은 기기를 지원할 수 있습니다. 하지만 오래된 Android 버전까지 고려해야 하므로 구현이 복잡해질 수 있습니다.
반대로 minSdk를 높게 잡으면 최신 기능을 사용하기 쉬워집니다. 하지만 오래된 기기 사용자는 앱을 설치할 수 없습니다.
따라서 선택 기준은 다음과 같습니다.
학습용 앱: 지나치게 낮게 잡을 필요가 없습니다.
실제 출시 앱: 사용자층의 기기 분포를 고려해야 합니다.
최신 API가 필요한 앱: 필요한 기능이 지원되는 최소 버전을 확인해야 합니다.
처음 학습할 때는 Android Studio가 기본으로 제안하는 값을 따르는 것이 좋습니다. 이후 실제 앱을 만들 때 사용자와 기능 요구사항에 맞춰 조정하면 됩니다.
targetSdk: 어떤 Android 정책에 맞출 것인가
targetSdk는 앱이 어느 Android 버전에 맞춰 동작하도록 준비되었는지 선언하는 값입니다.
targetSdk = 35이 값은 "이 앱은 Android 35 수준의 동작 변경과 정책을 고려했다"는 의미에 가깝습니다.
Android는 버전이 올라가면서 보안, 권한, 백그라운드 실행 제한 같은 정책을 바꿉니다. targetSdk를 올리면 최신 Android 정책이 앱에 적용될 수 있습니다.
예를 들어 어느 Android 버전부터 특정 권한 요청 방식이 바뀌었다면, targetSdk를 올렸을 때 그 방식에 맞게 코드를 수정해야 할 수 있습니다.
실제 출시 앱에서는 Google Play 정책에 따라 일정 수준 이상의 target SDK를 요구하는 경우가 많습니다. 그래서 앱을 오래 유지보수하려면 targetSdk를 주기적으로 올리고, 변경된 동작을 확인해야 합니다.
초기 학습 단계에서는 Android Studio가 설정한 최신 값 또는 권장 값을 사용하면 됩니다. 다만 나중에 오류가 생겼을 때 targetSdk 변경으로 인해 동작 방식이 달라질 수 있다는 점은 기억해야 합니다.
compileSdk: 어떤 도구로 컴파일할 것인가
compileSdk는 앱을 컴파일할 때 사용하는 Android SDK 버전입니다.
compileSdk = 35이 값은 앱이 최신 Android API를 코드에서 사용할 수 있는지와 관련이 있습니다. compileSdk가 낮으면 최신 API를 코드에서 참조할 수 없습니다.
중요한 점은 compileSdk를 높인다고 해서 앱이 자동으로 최신 Android에서만 실행되는 것은 아니라는 것입니다. 설치 가능 여부는 minSdk가 결정합니다.
예를 들어 다음 설정을 봅시다.
compileSdk = 35
minSdk = 23
targetSdk = 35이 설정은 다음 뜻입니다.
Android 35 SDK를 기준으로 앱을 컴파일합니다.
Android 23 이상 기기에 설치할 수 있습니다.
Android 35의 정책과 동작을 고려한 앱으로 선언합니다.
따라서 세 값을 한 문장으로 정리하면 다음과 같습니다.
compileSdk는 개발할 때 쓰는 기준,
minSdk는 설치 가능한 최소 기준,
targetSdk는 앱이 대응한다고 선언한 기준입니다.버전 선택 기준
처음 앱을 만들 때는 SDK 버전을 직접 깊게 고민하지 않아도 됩니다. Android Studio가 생성한 기본값을 사용하면 됩니다.
하지만 실제 앱을 만들거나 기존 프로젝트를 수정할 때는 다음 기준을 생각해야 합니다.
1. minSdk는 사용자 범위와 개발 비용의 균형이다
낮은 minSdk는 더 많은 사용자를 포함하지만, 오래된 버전 대응 비용이 늘어납니다. 높은 minSdk는 개발이 단순해질 수 있지만, 일부 사용자를 제외합니다.
2. targetSdk는 가능하면 최신 정책에 맞춘다
targetSdk는 앱의 보안과 플랫폼 정책에 영향을 줍니다. 출시 앱이라면 최신 요구사항을 계속 확인해야 합니다.
3. compileSdk는 최신 안정 버전을 사용하는 편이 좋다
최신 API와 빌드 도구의 개선을 활용할 수 있기 때문입니다. 다만 팀 프로젝트에서는 사용 중인 Android Gradle Plugin, Kotlin 버전, 라이브러리 호환성도 함께 확인해야 합니다.
버전 문제는 혼자 떨어져 있지 않습니다. Gradle, Kotlin, Compose, Android Gradle Plugin 버전이 서로 맞아야 합니다. 그래서 의존성 오류가 발생하면 SDK 값뿐 아니라 플러그인과 라이브러리 버전도 함께 확인해야 합니다.
프로젝트 구성 요소가 연결되는 흐름
이제 전체 흐름을 하나로 연결해 보겠습니다.
앱을 실행하면 Android 시스템은 먼저 Manifest를 확인합니다.
AndroidManifest.xml
→ MAIN + LAUNCHER Activity 확인
→ MainActivity 실행MainActivity가 실행되면 onCreate()가 호출됩니다.
MainActivity.onCreate()
→ setContent { ... }
→ Compose 화면 표시화면에서 문자열, 이미지, 색상을 사용하면 리소스를 참조합니다.
strings.xml → R.string.app_name
logo.png → R.drawable.logo
colors.xml → R.color...
themes.xml → @style/Theme...빌드할 때는 Gradle 설정이 사용됩니다.
app/build.gradle.kts
→ applicationId 확인
→ minSdk, targetSdk, compileSdk 확인
→ 의존성 확인
→ 앱 빌드즉 프로젝트의 주요 파일은 따로 떨어져 있지 않습니다. 다음처럼 서로 연결됩니다.
Gradle 설정: 앱을 어떻게 빌드할지 결정
Manifest: 앱의 시작점과 주요 설정 선언
MainActivity: 앱의 첫 화면 실행
res 디렉터리: 화면에 필요한 자원 제공
패키지 구조: 코드를 역할별로 정리이 연결을 이해하면 프로젝트를 훨씬 안정적으로 다룰 수 있습니다.
자주 만나는 오류와 확인 위치
프로젝트 구조를 알면 오류를 해결할 때도 도움이 됩니다. 초보자가 자주 만나는 상황을 정리해 보겠습니다.
앱 이름을 바꿨는데 반영되지 않는다
확인할 곳은 strings.xml입니다.
<string name="app_name">새 앱 이름</string>Manifest에서 android:label="@string/app_name"을 사용하고 있다면 앱 이름은 이 문자열 리소스에 의해 결정됩니다.
앱을 실행했는데 원하는 화면이 처음 뜨지 않는다
확인할 곳은 AndroidManifest.xml입니다.
MAIN과 LAUNCHER가 붙은 Activity가 시작 화면입니다. 여러 Activity를 사용하는 앱이라면 어떤 Activity에 이 설정이 있는지 확인해야 합니다.
이미지를 추가했는데 코드에서 찾을 수 없다
확인할 곳은 res/drawable과 파일 이름입니다.
파일 이름에 대문자나 하이픈이 있는지 확인합니다.
확장자가 올바른지 확인합니다.
Gradle Sync 또는 Rebuild가 필요한지 확인합니다.
R을 찾을 수 없다는 오류가 난다
Kotlin 코드만 보지 말고 리소스 XML을 확인해야 합니다.
strings.xml문법 오류colors.xml문법 오류잘못된 리소스 파일 이름
중복된 리소스 이름
리소스 처리에 실패하면 R 클래스 생성이 제대로 되지 않을 수 있습니다.
라이브러리를 추가했는데 import가 안 된다
확인할 곳은 app/build.gradle.kts입니다.
의존성을 추가한 뒤에는 Gradle Sync가 필요합니다. Sync가 실패하면 라이브러리를 코드에서 사용할 수 없습니다.
좋은 프로젝트 정리 습관
초기에는 프로젝트가 작기 때문에 아무 곳에나 파일을 두어도 문제가 없어 보입니다. 하지만 앱이 커지면 정리 습관이 개발 속도를 좌우합니다.
다음 원칙을 추천합니다.
화면 코드는
ui아래에 둡니다.데이터 클래스는
model또는 기능별 패키지에 둡니다.네트워크, 데이터베이스 코드는 화면 코드와 분리합니다.
문자열은 가능하면
strings.xml로 분리합니다.이미지 파일 이름은 소문자와 밑줄로 작성합니다.
Gradle 설정을 바꾼 뒤에는 Sync 결과를 확인합니다.
처음부터 완벽한 구조를 만들 필요는 없습니다. 중요한 것은 파일이 늘어날수록 역할에 맞게 옮기고 이름을 정리하는 것입니다.
이번 장에서 기억할 것
안드로이드 프로젝트는 여러 파일과 디렉터리가 함께 동작하는 구조입니다. MainActivity.kt만 보는 단계에서 벗어나 Gradle, AndroidManifest.xml, res, 패키지 구조를 함께 볼 수 있어야 합니다.
핵심을 정리하면 다음과 같습니다.
app모듈은 실제 앱 코드와 리소스를 담는 기본 단위입니다.Gradle 설정은 앱의 빌드 방식, SDK 버전, 의존성을 관리합니다.
AndroidManifest.xml은 앱의 시작점, 테마, 권한 같은 주요 정보를 선언합니다.Activity는 화면의 기본 단위이며 생명주기에 따라 생성, 중단, 재개, 종료됩니다.
res디렉터리는 문자열, 색상, 이미지, 테마를 관리합니다.minSdk,targetSdk,compileSdk는 각각 설치 가능 버전, 대응 정책 버전, 컴파일 기준 버전을 의미합니다.
프로젝트 구조를 이해하면 앱 개발이 덜 막연해집니다. 다음 장에서는 이 구조 위에서 Jetpack Compose를 사용해 실제 화면을 만드는 방법을 배웁니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
