-
서버와 통신하기(retrofit)Android 2025. 3. 16. 22:34
띡띡띡... 오늘의 할일 작성 완료! 😎
어? 근데 데이터가 어떻게 저장이 되는걸까?🤔
사용자가 입력한 데이터는 어떻게 저장이 될까요?
크게 내부 저장소와 외부 저장소로 나뉠 수 있는데, 데이터양이 많을 경우에 더 적합한 외부 저장소, 즉 서버에 데이터를 저장하는 방법에 대해 다뤄보고자 합니다.
안드로이드에서는 이를 위해 retrofit 을 주로 많이 사용합니다. 한번 하나씩 파헤쳐 보겠습니다!
1. retrofit 의존성 추가
retrofit 을 사용하기 위해서는 먼저 관련된 의존성을 모듈 단위의 build.gradle 에 추가해야 합니다.
implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0")
시간이 지나면 업데이트로 버전이 달라지기 때문에 정확한 버전은 공식 문서를 참고하면 됩니다.
2. retrofit 객체 만들기
object RetrofitInstance { private const val BASE_URL = "여기다가 base url 을 작성해주세요." val networkService: NetworkService by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(NetworkService::class.java) } }
base url 을 먼저 알아야하는데요. url 에서 슬래시(/) 를 기준으로 왼쪽이 base url, 오른쪽이 엔드 포인트 입니다.
예를 들어, url 이 localhost:8080/swagger-ui/ 라고 한다면, base url 은 슬래시를 기준으로 왼쪽인 localhost:8080 가 되는거죠.
NetworkService 는 서버와 통신하기 위한 인터페이스입니다. Retrofit 을 사용하여 인터페이스 객체를 만들때는 base url, converterFactory 를 넣어주면 됩니다.
3. 인터페이스 작성하기
이제 인터페이스에서 통신을 위한 구체적인 메소드를 작성해봅시다. 메소드에도 여러 종류가 있는데 하나씩 살펴보겠습니다.
1. GET (데이터 조회)
첫번째로, GET 입니다. 이는 원격 저장소인 서버에 저장되어있는 데이터를 조회할 때 사용합니다.
interface NetworkService { // 모든 항목 조회 @GET("/schedule/todo") fun getTodoItems(): Call<List<TodoItem>> // 특정 항목 조회 @GET("/schedule/todo/{id}") fun getTodoItem( @Path("id") id: Int, @Query("date") date: String ):Call<TodoItem> }
@GET 어노테이션과 함께 파라미터로 엔드포인트를 적습니다. 메소드 이름을 지정하고, 반환값으로 Call 에다가 data class 를 작성합니다. 반환값이 필요하지 않으면 Any 타입으로 적습니다. 한번 예를 들어볼까요?
서버 응답값(예시) 서버에서 다음과 같은 응답값을 준다고 합시다. 이는 json 형태로 실제로 사용하려면 data class 객체로 변환해야 하는데요. 다음과 같은 형태로 DTO(Data Transfer Object) 모델을 작성하면 됩니다.
data class TodoItem( val id: Int = 0, val date: String = "", val title: String = "", @SerializedName("completed") val isCompleted: Boolean = false, val calendar: CalendarItem ) data class CalendarItem( val id: Int = 0, val date: String = "", val day: String = "", val events: List<String> )
여기서 주목할만한 점은 두가지가 있는데요. 첫번째는, 기본값을 지정해주는 것입니다. 서버에서 받아오는 값들 중 어떤 값은 누락이 될 수 있는데, 기본값을 지정하지 않게 되면 해당 값 때문에 앱이 튕길 수 있습니다. 따라서, 기본값을 적는게 좋습니다. 두번째는, @SerializedName 어노테이션입니다. 위의 예시에서 서버에서는 completed 라는 변수값으로 데이터를 전달합니다. 하지만, completed 말고 다른 변수로 네이밍하고 싶을 수 있습니다. 이때는, 해당 어노테이션을 선언한 다음에 원하는 변수 이름을 적어주면 됩니다.
파라미터 - path 파라미터 - query 또한, 데이터를 조회하기 위해 서버로 값을 보내줘야 하는 경우가 있습니다. 대표적으로 header, path, query 가 있습니다. 보통 사용자를 식별하는 토큰같은 경우에는 header 로 넣어주고, id 같은 경우 path 로, 나머지는 query 로 넣어줍니다. 각각 맞는 어노테이션을 사용해주면 됩니다.
2. POST (데이터 전송)
두번째로는, POST 가 있습니다. 데이터를 조회할 수도 있지만, 새로운 데이터를 보내주기도 해야하죠.
@POST("/schedule/todo/create") fun registerTodoItem( @Query("date") date: String, @Query("title") title: String ): Call<TodoItem>
첫번째와 유사한 방식으로 작성하면 됩니다.
3. DELETE (데이터 삭제)
사용자의 데이터를 삭제하고 싶을 수도 있습니다.
@DELETE("/schedule/todo/{id}") fun deleteTodoItem( @Path("id") id: Int ):Call<Any>
파라미터 중 @Path 의 경우에는 엔드포인트 자체에 포함이 되는데요. 이때, 중괄호로 감싸져서 표현됩니다.
4. PATCH (데이터 일부 수정)
@PATCH("/schedule/todo/{id}/toggle-completed") fun toggleTodoItem( @Path("id") id: Int ):Call<TodoItem>
데이터를 수정할 때는 2가지 종류의 어노테이션이 있는데, 그 중 첫번째입니다.
데이터를 전체가 아닌 일부 수정하는 경우에는 PATCH 어노테이션을 사용합니다.
5. PUT (데이터 전체 수정)
@PUT("/schedule/todo/{id}") fun updateTodoItem( @Path("id") id: Int, @Query("date") date: String, @Query("title") title: String ):Call<TodoItem>
데이터를 전체 수정하는 경우에는 PATCH 가 아닌 PUT 어노테이션을 사용합니다. 두가지 어노테이션이 헷갈릴 수 있는데, 잘 구분하시길 바랍니다.
4. 실제 서버 통신해보기
이제 준비를 다했으니, 실제 통신을 해보겠습니다. 데이터 조회(GET)로 살펴볼까요?
RetrofitInstance.networkService.getTodoItems().enqueue(object : Callback<List<TodoItem>> { override fun onResponse( call: Call<List<TodoItem>>, response: Response<List<TodoItem>> ) { if(response.isSuccessful) { val todoItems = response.body() as ArrayList<TodoItem> toDoViewModel.updateTodoItems(todoItems) } else { Log.d("server success", "else") } } override fun onFailure(call: Call<List<TodoItem>>, t: Throwable) { Log.d("server error", ""+t.message) } }) }
object 로 선언되어 객체 생성 없이 접근 가능한 RetrofitInstance 에서 networkService 인터페이스 객체를 먼저 불러오고, 이후에 메소드를 호출합니다. 서버 통신이 성공할 경우 response.body 로 데이터를 받아오고, 이후에 UI 에 원하는 형태로 보여주면 됩니다. 실패할 경우는 대표적으로 와이파이나 데이터 접속이 안되는 경우를 들 수 있는데, 이때는 로그캣으로 정확한 원인을 파악해봅시다.
5. 시연영상
POST (등록) DELETE (삭제) PUT (수정) 이벤트를 주는 방법은 다양합니다. 이미지 뷰에 클릭리스너를 달아서 다이얼로그를 띄우거나, 길게 누르는 롱클릭리스너를 이용하는 방법 등등 원하는 형태로 활용하면 됩니다.
이렇게 서버에 데이터 저장하게 되면 앱을 종료한 후 다시 들어가더라도 여전히 그대로 있습니다. 다만, 와이파이 연결이나 데이터를 사용하지 않으면 제대로 불러오지 못한다는 단점은 있습니다. (이때는 토스트 메세지 등으로 어떤 상황인지 사용자에게 알려야 합니다.)
와이파이 및 데이터 연결을 하지 않더라도 데이터가 보여지게 하려면 내부 저장소를 사용하면 되는데요. 해당 방법은 추후에 한번 정리해서 소개해보겠습니다! 🙂
도움이 되셨다면 하트 한번씩 부탁드릴게요🩷
'Android' 카테고리의 다른 글
하단 내비게이션 바(BottomNavigationView) (2) 2025.05.04 알람 기능 구현하기(AlarmManager) (2) 2025.04.27 외부 앱 접속 막기 (0) 2025.03.03 핸드폰에 설치된 앱 불러오기 (0) 2025.02.25 액티비티간 데이터 전달 - startActivityForResult (0) 2024.10.10