빠른 포르투갈어 검색
터치 세 번만에 포르투갈어 단어를 찾을 수 있는 앱
사전을 켜지 않아도 단어를 앱에 공유하면 그 자리에서 바로 단어사전이 나타난다.
프론트엔드
과거에 만든 PT POPDIC을 Flutter로 재구현했다.
메서드 채널로 안드로이드 특수 기능을 사용했다.
Provider로 상태관리를 했다.
구현하기 어려웠던 부분
- 안드로이드 인텐트 사용 (플랫폼 특정 기능 사용)
- 사전 카드 이외 부분 터치 시 앱 종료
1. 안드로이드 인텐트 사용
Writing custom plaform-specific code를 보고 따라하니 텍스트 메뉴와 공유에 앱을 연결하는 건 쉬웠다.
자바 액티비티 속 메서드 채널로 공유된 문자열을 플러터 위젯에서 받으면 끝났다.
하지만 액티비티는 달라도 실행되는 플러터 인스턴스는 같고
플러터 인스턴스에서 자신이 실행 중인 액티비티를 알 방법을 찾을 수 없었다.
그래서 ‘그냥 앱 실행 시’와 ‘다른 앱 위에서 실행 시’를 구분하는 게 조금 까다로웠다.
메서드 채널에서 문자열을 받으면 ‘다른 앱 위에서 실행 중’
문자을 받는 걸 실패하면 ‘그냥 앱 실행 중’ 으로 구분하고
문자열을 받기 직전, 이도저도 아닌 상태에서는 로딩바를 띄웠다.
2. 사전카드 이외 부분 터치 시 앱 종료
옛날 PT POPDIC에서는 터치 이벤트의 X, Y축이 사전카드 밖이면 앱을 종료했다.
플러터에서는 위젯에 글로벌키를 연결해야 위젯 위치 정보를 얻을 수 있는다.
부모 위젯에서 자식 위젯 여러 개의 글로벌키를 연결해서 위치정보를 얻다보면 스파게티 코드가 되어 보기 싫은 코드가 작성된다.
그래서 좌표값으로 터치 영억을 알아내는 것을 포기했다.
대신 전체화면에 터치 시 앱이 종료되는 제스처 탐지 위젯을 씌우고
그 위에 사전카드에 제스처 탐지 위젯을 다시 씌워서 전체화면으로 터치 이벤트가 전파되는 걸 막았다.
엄청 간단하게 문제를 해결했다.
과거 방식에 집착하지 말고 다르게 생각하는 게 중요함을 느꼈다.
기타
Provider 패턴은 Vuex랑 비슷해서 사용하기 정말 편했다.
Provider에 빌드 컨텍스트랑 사용할 모델 타입만 건내주면 데이터 저장소에 접근할 수 있다.
차이점이라면 플러터에서는 내가 직접 notifyListeners()를 호출해서 UI를 다시 그리도록 하는 거 같다.
백엔드
네이버 포어 사전에서 데이터를 가져오는 기능을 네번째 구현 중이다.
첫번째는 Callback 두번째는 Promise 세번째는 Async/Await를 사용했고
이번에는 함수만을 사용했다.
함수를 정말 작은 단위로 쪼개고 이름을 붙여서 파이프라이닝했다.
옛날코드보다 보기 좋고 짧다.
Memoizing If 함수 – mif
const v = f()
if (v) g(v)
이런 코드를 로직 상 많이 작성했다.
이 코드를 _.if 함수를 사용해서 표현하려면
_.if(f())(g(f())
이렇게 f()를 두 번 호출해야 구색을 맞출 수 있다.
f()가 연산 비용이 크다면 문제가 되므로
술어에 들어온 값이 truthy하면 그 값을 그대로 g()에 적용하는 mif 함수를 만들었다.
mif(f())(g)
이런 식으로 사용할 수 있다.
덕분에
const extractEntryIds = getExtractEntryIds(query)
if (extractEntryIds) return searchDictsByEntry(searchDictsByEntry)
const rootsFromWiki = getRootsFromWiki(entry)
if (rootsFromWiki) return searchWords(rootsFromWiki)
const verbRoots = getVerbRoots(query)
if (verbRoots) return searchWords(verbRoots)
const postprocessed = needsToPostprocess(query)
if (postprocessed) return searchWords(postprocessed)
return []
이런 코드를
mif(extractEntryIds(query))(searchDictsByEntry)
.elseIf(rootsFromWiki)(searchWords)
.elseIf(_.c(getVerbRoots(query)))(searchWords)
.elseIf(_.c(needsToPostprocess(query)))(searchWord)
.else(_.c([]))
더 짧게, 동사 위주라 더 읽기 쉬운 코드로 작성할 수 있었다.
마치며
앱은 독창적으로 잘 만들었다고 생각하는데 다운로드 수는 3 밖에 되지 않는다.
원인은 1. 홍보 채널이 부족하다. 2 포르투갈어가 수요가 적다.
1은 극복할 수 있지만 2는 극복하기 힘들 것 같다.
그래서 다음부터는 독창성과 대중성을 모두 갖춘 앱을 만들어서 다운로드 수 100을 기록해봐야겠다.