728x90

들어가면서...

우리는 개발 소트코드는 git ,svn 등 형상관리툴을 이용하여 pull, commit 등을 매일 수백번씩하면서 관리해간다. 

하지만 db의 버전은 어떻게 관리해왔는가? 물론 지금 말하는 db 버전은 mysql 5.7 , mysql 8.0 oracle 10g 이런 버전이 아니고 개발도중에 자주 바뀌는 스키마, 기초 테스트 데이터 를 얘기한다. 팀 동료가 스키마가 바뀐상태로 개발소스들을 commit 하였다면 pull 내려 받은 나는 코드들이 제대로 작동하지 않을수 있고 반대로 내가 변경한 데이터들이 바뀌면 개발팀원 전체에 영향을 줄수 있다.

이런 문제들을 flyway라는 마이그레이션 툴을 사용하여 database를 형상관리해보자!

공식사이트에 기재되어 있는 flyway 작동방식을 설명해주는 많이 보았을 이미지 이다.  미안하지만 그대로 가지고와 봤다. 뭐 설명을 하려고 하니 딱히 설명할것도 없을것 같다. 우리가 이미 code 형상 관리에 대하여 어느정도 이해가 있으니 그림만 봐도 대충 이해가 될것 같다. 즉 형상관리를 시작하는 시점의 데이터가 있고 추후 변경되는 부분의 데이터들을 감지하여 변경된 부분만 엡데이트 해주는 방식이다.

 

flyway를 잘 사용하면 이렇게 Axel 과 Christian 라는 개발자 각자 필요한 DDL 을 만들고 배포할수 잇다. 공식사이트 소개를 예로 들자면 gradle, maven, CLI, java api 를 통하여 flyway 를 실행할수 있다고 하는데 이글에서 springboot 으로 실행하는 방법을 알아보자.

 

pom.xml dependency 추가

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-core</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

src/main/resources 폴더에 db 폴더 ,그리고 그안에 migration 폴더를 만든다.

migration 폴더에 V1__Base_version.sql 파일을 생성

DROP TABLE IF EXISTS user ;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'pk',
  `name` varchar(20) NOT NULL COMMENT '이름',
  `age` int(5) DEFAULT NULL COMMENT '나이',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

주의 : .sql 경로를 혹시 자기가 원하는 폴더에 위치하고 싶으면 spring.flyway.locations 설정을 하면 된다.

User 객체 생성

@Data
@NoArgsConstructor
public class User {

    private Long id;
    private String name;
    private Integer age;

}

Interface 작성

public interface UserService {

    int create(String name, Integer age);
    
    List<User> getByName(String name);

    int deleteByName(String name);

    int getAllUsers();

    int deleteAllUsers();

}

@Service
public class UserServiceImpl implements UserService {

    private JdbcTemplate jdbcTemplate;

    UserServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public int create(String name, Integer age) {
        return jdbcTemplate.update("insert into USER(NAME, AGE) values(?, ?)", name, age);
    }

    @Override
    public List<User> getByName(String name) {
        List<User> users = jdbcTemplate.query("select * from USER where NAME = ?", (resultSet, i) -> {
            User user = new User();
            user.setId(resultSet.getLong("ID"));
            user.setName(resultSet.getString("NAME"));
            user.setAge(resultSet.getInt("AGE"));
            return user;
        }, name);
        return users;
    }

    @Override
    public int deleteByName(String name) {
        return jdbcTemplate.update("delete from USER where NAME = ?", name);
    }

    @Override
    public int getAllUsers() {
        return jdbcTemplate.queryForObject("select count(1) from USER", Integer.class);
    }

    @Override
    public int deleteAllUsers() {
        return jdbcTemplate.update("delete from USER");
    }

}

테스트 코드 작성

@Slf4j
@SpringBootTest
public class FlywayDemoApplicationTests {

    @Autowired
    private UserService userSerivce;

    @Test
    public void test() throws Exception {
        userSerivce.deleteAllUsers();

        userSerivce.create("Tom", 10);
        userSerivce.create("Mike", 11);
        userSerivce.create("Didispace", 30);
        userSerivce.create("Oscar", 21);
        userSerivce.create("Linda", 17);

        // Oscar 라는 이름을 가진 사용자 조회 , 나이 가 정확한지 확인.
        List<User> userList = userSerivce.getByName("Oscar");
        Assertions.assertEquals(21, userList.get(0).getAge().intValue());

        // 5명 있음.
        Assertions.assertEquals(5, userSerivce.getAllUsers());

        // 2명 삭제.
        userSerivce.deleteByName("Tom");
        userSerivce.deleteByName("Mike");

        // 아직 5명 있을걸?
        Assertions.assertEquals(3, userSerivce.getAllUsers());
    }

}

테스트 코드 실행 결과

디비테이블 확인 2개 추가 된걸 확인 할수 있다.

  • user 현재 테이블
  • flyway_schema_history : flyway가 관리하고 있는 테이블. 해당 테이블에 수행된 .sql 스크립트 내역들을 기재하고 있다.

위 내용을 이서 진행해보자. 만일 특정 개발자가 address 라는 컬럼을 테이블에 추가했다면 어떻게 될까?

ALTER TABLE `user` ADD COLUMN `address` VARCHAR(20) DEFAULT NULL;

팁! 스크립트 파일 명명 규칙은 버전번호_쿼리에 대한 설명 .sql 이다.

다시 테스트 코드를 돌리면 아래와 같은 수행완료 로그를 볼수 있다.

2022-12-26 16:58:12.025  INFO 37330 --- [           main] o.f.c.i.database.base.DatabaseType       : Database: jdbc:mysql://localhost:3306/test (MySQL 8.0)
2022-12-26 16:58:12.063  INFO 37330 --- [           main] o.f.core.internal.command.DbValidate     : Successfully validated 2 migrations (execution time 00:00.020s)
2022-12-26 16:58:12.075  INFO 37330 --- [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `test`: 1
2022-12-26 16:58:12.082  INFO 37330 --- [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `test` to version "1.1 - alter table user"
2022-12-26 16:58:12.113  INFO 37330 --- [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema `test` (execution time 00:00.045s)

테이블을 확인해보자

마찬가리로 history 테이블에도 아래와 같이 내역이 추가되었다.

어떤가 어제는 더이상 누가 스키마를 바꿨는지 팀원들한테 일일이 확인 안해봐도 되지 않은가?

끝!

'Springboot2.x 강좌 > DB연결' 카테고리의 다른 글

트랜잭션 기본설정 알아보기  (0) 2023.01.05
MyBatis 의 다중 DataSource  (0) 2023.01.05
Spring Data JPA 다중 DataSource  (0) 2023.01.05
JdbcTemplate 다중 DataSource  (0) 2023.01.05
XML 로 Mybatis 설정하기  (0) 2023.01.05

+ Recent posts