MinIO 오브젝트 스토리지 구축

1. 오브젝트 스토리지 중심의 데이터 파이프라인

  • 왜 현대 데이터 엔지니어링 아키텍처는
    • 과거의 파일 시스템(NAS, SAN)이나 관계형 데이터베이스(RDBMS)를 넘어
    • 오브젝트 스토리지(Object Storage)를 중심축으로 삼게 되었는가?

1.1 기존 아키텍처의 한계와 오브젝트 스토리지

  • 과거의 데이터 관리 방식
    • 디렉토리/파일 구조(POSIX 파일 시스템):
      • 폴더 구조가 깊어질수록 파일 탐색 속도가 기하급수적으로 느려지는 계층적 한계가 있음
    • 데이터베이스 블록 구조(SAN/RDBMS):
      • 고성능이지만 저장 비용이 매우 비쌈
      • 비정형 데이터(이미지, 영상, 대용량 로그)를 담기에 부적합
  • 오브젝트 스토리지의 혁신
    • 오브젝트 스토리지는 데이터 관리를 “평면적 구조(Flat Structure)”로 완전히 전환함

    • 고유 키(Key) 매핑:
      • 폴더 계층 없이 모든 파일은 버킷(Bucket)이라는 거대한 단일 공간에 고유한 주소(Key)를 가진 ‘오브젝트’ 형태로 저장됨
        • 고유 주소(Key)의 형태 예시: raw-data-lake/2026/06/11/log.txt
    • 무한한 확장성(Scalability):
      • 주소값만 알면 데이터가 페타바이트(PB), 엑사바이트(EB) 단위로 늘어나도 \(O(1)\)에 수렴하는 일정한 탐색 성능을 보장함

1.2 현대 데이터 스택의 핵심: 저장과 연산의 분리

  • 과거의 데이터 웨어하우스(Hadoop, 전통적 RDBMS 등)
    • 데이터를 저장하는 하드웨어와 데이터를 계산(쿼리, 분석)하는 하드웨어가 물리적으로 묶여 있음
    • 이로 인해 분석을 많이 하려면 필요 없는 저장용 서버까지 세트로 증설해야 하는 비용 낭비가 심함
  • 현대 데이터 아키텍처
    • 철저한 컴퓨팅(Compute)과 스토리지(Storage)의 분리(Decoupling) 구조로 설계

    • 스토리지 레이어 (MinIO):
      • 오직 안전하고 저렴하게 데이터를 저장하는 데만 집중 (비용 극대화 효율)
    • 컴퓨팅 레이어 (Python, Spark, Trino 등):
      • 분석이나 데이터 정제가 필요할 때만 CPU/GPU 자원을 켜서 스토리지에 요청하여 데이터를 가져옴 (자원 효율성)
  • 이번 Python 실습이 바로 이 구조
    • MinIO(스토리지)는 가만히 있고, Python 스크립트(컴퓨팅)가 네트워크 API 통신을 통해 데이터를 넣고 빼는 구조적 분리의 경험이 목적

1.3 Schema-on-Read 개념

  • 전통적인 데이터베이스(Oracle, MySQL 등)
    • 데이터를 집어넣기 전에 반드시 테이블 구조(Column 이름, 데이터 타입 등)를 엄격하게 정의해야 하는 Schema-on-Write (쓰기 시점의 스키마 정의) 방식
    • 규격에 맞지 않는 데이터는 에러 발생
  • 오브젝트 스토리지 중심의 데이터 레이크(Data Lake)
    • Schema-on-Read (읽기 시점의 스키마 정의) 방식

    • 원시 데이터 그대로 저장(Landing):
      • 데이터가 생성되는 시점에는 그것이 텍스트든 JSON이든 관계없이 오브젝트 스토리지(MinIO)에 날것(Raw) 그대로 빠르게 적재함
    • 해석은 읽는 자의 몫:
      • 나중에 데이터를 가져가서 분석하는 프로그램(Python, Spark 등)이 데이터를 읽어 들이는 시점에 정의
        • 예: 데이터를 읽으면서 “이 구조는 콤마(,)로 분리되어 있으니 표 형태로 해석하겠다”라고 정의
    • 이점:
      • 시스템 변화에 매우 유연함
      • 데이터 유실 없이 모든 원천 데이터를 영구 보관할 수 있음

1.4 RESTful API 기반 데이터 파이프라인의 표준화

  • 과거에는 파일 전송을 위해 FTP, SAMBA, NFS 등 운영체제나 네트워크 환경에 종속적인 프로토콜을 사용해야 했음
  • 이는 방화벽 설정이 복잡하고 웹 환경에서 다루기 어려움

  • 오브젝트 스토리지는 모든 데이터의 입출력을 웹의 기본 통신 규격인 HTTP/HTTPS (RESTful API) 기반으로 처리함
    • 데이터 업로드: HTTP PUT /raw-data-lake/sample.txt
    • 데이터 다운로드: HTTP GET /raw-data-lake/sample.txt
    • 데이터 삭제: HTTP DELETE /raw-data-lake/sample.txt
  • Amazon S3가 정의한 이 HTTP 통신 규칙이 너무나 완벽했기에 전 세계 개발사들이 이를 표준으로 삼았음
  • MinIO는 이 HTTP API 명세를 로컬 시스템 상에 그대로 구현해 둔 소프트웨어


  • 이론적 배경 요약
    • 이번 실습은 단순히 코드를 실행해 보는 것을 넘어,
      • 비용이 저렴하고 무한 확장이 가능한 평면적 저장소(MinIO)에,
      • 데이터 형식을 가리지 않고 원시 형태(Schema-on-Read)로 안전하게 던져놓고,
      • 필요할 때마다 독립된 연산 장치(Python)가 웹 표준 프로토콜(S3 API)로 데이터를 제어하는
    • 현대 데이터 엔지니어링의 기본 패러다임을 몸소 체득하는 과정

2. MinIO기반 오브젝트 스토리지 파이프라인 구축 실습

  • 목표: 파일, 데이터를 프로그램(Python 등)을 통해 MinIO 스토리지에 직접 적재, 관리하는 오브젝트 스토리지 중심의 데이터 파이프라인 구축
    • 저장소 계층 (Storage Layer): 로컬 PC에 띄운 MinIO 엔진
    • 데이터 생성/소비 계층 (App Layer): Python 스크립트 (AWS S3 표준 라이브러리인 boto3 활용)

2.1 MinIO 환경 구축

  • 가장 빠르고 간편한 Docker 환경을 기준으로 진행함


  • [1단계] 로컬에 MinIO 스토리지 엔진 구동하기
    • 터미널(또는 커맨드 창)에서 아래 명령어를 실행하여 로컬 S3 환경을 만듦

        docker run -d \
        -p 9000:9000 \
        -p 9001:9001 \
        --name local-s3-storage \
        -v ~/minio/data:/data \
        -e "MINIO_ROOT_USER=myaccesskey" \
        -e "MINIO_ROOT_PASSWORD=mysecretkey" \
        minio/minio server /data --console-address ":9001"
      


  • [2단계] 웹 콘솔에서 실습용 버킷(Bucket) 만들기
    1. 브라우저에서 http://localhost:9001에 접속
    2. ID: myaccesskey / PW: mysecretkey로 로그인
    3. Buckets 메뉴 $\rightarrow$ Create Bucket을 눌러 raw-data-lake라는 이름의 버킷 생성

2.2 Python 코드로 MinIO(S3 API) 연동

  • Python 코드로 로컬 스토리지에 데이터를 넣고 빼는 실습 진행
  • 아마존 S3와 100% 호환되기 때문에 AWS 공식 라이브러리인 boto3를 그대로 사용함


  • [1단계] 라이브러리 설치

      pip install boto3
    


  • [2단계] 데이터 업로드 및 다운로드 스크립트
#//file: "app.py"
import boto3
from botocore.client import Config

# 1. 로컬 MinIO 서버 연결 설정 (핵심은 endpoint_url 지정!)
s3_client = boto3.client(
    's3',
    endpoint_url='http://localhost:9000',          # 로컬 MinIO API 포트
    aws_access_key_id='myaccesskey',               # MinIO ROOT_USER
    aws_secret_access_key='mysecretkey',           # MinIO ROOT_PASSWORD
    config=Config(signature_version='s3v4')
)

bucket_name = 'raw-data-lake'

# 2. 테스트용 텍스트 파일 생성 및 업로드
with open("local_sample.txt", "w", encoding="utf-8") as f:
    f.write("이 데이터는 로컬 MinIO 오브젝트 스토리지에 저장되는 테스트 데이터입니다.")

print("1. 파일 업로드 시작...")
s3_client.upload_file('local_sample.txt', bucket_name, 'cloud_storage_sample.txt')
print("업로드 완료! 웹 콘솔(http://localhost:9001)에서 파일을 확인해보세요.\n")


# 3. MinIO 스토리지에서 파일 다운로드 테스트
print("2. 스토리지에서 파일 다운로드 시작...")
s3_client.download_file(bucket_name, 'cloud_storage_sample.txt', 'downloaded_result.txt')

with open("downloaded_result.txt", "r", encoding="utf-8") as f:
    print(f"다운로드된 파일 내용: {f.read()}")


  • 요약 및 비유
    • 집(DataLake)을 지을 때,
      • MinIO는 ‘토지와 콘크리트 골조’를 만드는 것이고,
      • Iceberg는 ‘방을 나누는 인테리어(구조화)’,
      • Trino는 ‘거기에 입주해서 편리하게 가전을 이용하는 것(조회)’과 같음

    • 지금은 인테리어(Iceberg)와 입주 가전(Trino)이 없더라도,
      • **튼튼한 골조(MinIO 오브젝트 스토리지 구축)를 짜고
      • 그 빈 공간에 택배 박스(데이터 파일)를 넣고 빼는 작업**은 100% 완벽하게 진행할 수 있음

3. 시나리오 기반 실습

  • pip install boto3 pandas pyarrow 라이브러리가 설치된 상태를 전제로 진행함

3.1 Pandas 연계 대용량 데이터 파이프라인 구축

  • 시나리오 및 목적
  • 매초 수만 건씩 쏟아지는 주가/센서 데이터를 로컬 하드디스크에 파일로 저장한 뒤 업로드
    🡲 I/O 병목이 생기고 디스크가 남아나지 않음
  • 메모리상에서 곧바로 대용량 분석용 압축 포맷인 Parquet(파케)로 변환 🡲 MinIO 데이터 레이크에 연/월/일 구조로 적재


  • 실습 코드
#//file: "pandas_pipeline.py"
import io
import random
from datetime import datetime
import boto3
import pandas as pd

# 1. 가상 데이터 생성 로직 (10만 건의 주가 거래 데이터)
print("1. 대용량 가상 주가 데이터 생성 중...")
ticker_list = ["AAPL", "TSLA", "NVDA", "MSFT", "GOOGL"]
records = []

for _ in range(100000):
    records.append(
        {
            "timestamp": datetime.now().isoformat(),
            "ticker": random.choice(ticker_list),
            "price": round(random.uniform(10, 1000), 2),
            "volume": random.randint(1, 500),
        }
    )

df = pd.DataFrame(records)
print(f"   - 생성 완료: 총 {len(df)}행 / 메모리 사용량: {df.memory_usage().sum() / 1024**2:.2f} MB")

# 2. 로컬 디스크를 거치지 않고 메모리 상에서 Parquet 압축
print("2. 인메모리 버퍼를 이용한 Parquet 변환 및 압축 중...")
parquet_buffer = io.BytesIO()
df.to_parquet(parquet_buffer, index=False, compression="snappy")
parquet_buffer.seek(0)  # 버퍼의 포인터를 처음으로 되돌림

# 3. MinIO 연결 설정
s3_client = boto3.client(
    "s3",
    endpoint_url="http://localhost:9000",
    aws_access_key_id="minioadmin",
    aws_secret_access_key="minioadmin",  # 테스트를 위해 임시로 마스터 키 사용
)

# 4. 데이터 레이크 표준 경로 구조(Partition) 설계 및 적재
now = datetime.now()
s3_path = f"stocks/year={now.year}/month={now.strftime('%m')}/day={now.strftime('%d')}/stock_data.parquet"

print(f"3. MinIO 데이터 레이크 적재 시작 -> 버킷 경로: raw-data-lake/{s3_path}")
s3_client.put_object(
    Bucket="raw-data-lake", Key=s3_path, Body=parquet_buffer.getvalue()
)
print("   - 적재 완료! 웹 콘솔(http://localhost:9001)에서 구조를 확인하세요.")

  • 코드 설명
    • io.BytesIO():
      • 파이썬 메모리 공간에 가상의 ‘바이너리 파일’ 버퍼를 만듦
      • 물리적인 HDD/SSD를 읽고 쓰지 않으므로 연산 속도가 압도적으로 빠름
    • df.to_parquet(..., compression='snappy'):
      • 대규모 데이터 인프라의 표준 파일 포맷인 Parquet로 변환
      • 행(Row) 기반이 아닌 열(Column) 기반 저장 포맷
      • 내부적으로 강력한 압축이 들어가 용량을 70% 이상 절감
    • s3_path 설정:
      • year=2026/month=06/... 와 같은 계층 구조
        🡲 Apache Iceberg, Trino 등의 쿼리 엔진이 특정 날짜의 데이터만 초고속으로 골라 읽을 수 있도록 만드는 파티셔닝의 물리적 기초가 됨


  • 주의 깊게 봐야 할 점
    • “오브젝트 스토리지에는 원래 폴더(디렉토리)라는 개념이 존재하지 않음”
      • MinIO 웹 콘솔에서는 폴더 구조처럼 깔끔하게 분류되어 보이지만,
      • 실제로는 stocks/year=2026/.../stock_data.parquet라는 문자열 전체가 하나의 고유 Key(이름)
      • 평면적 구조를 콘솔이 가상으로 시각화해 주는 것뿐이라는 메커니즘을 이해하는 것이 중요

3.2 멀티 테넌시(Multi-tenancy) 권한 분리 실습

  • 시나리오 및 목적
  • 사내 인프라를 구축했음
  • 인사팀(hr) 개발자가 사용하는 프로그램이 재무팀(finance)의 민감한 급여 데이터 버킷을 들여다보면 🡲 대형 보안 사고
  • 웹 콘솔에서 제한된 권한의 전용 열쇠를 발급하고, 파이썬 코드 레벨에서 완벽하게 차단·안전제어(try-except)되는지 검증함


  • 실습 준비 (웹 콘솔 작업)

    1. http://localhost:9001 접속 (minioadmin 로그인)
    2. Buckets 🡲 hr-bucketfinance-bucket을 각각 생성함
    3. Access Keys 🡲 Create Access Key 클릭
    4. 인사팀 전용 키 발급:
      • 아래처럼 Restricted Policy 규칙을 수립
        • Account Policy 지정 시 readwrite를 고르되, 오직 hr-bucket에만 매핑되도록 콘솔 UI에서 지정하거나,
        • 생성 후 발급된 인사팀용 Access Key/Secret Key를 메모함
        • 귀찮다면 콘솔 우측의 Account 🡲 Users를 통해 간단히 제어해도 되지만,
        • 입문자는 Access Key 자체에 내장 정책을 부여하는 방식이 간편함


  • 실습 코드
#//file: "security_test.py"
import boto3
from botocore.exceptions import ClientError

# [주의] 웹 콘솔에서 발급받은 '인사팀 전용 restricted key'를 입력하
HR_ACCESS_KEY = "인사팀_액세스키_입력"
HR_SECRET_KEY = "인사팀_시크릿키_입력"

# 인사팀 권한으로 S3 클라이언트 초기화
hr_session = boto3.client(
    "s3",
    endpoint_url="http://localhost:9000",
    aws_access_key_id=HR_ACCESS_KEY,
    aws_secret_access_key=HR_SECRET_KEY,
)

# 시나리오 1: 본인 팀의 hr-bucket에 데이터 적재 시도
try:
    print("\n[테스트 1] 인사팀 문서고(hr-bucket)에 신규 인사 명부 적재 시도...")
    hr_session.put_object(
        Bucket="hr-bucket",
        Key="2026_organization_chart.txt",
        Body="1. 홍길동 부장, 2. 임꺽정 과장",
    )
    print("-> [성공] 인사팀 버킷에는 정상적으로 업로드되었습니다.")
except ClientError as e:
    print(f"-> [실패] 권한 오류 발생: {e}")

# 시나리오 2: 권한이 없는 finance-bucket에 접근 시도
try:
    print("\n[테스트 2] 보안 구역인 재무팀 문서고(finance-bucket) 탈취 시도...")
    # 억지로 조회를 시도함
    hr_session.list_objects_v2(Bucket="finance-bucket")
    print("-> [위험] 보안 뚫림! 재무팀 데이터가 조회되었습니다.")
except ClientError as e:
    # 실무 데이터 파이프라인에서 가장 중요한 예외 처리 패턴
    if e.response["Error"]["Code"] == "AccessDenied":
        print("-> [안전] 차단 성공! 전용 키 정책에 의해 접근이 거부되었습니다(403 Forbidden).")
    else:
        print(f"-> 기타 에러 발생: {e}")

  • 코드 설명
    • ClientError:
      • 외부 API(AWS, MinIO 등) 통신 중에 발생하는 모든 실패 응답을 잡아내는 botocore 라이브러리의 핵심 예외 클래스
    • e.response['Error']['Code'] == 'AccessDenied':
      • 인프라가 던진 에러 코드가 정밀하게 ‘권한 거부’인지를 소프트웨어 코드가 판별
      • 해킹 시도나 잘못된 권한 설정을 로그로 남기고
      • 시스템이 다운되지 않게 방어해 주는 실무형 예외 처리 로직


  • 주의 깊게 봐야 할 점
    • 백엔드 코드와 인프라의 권한 관리는 항상 “최소 권한의 원칙(Least Privilege)”을 지켜야 함
    • 파이썬 마스터 키(minioadmin)를 소스 코드에 하드코딩해 두는 버릇을 버릴것
    • 프로젝트 단위, 팀 단위로 쪼개진 환경 변수 기반의 Key 관리가 왜 필수적인지 흐름을 파악하는 것이 핵심

3.3 기간 한정 다운로드 및 대용량 파일 분할 업로드

  • 시나리오 및 목적
  • 사용자에게 결제용 PDF 영수증이나 AI가 생성한 고화질 이미지 리포트를 다운로드받게 해 주어야 함
  • 스토리지 전체를 대중에게 공개(Public) 상태로 열어두면 URL이 유출되었을 때 무단 트래픽 폭탄을 맞을 수 있음
  • 저장소는 철저히 폐쇄(Private)로 잠가두고, 파이썬 코드로 딱 5분간만 문이 열리는 일회용 특수 다운로드 통로(Presigned URL)를 발행


  • 실습 코드
#//file: "presigned_url.py"
import os
import boto3
from boto3.s3.transfer import TransferConfig

# 1. 안전한 연결 수립
s3_client = boto3.client(
    "s3",
    endpoint_url="http://localhost:9000",
    aws_access_key_id="minioadmin",
    aws_secret_access_key="minioadmin",
)

bucket_name = "raw-data-lake"
object_key = "secure_reports/executive_summary_2026.bin"

# 2. 실습을 위한 대용량 더미 파일(약 20MB) 생성
print("1. 실습용 대용량 가상 파일 생성 중 (약 20MB)...")
with open("large_dummy.bin", "wb") as f:
    f.write(os.urandom(20 * 1024 * 1024))  # 20MB 대역의 임의 바이너리 채움

# 3. 고급 전송 옵션(TransferConfig)을 적용한 안정적 분할 업로드
# 입문자가 대용량 메커니즘을 경험할 수 있도록 조각 단위를 5MB로 강제 하향 설정
config = TransferConfig(
    multipart_threshold=5 * 1024 * 1024,  # 5MB 이상이면 분할 시작
    multipart_chunksize=5 * 1024 * 1024,  # 조각 당 5MB 크기
)

print("2. 설정된 크기(5MB)로 쪼개어 MinIO에 멀티파트 업로드 시작...")
s3_client.upload_file(
    "large_dummy.bin", bucket_name, object_key, Config=config
)
print("   - 업로드 완료.")

# 4. 딱 5분(300초)만 유효한 보안 주소(Presigned URL) 발행
print("\n3. 해당 파일에 접근할 수 있는 5분 한정 비밀 링크 구워내는 중...")
secure_url = s3_client.generate_presigned_url(
    ClientMethod="get_object",
    Params={"Bucket": bucket_name, "Key": object_key},
    ExpiresIn=300,  # 유효 시간 초 단위 (5분)
)

print("-" * 60)
print("발행된 임시 보안 URL (아래 주소를 복사해 브라우저에 붙여넣어 보세요):")
print(secure_url)
print("-" * 60)
print("[실험 방법] 지금 즉시 브라우저에서 다운로드 해보고, 5분이 지난 후 다시 접속해보세요.")

# 실습 정리용 로컬 파일 삭제
if os.path.exists("large_dummy.bin"):
    os.remove("large_dummy.bin")
  • 코드 설명
    • TransferConfig:
      • 파일 업로드 시 네트워크 불안정으로 끊기면 처음부터 다시 올리는 불상사를 막기 위해,
      • 내부적으로 파일을 연산 조각 단위로 나누고, 멀티 스레드로 동시에 쏘아 올리는 Multipart Upload 기능을 활성화하는 클래스
    • generate_presigned_url():
      • 이 함수는 MinIO 서버와 통신하지 않고 파이썬 내부에서 작동함
      • 스토리지의 비밀키 정보를 기반으로 URL 문자열 자체에 시한부 디지털 서명 암호문을 인코딩하여 결합


  • 주의 깊게 봐야 할 점
    • 발행된 긴 URL의 끝부분을 보면 X-Amz-Expires=300&X-Amz-Signature=... 와 같은 서명 값이 붙어있음
    • 5분이 지난 후에 해당 URL로 새로고침을 하면,
      • MinIO 서버가 시간 초과를 계산하여
      • Request has expired라는 만료 에러 XML을 브라우저에 띄우며
      • 다운로드를 완벽히 거부하는 시스템 메커니즘의 정교함을 확인할 것


  • 실습한 3가지 핵심 메커니즘(인메모리 Parquet 파이프라인, 멀티 테넌시 권한 분리, 시한부 Presigned URL)은
  • 실제 IT 현업에서 수백억 대 자산을 다루는 엔터프라이즈 시스템의 핵심 뼈대

4. 실습 내용의 활용형태

  • 이 실습들을 현업의 어떤 상황에서, 어떤 아키텍처로 도입할 수 있는지 구체적인 비즈니스 유스케이스(Use Case)로 정리

4.1 실습 1 (3.1)의 활용

  • 사물인터넷(IoT) 스마트팩토리 센서 데이터 수집 파이프라인
    • 생산 공장의 설비나 진동 센서 등에서 24시간 내내 쏟아지는 초고속 비정형 데이터를 정제하고 누적할 때 도입
  • 도입 상황:
    • 공장 내 수백 대의 장비에서 초당 수천 건의 로그와 센서 계측치(온도, 압력 등)가 발생할 때,
      • 이를 RDBMS(MySQL 등)에 그대로 넣으면 DB가 동시 쓰기 부하를 견디지 못하고 뻗어버림
    • 로컬 서버에 파일로 저장하기에는 디스크 용량 한계와 관리 부담이 큼
  • 실무 도입 방식:
    1. Edge 디바이스나 데이터 수집 서버(Python/FastAPI)가 데이터를 메모리상에서 즉시 흡수함
    2. 데이터의 유실을 막기 위해 1분 단위 또는 10만 건 단위로 데이터를 묶어(Batching) 메모리 상에서 Parquet 포맷으로 압축
    3. 사내 프라이빗 서버에 구축된 MinIO에 year=2026/month=06/day=12/line_A_sensor.parquet 형태로 적재
  • 도입 효과:
    • RDBMS 대비 스토리지 비용을 10분의 1 이하로 절감하면서도, 대량의 쓰기 부하를 완벽하게 분산시킴
    • 이후 Apache Iceberg와 Trino를 위에 얹으면 이 데이터를 그대로 표준 SQL로 조회할 수 있는 대형 데이터 레이크하우스로 자연스럽게 확장됨

4.2 실습 2 (3.2)의 활용

  • SaaS 및 멀티 테넌트 B2B 기업용 시스템의 데이터 격리
    • 하나의 소프트웨어를 여러 기업 고객(고객사)들이 나누어 쓰는 SaaS(Software as a Service) 아키텍처를 설계할 때 도입
  • 도입 상황:
    • 회사가 엔터프라이즈 인사/재무 관리 프로그램을 만들어 A사, B사, C사에 동시에 구독형으로 판매하고 있음
      • 프로그램의 버그나 오작동으로 인해
      • A사 직원이 B사의 대외비 계약서 파일 주소를 유추하여 다운로드받는 대형 보안 사고를 원천 차단해야 함
  • 실무 도입 방식:
    1. 플랫폼의 최고 권한(Master Key)은 인프라 관리자만 보유
    2. 새로운 기업 고객(Tenant)이 가입할 때마다,
      • 백엔드 시스템이 자동으로 MinIO 웹 콘솔의 IAM API를 호출하여
      • 해당 고객 전용의 Access Key와 독점 버킷(customer-a-bucket) 정책을 동적으로 생성
    3. 해당 고객의 세션으로 구동되는 앱 컨테이너는 오직 자기 버킷에만 접근할 수 있는 제한된 열쇠로만 스토리지와 통신
  • 도입 효과:
    • 소스 코드 레이어(애플리케이션)에서 “이 유저가 A사 유저인가?”를 체크하는 로직이 실수로 누락되더라도,
    • 하부 인프라 레이어(MinIO IAM)에서 2중으로 완벽하게 접근을 차단
    • 강력한 컴플라이언스 및 데이터 격리 보안을 달성

4.3 실습 3 (3.3)의 활용

  • 유료 콘텐츠 플랫폼 및 뱅킹 시스템의 대용량 보안 다운로드
    • 결제된 유저에게만 한시적으로 파일을 제공해야 하는 비즈니스 모델이나 대용량 자산 업로드가 필요할 때 도입
  • 도입 상황:
    • 웹툰/웹소설, 유료 인터넷 강의 PDF 교재, 혹은 인터넷 뱅킹의 대용량 통장 거래 내역서 출력 등 ‘돈을 지불한 사람’이나 ‘본인’만 안전하게 다운로드해야 하는 상황
    • 스토리지 주소가 고정된 http://.../file.pdf 형태라면, 🡲 주소가 커뮤니티에 유출되는 순간 무단 트래픽과 지적재산권 침해로 막대한 손해가 발생함
  • 실무 도입 방식:
    1. MinIO 스토리지의 모든 버킷과 오브젝트는 [Private(비공개)]로 꽁꽁 잠가둠 🡲 외부 주소로는 절대 접근할 수 없음
    2. 사용자가 ‘다운로드’ 버튼을 클릭 🡲 백엔드(Python)가 세션과 결제 여부를 검증
    3. 검증 통과 🡲 boto3를 이용해 딱 3분간만 유효한 시한부 Presigned URL을 동적으로 생성 🡲 사용자의 웹 브라우저로 리다이렉트
    4. 동시에 100MB가 넘어가는 대용량 첨부파일의 경우,
      • 사용자가 업로드할 때 네트워크가 끊겨도 이어 올릴 수 있도록
      • 백엔드 단에서 TransferConfig 기반의 멀티파트 전송으로 안정성을 확보
  • 도입 효과:
    • 주소가 유출되더라도 몇 분 뒤면 완전히 무효화됨 🡲 무단 링크 복제(Hotlinking)를 원천 차단
    • 스토리지 서버가 직접 인증 처리를 분담 🡲 백엔드 웹 서버의 CPU/네트워크 부하를 획기적으로 낮춰 줌
  • 실무 도입을 위한 총괄 요약
    • 실습한 세 가지 기술은 현대 아키텍처의 핵심인 “싸고(Parquet), 안전하며(IAM), 효율적인(Presigned URL) 데이터 유통 채널”을 만드는 정석
    • 이 개념들을 뼈대로 잡고 운영하다가,
      • 데이터의 양이 늘어나 “과거 데이터 수정/삭제가 필요한 시점”이 오면 🡲 Apache Iceberg를 그 위에 얹음
      • 이 데이터들을 파이썬 코드 없이 BI 툴이나 대시보드에서 SQL로 즉시 뽑아보고 싶다” 할 때 🡲 Trino를 연동
    • 기업 표준의 완벽한 모던 데이터 스택(Modern Data Stack)이 완성됨

© 2020. AiDALab Co. All rights reserved.

Powered by Hydejack v9.2.1