본문으로 바로가기
윤창원uiwwsw · 작은 우주

회고와 성장

머랭트립 리팩토링: 기능 추가보다 완성도에 집착하기

머랭트립을 Flutter로 다시 만들었습니다.

1인 개발사 Brewstar에서 만드는 사이드 프로젝트입니다. 도보 반경 안에서 카페, 숙소, 술집 같은 여러 장소를 한 번에 검색하고 동선을 함께 살펴보는 앱입니다.

기존 버전도 필요한 기능은 갖추고 있었습니다. 그런데 직접 써보면 검색이 끝난 뒤에도 사람이 한 번 더 해석해야 할 일이 남았습니다. 눌렀을 때의 반응이나 화면 사이의 흐름도 더 다듬고 싶었습니다.

이번 전환에서는 기능의 수보다 그런 마찰을 줄이는 데 시간을 썼습니다. Flutter로 만들었다는 설명을 읽지 않아도, 쓰는 동안 무언가 달라졌다고 느끼게 하고 싶었습니다.

Flutter를 고른 이유

크게 세 가지를 봤습니다.

렌더링 구조를 활용해 일관된 화면 동작을 만들 수 있을 것

iOS와 Android의 UI를 함께 다듬을 수 있을 것

Hot Reload로 수정과 확인을 빠르게 반복할 수 있을 것

사용자에게 “이거 Flutter로 다시 짰어요”라고 설명할 수는 있습니다. 하지만 사용자가 느껴야 할 것은 기술 이름보다 화면의 쾌적함입니다. 렌더링 성능을 실제 기기에서 살피고, 누를 때마다 돌아오는 반응을 다듬는 일까지 해야 전환이 끝납니다.

기능을 더하지 말고, 경험을 다듬자. 이번 작업의 기준이었습니다.

‘카페’라는 글자와 카페를 찾는 마음

기존 검색은 Keyword Matching 방식이었습니다. 구현은 단순했지만 장소 이름에 들어 있는 단어와 사용자가 찾는 업종이 늘 일치하지는 않았습니다.

예를 들어 **카페**를 검색할 때 원하는 것은 커피를 마실 장소입니다. 그런데 이름에 그 단어가 들어간 식당도 함께 나온다면, 사용자가 결과를 다시 걸러야 합니다. 검색창에는 제대로 썼는데 뒷정리는 본인이 하는 셈입니다.

그래서 검색을 두 가지로 나눴습니다.

카테고리 검색: 카페나 숙소처럼 업종이 정해져 있을 때

키워드 검색: 특정 상호명이나 자유로운 검색어로 찾을 때

검색 타입에 따라 요청 경로를 나누는 구조입니다.

// SearchType Enum 정의
enum SearchType { category, keyword }

Future<List<Place>> searchPlaces(String input, SearchType type) async {
  if (type == SearchType.category) {
    // 1. 카테고리 검색: 정확한 분류 코드(Code) 기반 필터링
    return await repository.fetchByCategory(categoryCode: input);
  } else {
    // 2. 키워드 검색: 기존의 텍스트 매칭 방식 유지 (특정 상호명 검색 니즈)
    return await repository.fetchByKeyword(query: input);
  }
}

이제 카페 버튼을 누르면 해당 카테고리로 결과를 제한하고, 특정 이름을 찾을 때는 키워드 검색을 사용합니다. 코드에서는 작은 분기지만, 화면에서는 사용자가 무엇을 찾는지 먼저 구분하는 선택입니다.

저장한 장소가 다음 사람에게 가도록

이전에는 장소를 저장해도 그다음 과정이 앱 밖에서 이어졌습니다. 친구에게 보여주려면 스크린샷을 찍거나 텍스트를 복사해야 했습니다. 가고 싶은 곳은 앱에 모아놓았는데, 같이 갈 사람에게 건네는 일은 별도 작업이었습니다.

그래서 검색 → 저장 → 공유 → 재저장으로 이어지는 흐름을 설계했습니다.

저장한 목록을 고유 링크나 공유 가능한 객체로 전달

받은 목록에 좋아요를 누르거나 자신의 목록으로 가져오기

graph LR
A[사용자 A] -->|장소 검색| B(저장 리스트 생성)
B -->|공유| C{다른 사용자들}
C -->|확인| D[참고 및 방문]
C -->|Fork| E[내 리스트에 재저장]

(개념적 흐름도)

저장함에 모인 장소가 다음 사람의 여행으로 이어질 수 있게 된 것입니다. “나 여기 가고 싶어”라는 말과, 같이 갈 사람이 그곳을 자기 목록에 담는 일 사이를 앱이 연결합니다. 검색 도구를 만들다가, 취향과 동선이 오갈 자리를 함께 만들게 됐습니다.

쓸 때 드러나는 것들

로딩 중의 스켈레톤 애니메이션, 결과가 없을 때의 화면, 버튼을 눌렀을 때의 작은 햅틱 피드백도 함께 다듬었습니다.

이런 작업은 새 기능을 소개하는 목록에서는 한 줄로 지나가기 쉽습니다. 하지만 사용자는 검색 결과가 나오기 전에도 앱을 쓰고 있고, 결과가 없을 때도 앱을 쓰고 있습니다. 그 시간을 비워둘 수는 없다고 생각했습니다.

이번 리뉴얼에서 공들인 것은 그런 부분입니다. 찾을 곳을 더 많이 보여주는 것과, 찾는 과정을 덜 번거롭게 만드는 것은 서로 다른 작업이었습니다.

앱의 손맛은 기능 목록만으로 설명하기 어렵습니다. 검색하는 동안, 결과를 기다리는 동안, 아무것도 찾지 못한 순간에도 사용자는 이 앱이 얼마나 공들여 만들어졌는지 느낍니다.

앞으로도 기능의 가짓수보다 그 밀도를 높이고 싶습니다. 단 하나를 쓰더라도, 자신이 대접받고 있다고 느낄 수 있는 소프트웨어를 만들고 싶습니다.

Flutter 성능 가이드

Assisted by AI

© uiwwsw · 본인 소유의 글·이미지에 대한 무단 복제·재게시를 허용하지 않습니다. 개인 열람·원문 링크 공유·검색 색인과 제한적인 미리보기는 허용합니다. 법령상 이용 예외, 기존 이용 허락, 코드·제삼자 자료의 기존 라이선스는 유지합니다. 콘텐츠 이용 안내

무단 복제·재게시를 허용하지 않습니다. 콘텐츠 이용 안내

윤창원이 벨로그에 남긴 글을 이 작은 우주에도 모았습니다. 사진은 누르면 원본 크기로 볼 수 있습니다. 원문의 전체 서식 보기 ↗

모든 글 둘러보기 →