여러 단계를 거치며 서사가 길거나, 결론이 뒤집혔거나, 장점만큼 포기한 점이 무게있는 설계 결정들을 모았습니다. [[기능별 기술 비교]] 페이지와 겹치는 항목은 이곳에만 담았습니다.
배경: Follow는 조인 테이블이라 "관계 자체가 정체성"이라는 원칙으로 처음엔 FollowId 복합키(@EmbeddedId+Persistable) 사용을 확정했었다. 구현 직전 재검토하면서 이 결정이 뒤집혔다.
재검토 과정:
Follow.id(surrogate key)의 유일한 사용처가 FollowResponse.followId뿐이었고 그마저 어느 클라이언트도 소비하지 않았다(findById 호출 0건, 전부 findByFollowerAndArtist로만 조회). 코드가 실제로 쓰는 유일한 식별자는 이미 (follower_id, artist_id)였다.옵션 비교
| 옵션 | 설명 | 인덱스 | 보일러플레이트 | 단점의 성격 |
|---|---|---|---|---|
| 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.sql에 follows 테이블 DDL 직접 추가(복합 PK), Follow 클래스는 @Entity 제거하고 검증 로직만 남긴 순수 도메인 객체로 전환, FollowRepository를 JdbcTemplate 기반 네이티브 쿼리로 재작성, FollowResponse.followId 필드 제거.
알려진 제약 (문서화만, 코드로 안 고침): schema.sql의 CREATE TABLE IF NOT EXISTS follows는 테이블이 이미 있으면 아무 동작도 안 한다 — 이 리팩터링 이전 버전(surrogate id PK)의 follows 테이블이 이미 있는 환경에서는 새 스키마로 자동 반영되지 않는다. 혼자 개발 중이고 배포된 실 데이터가 없어 Flyway 같은 마이그레이션 툴 도입은 과설계라 판단했다. 새 환경에 구버전 테이블이 있다면 DROP TABLE follows CASCADE;를 수동 실행해야 한다.
배경: 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번 전환이 합리적일 수 있다. 지금은 캐시가 늘어날 때 다시 결정하기로 하고 미리 범용화하지 않는다.