[Flask+Android(Kotlin)] Flask REST API 서버를 만들고 Android Retrofit2 로 서버에서 데이터 가져오기_1

LeeEunJae·2022년 8월 16일
3

Study Kotlin

목록 보기
9/20

KBSC 공모전을 준비하면서 안드로이드에 AI 모델을 심는 과정에서 난관에 봉착했다. KoGPT2 라는 한글 자연어 처리 오픈소스 인공지능 모델인데, 모델에 입력을 넣을 때 파이썬의 tokenizer 라는 라이브러리를 사용해서 부호화 된 텍스트를 입력으로 줘야했다... 그런데, 코틀린에는 그런 라이브러리가 없어서 모델에 입력을 줄 수 없는 상황이었다.

그래서 생각해낸 방법은 파이썬과 안드로이드 소켓 통신을 통해 입력으로 텍스트를 서버에 전달하면 서버에서 출력 결과만 안드로이드에 전달하는식으로 구현하는 것 이었다.
하지만, 오류가 너무 많아서 더 좋은 방법이 없을까 생각하다가 Flask 라는 것을 발견했다! 이걸로 해결이 가능할지는 모르겠지만, 일단 공부해보자!!!

✅ Flask 란?

Flask 는 파이썬 기반 웹 프레임워크이다. 파이썬으로 간단하게 서버를 만들 수 있다는 의미이다.

📌 Flask Rest API 서버 만들기

❗️이 글은 맥북 에어(M1) 기준으로 작성되었습니다.

  1. 터미널에서 라이브러리 설치
pip3 install Flask
pip3 install Flask_restful
  1. flask 인스턴스 정리
from flask import Flask
from flask_restful import Resource, Api, reqparse, abort

# Flask 인스턴스 정리

app = Flask(__name__)

api = Api(app)
  1. 웹 화면에 띄워줄 Json 을 만들기
//#할 일 목록들
Todos = {
    'todo1': {"task": "exercise"},
    'todo2': {'task': "eat delivery food"},
    'todo3': {'task': 'watch movie'}
}
  1. Json 을 반환하는 클래스
class TodoList(Resource):
    def get(self):
        return Todos
  1. api 에 리턴값이 있는 클래스와 그 리턴값이 나타날 경로를 입력
api.add_resource(TodoList, '/todos/')
  1. 서버를 실행 할 host 와 port 를 지정
if __name__ == '__main__':
    app.run(host="ip주소", port=5000, debug=True)
  1. 실행 후 터미널에 나오는 주소로 이동

해당 주소로 이동하면 아무것도 나오지 않을 텐데, 주소 뒤에 /todos 를 입력 후 이동해보면 아래와 같이 JSON 형식의 데이터가 나오게 된다.

📌 안드로이드 코틀린 Retrofit2 로 데이터 가져오기

  1. 의존성 추가
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
  1. RetrofitAPI 인터페이스

import com.google.gson.JsonObject
import retrofit2.Call
import retrofit2.http.GET

interface RetrofitAPI {
    @GET("/todos") // 서버에 GET 요청을 할 주소 입력
    fun getTodoList() : Call<JsonObject> // json 파일을 가져오는 메소드
}
  1. DTO

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)
}
  1. MainActivity 변수 선언
lateinit var mRetrofit : Retrofit // 사용할 레트로핏 객체
lateinit var mRetrofitAPI: RetrofitAPI // 레트로핏 api 객체
lateinit var mCallTodoList : retrofit2.Call<JsonObject> // Json 형식의 데이터를 요청하는 객체
  1. retrofit 세팅

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)
    }
  1. 요청에 대한 응답을 받을 수 있는 Callback

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
        }
    })
  1. Retrofit API 와 콜백 연결
private fun callTodoList(){
        mCallTodoList = mRetrofitAPI.getTodoList() // RetrofitAPI 에서 JSON 객체를 요청해서 반환하는 메소드 호출
        mCallTodoList.enqueue(mRetrofitCallback) // 응답을 큐에 넣어 대기 시켜놓음. 즉, 응답이 생기면 뱉어낸다.
    }
  1. 초기화 작업

버튼 클릭시 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()
        }

    }
  1. 권한추가 후 실행
<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"
/>

📌 실행결과

👀 참고자료 👀

https://m.blog.naver.com/PostView.naver?blogId=ljk041180&logNo=221590988597&targetKeyword=&targetRecommendationCode=1

profile
매일 조금씩이라도 성장하자

0개의 댓글