기존에 riot API를 활용하여 league of legend 게임 데이터를 수집하는 프로젝트를 진행했었다.
https://github.com/hamwoojo/riot-api
GitHub - hamwoojo/riot-api: riot-api collect server
riot-api collect server. Contribute to hamwoojo/riot-api development by creating an account on GitHub.
github.com
해당 프로젝트에 아쉬운 부분이 있어 많은 리팩토링을 진행하였었는데 항상 다시 볼 때마다 개선점이 보인다.
그중에 한 문제를 개선해보려고 한다.
이번에 맥북을 구매하면서 내 로컬에 환경 세팅을 하려고 하니 DB에 관한 새로운 세팅을 하는 게 너무 번거로웠다.
해당 문제를 해결하기 위해서 데이터베이스 마이그레이션을 적용해보려고 한다.
데이터베이스 마이그레이션이란?
데이터베이스 마이그레이션이란 데이터베이스 스키마의 버전을 관리하고, 애플리케이션의 변경 사항을 추적하여 데이터베이스 스키마를 업데이트하는 작업을 의미한다.
물론 해당 프로젝트는 Spring Data JPA를 활용하여 진행한 프로젝트여서, hibernate의 ddl 생성 옵션
jpa:
hibernate:
ddl-auto: create
을 사용하면 되지만 해당 옵션은 로컬에서 환경 구성할 때는 쓸 수 있지만 운영 서버에서는 데이터 손실의 위험이 있어 너무 위험하여 쓸 수 없다. 또한 추후에 어떤 테이블이 업데이트된다면 그 내용을 관리해야 할 필요도 있다.
해당 이슈들을 방지하기 위해서 데이터베이스 마이그레이션을 적용해보기로 하였다.
해당 프로젝트에 사용한 기술 스택 버전은 다음과 같다.
Spring Boot
- Version 2.7.10
Maven
- Version : 3.8.1
PostgreSQL
- Version : 14.1
postgresql 환경 구성하기위해서 도커를 활용하기로 하였다.
postgresql 컨테이너 생성 및 실행을 위해서 docker-compose.yml 파일을 작성하였다.
version: "3.0"
services:
db:
image: postgres:14.1
container_name: postgres
ports:
- "5432:5432"
volumes:
- ./db_data:/var/lib/postgresql/data
restart: always
environment:
POSTGRES_USER: "root"
POSTGRES_PASSWORD: "local"
POSTGRES_DB: "riot"
docker compose up -d 명령어로 postgresql을 실행하였다.
Spring Boot에서는 flyway를 활용하면 데이터베이스 마이그레이션을 적용할 수 있다.
flyway를 적용해보자.
pom.xml에 dependency를 추가한다.
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>9.21.1</version>
</dependency>
application.yml에 flyway를 사용한다는 내용을 추가한다.
spring:
flyway:
enabled: true
src/main/resources/db/migration 디렉터리에 V0__init.sql 파일을 작성한다. 테이블을 생성할 DDL을 써주면 된다.
너무 길어서 테이블 중 하나의 예시만 작성 첨부하겠다.
CREATE TABLE public.api_info (
api_info_id bigserial NOT NULL,
api_name varchar(255) NULL,
api_host varchar(255) NULL,
api_url varchar(255) NULL,
content_type varchar(255) NULL,
description varchar(255) NULL,
http_method varchar(255) NULL,
rate_limit int4 NULL,
rate_limit_interval int4 NULL,
api_scheme varchar(5) NULL,
CONSTRAINT api_info_pkey PRIMARY KEY (api_info_id)
);
그리고 애플리케이션을 실행하였는데, 오류가 발생했다.
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration$FlywayConfiguration.configureProperties(FlywayAutoConfiguration.java:227)
The following method did not exist:
'org.flywaydb.core.api.configuration.FluentConfiguration org.flywaydb.core.api.configuration.FluentConfiguration.oracleSqlplus(boolean)'
The calling method's class, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration$FlywayConfiguration, was loaded from the following location:
jar:file:/Users/mac/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.7.10/spring-boot-autoconfigure-2.7.10.jar!/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class
The called method's class, org.flywaydb.core.api.configuration.FluentConfiguration, is available from the following locations:
jar:file:/Users/mac/.m2/repository/org/flywaydb/flyway-core/9.21.1/flyway-core-9.21.1.jar!/org/flywaydb/core/api/configuration/FluentConfiguration.class
The called method's class hierarchy was loaded from the following locations:
org.flywaydb.core.api.configuration.FluentConfiguration: file:/Users/mac/.m2/repository/org/flywaydb/flyway-core/9.21.1/flyway-core-9.21.1.jar
Action:
Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration$FlywayConfiguration and org.flywaydb.core.api.configuration.FluentConfiguration
해당 에러를 찾다가 확인해 보니 무지성으로 flyway 최신 버전을 적용하려다가 스프링 부트의 버전과 호환이 안돼서 생긴 문제였다.
내 스프링 부트의 버전은 2.7.10인데 해당 버전은 8.5 버전의 flyway를 사용해야 한다.
해당 내용은 스프링 페이지에서 확인이 가능하다
https://spring.io/projects/spring-boot#learn
Spring Boot
spring.io
본인이 사용하는 버전의 Reference Doc을 보면 된다.
본인은 2.7 버전이라서 2.7.15 버전을 택했다.
클릭한 뒤 제일 아래에 있는 Dependency Versions를 선택한다.
찾기(ctrl + f)로 flyway를 찾는다.
8.5 버전을 써야 한다는 걸 알 수 있었다.
8.5 버전으로 pom.xml을 수정하자.
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>8.5.13</version>
</dependency>
DDL이 제대로 됐는지 확인하기 위해서 validate 옵션을 추가하였다.
jpa:
hibernate:
ddl-auto: validate
실행해 보면 성공적으로 된 걸 알 수 있다.
db에서도 flyway_schema_history 테이블이 자동으로 생성되어 있는 걸 확인할 수 있는데 이름에서 알 수 있듯이 flyway 히스토리를 관리해 주는 테이블이다. 테이블 내용을 한 번 알아보자.
flyway_schema_history는 Flyway가 관리하는 테이블 중 하나로, 데이터베이스의 스키마 버전 및 마이그레이션 작업에 대한 기록을 저장하는 테이블입니다.
Flyway는 이 테이블을 사용하여 데이터베이스의 상태를 추적하고, 이미 적용된 마이그레이션 스크립트를 기록하여 중복 실행을 방지합니다.
다음은 flyway_schema_history 테이블 칼럼에 대한 설명이다.
installed_rank: 마이그레이션 스크립트가 설치된 순서를 나타내는 숫자입니다. 낮은 숫자일수록 이전에 실행된 스크립트를 의미합니다.
version: 마이그레이션 스크립트의 버전을 나타냅니다. 버전은 마이그레이션 스크립트 파일 이름에 포함된 숫자 또는 타임스탬프입니다.
description: 마이그레이션 스크립트의 설명을 나타냅니다. 마이그레이션 스크립트 파일 이름에서 파생될 수 있습니다.
type: 마이그레이션 스크립트의 유형을 나타냅니다. 주로 "SQL" 또는 "BASELINE"이 사용됩니다. SQL은 일반적인 마이그레이션 스크립트를 의미하고, BASELINE은 초기 베이스라인 마이그레이션을 의미합니다.
script: 실행된 마이그레이션 스크립트 파일의 이름을 나타냅니다.
checksum: 마이그레이션 스크립트의 체크섬 값입니다. 체크섬은 스크립트의 내용을 해시한 값으로, Flyway가 스크립트를 변경하지 않았는지 검증하는 용도로 사용됩니다.
installed_by: 마이그레이션 스크립트를 설치한 사용자를 나타냅니다.
installed_on: 마이그레이션 스크립트가 설치된 날짜와 시간을 나타냅니다.
execution_time: 마이그레이션 스크립트 실행에 소요된 시간을 나타냅니다.
flyway_schema_history 테이블을 통해 Flyway는 현재 데이터베이스의 상태와 적용된 마이그레이션 스크립트들에 대한 정보를 추적하며, 이를 통해 중복 실행을 방지하고 데이터베이스 스키마의 버전을 관리합니다.
다음은 기초 데이터를 추가로 insert 해보려고 한다.
flyway도 버전을 통하여 관리하는데 버전 관리는 프로젝트나 팀의 네이밍 룰을 따르면 된다.
본인은 다음 버전을 0.1로 작성할 것이다.
INSERT INTO public.api_info (api_name,api_host,api_url,content_type,description,http_method,rate_limit,rate_limit_interval,api_scheme)
VALUES
('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/GRANDMASTER/I','json','Retrieve a list of users in the Grandmaster tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/PLATINUM/IV','json','Retrieve a list of users in the Platinum IV tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/PLATINUM/III','json','Retrieve a list of users in the Platinum III tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/PLATINUM/II','json','Retrieve a list of users in the Platinum II tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/PLATINUM/I','json','Retrieve a list of users in the Platinum I tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/DIAMOND/IV','json','Retrieve a list of users in the Diamond IV tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/DIAMOND/III','json','Retrieve a list of users in the Diamond III tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/DIAMOND/II','json','Retrieve a list of users in the Diamond II tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/DIAMOND/I','json','Retrieve a list of users in the Diamond I tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/MASTER/I','json','Retrieve a list of users in the Master tier.','get',100,120,'https')
,('getUserEntries','kr.api.riotgames.com','/lol/league-exp/v4/entries/RANKED_SOLO_5x5/CHALLENGER/I','json','Retrieve a list of users in the Challenger tier.','get',100,120,'https')
,('getMatchList','asia.api.riotgames.com','/lol/match/v5/matches/by-puuid/{puuid}/ids','json','Retrieve a list of matches by PUUID.','get',100,120,'https')
,('getMatchDetail','asia.api.riotgames.com','/lol/match/v5/matches/{matchId}','json','Retrieve detailed match information by match ID.','get',100,120,'https')
,('getItems','ddragon.leagueoflegends.com','/cdn/{version}/data/ko_KR/item.json','json','Retrieve item information by version.','get',100,120,'https')
,('getChampions','ddragon.leagueoflegends.com','/cdn/{version}/data/ko_KR/champion.json','json','Retrieve champion information by version.','get',100,120,'https')
,('getSpells','ddragon.leagueoflegends.com','/cdn/{version}/data/ko_KR/summoner.json','json','Retrieve summoner spell information by version.','get',100,120,'https')
,('getRunes','ddragon.leagueoflegends.com','/cdn/{version}/data/ko_KR/runesReforged.json','json','Retrieve rune information by version.','get',100,120,'https')
,('getUserInfoDetail','kr.api.riotgames.com','lol/summoner/v4/summoners/{encryptedSummonerId}','json','Retrieve user information details by summoner ID to fetch match information.','get',100,120,'https')
,('getVersions','ddragon.leagueoflegends.com','/api/versions.json','json','Retrieve version information.','get',100,120,'https');
INSERT INTO public.api_params (api_info_id,data_type,description,is_required,"name",api_key,api_value,date_param_required)
VALUES
(13,'Long','Start time',true,'startTime','startTime',NULL,true)
,(13,'Long','End time',true,'endTime','endTime',NULL,true)
,(13,'String','Game type',true,'type','type','ranked',false)
,(13,'int','Page',true,'start','start','0',false)
,(13,'int','Number of items to load per page',true,'count','count','100',false);
V0.1__insert_base_data.sql로 해당 내용을 작성하고 실행하였다.
잘 된 걸 볼 수 있다.
다음에는 kafka와 연동하는 걸 써보려고 한다.
'Side Project > Riot Project' 카테고리의 다른 글
Mac os에서 kafka clustering 구축하기 (0) | 2023.08.13 |
---|