해도해도 헷갈리는 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
'안드로이드앱' 카테고리의 다른 글
안드로이드 코틀린 결제창 연동! Bootpay 이니시스 테스트 모드 (1) | 2023.10.31 |
---|---|
빗썸 코인 API Room DB사용하여 저장하기 (0) | 2023.10.23 |
안드로이드 스튜디오 Sqlite 혹은 Room 파일 erd만들기 (0) | 2023.10.09 |
SDK33 permission 외장메모리 AUDIO 승인 대응하기 (0) | 2023.10.06 |
java.lang.IllegalStateException 오류 해결하기 (0) | 2023.09.26 |