[실전 커뮤니티 05] 인기순의 함정

지난 편에서 추천과 비추천으로 표를 모으는 이야기를 했다. 이제 그 표로 목록의 순서를 정할 차례다. 표가 많은 글을 위로 올리는 인기순은 당연해 보이지만, 그 단순함 속에 커뮤니티를 서서히 죽이는 함정이 숨어 있다. 이번 편은 정렬 알고리즘, 그러니까 신선함과 공정함 사이에서 내가 어떻게 균형을 잡았는지에 대한 기록이다.


1. 단순 인기순의 함정

가장 먼저 떠오르는 정렬은 추천 수 높은 순이다. 표를 많이 받은 글이 위에 있으면 좋은 글이 잘 보이니 합리적으로 들린다. 그런데 이 방식을 실제로 적용하면 커뮤니티가 화석이 된다. 한 번 상단에 오른 인기 글은 계속 눈에 띄어 표를 더 받고, 그 표 덕에 계속 상단에 머문다. 부익부 빈익빈이 벌어지며 목록 맨 위는 몇 주째 같은 글이 자리를 지킨다.


이러면 새 글은 아무리 좋아도 빛을 못 본다. 갓 올라온 글은 표가 0인 상태로 시작하니, 이미 수백 표를 가진 고인 글을 이길 방법이 없다. 새 글이 안 보이니 사람들은 새 글을 안 쓰고, 안 쓰니 커뮤니티는 옛날 글만 맴돈다. 첫 편에서 강조한 신선함, 즉 매일 뭔가 새로운 일이 벌어지는 느낌이 사라진다. 단순 인기순은 과거를 박제하는 정렬이었다.


반대로 최신순만 쓰면 어떨까. 새 글이 항상 위에 오니 신선하긴 하다. 하지만 이번엔 품질이 사라진다. 방금 올라온 영양가 없는 글이 어제 올라온 명문을 밀어낸다. 좋은 글이 몇 시간 만에 아래로 쓸려 내려가 아무도 못 보게 된다. 순수 최신순은 품질을 버리고, 순수 인기순은 신선함을 버린다. 두 극단 모두 커뮤니티를 망쳤다.


그래서 좋은 정렬의 핵심 질문은 이것이었다. 어떻게 하면 표가 많은 글의 품질과, 새로 올라온 글의 신선함을 동시에 살릴 것인가. 이 둘은 언뜻 상충하는 것 같지만, 시간이라는 변수를 표와 함께 버무리면 절묘하게 화해시킬 수 있었다. 정렬 알고리즘의 세계는 바로 이 화해의 기술이었다.


2. 시간을 표와 버무리다

유명한 소셜 뉴스 사이트들의 정렬 방식을 뜯어보며 나는 큰 힌트를 얻었다. 핵심은 표에 시간을 결합하는 것이었다. 어떤 사이트는 글의 점수를 표의 수와 올라온 뒤 흐른 시간으로 계산하는데, 시간의 영향력을 표의 영향력보다 크게 뒀다. 그래서 아무리 표가 많아도 시간이 지나면 점수가 결국 0으로 떨어진다. 오래된 글은 자연스럽게 가라앉는 것이다.


또 다른 사이트는 로그를 써서 초반의 표를 뒤의 표보다 무겁게 쳤다. 처음 열 개의 표가 그다음 백 개의 표보다 순위에 더 큰 영향을 준다. 이러면 새 글이 초반에 몇 표만 받아도 빠르게 치고 올라올 수 있어, 신인에게 기회가 열린다. 동시에 최신성 보정이 있어 시간이 지나면 밀려난다. 표와 시간, 둘의 절묘한 조합이었다.


나도 이 아이디어를 내 커뮤니티에 맞게 변형했다. 개념적으로는 표의 점수에 시간이 지날수록 커지는 감쇠 항을 결합해, 새 글에 가산점을 주되 표가 쌓이면 오래 버티게 하는 식이었다. 하루 이내의 글에는 신선함 가산점을 주고, 그 안에서 표로 순위를 다투게 했다. 이러면 어제의 명문과 오늘의 새 글이 같은 무대에서 겨룰 수 있었다.


이 방식의 아름다움은 목록이 살아 움직인다는 데 있었다. 아침과 저녁의 상단이 달랐고, 매일 새로운 글이 정상에 오를 기회를 얻었다. 그러면서도 좋은 글은 하루 이틀은 버텨 충분히 노출됐다. 신선함과 품질이 시간이라는 매개로 화해한 것이다. 이 균형을 맞추고 나서야 목록에 생기가 돌기 시작했다. 사용자들은 매번 들어올 때마다 뭔가 달라져 있다는 느낌을 받았고, 그 느낌이야말로 커뮤니티가 살아 있다는 가장 확실한 증거였다. 고인 목록은 죽은 커뮤니티의 얼굴이고, 매일 바뀌는 목록은 살아 있는 커뮤니티의 맥박이었다.


3. 표 하나의 무게는 다르다

정렬을 파고들며 알게 된 또 하나의 통찰은, 모든 표가 같은 무게가 아니라는 점이다. 추천 하나만 받은 글은 그 하나가 우연일 수 있다. 표본이 너무 적어 신뢰할 수 없다. 반면 추천 백 개 중 아흔이 긍정인 글은 그 비율을 믿을 만하다. 어떤 커뮤니티는 이걸 통계적 신뢰 구간으로 처리한다. 표가 적을수록 점수를 보수적으로 깎아 함부로 위로 올리지 않는 것이다.


이 개념을 지난 편의 댓글 정렬에서 잠깐 언급했었다. 추천 열 개에 비추천 하나인 댓글이, 추천 마흔 개에 비추천 스무 개인 댓글보다 위로 갈 수 있는 이유가 이것이다. 단순히 추천에서 비추천을 뺀 순 점수로 줄 세우면 표본이 큰 쪽이 무조건 유리하지만, 비율의 신뢰도로 보면 다르다. 나는 이 발상을 정렬 곳곳에 녹였다.


이 접근의 장점은 소수의 몰표에 흔들리지 않는다는 것이다. 몇 사람이 작정하고 표를 몰아줘도, 표본이 작으면 신뢰 구간이 그 점수를 눌러준다. 다음 편들에서 다룰 여론 조작 방어와도 맞닿는 지점이다. 순위 계산 자체에 통계적 겸손을 심어두면, 어뷰징이 만들어낸 가짜 인기가 덜 먹힌다. 알고리즘이 스스로 방어의 일부가 되는 셈이다.


물론 이런 계산은 무겁다. 그래서 모든 글에 매번 복잡한 통계를 돌릴 수는 없었다. 나는 자주 바뀌는 최근 글에만 정교한 계산을 적용하고, 오래된 글은 단순하게 처리하는 식으로 비용을 아꼈다. 정확성과 성능 사이의 타협이었다. 이 타협 이야기가 바로 다음 절로 이어진다.


4. 정렬은 성능과 직결된다

정렬은 사용자 눈에는 순서일 뿐이지만, 개발자 눈에는 성능 문제다. 나는 이걸 아프게 배웠다. 처음에 나는 목록을 불러올 때마다 현재 시각과 글의 나이를 실시간으로 계산해 순위를 매겼다. 코드로는 깔끔했지만, 데이터베이스 입장에서는 재앙이었다. 계산식으로 정렬하니 미리 만들어둔 색인을 전혀 활용하지 못하고, 매번 글 전체를 훑어 임시로 줄 세웠다.


글이 수천 개로 늘자 이 방식은 눈에 띄게 느려졌다. 목록 한 번 여는 데 서버가 전체를 뒤지니, 방문자가 늘수록 부하가 폭증했다. 개념적으로는 정렬 기준이 저장된 값이 아니라 매번 계산되는 식이라, 인덱스를 못 타고 전체 스캔에 빠지는 전형적인 함정이었다. 예쁜 알고리즘이 현실의 데이터베이스에서 무릎을 꿇은 순간이었다.


해결의 방향은 계산을 미리 해두는 것이었다. 매 요청마다 순위를 계산하는 대신, 순위에 쓰이는 값을 미리 구해 저장해두고 그 저장된 값으로 정렬했다. 자주 바뀌는 값은 주기적으로 갱신하고, 그 갱신된 값에는 색인을 걸어 빠르게 정렬되게 했다. 실시간 정확성을 약간 포기하는 대신 성능을 크게 얻는 거래였다.


이 경험은 내게 중요한 교훈을 남겼다. 정렬 알고리즘을 설계할 때는 그것이 어떻게 계산되고 어떻게 저장되고 어떻게 조회되는지를 함께 생각해야 한다는 것이다. 종이 위에서 완벽한 공식도, 색인을 못 타면 서비스를 마비시킨다. 좋은 정렬은 공정할 뿐 아니라 빨라야 했다. 이 둘을 함께 잡는 것이 진짜 실력이었다.


5. 정렬은 가치관의 표현이다

정렬을 오래 다루며 깨달은 가장 큰 것은, 정렬이 곧 커뮤니티의 가치관이라는 사실이다. 무엇을 위로 올리느냐가 곧 우리가 무엇을 좋은 것으로 여기느냐다. 자극을 위로 올리면 자극이 좋은 것이 되고, 정성을 위로 올리면 정성이 좋은 것이 된다. 사람들은 상단에 오르는 글을 보며 어떤 글을 써야 하는지를 무의식적으로 학습한다.


그래서 나는 정렬을 단순한 기술 결정이 아니라 문화 결정으로 대했다. 논쟁만 부추기는 글이 상단을 독식하지 않도록, 어떤 사이트가 쓰는 것처럼 댓글이 지나치게 과열된 글에는 오히려 페널티를 주는 장치도 참고했다. 표가 아니라 싸움으로 뜬 글을 걸러내려는 시도였다. 정렬이 커뮤니티의 온도를 조절하는 손잡이가 될 수 있음을 그때 알았다.


또 나는 정렬에 운영자의 개입 여지를 조금 남겨뒀다. 알고리즘이 놓치는 좋은 글을 손으로 끌어올리거나, 알고리즘이 과하게 띄운 문제 글을 내릴 수 있게 했다. 완전 자동은 편하지만 맹목적이고, 완전 수동은 공정하지만 지친다. 자동을 기본으로 하되 사람의 판단이 최종 안전장치로 남는 구조가, 내가 찾은 균형이었다.


이제 목록은 신선하고 공정하게 굴러가기 시작했다. 다음으로 사람들을 다시 불러들일 장치가 필요했다. 내 글에 댓글이 달렸어, 내 댓글에 답이 왔어 하고 알려주는 알림이다. 다음 편에서는 알림 시스템을 다룬다. 무엇을 언제 알릴 것인가, 그 선택이 왜 커뮤니티의 생사를 가르는지 이야기하겠다.