카테고리 없음
[안드로이드][피드][9] API 연동하기
sryang
2021. 5. 19. 23:29
그동안 테스트 저장소로 진행해왔던 기능들을 하나씩 서버와 직접 연동해보려고 합니다.
먼저 메인화면에 해당하는 피드화면을 연동해보려고 합니다.
1. 피드 구조 정리
2. 통신 모듈 생성하기
2.1 Dependencies 추가하기
2.2 Retrofit을 사용한 통신 Instance 구현
2.3 Repository 구현
3. TimeLineViewModel에 통신모듈 적용하기
1. 피드 구조 정리
기존에 구현되었던 구조를 정리해보았고 빨간원을 친 곳이 이번에 새로 추가하게될 실제 저장소 구현 모듈부분 입니다.

2. 통신 모듈 생성하기
2.1 Dependencies 추가하기
Repository Dependencies 구조
Room과 Retrofit가 주요 추가 라이브러리입니다.
TorangRoom : Room 이용한 CRUD 구현
torangdi : 저장소의(Repository) 인터페이스 정의
data: 데이터 정의
위 세가지 모듈은 추후에 합치는 작업이 필요해보입니다.
dependencies {
/** ----------------------------------------------------------- *
* Common *
* ------------------------------------------------------------ */
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
/** ----------------------------------------------------------- *
* Retrofit *
* ------------------------------------------------------------ */
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
/** ----------------------------------------------------------- *
* Room *
* ------------------------------------------------------------ */
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"
implementation "androidx.work:work-runtime-ktx:$rootProject.workVersion"
/** ----------------------------------------------------------- *
* Module *
* ------------------------------------------------------------ */
implementation project(path: ':TorangRoom')
implementation project(path: ':torangdi')
implementation project(path: ':data')
}
2.2 Retrofit을 사용한 통신 Instance 구현
private fun getService(): RestaurantService {
val httpClient = OkHttpClient.Builder()
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.HEADERS
logger.level = HttpLoggingInterceptor.Level.BODY
httpClient.addInterceptor(logger)
httpClient.writeTimeout(10, TimeUnit.SECONDS)
httpClient.connectTimeout(10, TimeUnit.SECONDS)
httpClient.writeTimeout(10, TimeUnit.SECONDS)
httpClient.readTimeout(10, TimeUnit.SECONDS)
httpClient.addInterceptor { chain ->
val original = chain.request()
val request = original.newBuilder()
.header("User-Agent", "android")
.header("accessToken", "")
.method(original.method(), original.body())
.build()
chain.proceed(request)
}
val client = httpClient.build()
//레트로핏 초기화 BASE URL 설정하는 곳
val retrofit: Retrofit = Retrofit.Builder()
.client(client)
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
//통신인터페이스 기반 서비스 생성
return retrofit.create(RestaurantService::class.java)
}
2.3 Repository 구현
TorangRepository는 FeedRepository를 상속하고 있습니다.
TimeLineViewModel에서 FeedRepository의 인터페이스만 알고있습니다.
ViewModelProvider을 통해 ViewModel 생성 시 FeedRepository의 인스턴스를 주입받는 구조로 구현했습니다.
class TorangRepository(private val context: Context) : FeedRepository {
...
/**
* 서버에 피드를 요청합니다.
*/
override suspend fun loadFeed() {
// 서버요청
val list: ArrayList<FeedResponse> = getService().getFeeds(HashMap())
val reviewImages = ArrayList<ReviewImage>()
val users = ArrayList<UserData>()
val feeds = ArrayList<FeedData>()
val likes = ArrayList<Like>()
val restaurants = ArrayList<RestaurantData>()
//서버에서 받은 값을 리스트로 만듬 하나하나 삽입 할 경우 observe 이벤트가 과도하게 발생하여 아래와 같이 처리
for (feed in list) {
try {
feed.pictures?.also { for (picture in it) { reviewImages.add(picture.toReviewImage()) } }
feeds.add(feed.toFeedData())
feed.user?.also { users.add(UserData.parse(it)!!) }
feed.like?.also { likes.add(it) }
feed.restaurant?.also { restaurants.add(RestaurantData.parse(it)) }
} catch (e: Exception) {
Logger.e(e.toString())
}
}
userDao.insertPictures(reviewImages)
userDao.insertAll(users)
userDao.insertFeed(feeds)
userDao.insertLikes(likes)
restaurantDao.insertAllRestaurant(restaurants)
}
...
}
3. TimeLineViewModel에 통신모듈 적용하기
loadTimeLine을 통해 피드를 불러옵니다.
class TimeLineViewModel(
private val testFeedRepository: FeedRepository, // 테스트 저장소
private val feedRepository: FeedRepository // 실제 저장소
) : ViewModel() {
/** 피드 라이브데이터 */
val feeds = feedRepository.getFeed()
/**
* 피드 요청
*/
fun loadTimeline() {
viewModelScope.launch {
try {
feedRepository.loadFeed()
} catch (e: Exception) {
Log.e("__sarang", e.toString())
}
}
}
...
init {
loadTimeline()
}
}
