Home JPA를 사용할 때, 하나의 서버, 여러대의 데이터베이스는 어떻게 처리할까?
Post
Cancel

JPA를 사용할 때, 하나의 서버, 여러대의 데이터베이스는 어떻게 처리할까?

글을 작성하게 된 계기


회사에서 하나의 서버에 두 개의 데이터베이스를 운영하고 있습니다. 이를 어떻게 JPA로 매핑하는지, 어떻게 스키마를 관리했는지 정리하기 위해 글을 작성하게 되었습니다.





1. 문제 상황


현재 회사에서 하나의 데이터베이스 서버여러개의 데이터베이스를 사용 하고 있습니다. 예를 들어, 다음과 같이요. hello라는 로컬 서버의 데이터베이스에 hello, world라는 두 개의 데이터베이스가 존재하는 것이죠.

image





물리적으로는 하나의 서버 지만, 논리적으로 다른 데이터베이스를 사용 하는 것이죠. 현재 프로젝트에서는 JPA 를 사용하고 있고, 논리적으로 다른 두 데이터베이스를 조인 하기 때문에 두 데이터베이스의 JPA 엔티티를 한 프로젝트에 매핑 해야 했습니다.

image





한 데이터베이스의 칼럼 값이 다른 데이터베이스에 존재하며, 연관 관계를 맺고 있었기 때문에 연관 관계 매핑, 데이터베이스 설정, 권한 등 고려할 점이 굉장히 많았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# hello 데이터베이스에서 world partner_id 가진 경우 
CREATE TABLE IF NOT EXISTS hello.ms_merchants
(
    id         BIGINT AUTO_INCREMENT PRIMARY KEY,
    name       VARCHAR(255),
    biz_no     VARCHAR(255),
    partner_id BIGINT
);

CREATE TABLE IF NOT EXISTS world.partners
(
    id   BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255)
);





뿐만 아니라 통합 테스트 를 할 때, 스키마 생성 에 대해서도 이슈가 많았는데요, 두 데이터베이스의 테이블을 모두 생성 해야 했기 때문입니다. 문제를 정리하면 아래와 같은데, 이를 어떻게 해결했는지 하나씩 살펴보겠습니다.

  1. 서로 다른 두 데이터베이스의 JPA 엔티티를 한 프로젝트에 매핑
  2. 통합 테스트 실행 전, 두 데이터베이스의 스키마 생성







2. Entity 매핑


엔티티를 매핑하기 전, 먼저 스키마를 살펴보겠습니다. hello 데이터베이스에는 merchant_size_data와 ms_merchants 라는 두 개의 테이블이 존재하며, world 데이터베이스에는 partners라는 테이블이 존재합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# hello 데이터베이스
CREATE TABLE IF NOT EXISTS hello.merchant_size_data
(
    id                 BIGINT AUTO_INCREMENT PRIMARY KEY,
    merchant_size_type VARCHAR(255)
);

CREATE TABLE IF NOT EXISTS hello.ms_merchants
(
    id         BIGINT AUTO_INCREMENT PRIMARY KEY,
    `name`       VARCHAR(255),
    biz_no     VARCHAR(255),
    partner_id BIGINT
);

# world 데이터베이스
CREATE TABLE IF NOT EXISTS world.partners
(
    id   BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255)
);





이를 다이어그램으로 보면 다음과 같습니다. 각 네모는 한 데이터베이스 단위를 나타내며, 즉, 하나의 서버에 두 개의 데이터베이스가 존재하는 것이죠. 테이블 간 연관관계가 존재하면서요.

image





해결책은 정말 간단한데요, 다음과 같이 엔티티에 schema 옵션을 추가해 어떤 데이터베이스의 테이블인지 명시해주면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Getter
@Entity(name = "ms_merchants")
@Table(name = "ms_merchants", schema = "hello")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MsMerchantJpaEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "biz_no")
    private String bizNo;

    @Column(name = "partner_id")
    private Long partnerId;
}
1
2
3
4
5
6
7
8
9
10
11
12
@Getter
@Entity(name = "partners")
@Table(name = "partners", schema = "world")
public class PartnerJpaEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;
}





이는 Hibernate가 어노테이션을 기반 으로 엔티티를 빈으로 등록하며, 메타데이터를 관리하기 때문입니다. 이를 통해 같은 서버의 서로 다른 두 데이터베이스를 저장하고 관리할 수 있게 됩니다. 메타데이터를 어떻게 관리하는지에 대한 상세한 내용은 해당 포스팅을 참조해주세요.

JPA의 메타데이터는 내부적으로 어떻게 관리될까?







3. 스키마 생성


통합 테스트는 데이터베이스를 포함하기 때문에 테스트 실행 전, 스키마가 생성 돼 있어야 합니다. 그런데 데이터베이스를 두 개 사용하고 있기 때문에, 조금 더 추가적인 설정이 필요한데요, 다음과 같이 어떤 데이터베이스를 사용하는지 명시 해 줘야 합니다.

1
2
CREATE TABLE IF NOT EXISTS hello.ms_merchants(......);
CREATE TABLE IF NOT EXISTS world.partners(......);





이후 테스트를 실행하기 전, @Sql 어노테이션 이나 Flyway, Liquibase 와 같은 툴을 이용해 이를 실행만 시켜주면 됩니다.

1
2
3
4
5
6
7
8
9
10
@ActiveProfiles("test")
@Sql(
    scripts = "classpath:init.sql",
    config = @SqlConfig(
        dataSource = "lazyDataSource",
        transactionManager = "transactionManager"
    )
)
@SpringBootTest(classes = App.class)
public abstract class IntegrationTestBase { ...... }





처음에는 테스트 컨테이너 를 이용해 해당 스키마를 실행하려고 했는데, 권한 이슈 가 있었습니다. 망 분리 환경이다보니 테스트 컨테이너를 사용하기도 꽤 힘들었는데요, 어쩔 수 없이 도커 컨테이너를 띄워 스키마를 생성했습니다.







4. 정리


하나의 서버에 두 개의 서로 다른 데이터베이스를 사용하는 경우 발생할 수 있는 문제점과 해결책을 살펴보았습니다. 솔직히 좋은 방법이라고는 생각되지 않는데요, 어쩌겠습니까. 이미 회사에서 수 십년간 사용한 방식인데요.


This post is licensed under CC BY 4.0 by the author.