[최종 프로젝트] Problem Shooting

2024. 8. 23. 03:24카테고리 없음

# Fragment간에 Viewmodel을 통한 데이터 공유가 되지 않는 문제

- 문제 상황: FilterFragment에서 setFilter()를 통해 필터의 조건을 SharedViewModel에 저장하고, 

  저장한 데이터를 ItemListFragment에서 getFilteredData를 통해 반영하여 불러오는 과정에서 

  getFilteredData() 시 애초에 저장된 데이터 자체가 불러와지지 않고 null로 표시되는 오류가 발생함

- 문제 원인: 구글링한 결과 프래그먼트 간에 viewModels를 활용할 때에는 아래 1번의 by viewModels()가 아니라, 2번의 

  by activityViewModels() 를 활용해야 함 (1번의 경우 프래그먼트 내로 데이터 범위가 국한됨)

- 해결: 아래 코드 참조

// 수정 전
    private val viewModel: SharedViewModel by ViewModels() {
        viewModelFactory { initializer { SharedViewModel(GonggongFoodRepositoryImpl(), MarketRepositoryImpl()) } }
    }
    
// 수정 후
    private val viewModel: SharedViewModel by activityViewModels() {
        viewModelFactory { initializer { SharedViewModel(GonggongFoodRepositoryImpl(), MarketRepositoryImpl()) } }
    }

 

# List에 동적으로 값을 추가하는 과정에서 .plus()를 사용했을 때는 안되었다가 + 연산자를 사용하니까 해결

- 문제 상황: FilterFragment에서 필터링할 알러지 성분을 입력받고 입력받은 값을 selectedAllergies 리스트에 추가할 때, plus()를 사용하니 값이 제대로 추가되지 않아 IndexOutOfBoundException이 발생함

- 문제 원인: .plus(), .minus()는 기존의 값을 변경하는 것이 아니라, 변경된 새로운 값을 만드는 역할을 하는데

  새로운 값을 지정해주는 게 아니라 원본 값에다 바로 .plus()를 해버리니까 원본 데이터를 그대로 빈 리스트로 남아있었던 것

- 해결: 아래 코드 참조

// 기존
        egg.setOnCheckedChangeListener{ _, isChecked ->
            if (isChecked) {
                selectedAllergies.plus("계란")
            } else {
                selectedAllergies.minus("계란")
            }
        }
        
// 수정
        egg.setOnCheckedChangeListener{ _, isChecked ->
            if (isChecked) {
                selectedAllergies += "계란"
            } else {
                selectedAllergies -= "계란"
            }
        }

 

 

# Retrofit으로 데이터를 가져올 때, null로 불러와질 경우

- 문제 상황: ItemDetailFragment에 일부 데이터가 null로 표시됨

- 문제 원인: <json> 형식의 파일을 그대로 긁어와서 json file to Kotlin 했어야 하는데 네이버 쇼핑 API 문서에 있는 xml 형식의 파일을 긁어오는 바람에 데이터클래스가 다 꼬였음. 

- 해결: json 형식으로 긁어와서 다시 데이터 클래서 생성해서 해결

 

// 기존 xml 형식
<rss version="2.0">
    <channel>
        <title>Naver Open API - shop ::'가방'</title>
        <link>http://search.naver.com</link>
        <description>Naver Search Result</description>
        <lastBuildDate>Tue, 04 Oct 2016 13:23:58 +0900</lastBuildDate>
        <total>17161390</total>
        <start>1</start>
        <display>10</display>
        <item>
            <title>허니트립 보스턴백</title>
            <link>http://openapi.naver.com/l?AAABWLsQ7CIBRFv+Z1JLzSShkYqLajRmPcG6TQRCgiNunfizdnODnJfX9N2iUMCnoKHYWh/4sSlUtmli7nCExBPRY+bo1xCZaEaTOJ6NWXaKdsSHAB2Lg8gZ2QMmybA0cuqiyx4W0ZZR2KrvJyx2CPV3RQ95fbnDT3r+Fh2kbfz5su7x8wIs7ZjgAAAA==</link>
            <image>http://shopping.phinf.naver.net/main_1031546/10315467179.jpg</image>
            <lprice>6700</lprice>
            <hprice>0</hprice>
            <mallName>허니트립</mallName>
            <productId>10315467179</productId>
            <productType>2</productType>
            <brand></brand>
            <maker>허니트립</maker>
            <category1>패션잡화</category1>
            <category2>여행용가방/소품</category2>
            <category3>보스턴백</category3>
            <category4></category4>
        </item>
        ...
    </channel>
</rss>
}
        
// 수정 json 형식
{
	"lastBuildDate":"Mon, 26 Aug 2024 09:34:50 +0900",
	"total":41669,
	"start":1,
	"display":5,
	"items":[
		{
			"title":"롯데 <b>꼬깔콘<\/b> 고소한맛 67g",
			"link":"https:\/\/search.shopping.naver.com\/catalog\/31424520970",
			"image":"https:\/\/shopping-phinf.pstatic.net\/main_3142452\/31424520970.20220531161830.jpg",
			"lprice":"810",
			"hprice":"",
			"mallName":"네이버",
			"productId":"31424520970",
			"productType":"1",
			"brand":"롯데",
			"maker":"롯데웰푸드",
			"category1":"식품",
			"category2":"과자\/베이커리",
			"category3":"스낵",
			"category4":"일반스낵"
		},
		{
			"title":"롯데 <b>꼬깔콘<\/b> 군옥수수맛  72g",
			"link":"https:\/\/search.shopping.naver.com\/catalog\/19809428787",
			"image":"https:\/\/shopping-phinf.pstatic.net\/main_1980942\/19809428787.20190617152304.jpg",
			"lprice":"790",
			"hprice":"",
			"mallName":"네이버",
			"productId":"19809428787",
			"productType":"1",
			"brand":"롯데",
			"maker":"롯데웰푸드",
			"category1":"식품",
			"category2":"과자\/베이커리",
			"category3":"스낵",
			"category4":"일반스낵"
		},
		{
			"title":"롯데제과 봉지 과자 스낵 100종 골라담기\/새우깡 포카칩 <b>꼬깔콘<\/b>",
			"link":"https:\/\/link.auction.co.kr\/gate\/pcs?item-no=C290831366&sub-id=1&service-code=10000003",
			"image":"https:\/\/shopping-phinf.pstatic.net\/main_2609356\/26093566252.1.jpg",
			"lprice":"900",
			"hprice":"",
			"mallName":"옥션",
			"productId":"26093566252",
			"productType":"2",
			"brand":"롯데웰푸드",
			"maker":"롯데웰푸드",
			"category1":"식품",
			"category2":"과자\/베이커리",
			"category3":"스낵",
			"category4":"일반스낵"
		},
		{
			"title":"롯데 <b>꼬깔콘<\/b> 매콤달콤한맛 67g",
			"link":"https:\/\/search.shopping.naver.com\/catalog\/32600420618",
			"image":"https:\/\/shopping-phinf.pstatic.net\/main_3260042\/32600420618.20220526173459.jpg",
			"lprice":"850",
			"hprice":"",
			"mallName":"네이버",
			"productId":"32600420618",
			"productType":"1",
			"brand":"롯데",
			"maker":"롯데웰푸드",
			"category1":"식품",
			"category2":"과자\/베이커리",
			"category3":"스낵",
			"category4":"일반스낵"
		},
		{
			"title":"롯데웰푸드 <b>꼬깔콘<\/b> 고소한맛 72g",
			"link":"https:\/\/search.shopping.naver.com\/catalog\/8581572484",
			"image":"https:\/\/shopping-phinf.pstatic.net\/main_8581572\/8581572484.20150607225351.jpg",
			"lprice":"670",
			"hprice":"",
			"mallName":"네이버",
			"productId":"8581572484",
			"productType":"1",
			"brand":"롯데웰푸드",
			"maker":"롯데웰푸드",
			"category1":"식품",
			"category2":"과자\/베이커리",
			"category3":"스낵",
			"category4":"일반스낵"
		}
	]
}

 

※ viewModel.observe()~~~ 를 한다는 것은, 새로 Observing한 데이터를 가지고 새로운 동작을 하려면 Observing하는 코드 안에서 이뤄져야 한다는 것을 뜻함. setonClickListener가 클릭된 이벤트와 그 이후의 동작을을 해당하는 {} 안에서 수행하듯이, viewModel.observe() 역시도 새로 받아온 데이터와, 이를 새로 UI에 반영해야 하는 동작의 경우에는 해당하는 코드 안에서 진행되어야 함.

 

# SignUpFragment에서 새 유저를 생성하고 SignInFragment로 넘어가서 같은 정보로 로그인하려고 하는데 존재하지 않는 아이디라고 나오는 오류

- 문제 상황: 상동

- 문제 원인: User 정보를 받아오는 UserViewModel에서 users.value에다가 값을 add만 해주고 users.value 자체를 업데이트된 값으로 재선언해주지 않아서 계속 users.value가 초기값인 null로 나왔음

- 해결: users.value에 업데이트된 값을 새로 넣어주게끔 변경함으로써 해결. 보통은 viewModel의 value값을 업데이트해줄 때에는 아래처럼 newUser과 같이 새로운 변수를 선언해주고, 그 newUser 값을 users.value에 대입하는 형식으로 업데이트를 많이 진행함.

// 기존
    fun setUser(user: User) {
//        _uiState.value = UiState.Loading
        viewModelScope.launch {
            runCatching {
                _users.value.add(user)
                // 보통은 변수로 LiveData.value 에 새로운 값을 써줌

            // 알
//                _uiState.value = UiState.Success(_homeFoods)
            }.onFailure {
                Log.e(TAG, "setUser() failed! : ${it.message}")
                handleException(it)
//                _uiState.value = UiState.Error("Error")
            }
        }
    }

// 수정
    fun setUser(user: User) {
//        _uiState.value = UiState.Loading
        viewModelScope.launch {
            runCatching {
                val newUsers = _users.value?: mutableListOf()
                newUsers.add(user)
                _users.value = newUsers
                // 보통은 변수로 LiveData.value 에 새로운 값을 써줌

            // 알
//                _uiState.value = UiState.Success(_homeFoods)
            }.onFailure {
                Log.e(TAG, "setUser() failed! : ${it.message}")
                handleException(it)
//                _uiState.value = UiState.Error("Error")
            }
        }
    }