KBSC 공모전을 준비하면서 안드로이드에 AI 모델을 심는 과정에서 난관에 봉착했다. KoGPT2 라는 한글 자연어 처리 오픈소스 인공지능 모델인데, 모델에 입력을 넣을 때 파이썬의 tokenizer 라는 라이브러리를 사용해서 부호화 된 텍스트를 입력으로 줘야했다... 그런데, 코틀린에는 그런 라이브러리가 없어서 모델에 입력을 줄 수 없는 상황이었다.
그래서 생각해낸 방법은 파이썬과 안드로이드 소켓 통신을 통해 입력으로 텍스트를 서버에 전달하면 서버에서 출력 결과만 안드로이드에 전달하는식으로 구현하는 것 이었다.
하지만, 오류가 너무 많아서 더 좋은 방법이 없을까 생각하다가 Flask 라는 것을 발견했다! 이걸로 해결이 가능할지는 모르겠지만, 일단 공부해보자!!!
Flask 는 파이썬 기반 웹 프레임워크이다. 파이썬으로 간단하게 서버를 만들 수 있다는 의미이다.
❗️이 글은 맥북 에어(M1) 기준으로 작성되었습니다.
pip3 install Flask
pip3 install Flask_restful
from flask import Flask
from flask_restful import Resource, Api, reqparse, abort
# Flask 인스턴스 정리
app = Flask(__name__)
api = Api(app)
//#할 일 목록들
Todos = {
'todo1': {"task": "exercise"},
'todo2': {'task': "eat delivery food"},
'todo3': {'task': 'watch movie'}
}
class TodoList(Resource):
def get(self):
return Todos
api.add_resource(TodoList, '/todos/')
if __name__ == '__main__':
app.run(host="ip주소", port=5000, debug=True)
해당 주소로 이동하면 아무것도 나오지 않을 텐데, 주소 뒤에 /todos 를 입력 후 이동해보면 아래와 같이 JSON 형식의 데이터가 나오게 된다.
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.GET
interface RetrofitAPI {
@GET("/todos") // 서버에 GET 요청을 할 주소 입력
fun getTodoList() : Call<JsonObject> // json 파일을 가져오는 메소드
}
JSON 데이터를 알아볼 수 있는 데이터로 변환 시켜주기 위해 필요한 데이터 클래스
class RetrofitDTO {
data class TodoInfo1(val todo1: TodoInfo)
data class TodoInfo2(val todo2: TodoInfo)
data class TodoInfo3(val todo3: TodoInfo)
data class TodoInfo(val task: String)
}
lateinit var mRetrofit : Retrofit // 사용할 레트로핏 객체
lateinit var mRetrofitAPI: RetrofitAPI // 레트로핏 api 객체
lateinit var mCallTodoList : retrofit2.Call<JsonObject> // Json 형식의 데이터를 요청하는 객체
retrofit 세팅을 위해 필요한 baseUrl을 string 에 추가
<string name="baseUrl">http://본인 컴퓨터 IP주소:5000</string>
레트로핏을 세팅하는 함수
private fun setRetrofit(){
//레트로핏으로 가져올 url설정하고 세팅
mRetrofit = Retrofit
.Builder()
.baseUrl(getString(R.string.baseUrl))
.addConverterFactory(GsonConverterFactory.create())
.build()
//인터페이스로 만든 레트로핏 api요청 받는 것 변수로 등록
mRetrofitAPI = mRetrofit.create(RetrofitAPI::class.java)
}
viewBinding 을 사용했습니다.
private val mRetrofitCallback = (object : retrofit2.Callback<JsonObject>{
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
val result = response.body()
Log.d("testt", "결과는 ${result}")
var gson = Gson()
val dataParsed1 = gson.fromJson(result, RetrofitDTO.TodoInfo1::class.java)
val dataParsed2 = gson.fromJson(result, RetrofitDTO.TodoInfo2::class.java)
val dataParsed3 = gson.fromJson(result, RetrofitDTO.TodoInfo3::class.java)
binding.textView.text = "=== 해야 할 일 ===\n${dataParsed1.todo1.task}\n${dataParsed2.todo2.task}\n${dataParsed3.todo3.task}"
binding.progressBar.visibility = View.GONE
binding.button1.visibility = View.VISIBLE
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
t.printStackTrace()
Log.d("testt", "에러입니다. ${t.message}")
binding.textView.text = "에러입니다. ${t.message}"
binding.progressBar.visibility = View.GONE
binding.button1.visibility = View.VISIBLE
}
})
private fun callTodoList(){
mCallTodoList = mRetrofitAPI.getTodoList() // RetrofitAPI 에서 JSON 객체를 요청해서 반환하는 메소드 호출
mCallTodoList.enqueue(mRetrofitCallback) // 응답을 큐에 넣어 대기 시켜놓음. 즉, 응답이 생기면 뱉어낸다.
}
버튼 클릭시 callTodoList() 호출 -> 서버로부터 데이터를 가져옴
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setRetrofit()
binding.button1.setOnClickListener {
binding.button1.visibility = View.INVISIBLE
binding.progressBar.visibility = View.VISIBLE
callTodoList()
}
}
<uses-permission android:name="android.permission.INTERNET"/>
CLEARTEXT communication to (ip address) not permitted by network security policy
다 제대로 하고 실행시켜보니 이런 오류가 떠서 구글링을 해서 해결 방법을 찾았습니다.
manifest 파일에 다음 코드를 추가하면 됩니다.
참고자료
https://gun0912.tistory.com/80
<application
....
android:usesCleartextTraffic="true"
/>