[Android 앱 개발 심화] 5. Retrofit

서버와 클라이언트

  • 서버(Server)
    • 데이터나 리소스를 제공하는 시스템
    • 사용자의 요청을 기다리고, 요청이 들어오면 그에 맞는 응답 전송
  • 클라이언트(Client)
    • 사용자를 대표하여 서버에 정보나 서비스를 요청하는 시스템
    • 웹 브라우저, 모바일 앱, 데스크톱 앱 등 다양한 형태로 존재
  • 3-Tier 아키텍쳐: 클라이언트 <-> 서버 <-> 데이터베이스

서버-클라이언트 통신

  • 사용하는 프로토콜, 용도, 성능 요구사항 등에 따라 적절한 방식 선택됨
    • HTTP/HTTPS:
      • 웹 기반의 애플리케이션에서 주로 사용
      • REST API나 SOAP와 같은 웹 서비스 통신 방식의 기반
    • WebSockets:
      • 실시간 양방향 통신이 필요한 애플리케이션에서 사용
      • ex. 채팅 애플리케이션, 실시가 게임, 등
    • Socket(TCP/UDP):
      • TCP 또는 UDP 프로토콜을 사용하여 데이터 전송
      • 지속적인 연결을 유지하며 양방향 통신 가능
    • FTP(File Transfer Protocol):
      • 파일 전송에 특화된 프로토콜
    • RPC(Remote Procedure Call):
      • 네트워크를 통해 다른 주소 공간에 있는 Procedure(또는 함수)를 호출하는 방식
    • SOAP(Simple Object Access Protocol):
      • XML 기반의 메세징 프로토콜
      • 주로 웹 서비스에서 사용
    • GraphQL:
      • 페이스북이 개발한 데이터 쿼리 및 조작 언어
      • 클라이언트가 필요한 데이터의 구조를 명시하여 서버로부터 데이터를 받아옴
    • gRPC:
      • 구글이 개발한 RPC 프로토콜
      • 효율적인 양방향 통신 지원, 여러 프로그래밍 언어 지원
    • MQTT:
      • IoT 기기와 같은 경량 클라이언트에서 사용되는 메세지 프로토콜
    • Message Queues:
      • 비동기 메세징을 위한 시스템
      • 메세지를 전송하는 생산자와 메세지를 받아 처리하는 소비자 사이에 메세지 전송
      • ex. RabbitMQ, Kafka

REST API

  • REST: Representational State Transfer

REST의 핵심 원칙

  • REST 원칙을 지킨 API = RESTful API
  • 자원(리소스) 식별: 각 리소스는 고유한 URL로 식별
  • 메세지의 상태를 통한 표현: 리소스는 JSON, XML 등의 형식으로 표현
  • Stateless 통신: 각 요청을 서버에서 필요한 모든 정보를 포함해야
    -> 서버는 각 요청을 개별적으로 처리
  • 클라이언트-서버 구조: 사용자 인터페이스와 데이터 저장소 분리
    -> 독립성 높아짐
  • 캐시 처리 가능: 응답 데이터에 캐싱이 사능한지 여부 명시
    -> 성능 향상
  • 계층화된 시스템: 서버와 클라이언트 사이에 다양한 계층(ex. 보안, 로드 밸런싱, 등) 존재 가능

REST와 HTTP

  • REST는 HTTP 프로토콜 위에서 구현되는 경우가 많음
  • 주요 HTTP 메서드:
    • GET: 리소스 조회
    • POST: 리소스 생성
    • PUT: 리소스 수정
    • DELETE: 리소스 삭제

JSON

  • JSON?
    • JavaScript Object Notation
    • 데이터를 저장하거나 전송할 때 사용되는 경량 데이터 교환 방식
  • JSON 데이터는 name과 value로 이루어짐
    "name":value
  • JSON 배열은 대괄호([])로 표현
    • "name":["name":value, "name":value, "name":value]
  • 직렬화(Serialization):
    사용하는 프로그래밍 언어의 객체를 JSON 형태의 데이터로 변환
  • 역직렬화(Deserialization):
    JSON 형태 데이터를 사용하는 프로그래밍 언어의 객체로 변환

GSON

  • GSON?
    • 구글에서 제공하는 오픈소스 라이브러리
    • Java, Kotlin에서 주로 사용
    • 직렬화와 역직렬화 작업을 간단하게 해줌

기본 사용법

  • Kotlin 객체를 JSON 데이터로 변환하기
val gson = Gson()
val jsonString = gson.toJson(someObject)
  • JSON 데이터를 Kotlin 객체로 변환하기
val myClassInstance: MyClass = gson.fromJson(jsonString, MyClass::class.java)

고급 사용법

  • Custom Serializer/Deserializer 정의하기
  • @Serialized 애노테이션으로 Kotlin 필드와 JSON name 매핑하기
data class Person(
    @SerializedName("person_name")
    val name: String
)
  • Exclution Strategies로 특정 필드 직렬화/역직렬화 제외

Retrofit

  • Retrofit?
    • Square Inc.에서 개발한 안드로이드 및 자바를 위한 Type-Safe HTTP 클라이언트 라이브러리
    • REST API의 HTTP 요청을 자바 인터페이스로 변환하는 목적
  • Retrofit의 장점
    • 코드의 간결성:
      • 간단한 애노테이션을 통해 요청 메서드와 URL 정의 가능
    • 안정성과 확장성:
      • 내부적으로 OkHttp 라이브러리 사용하여 통신 -> 안정성
      • 인터셉터를 사용하여 요청/응답 프로세스 확장/수정 가능 -> 확장성
    • 다양한 플러그인과 컨버터 지원
      • 다양한 데이터 형식(JSON, XML, 등)에 대해 데이터 변환 컨버터 제공
      • 비동기 프로그래밍 라이브러리(RxJava, Coroutines, 등)과 연동 가능

Retrofit 시작하기

  • Gradle에 Retrofit 라이브러리 추가
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.x.x'
    // Gson 컨버터 추가
    implementation 'com.squareup.retrofit2:converter-gson:2.x.x' 
}
  • API 인터페이스 정의
interface ApiService {
    @GET("users/{id}")
    fun getUser(@Path("id") id: Int): Call<User>
}
  • Retrofit 인스턴스 생성
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create(ApiService::class.java)

응답 요청하기

  • 동기식 요청:
    • 현재 스레드에서 실행됨
    • 응답이 올 때까지 다음 코드의 실행 중단됨
val response: Response<User> = apiService.getUser(id).execute()
  • 비동기식 요청:
    • 콜백을 사용하여 백그라운드에서 실행
    • 응답이 오면 해당 콜백 호출됨
apiService.getUser(id).enqueue(object: Callback<User> {
    override fun onResponse(call: Call<User>, response: Response<User>) {
        // 응답 처리
    }
    override fun onFailure(call: Call<User>, t: Throwable) {
        // 오류 처리
    }
})

응답 처리하기

  • 응답 처리: Responce 객체를 통해 HTTP 응답의 여러 정보에 접근 가능
override fun onResponse(call: Call<User>, response: Response<User>) {
	if (response.isSuccessful) {
    	val user: User? = response.body()
	} else {
    	// 오류 메시지 처리
    	val error: String = response.errorBody()?.string() ?: "Unknown error"
	}
}
  • 오류 처리: 네트워크 오류/데이터 변화 오류 등에서 콜백 호출됨
override fun onFailure(call: Call<User>, t: Throwable) {
    // 오류 메시지 표시
    Log.e("API_ERROR", t.message ?: "Unknown error")
}
profile
Be able to be vulnerable, in search of truth

0개의 댓글