상세 컨텐츠

본문 제목

[Spring] Android, Spring 연동 (retrofit2)

PROGRAMMING/Backend

by koharin 2022. 1. 24. 15:57

본문

728x90
반응형

Retrofit

  • Server와 Client 사이 http 통신을 위한 라이브러리
  • HttpClient, Volley, Okhttp 등 라이브러리가 있었지만 HttpClient, Volley는 deprecated되었고, Okhttp를 더 쉽게 사용하도록 한 것이 retrofit이다.
  • Retrofit은 Okhttp 기반으로 만들어서 Retrofit에서 Okhttp 메소드를 사용할 수 있다.

 

Android에서 retrofit을 이용해서 spring과 통신하는 테스트를 진행해본다.

 

Android Setting


build.gradle

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0
  • retrofit 라이브러리 추가
  • converter-gson: request/response 데이터 Json을 자바에서 사용 가능한 Gson 형태로 바꿔주는 라이브러리

 

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
<application
        ...
        android:usesCleartextTraffic="true">
  • android:usesCleartextTraffic="true” 안드로이드는 디폴트로 http 접근 허용하지 않기 때문에 http로 시작하는 사이트에 접근하기 위해 사용

 

User 객체 (User.kt)

package org.smu.blood.data

class User {
    var id: String? = null
        get(){ return field }
        set(value){ field = value }
    var password: String? = null
        get(){ return field }
        set(value){field = value}
}

 

Retrofit 통해 통신할 Interface (API.kt)

package org.smu.blood.api

import org.smu.blood.User
import retrofit2.Call
import retrofit2.http.*

public interface API {
    // login
    @POST("android")
    fun getLoginResponse(@Body user: User): Call<String>
    
}
  • API interface에는 어떤 파라미터와 함께 어떤 Method로 요청을 보내고, 어떤 형태로 응답을 받을 것인지 정의한다.
  • @GET : 서버에서 데이터를 받아올 때 사용
    • 서버의 데이터를 받아올 때 Access-Token이 필요하면 @Header에 Access-Token을 넣고 @Query 내용을 적어서 보낸다.
  • @POST : 서버에 데이터를 보낼 때 사용
    • @Header에 보내는 데이터가 json임을 명시하고 User 객체를 파라미터 타입으로 @Body annotation을 사용한다. @Body annotation 내용은 HTTP 요청 본문에 들어간다.
  • @GET, @POST 옆 url은 서버에서 정해진 요청으로 보낸다.
  • 위의 인터페이스는 간단하게 id, password 필드를 가지는 User 객체를 요청 파라미터로 보내므로 @Body annotation에 User 타입의 객체를 지정했다. Call<String>은 응답 타입을 <> 안에 나타내는데 서버에서 String을 반환하는 경우 String을 쓴다.
  • @Field annotation의 경우 데이터를 form-encoded로 전송할 때 사용하고, @FormUrlEncoded (form-encoded 데이터로 전송) annotation을 함께 사용해야 오류가 발생하지 않는다.
  • JSON이 아닌 html로 응답해주는 서버이면 <ResponseBody> 를 Call에 사용한다.
  • 응답이 JSON이면 따로 User.kt와 같이 객체 (data class)를 만들어준다.

 

Retrofit 객체 생성 (RetrofitBuilder.kt)

package org.smu.blood.api

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

object RetrofitBuilder {
    var api: API
    init{
        val retrofit = Retrofit.Builder()
            .baseUrl("http://<서버IP>:8090/connection/") // 요청 보내는 API 서버 url. /로 끝나야 함함
            .addConverterFactory(GsonConverterFactory.create()) // Gson을 역직렬화
            .build()
        api = retrofit.create(API::class.java)
    }
}
  • .addConverterFactory(GsonConverterFactory.create()) : 데이터 파싱하는 converter. Json을 Gson 형태로 변환
  • 서버 url은 항상 /로 끝나야 한다.
  • retrofit 객체 이용해서 Interface 구현
  • 싱글톤 패턴 적용 위해 object로 생성

 

HTTP 요청 & 응답 처리 코드 (MainActivity.kt)

package org.smu.blood

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
import com.google.gson.Gson
import org.smu.blood.api.RetrofitBuilder
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainActivity : AppCompatActivity() {

    var id: String = ""
    var pw: String = ""
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button = findViewById<Button>(R.id.button)
        val userId = findViewById<EditText>(R.id.userId)
        val userPassword = findViewById<EditText>(R.id.userPassword)

        button.setOnClickListener{
            id = userId.text.toString()
            pw = userPassword.text.toString()
            val user = User()
            user.id = userId.text.toString()
            user.password = userPassword.text.toString()
         
            Log.d("BUTTON CLICKED", "id: " + user.id + ", pw: " + user.password)
            Login(user)
        }
    }
    fun Login(user: User){
        val call = RetrofitBuilder.api.getLoginResponse(user)
        call.enqueue(object : Callback<String> { // 비동기 방식 통신 메소드
            override fun onResponse( // 통신에 성공한 경우
                call: Call<String>,
                response: Response<String>
            ) {
                if(response.isSuccessful()){ // 응답 잘 받은 경우
                    Log.d("RESPONSE: ", response.body().toString())

                }else{
                    // 통신 성공 but 응답 실패
                    Log.d("RESPONSE", "FAILURE")
                }
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                // 통신에 실패한 경우
                Log.d("CONNECTION FAILURE: ", t.localizedMessage)
            }
        })
    }
}
  • 간단한 안드로이드와 스프링 연동 테스트이므로 따로 Activity를 만들지 않고 MainActivity 상에 구현했다.
  • 안드로이드 앱에서 사용자가 id와 password를 EditText에 입력하고 버튼을 누르면, Activity에서는 id, password 값을 User 객체의 id와 password 필드로 지정한 후 Login 메소드를 호출한다.
  • Login 메소드는 사용자 입력 User 객체를 JSON 형태로 서버에 전달한다. 서버와 연결이 성공하고 서버에서 지정한 응답을 잘 받은 경우(isSuccessful) 서버의 응답 값을 디버깅 상에 출력하도록 했다.

 

 

Spring Setting


User 객체 (User.java)

package com.example.connection;

import org.springframework.data.annotation.Id;

public class User {
	@Id
	private String id; // _id로 지정
	private String password;
	
	public User() {}
	
	public User(String userId, String password) {
		this.id = userId;
		this.password = password;
	}
	public String getid() {
		return id;
	}
	public String getpassword() {
		return password;
	}

	public String toString() {
		return String.format("User[userId:%s, password: %s]", id, password);
	}

}

 

Controller (HomeController.java)

package com.example.connection;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.connection.User;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {


	@PostMapping(value="android")
	@ResponseBody
	public String androidResponse(@RequestBody User user) {

		System.out.println("Connection from Android");
		System.out.println("id: " + user.getid() + ", pw: " + user.getpassword());
		
		return "1";
	}
	
}
  • @RequestMapping annotation에 method로 POST를 지정하는 대신, @PostMapping annotation을 사용했다. 
  • 응답은 html body에 넣지 않기 때문에 @ResponseBody annotation을 명시했다.
  •  POST 요청에 대한 request 파라미터를 User 객체에 담기 때문에 @RequestBody annotation을 사용했다. 

 

 

Android & Spring Connection Result


  • 사용자 입력 후 LOGIN 버튼 클릭

 

  • Logcat으로 Spring 서버로부터 응답값 1을 확인할 수 있다.

 

  • Spring 프로젝트에서 콘솔 출력 결과
  • 안드로이드 앱에서 입력한 id와 password 값을 Controller에서 받아서 처리된 것을 확인할 수 있다.
728x90
반응형

관련글 더보기