Retrofit2 사용해보기

2020. 8. 19. 15:20android

레트로핏은 서버와 클라이언트 간의 데이터를 주고 받기 위해서 사용되는 하나의 도구라고 생각하면 쉽습니다. 그런데

레트로핏하면 항상 따라다니는 용어가 있죠. 바로 RESTful API입니다. 혹은 REST API에 대해서도 많이 들어 봤을 것입니다. 그럼 레트로핏은 RESTful API, REST API 둘 중 어느 표현이 더 정확한 표현일까요? 이를 알기 위해서 먼저 REST API부터 차근차근 알아보도록 하겠습니다.

 

1. REST API란?


REST API는 기본적으로 웹 상에서 자원을 주고 받기 위해 HTTP 아키텍쳐를 사용하는 것을 의미합니다. 조금 더 풀어서 설명하면 HTTP Method인 GET, POST, PUT, DELETE 등을 이용하여 자원을 처리하는 것입니다. 이러한 기준을 따라서 만든 서비스 디자인이 바로 레트로핏이고, REST API의 기본 원칙을 잘 지켜서 만든 것을 "RESTful"하다라고 말합니다.

 

만약에 POST만 이용하여 서비스를 한다면 이는 RESTful하지 못한 서비스 디자인인 것이죠! 한마디로 용도에 맞게 적절하게 사용하여 만든 서비스 디자인을 RESTful하다라고 하는 것입니다. 

 

그럼 여기서 말하는 기본 원칙이 무엇일까요?

 

1. REST의 기본원칙 2가지


1) URI는 반드시 자원을 의미해야 한다.

 

2) HTTP Method인 GET, POST, PUT, DELETE를 사용해서 자원을 주고 받아야한다. 

 

물론 이 밖의 중요한 사항들이 많지만 가장 핵심이 되는 내용은 바로 두가지 정도로 추릴 수 있습니다. 먼저 자원을 의미하는 것은 여러가지가 될 수 있는데 대표적으로 이미지나 데이터 정도를 의미합니다. 그리고 이 자원들을 주고 받을 수 있도록 해주는 것이 바로 http method 인 것이구요. 

 

지금 정리한 개념들은 자원을 주고받기 위한 방법입니다. 그러면 내부적으로는 어떠한 방식으로 돌아가는지 간단하게 정리해보고 실제 코드를 구현해 보겠습니다.

 

2. MIME(Multipurpose Internet Mail Extension)


MIME이 바로 내부적으로 자원을 보내는데 사용되는 기술입니다. MIME은 기본적으로 파일을 텍스트 문자로 변환하여 이메일 시스템을 통해 데이터를 보내는 방식입니다. 하지만 이 기술은 현재 메일을 보내는 곳에서만 사용되는 것이 아닌 웹 상에서 정보를 주고 받는 등의 다용도로 사용되고 있습니다. 그래서 앞에 다용도라고 명시하고 있습니다! 

(MIME에 대해서 이렇게 소개하는 이유는 retrofit이 REST API를 기반으로 하고 있기 때문이고 REST API는 HTTP 아키텍처를 기반으로 하기 때문에 결국 웹 상의 정보를 공유하는 방식을 채택하고 있는 것입니다. 때문에 MIME을 간략하게 소개하고 코드 예제로 넘어가겠습니다.)

 

웹에서는 파일의 확장자 명이 크게 중요하지 않습니다. 때문에 클라이언트가 요청한 작업이 어떤 것이고 데이터의 타입이 어떤 것인지를 확실하게 하는 것이 중요합니다. 이 때문에 MIME은 클라이언트에게 전송될 수 있는 문서의 다양성을 제공하면서, 각 문서에 맞게 알맞는 타입을 지정하도록 서버가 정확하게 설정하는 것이 중요합니다. 

 

쉽게 말해서 서버가 문서에 알맞게 행동하기 위해서 방침을 다 정해주고 이를 클라이언트가 방침에 따라 데이터의 타입을 지정하여 전송하게 끔 만드는 것입니다. 그러면 서버가 설정한대로 작동하게 되겠죠. 바로 이것이 중요하다는 것입니다.  

 

3. MIME 문법


MIME은 "/"를 기준으로 type과 subtype을 나누는 단순한 구조를 띄고있습니다. type은 카테고리를 의미하게 되며 개별적인 것 혹은 멀티파트로 나뉩니다. subtype는 카테고리 내에서 존재하는 type들을 의미합니다. 

 

- 개별타입

개별 타입은 말그대로 데이터 하나를 전송할 때를 의미합니다. subtype은 그 하나의 데이터가 어떠한 타입인지 명시하기 위한 타입이구요. 예를들면 제가 이미지 한개를 서버로 보낼 것이라면 개별 type과 subtype을 image/png나 image/jpeg로 해야겠죠? 말 그대로 Text라면 text/plain으로 타입을 지정하여 서버가 만들어 놓은 문서에 맞는 api로 전송하면 되는 것입니다. 

 

 

- 멀티파트 타입

멀티파트 타입은 MIME의 개별 타입들을 묶어 놓은 것이라고 생각하면 됩니다. 때문에 각각의 개별 타입들은 하나의 객체이므로 별도의 http 헤더를 갖고 있습니다. 또한 각 헤더를 구분선(--)으로 구분하기 때문에 html form을 보면 아래 사진처럼 구분선으로 구분되어지는 것을 볼 수 있습니다.

 

 

위의 사진은 레트로핏을 이용하여 회원가입을 위해 클라이언트가 서버에게 정보를 보내는 것 중 일부를 발췌한 것입니다. 이처럼 서버에게 전송하는 값이 개별적인 값만 존재할 수 없음으로 멀티파트 타입을 이용하여 이러한 일 처리를 할 수 있습니다. 또한 빨간색으로 밑 줄 친 부분을 보면 서버에서 지정한 대로 name은 text/plain 타입으로 전송하고 있습니다. 만약 제 이름을 다른 타입으로 지정해버리면 만들어 놓은 문서에 맞는 행동을 할 수 없음으로 일처리가 제대로 되지 않을 것입니다.

 

ps)

그리고 레트로핏에서 html-form과 POST 관계 속에서 multipart/form-data가 정의 되기 때문에 반드시 HTTP Method로 POST를 사용하여 API를 만들어야 하는 것이 중요합니다.

 

 

4. HTTP Header


HTTP Header는 클라이언트와 서버 간의 요청과 응답에 대한 부가적인 정보들을 포함하고 있습니다. 위에서 MIME이 타입이 무엇인지 확인하고 일처리를 할 수 있는 것도 전부 HTTP Header에 포함되어져 있는 정보들을 갖고 있기 때문입니다. HTTP Header가 갖고 있는 정보들은 정말 많지만 그 중 위의 사진에서 보이는 content-disposition, content-type, content-length 등 몇 가지만 조금 더 자세하게 알아보겠습니다. 

 

 

1) content-disposition

http header에 포함된 content-disposition은 컨텐츠가 브라우저에 inline 되어야 하는 웹페이지 자체이거나, 웹페이지의 일부인지, 아니면 attachment로써 다운로드 되거나 로컬에 저장될 용도록 쓰이는 것인지를 알려주는 헤더입니다.

multiPart/form-data에서 content-disposition은 multipart의 하위파트에서 사용되어 집니다. 그렇기 때문에 이 헤더는 multipart 본문 내에 존재하는 필드들에 대한 정보를 갖게 됩니다. 추가적으로 content-disposition의 파라미터 중에서 기form-data, name, filename만이 html form과 post 요청에 응답할 수 있습니다. 

 

다시 말해서 multipart/form-data는 개별 타입마다 http header와 html form을 각각 갖는다고 했습니다. 그렇기 때문에 content-disposition은 개별 타입에 대한 정보를 내포하고 있는 헤더가 되는 것입니다. 그리고 HTTP Method로 POST를 사용했기 때문에 content-disposition의 파라미터가 form-data 와 name으로 이뤄져 있는 것이구요!

 

2) content-type

content-type은 말그대로 개별 타입이 무엇인지 알려주는 것입니다. 위의 예제에서 처럼 multiPart/form-data에서는 각각의 개별 타입을 갖는다고 했으니 개별타입의 개수만큼 content-type도 구분되어져서 명시되어질 것입니다. 

 

3) content-length

content-length는 인코딩 하는 value에 대한 byte의 갯수를 표시해줍니다. UTF-8 인코딩을 이용했을 때 한글은 하나의 문자당 3byte를 차지하기 때문에 이름이 3글자인 저는 length가 9로 위의 사진에서 표시되어지고 있습니다. 

 

 

5. 정리


레트로핏을 공부하려다가 REST API부터 MIME, HTTP Header까지 다양한 개념을 언급했기 때문에 많이 혼란스러울 수 있습니다. 그래서 간단하게 정리하고서 본격적으로 예제를 통해서 레트로핏을 사용하는 방법을 알아보도록 하겠습니다. 

 

레트로핏은 REST API를 바탕으로 서버와 클라이언트간에 자원을 주고받을 수 있는 라이브러리입니다. 자원을 주고받기 위해서는 자원을 주고받을 수 있는 기술이 필요했기 때문에 MIME을 사용하게 됩니다.

파일을 모두 텍스트 문자로 바꾸기 때문에 파일 형식이 크게 상관없는 MIME에서는 서버 측에서 예상된 행동을 하기 위한 엄격한 문서를 만들어야하고 이 때문에 클라이언트 측에서는 보내고자 하는 자료가 크게는 개별 타입인지, 혹은 멀티타입인지 따져야하며 subtype으로는 문자인지 이미지인지 등을 확실하게 정해줘야 합니다. 그래야 서버에서 약속된 행동을 통해서 정보를 처리할 수 있기 때문입니다.

이렇게 만들어진 infomation들은 HTTP Header에 포함되어져서 전송되어지기 때문에 서버 측에서는 HTTP Header에 들어가 있는 정보를 가지고서 약속된 행동을 행하게 되므로 서버와 클라이언트간의 정보를 주고 받을 수 있게 됩니다.  

 

 

6. 예제


1) 의존성 추가

 

위에서 언급한 방식대로 작동하려면 다양한 라이브러리가 필요합니다. 그 중에서 okhttp 라이브러리는 http 통신을 조금 더 간편하게 만들어 주는 라이브러리 입니다. 또 convert- gson은 받은 데이터를 gson으로 받기위해서 사용됩니다. 

 

 

2) 회원가입 api 

 

먼저 저는 제가 사용하고 있는 회원가입 api를 postman을 통해서 어떠한 인자 값이 필요하고 어떤 값을 주는지에 대해서 알아보겠습니다.

첫번째 사진에서는 기존에 만들어 놓은 api(문서)입니다.  두번 째 사진은 key값에 따르는 json 결과 값이구요! 한마디로 클라이언트에서 key값을 모두 적어서 보내면 json 값으로 응답을 주는 것입니다. 저는 크게 status와 msg 두개의 값을 받도록 되어져 있습니다. 

 

3) API interface 

 

postman을 통해서 api를 알아봤습니다. 이제 이 api를 이용하기 위해서 별도의 인터페이스를 만들도록 하겠습니다. 

api 구성은 안드로이드 스튜디오에서 어노테이션을 이용하여 multipart와 Http method인 POST를 지정해 줬습니다. multiPart로 지정한 이유는 하나 이상의 개별 객체 값을 한번에 묶어 보내기 위함입니다. 따라서 multipart/form-data로 전송하게 될 테니까 반드시 POST를 이용해야 하기 때문에 HTTP Method로 POST를 이용하는 것입니다.

 

위의 코드에서는 몇가지 집고 넘어가야 할 것이 있습니다. 첫번 째는 MyStatus class는 무엇인지와 두번 째는 POST의 address 표기 방법입니다. 

 

먼저 MyStatus class는 pojo class라고 부릅니다. 위에 포스트맨에서 넘겨받는 값을 json이라 하였고 이를 사용하기 위해서 여러가지 parsing 작업을 거쳐야합니다. 하지만 json 값을 자동으로 parsing하여 넘겨 받을 수 있도록 class를 만들어서 사용한다면 자동으로 parsing되어져서 class에서 선언한 필드 값으로 json값이 알아서 parsing됩니다. 

 

postman에서 결과 값으로 넘겨주는 json 값의 name값을 필드로 하여 value들이 자동으로 parsing하게 되기 때문에 편하게 이용할 수 있습니다. 

 

두번 째는 포스트맨에서는 http:// ip address :port/account/signup 이렇게 사용되는데 인터페이스에서 POST는 주소를 /account/signup만 선언하고 ipadress:port 이 부분에 대한 언급이 없습니다. 이는 아래에서 조금씩 설명해보겠습니다.   

 

4. 통신을 위한 builder 생성

 

만들어 놓은 api interface를 이용하기 위해서 별도의 생성 클래스를 통해 접근하는 방식으로 해보겠습니다. 여기서 선언된 상수 URL은 서버의 주소입니다. 아래 코드에서 설정한 것은 임의의 값이므로 반드시 서버의 ip address로 설정하세요.

 

먼저 provideOkHttpClient 메서드는 서버와 연결되고 읽고 쓰는 것에 대한 타입아웃을 설정합니다. 또한 interpretor를 이용하면 실제로 서버에게 어떤 데이터가 넘어갔는지 뿐만아니라, 서버에서 처리된 결과 값에 대해서도 log로 뿌려줄 수 있기 때문에 반드시 사용하는 것이 도움이 됩니다. 

 

두번 째는 interpretor입니다. 위에서 말했듯이 어떤 데이터가 넘어가는지, 결과가 무엇인지에 대해서 log로 알려줍니다. 또한 어느 부분에 대한 정보를 log로 남길지를 정해주면 됩니다. 저는 BODY로 했습니다. 이 뿐만아니라 HEAD로 설정하면 header에 대한 정보를 log로 받을 수 있습니다. (참고로 MIME 설명에서 사용했던 그림에서 보여지는 로그들이 바로 interpretor가 주는 log입니다.)

 

세번 째는 바로 Retrofit 객체를 생성하는 것입니다. 여기서 기본적으로 baseURL을 설정하기 때문에 api에서 별도로 URL을 설정하지 않아도 자동으로 URL을 붙여서 form을 전송하게 됩니다. 따라서 signUp api에서 사용한 POST가 왜 ip address를 붙이지 않았는지 설명이 될 수 있습니다.

 

또한 ConverterFactory를 통해서 json 데이터를 자바의 객체로 역직렬화 하도록 만들어줍니다. 덕분에 pojo class를 만들면 json 데이터를 자동으로 pojo class의 필드 값에 parsing 되어집니다. 

 

 

5.  Retrofit 사용하기 

 

레트로핏을 이용하기 전에 먼저 웹에서는 어떻게 통신이 이루어지는지 살피고 가겠습니다. 웹에서는 multipart를 이용하기 위해서 아래의 사진 구조처럼 사용하여 form을 전송하는 방법을 택하고 있습니다. 이와 비슷하게 레트로핏에서도 multipart를 구성하게 됩니다. (추가적으로 서버와 통신하는 부분은 지연이 발생할 수 있기 때문에 비동기 테스크 처리하는 것이 좋습니다. 때문에 RxJava2를 이용하여 비동기 통신을 하는 것이 좋지만 부가적인 설명을 많이 해야하기 때문에 AsyncTask를 이용하여 서버와 통신하는 방법을 사용하겠습니다.)

 

hashMap을 사용하여 웹에서 통신하는 방법과 유사하게 만들 수 있습니다. 별도의 객체마다 타입을 지정하는 것과 데이터를 입력하고 이를 api를 통해서 서버로 전송하게 됩니다. 참고로 null 체크를 하는 이유는 api에서 요구하는 input값은 nonnull이기 때문에 모든 값에 대해서 null 체크 후, 데이터를 넣어서 전송하게 됩니다. 

 

마지막으로 onPostExcute 메서드를 통해서 서버로부터 받은 Json 값을 자동 파싱해서 분기를 하여 적절하게 핸들링할 수 있습니다 .

'android' 카테고리의 다른 글

room과 Rxjava2 같이 사용하기  (7) 2020.08.21
ViewModel 사용하기  (4) 2020.08.19
DataBinding 사용해보기  (2) 2020.08.17
Filterable로 자동검색 만들기  (0) 2020.08.17
TabLayout과 viewPager2 연결하기  (0) 2020.08.12