[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'
}
interface ApiService {
@GET("users/{id}")
fun getUser(@Path("id") id: Int): Call<User>
}
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")
}