안드로이드앱

빗썸 코인 리스트 가져오기 API 불러오기 통신하기

재개 2023. 10. 18. 13:38

해도해도 헷갈리는 API통신 오늘이야 말로 정리하고야 말겠다.

가져온 API 링크 https://api.bithumb.com/public/ticker/ALL_KRW

 

총 파일은 아래 이미지와 같이 필요하다. 원래는 뷰모델도 사용하지 않고 냅다 선언했는데 최근에 뷰 모델을 공부하고 있어서 사용해보려고 한다.

 

빌드 그레들에는 아래와 같이 추가했다.

//뷰모델 스코프 오류났을 때 해결
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation 'androidx.activity:activity-ktx:1.7.1'

// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

// Coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"

 

RetrofitInstance object 파일을 만든다. 

object RetrofitInstance {

    private const val BASE_URL = "https://api.bithumb.com/"

    private val client = Retrofit
        .Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun getInstance() : Retrofit{
        return client
    }
}

BASE_URL을 선언하고, 통신을 위해 Retrofit을 만들어 거기 baseUrl에 BASE_URL을 넣는다. 그리고  getInstance() 을 만들어서  여러 곳에서 동일한 Retrofit를 사용하게 만든다.

 

BASE_URL은 API 완성 링크가 아니다. 한 사이트에서 하나의 링크만 가져오는것이 아니기에 https://api.bithumb.com/{추가추가추가 } 여기 { 추가추가추가 }에 뒤에서  " public/ticker/ALL_KRW " 을 넣어줄거다. 그렇게 이 두개를 연동해서 https://api.bithumb.com/public/ticker/ALL_KRW 이렇게 하나의 링크가 완성되는거다. 

*여기서 잠깐!! 

왜 object 파일일까?
object 키워드가 RetrofitInstance 앞에 붙어 있는 이유는 Kotlin에서 싱글턴 패턴을 쉽게 구현하기 위해서입니다. object 키워드를 사용하면 해당 클래스의 인스턴스가 애플리케이션 내에서 하나만 생성되며, 어디서든지 이 인스턴스에 접근할 수 있습니다. 이것은 Retrofit 라이브러리의 Retrofit 인스턴스를 하나만 생성하고 이를 여러 곳에서 공유하고자 할 때 유용합니다.

RetrofitInstance 객체는 애플리케이션 내에서 Retrofit을 사용하기 위한 공유 인스턴스로 사용되며, 다른 부분에서도 이 인스턴스를 공유하여 네트워크 호출을 처리할 수 있습니다. 이렇게 하면 중복된 Retrofit 인스턴스의 생성을 피하고 메모리를 효율적으로 관리할 수 있습니다.

따라서 object 키워드를 사용하여 RetrofitInstance를 정의한 것은 Retrofit을 초기화하고 애플리케이션 전체에서 공유하기 위한 목적으로 사용되었습니다.

 

 

코인리스트를 담을 data class CurrentPriceList 만들기

코인데이터는 각자 기호에 맞게 가져오면 되는데 나는 아래와 같이 선언하고 가져왔다. 

data class CurrentPriceList (

    val status : String,
    val data : Map<String, Any>

    )

내가 이렇게 가져온 이유는 맨 위  API 링크를 확인하면 좋다. 내가 가져오려고 하는 API는 아래와 같이 데이터가 있다.

빨간 박스로 되어있는 BTC가 코인이름이다. val status : String에 맨위줄 status를 가져오고, val data : Map<string, any="">에는 빨간색 상자와 파란색 상자의 데이터를 저장한다. 

 

 

 

interface Api 만들기

1번 말한 " public/ticker/ALL_KRW " 을 넣어줄거다.

interface Api {

    @GET("public/ticker/ALL_KRW")
    suspend fun getCurrentCoinList() : CurrentPriceList
}
  • interface Api: Retrofit을 사용하여 HTTP 요청을 정의하는 인터페이스입니다. 이 인터페이스를 통해 서버에 요청을 보내고 응답을 받을 수 있습니다.
  • @GET("public/ticker/ALL_KRW"): 이 어노테이션은 해당 메서드가 GET 요청을 나타내며, 괄호 안의 문자열은 요청할 엔드포인트(경로)를 지정합니다. 여기에서는 "public/ticker/ALL_KRW" 엔드포인트에 GET 요청을 보내는 것을 나타냅니다.
  • suspend fun getCurrentCoinList() : CurrentPriceList: 이 메서드는 Retrofit을 통해 호출되면 서버로부터 현재 암호화폐 가격 목록을 가져오는 suspend 함수입니다. CurrentPriceList 객체를 반환하며, 이 객체는 서버에서 반환되는 JSON 응답을 파싱하여 나타내는 데이터 클래스입니다.
  • 즉, 이 코드는 Retrofit을 사용하여 "public/ticker/ALL_KRW" 엔드포인트로 GET 요청을 보내고, 서버로부터 받은 응답을 CurrentPriceList 객체로 파싱하여 반환하는 메서드를 정의한 것입니다.
https://api.bithumb.com/public/ticker/ALL_KRW 이 링크에 있는게 RestAPI야 ?

주어진 URL ("https://api.bithumb.com/public/ticker/ALL_KRW")은 Bithumb의 공개 API 중 하나로 보입니다. 이 URL을 통해 제공되는 데이터를 가져오는 것은 RESTful 웹 서비스의 한 예입니다. HTTP GET 요청을 사용하여 해당 URL에 액세스하면, 해당 서비스는 JSON 형식의 데이터 응답을 반환합니다.

이러한 특성 - URL을 통한 자원의 식별, HTTP 메소드를 사용한 연산, 그리고 상태 메시지를 포함한 응답 - 이 모든 것이 RESTful 웹 서비스의 특징입니다. 따라서, "https://api.bithumb.com/public/ticker/ALL_KRW"는 REST API의 예로 볼 수 있습니다.

 

Class NetWoekRepository를 만들어준다.

val client을 선언 후  ①, ③ 번을 이어준다. 구체적으로는 ①번에서 getInstance()을 create 하여 여기에 ③번에서 만든 API파일 자체를 넣어준다. 그럼 API파일에 만들었던 fun getCurrentCoinList()을 사용할 수 있게 된다. 

class NetWorkRepository {

    private val client = RetrofitInstance.getInstance().create(Api::class.java)
    suspend fun getCurrentCoinList() = client.getCurrentCoinList()
    }
* 여기서 잠깐!!

Repository를 꼭 만들어서 사용해야 할까?? 꼭 사용하진 않아도 되고 그래도 불러와진다. 근데 왜 사용할까? 나는 ViewModel에서 이 클래스를 사용해 가져오려고 한다. 꼭 뷰모델이 아니어도 상관은 없다. API을 통신하기 위해 만들어진 ①,② ,③번의 파일의 최종 결과물? 이라고 나는 생각한다.

장점은 해당 Repository가 여러곳에서 사용되고 있고 만일 ①,② ,③ 번 중 하나를 변경해야 하는 상황이 온다면 유지보수성이 편리할것이다. 

 

MainView Model에서 사용해준다.

class MainViewModel : ViewModel() {

    private val netWorkRepository = NetWorkRepository()

    fun getCurrentCoinList() = viewModelScope.launch {

        val result = netWorkRepository.getCurrentCoinList()
        Log.e("코인리스트 가져오기",result.toString())
    }
}

 

API 불러오는 코드가 viewModelScope.launch { } 안에서  불러져야 하는 이유는 무엇일까?

밖으로 빼면 에러가 난다.
"Suspend function 'getCurrentCoinList' should be called only from a coroutine or another suspend function"
오류가 발생하는 이유는 해당 함수가 코루틴 내에서 호출되어야 하는 suspend 함수이기 때문입니다. Kotlin에서 suspend 함수는 일반적인 함수와는 다르게 비동기적인 작업을 처리할 때 사용됩니다. 

그럼 비동기 작업을 안하면 어떻게 될까?
이 코드가 메인 스레드에서 실행되기 때문에 네트워크 요청을 수행하는 동안 앱의 메인 스레드가 차단(block)됩니다. 안드로이드에서는 메인 스레드에서 긴 작업을 수행하면 앱이 응답하지 않는 것처럼 느껴질 수 있으며, 사용자 경험이 좋지 않습니다. 그래서 네트워크 호출과 같은 긴 작업은 메인 스레드에서 실행하면 안 되며, 대신 백그라운드 스레드에서 실행해야 합니다.

viewModelScope.launch를 사용하여 코드를 비동기적으로 실행하면 작업이 메인 스레드를 차단하지 않고 백그라운드 스레드에서 실행됩니다. 이렇게 하면 앱이 응답성을 유지하면서 네트워크 호출을 처리할 수 있습니다.

 

MainActivity에서 MainViewModel 을 불러서 사용해준다.

class MainActivity : AppCompatActivity() {

    private val viewModel : MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.getCurrentCoinList() <---- 사용하기
    }
}

 

이렇게 하면 로그값이 쭉쭉 찍힌다.

 

 

다음 포스팅에서는 해당 값을 가지고 리사이클러뷰에 뿌려주는것을 올려야겠다.

여기까지 작성한 깃허브 링크

 

GitHub - AnMyungwoo94/API_test

Contribute to AnMyungwoo94/API_test development by creating an account on GitHub.

github.com