여러 단계를 거치며 서사가 길거나, 결론이 뒤집혔거나, 장점만큼 포기한 점이 무게있는 설계 결정들을 모았습니다. [[기능별 기술 비교]] 페이지와 겹치는 항목은 이곳에만 담았습니다.


1. Follow 식별자(PK) 설계 재검토

배경: Follow는 조인 테이블이라 "관계 자체가 정체성"이라는 원칙으로 처음엔 FollowId 복합키(@EmbeddedId+Persistable) 사용을 확정했었다. 구현 직전 재검토하면서 이 결정이 뒤집혔다.

재검토 과정:

  1. 성능 근거 재검토 — "복합키=클러스터드 인덱스라 더 빠름"은 MySQL(InnoDB) 이야기이고, 이 프로젝트 DB는 PostgreSQL이라 힙(heap) 저장 구조라 PK도 평범한 B-tree 인덱스일 뿐 물리적 정렬 이점이 없다. 복합키의 성능 근거 자체가 이 프로젝트엔 성립하지 않는다.
  2. "아무도 안 쓰는 followId" 발견 — Follow.id(surrogate key)의 유일한 사용처가 FollowResponse.followId뿐이었고 그마저 어느 클라이언트도 소비하지 않았다(findById 호출 0건, 전부 findByFollowerAndArtist로만 조회). 코드가 실제로 쓰는 유일한 식별자는 이미 (follower_id, artist_id)였다.
  3. 근거가 성능에서 스타일로 슬쩍 바뀌었음을 자각 — followId가 쓸모없다는 사실을 복합키 근거로 쓰려다, 성능이 아니라 스타일/일관성 논리로 근거가 바뀐 것을 자각했다. unique constraint나 복합키나 DB 레벨 무결성 보장은 동일하고 성능 이득이 없다는 게 확인됐다.
  4. "그럼 단점이 없다는 거냐" 재검토 — Long PK 유지 옵션의 진짜 단점(인덱스 2개 유지)을 다시 짚었다.

옵션 비교

옵션 설명 인덱스 보일러플레이트 단점의 성격
1. Long PK 유지 Long id PK + (follower_id, artist_id) unique constraint 2개 없음 영구적(작지만 계속)
2. JPA 복합키 EmbeddedId FollowId + Persistable 1개 많음(FollowId 클래스, equals/hashCode, Persistable) 영구적(코드에 계속 남음), 실질 이득 없음
3. Follow를 JPA 엔티티에서 제외 (선택) Entity 없이 INSERT/DELETE/조회를 직접 쿼리로, PK는 DDL에서 (follower_id, artist_id) 직접 지정 1개 없음 일회성(패턴이 다르다는 이해 비용)

선택 이유: Follow는 수정(update)이 없고, 다른 엔티티가 FK로 참조하지도 않고, 객체 동일성/더티체킹 같은 JPA 기능이 전혀 필요 없는 순수 조인 테이블이라 JPA 엔티티 정체성 자체가 불필요하다는 결론에 도달했다.

구현 내용: schema.sqlfollows 테이블 DDL 직접 추가(복합 PK), Follow 클래스는 @Entity 제거하고 검증 로직만 남긴 순수 도메인 객체로 전환, FollowRepositoryJdbcTemplate 기반 네이티브 쿼리로 재작성, FollowResponse.followId 필드 제거.

알려진 제약 (문서화만, 코드로 안 고침): schema.sqlCREATE TABLE IF NOT EXISTS follows는 테이블이 이미 있으면 아무 동작도 안 한다 — 이 리팩터링 이전 버전(surrogate id PK)의 follows 테이블이 이미 있는 환경에서는 새 스키마로 자동 반영되지 않는다. 혼자 개발 중이고 배포된 실 데이터가 없어 Flyway 같은 마이그레이션 툴 도입은 과설계라 판단했다. 새 환경에 구버전 테이블이 있다면 DROP TABLE follows CASCADE;를 수동 실행해야 한다.


2. 게시판 캐시 직렬화 방식 결정

배경: Post JPA 엔티티(지연 로딩 프록시 포함)를 그대로 캐싱하려다 NotSerializableException이 발생해 PostResponse DTO로 캐싱하도록 수정했다. 이 과정에서 "Redis 저장 시 JSON 직렬화 방식을 어떻게 할까"라는 별도 결정 지점이 생겼다.

옵션 설명 장점 단점
1. 범용 타입 태그(unsafe) JSON에 자바 타입을 @class로 저장, enableUnsafeDefaultTyping() 캐시가 몇 개든 하나의 설정으로 처리 임의 클래스 역직렬화 기능이 영구적으로 켜져있음
2. 화이트리스트 타입 태그 enableDefaultTyping(PolymorphicTypeValidator)로 패키지 제한 1번 위험 줄임 여전히 태그 메커니즘이 켜져있고 화이트리스트 유지보수 필요
3. 캐시별 전용 직렬화 (선택) 타입 태그 없이 캐시 이름별로 정확한 타입(JacksonJsonRedisSerializer<List<PostResponse>>) 직접 지정 태그 기능 자체가 없음(보안 표면 0), 순수 JSON이라 디버깅 깔끔함 캐시가 늘 때마다 CacheConfig에 설정을 하나씩 추가해야 함

선택 이유: Follow 식별자 논의와 동일한 판단 기준 — 1·2번의 단점은 영구적 비용, 3번의 단점(설정 추가)은 그 순간만 치르는 일회성 비용이다.

인지하고 있는 확장성 제약: 캐시가 5개, 10개로 늘면 하나하나 타입 지정이 반복 작업이 될 수 있다 — 그때는 2번/1번 전환이 합리적일 수 있다. 지금은 캐시가 늘어날 때 다시 결정하기로 하고 미리 범용화하지 않는다.