550만개 문서를 1초안에 검색하는 DB를 구축한 썰

Kyle Jung
11 min readMar 31, 2024

업계에 더 뛰어난 기술적 업적을 이루신 많은 분들에 견주어 아직 초라하지만, 개인적으로 큰 도전이 됐던 구축과정 속에서 겪었던 여러가지 기술적인 난관들에 대해 개인적으로 정리할 겸, 누구에게 도움이 될 수 있을수도 있겠다 싶어 글을 씁니다.

나는 올해로 10년차 개발 경력에 접어든다, 그리고 이번 프로젝트는 단연코 내 경력 중에서 가장 어려웠다고 이야기 할 수 있을 것 같다.. 마치 눈떠보니 한달이 지나있는듯한 경험이었다고 해야할까.

우리가 어떤걸 하는지부터 알려야겠지... Cliwant는 AI로 RFP 자동 분석 솔루션을 만들어 낙후된 입찰 시장을 혁신하고자한다. 나라장터에는 하루에도 전국에서 수천건씩의 공고가 올라온다, 그리고 국내의 많은 회사들이 나라장터에 올라오는 입찰을 따서 매출을 올린다.

얼마나 낡았는지 보이는가..?

전국에 이렇게나 많이 사용하는데.. 왜 이 시장을 뒷밭침하는 시스템은 20여년전에 만들었던 그대로인가? 마치 낡은 호텔을 레노베이션 하는 것처럼 낙후된 입찰 시장을 개선하는게 Cliwant 의 목표이다. 입찰에는 모든 내용이 RFP (Request For Proposal) 라는 문서 안에 담겨있고, 때론 100페이지가 넘어간다.

실제로 나는 클라이원트를 시작하기 전에 낡은 호텔을 레노베이션하는 꿈을 꾸었다. 입구의 간판은 낡았고, 엘리베이터는 작동하지 않았고, 방은 먼지투성이었다. 꿈이 끝날쯤에 입구는 빛나는 간판으로 새단장했고 나름 매력적인 입구가 되었다. 하지만 여전히 속은 썩어있었다..

예수를 믿는 나한테 방향을 놓고 기도하는 중에, 이 꿈은 마치 이 길로 가라는 신호 같았다.

변화를 싫어하는 세상은 아직도 하이라이터와 펜을 들고 이 많은 문서를 프린트하여 이 부서 저 부서 돌아다니면서 정보를 얻으며 일한다. 우리가 이 프로젝트에 참여할 수 있을까? 이 프로젝트를 수행하기 위한 실적 요건을 충족하는가? 다양한 정보를 RFP 내에서 밑줄치면서 말이다.

얼마나 비효율적인가? 하루에도 공고는 수천건씩 올라오는데 이 작업을 매일 몇시간씩 할수밖에 없다는 것이다. 전국, 아니 전세계적으로 수많은 회사들이 아직 이렇게 일 하고 있는데, 이 문제만 해결해줄 수 있다면 얼마나 큰 영향력을 줄 수 있을까?

그래서 우리는 이 문제를 해결하고자 나섰고, 아래는 CTO로써 내가 겪었던 기술적 난관들에 대해서 풀어써본다. 읽는분들을 고려해서 최대한 어렵지 않게 써보겠다!

긴 텍스트를 초스피드로 검색하는 Fulltext Index, 그리고 MySQL의 한계

우리의 첫번째 미션이었다, 나라장터 자체 시스템은 제목 외 기본 필터로밖에 검색이 안되지만, 우리는 이 뿐만이 아닌 RFP 본문 내에서 추출한 세세한 항목까지 검색이 되는 엔진을 구축하는 것이었다.

검색이라는 분야에 문외안인 나는 문서에서 텍스트를 추출해서 SQL의 LIKE 연산자를 쓰면 되겠지 생각했지만 얼마 가지 않아 막다른 길에 다다르게 되었다.

SQL의 LIKE문은 텍스트 전체를 일일히 훑어가며 입력된 문자열을 찾는 것이다. 글자가 10만개가 넘는 RFP도 많은데, 그 많은 텍스트 내에서 입력한 문자열을 찾는다. 하루에도 수천개의 공고가 올라오는데, 과거 데이터까지 검색에 포함하려면 하면 몇십만건의 문서의 텍스트를 일일히 탐색해야한다.

1주일도 채 되지 않는 데이터를 검색하는데 10초가 넘어간다니. 원활한 서비스에서는 절대 있어서는 안되는 치명적인 점이었다. 그 때 Fulltext Index를 발견하게 되었고 텍스트가 담긴 MySQL 테이블 컬럼에 세팅해두니, 수만개의 문서에서 입력한 문자열이 담긴 텍스트를 금방 찾더라.

간단한게 원리를 설명하자면, 텍스트의 모든 글자를 쪼개서 맵핑 테이블을 만들어두는 것 과 같다.

예를들어 “백엔드 개발자 교육자료" 이라는 텍스트가 있으면.

백엔, 엔드, 백엔드, 개발, 발자, 개발자, 교육, 육자, 자료, 교육자, 육자료, 교육자료 로 매핑을 만들어서 “개발” 이라는 키워드를 검색하면 해당 문서로 바로 매핑해서 순식간에 찾는다.

20000개가량의 문서에서 “소프트웨어" 문자열이 담긴 프로젝트를 0.1초 내에 찾는다.

하지만 문제는 텍스트 검색만 한다고 되는게 아니었다. 공고 유형, 업로드 날짜, 사업 예산 범위 등 다양한 필터가 들어가야했다. MySQL 테이블에는 검색 속도 향상을 위해 다양한 Index를 설정할 수 있는데 Fulltext Index를 사용하게되면 다른 Index를 사용하지 않게 되어 쿼리 짜는데 심혈을 기울여야한다.

추가로 다양한 기준으로 정렬을 해야해야 했는데 Index가 먹히지 않는 정렬은 너무 오래 걸렸다. 공고가 쌓일수록 복합적인 요소로 API 호출이 Timeout에러가 발생하는 것을 발견했다. 서비스가 불가능해진 것이다.

그 때 당시 검색하는 문서 수량이 10만건정도 되었는데, 마감일이 지난 공고를 검색 범위에 포함하지 않는 방향으로 이야기가 되어서 검색 범위를 2만개가량으로 줄였다. 어차피 유저는 과거 공고에는 관심이 없을 것이라고 여겨졌기 때문이다.

왜 ElasticSearch를 사용하지 않았냐는 질문도 있을 것 같다. 첫번째로 내가 할줄 몰라서 처음부터 배워야했고, 두번째로 스타트업의 생명은 속도다. 팔리는 서비스를 빠르게 만드는게 중요하다고 여겨졌고, 몇달에 걸쳐 팔릴지 안팔릴지 모르는 서비스 구축하는 것은 옳지 않은 선택이었을 것이다.

그렇게 우리는 첫 서비스를 구축했다. 준호님께서 매출을 만들어주신 덕분에 다음 단계를 밟을 수 있게 되었다.

AECA DB— 20만개 문서가 0.1초만에 검색된다고?

우리 서비스는 고객들에게 관심을 받기 시작했고 팔리기 시작했다. 신기했다. 우리가 만든 서비스가 실제로 팔리다니… 고객들은 더 많은 것을 요구하기 시작했고, 과거 데이터까지 보고싶어하는 니즈도 생겼다. 하지만 하… 이거 어떻게 만드나. 불과 몇달치 데이터를 가지고 테스트 해봤을 때도 10초가 넘어갔는데.

그때 구원 투수로 승도님의 네트워킹을 통해 알게 된 AECA라는 검색 DB솔루션을 제공하는 스타트업과 연이 닿아 미팅을 하게 되었다. 위키피디아에 있는 모든 문서를 1초 내에 검색할 수 있다고 하셨다. 이게 가능하다고? 반신반의 하면서 20만건의 문서를 넣어서 테스트 해보았다.

??

내가 입력한 키워드가 본문에 담긴 문서들이 0.1 초만에 검색이 되더라.. 처음에 이게 실화인지 의심이 들어 여러개 키워드로 테스팅을 계속 해봤다. 계속 잘된다. 빠르다. 그리고 심지어 결과마저 정확하다. 신기한건 다양한 필터도 Fulltext 와 함께 Index걸 수 있게 되었다.

성능검증도 되었고, 데이터도 있고, 모든건 준비가 되었다. 이제 만들기만 하면 된다. 개발 시작하기 전, 어두워지는 하늘이 보이는 넓은 창가앞에서 잠시 쉬면서 핸드폰을 보고있는데 사용하는 성경앱에서 노티가 날라오더니 아래 말씀이었다.

내가 사망의 음침한 골짜기로 다닐찌라도 해를 두려워하지 않을 것은 주께서 나와 함께 하심이라 주의 지팡이와 막대기가 나를 안위하시나이다
시편: 23:4

매일 신경을 곤두세워 음성을 들으려다 보면 나한테 말씀하시는 것 같은 순간들이 있다. 위 말씀을 읽을 때 뭔가 마음속에 꽂히는게 느껴졌다. 이때는 몰랐다 이게 실제로 만드는게 얼마나 어려울지…

첫번째 난관 — 데이터 지옥

나는 과거 데이터 수집하고 가공하는건 쉽겠지, 하면서 안일하게 생각했었다. 하지만, 방대한 데이터를 수집하려다 보니 중간에 부하가 생겨서 서버가 꺼지는 일, 병렬로 처리하다보니 어디는 수집되고 어디는 수집되지 않는 일, 데이터 정합성이 맞지 않는 일… 다양하게 발생했다.

300만건의 공고, 550만건의 문서. 정확한 정보를 제공하고싶었다. 나는 몰랐지 얼마나 이게 복잡할지.

이거 맞추는게 겁나 빡셌다.

이 많은 데이터 사이에서 날짜와 카테고리별로 일일히 나라장터에서 올라온 공고 수랑 비교해가면서 숫자가 조금이라도 틀리면 이게 왜 틀릴까? 집요하게 찾아가보았다. 많은 데이터 속 좀 틀릴 수 있지.. 넘어가고자하는 유혹이 강했지만, 찾다보면 항상 이유는 발견되었다. 데이터가 이렇게 많을 경우, 처음 작업할 때 잘못 입력하면 돌이키기 며칠, 몇 주가 걸릴 수 있어서 처음부터 잘하는게 중요하다.

그렇게 데이터 검수하고, 고치고, 밤낮없이 1주일정도 걸렸던 것 같다.

두번째 난관 — 처리하는데 걸리는 어마어마한 시간

그렇게 나름 노력하여 데이터는 잘 준비가 되어서 AECA DB 에 올릴 준비가 되었다. (업로드하다가 중간에 또 안맞는걸 발견해서 하루를 다시 돌려야했던 일도 있었다). 이제 다 됐다고 생각했지만..

1초에 2개정도 올라간다..

아니.. 1초에 공고가 2개씩밖에 올라가지 않는 것이다..! 올려야되는 공고는 총 300만개. 이대로라면 다 업로드하는데 3주가 걸린다.. 이러다가는 데드라인은 커녕, 너무 늦어질 것 같았다. 그래서 업로드해야하는 데이터를 7트랙으로 쪼개서 업로드 했다. 그러자 월 100만원인 서버가 뻗었다… 해결책으로 무식하게 자원을 때려박아 월 400만원 mdl.8xlarge (128GB RAM, 32개 CPU)를 써서 돌렸다.

그래도 다 올리는데 4일~5일정도 걸리더라.

이게 왜 이렇게 오래걸리는건가 봤더니, 문서마다 Fulltext Index를 생성하기 때문이었다. 다양한 필터를 걸기 위해 공고의 모든 필드마다 Index를 생성하기때문에 오래 걸렸던 것, ngram parser를 사용하면 문서의 모든 텍스트를 2개~4개의 단어로 쪼개서 그거에 맞는 Index를 만든다. 따라서 해당 방식을 사용하면 배보다 배꼽이 더 크다. 이게 얼마나 크냐면, 서버 용량의 어림잡아 80%는 인덱스가 잡아먹고 있는 것을 확인했다. 데이터 자체는 300GB이지만 실제 잡아먹는 용량이 대략 1.8TB 였다.

안정적으로 잘 올라가는 것을 확인하고 나는 쿼리 튜닝에 들어갔다

세번째 난관 — 검색 쿼리의 속도

시간이 지나니 데이터는 잘 올라갔지만 원하는 만큼 쿼리의 속도가 나오지 않았다. 대부분의 쿼리는 잘 되지만, 다양한 필터 조건과 정렬 조건에 다 서비스 가능할정도의 속도가 나와야하지만 일부 쿼리에는 속도가 나오지 않았다.

AECA DB의 검색엔진의 기본 정렬은 입력한 키워드의 정확도 기준으로 정렬되서 나왔다, 즉 단적으로 “소프트웨어", “용역" 라는 키워드를 입력했으면 두 키워드가 가장 많이 담긴 결과 순으로 점수를 매겨 정렬이 되어 나타난다. 문제는 우리 고객들은 날짜별로 정렬해서 보고싶은 니즈가 컸다는 것이고, 최악의 검색 쿼리의 경우 300만개의 공고를 다 정렬한 후 결과를 뱉어야한다는 것이었다. 그런 경우 3분이 넘어갈 때도 있었다.

사실 이 순간 약간 비상이었다. 이 기능이 안되면 고객이 필요하는 큰 기능 하나를 제공하지 못하게 되는 것이었고, 검색 DB솔루션을 도입하는데 큰 메리트가 떨어진 셈이었다. 그래서 이 문제를 두고 AECA 대표님께 전화드려서 회의를 여러차례 잡았었고, 감사하게도 이 문제의 심각성을 인지한 AECA 팀은 신속하게 대처해주었다.

AECA는 질의에 빠르게 답장해주었고 패치도 빨리 해주었다.

AECA 기술팀은 빠르게 원하는 필드 값을 기준으로 점수를 매길 수 있도록 해주는 기능을 제공해주었다. 덕분에 지금은 어떤 쿼리를 입력 하더라도 빠르게 정렬하는 검색 시스템을 구축하게 되었다.

그렇게 모든 난관은 헤쳐나가게 되었고, 구축이 완료된 2주가 지난 이 시점. 서비스는 나름 안정적으로 운영되고 있다.

마치며

구축하던 한달동안은 찜질방이 마치 내 집같이 편해졌던적도 있다, 그랬던 만큼 고객들이 잘 써주기만 한다면 정말 기쁠 것 같다.

그리고.. 이번 프로젝트를 진행하면서 과거에 함께 했던 동료들이 많이 생각났다, 지난 날들 거쳐왔던 과정 속에서 겪었던 모든 경험을 끌어모아 이번 프로젝트를 수행할 수 있었고, 모두 멘션은 못하지만 성장에 도움 주신 모두에게 감사한 마음이 들었다!

아직 크게 이룬 것도 아니고, 앞으로 갈 길도 멀지만, 항상 내 안에서 잘 마칠거라는 확신 주시며 함께 계셨고, 앞으로도 반드시 함께 하실 하나님께 감사와 영광을 드립니다.

Cliwant 는 성장에 목마른 백엔드 개발자를 찾고 있습니다.

예비창업을 포함해 10개월정도가 지난 시점, 클라이원트의 백엔드가 빠르게 커졌음을 느낍니다. 지금 내부에서 돌아가고있는 수집 모듈, ETL, API를 포함해서 6개이상의 시스템이 있고 DB도 스펙이 꽤 커져서, 안정성, 백업, 등 신경 써야하는 요소들이 많아져서 날이 갈수록 관리가 어려워지고 있습니다.

앞으로 더 추가될 모듈들도 많이 있을 것이어서, 다음은 Kubernetes를 도입해서 내부 시스템을 효과적으로 관리해보고자 합니다.

추가로 최근에 OpenAI 대회에서 수상하게 되어서 LLM을 더 적극적으로 도입할 수 있는 발판도 마련하였습니다.

성장에 목마른 분, 최신 기술로 낙후된 이 입찰 시장을 함께 혁신해 나가는 미션을 함께 하고 싶은 분이시라면 링크드인 통해 연락 주시면 감사하겠습니다!

--

--

Kyle Jung

Software Engineer who aims to make the world a better place by helping people discover their potential