폐쇄망 환경에서 서로 다른 두 DB를 다뤄야 할 때

2025. 11. 11. 21:44·Backend

현재 진행 중인 프로젝트는 공공기관 시스템으로, 온프레미스 환경에서 전자정부프레임워크(eGovFrame) 기반으로 개발 중이다.
그런데 예상치 못한 구조적 문제가 하나 있었다.

서비스 로직상 서로 다른 두 개의 데이터베이스(DB A, DB B) 를 동시에 조회하고, 심지어 일부 기능에서는 두 DB의 테이블을 조인해야 하는 상황이 발생했다.

 

예를 들어보면 아래와 같은 케이스다.

  • DB_A.user 와 DB_B.approval 테이블을 조인해야 하는 업무 로직이 있음
  • 두 DB 모두 user 테이블을 가지고 있어, 데이터 싱크를 맞춰야 하는 이슈도 존재함

처음에는 단순히 DataSource를 두 개 선언해서 해결될 줄 알았지만 막상 실제 조인이 필요한 부분에서 iBatis나 MyBatis 수준에서는 처리 불가한 제약이 존재했다.


이 문제를 한 문장으로 요약하면 이렇다.

“두 개의 서로 다른 DB를 바라보면서 때로는 두 DB를 조합해 조회해야 하고 또 때로는 동일한 테이블의 데이터를 동기화해야 하는 상황”

 

즉, 단순히 멀티 데이터소스(Multi-DataSource) 문제를 넘어서 DB 간의 연계 및 일관성(Consistency) 유지가 핵심 이슈였다.


문제를 해결하기 위해 찾아본 대표적인 방법은 아래 여섯 가지다.

번호 방식 설명
① DB Link 한 DB에서 다른 DB로 직접 연결(Link)을 걸어 SQL 수준에서 조인 수행. 빠르고 간단하지만, 보안 정책상 제약이 많고 폐쇄망에서는 적용이 어려움.
② DB 이중화(Replication) 한쪽 DB의 데이터를 다른 DB로 실시간 또는 주기적으로 복제하여 일관성을 유지하는 방식. 데이터 동기화에는 유리하지만 관리 복잡도가 높고, DBMS가 다르면 적용이 까다로움.
③ 서버(Spring) 단에서 처리 각 DB에서 필요한 데이터를 각각 조회한 뒤, 서버 애플리케이션 단에서 병합(Join) 처리하는 방식. 트랜잭션 제어가 명확하고 보안상 안전하지만, 로직 복잡성과 성능 부담이 생길 수 있음.
④ 중간 연계 DB(Bridge DB) 또는 통합 View 두 DB의 데이터를 배치나 ETL로 중간 통합 DB에 모으는 방식. 서비스는 이 DB만 조회하므로 단순하고 안정적이나, 실시간성이 떨어짐.
⑤ API 연동 (Service-to-Service) 두 서버(Spring)가 각각의 DB를 관리하고, 필요한 데이터를 REST API로 주고받는 구조. 시스템 간 결합도가 낮고 유연하지만, 네트워크/응답 지연 리스크가 존재함.
⑥ 메시지 큐 기반 비동기 동기화 (Kafka, RabbitMQ 등) 한 시스템의 데이터 변경 이벤트를 메시지 큐로 전달하여 다른 시스템이 비동기로 반영. 실시간성은 확보되지만 폐쇄망에서는 인프라 구축이 어렵고 운영 복잡도가 높음.

 

각 방법의 구조와 장단점을 좀 더 구체적으로 살펴봤다.


① DB Link 방식

개념:
DB A에서 DB B를 "링크"로 연결하여, A 쿼리 내에서 B의 테이블을 접근할 수 있게 하는 방식이다.
예를 들어 Oracle이라면 다음과 같은 형태로 접근이 가능하다.

SELECT a.user_name, b.approval_date 
FROM user@DB_A a 
JOIN approval@DB_B b ON a.user_id = b.user_id;

장점:

  • 쿼리 레벨에서 직접 조인이 가능하므로 개발 로직이 단순함
  • 트랜잭션 단위로 일관성 유지가 쉬움

단점:

  • DB 간 네트워크 연결이 필요 → 폐쇄망에서는 네트워크 정책상 차단된 경우가 많음
  • DB 종류가 다르면(Link 불가능) 적용 불가 (예: PostgreSQL ↔ Oracle)
  • 링크 연결이 느려질 경우 전체 쿼리 성능 저하

적용 판단:
공공기관의 폐쇄망에서는 DB 간 통신이 제한되어 있어 현실적으로 사용이 어렵다.
보안정책상 DB Link 자체를 허용하지 않는 경우가 많았다.


② DB 이중화(Replication)

개념:
DB A와 DB B의 특정 테이블을 복제(replication) 하여 한쪽 DB에서만 읽도록 하는 방식이다.
PostgreSQL이라면 logical replication을 Oracle이라면 Materialized View나 GoldenGate를 사용할 수 있다.

장점:

  • 물리적 연결 없이 데이터 일관성 유지 가능
  • 애플리케이션 단에서는 단일 DB로 접근하므로 코드가 단순

단점:

  • 설정 및 관리가 복잡 (특히 폐쇄망에서 replication 구성은 매우 제한적)
  • 양방향 싱크가 필요한 경우 충돌(conflict) 발생 가능
  • 복제 지연 시 데이터 불일치 발생 위험

적용 판단:
읽기 전용 데이터라면 유효하지만 양방향 쓰기(update) 상황에서는 충돌 위험이 크다.
공공기관 특성상 DB 관리권이 나뉘어 있는 경우도 많아 실무 적용이 까다롭다.


③ 서버(Spring) 단에서 처리 (Application Join)

개념:
각 DB의 Repository(or Mapper)를 통해 데이터를 따로 조회한 뒤,
서버 메모리에서 데이터를 조합(Join) 하는 방식이다.

List<User> users = userRepositoryA.findAll(); 
List<Approval> approvals = approvalRepositoryB.findAll(); 

List<JoinedData> result = users.stream()
								.flatMap(u -> approvals.stream() 
                                .filter(a -> a.getUserId().equals(u.getUserId())) 
                                .map(a -> new JoinedData(u, a))) 
                                .collect(Collectors.toList());

장점:

  • DB 간 연결 불필요 (보안 문제 없음)
  • DB 종류가 달라도 가능 (예: Oracle ↔ PostgreSQL)
  • 트랜잭션 단위를 명확히 제어 가능

단점:

  • 데이터 양이 많으면 성능 저하
  • 조합 로직을 코드로 작성해야 해서 개발 복잡도 증가
  • 완전한 조인(특히 Left Join 등)을 구현하려면 추가 로직 필요

적용 판단:
보안·망 정책이 까다로운 폐쇄망 환경에서는 현실적으로 가장 유연한 방법이다.
데이터량이 크지 않다면 충분히 효율적인 대안이다.


④ 중간 연계 DB(Bridge DB) 또는 통합 View 구축

개념:

DB A와 DB B 사이에 “중간 저장소(Bridge DB)”를 하나 두고 두 DB의 데이터를 일정 주기로 집계·통합하여 저장하는 방식이다.
결국 서비스 레이어에서는 이 Bridge DB만 바라보면 된다.

예를 들어, 아래처럼 구성한다.

DB A → ETL 스크립트 → Bridge DB ← ETL 스크립트 ← DB B

Bridge DB에는 아래처럼 통합 테이블을 둔다.

CREATE TABLE unified_user AS 
SELECT a.user_id, a.user_name, b.approval_status 
FROM user_a a 
LEFT JOIN user_b b ON a.user_id = b.user_id;
장점:
  • 복잡한 실시간 연동 대신 “집계” 중심이므로 구조가 안정적
  • 서비스 쿼리는 단일 DB에서 수행되어 성능 안정성 확보
  • 장애 시 원본 DB에 영향 없음

단점:

  • 실시간성이 떨어짐 (주기적 배치로 동기화됨)
  • ETL 스크립트나 배치 관리가 필요
  • 데이터가 많을수록 Bridge DB 용량이 커짐

적용 판단:

읽기 중심의 리포팅, 통계, 조회성 업무에 매우 적합하다.
특히 업무 특성상 실시간성이 크게 중요하지 않은 공공시스템이라면
이 방식이 가장 “운영 안정성”이 높다.


⑤ API 연동 방식 (DB-to-DB 대신 Service-to-Service)

개념:

두 시스템이 DB 레벨에서 직접 연결되는 대신,
각 서버(Spring) 가 REST API로 필요한 데이터를 서로 호출하는 구조다.
즉, “데이터를 공유하는 대신 데이터를 요청한다”는 접근이다.

[서버 A] ----(HTTP Request/Response)----> [서버 B의 API] ----> DB B

 

장점:
  • DB 간 직접 연결이 없어 보안 정책 위반 가능성 최소화
  • 비동기/캐싱 구조로 유연한 확장 가능
  • API Gateway 등으로 호출 단위 제어 가능

단점:

  • 응답 지연, 네트워크 단절 시 장애 가능성
  • 데이터 JOIN이 아니라 “조합”이 되므로 로직 복잡
  • 폐쇄망에서는 서버 간 통신 자체가 제한될 수 있음

적용 판단:

만약 동일한 망 내부(예: 기관 내 인트라넷)라면 API 방식이 장기적으로 가장 유연한 구조다.
데이터 종속성을 줄이고 서비스 경계를 명확히 할 수 있다.

공공 프로젝트에서도 최근 “DB 직접 연결 금지” 정책이 늘고 있어 API 방식은 점차 표준화되는 추세다.


⑥ 메시지 큐 기반 비동기 동기화 (Kafka, RabbitMQ 등)

개념:

DB 간 실시간 동기화를 직접 하지 않고 서버 애플리케이션 단에서 데이터 변경 이벤트를 메시지 큐로 전달하여 상대 DB가 비동기로 반영하도록 하는 구조다.

DB A ←→ Spring A → Kafka Topic → Spring B → DB B

 

장점:

  • 데이터 변경 이벤트를 실시간 전달 가능
  • 트랜잭션을 분리할 수 있어 시스템 간 결합도 감소
  • 장애 시 재처리(consume) 가능

단점:

  • 메시지 큐 인프라가 필요 (폐쇄망에선 설치 어려움)
  • 메시지 누락, 중복 처리 등 운영 복잡성 증가
  • 완전한 실시간 트랜잭션 일관성은 불가능

적용 판단:

보안이나 네트워크 정책상 제약이 덜한 경우라면 DB 간 실시간 싱크를 대체하는 가장 현대적인 접근법이다.
다만 폐쇄망에서는 Kafka, RabbitMQ 설치가 쉽지 않기 때문에 적용 가능성은 낮지만 “향후 전환 가능성”을 고려하면 좋은 선택지다.


"폐쇄망 환경 + 서로 다른 DB + 일부 조인 필요"

→ 현실적으로 ③ 서버 단에서 처리 방식이 가장 실용적이다.

실제로 예전 프로젝트에서 대용량 연산 데이터를 DB에서 처리하는 방식과 애플리케이션에서 처리하는 방식을 테스트 해본 결과연산이 크게 복잡하지 않은 데이터라면 처리속도가 비슷하다는 결론을 도출했었다.


이번 경험을 통해 느낀 건 “DB 설계 단계에서부터 통합 전략을 충분히 고민해야 한다”는 점이다.
애플리케이션 단에서의 우회로는 결국 임시방편에 불과하고 장기적으로는 데이터 구조 자체를 통합하거나 API 기반으로 분리하는 것이 바람직하다.

하지만 공공기관·폐쇄망 프로젝트에서는 정답보다 ‘현실적인 선택’이 더 중요하다.
그런 의미에서 이번 접근은 완벽하지는 않지만 충분히 실무적인 해결책이었다.

저작자표시 (새창열림)
'Backend' 카테고리의 다른 글
  • API 응답 속도를 빠르게 만드는 방법
  • @Transactional 동작 원리
  • 온프레미스 환경에서 무중단 배포(Zero Downtime Deployment) 구축 방안 탐구기
  • REST API
쭈니어 개발자
쭈니어 개발자
    홈 |
  • 쭈니어 개발자
    주니어 개발자 공부 기록
    쭈니어 개발자
  • 글쓰기 관리
  • 전체
    오늘
    어제
  • GitHub

    Notion

    • 분류 전체보기 (134)
      • Frontend (4)
      • Backend (21)
      • Database (4)
      • Data Structure & Algorithm (41)
      • Network (16)
      • IT Education (48)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 인기 글

  • 태그

    코테
    트리의 지름
    백준
    자바
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.4
쭈니어 개발자
폐쇄망 환경에서 서로 다른 두 DB를 다뤄야 할 때
상단으로

티스토리툴바