retrofit 개념 및 간단한 예제 적용해보기

순순·2024년 12월 9일

Android

목록 보기
12/12

깃헙 유저네임을 입력 후 버튼을 누르면, retrofit 을 통해 api 를 호출하여 유저 정보를 불러오는 예제를 구현해보았다.

개념

  • http 통신을 쉽게 하기 위한 라이브러리이다.
  • 싱글톤 패턴이 권장된다.

싱글톤 패턴
싱글톤 패턴 관련 설명 및 코드는 해당 블로그의 내용을 인용했다.

  • 싱글톤 패턴이란 객체의 인스턴스를 하나만 생성해서 전역적으로 쓰는 디자인 패턴을 말한다.
  • 인스턴스를 하나만 생성해서 쓰므로 메모리상의 이점이 있다.
  • 코틀린에서 싱글톤 객체를 생성하는 방법은 object, compainon object 키워드 선언이 있다.
  • 싱글톤 클래스는 멀티쓰레딩 환경에서 오직 한개의 인스턴스만 생성됨을 보장해야하기 때문에 이와 관련된 처리가 필요하다 (synchronized)

  • 코드 예시
    myinfo1, 2, 3은 모두 같은 인스턴스, 하나의 인스턴스를 참조한다.
    잘못된 값을 불러올 일이 없다.
    class MyInfoManager private constructor() {
    
        // singleton pattern
        companion object {
            private var instance: MyInfoManager? = null
            fun getInstance(): MyInfoManager {
                return instance ?: synchronized(this) {
                    instance ?: MyInfoManager().also {
                        instance = it
                    }
                }
            }
        }
    
        private var name = ""
        ...
    
        fun setInfo(name: Strng) {
    	    self.name = name
        }
    
        fun getName(): String {
        	return name
        }
    }
    
    ...
    
    var myInfo1 = MyInfoManager.getInstance()
    var myInfo2 = MyInfoManager.getInstance()
    var myInfo3 = MyInfoManager.getInstance()
  • 1개의 객체만을 참조 하는 companion 변수 instance를 생성.
  • 객체를 참조 하기 위한 companion 함수 getInstance를 정의한다.

구현

textField 에 깃헙 유저네임을 입력 후 버튼을 누르면, userName 과 userInfo를 불러오는 api 를 호출하는 코드를 구현해보았다.

private constructor 선언

class Person private constructor () { ... }

private 접근자를 통해 외부에서 생성자를 통한 객체생성을 막는다.

private constructor 키워드를 사용하면 클래스 내부에서만 생성자에 접근 할 수 있다. 코틀린에서 singleton을 만들때 생성자를 외부에 노출하지 않는 방법이다.


생성자
클래스 생성 시 같이 선언하는게 주생성자이며 1개만 선언 가능하다. 주생성자에 실행문은 넣을 수 없기 때문에 초기화가 필요하다면 init 블럭 내에 적으면 된다. 부생성자(secondary constructor) 는 여러개 가질 수 있고, constructor 키워드를 생략할 수 없다.


viewBinding 적용

(생략)


gradle 설정

    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")

manifest 설정

너무 당연한 말이지만... 네트워크 통신이기 때문에 인터넷 사용권한 추가, 네트워크 트래픽 사용을 허용해야 한다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- 1) 인터넷 사용권한 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application   
        <!-- 2) 네트워크 트래픽 사용 허용 -->
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Retropit"
        android:usesCleartextTraffic="true"
        tools:targetApi="31">
        <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>

xml 코드 작성

유저 이름을 입력 받을 TextField, 불러오기 버튼, 유저 정보를 나타낼 TextView 를 선언했다.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <EditText
            android:id="@+id/usernameInput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter GitHub Username" />
    
        <TextView
            android:id="@+id/userName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="user info"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:padding="10dp" />
    
        <TextView
            android:id="@+id/userInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="user info"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:padding="10dp" />
    
        <Button
            android:id="@+id/mainButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="불러오기" />
    
    </LinearLayout>

데이터 클래스 생성

통신으로 받아온 데이터를 담을 클래스를 생성해준다.

// User.kt
package com.example.retropit

// 데이터 클래스 정의
data class User (
    val login: String,
    val id: Int,
    val avatar_url: String
)

인터페이스 파일 생성

// Api.kt
package com.example.retropit

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path

// Retrofit에게 네트워크 요청의 구성을 설명하는 인터페이스
// 이 인터페이스를 기반으로 Retrofit이 요청을 수행하는 실제 객체를 생성.
interface Api {
    @GET("users/{username}")
    fun getUser(@Path("username") username: String) : Call<User>
}

Retrofit이 네트워트 요청을 수행하는데에 필요한 정보들을 포함하고 있다. 중요한것은 어노테이션, 매개변수, 반환 타입이다. Get, Path 어노테이션에 대한 설명은 다음과 같다


@GET
: HTTP GET 요청을 의미

사용예시
@GET("users/{username}")

https://api.github.com/users/{username}와 같은 URL에 GET 요청을 보내겠다는 의미. 여기서 username 은 매개변수로, 요청 시 동적으로 채워지는 부분이다. 만약 https://api.github.com/users/someUsername로 요청을 보내는 경우, "someUsername"으로 {username}을 채우게 된다.


@Path
: 경로에서 동적으로 값을 넣는 부분을 표시함

사용예시
@Path(”username”)

username이라는 경로 매개변수에 함수 호출 시 전달하는 username 값을 삽입하겠다는 의미이다. 만약 getUser(”JohnDoe”)를 호출하면 https://api.github.com/users/JohnDoe 로 요청을 보낸다.


Call
: Call< User>는 비동기적으로 User 데이터를 받을 수 있는 객체를 의미함

  • Call 객체는 Retrofit 의 요청을 enqueue() 로 비동기 실행하거나, execute() 로 동기 실행 할 수 있다.

  • 아래 코드에서 getUser라는 함수가 Call<User>를 반환하도록 정의하고 있다. (User 는 위에서 만들어줬던 데이터 클래스)


레트로핏 초기화 및 객체 생성 코드


class MainActivity : AppCompatActivity() {
    lateinit var activityMainBinding: ActivityMainBinding
    lateinit var api: Api

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // viewBinding 초기화
        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)

        // retrofit 초기화
        val retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            
            // Retrofit 객체에게 인터페이스에 정의된 대로 네트워크 요청을 수행하는 구현 객체를 만들라고 요청
        api = retrofit.create(api::class.java)

        enableEdgeToEdge()

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}
profile
플러터와 안드로이드를 공부합니다

0개의 댓글