Skip to content

[TMT-295] test: persistence 통합 테스트 환경 — Testcontainers PostGIS - #70

Open
mingdodev wants to merge 4 commits into
mainfrom
feat/TMT-295-persistence-testcontainers
Open

[TMT-295] test: persistence 통합 테스트 환경 — Testcontainers PostGIS#70
mingdodev wants to merge 4 commits into
mainfrom
feat/TMT-295-persistence-testcontainers

Conversation

@mingdodev

@mingdodev mingdodev commented Aug 30, 2026

Copy link
Copy Markdown
Member

Related Issue

Why

테스트 48개 중 실제 SQL이 도는 것이 하나도 없습니다. MockMvc standaloneSetup(17개)과 Fake 포트(12개)뿐이고 tmt-output-persistence에는 테스트 디렉터리 자체가 없었습니다.

두 가지는 Fake로 원리적으로 검증이 안 됩니다.

대상 왜 Fake로 안 되나 티켓
티켓 소비 동시성 조건부 UPDATE의 경합은 실제 트랜잭션·커밋 없이 재현되지 않는다. 조회 후 갱신하면 한 장이 두 번 쓰이는데 그 버그가 Fake에서는 드러나지 않는다 TMT-227
공간 쿼리 · 커서 EXPLAIN과 인덱스 선택은 실제 플래너가 필요하다. GiST를 타는지, 141k 실데이터에서 seq scan으로 떨어지는 조건이 있는지는 PostGIS 위에서만 알 수 있다 TMT-208

전 어댑터에 붙이지 않고 이 둘로 한정했습니다. 나머지는 대부분 단순 CRUD라 Fake로도 잡히고, 9/12까지 일정에 맞지 않습니다.

그리고 main 자동 배포를 끄면서(TMT-290) DB 경로가 도는 환경이 하나도 없어졌습니다. 이대로면 릴리즈 태그를 끊는 순간이 실 DB 경로의 첫 실행이자 운영 배포가 됩니다.

What

persistence 어댑터를 실 PostGIS 위에서 테스트할 수 있게 됩니다. 컨테이너·Flyway·슬라이스 설정이 갖춰졌고, 각 구현 티켓은 PersistenceTest를 상속해 테스트만 쓰면 됩니다.

스모크 4개로 바닥이 서 있는지 확인합니다 — PostGIS 확장 존재, Flyway 전량 적용, place.location SRID 4326, 롤백이 꺼져 커밋이 남는 것.

테스트 자체는 각 구현 티켓(TMT-208·227)에서 씁니다. 이 PR은 바닥만 깝니다.

How

컨테이너는 모듈 전체가 하나를 공유합니다. 테스트 클래스마다 @Container를 붙이면 클래스 수만큼 기동돼 CI 시간이 배로 늡니다. 이 형태여야 나중에 테스트를 늘려도 기동 비용이 다시 들지 않습니다.

트랜잭션 롤백을 끕니다 (Propagation.NOT_SUPPORTED). 롤백을 두면 커밋이 없어 동시성 경합을 재현할 수 없습니다. 대신 테스트 간 데이터를 지우지 않고, 각 테스트가 자기 데이터를 만들어 그 id로만 단언하는 규칙으로 격리합니다. TRUNCATE보다 강한 조건이고, 덕분에 컨테이너 재사용도 안전합니다.

스키마는 Flyway가 올립니다 — 마이그레이션 자체가 검증 대상이 됩니다.

이미지는 imresamu/postgis:16-3.4docker-compose.local.yml과 맞췄습니다. 버전이 갈리면 실행 계획이 달라집니다.

Notes for Reviewer

리뷰어가 로컬에서 돌리려면 Docker가 떠 있어야 합니다. ~/.testcontainers.propertiestestcontainers.reuse.enable=true를 넣으면 컨테이너를 재사용해 반복 실행이 빨라집니다(선택). README에 한 줄 넣었습니다.

CI 시간을 봐주세요. 도입 전 1분 39초2분 10초 → 도입 후 3분 11초로 1분1분 30초 늘었습니다. 컨테이너를 모듈당 하나만 띄우므로 테스트를 늘려도 이 비용은 다시 들지 않지만, 2분 이상 더 늘어나면 integrationTest 태스크 분리를 다시 이야기하는 게 좋겠습니다.

범위 밖으로 남긴 것QuerydslConfigJPAQueryFactory 빈을 등록하는데 레포에서 쓰는 코드가 한 곳도 없습니다(어댑터 14개가 전부 Spring Data, 공간 쿼리는 네이티브 @Query). 그래서 이 슬라이스에 넣지 않았습니다. 죽은 코드 정리는 별건이라 티켓을 따로 낼지 판단이 필요합니다.

의존성은 test-containers 번들로 묶어 persistence 모듈에만 넣었습니다. 공통 test 번들에 넣으면 전 모듈이 컨테이너 의존성을 끌어옵니다.

Prompt Log

컨테이너 수명·격리 방식·이미지·스키마 적재·슬라이스 범위를 하나씩 정하고 시작했습니다.

격리는 처음에 매 테스트 TRUNCATE를 제안했는데, 근거가 "@Transactional 롤백이 동시성 테스트와 안 맞는다"였고 그건 "테스트마다 고유 데이터" 방식에는 해당되지 않았습니다. 후자가 더 강한 원칙이라 그쪽으로 정하고 TRUNCATE는 뺐습니다.

슬라이스에 QueryDSL을 넣을지 물었다가, 실제로 쓰는 코드가 있는지 먼저 확인해서 없다는 것을 보고 제외했습니다.

Boot 4의 test 모듈 분리·JdbcTemplate 빈 미등록·PostGIS 이미지 호환 세 곳에서 막혔고, 다음에 같은 곳을 밟지 않도록 코드 주석으로 남겼습니다.

@wnsvy607 wnsvy607 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컨테이너 수명(모듈당 싱글턴 1회 기동)·롤백 끈 이유·Flyway 적재·이미지 버전(imresamu/postgis:16-3.4, 로컬 compose와 일치) 고정까지 설계 판단이 전부 옳고 근거가 주석에 잘 남았습니다. 다만 한 줄짜리 [must]가 하나 있습니다.

[must] support/PersistenceTestConfiguration.kt@SpringBootConfigurationsupport 패키지에 있는데, @DataJpaTest는 이 클래스를 테스트 클래스의 패키지에서 상위 방향으로만 탐색합니다(형제 패키지는 안 봄). 지금 스모크 테스트는 같은 패키지라 돌지만, 이 PR의 존재 이유인 TMT-208·227 어댑터 테스트를 관례대로 ...postgres.adapter에 만들면 Unable to find a @SpringBootConfiguration으로 기동 자체가 안 됩니다 — "PersistenceTest를 상속만 하면 된다"는 이 PR의 계약이 support 밖에서 깨져요. 수정은 한 줄: 설정 클래스를 모듈 루트 패키지(com.tmt.output.persistence.postgres)로 옮기거나, PersistenceTest@ContextConfiguration(classes = [PersistenceTestConfiguration::class])를 붙이면 됩니다.

[want] PersistenceTest.kt — persistence 모듈엔 test용 yml이 없어 슬라이스가 ddl-auto 기본값(none)으로 돕니다. 운영·로컬은 validate로 엔티티↔마이그레이션 드리프트에서 기동이 막히는데 정작 이 테스트는 그걸 못 잡아요. @DataJpaTest(properties = ["spring.jpa.hibernate.ddl-auto=validate"]) 한 줄이면 "마이그레이션이 검증 대상"이라는 이 PR의 의도가 엔티티 매핑까지 확장됩니다.

@mingdodev

Copy link
Copy Markdown
Member Author

둘 다 반영했습니다. [must] 지적이 정확했습니다 — 형제 패키지 문제를 놓쳤습니다.

[must] 설정 클래스를 모듈 루트로...postgres.support...postgres. 그리고 지적하신 시나리오를 실제로 테스트로 박아뒀습니다.

adapter/AdapterSliceContextTest를 추가했습니다. TMT-208·227 어댑터 테스트가 놓일 바로 그 패키지에서 컨텍스트가 뜨는지 확인합니다. 설정이 다시 형제 패키지로 가면 여기서 Unable to find a @SpringBootConfiguration으로 먼저 걸립니다 — 말씀대로 "상속만 하면 된다"가 이 PR의 계약이라, 그 계약을 테스트가 지키게 두는 게 맞다고 봤습니다.

[want] ddl-auto=validate — 붙였고 통과합니다. 엔티티와 마이그레이션이 실제로 맞다는 게 이걸로 처음 확인됐네요. "마이그레이션이 검증 대상"이라는 의도가 엔티티 매핑까지 넓어진다는 지적 그대로입니다.

./gradlew build 통과, 테스트 5개(스모크 4 + 어댑터 슬라이스 1).

@mingdodev
mingdodev requested a review from wnsvy607 September 1, 2026 12:50

@wnsvy607 wnsvy607 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

두 건 다 확인했습니다. 설정 클래스가 모듈 루트로 갔고, 무엇보다 adapter/AdapterSliceContextTest로 "형제 패키지에서도 뜬다"는 계약을 테스트가 지키게 한 것이 좋습니다 — 다음에 누가 support로 되돌리면 설명 없이 바로 걸립니다. ddl-auto=validate 고정도 들어가서 이제 엔티티↔마이그레이션 드리프트를 CI가 잡습니다. approve합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants