[안드로이드]Retrofit

dada·2021년 9월 29일
3

Android

목록 보기
11/24
post-thumbnail

REST는 서버에 존재하는 데이터에 접근하기 위한 규칙의 모음으로 행위,자원,표현으로 이루어져 있다
이러한 REST원칙을 잘 준수하는 APIRESTful API라고 한다.
REST API는 HTTP를 설계할 때 지켜야 할 아키텍처(= 청사진,가이드, 규칙, 지침서)인데 OkHttpRetrofit은 이걸 간편하게 구현할 수 있도록 해준다. REST API를 쉽게 사용하기 위한 도구쯤으로 생각하면 될 것 같다.
Okhttp는 REST API, HTTP 통신을 간편하게 구현할 수 있도록 다양한 기능을 제공해주는 라이브러리이다. Retrofit이라는 라이브러리의 베이스가 된다.
그리고 Retrofit은 OkHttp를 기반으로 만들어진 친구이다.


유용한 정보 링크 참고하세욥
github소셜로그인참고
Retrofit쉽게 정리한 블로그/배민REST API써보기
retrofit간결 소개
ResponseWrapper
OkHttp사용해서 Header 빼기


✅Retrofit 이란?

  • Retrofit은 앱 개발 시 서버통신에 사용되는 HTTP API를 자바, 코틀린의 인터페이스 형태로 변환해 안드로이드 개발시 API를 쉽게 호출할 수 있도록 지원하는 라이브러리
  • 예를 들어 https://~~/user/{user}/repos 라는 HTTP API가 존재한다고 가정했을때 Retrofit 라이브러리를 사용하면 HTTP API를 아래 코드와 같은 인터페이스 형태로 변환할 수 있음
interface GitHubService {
    @GET("users/{user}/repos")
    fun getRepoList(@Path("user") user: String): Call<List<Repo>>
}

✅Retrofit을 사용해 서버와 통신하기 전, 개발자가 준비할 단계

  1. 안드로이드 프로젝트에 Internet 퍼미션 추가하기
  2. Retrofit 라이브러리(필요하다면 Gson도)를 프로젝트에 추가하기
  3. 서버 개발자와 필요한 데이터에 대해서 논의하는 시간 갖기(POST MAN)
  4. 서버 개발자가 만들어준 API 문서 확인하기(POST MAN)

1.안드로이드 프로젝트에 Internet 퍼미션 추가하기

  • 서버와의 통신은 인터넷을 거쳐야하기 때문에 안드로이드 Manifest파일에 internet 퍼미션 추가해줘야 함
    <uses-permission android:name="android.permission.INTERNET" />

2.Retrofit 라이브러리(필요하다면 Gson도)를 프로젝트에 추가하기


  • Retrofit라이브러리를 사용하기 위해 프로젝트의 app단위 build.gradle파일에 Retrofit 라이브러리 종속성 추가
  • Retrofit공식 문서 의 다운로드 부분을 보면 종속성 추가 정보를 찾을 수 있고,
    compliment는 옛날 버전이라 implement로 바꿔서 추가해줬고, 최신버전이 몇인지는 GitHub에서 확인 가능
  • 결론적으로 해당 포스트 작성 시점엔 하단 코드 종속성 추가함
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
  • retrofit2는 HTTP API를 자바 인터페이스 형태로 사용하는 라이브러리이고 응답 데이터가 json형식이라서 java<->gson파싱을 위한 Gson라이브러리와 Gson컨버터 라이브러리를 추가해야함
    implementation 'com.google.code.gson:gson:2.8.9' //Gson라이브러리 추가
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0' //Gson 컨버터 라이브러리 추가
    • - Gson라이브러리 추가: 위의 응답 데이터 형식은 JSON형식이기 때문에 Retrofit에서 Call<>객체 안에서 응답될 Body타입의 data클래스인 것과 호환되지 않음
    • 즉, 서버에서 응답받는 데이터는 json형식의 데이터인데 클라이언트에서 응답받을 데이터 타입은 data클래스 객체로 주고 받으려 하니 호환 안됨
    • 따라서 서버에서 응답되는 json포맷의 데이터를 data클래스 객체로 파싱해주어야함 이는 클라이언트에서 서버에 데이터를 보낼때도 data클래스 객체를 json형식으로 보내야 서버가 받아볼 수 있음
    • data 클래스<->json 간의 파싱을 위해 Retrofit라이브러리에 Gson이라는 Java라이브러리를 추가로 묶어서 사용하면 됨.(Gson은 json문자열과 java객체를 서로 변환해주는 라이브러리)
    • Gson은 Java 라이브러리이기 때문에 Gson만 단독으로도 사용 가능. 단독으로 사용하려면 Gson Github 에서 Gson 종속성 추가 방법을 알아보면 됨.

3. 서버 개발자와 필요한 데이터에 대해서 논의하는 시간 갖기

  • UI를 보면서 화면을 구현하기 위해 필요한 정보들을 논의후, 이러한 정보들을 서버에 요청해 받아와야 함
  • 해당 포스팅에선 github의 repository에 관한 데이터를 받아와야 한다고 가정해보자.

4. 서버 개발자가 만들어 준 API 문서 확인하기

  • 일반적으로 서버 개발자와 협업시, 서버 개발자가 만들어준 Rest API문서를 통해 통신할테지만 현재 포스터에서는 Github에서 제공하는 REST API문서 사용.
  • 자체 REST API가 있는 경우는 여기를 참고

  • RESTful 통신에 대한 사전지식이 있어야 Github의 REST API문서 사용 가능

    • REST는 Representational State Transfer 의 약자로, 서버에 존재하는 데이터에 접근하기 위한 규칙들의 모음임
      • 어떤 자원을 식별자(이름)으로 구분해, 자원의 정보를 주고받는 것
      • 프로토콜이나 표준이 아닌 아키텍처 원칙 세트
      • REST는 3가지의 구성요소로 이루어져 있는데 1.행위(HTTP Method)+2.자원(URL) =3.표현(GET https://~)
      • 이러한 REST원칙을 잘 준수하는 API를Restful API라고 함
    • RESTful하게 짜여진 API는 URL로 서버에 있는 데이터나 서버의 행위를 식별할 수 있고, GET,POST,PUT,DELETE 이렇게 4가지 행위를 할 수 있음(REST의 구성요소중 하나였던 '행위')
      • GET:데이터를 서버에서 얻는 행위(조희)
      • POST:데이터를 서버에 제출하는 행위(저장)
      • PUT:서버의 데이터를 변경하는 행위(정보수정/갱신)
      • DELETE:서버의 데이터를 삭제하는 행위
    • 이 4가지 행위로 기본적인 데이터 처리 기능인 CRUD를 할 수 있음
    • Github REST API문서의 Current Version 문서를 보면 Github REST API의 최신 버전을 확인할 수 있는데, http://api.github.com 이라는 서버로 들어오는 모든 요청은 v3버전의 API가 응답한다는 의미이며 이 말을 통해, BASE URLhttps://api.github.com 라는 것을 알 수 있음
    • BASE URL이란 데이터를 요청할 서버의 이름을 말함. 이 BASE URL 뒤에 붙는 것이 어떤 것인지에 따라 이 서버에 어떤 서비스를 요청하는 API인지 정해지게됨
    • 데이터를 요청할 자체 서버가 있으면 해당 BASE URL에게 데이터 요청하면 되겠죠?

  • 그렇다면 우리가 필요한 데이터(repository)를 Github에서 제공하는 REST API를 통해 얻기 위해서는 어떤 API를 요청해야 할까?(API마다 제공하는 서비스가 다르니까)

    • Repository에 관한 데이터를 제공하는 API문서를 보면 우리가 필요한 데이터를 얻기 요청을 하는 API에 대해 알 수 있음
    • 해당 API문서는 특정 계정의 Repository 리스트를 얻을 수 있는 API에 대한 문서임
    • 일단, URL앞에 GET이 붙어있으므로 해당 API는 서버에 있는 데이터를 얻을 수 있는 API
    • GET 뒤에 /org/{org}/repos 부분은 BASE URL 뒤에 붙는 식별URL 부분임. 특정 계정 {org}의 레포지토리 리스트 repos를 얻을 수 있는 API라는 것을 알 수 있음
    • 또한 Parameters 부분을 보면 여러 파라미터들을 적절한 위치에 사용하여 얻고자 하는 데이터를 구체적으로 얻을 수 있음을 알 수 있음

  • 각 행위를하는 메서드들은 요청헤더/요청바디/요청파라미터(요청쿼리)/응답바디 가 있을 수 있음 따라서 POST MAN에서 테스트 해볼때는 각 메서드에 필요한 헤더,요청쿼리,요청바디를 넣어서 테스트해야함

    • GET:(요청헤더)+요청쿼리+응답바디
    • POST:(요청헤더)+요청바디+응답바디
    • PUT:(요청헤더)+요청바디+응답바디
    • DELETE:(요청헤더)+요청쿼리+응답바디
  • POST MAN에서 테스트 해볼때는 식별URL 중 동적으로 변하는 부분은 직접 이메일이나 아이디를 넣어서 테스트해보면 됨

  • Header에 권한 토큰이 필요하다면 토큰 줄바꿈해서 쓰는거 주의!

✅Retrofit을 사용해보자

  • 서버에서 작성한 REST API문서를 확인했으니 이 문서를 토대로 Retorofit을 이용해 서버와 통신할 수 있음 (해당 포스팅에서는 서버가 작성한 API문서=github REST API문서)
    *Retrofit은 HTTP API를 인터페이스 형태로 변환해서 쉽게 호출할 수 있도록 하는 라이브러리라고 했었다~!~!

Retrofit 사용법은 아래 3가지 단계로 진행
1. Retrofit Interface를 생성하고 Interface 안에 Request메서드 작성
2. Retrofit Interface의 실제 구현제 만들기
3. Request 메서드 호출해서 통신 처리하기

1. Retrofit Interface를 생성하고 Interface 안에 Request메서드 작성

✔Retrofit 인터페이스를 만들자

  • 위 사진처럼 이름이 GithubService라는 인터페이스를 하나 만듦

  • 해당 인터페이스 안에서 Request메소드를 작성해주어야함 Reauest메소드란 위 사진의 노란색 부분처럼 무언가를 요청하는 메소드를 말하는데, 이전에 Repository API에서 보았던 URL이나 parameters들을 메소드 형태로 작성한 것이라고 생각하면 됨. 즉 하나의 Request메소드가 하나의 API를 대신하게 되는것
  • 만약, 호출해야 하는 API의 종류가 많다면, 여러개의 Request 메소드를 인터페이스 안에 작성하면 됨.
  • Request메소드를 작성할 땐 Retrofit에서 제공하는 어노테이션을 사용할 수 있음, 이런 어노테이션은 Request메소드에 작성할 파라미터들을 대신할 수도 있고, 쿼리 파라미터를 지원하는 용도로 사용될 수도 있음.
  • 또한 Mutipart Request의 바디와 파일 업로드에 대해서도 어노테이션을 사용할 수 있음

✔Request메소드의 HTTP어노테이션 작성

  • 특정 깃허브 계정의 Repository 리스트를 얻을 수 있는 API 문서를 보고 이 API를 대신하는 Request메소드를 작성해볼 것임.
  • 일단, 모든 Request 메소드는 HTTP어노테이션을 작성함으로써 시작됨. 즉, HTTP어노테이션을 먼저 작성한 후 그 밑에 API에 관한 Request 메소드를 작성하면 됨
    • 어노테이션 예시: @GET("users/list")
    • 예시의 어노테이션은 GET을 표현한 어노테이션이지만 이외에 사용할 수 있는 HTTP어노테이션에는 HTTP,GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD 로 총 8개
    • @GET("users/list?sort=desc") 처럼 URL에 특정 쿼리 파라미터를 넣어주는 것도 가능함.
  • 우리가 얻고자 하는 API문서에 나와있는 URL대로 HTTP어노테이션을 작성하면 위와 같은 사진처럼 작성하면 됨.

✔Request메소드 작성

  • HTTP어노테이션 아래에 Request메소드를 작성할 것.이때, Request메소드의 매개변수에는

    • 1. API URL에서 동적으로 변경되어야 할 값들을 넣어줌

      • HTTP 어노테이션에서 작성한 URL부분인 /orgs/{org}/repos에서 {org}부분은 계정 이름이기때문에 동적으로 바뀌는 부분임. 즉, 계정이름에 따라 가져올 데이터가 다르므로 {org}로 되어있는 것.
      • 이렇게 동적으로 바뀔 수 있는 부분에 대해 매개변수를 작성할때는 @Path("동적으로 변하는 부분의 URL 이름") 이라는 Retrofit 어노테이션을 변수 앞에 붙여줘야함.
    • 2. 리턴타입에는 Call<T> 인터페이스를 적어줘야함

      • Retrofit의 Request 메소드가 호출된 후 리턴하는 타입은 Retrofit이 제공하는 Call<T>라는 타입임
      • Call<T> 는 여러 메소드를 정의하고 있는 인터페이스로, T는 성공적으로 응답될 response body type의 클래스이고, 안드로이드에서 response body type의 클래스를 data class로 사용하고 있음. 즉, <>안에는 성공적으로 응답될 Body타입의 data class를 넣어주면 됨
      • 정리해보자면, Request메소드가 호출된 후 반환값으로 Call타입의 인터페 이스를 반환하는데, T는 성공적으로 응답될 Body타입의 date class를 작성해서 넣어주면 된다.
  • 주의

  interface SampleService {
    @Headers("Content-Type:application/json") //Header에서 정의되는 부분
    @POST("user/login") //HTTP메소드 + end point URL
    fun postLogin(
        @Body body:RequestLoginData  //POST는 응답바디가 있기때문에 @Body어노테이션을 통해 요청바디를 정의
    ):Call<Data> //요청바디로 부터 받아온 데이터인 응답바디를 callback 타입으로 정의
}

interface GithubService {
    @GET("/orgs/{org}/repos")
    fun getRepoList(
        @Path("org") username: String
    ):Call<GitRepositoryResponseData>
}

✔Body타입의 date class 작성

- data class작성


  • data class를 생성한 후, data class의 인자로 API 문서에서 확인할 수 있었던 응답시 받을 수 있는 모든 데이터들의 Key값을 변수화하여 작성해주면 된다. 단, 변수화할 때 변수 이름은 반드시 Key 와 동일한 이름
  • 필요한 Key 값들만 변수화해서 작성하면 됨
  • @Serializedname 어노테이션: data class를 작성할 때 각각의 변수마다 어노테이션을 붙이는게 보통인데 그 이유는, SerializedName이라는 이름 그대로 data class 객체가 json 포맷 데이터로 변환된 이름이라는 걸 명시하는 역할임
    • 즉, data class에서 사용할 이름은 repoName이지만 이 변수가 서버와 통신하기 위해 json 포맷 데이터로 변환될 때는 name 이라는 이름으로 변환된다는 것을 명시한 것이다.
    • 이렇게 @SerializedName 어노테이션은 data class 변수 이름을 서버 쪽 json 포맷 데이터의 key 이름과 동일하게 사용하고 싶지 않을 때 작성해주면 이름을 다르게 사용할 수 있게 도와주는 녀석이다.

2. Retrofit Interface의 실제 구현체 만들기

✔object interface만들기

  • 인터페이스를 만들고 인터페이스 안에 필요한 API를 Request메소드(API호출을 대신하는 메소드)로 만들어 놓았지만, 인터페이스를 사용하기 위해선 인터페이스를 구현 해야함. 즉 Request메소드를 정의하고 있는 인터페이스를 사용할 곳에서 직접 구현해야 한다는 건데 앱 안에서 서버 호출이 필요한 곳은 정말 많음.
  • 서버 호출이 필요한 곳마다 해당 인터페이스를 구현해야한다는 것이고 인터페이스 구현 코드가 산발적으로 존재하게 될 것. 따라서 이 인터페이스를 한번만 구현해두고 필요한 곳에서 재구현 없이 사용할 수 있는 방법이 필요함.
    • 이 방법이 코틀린의 object임. object는 싱글톤이기 때문에 전역변수처럼 앱의 모든 곳에서 접근 가능
    • 즉, object로 인터페이스의 구현체를 만들어 놓으면 필요한 곳에서 재구현없이 전역공간에 있는 구현체를 가져다 사용 가능

  • 위 사진처럼 object파일을 생성함

  • 인터페이스의 구현체는 위와 같이 작성하면 된다고 Retofit 문서에 나와있음. 코틀린으로 바꿔서 작성하면 실제 구현체를 싱글톤으로 구현하는 것까지 완료된 것.
  • 앞에서 Gson라이브러리를 추가해서 data class<->json간의 파싱을 하겠다고 했으니, Retrofit의 Converter로 Gson라이브러리를 사용하겠다는 설정을 해당 구현체에서 해주어야함.
  • 이제 인터페이스에 작성해높은 Request메소드들을 서버 호출이 필요한곳에서 호출할 준비 끗!
  package com.example.kotlin_study.Server

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object ServiceCreator {
    private val BASE_URL = "베이스"

    private val retrofit: Retrofit = Retrofit.Builder() //레트로핏 빌더 생성(생성자 호출)
        .baseUrl(BASE_URL) //빌더 객체의 baseURL호출=서버의 메인 URL정달
        .addConverterFactory(GsonConverterFactory.create()) //gson컨버터 연동
        .build() //객체반환

    val sampleService: SampleService = retrofit.create(SampleService::class.java)  //SampleService 인터페이스를 구현한 구현체
}

✅Activity에서 서버 호출

//인터페이스
  interface SampleService {
      @Headers("Content-Type:application/json") //Header에서 정의되는 부분
      @POST(	"user/login") //HTTP메소드 + end point URL
      fun postLogin(
          @Body body:RequestLoginData  //POST는 응답바디가 있기때문에 @Body어노테이션을 통해 요청바디를 정의
      ):Call<Data> //요청바디로 부터 받아온 데이터인 응답바디를 callback 타입으로 정의
  }
//인터페이스 구현한 싱글톤
  object ServiceCreator {
      private val BASE_URL = "https://asia-northeast3-we-sopt-29.cloudfunctions.net/api/"

      private val retrofit: Retrofit = Retrofit.Builder() //레트로핏 빌더 생성(생성자 호출)
          .baseUrl(BASE_URL) //빌더 객체의 baseURL호출=서버의 메인 URL정달
          .addConverterFactory(GsonConverterFactory.create()) //gson컨버터 연동
          .build() //객체반환

      val sampleService: com.example.kotlin_study.Server.SampleService = retrofit.create(com.example.kotlin_study.Server.SampleService::class.java)  //SampleService 인터페이스를 구현한 구현체
  }

  private fun initNetwork() {
      val requestLoginData = RequestLoginData(
          email = binding.etId.text.toString(),
          password = binding.etPassword.text.toString()
      )

      val call: Call<Data> = ServiceCreator.sampleService.postLogin(requestLoginData)

      call.enqueue(object : Callback<Data> {
          override fun onResponse(
              call: Call<Data>,
              response: Response<Data>
          ) {
              if (response.isSuccessful) {
                  Toast.makeText(this@ServerActivity,"성공",Toast.LENGTH_SHORT).show()
                  val data = response.body()?.email
                  Toast.makeText(this@ServerActivity, "${data}님 반갑습니다!", Toast.LENGTH_SHORT).show()
              } else {
                  Toast.makeText(this@ServerActivity, "로그인 실패", Toast.LENGTH_SHORT).show()
              }
          }

          override fun onFailure(call: Call<Data>, t: Throwable) {
              Toast.makeText(this@ServerActivity, "로그인 실패", Toast.LENGTH_SHORT).show()
          }
      })

  }
}
  • 이렇게 Activity에서 api통신을 할 수 있다! 글이 정~~말 길었는데,, 여기까지 읽고 따라해보셨다면 우리 Retrofit 어느정도 이해하고 사용해봤다고 할 수 있다!!ㅎㅎ 앞으로 공부할게 많이 남았지만 지금처럼 차근히 공부하면 언젠가 api연동의 신이 될 수 있겠지?! 그날까지 화이팅!
profile
'왜?'라는 물음을 해결하며 마지막 개념까지 공부합니다✍

0개의 댓글

관련 채용 정보