안드로이드 앱 개발에 있어 Kotlin은 강력하고 현대적인 프로그래밍 언어로 자리 잡고 있습니다. 본 강좌에서는 Kotlin을 활용하여 실제 뉴스 앱을 만드는 방법을 단계별로 안내합니다. 앱은 외부 API로부터 뉴스를 가져와 사용자에게 보여주는 구조를 가지고 있습니다. 이 글을 통해 안드로이드 애플리케이션 아키텍처, 네트워킹, UI/UX 디자인 및 기타 필수 기능들을 익힐 수 있습니다.
1. 프로젝트 설정
이번 프로젝트의 첫 번째 단계는 Android Studio에서 새로운 프로젝트를 설정하는 것입니다. 먼저, Android Studio를 열고 새로운 프로젝트를 시작합니다.
- 프로젝트 이름:
NewsApp
- 패키지 이름:
com.example.newsapp
- 언어:
Kotlin
- 기기:
Phone and Tablet
- API Level:
API 21: Android 5.0 (Lollipop)
프로젝트가 생성되면 AndroidManifest.xml 파일을 열고 아래와 같이 인터넷 권한을 추가합니다.
<uses-permission android:name="android.permission.INTERNET" />
2. 뉴스 API 선택
뉴스 앱을 제작하기 위해 사용할 API를 선택합니다. 여기서는 NewsAPI를 사용합니다. 결과를 JSON 형식으로 반환하며 쉽게 사용할 수 있도록 구성되어 있습니다.
API 키를 발급받으려면 해당 사이트에 회원가입 후 API 키를 생성하세요.
3. 네트워킹 라이브러리 추가
Android Jetpack의 Retrofit을 사용하여 네트워크 요청을 관리합니다. Retrofit 라이브러리를 build.gradle 파일에 추가합니다.
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
}
Gradle 파일을 sync합니다.
4. 데이터 모델 클래스 생성
뉴스 API로부터 받아올 데이터를 매핑하기 위해 데이터 클래스를 생성합니다. Article
와 NewsResponse
클래스를 만들어 보세요.
data class Article(
val source: Source,
val author: String?,
val title: String,
val description: String?,
val url: String,
val urlToImage: String?,
val publishedAt: String,
val content: String?
)
data class Source(
val id: String?,
val name: String
)
data class NewsResponse(
val status: String,
val totalResults: Int,
val articles: List<Article>
)
5. Retrofit 인터페이스 정의
NewsAPI와 통신하기 위한 Retrofit 인터페이스를 정의합니다. 이를 통해 HTTP 요청을 구성하게 됩니다.
interface NewsApiService {
@GET("v2/top-headlines")
suspend fun getTopHeadlines(
@Query("country") country: String = "us",
@Query("apiKey") apiKey: String
): NewsResponse
}
6. Retrofit 인스턴스 생성
Retrofit 인스턴스를 생성하여 API 호출을 준비합니다. Singleton 패턴을 사용해 인스턴스를 관리합니다.
object RetrofitInstance {
private const val BASE_URL = "https://newsapi.org"
private val retrofit by lazy {
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
}
val api: NewsApiService by lazy {
retrofit.create(NewsApiService::class.java)
}
}
7. ViewModel 및 LiveData 활용
MVVM 패턴을 적용하기 위해 ViewModel과 LiveData를 사용하여 UI와 데이터를 분리합니다. ViewModel을 생성하여 뉴스 데이터를 관리합니다.
class NewsViewModel(private val apiKey: String) : ViewModel() {
private val _news = MutableLiveData<List<Article>>()
val news: LiveData<List<Article>> get() = _news
fun fetchTopHeadlines() {
viewModelScope.launch {
val response = RetrofitInstance.api.getTopHeadlines(apiKey = apiKey)
_news.postValue(response.articles)
}
}
}
8. UI 설계
MainActivity의 레이아웃 파일을 수정하여 RecyclerView를 추가합니다. 다음은 activity_main.xml의 예입니다.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_article"/>
또한 각 뉴스 아이템을 표시하기 위한 레이아웃 item_article.xml
을 생성합니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="8dp"/>
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:padding="8dp"/>
</LinearLayout>
9. RecyclerView 어댑터 구성
뉴스 데이터를 화면에 표시하기 위해 RecyclerView.Adapter를 구현합니다.
class NewsAdapter(private val articles: List<Article>) : RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() {
inner class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
val descriptionTextView: TextView = itemView.findViewById(R.id.descriptionTextView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_article, parent, false)
return NewsViewHolder(itemView)
}
override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
val currentArticle = articles[position]
holder.titleTextView.text = currentArticle.title
holder.descriptionTextView.text = currentArticle.description
// 이미지 로딩은 Glide 또는 Picasso 같은 라이브러리로 처리
}
override fun getItemCount() = articles.size
}
10. MainActivity에 데이터 바인딩
MainActivity에서 RecyclerView를 설정하고 ViewModel을 통해 데이터를 가져옵니다.
class MainActivity : AppCompatActivity() {
private lateinit var newsViewModel: NewsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val apiKey = "YOUR_API_KEY" // API 키 입력
newsViewModel = ViewModelProvider(this, ViewModelFactory(apiKey)).get(NewsViewModel::class.java)
newsViewModel.fetchTopHeadlines()
newsViewModel.news.observe(this, { articles ->
val adapter = NewsAdapter(articles)
recyclerView.adapter = adapter
})
}
}
11. 사용자 인터페이스 개선
각 뉴스 아이템에 클릭 이벤트를 추가하여 사용자가 기사를 클릭하면 해당 URL로 이동할 수 있도록 합니다.
class NewsAdapter(private val articles: List<Article>, private val context: Context) : RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() {
inner class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
val descriptionTextView: TextView = itemView.findViewById(R.id.descriptionTextView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
init {
itemView.setOnClickListener {
val position: Int = adapterPosition
val article = articles[position]
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(article.url))
context.startActivity(browserIntent)
}
}
}
}
12. 달라지는 사용자 경험
앱의 사용자 경험을 개선하기 위해 ProgressBar를 추가하여 데이터를 로드할 때 사용자에게 진행 상황을 알립니다.
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
데이터 로드 중 ProgressBar를 보이게 하고 로딩이 끝나면 숨기도록 MainActivity 코드를 수정합니다.
newsViewModel.news.observe(this, { articles ->
progressBar.visibility = View.GONE
recyclerView.adapter = NewsAdapter(articles, this)
})
// 데이터 로딩 전 ProgressBar 보여주기
progressBar.visibility = View.VISIBLE
종합 및 결론
이번 강좌에서는 Kotlin을 사용하여 뉴스 앱을 만드는 전 과정을 살펴보았습니다. 네트워크 통신을 위한 Retrofit 설정, MVVM 아키텍처를 적용한 데이터 관리, RecyclerView 사용 등 여러분이 실제 앱 개발에 필요한 기초부터 고급 기술까지 익힐 수 있는 기회였습니다. 추가적으로, 디자인을 개선하거나 기능을 확장하여 더 나은 사용자 경험을 제공할 수 있는 방향으로 나아가면 됩니다.
이제 여러분만의 뉴스 앱을 만들 준비가 되셨습니다! 직접 실습하고 다양한 기능을 추가해 보세요.