↓ 본문으로 건너뛰기

Content Ranking Metrics SEO

콘텐츠 랭킹 알고리즘이란: 조회수와 좋아요 너머의 신호들

콘텐츠 랭킹 알고리즘이 하는 일, 조회수와 좋아요만으로 순위를 매기면 인기 있는 것만 더 인기 있어지는 이유, 그리고 하나의 점수에 섞어 넣을 만한 신호들을 정리했습니다.

콘텐츠 랭킹 알고리즘이란: 조회수와 좋아요 너머의 신호들

콘텐츠 랭킹 알고리즘은 피드나 검색 결과, ‘인기 글’ 목록에 무엇을 어떤 순서로 보여줄지 정하는 장치입니다. 항목마다 여러 신호(사람들이 어떻게 반응하는지, 콘텐츠 자체가 어떤지, 얼마나 새롭고 관련성이 있는지)로 점수를 매기고, 그 점수대로 정렬하죠. 그런데 조회수와 좋아요만으로 순위를 매기면 ‘부익부’ 루프가 생깁니다. 위에 있는 글이 눈에 띄니 조회수와 좋아요가 더 쌓이고, 그래서 계속 위에 머무는 식입니다.

이게 조회수와 좋아요의 뻔한 결함입니다. 이미 인기 있는 것에만 보상을 준다는 거죠. 새 콘텐츠는 피드 바닥에서 굶고, 플랫폼은 서서히 재방송 채널이 되어 갑니다. 피드 랭킹은 지금 다니는 회사에서도 은근히 어려운 문제라서, 어떤 다른 신호를 믿을 만한지 꽤 오래 고민해 왔습니다.

클릭 말고 행동을 본다
#

명시적 피드백은 양도 적고 편향돼 있습니다. 반면 행동 데이터는 넘칩니다. 체류 시간과 스크롤 깊이를 보면 실제로 읽었는지, 첫 문단에서 이탈했는지 알 수 있죠. 재방문은 더 강력합니다. 같은 글에 다시 돌아온다는 건 어떤 좋아요보다 확실한 지지입니다. 반대로 빠른 이탈은 유용한 부정 신호입니다. 콘텐츠가 약속한 걸 독자가 받지 못했다는 뜻이니까요.

콘텐츠 자체에 박혀 있는 신호
#

좋은 콘텐츠가 처음부터 좋아요를 쓸어 담는 건 아니지만, 품질은 다른 방식으로 드러납니다. 하나는 새로움입니다. 플랫폼에서 아직 다뤄지지 않은 영역을 다루고 있는가. 또 하나는 구조인데, 깔끔한 포맷과 멀쩡한 문법, 명료함은 인정하고 싶지 않을 만큼 품질과 상관관계가 높습니다. 작성자의 이력도 괜찮은 사전 정보가 됩니다. 다만 좋아요 수가 아니라 과거 글이 독자를 얼마나 붙잡았는지로 판단해야 공정하겠죠. 초기 댓글도 도움이 됩니다. 진심 어린 긍정 댓글 세 개가 반사적인 추천 서른 개를 이깁니다. 여기에 의미 분석을 곁들이면 플랫폼에 비어 있던 구멍을 메워 주는 콘텐츠도 찾아낼 수 있습니다.

바깥에서 신호를 빌려온다
#

지금 무엇이 통하는지는 바깥 인터넷이 알려줍니다. 검색 엔진의 트렌딩 키워드와 맞아떨어지는 콘텐츠는 태생적으로 시의성이 있고, 뜨는 주제를 빠르게 다룬 글에는 부스트를 줄 가치가 있습니다. 트위터나 레딧에서 화제인 것은 우리 사용자들이 다음에 원할 것의 예고편인 경우가 많아서, 예측을 조금만 얹으면 파도가 오기 전에 해당하는 새 콘텐츠를 밀어줄 수 있습니다. 자체 검색창도 저평가돼 있습니다. 사용자들이 어떤 주제를 계속 검색하고 있거나, 어떤 글이 검색 엔진에서 꾸준히 유입을 끌어오고 있다면, 그건 추측할 필요도 없는 시의성의 증거입니다.

신호를 하나의 점수로 섞기
#

아이디어를 파이썬 몇 줄로 옮기면 이렇습니다. 설명을 위한 예시일 뿐, 실제 운영 시스템은 아닙니다. 각 신호를 0~1 범위로 정규화해서 원래 숫자가 큰 신호가 나머지를 묻어 버리지 않게 하고, 가중합으로 점수 하나를 만들고, 시간 감쇠로 새 글도 지난달 인기 글과 겨뤄 볼 기회를 줍니다.

WEIGHTS = {"read_depth": 0.3, "revisits": 0.25, "quality": 0.25, "early_comments": 0.2}
HALF_LIFE_HOURS = 48  # a post loses half its score every two days

def normalise(values):
    lo, hi = min(values), max(values)
    return [(v - lo) / (hi - lo) if hi > lo else 0.0 for v in values]

def rank(items):
    """items: dicts holding raw signal values plus "age_hours"."""
    norm = {name: normalise([item[name] for item in items]) for name in WEIGHTS}
    scores = []
    for i, item in enumerate(items):
        base = sum(weight * norm[name][i] for name, weight in WEIGHTS.items())
        scores.append(base * 0.5 ** (item["age_hours"] / HALF_LIFE_HOURS))
    order = sorted(range(len(items)), key=lambda i: scores[i], reverse=True)
    return [items[i] for i in order]

가중치는 아래에서 말할 테스트로 확인하기 전까지는 그냥 짐작입니다. 조회수와 좋아요를 넣어도 괜찮습니다. 점수의 전부가 아니라 여러 입력 중 하나가 될 뿐이니까요. 반감기는 오래된 글이 얼마나 빨리 가라앉을지를 정하는데, 이건 그 자체로 큰 주제라서 피드 랭킹 알고리즘은 오래된 글을 언제 묻어버릴까에 따로 정리해 뒀습니다. 실제 시스템은 가중치를 손으로 정하기보다 모델로 학습시키는 경우가 많지만, 모양은 같습니다. 신호 여러 개를 넣고, 숫자 하나를 뽑고, 정렬하는 거죠.

마지막엔 정직하게 테스트
#

신호가 아무리 영리해도 A/B 테스트는 필수입니다. 새 콘텐츠를 일부 사용자에게 노출하고 기존 콘텐츠와 인게이지먼트를 비교하는 거죠. 그리고 퍼지는 모양을 관찰합니다. 플랫폼 안팎의 공유, 언급, 임베드는 조회수 카운터보다 훨씬 많은 걸 말해 줍니다.

현실을 견뎌내는 단일 지표는 없습니다. 실제로 작동하는 랭킹은 행동, 콘텐츠 자체의 품질, 시의성, 정직한 실험을 섞은 것이고, 어느 신호에게도 최종 결정권을 주지 않습니다.