테스트에서 GIS 쿼리를 사용하기 위해 + 빠른 테스트를 위해

eora21·2024년 4월 4일
0

너나드리 개발기

목록 보기
6/8

일반적인 embedded h2로는 GIS 쿼리를 사용할 수 없습니다. 따라서 이를 해결하기 위해 고민한 흔적을 남기도록 하겠습니다.

embedded H2GIS

embedded h2에서 GIS가 먹히지 않는 문제를 해결하기 위해 H2GIS를 사용해 보았습니다.
먼저 build.gradle에 종속성을 작성하였습니다.

testRuntimeOnly 'org.orbisgis:h2gis:2.2.1'

하지만 종속성을 추가하는 것만으로는 GIS 쿼리를 동작시킬 수 없습니다. GIS 함수 사용 설정을 하기 위한 sql을 작성해야 합니다.

CREATE ALIAS IF NOT EXISTS H2GIS_SPATIAL FOR "org.h2gis.functions.factory.H2GISFunctions.load";
CALL H2GIS_SPATIAL();

그 후 properties에 아래와 같이 작성합니다.

spring.sql.init.schema-locations=classpath:sql/h2gis-setting.sql

테스트 코드를 동작시켜 봅니다. GIS 함수가 잘 동작되는 것을 확인했습니다.

하지만, MySQL에서 GIS 함수를 사용하셨다면 문제가 발생할 수 있습니다.

특정 함수간의 차이

If the distance is not negative and no strategies are specified, the function returns the geographic buffer of the Point in its SRS. The distance argument must be in the SRS distance unit (currently always meters).

MySQL은 SRID가 무엇이든간에 미터(meter) 단위로 ST_Buffer()를 측정합니다.
그러나 SRID 4326에서 H2GIS는 도(degree) 단위로 측정합니다.

SRID를 변경하지 않으면서 H2GIS 내부의 모든 연산을(혹은 ST_Buffer() 연산만이라도) 도 단위에서 미터 단위로 변경해야 테스트 환경을 일치시킬 수 있을텐데, 그러한 방법을 따로 찾지 못 했습니다.

허나 그러한 방법을 찾아서 적용했다 한들, 지금과 같은 DB간의 차이로 인한 테스트 결과 불일치는 언제든 발생할 수 있다는 생각이 들었습니다.

고민 끝에, 테스트 환경에서도 실제 운영 환경과 같은 DB를 사용해야 한다는 결론이 나왔습니다.
따라서 testContainers를 사용해보기로 했습니다.

testContainers 사용

testRuntimeOnly 'org.testcontainers:mysql:1.19.7'
testRuntimeOnly 'org.testcontainers:jdbc:1.19.7'
ALTER DATABASE test CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
spring.datasource.url=jdbc:tc:mysql:///test?characterEncoding=UTF-8&serverTimezone=UTC
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:sql/schema-test.sql
spring.jpa.hibernate.ddl-auto=create

테스트코드 실행 전 sql을 통해 한글이 깨지는 문제를 방지하였습니다.
버전 명시가 없을 경우 5버전대의 MySQL이 실행됩니다. amd 관련 예외도 발생하고, GIS 쿼리를 사용하려면 8버전 이상을 사용해야 해서 8.3.0을 선언하였습니다.

spring.datasource.url=jdbc:tc:mysql:8.3.0:///test?characterEncoding=UTF-8&serverTimezone=UTC
spring.jpa.hibernate.ddl-auto=create

버전을 올리면 위와 같은 설정으로도 잘 돌아갑니다.

spring.datasource.url=jdbc:tc:mysql:8.3.0:///test?TC_INITSCRIPT=file:src/test/resources/sql/mysql.sql&characterEncoding=UTF-8&serverTimezone=UTC

만약 hibernate의 자동 ddl이 걱정되시거나 내부적인 문제가 발생하신다면, TC_INITSCRIPT를 사용하여 직접 ddl을 명시해주셔도 됩니다.

하지만 도커 컨테이너가 생성되는 시간도 꽤나 오래 걸렸기에 이를 타파할 방법을 고민했습니다.

local 환경과 release 환경을 나누기

도커 컨테이너가 매번 생성되는 것을 막기 위해 위와 같이 testcontainers:jdbc 설정 및 각종 테스트에서 @SpringBootTest 어노테이션이 작성된 abstract class를 상속받는 구조를 택했습니다.

그러나 local 환경에서는 각각의 테스트를 작성하고 돌릴 때가 많습니다.
그럴 때마다 도커 컨테이너가 생성되니 너무나 답답했습니다.
어차피 개발할 때 사용하는 mysql을 docker hub를 통해 유지하고 있었기 때문에, 같은 이미지로 생성된 개발용 db 컨테이너(port 3306)와 local 테스트용 db 컨테이너(port 3307)로 나눠 동작시키도록 팀원들과 결정하였습니다.
만약 실수 혹은 고의로 테스트용 db 컨테이너 내에 데이터를 저장했어도, 해당 값만 지우거나 컨테이너를 새로 띄우면 해결되는 문제이기 때문입니다.

그러나 배포할 때는 데이터가 남는 경우가 아예 없어야 하기에, release 환경에서 돌아가는 테스트는 testcontainers를 적극적으로 사용해보기로 결정했습니다.

실무에서는 어떠한 방법을 사용하는지 많이 궁금합니다.
더 나은 해결법을 공유해주시면 감사하겠습니다.

Reference

https://stackoverflow.com/questions/33607183/how-to-configure-spring-boot-project-to-work-with-inmemory-spatial-database-for
https://java.testcontainers.org/

profile
나누며 타오르는 프로그래머, 타프입니다.

0개의 댓글