bibimbap/.atp/work-session/20260629-151216/research/like-count-rootcause.md

7.9 KiB

phase agent agent_version generated_at concerns concerns_checked source_confidence workers_spawned
research research-advisor 2 2026-06-29T06:17:17Z
low source confidence — DDL(games.like_count default/NOT NULL, game_likes 제약)은 비권위 복원본(추론값). 운영 pg_dump 대조 전 권위 데이터로 승격 금지
true mixed 4

조사 결과 — 게임 "좋아요" 카운트 버그 근본 원인

주제

게임 상세페이지에서 좋아요를 누르면 토글은 되지만, explore(탐색) 페이지로 가면 좋아요 수가 올라가지 않는다. 코드에서 근본 원인을 확정한다.

포인트별 발견

포인트 1: 좋아요 토글 엔드포인트 — 서버 핸들러 자체가 없음

  • 경로: src/main/java/com/pandoli365/bibimbap/controller/api/GameController.java, .../mapper/GameLikesMapper.java, src/main/webapp/WEB-INF/views/game-detail.jsp
  • 요약: 게임 좋아요 토글을 처리하는 서버 엔드포인트가 존재하지 않는다. GameController 매핑은 /game/new(POST), /game/{id}(GET), /game/{id}/edit(GET/POST), /game/{id}(DELETE)뿐이며 좋아요용 POST/PUT/DELETE 핸들러가 없다. addGameLike/updateGameLike/getGameLike(GameLikesMapper) 매퍼 메서드는 src 전체에서 호출자 0건(dead code). 상세 JSP 의 좋아요 버튼은 완전히 클라이언트 사이드로, localStorage('bibimbap-game-liked') 만 토글하고 화면 카운트를 baseLikes + (liked ? 1 : 0)로 다시 그릴 뿐 어떤 서버 fetch/ajax 도 호출하지 않는다. (advisor 직접 확인: game-detail.jsp:1480-1485 클릭 핸들러는 setLiked()+syncLike()만 호출.)
  • 관련 파일:라인:
    • GameController.java:63,132,169,196,268 (매핑 전체 — 좋아요 토글 매핑 없음)
    • GameLikesMapper.java:22,34,43 (getGameLike/addGameLike(INSERT INTO game_likes)/updateGameLike — 호출자 0건)
    • game-detail.jsp:1401-1408 (setLiked → localStorage 기록), :1473-1478 (syncLike → 로컬 ±1), :1480-1485 (클릭 핸들러, 서버 요청 없음)
  • 신뢰도: 확인됨

포인트 1-b: GamesMapper 에 like_count 증감 UPDATE 메서드 부재

  • 요약: GamesMapper 에 incrementLikeCount/updateLikeCount/SET like_count 류 UPDATE 메서드가 존재하지 않는다(검색 0 hit). UPDATE games 문은 updateGame(L149-164)과 softDeleteGame(L192-200) 둘뿐이며 어느 것도 like_count를 SET 하지 않는다. like_count 는 SELECT 절에만 등장.
  • 관련 파일:라인: GamesMapper.java:149-164, 192-200; increment 류 검색 0 hit
  • 신뢰도: 확인됨

포인트 2: explore 목록 like 수 출처 — games.like_count 컬럼 직접 SELECT

  • 요약: 목록 조회 5개 메서드(getVisibleGames, searchVisibleGames, listVisibleKeyset, searchVisibleKeyset, searchGamesAdvanced) 전부 g.like_count AS likeCount(FROM games g)로 컬럼을 직접 SELECT한다. 어느 메서드도 game_likes 테이블을 COUNT 조인/서브쿼리로 집계하지 않는다. game_likes가 GamesMapper 에서 참조되는 곳은 게임 삭제용 deleteGameLikes(DELETE)뿐. sort='likes' 정렬도 ORDER BY g.like_count DESC, g.id DESC(L247)로 컬럼 사용.
  • 관련 파일:라인: GamesMapper.java:51 / 75 / 206 / 275 / 312 (각 메서드 SELECT 절의 g.like_count AS likeCount), :186-190 (deleteGameLikes)
  • 신뢰도: 확인됨

포인트 3: 상세페이지 like 수 출처 — 동일하게 games.like_count 컬럼

  • 요약: 상세 페이지도 explore 와 같은 출처(games.like_count 컬럼)를 읽는다. GET /game/{id} 핸들러가 gamesMapper.getGame(id)로 조회 → getGame SQL 은 g.like_count AS likeCount(games g JOIN users u 만, game_likes 미참조) → addGameModelgame.getLikeCount()로 꺼내 likeCount/likeCountFormatted 모델 속성으로 주입 → JSP 가 var baseLikes = ${likeCount};로 기준선 사용. 좋아요 클릭 후 갱신은 서버 재조회가 아니라 로컬 JS 증감(baseLikes ±1) 일 뿐이다.
  • 관련 파일:라인: GameController.java:132,135,138,304,309-310; GamesMapper.java:28 (getGame g.like_count AS likeCount, JOIN users u 만); game-detail.jsp:1248(초기 표시), :1386(baseLikes), :1477(로컬 증감)
  • 신뢰도: 확인됨

포인트 4: DDL/스키마

  • 경로: db/schema.sql (단일 부트스트랩 파일)
  • 요약: games.like_count 컬럼은 존재한다 — "like_count" integer DEFAULT 0 NOT NULL(schema.sql:95). game_likes 테이블은 id(bigint PK), game_id(bigint NOT NULL FK→games), user_key(varchar(200) NOT NULL), created_at(timestamptz DEFAULT now()) 4컬럼, (game_id, user_key) UNIQUE 제약 없음(검색 0 hit). 단, schema.sql 헤더(4-11행)에 games/game_likes 는 "비권위 복원본 — 매퍼/POJO 에서 역추출, 타입·기본값·제약은 추론값" 경고가 명시.
  • 관련 파일:라인: db/schema.sql:95(like_count), :253-261(game_likes), :4-11(비권위 경고)
  • 신뢰도: 컬럼 존재 여부=확인됨 / default·NOT NULL·UNIQUE부재=추정(비권위 복원본, 운영 pg_dump 미대조)

종합 판단

네 포인트가 일관되게 수렴한다. explore 와 상세는 동일한 출처(games.like_count 컬럼)를 읽으며, 둘 다 실시간 game_likes COUNT 집계를 쓰지 않는다. 따라서 버그는 "explore 와 상세의 출처가 다르다"가 아니라 좋아요 토글이 어떤 영속 저장소(DB)에도 쓰이지 않는다는 데 있다.

근본 원인 가설

1순위 (확정에 가까움): 좋아요는 DB 에 전혀 기록되지 않고 브라우저 localStorage 에만 저장된다.

  • 상세 페이지 좋아요 버튼은 서버 엔드포인트를 호출하지 않고 localStorage('bibimbap-game-liked')만 토글한다(game-detail.jsp:1480-1485, 1401-1408). 좋아요 토글 서버 핸들러가 애초에 없다(GameController 매핑 0개, GameLikesMapper 호출자 0건).
  • 그 결과 games.like_count 컬럼도, game_likes 행도 갱신되지 않는다(GamesMapper 에 increment UPDATE 부재). explore 목록은 games.like_count 컬럼을 읽으므로 영원히 올라가지 않는다.
  • 증상 정합: 상세에서 "토글되어 보이는" 것은 같은 브라우저의 localStorage + 로컬 ±1 표시(baseLikes+1)일 뿐이고, explore(다른 페이지/다른 사용자/새로고침 후 DB값)에는 반영되지 않는다. 사용자가 보고한 "상세는 올라가 보이는데 explore 는 안 올라간다"와 정확히 일치.
  • 근거: GameController.java:63/132/169/196/268, GameLikesMapper.java:22/34/43(호출자 0), GamesMapper increment 부재, game-detail.jsp:1473-1485, GamesMapper.java:51/75/206/275/312(explore=컬럼) + :28(상세=컬럼).

2순위 (1순위에 포섭되는 보조 가설): 좋아요 영속화 백엔드 배선이 미완성/dead code 로 남아있다.

  • GameLikesMapper(addGameLike/updateGameLike/getGameLike)와 games.like_count 컬럼은 좋아요를 DB 에 영속화하려던 설계 흔적이지만 컨트롤러/서비스 배선이 없어 dead code 다. 즉 기능이 "절반만 구현"된 상태. 만약 과거에 배선이 있었다면 회귀로 끊겼을 가능성(git 이력 확인은 본 조사 범위 밖).
  • 근거: GameLikesMapper.java:22/34/43(정의는 있으나 호출 0), GamesMapper.java:95(like_count 컬럼 존재) vs increment UPDATE 부재.

두 가설은 배타적이지 않다 — 1순위가 현상의 직접 원인이고, 2순위는 그 구조적 배경(미완성 영속화 계층)이다.

미해결

  • 과거에 좋아요 영속화 배선(서버 토글 엔드포인트)이 존재했다가 제거/회귀된 것인지 여부 — git 이력 추적은 본 조사 범위 밖. (필요 시 후속 조사)
  • games.like_count 의 운영 DB 실제 default/제약, game_likes (game_id,user_key) UNIQUE 존재 여부 — schema.sql 이 비권위 복원본이라 미확정. 운영 pg_dump 대조 전 권위 데이터로 승격 금지.
  • index.jsp:843, profile.jsp:509 도 game.getLikeCount()(=컬럼)를 표시하나, 이 두 화면의 like 표시 정합성은 본 조사 4포인트 밖(관찰만).