카테고리 없음

안드로이드 스튜디오와 Django 연결하기-3

sorecord 2025. 3. 10. 15:39

회원가입이나 로그인 화면과 같은 어떠한 화면도 구현하지 않고 오로지 데이터를 주고받는 것에만 집중해서 코드를 구현하였습니다. 

 

총 4개의 파일을 만들 것이다. 

  

1. Test.kt (데이터 모델)

2. ApiService.kt 

3. RetrofitInstance.kt (Retrofit 인스턴스)

4.MainActivity.kt 


Test.kt - Django 서버에서 받아올 Test 데이터를 담는 클래스.

package com.example.myapplication

data class Test(
    val id: Int,
    val test: String
)

 

data class는 Kotlin에서 간단한 데이터를 저장하기 위한 클래스이다. 


ApiService.kt -  Retrofit을 사용하여 API를 호출하는 인터페이스

package com.example.myapplication

import retrofit2.Call
import retrofit2.http.GET

interface ApiService {
    @GET("tests/")
    fun getTests(): Call<List<Test>>
}
  • Call: Retrofit에서 API 호출의 결과를 나타내는 객체이다. 이 객체를 사용해서 호출의 성공 또는 실패를 처리. 
  • @GET: Retrofit에서 GET 요청을 정의하는 어노테이션. "endpoint"는 호출할 API의 경로를 나타낸다.

정리

tests/ 엔드포인트에 GET 요청을 보내는 API 서비스 인터페이스를 정의 => getTests 메서드는 서버에서 테스트 데이터를 가져온다. => 그 결과를 Call<List<Test>> 형태로 반환하여 호출자가 응답을 처리


 

RetrofitInstance.kt -  Retrofit 객체를 생성하고 API 인터페이스를 초기화

package com.example.myapplication

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitInstance {
    private const val BASE_URL = "http://10.0.2.2:8000/" // 에뮬레이터에서 로컬 서버 접근

    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

 

  • BASE_URL: API 요청의 기본 URL을 정의하는 상수입니다. 여기서 10.0.2.2는 Android 에뮬레이터에서 로컬 개발 서버에 접근하기 위한 주소 + 실제 디바이스에서는 로컬 서버의 IP 주소를 사용
  • .addConverterFactory(GsonConverterFactory.create()): JSON 데이터를 Kotlin 객체로 변환하기 위해 Gson 변환기를 추가+ 이 설정이 없으면 JSON 응답을 처리할 수 없다.
  • .create(ApiService::class.java): 생성된 Retrofit 인스턴스를 사용하여 ApiService 인터페이스의 구현체를 생성

정리

기본 URL을 로컬 서버의 주소로 설정하고, Gson 변환기를 추가하여 JSON 응답을 Kotlin 객체로 변환한 후,

 apiService 프로퍼티는 ApiService 인터페이스의 인스턴스를 지연 초기화하여 제공한다.


-지연 초기화란?

변수를 선언만 하고, 실제로 사용될 때 그 값을 설정하는 방식이다. 불필요한 초기화를 피하고 성능을 최적화할 수 있다.

 

apiService는 변수는 ApiService의 인스턴스를 저장하기 위한 것이다. by lazy { ... }: 이 부분이 지연 초기화를 의미한다. 즉, apiService를 호출하는 순간에만 이 블록 안의 코드가 실행


 

MainActivity.kt 

 

package com.example.myapplication

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApp()
        }
    }

    @Composable
    fun MyApp() {
        var tests by remember { mutableStateOf<List<Test>>(emptyList()) }

        Column(modifier = Modifier.padding(16.dp)) {
            Button(onClick = {
                fetchTests { fetchedTests ->
                    tests = fetchedTests ?: emptyList()
                }
            }) {
                Text("Fetch Tests")
            }

            Spacer(modifier = Modifier.height(16.dp))

            tests.forEach { test ->
                Text(text = "ID: ${test.id}, Test: ${test.test}")
            }
        }
    }

    private fun fetchTests(onResult: (List<Test>?) -> Unit) {
        RetrofitInstance.apiService.getTests().enqueue(object : Callback<List<Test>> {
            override fun onResponse(call: Call<List<Test>>, response: Response<List<Test>>) {
                if (response.isSuccessful) {
                    onResult(response.body())
                } else {
                    Log.e("MainActivity", "Error: ${response.code()}")
                    onResult(null)
                }
            }

            override fun onFailure(call: Call<List<Test>>, t: Throwable) {
                Log.e("MainActivity", "Failure: ${t.message}")
                onResult(null)
            }
        })
    }
}

 

 

@Composable 이 함수가 Compose UI를 구성하는 함수임을 나타낸다. 

Column부분은 UI부분이다.

forEach: tests 리스트의 각 테스트를 순회하며, 각 테스트의 ID와 내용을 텍스트로 표시

 

fetchTests 함수

API를 호출하여 테스트 데이터를 가져온다. 

getTests(): Retrofit 인스턴스를 사용하여 API 호출을 수행

enqueue: 비동기적으로 API 요청을 수행하며, 결과를 처리하기 위해 Callback을 구현

onResponse: API 호출이 성공적으로 완료되면 호출되는 메서드

onFailure: API 호출이 실패할 경우 호출되는 메서드


-콜백이란?

콜백은 특정 작업이 완료된 후에 호출되는 함수를 의미

예> ' 내가 요리를 마치고 다 만들고 나면 너에게 전화해줄게'. 여기서 "전화해줄게"가 콜백이다. 요리를 하는 작업이 끝난 후에 친구에게 전화를 하는 것

위의 코드에서  onResult: (List<Test>?) -> Unit  이 부분이 바로 콜백 함수의 타입을 정의하는 부분이다. 


 

fetchtests의 함수의 동작 과정

  1. API 호출: fetchTests가 호출되면, API를 통해 테스트 데이터를 가져오는 요청을 보낸다.
  2. 비동기 처리: API 요청은 비동기적으로 처리된다. 즉, 요청이 완료될 때까지 기다리지 않고 다음 코드를 실행합니다.
  3. 결과 처리: API 요청이 완료되면, 성공 또는 실패에 따라 onResult 콜백 함수를 호출.
    • 성공적으로 데이터를 가져온 경우: onResult(response.body())를 호출하여 데이터를 전달.
    • 실패한 경우: onResult(null)을 호출하여 null을 전달.

 

위의 동작과정을 이해하고 전체 과정의 흐름을 다시 살펴보자

fetchTests { fetchedTests ->
    tests = fetchedTests ?: emptyList()
}

 

fetchTests를 호출할 때, { fetchedTests -> ... } 부분이 콜백 함수이다.

 

API 요청이 완료되면, fetchedTests에 결과가 담겨서 이 콜백이 실행됩니다. 이 콜백 안에서 tests 변수에 결과를 할당 (위의 3번과정에서 성공적으로 데이터를 가져왔을 경우)

 

tests의 할당된 결과를 foreach로 출력 

 


오늘은 데이터를 받아오는 코드이다. 내일은 안드로이드에서 장고 DB로 값을 보내보겠습니다. 

 

오늘 작성한 코드는 ai의 도움을 많이 받았습니다.