Mini Project 4 06

BookShelf, 탐색 가능한 책장의 비밀

CRUD 앱이라고 들으면 등록/조회/수정/삭제만 떠오른다. 그런데 BookShelf의 독자 경험은 책을 찾고 비교하고 비슷한 책으로 이동하는 탐색 흐름까지 포함한다.

근거 · 4차 검색·필터·유사 도서 UI

BookShelf, 탐색 가능한 책장의 비밀 대표 이미지

1장: 탐색 가능한 책장, CRUD를 넘어서

솔라는 완성된 BookShelf 프로젝트 화면을 띄워놓고도 어딘가 개운치 않은 표정으로 의자에 깊숙이 몸을 묻었다. 분명 요구사항에 맞춰 책을 등록하고(Create), 목록을 조회하며(Read), 정보를 수정하고(Update), 책장에서 빼는(Delete) 기능을 모두 구현했다. 스스로 생각하기에도 깔끔한 CRUD 애플리케이션이었다.

하지만 프로젝트 리뷰 문서에 적힌 한 문장이 계속 마음 한구석에 걸렸다.

“4차 BookShelf는 도서를 저장하는 데서 멈추지 않고, 검색어, 장르 필터, 태그/장르 기반 유사 도서 추천을 통해 책장을 탐색 가능한 UI로 만든다.”

“탐색 가능한 UI…?”

솔라는 혼잣말처럼 중얼거렸다. 자신이 만든 앱에는 분명 검색 기능도, 필터 기능도, 추천 기능도 들어있다. 하지만 그건 그저 CRUD라는 기본 뼈대 위에 추가된 부가 기능 목록처럼 느껴졌다. 검색, 필터, 추천. 각각의 기능일 뿐인데, 왜 이 문서는 그것들을 묶어 ‘탐색’이라는 거창한 이름으로 부르는 걸까.

그때, 조용히 책을 읽고 있던 루나가 솔라의 중얼거림을 들었는지 시선을 들어 솔라의 모니터를 바라보았다.

“꽤 그럴듯한 책장이네. 책이 한 스무 권쯤 되려나?”

“응. 테스트 데이터로 그 정도 넣어뒀어. 그런데 언니, 내가 만든 이 BookShelf 앱 말이야. 그냥 책 등록하고, 보고, 수정하고, 삭제하는 거 아냐? CRUD. 그런데 왜 프로젝트 설명에는 ‘탐색 가능한 UI’ 같은 말이 붙어있는지 모르겠어. 검색, 필터, 추천 기능이 있다고 해서 갑자기 앱의 정체성이 바뀌는 건 아니잖아.”

솔라의 목소리에는 기능 목록을 나열하는 것 이상의 의미를 찾지 못하겠다는 미묘한 불만이 섞여 있었다. 루나는 솔라의 질문에 바로 답하는 대신, 다른 질문을 던졌다.

“잠깐 기능 목록은 잊어보자, 솔라. 지금 이 책장에 책이 스무 권이 아니라, 오백 권이 들어있다고 상상해 봐. 그럼 이 화면을 처음 본 사용자는 뭘 가장 먼저 하고 싶을까?”

“오백 권?”

솔라는 순간 아찔한 기분으로 자신의 BookShelf 화면을 다시 바라보았다. 스무 권의 책 카드가 아기자기하게 배열된 지금의 모습과는 전혀 다른 풍경이 머릿속에 그려졌다. 끝없이 이어지는 스크롤. 수많은 책 표지들 사이에서 길을 잃은 사용자의 답답한 얼굴이 떠올랐다.

“일단… 스크롤만으로는 원하는 걸 찾기 힘들겠네. 만약 특정 책을 찾으러 온 사람이라면, 당연히 검색창에 제목이나 저자를 입력하겠지.”

“그렇지. 그럼 아주 뚜렷한 목표가 없는 사람은 어떨까? 그냥 ‘오늘은 재밌는 SF 소설이나 한번 읽어볼까’ 하는 마음으로 들어온 사람이라면?”

루나의 질문에 솔라는 자연스럽게 다음 행동을 떠올렸다.

“그렇다면… 아마 장르 목록에서 ‘SF’를 선택해서, 오백 권 중에서 SF 소설만 따로 모아 보려고 할 거야.”

“바로 그거야.”

루나는 짧게 말하며 솔라가 스스로 답을 찾도록 잠시 기다려주었다. 솔라는 방금 자신이 했던 두 가지 상상을 되짚어 보았다. 첫 번째는 ‘특정 책을 찾는’ 명확한 의도, 두 번째는 ‘특정 장르를 둘러보고 싶은’ 막연한 의도. 두 경우 모두 사용자는 단순히 화면에 주어진 목록을 수동적으로 읽는 데서 그치지 않았다. 검색창과 필터 버튼을 이용해 오백 권이라는 거대한 정보의 바다를 자신만의 기준으로 헤쳐나가고 있었다.

그 순간, 솔라는 무언가 깨달은 듯 작은 탄성을 내뱉었다.

“아…! 그러니까 검색이나 필터는 단순히 목록에서 몇 개를 거르거나 찾는 ‘부가 기능’이 아니구나. 책이 많아졌을 때, 사용자가 이 많은 책 속에서 길을 잃지 않고 자신만의 길을 만들어 나갈 수 있도록 돕는 ‘도구’인 셈이네.”

“맞아. 사용자는 더 이상 주어진 목록을 그냥 구경만 하는 관람객이 아니야. 직접 질문을 던지고, 범위를 좁히고, 원하는 것을 향해 움직이는 ‘탐험가’가 되는 거지.”

솔라는 다시 자신의 모니터 화면을 보았다. 이제 BookShelf는 단순히 데이터를 보여주는 정적인 전시장이 아니라, 사용자의 의도에 따라 모습이 바뀌는 역동적인 공간처럼 보이기 시작했다. CRUD가 데이터를 ‘관리’하기 위한 개발자의 관점이었다면, ‘탐색’은 데이터를 ‘경험’하게 하려는 사용자의 관점이었다. 두 관점의 차이가 ‘탐색 가능한 UI’라는 말의 진짜 의미였던 것이다.

“그럼 BookShelf의 진짜 완성은 책을 등록하고 목록으로 보여주는 데서 끝나는 게 아니었구나. 사용자가 이 책장을 자신만의 방식으로 헤쳐 나갈 수 있게 만드는 경험까지 포함하는 거였어.”

솔라는 프로젝트 리뷰 문서의 그 한 문장을 다시 떠올렸다. 이제는 그 문장이 당연하게 느껴졌다. 오히려 그 문장 덕분에 자신의 프로젝트가 가진 진짜 가치를 발견한 기분이었다.

생각이 정리되자 새로운 질문이 고개를 들었다. 탐색이 사용자가 자신만의 길을 만들어가는 여정이라면, 그 여정은 어디서부터 시작되는 걸까? 사용자가 마주하는 첫 화면, 책들이 늘어선 BookListPage에서 그 첫걸음은 어떻게 시작될까? 그곳은 단순히 책들을 나열하는 곳 이상의 의미를 품고 있을 것만 같았다.

2장: BookListPage: 원하는 책을 찾는 첫걸음

생각의 실마리를 잡은 솔라의 손가락이 분주하게 움직였다. 어제까지만 해도 그저 기능의 집합으로 보였던 BookShelf 프로젝트를 다시 열고, 책 목록 페이지인 BookListPage를 화면에 띄웠다. 빼곡하게 늘어선 책 카드들. 이제는 이것이 단순한 목록이 아니라, 누군가의 ‘탐색’이 시작될 광활한 우주처럼 느껴졌다. 솔라는 마우스 커서를 페이지 상단에 있는 검색창으로 옮겼다. 커서가 조용히 깜박이고 있었다.

이 작은 입력창이 바로 탐험의 첫 번째 관문일까? 솔라는 가상의 사용자가 되어보기로 했다. 책이 오백 권쯤 있다고 상상하며, 검색창에 ‘여행’이라는 단어를 입력하고 엔터 키를 눌렀다. 잠시 후, 화면에 있던 수많은 책 카드가 사라지고 ‘우주 여행 안내서’, ‘마음의 여행’처럼 제목에 ‘여행’이 포함된 책들만 남았다. 기술적으로는 당연한 결과였다. 전체 목록에서 입력된 문자열을 포함하는 항목만 남기는, 간단한 필터링 로직일 뿐이다.

“역시 그냥 문자열 검색 기능이네. 데이터베이스에서 특정 레코드를 조회하는 거랑 본질적으로 다를 게 없잖아. 이게 어떻게 ‘탐색’의 시작이라는 거지?”

솔라의 혼잣말에, 옆에서 솔라의 화면을 잠시 지켜보던 루나가 조용히 입을 열었다.

“사용자가 그 검색창에 ‘여행’이라는 글자를 입력하기까지, 어떤 마음이었을까?”

“마음?”

솔라는 예상치 못한 질문에 잠시 말문이 막혔다. 개발자의 관점에서 기능의 구현 방식만 생각하던 터라, 사용자의 마음 상태까지는 고려해본 적이 없었다.

“음… 아마 여행에 관한 책을 읽고 싶었겠지. 서점에 가서 ‘여행 서적 코너가 어디예요?’ 하고 묻는 거랑 비슷하지 않을까?”

“맞아. 그럼 사용자가 우리 책장에 들어와서 하는 행동을 두 가지로 나눠볼까?”

루나는 메모지와 펜을 가져와 가운데 선을 그었다. 그리고 왼쪽에는 ‘의도가 명확한 사용자’, 오른쪽에는 ‘의도가 막연한 사용자’라고 적었다.

“의도가 명’확’하다는 건… ‘나는 헤르만 헤세의 데미안을 찾고 싶어’처럼 제목과 저자를 정확히 아는 경우일 거야. 이 사람은 지체 없이 검색창에 ‘데미안’을 입력하겠지.”

솔라는 루나가 만든 표의 왼쪽 칸을 채워나가며 말했다. 수많은 책들 사이에서 단 하나의 목표를 향해 직선으로 나아가는 사용자의 모습이 그려졌다.

“그럼 그 사용자의 행동은 우리 시스템에 어떤 신호를 보내는 걸까? ‘전체 목록은 필요 없고, 당신이 입력한 조건과 일치하는 바로 그 항목을 보여주세요’라는 명확한 요구지.”

루나가 솔라의 말을 받았다. 그 순간 솔라는 무언가 머리를 스치는 것을 느꼈다.

“아…! 단순한 데이터 조회가 아니구나. 사용자가 스크롤을 멈추고 검색창에 키워드를 입력하는 그 행위 자체가, ‘나는 더 이상 수동적으로 정보를 훑어보지 않고, 능동적으로 원하는 것을 찾겠다’는 의지의 표명이구나. 탐색의 시작점이 맞는 거네. 사용자가 자신의 의도를 시스템에 처음으로 전달하는 순간이니까.”

방금 전까지만 해도 단순한 기능으로 보였던 검색창이 전혀 다르게 보이기 시작했다. 그것은 개발자가 만들어둔 기능 목록 중 하나가 아니었다. 정보의 바다에 뛰어든 사용자가 자신만의 항해를 시작하기 위해 처음으로 띄우는 깃발과도 같았다. 수많은 가능성 중에서 ‘나는 이것을 원한다’고 선언하며 범위를 극적으로 좁히는, 탐색의 가장 기본적이면서도 강력한 도구였다.

“그래. 검색 기능의 본질은, 방대한 정보 속에서 사용자가 목표로 하는 단 하나의 항목을 정확하게 짚어낼 수 있게 돕는 것. 말하자면 ‘특정 항목 찾기’라는 임무를 수행하는 거야.”

솔라는 자신이 막 깨달은 개념을 입 밖으로 내뱉으며 정리했다. 이로써 BookListPage에서 시작되는 탐색의 첫 번째 갈래는 분명해졌다.

하지만 곧바로 새로운 질문이 떠올랐다. 루나가 만든 표의 오른쪽 칸, ‘의도가 막연한 사용자’는 아직 빈칸으로 남아 있었다. ‘데미안’을 찾는 사람이 아니라, ‘오늘은 그냥 재밌는 판타지 소설이나 읽어볼까?’ 하고 막연한 기분으로 책장을 방문한 사람은 어떡하지? 그 사람에게 검색창은 그다지 유용한 도구가 아닐 것이다. 그들은 어떻게 자신만의 탐색을 시작할 수 있을까?

3장: BookListPage: 장르 필터로 취향에 맞는 책 좁히기

솔라의 시선은 루나가 가져다 놓은 메모지 위에 머물러 있었다. ‘의도가 명확한 사용자’와 ‘의도가 막연한 사용자’. 반으로 나뉜 표의 왼쪽 칸은 ‘검색창에 “데미안” 입력’이라는 명쾌한 행동으로 채워져 있었다. 하지만 오른쪽 칸은 텅 비어 있었다. 그 공백이 마치 솔라 자신의 막막한 마음을 비추는 것 같았다.

막연한 기분으로 책장을 방문한 사람. ‘오늘은 재밌는 판타지 소설이나 읽어볼까?’ 하는 느슨한 의도를 가진 사용자는 대체 무엇을 해야 할까. 그들에게 검색창은 아무런 쓸모가 없는 무기였다. 무엇을 검색해야 할지조차 모르기 때문이다. 솔라는 자신의 BookShelf 화면을 다시 쳐다보았다. 검색창 바로 옆에 자리 잡은 몇 개의 버튼이 눈에 들어왔다. [전체], [소설], [SF], [에세이]…

“이건… 그냥 장르별로 모아보는 거잖아. 이것도 결국 검색이랑 비슷한 거 아냐? ‘SF’라는 조건으로 목록을 줄여주는 거니까.”

솔라는 스스로 질문하고 스스로 답하며 고개를 갸웃거렸다. 전체 목록에서 일부를 숨기고 원하는 것만 보여준다는 점에서, 필터 기능은 검색과 본질적으로 같아 보였다. 둘 다 목록의 개수를 줄이는 역할을 할 뿐이었다. 루나는 말없이 솔라의 시선이 머무는 화면을 바라보다가, 텅 비어있는 메모지의 오른쪽 칸을 펜 끝으로 톡톡 두드렸다.

“그럼 그 사용자가 되어서 직접 한번 해볼까? 오백 권의 책이 펼쳐진 BookListPage에서 ‘SF 소설이나 읽어볼까’ 하는 마음으로.”

솔라는 루나의 말에 따라 마우스를 움직여 [SF] 버튼을 클릭했다. 다음 순간, 화면을 가득 채웠던 수많은 책 카드가 순식간에 사라지고, 서너 권의 SF 소설만이 화면에 남았다. ‘안드로이드는 전기양을 꿈꾸는가’, ‘듄’, ‘은하수를 여행하는 히치하이커를 위한 안내서’ 같은 책들이었다.

“자, 이제 뭐가 달라졌지? ‘데미안’을 검색했을 때와 지금, 화면에 나타난 결과의 성격이 어떻게 다른 것 같아?”

“결과의 성격?”

솔라는 잠시 생각에 잠겼다. ‘데미안’을 검색했을 때, 목표는 명확했다. 오직 ‘데미안’이라는 단 하나의 책. 다른 책들은 정답이 아닌 오답일 뿐이었다. 하지만 [SF] 버튼을 눌렀을 때 나타난 이 목록은 달랐다.

“‘데미안’을 검색했을 땐, 목록에 책이 딱 한 권만 나와야 성공이야. 다른 책이 섞여 있으면 실패한 검색이지. 하지만… 지금은 여러 권의 책이 나왔는데, 이게 실패라고 느껴지지 않아. 오히려 ‘이 중에서 뭘 읽어볼까?’ 하고 즐거운 고민을 하게 돼.”

그 말을 내뱉는 순간, 솔라는 중요한 차이점을 깨달았다. 검색은 단 하나의 정답을 ‘찾아내는’ 과정이었다. 반면 필터는 여러 가능성을 가진 후보들을 ‘모아놓고 둘러보는’ 과정이었다. 사용자의 의도 자체가 달랐다.

“아…! 알겠다. 검색은 사용자가 ‘나는 이것을 원한다’고 선언하며 탐색의 범위를 하나의 점으로 좁히는 행위였어. 하지만 필터는 달라. ‘나는 이 안에서 놀고 싶다’고 선언하면서, 탐색의 범위를 거대한 광장에서 아늑한 놀이터로 바꾸는 행위구나!”

필터링은 단순히 목록의 일부를 숨기는 소극적인 기능이 아니었다. 사용자가 자신의 취향과 관심사를 시스템에 알려주고, 그에 맞춰 탐색의 판을 새로 짜는 능동적인 행위였다. 오백 권이라는 망망대해 앞에서 막막해하던 사용자가 ‘SF’라는 자신만의 섬을 만들어 그 안에서 자유롭게 항해를 시작하는 것과 같았다. 숨겨진 게 아니라, 새로운 세계가 열린 것이다.

“그래, 바로 그거야. 검색이 명확한 목표를 향해 직선으로 달려가는 창이라면, 필터는 넓은 관심 영역 안에서 구불구불한 오솔길을 산책하도록 돕는 지도 같은 거지.”

루나가 표의 비어있던 오른쪽 칸에 글자를 채워 넣었다.

의도가 막연한 사용자: 장르 필터로 ‘관심 영역 좁히기’

솔라는 이제 BookListPage가 두 종류의 탐험가를 모두 환영하는 공간임을 이해했다. 명확한 보물 지도를 가진 탐험가와, 그저 흥미로운 풍경을 찾아 나선 탐험가. 책장은 두 사람 모두에게 각자에게 맞는 첫 번째 도구를 쥐여주고 있었다.

이제 두 갈래의 탐색 경로가 모두 선명해졌다. 솔라는 방금 필터로 찾아낸 SF 소설 중 하나인 ‘듄’의 책 카드를 클릭했다. 화면이 바뀌며 책의 상세 정보를 보여주는 BookDetailPage로 이동했다. 커다란 책 표지와 함께 줄거리, 저자 정보가 나타났다. 탐색의 한 단계가 끝난 것 같았다. 그런데, 정말 이게 끝일까? 검색이든 필터든, 마침내 마음에 드는 책을 하나 발견해서 상세 페이지까지 들어왔다면, 탐색의 여정은 여기서 멈추는 걸까? ‘듄’이 마음에 든 사용자가 ‘듄’과 비슷한 다른 SF 소설을 발견하고 싶어진다면, 다시 책 목록으로 돌아가서 처음부터 모든 걸 다시 시작해야 하는 걸까? 무언가 연결이 끊어진 느낌이 들었다. 탐색의 흐름은 여기서 막다른 길에 부딪히는 것만 같았다.

4장: BookDetailPage: 책 속에서 다음 책으로 연결되는 길

솔라의 손가락 끝에서 클릭 소리가 나자, 화면은 수많은 책이 늘어선 BookListPage에서 단 한 권의 책을 위한 공간, BookDetailPage로 바뀌었다. 화면 중앙에는 방금 그녀가 선택한 SF 소설 ‘듄’의 거대한 표지가 자리 잡고 있었다. 그 아래로는 책의 줄거리, 저자 소개, 출판일 같은 상세 정보가 빼곡하게 이어졌다. 탐색의 여정 끝에 마침내 목적지에 도착한 듯한, 완결된 느낌을 주는 화면이었다.

솔라는 마우스 휠을 천천히 아래로 굴렸다. 모든 정보가 끝나고 화면의 맨 아래, 하얀 여백에 다다르자 무언가 툭, 하고 끊어진 기분이 들었다. 마치 잘 닦인 길을 따라 걷다가 갑자기 나타난 벽에 부딪힌 기분이었다. 탐험은 여기서 끝이었다. 만약 ‘듄’이 마음에 든 사용자가 비슷한 분위기의 다른 책을 더 찾아보고 싶다면, 방법은 하나뿐이었다. ‘뒤로 가기’ 버튼을 눌러 책 목록으로 돌아간 뒤, 다시 필터를 조정하거나 다른 키워드로 검색을 시작하는 것. 애써 찾아낸 이 멋진 책과 다음 탐색 사이에는 아무런 연결고리가 없었다.

“결국 막다른 길이잖아.”

솔라는 자신도 모르게 실망 섞인 혼잣말을 내뱉었다.

“검색이나 필터로 애써 마음에 드는 책을 찾아서 여기까지 왔는데, 여정이 여기서 끝나버리네. 사용자는 다시 광활한 책 목록으로 돌아가서 처음부터 다시 시작해야 하는 거야? 탐색의 흐름이 완전히 끊어지는 느낌인데. 이건 그냥 책 정보를 보여주는 페이지, 그 이상도 이하도 아니야.”

솔라의 생각은 명확했다. 상세 페이지는 탐색의 종착역이었다. 옆에서 조용히 솔라의 화면을 보던 루나가 모니터의 한 부분을 가리켰다. 솔라가 방금 스크롤해 지나쳤던, 책의 기본 정보가 적힌 부분이었다.

“그 책에 대해 우리가 아는 정보가 줄거리 말고 또 뭐가 있지?”

“정보? 음… 여기, 장르는 ‘SF’고, 태그는 #스페이스 오페라, #정치, #모험… 이렇게 붙어있네.”

솔라는 화면에 적힌 글자를 무심코 읽어 내렸다. 그녀에게 그것은 그저 책을 분류하기 위한 몇 개의 꼬리표일 뿐이었다.

“좋아. 그럼 이제 이 책을 좋아하는 사람이 다음으로 읽을 만한 책을 추천해준다고 상상해 봐. 어떤 책을 권해주고 싶어? 이 책장에 꽂힌 오백 권의 책 중에서.”

루나의 질문에 솔라는 잠시 고민에 빠졌다. ‘듄’을 재미있게 읽었다면… 당연히 비슷한 분위기의 다른 SF 소설을 좋아할 것이다.

“일단 같은 SF 장르여야 할 거고, 이왕이면… ‘스페이스 오페라’ 태그가 붙은 다른 책을 찾아보면 실패할 확률이 적겠지? 비슷한 세계관이나 스케일을 가졌을 테니까.”

“바로 그거야. ‘장르’와 ‘태그’.”

루나의 말에 솔라는 다시 화면을 바라보았다. 장르: SF, 태그: #스페이스 오페라, #정치, #모험. 방금 전까지 그저 책을 설명하는 딱딱한 정보 조각으로 보였던 이 단어들이, 이제는 다른 책으로 건너갈 수 있는 비밀 통로의 열쇠처럼 보이기 시작했다. 이 정보들이야말로 ‘듄’과 다른 책 사이의 ‘유사성’을 증명하는 구체적인 단서였다.

그 순간, 솔라는 무언가를 깨닫고 다시 페이지를 아래로 스크롤했다. 아까는 그저 막다른 길이라고 생각했던 페이지의 끝. 하지만 그 하얀 여백 바로 위에, 그녀가 미처 주목하지 않았던 작은 섹션이 있었다.

[이런 책은 어떠세요?]

그 아래에는 익숙하지만 다른 책 세 권의 표지가 나란히 놓여 있었다. ‘하이페리온’, ‘삼체’… 모두 ‘듄’처럼 장대한 서사를 다룬 SF 소설들이었다. 이것은 단순한 광고가 아니었다. 시스템이 ‘듄’의 장르와 태그를 분석해서, 같은 속성을 공유하는 다른 책들을 조용히 제시하고 있었던 것이다.

“아…! 상세 페이지는 막다른 길이 아니었어. 새로운 길이 시작되는 교차로였구나!”

솔라는 작은 탄성을 질렀다. 사용자는 탐색을 멈출 필요가 없었다. 오히려 자신이 고른 책을 발판 삼아 더 깊고 넓은 세계로 나아갈 수 있었다. ‘듄’이라는 하나의 관심사를 확인한 사용자에게, 시스템이 “그렇다면 이런 책들도 당신의 취향일 수 있어요”라며 새로운 탐색의 지도를 건네주는 셈이었다. 흐름은 끊어지는 것이 아니라, 오히려 더 정교하게 뻗어 나가고 있었다.

“그래. 사용자가 직접 다음 탐색의 키워드를 고민할 필요가 없지. 현재 관심사를 바탕으로 시스템이 자연스럽게 다음 경로를 열어주니까. 이건 하나의 지점에서 시작되는 ‘탐색의 확장’인 셈이야.”

솔라는 자신이 발견한 개념을 정리하며 고개를 끄덕였다. 목록 페이지에서 범위를 좁혀 들어오는 탐색과는 또 다른, 하나의 지점에서 밖으로 뻗어나가는 새로운 종류의 탐색이었다. 책 한 권의 정보 페이지가 다음 탐험을 위한 베이스캠프가 되는 순간이었다.

이제 검색, 필터, 그리고 지금 발견한 유사 도서 추천까지. 세 가지 도구의 역할이 모두 명확해졌다. 하지만 솔라의 머릿속에는 마지막 퍼즐 조각이 남아 있었다. 넓은 목록에서 시작하는 BookListPage의 두 가지 탐색 방법(검색과 필터), 그리고 하나의 책에서 뻗어나가는 BookDetailPage의 탐색 방법(추천). 이 세 가지는 정말 별개의 기능일까? 아니면 사용자가 책장을 헤쳐나가는 하나의 거대한 여정 속에서 서로 맞물려 돌아가는 톱니바퀴 같은 것일까? 이 기능들이 어떻게 연결되어 하나의 끊김 없는 ‘탐색 경험’이라는 흐름을 만들어내는지, 그 전체적인 그림이 궁금해졌다.

5장: BookShelf의 탐색 경험: 기능들의 유기적 연결

솔라의 책상 위에는 그녀가 지난 며칠간의 깨달음을 정리한 메모지가 놓여 있었다. 세 장의 작은 카드였다. 첫 번째 카드에는 ‘특정 항목 찾기 (검색)’, 두 번째에는 ‘관심 영역 좁히기 (필터)’, 그리고 세 번째에는 ‘유사성 기반 탐색 확장 (추천)’이라고 적혀 있었다. 명확한 목표를 가진 사용자를 위한 창, 막연한 기분으로 둘러보는 사용자를 위한 지도, 그리고 하나의 관심사에서 다음 관심사로 건너가는 다리. 각 도구의 역할은 이제 선명하게 이해되었다.

하지만 솔라는 카드를 나란히 늘어놓고 보면서도 어딘가 해결되지 않은 답답함을 느꼈다. 마치 잘 만든 부품 세 개를 손에 들고도 완성된 기계의 작동 방식을 모르는 기분이었다. 각각은 훌륭한 도구지만, 따로따로 흩어져 있었다. 사용자는 정말 이 세 가지 도구를 별개의 기능으로 인식하고 따로따로 사용하는 걸까? 아니면 이 모든 것이 자신도 모르는 사이 하나의 거대한 흐름으로 연결되는 걸까? 솔라는 손가락으로 카드 사이의 빈 공간을 톡톡 건드렸다. 연결, 흐름. 그게 마지막 퍼즐 조각이었다.

그때 조용히 솔라를 지켜보던 루나가 책상 위 카드들을 잠시 바라보더니, 입을 열었다.

“그럼, 가상의 사용자를 한 명 만들어서 그 사람의 하루를 따라가 볼까? 그 사람이 우리 책장을 어떻게 여행하는지.”

루나의 제안은 마치 역할극 같았다. 솔라는 잠시 생각하더니, 구체적인 인물을 떠올려냈다.

“좋아. 이름은 ‘김소라’. 오늘 저녁에 읽을 만한 책을 찾고 있어. 요즘 유행하는 소설보다는, 고전적인 SF 명작이 읽고 싶다는 막연한 생각을 가지고 있지.”

“좋은 출발이야. 그럼 김소라 씨는 가장 먼저 뭘 할까?”

솔라는 김소라가 되어 BookShelf 앱을 머릿속으로 실행했다. 눈앞에 펼쳐진 것은 수백 권의 책이 뒤섞인 BookListPage. 여기서 ‘고전 SF’를 어떻게 찾을까?

“일단 고전 명작의 제목을 정확히 아는 건 아니니까, 검색창은 소용없겠네. 대신 [SF] 장르 필터 버튼을 누를 거야. 그러면 화면에 있던 수많은 책이 사라지고, SF 소설 스무 권 정도만 남겠지.”

솔라는 두 번째 카드, ‘관심 영역 좁히기 (필터)’를 앞으로 쓱 밀었다. 여행의 첫 단계였다.

“좋아. 이제 스무 권의 SF 소설 목록이 있어. 그 다음은?”

“스무 권 정도면 스크롤하면서 훑어볼 만해. 책 표지들을 보다가 ‘안드로이드는 전기양을 꿈꾸는가’라는 제목에 흥미가 생겨서 책 카드를 클릭하겠지. 그럼 책 정보를 자세히 볼 수 있는 BookDetailPage로 이동할 거야.”

이것은 필터나 검색 같은 뚜렷한 도구는 아니었지만, 좁혀진 목록 안에서 사용자가 직접 선택하는 중요한 과정이었다. 필터를 통해 만들어진 ‘놀이터’ 안에서 마음에 드는 ‘놀이기구’를 고르는 순간.

“이제 김소라 씨는 ‘안드로이드…’ 상세 페이지에 도착했어. 줄거리도 마음에 들고, 작가 정보도 흥미로워. 그럼 탐색은 여기서 끝일까?”

“아니!”

솔라는 확신에 차서 외쳤다. 바로 어제 막다른 길이라고 생각했다가 새로운 교차로임을 깨달았던 바로 그 지점이었다.

“페이지 맨 아래로 내려가면 ‘이런 책은 어떠세요?’라는 추천 목록이 나타날 거야. 시스템은 이 책의 장르가 ‘SF’이고, 태그가 ‘#철학’, ‘#디스토피아’라는 걸 아니까, 비슷한 태그를 가진 ‘멋진 신세계’나 ‘1984’ 같은 책을 보여주겠지. 김소라 씨는 ‘아, 맞아! 나 이런 분위기의 책 좋아했지!’ 하면서 ‘1984’를 클릭해서 또 다른 책의 세계로 넘어갈 거야.”

솔라는 세 번째 카드, ‘유사성 기반 탐색 확장 (추천)’을 첫 번째 카드 옆으로 가져와 붙였다. 그 순간, 흩어져 있던 세 개의 카드가 하나의 경로로 이어졌다.

[SF] 필터 클릭 (BookListPage) → 목록에서 책 선택 (BookListPage) → 상세 정보 확인 (BookDetailPage) → 추천 도서로 이동 (BookDetailPage).

“아…”

솔라는 나지막이 탄성을 내뱉었다. 그녀가 머릿속으로 시뮬레이션한 김소라 씨의 여정에는 검색, 필터, 추천 기능이 따로따로 등장하지 않았다. ‘고전 SF를 읽고 싶다’는 막연한 의도가 필터를 통해 구체화되고, 구체화된 선택이 추천을 통해 새로운 가능성으로 확장되었다. 각 기능은 서로의 결과를 입력값으로 삼아 유기적으로 작동하는 하나의 거대한 흐름, 즉 ‘사용자 경험’ 그 자체였다. 목록에서 상세 페이지로, 상세 페이지에서 또 다른 상세 페이지로. 사용자는 기능의 이름을 의식할 필요조차 없이, 그저 자신의 관심사를 따라 물 흐르듯 움직일 뿐이었다.

“검색, 필터, 추천은 독립된 기능 목록이 아니었어. 사용자의 다양한 의도를 받아내고, 그 여정을 끊김 없이 다음 단계로 안내하는 시스템이었구나. 책의 개수가 많아질수록 사용자는 자연스럽게 찾고(검색), 좁히고(필터), 따라가게(추천) 되는데, 우리 앱은 바로 그 흐름을 디자인한 거였어.”

솔라는 비로소 프로젝트 리뷰 문서에 적혀 있던 ‘탐색 가능한 UI’라는 말의 무게를 온전히 이해했다. 그것은 단순히 기능의 합이 아니라, 사용자의 의도와 데이터가 만나는 모든 접점에서 일어나는 상호작용의 총합이었다.

생각을 정리한 솔라는 새로운 문서를 열었다. 만약 자신이 ‘BookShelf’와 비슷한, 영화 추천 앱을 새로 기획한다면 어떻게 시작할까? 예전 같았으면 ‘기능 목록’이라는 제목 아래 ‘1. 영화 등록(Create), 2. 영화 목록(Read)… 5. 영화 검색, 6. 장르 필터’ 라고 적었을 것이다.

하지만 이제 솔라는 달랐다. 그녀는 문서의 제목을 ‘사용자 탐색 흐름 설계’라고 적었다. 그리고 기능 목록 대신, 두 명의 가상 사용자가 겪을 여정을 먼저 그리기 시작했다.

  1. 사용자 A (목표 명확): ‘톰 행크스가 나온 90년대 영화’를 보고 싶어 함 → 검색창에 ‘톰 행크스’ 입력 → ListPage에서 필터로 ‘90년대’ 적용 → ‘포레스트 검프’ 선택 → DetailPage에서 감독 정보 확인 → 감독의 다른 영화 ‘캐스트 어웨이’ 추천으로 연결.
  2. 사용자 B (목표 막연): ‘오늘 밤 가볍게 볼 코미디 영화’를 찾고 있음 → ListPage에서 ‘코미디’ 장르 필터 적용 → 상위에 노출된 ‘나 홀로 집에’ 선택 → DetailPage 확인 → ‘가족’, ‘크리스마스’ 태그 기반으로 유사 영화 ‘엘프’ 추천으로 연결.

솔라는 이 두 개의 흐름을 완성하고 나서야, 이 여정을 현실로 만들기 위해 어떤 기능들이 필요한지 역으로 도출해 나갔다. 검색, 필터, 추천은 더 이상 독립적인 기능이 아니었다. 사용자의 만족스러운 탐색 경험이라는 최종 목적지를 향해 유기적으로 연결된 이정표였다. 그녀는 이제 단순히 기능을 구현하는 개발자를 넘어, 사용자의 여정을 설계하는 경험 설계자의 첫걸음을 내디딘 참이었다.