1.  레트로핏 데이터 통신 라이브러리

적은 양의 코드로 데이터를 통신 할 수 있게 도와줌. 안드로이드용 레트로핏 라이브러리는 인터페이스를 사용하기 때문에 처음에는 낯설수 있지만, 익숙해지면 네트워크와 관련된 코드가 단순해지고 관리도 쉬워짐. 

 

레트로핏의 공식 사이트
 

Retrofit

A type-safe HTTP client for Android and Java

square.github.io

 


1)  레트로핏을 위한 준비사항

  🚀  레트로핏을 사용하기 전에 두 가지 주의사항이 필요

  • 데이터를 가져올 곳 (웹 사이트 또는 API 서버) 결정
  • 어떤 (표준 프로토콜) 데이터를 사용할 것인지 데이터의 형식을 결정


①  사용자 정보 API를 무료로 제공하는 Githhub API


    ✓  서울시에서 제공하는 '서울 열린데이터광장'의 데이터나 날씨 정보를 제공하는 API 등을 사용할 수도 있지만 우선은 깃 허브에서 공개한 Githhub API를 사용. 깃허브는 개발자를 위해서 가입 없이 무료로 사용할 수 있는 API를 제공

Githhub API : https://developer.github.com/v3/

 

    ✓  예제에서는 Githhub API 중에서 사용자 정보를 검색하고 사용자 정보의 저장소를 보여주는 API를 사용

 

②  간단한 데이터 구조를 가진 JSON

 

    ✓  HTML은 구조가 복잡해서 짧은 시간에 분석하고 처리하기에는 거의 불가능한 수준의 프로토콜
    ✓  그런 이유로 현재 데이터 통신용으로 가장 많이 사용되고 있고 구조 또한 간단한 JSON Javascript Object Notation을 사용
    ✓  Githhub API는 JSON 형식으로 만들어진 데이터를 제공. JSON은 데이터 교환에 사용하는 표준 데이터 형식으로 사람과 컴퓨터가 이해하기 쉬우면서 데이터 용량이 적다는 장점이 있음
    ✓  네트워크 관점에서 JSON은 HTTP와 같은 데이터 프로토콜에서 바디 영역에 정의된 데이터 통신을 위한 개방형 규격

 


2)  JSON의 구조

  🚀  간단한 구조로 되어있지만, 각각의 형식이 의미하는 바를 알고 있어야 함. JSON은 크게 세 가지 형태의 조합으로 구성

  • JSON 오브젝트
  • JSON 데이터
  • JSON 배열


①  JSON 오브젝트


    ✓  JSON 객체는 여는 중괄호 '{'로 시작해 닫는 중괄호 '}'로 끝남

 

②  JSON 데이터


    ✓  JSON 오브젝트인 중괄호 '{}' 사이에 "데이터 이름": 값의 형식으로 표현되며 이름은 항상 쌍따옴표 ""로 감싸야 하고 이름과 값의 사이에는 콜론 ':'으로 구분. 데이터가 여러 개일 경우에는 쉼표 ','로 구분

{ "데이터 이름": "", "데이터2 이름": "값2" }

 

   ✓  데이터의 값은 문자, 숫자, 불린, null, JSON 객체, JSON 배열이 될 수 있는데 표현식은 조금씩 다름

데이터 형식 데이터 이름 : 값 표현 비고
문자 "데이터 이름": "값" 값을 쌍따옴표로 감싸야 함.
숫자 "데이터 이름": 123 값을 쌍따옴표로 사용하지 않음.
boolean "데이터 이름": true true, false를 값으로 사용하되 쌍따옴표를 사용하지 않음.
null "데이터 이름": null null 값을 사용할 수 있음
JSON 객체 "데이터 이름": { } 데이터의 값으로 JSON 오브젝트를 사용할 수 있음.
JSON 배열 "데이터 이름": [ ] 데이터의 값으로 JSON 배열을 사용할 수 있음.

 

 

③  JSON 배열


    ✓  JSON 배열은 JSON 오브젝트의 컬렉션으로 여는 대괄호로 시작해 닫는 대괄호로 끝남
    ✓  배열에 입력되는 JSON 오브젝트가 복수 개일 경우는 쉼표로 구분

[ {"데이터 이름": ""}, {"데이터1 이름": "값1"}, {"데이터2 이름": "값2"} ]

 


3)  깃허브 사용자 정보를 가져오는 앱 개발하기

깃허브에서 가져온 목록 데이터에는 이미지 정보인 아바타 주소가 포함되어 있음.  HttpURLConnection 을 직접 구현해서 서버에 있는 아바타 이미지를 화면에 보여줄 수도 있지만, 구현 난이도는 높은 반면에 효율성은 떨어지므로 라이브러리를 사용

이미지를 화면에 보여주기 위해서는 이미지 로딩 라이브러리를 사용할 수 있는데 이미지가 있는 URL 주소만 알려주면 해당 이미지가 있는 서버에 접속하여 이미지를 다운로드해서 이미지뷰에 보내는 편리한 도구.

현재 라이브러리 중에 많이 사용되고 있는 것으로는 Glide와 피카소가 있으며 여기서는 조금 더 많은 사용자 층을 가지고 있는 Glide를 사용.

 

Glide 홈페이지
https://bumptech.github.io/glide/
https://github.com/bumptech/glide

 


①  Retrofit과 Glide 설정하기

 

    ✓  build.gradle 파일을 열고 viewBinding 설정을 해줌

buildFeatures {
    viewBinding true
}

 

    ✓ dependencies에 레트로핏과 converter-gson 의존성을 추가

          ➡️  converter-gson은 레트로핏에서 JSON 데이터를 사용하기 위해서 사용하는 부가적인 라이브러리

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

 

    ✓  dependencies에 Glide 의존성을 추가

implementation 'com.github.bumptech.glide:glide:4.11.0'

 


②  권한 설정하고 데이터 클래스 정의하기


    ✓  인터넷 접근을 위해 AndroidManifest.xml에 인터넷 권한을 추가

<uses-permission android:name="android.permission.INTERNET"/>

 

    ✓  안드로이드 스튜디오는 앱 개발에 도움을 주는 다양한 플러그인을 지원. 그중에서 JSON TO Kotlin Class 플러그인은 JSON 형식으로 된 텍스트 데이터를 코틀린 클래스로 간단하게 변환해 줌

  • 안드로이드 스튜디오의 상단 메뉴에서 [FIle] - [Settings]를 클릭한 후 나오는 세팅 창에서 [Plugin]을 선택한 다음 'JSON To Kotlin Class' 플러그인을 검색하고 설치
  • 브라우저에서 https://api.github.com/users/Kotlin/repos 웹페이지를 열고, 이 페이지의 JSON 데이터를 전체선택 Ctrl + A, 복사 Ctrl + C 키를 연속으로 눌러 데이터를 복사
  • 다시 안드로이드 스튜디오에서 기본 패키지를 마우스 우클릭하고 [New] - [Kotlin data class File from JSON]을 클릭.
  • 새 창이 뜨면 복사한 JSON 데이터를 붙여넣고, [Class Name]에 'Repository'를 입력하고 [Generate] 버튼을 클릭하면 변환된 데이터 클래스를 자동으로 생성.
  • License, Owner, Repository, RepositoryItem 클래스가 생성. License, Owner 클래스는 JSON 데이터가 JSON 오브젝트를 값으로 사용하는 경우, 해당 데이터의 이름으로 클래스를 생성하고 사용

 


③  화면 만들기

activity_main.xml 파일을 편집
  • 깃허브의 데이터 API 주소를 요청할 버튼을 화면 상단에 배치
  • id 속성을 'buttonRequest', text 속성은 'GITHUB 사용자 가져오기'로 입력

  • 가져온 데이터의 목록을 보여줄 리사이클러뷰를 버튼 아래쪽 공간에 배치
  • id 속성 'recyclerView'라고 입력
  • 버튼과 리사이클러뷰의 컨스트레이트를 연결
리사이클러뷰 안에 넣을 아이템을 위한 새 파일을 생성
  • [app] - [res] - [layout] 디렉토리를 마우스 우 클릭하면 나오는 메뉴에서 [New] - [Layout Resource File]을 클릭
  • File name은 'item_recycler'로 생성
  • 레이아웃의 layout_height 속성은 '100dp' 정도로 설정
  • 이미지와 같이 이미지뷰 1개와 텍스트뷰 2개를 배치하고 id 속성을 입력
  • imageAvatar, textName, textId

 


④  리사이클러뷰 어탭터 만들기

  • 사용자 정보를 목록으로 보여주기 위해 리사이클러뷰 어댑터를 생성하고 사용
  • [app] - [java] 디렉토리 밑에 있는 기본 패키지에 CustomAdapter 클래스를 하나 생성
  • 생성된 클래스 파일을 열고 CustomAdapter 클래스 밑에 Holder 클래스를 추가
class CustomAdapter {
}

class Holder {
}

 

  • 홀더의 생성자에 바인딩을 전달하고 상속받은 ViewHolder()에는 binding.root를 전달
class Holder(val binding: ItemRecyclerBinding) : RecyclerView.ViewHolder(binding.root) {
}

 

  • CustomAdapter에 RecyclerView.Adapter를 상속하고 제네릭으로 Holder를 지정
class CustomAdapter: RecyclerView.Adapter<Holder>() {
}

 

  • 3개의 필수 메서드를 자동으로 생성. 함께 생성된 TODO() 행은 모두 삭제
class CustomAdapter: RecyclerView.Adapter<Holder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {

    }

    override fun onBindViewHolder(holder: Holder, position: Int) { 
    
    }

    override fun getItemCount(): Int {

    }
}

 

  • 자동 생성된 코드는 그대로 두고 어댑터 코드 블록 가장 위에 어댑터에서 사용할 데이터 컬렉션을 변수로 만들어 둠
  • 사용할 데이터셋은 앞에서 자동으로 생성해 두었던 Repository이고 nullable로 선언
class CustomAdapter: RecyclerView.Adapter<Holder>() {
    var userList: Repository? = null

 

  • 목록에 출력되는 총 아이템 개수를 정하는 getItemCount()를 구현
override fun getItemCount(): Int {
    return userList?.size?: 0
}

 

  • 홀더를 생성하는 onCreateViewHolder()를 구현. 레이아웃을 인플레이트한 뷰 바인딩에 담아서 반환
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
    val binding = ItemRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    return Holder(binding)
}

 

  • 실제 목록에 뿌려지는 아이템을 그려주는 onBindViewHolder()를 구현
  • 현 위치의 사용자 데이터를 userList에서 가져오고 아직 만들어지지 않은 홀더의 setUser()메서드에 넘겨줌
override fun onBindViewHolder(holder: Holder, position: Int) {
    val user = userList?.get((position))
    holder.setUser(user)
}

 

  • 다시 Holder 클래스로 돌아가서 setUser() 메서드를 구현
  • setUser() 메서드는 1개의 RepositoryItem을 파라미터로 사용
  • 클래스의 가장 윗줄에서 userList가 nullable이기 때문에 user 파라미터도 nullable로 설정되어야 함
class Holder(val binding: ItemRecyclerBinding) : ViewHolder(binding.root) {
    fun setUser(user: RepositoryItem?) {

    }
}

 

홀더가 가지고 있는 아이템 레이아웃에 데이터를 하나씩 세팅해주면 되는데 우리가 사용하는 데이터는 세 가지

변수 user: RepositoryItem에 있는 각각의 데이터 이름은 다음과 같음


  ✓  아바타 주소 : user.owner.user.owner.avatar_url
  ✓  사용자 이름 : user.name
  ✓  사용자 ID : user.node_id

  • 먼저 사용자 이름과 아이디를 세팅. 아바타는 Glide를 사용해서 이미지뷰에 세팅.
class Holder(val binding: ItemRecyclerBinding) : ViewHolder(binding.root) {
    fun setUser(user: RepositoryItem?) {
        user?.let {
            binding.textName.text = user.name
            binding.textId.text = user.node_id
            Glide.with(binding.imageAvatar).load(user.owner.avatar_url).into(binding.imageAvatar)
        }
    }
}

 

 


⑤  레트로핏 사용하기

 

레트로핏을 사용해서 데이터를 조회해서 가져오고 어댑터를 통해 목록에 출력.  레트로핏을 사용하기 위해서는 인터페이스가 정의되어 있어야 함

MainActivity.kt를 열고 onCreate() 메서드 위에 바인딩을 생성한 후 binding 프로퍼티에 저장하고
setContentView()에 binding.root를 입력
class MainActivity : AppCompatActivity() {
    private val binding by lazy {
        ActivityMainBinding.inflate(layoutInflater) 
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
    }
}

 

클래스 아래에 GithhubService 인터페이스 생성
레트로핏 인터페이스는 호출 방식, 주소, 데이터 등을 지정
Retrofit 라이브러리는 인터페이스를 해석해 HTTP 통신을 처리
class MainActivity : AppCompatActivity() {
    private val binding by lazy {
        ActivityMainBinding.inflate(layoutInflater) 
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
    }
}

interface GithubService {

}

 

인터페이스 안에 Github API를 호출할 users() 메서드를 만들고 @GET 어노테이션을 사용해서 요청 주소를 설정
요청 주소는 Github의 도메인을 제외하고 작성. 반환값은 Call<List<데이터 클래스>> 형태로 작성
레트로핏은 이렇게 만들어진 인터페이스에 지정된 방식으로 서버와 통신하고 데이터를 가져옴
interface GithubService {
    @GET("users/Kotlin/repos")
    fun users(): Call<Repository>
}

 

 

onCreate() 블록 안에서 recyclerView의 adapter에 앞에서 만들었던 CustomAdapter를 생성하고 recyclerView에 연결
val adapter = CustomAdapter()
binding.recyclerView.adapter = adapter

 

리니어 레이아웃 매니저 연결
binding.recyclerView.layoutManager = LinearLayoutManager(this)

 

Retrofit.Builder()를 사용해서 레트로핏을 생성하고 retrofit 변수에 담음


    ✓  baseUrl이 되는 Github의 도메인 주소와 JSON 데이터를 앞에서 생성한 Repository 클래스의 컬렉션으로 변환해주는 컨버터를 입력하고 build() 메서드를 호출해서 생성

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

 

 

요청 버튼을 클릭하면 앞에서 생성해둔 레트로핏을 이용해 데이터를 불러오고 어댑터에 세팅
buttonRequest에 클릭리스너를 연결
binding.buttonRequest.setOnClickListener {
}

 

레트로핏의 create() 메서드에 앞에서 정의한 인터페이스를 파라미터로 넘겨주면 실행 가능한 서비스 객체를 생성해서 반환
val githubService = retrofit.create(GithubService::class.java)

 

  ✓  githubService에는 GithubService 인터페이스를 이용해서 객체를 생성했기 때문에 실행(호출)가능한 상태의 users() 메서드를 가지고 있음
  ✓  레트로핏의 create() 메서드는 인터페이스를 실행 가능한 서비스 객체로 만들면서 users() 메서드 안에 비동기 통신으로 데이터를 가져오는 enqueue() 메서드를 추가. enqueue()가 호출되면 통신이 시작됨.

binding.buttonRequest.setOnClickListener {
    val githubService = retrofit.create(GithubService::class.java)
    githubService.users().enqueue()
}

 

  ✓  enqueue() 메서드를 호출한 후 Github API 서버로부터 응답을 받으면 enqueue() 안에 작성하는 콜백 인터페이스가 작동하게 됨
  ✓  enqueue()의 파라미터로 콜백 인터페이스를 구현

githubService.users().enqueue(object: Callback<Repository> {

})

 

콜백 인터페이스의 필수 메서드도 구현. 메서드 2개를 선택해서 자동 생성
선택한 메서드와 함께 자동 생성된 TODO() 행은 모두 지움
메서드의 이름에서 유추할 수 있듯이 통신이 성공적이면 onResponse()가 호출
override fun onFailure(call: Call<Repository>, t: Throwable) {

}

override fun onResponse(call: Call<Repository>, response: Response<Repository>) {

}

 

  ✓  onResponse() 메서드의 두 번째 파라미터인 response의 body() 메서드를 호출하면 서버로부터 전송된 데이터를 꺼낼 수 있음
  ✓  꺼낸 데이터를 List<Repository>로 형변환한 후에 어댑터의 userList에 담음
  ✓  마지막으로 어댑터의 notifyDataSetChanged()를 호출하면 리사이클러뷰에 변경된 사항이 반영

override fun onResponse(call: Call<Repository>, response: Response<Repository>) {
    adapter.userList = response.body() as Repository
    adapter.notifyDataSetChanged()
}

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts