IT Developer/Spring

Spring 기초 <23. Spring Boot에서 Multi-Tenancy 아키텍처 구현>

TEMA_ 2025. 4. 9. 13:45
반응형

스프링 Spring

23. Spring Boot에서 Multi-Tenancy 아키텍처 구현

 

안녕하세요! 태마입니다.

Spring 기초 강좌입니다.

 

강좌의 경우 

1. 주제 간단 정리

2. 상세 주제 정리

으로 이루어져 있습니다.

 

스프링 Spring

포스팅 시작하겠습니다 :)

 

 

 

 


 

1. 주제 간단 정리

 

1. Multi-Tenancy(멀티 테넌시)란?

Multi-Tenancy(멀티 테넌시)는 하나의 애플리케이션이 여러 개의 테넌트(Tenant)를 지원하는 아키텍처
각 테넌트는 독립적인 데이터를 유지하면서도 동일한 애플리케이션을 공유 가능

📌 Multi-Tenancy의 주요 특징

특징설명
여러 고객(테넌트)이 하나의 애플리케이션을 공유 동일한 코드베이스에서 여러 개의 데이터를 분리하여 운영 가능
독립적인 데이터 저장 테넌트마다 별도의 데이터베이스 또는 스키마를 유지
보안 및 격리 각 테넌트의 데이터는 서로 접근할 수 없음
유지보수 비용 절감 여러 고객을 하나의 애플리케이션에서 관리 가능

📌 멀티 테넌시는 "SaaS(Software as a Service) 모델에서 필수적인 아키텍처"


2. Single-Tenancy vs Multi-Tenancy 비교

Single-Tenancy는 각 고객마다 개별 애플리케이션을 운영하고, Multi-Tenancy는 하나의 애플리케이션을 공유

📌 Single-Tenancy vs Multi-Tenancy 비교

비교 항목Single-TenancyMulti-Tenancy
애플리케이션 각 고객마다 개별 운영 하나의 애플리케이션을 공유
데이터 저장 방식 각 고객별 개별 데이터베이스 하나의 데이터베이스에서 고객별 데이터 분리
운영 비용 높음 (고객별 관리 필요) 낮음 (하나의 시스템 관리)
보안 및 격리 높은 보안 (고객별 분리) 데이터 격리를 추가적으로 고려해야 함
적용 사례 금융, 엔터프라이즈 시스템 SaaS 기반 서비스 (Slack, Shopify, Salesforce 등)

📌 Multi-Tenancy를 사용하면 "비용을 절감하고 하나의 시스템으로 여러 고객을 지원 가능"


✅ 여기까지 Multi-Tenancy의 개념과 필요성을 배웠습니다!
👉 "그렇다면, Spring Boot에서 Multi-Tenancy를 어떻게 구현할까?"
✅ 2부에서 Spring Boot에서 Multi-Tenancy 아키텍처를 적용하는 방법을 배워봅시다!

 

반응형

 


 

2. 상세 주제 정리

 

1. Multi-Tenancy를 적용하는 방식

Spring Boot에서 Multi-Tenancy를 적용하는 방법은 크게 3가지가 있음

📌 Multi-Tenancy 적용 방식

방식설명장점단점
Separate Database 테넌트마다 개별 데이터베이스 사용 데이터 격리 보장 비용 증가, 관리 복잡
Shared Database, Separate Schema 하나의 데이터베이스에서 테넌트별 스키마 사용 데이터 격리 가능, 성능 최적화 복잡한 쿼리 관리 필요
Shared Database, Shared Schema 하나의 데이터베이스에서 테넌트별 컬럼으로 구분 운영 비용 절감, 유지보수 용이 보안 및 성능 문제 가능

📌 SaaS 환경에서는 주로 "Shared Database, Separate Schema" 방식이 많이 사용됨


2. Multi-Tenancy 적용 (테넌트별 데이터베이스 분리 방식)

각 테넌트별로 별도의 데이터베이스를 유지하는 방식 구현

📌 1️⃣ Multi-Tenant DataSource 설정

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;

public class MultiTenantDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getCurrentTenant(); // 현재 테넌트 가져오기
    }

    public void setTenantDataSources(Map<Object, Object> dataSources) {
        super.setTargetDataSources(dataSources);
        super.afterPropertiesSet();
    }
}

determineCurrentLookupKey()를 통해 현재 테넌트의 데이터베이스 선택

📌 2️⃣ TenantContext (현재 테넌트 식별)

public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setCurrentTenant(String tenantId) {
        currentTenant.set(tenantId);
    }

    public static String getCurrentTenant() {
        return currentTenant.get();
    }

    public static void clear() {
        currentTenant.remove();
    }
}

각 요청마다 ThreadLocal을 사용하여 현재 테넌트를 관리

📌 3️⃣ Multi-Tenant Filter (테넌트 식별 필터 추가)

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

public class TenantFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String tenantId = httpServletRequest.getHeader("X-Tenant-ID"); // 헤더에서 테넌트 ID 가져오기
        TenantContext.setCurrentTenant(tenantId);
        
        try {
            chain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

클라이언트가 X-Tenant-ID 헤더를 보내면 해당 테넌트의 데이터베이스를 사용

📌 4️⃣ DataSource 설정 (다중 데이터베이스 지원)

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource multiTenantDataSource(@Qualifier("tenant1DataSource") DataSource tenant1DataSource,
                                            @Qualifier("tenant2DataSource") DataSource tenant2DataSource) {
        MultiTenantDataSource dataSource = new MultiTenantDataSource();
        
        Map<Object, Object> dataSources = new HashMap<>();
        dataSources.put("tenant1", tenant1DataSource);
        dataSources.put("tenant2", tenant2DataSource);

        dataSource.setTenantDataSources(dataSources);
        return dataSource;
    }

    @Bean(name = "tenant1DataSource")
    public DataSource tenant1DataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/tenant1_db")
                .username("user1")
                .password("password1")
                .build();
    }

    @Bean(name = "tenant2DataSource")
    public DataSource tenant2DataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/tenant2_db")
                .username("user2")
                .password("password2")
                .build();
    }
}

각 테넌트에 대한 데이터베이스 연결을 설정하고, 동적으로 테넌트별 데이터베이스를 선택

📌 Multi-Tenancy를 적용하면 "하나의 애플리케이션에서 여러 고객을 분리하여 운영 가능"


✅ 여기까지 Spring Boot에서 Multi-Tenancy 아키텍처 구현 방법을 배웠습니다!
👉 "그렇다면, Spring Boot에서 Kafka 이벤트 스트리밍 시스템 구축은 어떻게 할까?"
✅ 다음 회차에서 Spring Boot에서 Kafka 이벤트 스트리밍 시스템 구축을 배워봅시다!

 

반응형