Compose Navigation 을 통해 Argument 를 전달할 때 주의 해야할 점

이지훈·2023년 11월 21일
0

서두

https://velog.io/@mraz3068/Passing-URL-as-Compose-Navigation-Argument

최근에 프로젝트를 진행 하면서 compose navigation 을 통해 argument 를 전달하는 과정에서 발생했던 이슈들을 위에 글에 이어서 정리 해보려고 한다.

문제 상황 1. Argument 내에 '/'가 존재하는 경우

내가 전달해야 했던 문자열이 "슬픔/우울" 과 같이 감정/원인 형식의 문자열 이었는데 이를 전달할 때 위에 글에서와 동일한

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest

형식의 에러가 발생하고 앱이 터졌었다.

문제 해결

원인은 마찬가지로

Navigation routes are equivalent to urls. Generally you're supposed to pass something like id there.
When you need to pass a url inside another url, you need to encode it:

Navigation route 는 url 과 같이 때문에 url 에서 사용되는 형식인 '/'가 argument에 포함되어있으면 정상적으로 작동하지 않는 것으로 추론된다. 번거롭다. 그립읍니다 fragment-navigation
따라서 마찬가지로 인코딩의 과정이 선행되어야 한다. 인코딩을 해준뒤에 이를 argument 로 담아서 전달하면 정상적으로 동작하는 것을 확인할 수 있었다.

     _eventFlow.emit(
            ChatUiEvent.NavigateToResult(emotion = URLEncoder.encode(emotion, StandardCharsets.UTF_8.toString())),
          )

흥미로웠던 점은, 이전에는 발생하지 않았던 .encode() 함수에서의 lint check, code suggestion이 발생하는 것을 확인할 수 있는데

Wrap call 'withContext' 를 누르면 다음과 같이 코드가 변환되는 것을 확인할 수 있었다.

URL을 인코딩하는 작업이 IO 작업에 속하므로 블로킹 작업으로 간주되는 듯 하다.
내부 코드를 확인해보았을 때 encode 함수 내에서

charArrayWriter.write(c); 

다음과 같은 작업을 수행하는데

    /**
     * Writes a character to the buffer.
     */
    public void write(int c) {
        synchronized (lock) {
            int newcount = count + 1;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            buf[count] = (char)c;
            count = newcount;
        }
    }

해당 함수 때문이지 않을까 추측된다. 무튼 인코딩을 해줄 때, 인코딩 연산은 withContext(Dispatchers.IO) {} 블럭 내부에서 해주도록 하자.

문제 상황 2. Argument 내에 띄어쓰기가 존재하는 경우

전달하려는 감정 중에서는 원인에 자세한 이유가 서술되어 있는 감정도 존재 했는데 예를 들면, 기억은 안나지만 '슬픔/시험 망함' 과 비슷한 느낌이었다.
마찬가지로 '/'가 포함되어 있으므로 인코딩을 해주었고 이를 전달하였는데 문제는 전달 받은 측에서 잘 받았는지 확인을 위해 로그를 찍어보았는데
'슬픔/시험+망함' 이라고 변환되어 있는 것을 확인할 수 있었다. 해당 감정으로 api를 호출해야 했기 때문에 서버에 없는 감정을 전달하여 response로 null이 내려오는 것을 확인할 수 있었다.

문제 해결

kotlin 에서의 URLEncoder의 encode 함수가 실행될 때, 인코딩 과정에서 띄어쓰기가 자동으로 '+'로 변환되는 것 같다.
따라서 부수적으로 '+'를 다시 공백 ' '으로 변환해주는 작업을 해주어야 한다. 번거롭다x2

다행히 kotlin 에서는 강력한 문자열 가공 함수들이 존재하므로

val result = getRecommendedContentListUseCase(
        EmotionRequestEntity(
          emotion = emotion.replace("+", " "),
        ),
      )

다음과 같이 api를 호출할 때, replace 함수를 호출하여 문자열 내에 "+"이 존재하면 " "로 전부 변환하여 api를 호출 하도록 하였고 정상적으로 response를 받아올 수 있었다.

Velog 에서는 url에 띄어쓰기(공백)이 존재하는 경우, 몇칸 띄어쓰기이든 '-'로 변환하던데, 아마 자바스크립트 기반의 언어로 추측되는데 그쪽은 정책이 그런가 보다.

%20 과 같은 것으로 변경되어서 url 을 공유할 때 굉장히 못생긴(?) url 이 되는 것도 봤던 것 같은데, 언어마다 인코딩 함수가 동작하는 방식이 다르다는 것을 알 수 있었다.

해당 문제 해결 코드가 포함된 전체 코드는 아래 깃허브 링크에서 확인할 수 있습니다.
https://github.com/KU-LAST/psychat-android

profile
실력은 고통의 총합이다. Android Developer

0개의 댓글