본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

그림은 없던 게 아니라 묻혀 있었어

~12 min · measurement, ranking, diagnosis, product-design

Level 0미분류
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

원하는 게 이미 어디 있는지 물어봐

다들 원하던 처리가 도움이 안 된다는 걸 확립한 뒤, 질문이 "그림을 어떻게 만들지?"에서 "안 보여주고 있는 그림이 이미 있나?"로 바뀌었어. 다른 질의고, 조사를 끝낸 게 그거야.

출처별로 쪼개니까 답이 선명했어. 발행사한테서 직접 오는 피드는 거의 모든 항목에 이미지를 실어줬어. 열두 개는 사실상 백 퍼센트였고, 통신사 하나는 구십몇 퍼센트. 수집 서비스 검색 질의로 만든 출처는 거의 아무것도 안 실어줬고. 그림은 저장고에서 빠진 게 아니었어. 저장고 안에, 화면에 절대 못 닿는 행 위에 앉아 있었지.

순수 시간순 선반에서 좋은 행이 지는 이유

메커니즘은 물량이고, 뉴스의 특이점이 아니라 일반 성질로 적을 값어치가 있어. 넓은 검색 질의 출처는 하루에 수백 건을 내. 전문 매체는 수십 건을 내고. 엄격히 시간순인 선반에선 시끄러운 출처가 첫 화면 전체를 사실상 소유해. 기사가 의미 있게 더 좋거나 더 새로워서가 아니라 시간당 더 많이 뱉어서.

선반 하나가 모양을 완벽하게 보여줬어. 화면엔 질의 출처의 그림 없는 행 열두 개, 같은 선반에 전문 매체 둘의 그림 달린 기사 수십 개가 스크롤 한 번 아래 안 보인 채로. 시간순은 중립이 아냐. 발행 빈도로 가중하는 순위 함수고, 아무도 그걸 고르지 않았지.

해법은 공짜야

재료가 이미 있으니까 정정은 수집이 아니라 렌더링 시점 투영이야. 최근 창 안에서 그림 달린 기사가 보이는 영역의 바닥까지 채우게 해. 요청 0 건. 저장고에 뭐가 들어오는지도, 수집 라운드가 뭘 하는지도 안 바뀌어.

제약 둘이 이걸 정직하게 유지해. 최근 창이 낡은 그림을 끌어올리는 걸 막아. 오래된 이미지로 가득한 선반은 신선한 헤드라인 선반보다 나쁜 실패니까. 그리고 몇 행을 옮겼는지 보고해서, 선반이 조용히 순서를 바꾸는 대신 자기가 뭘 했는지 말할 수 있게 해.

나머지 절반 — 입력도 고쳐

렌더링 시점 해법은 있는 걸 재분배만 할 수 있으니까, 측정은 장보기 목록도 낳았어. 선반 몇 개는 그림 달린 출처가 아예 하나도 없었어. 출처 전부가 검색 질의였지. 어떤 투영도 걔들은 못 구하고, 답은 기술이 아니라 편집이야. 진짜 발행사 피드를 추가하되, 하나하나를 잘 되겠거니 넘겨짚는 대신 시스템 자기 파서로 재본 다음에 넣는 거.

생산자를 짓기 전에, 만들려는 걸 이미 버리고 있진 않은지 확인해. "X 가 부족해"랑 "X 가 있는데 안 내보내고 있어"는 밖에서 보면 똑같고 해법은 완전히 달라. 하나는 서브시스템 값이고 하나는 정렬 값이야.

Code

렌더링 시점 투영으로서의 끌어올림 — 요청 없고, 제한적이고, 스스로 보고하는·python
HEAD_CARDS = 12                  # the visible region
IMAGE_FLOOR = 8                  # how many of it may carry pictures
IMAGE_FLOOR_WINDOW_HOURS = 24.0  # never promote a stale picture


def apply_image_floor(rows: list[dict], limit: int) -> list[dict]:
    """Let pictures reach the head, without letting stale ones in.

    Zero requests: the pictures already exist on rows a purely
    chronological shelf buries, because a broad query source emits
    hundreds of items a day and a specialist publication a few dozen.
    """
    screen = min(HEAD_CARDS, limit)      # the SCREEN, not the page
    head = rows[:screen]
    have = sum(1 for r in head if r.get("image_url"))
    if have >= IMAGE_FLOOR:
        return rows                      # already fine: promote nothing

    now = datetime.now(timezone.utc)
    fresh = [r for r in rows
             if r.get("image_url")
             and _age_hours(r, now) <= IMAGE_FLOOR_WINDOW_HOURS][:IMAGE_FLOOR]
    if len(fresh) <= have:
        return rows                      # nothing fresher to promote

    picked = {r["id"] for r in fresh}
    keep = list(fresh)
    for row in head:                     # backfill the rest of the screen
        if len(keep) >= screen:
            break
        if row["id"] not in picked:
            keep.append(row)
            picked.add(row["id"])

    keep.sort(key=lambda r: _age_hours(r, now))   # still newest-first
    was_on_screen = {r["id"] for r in head}
    for row in keep:
        if row["id"] not in was_on_screen:
            row["promoted"] = True       # say what you moved
    return keep + rows[screen:]

External links

Exercise

네 시스템에서 시간순으로 정렬된 목록을 골라, 보이는 머리를 항목을 만들어내는 주체별로 묶어봐. 한 생산자가 머리 대부분을 소유하면, 걔가 다른 애들 대비 얼마나 뱉는지 계산해. 그리고 그 몫이 네가 실제로 변호할 편집 판단인지, 아니면 아무도 안 고른 처리량 차이일 뿐인지 정해.
Hint
묶기는 결과 집합 전체가 아니라 보이는 머리에 대고 해. 창을 크게 잡으면 몫이 그럴싸해 보여. 조용한 생산자도 결국 쌓이니까. 왜곡은 정확히 사람이 보는 영역에 몰려 있고, 그 영역은 어떤 집계 질의도 안 보고 있는 데야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.