← Theory 목록으로

Session 7: 쿼리 & 분석 서비스

Athena, QuickSight, OpenSearch 심화 및 시각화 패턴

1. Amazon Athena 심층 분석

Amazon Athena는 S3 데이터를 표준 SQL로 쿼리하는 서버리스 대화형 쿼리 서비스입니다. Presto/Trino 엔진 기반으로 페타바이트 규모 데이터를 분석할 수 있습니다.

Athena 아키텍처
┌─────────────────────────────────────────────────────────────────────┐
│                    Amazon Athena Architecture                        │
│                                                                      │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────────────────┐  │
│  │   Client    │───▶│   Athena    │───▶│    Glue Data Catalog    │  │
│  │ (Console/   │    │   Engine    │    │    (메타데이터)         │  │
│  │  SDK/JDBC)  │    │ (Presto)    │    └─────────────────────────┘  │
│  └─────────────┘    └──────┬──────┘                                 │
│                            │                                         │
│                            ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                         Amazon S3                                ││
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐               ││
│  │  │   Parquet   │ │    JSON     │ │     CSV     │               ││
│  │  │   (권장)    │ │             │ │             │               ││
│  │  └─────────────┘ └─────────────┘ └─────────────┘               ││
│  └─────────────────────────────────────────────────────────────────┘│
│                                                                      │
│  비용: 스캔한 데이터량 기준 ($5/TB)                                 │
└─────────────────────────────────────────────────────────────────────┘

1.2 비용 최적화

Athena 비용 절감 전략

1. Parquet/ORC 사용

컬럼형 포맷은 필요한 컬럼만 읽어 스캔량 대폭 감소

JSON 100GB → Parquet 10GB (90% 절감)

2. 파티셔닝

WHERE 절에서 파티션 필터링으로 스캔 범위 제한

-- 파티션 프루닝
SELECT * FROM logs 
WHERE year='2024' AND month='01'  -- 해당 파티션만 스캔

3. 컬럼 선택

SELECT * 대신 필요한 컬럼만 명시

-- Bad: SELECT * FROM orders (전체 컬럼 스캔)
-- Good: SELECT order_id, amount FROM orders

4. 압축

GZIP, Snappy, ZSTD 압축으로 파일 크기 감소 → 스캔 비용 감소

💰 비용 계산 예시

원본 JSON 1TB 쿼리: $5.00
Parquet 변환 후 (100GB): $0.50
+ 파티션 프루닝 (10GB): $0.05
총 절감: 99%

1.3 Athena 고급 기능

-- CTAS: 쿼리 결과를 새 테이블로 저장
CREATE TABLE curated.orders_summary
WITH (
  format = 'PARQUET',
  parquet_compression = 'SNAPPY',
  partitioned_by = ARRAY['year', 'month']
) AS
SELECT 
  customer_id,
  SUM(amount) as total_amount,
  year, month
FROM raw.orders
GROUP BY customer_id, year, month;

-- Federated Query: 다른 데이터 소스 쿼리
SELECT * FROM awsdatacatalog.database.table  -- Glue Catalog
UNION ALL
SELECT * FROM mysql_connector.database.table  -- RDS MySQL

-- Workgroups: 비용 제어
-- 쿼리당 스캔 제한, 팀별 비용 분리

2. Amazon QuickSight

Amazon QuickSight는 클라우드 네이티브 BI 서비스입니다. SPICE 인메모리 엔진으로 빠른 대시보드를 제공하고, ML 기반 인사이트를 자동 생성합니다.

QuickSight 아키텍처
┌─────────────────────────────────────────────────────────────────────┐
│                    Amazon QuickSight                                 │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                      Data Sources                                ││
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐  ││
│  │  │ Athena  │ │Redshift │ │   RDS   │ │   S3    │ │  SaaS   │  ││
│  │  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘  ││
│  └───────┼──────────┼──────────┼──────────┼──────────┼───────────┘│
│          └──────────┴──────────┴──────────┴──────────┘            │
│                              │                                     │
│                              ▼                                     │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                    SPICE Engine                                  ││
│  │  • 인메모리 캐시 (Super-fast, Parallel, In-memory)              ││
│  │  • 자동 데이터 새로고침                                          ││
│  │  • 10GB/사용자 포함                                              ││
│  └─────────────────────────────────────────────────────────────────┘│
│                              │                                     │
│                              ▼                                     │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                    Dashboards & Analysis                         ││
│  │  • 대화형 대시보드                                               ││
│  │  • ML Insights (이상 탐지, 예측)                                 ││
│  │  • 임베딩 (웹앱 통합)                                            ││
│  └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘

SPICE 모드

  • • 데이터를 SPICE로 가져옴
  • • 빠른 대시보드 로딩
  • • 스케줄 새로고침
  • • 소스 부하 없음

Direct Query 모드

  • • 실시간 데이터 쿼리
  • • 대용량 데이터셋
  • • 소스 성능에 의존
  • • 추가 SPICE 비용 없음

2.2 QuickSight Q (자연어 쿼리)

ML 기반 기능

QuickSight Q

자연어로 질문하면 자동으로 시각화 생성

"지난 달 지역별 매출은?"

→ 자동으로 막대 차트 생성

ML Insights

  • 이상 탐지: 비정상 패턴 자동 감지
  • 예측: 시계열 데이터 예측
  • 기여도 분석: 변화 원인 분석

2.3 임베딩

QuickSight 대시보드를 웹 애플리케이션에 임베딩하여 고객에게 분석 기능을 제공할 수 있습니다.

// Lambda - 임베딩 URL 생성
const quicksight = new AWS.QuickSight();

const params = {
  AwsAccountId: '123456789012',
  DashboardId: 'dashboard-id',
  IdentityType: 'ANONYMOUS',  // 또는 IAM, QUICKSIGHT
  SessionLifetimeInMinutes: 600,
  Namespace: 'default'
};

const response = await quicksight.getDashboardEmbedUrl(params).promise();
// response.EmbedUrl을 iframe src로 사용

3. Amazon OpenSearch

Amazon OpenSearch Service는 실시간 검색, 로그 분석, 모니터링을 위한 관리형 서비스입니다. Elasticsearch 호환 API를 제공하며 OpenSearch Dashboards로 시각화합니다.

OpenSearch 사용 사례

로그 분석

  • • 애플리케이션 로그 검색
  • • 에러 패턴 분석
  • • 실시간 모니터링

전문 검색

  • • 제품 검색
  • • 문서 검색
  • • 자동 완성
┌─────────────────────────────────────────────────────────────────────┐
│                    로그 분석 파이프라인                               │
│                                                                      │
│  ┌─────────┐    ┌─────────┐    ┌─────────────┐    ┌─────────────┐  │
│  │ 로그    │───▶│ Kinesis │───▶│  OpenSearch │───▶│ Dashboards  │  │
│  │ 소스    │    │Firehose │    │   Cluster   │    │  (시각화)   │  │
│  └─────────┘    └─────────┘    └─────────────┘    └─────────────┘  │
│                                                                      │
│  CloudWatch Logs → Subscription Filter → OpenSearch                 │
└─────────────────────────────────────────────────────────────────────┘

3.2 OpenSearch Serverless

Serverless vs Provisioned
특성ServerlessProvisioned
관리완전 자동클러스터 관리
스케일링자동 (OCU 기반)수동
비용사용량 기반인스턴스 시간
적합한 사용간헐적, 예측 불가지속적, 대규모

// OpenSearch 쿼리 예시

// 로그 검색
GET /logs-*/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "level": "ERROR" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "aggs": {
    "errors_by_service": {
      "terms": { "field": "service.keyword" }
    }
  }
}

4. 정리 및 다음 세션 예고

4.1 서비스 선택 가이드

사용 사례권장 서비스
Ad-hoc SQL 쿼리Athena
BI 대시보드QuickSight
로그 분석/검색OpenSearch
복잡한 분석/MLRedshift

4.2 핵심 요약

Athena

서버리스 SQL 쿼리. Parquet + 파티셔닝으로 비용 99% 절감 가능.

QuickSight

SPICE 인메모리 엔진으로 빠른 대시보드. ML Insights, 임베딩 지원.

OpenSearch

실시간 로그 분석, 전문 검색. Serverless로 관리 부담 제거.

4.3 다음 세션 예고

Session 8: 데이터 거버넌스 & 보안

  • AWS Lake Formation 권한 관리
  • 데이터 암호화 및 IAM 정책
  • 데이터 품질 및 계보 (Lineage)

5. Athena 고급 기능

5.1 Athena Workgroups

워크그룹 구성
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Athena Workgroups                                         │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Workgroup: analytics-team                                              ││
│  │  ├── Query Result Location: s3://athena-results/analytics/             ││
│  │  ├── Data Scanned Limit: 100 GB per query                              ││
│  │  ├── Encryption: SSE-KMS                                                ││
│  │  ├── Enforce Workgroup Settings: true                                  ││
│  │  └── CloudWatch Metrics: enabled                                        ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Workgroup: data-science                                                ││
│  │  ├── Query Result Location: s3://athena-results/data-science/          ││
│  │  ├── Data Scanned Limit: 1 TB per query                                ││
│  │  ├── Requester Pays: enabled                                            ││
│  │  └── Query Execution Timeout: 30 minutes                               ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Workgroup: etl-jobs                                                    ││
│  │  ├── Engine Version: Athena engine version 3                           ││
│  │  ├── Concurrent Query Limit: 20                                         ││
│  │  └── Per-Query Data Usage Control: 500 GB                              ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

// Terraform - Workgroup 생성

resource "aws_athena_workgroup" "analytics" {
  name = "analytics-team"

  configuration {
    enforce_workgroup_configuration    = true
    publish_cloudwatch_metrics_enabled = true

    result_configuration {
      output_location = "s3://${aws_s3_bucket.athena_results.id}/analytics/"

      encryption_configuration {
        encryption_option = "SSE_KMS"
        kms_key_arn       = aws_kms_key.athena.arn
      }
    }

    engine_version {
      selected_engine_version = "Athena engine version 3"
    }

    bytes_scanned_cutoff_per_query = 107374182400  # 100 GB
  }

  tags = {
    Team = "analytics"
  }
}

# 워크그룹별 IAM 정책
resource "aws_iam_policy" "athena_analytics" {
  name = "athena-analytics-workgroup"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "athena:StartQueryExecution",
          "athena:GetQueryExecution",
          "athena:GetQueryResults"
        ]
        Resource = aws_athena_workgroup.analytics.arn
      }
    ]
  })
}

5.2 Athena ACID 트랜잭션

Apache Iceberg 통합
-- Iceberg 테이블 생성
CREATE TABLE iceberg_db.orders (
    order_id STRING,
    customer_id STRING,
    amount DECIMAL(10,2),
    order_date DATE,
    status STRING
)
PARTITIONED BY (order_date)
LOCATION 's3://data-lake/iceberg/orders/'
TBLPROPERTIES (
    'table_type' = 'ICEBERG',
    'format' = 'parquet',
    'write_compression' = 'snappy'
);

-- INSERT (ACID 지원)
INSERT INTO iceberg_db.orders
VALUES ('ORD-001', 'CUST-123', 99.99, DATE '2024-01-15', 'completed');

-- UPDATE (ACID 지원)
UPDATE iceberg_db.orders
SET status = 'shipped'
WHERE order_id = 'ORD-001';

-- DELETE (ACID 지원)
DELETE FROM iceberg_db.orders
WHERE order_date < DATE '2023-01-01';

-- MERGE (Upsert)
MERGE INTO iceberg_db.orders t
USING staging.new_orders s
ON t.order_id = s.order_id
WHEN MATCHED THEN
    UPDATE SET status = s.status, amount = s.amount
WHEN NOT MATCHED THEN
    INSERT (order_id, customer_id, amount, order_date, status)
    VALUES (s.order_id, s.customer_id, s.amount, s.order_date, s.status);

-- Time Travel (스냅샷 쿼리)
SELECT * FROM iceberg_db.orders
FOR TIMESTAMP AS OF TIMESTAMP '2024-01-14 10:00:00';

-- 특정 스냅샷 ID로 쿼리
SELECT * FROM iceberg_db.orders
FOR VERSION AS OF 1234567890;

-- 스냅샷 히스토리 조회
SELECT * FROM "iceberg_db"."orders$snapshots";

Iceberg 장점

  • • ACID 트랜잭션 지원
  • • Time Travel 쿼리
  • • Schema Evolution
  • • Hidden Partitioning

사용 사례

  • • CDC 데이터 처리
  • • 데이터 수정/삭제 필요
  • • 감사 추적 (히스토리)
  • • 동시 쓰기 워크로드

5.3 Athena Federated Query

Athena Federated Query를 사용하면 S3 외에도 다양한 데이터 소스를 쿼리할 수 있습니다.

┌─────────────────────────────────────────────────────────────────────────────┐
│                    Athena Federated Query                                    │
│                                                                              │
│                         ┌─────────────────┐                                 │
│                         │     Athena      │                                 │
│                         │                 │                                 │
│                         │  SELECT * FROM  │                                 │
│                         │  catalog.db.tbl │                                 │
│                         └────────┬────────┘                                 │
│                                  │                                          │
│              ┌───────────────────┼───────────────────┐                      │
│              ▼                   ▼                   ▼                      │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐               │
│  │  Lambda         │ │  Lambda         │ │  Lambda         │               │
│  │  Connector      │ │  Connector      │ │  Connector      │               │
│  │  (DynamoDB)     │ │  (RDS/Aurora)   │ │  (Redshift)     │               │
│  └────────┬────────┘ └────────┬────────┘ └────────┬────────┘               │
│           │                   │                   │                         │
│           ▼                   ▼                   ▼                         │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐               │
│  │    DynamoDB     │ │   RDS/Aurora    │ │    Redshift     │               │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘               │
└─────────────────────────────────────────────────────────────────────────────┘

// Federated Query 예시

-- DynamoDB 테이블 쿼리
SELECT * FROM dynamodb_catalog.default.users
WHERE user_id = 'USER-123';

-- RDS MySQL 쿼리
SELECT * FROM mysql_catalog.mydb.orders
WHERE order_date >= '2024-01-01';

-- S3와 DynamoDB 조인
SELECT 
    s.order_id,
    s.amount,
    d.user_name,
    d.email
FROM s3_catalog.analytics.orders s
JOIN dynamodb_catalog.default.users d
ON s.user_id = d.user_id
WHERE s.order_date = '2024-01-15';

-- Redshift와 S3 조인
SELECT 
    r.product_name,
    r.category,
    SUM(s.quantity) as total_sold
FROM redshift_catalog.sales.products r
JOIN s3_catalog.raw.transactions s
ON r.product_id = s.product_id
GROUP BY r.product_name, r.category;

5.4 비용 최적화 전략

Athena 비용 절감
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Cost Optimization Strategies                              │
│                                                                              │
│  1. 파일 포맷 최적화                                                         │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  CSV (1 TB) ────────────────────────────────────────▶ $5.00            ││
│  │  JSON (1 TB) ───────────────────────────────────────▶ $5.00            ││
│  │  Parquet (100 GB, 압축) ────────────────────────────▶ $0.50  (90% 절감)││
│  │  ORC (100 GB, 압축) ────────────────────────────────▶ $0.50  (90% 절감)││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  2. 파티셔닝                                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  전체 스캔 (1 TB) ──────────────────────────────────▶ $5.00            ││
│  │  파티션 필터 (10 GB) ───────────────────────────────▶ $0.05  (99% 절감)││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  3. 컬럼 선택                                                                │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  SELECT * (100 컬럼) ───────────────────────────────▶ $5.00            ││
│  │  SELECT col1, col2 (2 컬럼) ────────────────────────▶ $0.10  (98% 절감)││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  4. 결과 재사용                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  CTAS로 중간 결과 저장 → 반복 쿼리 비용 절감                            ││
│  │  Query Result Reuse 활성화 (동일 쿼리 캐시)                             ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

// CTAS로 최적화된 테이블 생성

-- CSV를 Parquet으로 변환 + 파티셔닝
CREATE TABLE optimized_orders
WITH (
    format = 'PARQUET',
    parquet_compression = 'SNAPPY',
    partitioned_by = ARRAY['year', 'month'],
    external_location = 's3://data-lake/optimized/orders/'
) AS
SELECT 
    order_id,
    customer_id,
    amount,
    order_date,
    YEAR(order_date) as year,
    MONTH(order_date) as month
FROM raw_orders;

-- 버킷팅 추가 (조인 최적화)
CREATE TABLE bucketed_orders
WITH (
    format = 'PARQUET',
    partitioned_by = ARRAY['order_date'],
    bucketed_by = ARRAY['customer_id'],
    bucket_count = 10
) AS
SELECT * FROM raw_orders;

6. QuickSight 고급 기능

6.1 SPICE 최적화

SPICE 아키텍처
┌─────────────────────────────────────────────────────────────────────────────┐
│                    SPICE (Super-fast, Parallel, In-memory                    │
│                           Calculation Engine)                                │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Data Sources                                                           ││
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                    ││
│  │  │   S3    │  │ Athena  │  │Redshift │  │   RDS   │                    ││
│  │  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘                    ││
│  │       └────────────┴────────────┴────────────┘                          ││
│  │                           │                                              ││
│  │                           ▼                                              ││
│  │  ┌─────────────────────────────────────────────────────────────────┐    ││
│  │  │                    SPICE Engine                                 │    ││
│  │  │                                                                 │    ││
│  │  │  ┌─────────────────────────────────────────────────────────┐   │    ││
│  │  │  │  In-Memory Columnar Storage                             │   │    ││
│  │  │  │  • 압축된 컬럼 데이터                                    │   │    ││
│  │  │  │  • 병렬 쿼리 처리                                        │   │    ││
│  │  │  │  • 자동 데이터 새로고침                                  │   │    ││
│  │  │  └─────────────────────────────────────────────────────────┘   │    ││
│  │  │                                                                 │    ││
│  │  │  Capacity: 10 GB (Standard) / 500 GB (Enterprise)              │    ││
│  │  │  Refresh: 매시간, 매일, 매주 스케줄                            │    ││
│  │  └─────────────────────────────────────────────────────────────────┘    ││
│  │                           │                                              ││
│  │                           ▼                                              ││
│  │  ┌─────────────────────────────────────────────────────────────────┐    ││
│  │  │                    Dashboard                                    │    ││
│  │  │  • 서브초 응답 시간                                             │    ││
│  │  │  • 동시 사용자 지원                                             │    ││
│  │  └─────────────────────────────────────────────────────────────────┘    ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

SPICE 사용 시

  • • 빠른 대시보드 응답
  • • 데이터 소스 부하 감소
  • • 오프라인 분석 가능
  • • 예측 가능한 비용

Direct Query 사용 시

  • • 실시간 데이터 필요
  • • 데이터가 너무 큼
  • • 자주 변경되는 데이터
  • • SPICE 용량 부족

6.2 QuickSight Q (자연어 쿼리)

ML 기반 자연어 분석
┌─────────────────────────────────────────────────────────────────────────────┐
│                    QuickSight Q                                              │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Natural Language Query                                                 ││
│  │                                                                          ││
│  │  User: "지난 달 지역별 매출은?"                                         ││
│  │                                                                          ││
│  │  ┌─────────────────────────────────────────────────────────────────┐    ││
│  │  │  Q Engine                                                       │    ││
│  │  │  1. 자연어 파싱                                                 │    ││
│  │  │  2. 의도 파악 (매출, 지역, 시간)                                │    ││
│  │  │  3. 데이터셋 매핑                                               │    ││
│  │  │  4. 쿼리 생성 및 실행                                           │    ││
│  │  │  5. 시각화 자동 선택                                            │    ││
│  │  └─────────────────────────────────────────────────────────────────┘    ││
│  │                                                                          ││
│  │  Result:                                                                ││
│  │  ┌─────────────────────────────────────────────────────────────────┐    ││
│  │  │  [Bar Chart]                                                    │    ││
│  │  │  서울: $1.2M  |████████████████████                            │    ││
│  │  │  부산: $800K  |█████████████                                    │    ││
│  │  │  대구: $500K  |████████                                         │    ││
│  │  │  인천: $450K  |███████                                          │    ││
│  │  └─────────────────────────────────────────────────────────────────┘    ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  Q Topics (데이터셋 준비)                                                    │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  • 필드 동의어 설정: "매출" = "revenue", "sales", "금액"               ││
│  │  • 계산 필드 정의: "이익률" = (revenue - cost) / revenue               ││
│  │  • 날짜 필드 지정: order_date → 시간 분석 활성화                       ││
│  │  • 지역 필드 지정: region → 지도 시각화 활성화                         ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

// Q Topic 설정 예시

Q Topic: Sales Analytics

Fields:
├── order_date (Date)
│   └── Synonyms: 주문일, 날짜, date
├── revenue (Measure)
│   └── Synonyms: 매출, 수익, sales, 금액
├── region (Dimension)
│   └── Synonyms: 지역, 지방, area
├── product_category (Dimension)
│   └── Synonyms: 카테고리, 제품군, category
└── customer_segment (Dimension)
    └── Synonyms: 고객군, 세그먼트, segment

Calculated Fields:
├── profit_margin = (revenue - cost) / revenue
│   └── Synonyms: 이익률, 마진, margin
└── yoy_growth = (current_revenue - previous_revenue) / previous_revenue
    └── Synonyms: 전년대비, YoY, 성장률

Sample Questions:
• "이번 분기 매출 추이는?"
• "지역별 이익률 비교"
• "상위 10개 제품 매출"
• "전년 대비 성장률"

6.3 임베디드 분석

애플리케이션 통합

// 임베디드 대시보드 URL 생성

import boto3

def get_dashboard_embed_url(dashboard_id, user_arn):
    """QuickSight 대시보드 임베디드 URL 생성"""
    
    client = boto3.client('quicksight', region_name='ap-northeast-2')
    
    response = client.generate_embed_url_for_registered_user(
        AwsAccountId='123456789012',
        SessionLifetimeInMinutes=600,
        UserArn=user_arn,
        ExperienceConfiguration={
            'Dashboard': {
                'InitialDashboardId': dashboard_id
            }
        },
        AllowedDomains=['https://myapp.example.com']
    )
    
    return response['EmbedUrl']

# 익명 사용자용 (Row-Level Security 적용)
def get_anonymous_embed_url(dashboard_id, session_tags):
    """익명 사용자용 임베디드 URL (RLS 적용)"""
    
    client = boto3.client('quicksight', region_name='ap-northeast-2')
    
    response = client.generate_embed_url_for_anonymous_user(
        AwsAccountId='123456789012',
        Namespace='default',
        SessionLifetimeInMinutes=600,
        AuthorizedResourceArns=[
            f'arn:aws:quicksight:ap-northeast-2:123456789012:dashboard/{dashboard_id}'
        ],
        ExperienceConfiguration={
            'Dashboard': {
                'InitialDashboardId': dashboard_id
            }
        },
        SessionTags=[
            {'Key': 'region', 'Value': session_tags['region']},
            {'Key': 'department', 'Value': session_tags['department']}
        ],
        AllowedDomains=['https://myapp.example.com']
    )
    
    return response['EmbedUrl']

// React 컴포넌트에서 임베딩

import { useEffect, useState } from 'react';
import { createEmbeddingContext } from 'amazon-quicksight-embedding-sdk';

function EmbeddedDashboard({ dashboardId }) {
  const [embedUrl, setEmbedUrl] = useState(null);

  useEffect(() => {
    // 백엔드에서 임베드 URL 가져오기
    fetch(`/api/quicksight/embed-url?dashboardId=${dashboardId}`)
      .then(res => res.json())
      .then(data => setEmbedUrl(data.embedUrl));
  }, [dashboardId]);

  useEffect(() => {
    if (!embedUrl) return;

    const embedDashboard = async () => {
      const embeddingContext = await createEmbeddingContext();
      
      const dashboard = await embeddingContext.embedDashboard({
        url: embedUrl,
        container: '#dashboard-container',
        height: '700px',
        width: '100%',
        locale: 'ko-KR',
        footerPaddingEnabled: true,
        undoRedoDisabled: false,
        resetDisabled: false
      });

      // 이벤트 리스너
      dashboard.on('error', (error) => {
        console.error('Dashboard error:', error);
      });

      dashboard.on('load', () => {
        console.log('Dashboard loaded');
      });
    };

    embedDashboard();
  }, [embedUrl]);

  return <div id="dashboard-container" />;
}

QuickSight 에디션 비교

기능StandardEnterprise
SPICE 용량10 GB/사용자500 GB/사용자
Row-Level Security
QuickSight Q✅ (추가 비용)
임베디드 분석
ML Insights기본고급 (이상 탐지, 예측)
Active Directory 통합

7. 핵심 요약

쿼리 & 분석 서비스 핵심 포인트

Amazon Athena

  • • 서버리스 SQL 쿼리 엔진 (Presto/Trino 기반)
  • • 스캔 데이터량 기반 과금 ($5/TB)
  • • 비용 최적화: Parquet + 파티셔닝 + 컬럼 선택
  • • Workgroups: 팀별 비용/권한 분리
  • • Iceberg: ACID 트랜잭션, Time Travel
  • • Federated Query: DynamoDB, RDS, Redshift 연동

Amazon QuickSight

  • • 서버리스 BI 서비스
  • • SPICE: 인메모리 엔진, 서브초 응답
  • • QuickSight Q: 자연어 쿼리 (ML 기반)
  • • ML Insights: 이상 탐지, 예측, 기여도 분석
  • • 임베디드 분석: 애플리케이션 통합
  • • Row-Level Security: 사용자별 데이터 필터링

Amazon OpenSearch

  • • 검색 및 로그 분석 엔진
  • • OpenSearch Dashboards: 시각화
  • • Serverless: 자동 스케일링, 관리 불필요
  • • UltraWarm/Cold: 비용 최적화 스토리지
  • • 실시간 로그 분석, 모니터링
  • • 전문 검색, 벡터 검색 지원

🎯 시험 포인트

Athena 비용 최적화

Parquet + 파티셔닝 = 최대 99% 비용 절감

SPICE vs Direct Query

SPICE = 빠른 응답, Direct = 실시간 데이터

Athena Iceberg

UPDATE/DELETE/MERGE + Time Travel 지원

OpenSearch Serverless

OCU 기반 과금, 자동 스케일링

QuickSight Q

자연어 → SQL 변환, Topic 설정 필요

Federated Query

Lambda 커넥터로 다양한 소스 쿼리

서비스 선택 가이드

요구사항권장 서비스이유
Ad-hoc S3 쿼리Athena서버리스, 사용량 기반 과금
복잡한 조인/집계RedshiftMPP, 최적화된 성능
BI 대시보드QuickSightSPICE, 임베디드 분석
로그 분석OpenSearch전문 검색, 실시간 분석
자연어 분석QuickSight QML 기반 자연어 처리
ACID 트랜잭션Athena + IcebergUPDATE/DELETE/MERGE 지원

통합 분석 아키텍처

┌─────────────────────────────────────────────────────────────────────────────┐
│                    Unified Analytics Architecture                            │
│                                                                              │
│  Data Sources                                                                │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                        │
│  │   S3    │  │   RDS   │  │DynamoDB │  │  Logs   │                        │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘                        │
│       │            │            │            │                              │
│       └────────────┴────────────┴────────────┘                              │
│                           │                                                  │
│                           ▼                                                  │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │                    Glue Data Catalog                                    ││
│  │                    (Unified Metadata)                                   ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                           │                                                  │
│       ┌───────────────────┼───────────────────┐                             │
│       ▼                   ▼                   ▼                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐                     │
│  │   Athena    │    │  Redshift   │    │ OpenSearch  │                     │
│  │             │    │  Spectrum   │    │             │                     │
│  │ • Ad-hoc    │    │ • BI/Report │    │ • Log/Search│                     │
│  │ • Federated │    │ • Complex   │    │ • Real-time │                     │
│  └──────┬──────┘    └──────┬──────┘    └──────┬──────┘                     │
│         │                  │                  │                             │
│         └──────────────────┼──────────────────┘                             │
│                            ▼                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │                       QuickSight                                        ││
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                     ││
│  │  │  Dashboard  │  │  Q (NLQ)    │  │  Embedded   │                     ││
│  │  └─────────────┘  └─────────────┘  └─────────────┘                     ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

8. OpenSearch 심화

8.1 인덱스 설계

인덱스 매핑 & 설정

// 인덱스 템플릿 생성

PUT _index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s",
      "index.codec": "best_compression",
      "index.mapping.total_fields.limit": 2000
    },
    "mappings": {
      "properties": {
        "@timestamp": {
          "type": "date",
          "format": "strict_date_optional_time||epoch_millis"
        },
        "message": {
          "type": "text",
          "analyzer": "standard",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "level": {
          "type": "keyword"
        },
        "service": {
          "type": "keyword"
        },
        "host": {
          "type": "keyword"
        },
        "trace_id": {
          "type": "keyword"
        },
        "duration_ms": {
          "type": "long"
        },
        "request": {
          "properties": {
            "method": { "type": "keyword" },
            "path": { "type": "keyword" },
            "status_code": { "type": "integer" },
            "user_agent": { "type": "text" }
          }
        },
        "geo": {
          "properties": {
            "location": { "type": "geo_point" },
            "country": { "type": "keyword" },
            "city": { "type": "keyword" }
          }
        }
      }
    }
  },
  "priority": 100
}

// Index Lifecycle Management (ILM)

PUT _plugins/_ism/policies/logs-policy
{
  "policy": {
    "description": "Log retention policy",
    "default_state": "hot",
    "states": [
      {
        "name": "hot",
        "actions": [
          {
            "rollover": {
              "min_size": "50gb",
              "min_index_age": "1d"
            }
          }
        ],
        "transitions": [
          {
            "state_name": "warm",
            "conditions": {
              "min_index_age": "7d"
            }
          }
        ]
      },
      {
        "name": "warm",
        "actions": [
          {
            "replica_count": {
              "number_of_replicas": 0
            }
          },
          {
            "force_merge": {
              "max_num_segments": 1
            }
          }
        ],
        "transitions": [
          {
            "state_name": "cold",
            "conditions": {
              "min_index_age": "30d"
            }
          }
        ]
      },
      {
        "name": "cold",
        "actions": [
          {
            "read_only": {}
          }
        ],
        "transitions": [
          {
            "state_name": "delete",
            "conditions": {
              "min_index_age": "90d"
            }
          }
        ]
      },
      {
        "name": "delete",
        "actions": [
          {
            "delete": {}
          }
        ]
      }
    ],
    "ism_template": {
      "index_patterns": ["logs-*"],
      "priority": 100
    }
  }
}

8.2 검색 쿼리

고급 검색 쿼리

// 복합 쿼리

GET logs-*/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "message": "error"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "now-1h",
              "lte": "now"
            }
          }
        }
      ],
      "filter": [
        {
          "term": {
            "service": "payment-service"
          }
        },
        {
          "terms": {
            "level": ["ERROR", "FATAL"]
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "message": "health check"
          }
        }
      ],
      "should": [
        {
          "match": {
            "message": "timeout"
          }
        },
        {
          "match": {
            "message": "connection refused"
          }
        }
      ],
      "minimum_should_match": 1
    }
  },
  "sort": [
    { "@timestamp": "desc" }
  ],
  "size": 100,
  "highlight": {
    "fields": {
      "message": {}
    }
  }
}

// 집계 쿼리

GET logs-*/_search
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-24h"
      }
    }
  },
  "aggs": {
    "errors_over_time": {
      "date_histogram": {
        "field": "@timestamp",
        "fixed_interval": "1h"
      },
      "aggs": {
        "by_service": {
          "terms": {
            "field": "service",
            "size": 10
          },
          "aggs": {
            "error_count": {
              "filter": {
                "term": {
                  "level": "ERROR"
                }
              }
            },
            "avg_duration": {
              "avg": {
                "field": "duration_ms"
              }
            },
            "percentiles_duration": {
              "percentiles": {
                "field": "duration_ms",
                "percents": [50, 90, 95, 99]
              }
            }
          }
        }
      }
    },
    "top_errors": {
      "terms": {
        "field": "message.keyword",
        "size": 10,
        "order": {
          "_count": "desc"
        }
      }
    },
    "status_codes": {
      "terms": {
        "field": "request.status_code",
        "size": 20
      }
    }
  }
}

8.3 성능 최적화

OpenSearch 튜닝
┌─────────────────────────────────────────────────────────────────────────────┐
│                    OpenSearch Performance Tuning                             │
│                                                                              │
│  샤드 설계                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  • 샤드 크기: 10-50 GB 권장                                             ││
│  │  • 샤드 수 = 데이터 크기 / 30 GB                                        ││
│  │  • 노드당 샤드: 20개 이하 권장                                          ││
│  │  • Primary + Replica = 총 샤드 수                                       ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  인덱싱 최적화                                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  • refresh_interval: 30s (기본 1s)                                      ││
│  │  • bulk API 사용 (개별 인덱싱 X)                                        ││
│  │  • 적절한 매핑 (불필요한 필드 제외)                                     ││
│  │  • _source 필드 최적화                                                  ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  검색 최적화                                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  • filter 컨텍스트 사용 (캐시 활용)                                     ││
│  │  • 필요한 필드만 반환 (_source filtering)                               ││
│  │  • 페이지네이션: search_after 사용                                      ││
│  │  • 집계: doc_values 활용                                                ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

인덱싱 성능

  • • Bulk API로 배치 인덱싱
  • • refresh_interval 증가
  • • 복제본 임시 비활성화
  • • 적절한 샤드 수

검색 성능

  • • filter 컨텍스트 활용
  • • 필요한 필드만 반환
  • • 캐시 활용
  • • 적절한 복제본 수

9. 데이터 시각화 패턴

9.1 QuickSight 시각화 유형

차트 유형별 사용 사례
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Visualization Types                                       │
│                                                                              │
│  시계열 데이터                                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Line Chart: 시간에 따른 추세                                           ││
│  │  • 매출 추이, 사용자 증가, 트래픽 변화                                  ││
│  │                                                                          ││
│  │  Area Chart: 누적 또는 비율 변화                                        ││
│  │  • 카테고리별 매출 비중 변화                                            ││
│  │                                                                          ││
│  │  Bar Chart (시간축): 기간별 비교                                        ││
│  │  • 월별/분기별 실적 비교                                                ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  비교 데이터                                                                 │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Bar Chart: 카테고리 간 비교                                            ││
│  │  • 지역별 매출, 제품별 판매량                                           ││
│  │                                                                          ││
│  │  Grouped Bar: 다중 측정값 비교                                          ││
│  │  • 지역별 매출 vs 비용                                                  ││
│  │                                                                          ││
│  │  Stacked Bar: 구성 요소 비교                                            ││
│  │  • 지역별 제품 카테고리 구성                                            ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  비율/구성                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Pie Chart: 전체 대비 비율 (5개 이하)                                   ││
│  │  • 시장 점유율, 비용 구성                                               ││
│  │                                                                          ││
│  │  Donut Chart: 중앙에 KPI 표시                                           ││
│  │  • 목표 달성률                                                          ││
│  │                                                                          ││
│  │  Treemap: 계층적 비율                                                   ││
│  │  • 카테고리 > 서브카테고리 매출                                         ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  분포/관계                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  Scatter Plot: 두 변수 간 관계                                          ││
│  │  • 가격 vs 판매량, 광고비 vs 매출                                       ││
│  │                                                                          ││
│  │  Histogram: 데이터 분포                                                 ││
│  │  • 주문 금액 분포, 응답 시간 분포                                       ││
│  │                                                                          ││
│  │  Box Plot: 분포 및 이상치                                               ││
│  │  • 지역별 주문 금액 분포                                                ││
│  └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘

9.2 대시보드 설계 원칙

효과적인 대시보드
┌─────────────────────────────────────────────────────────────────────────────┐
│                    Dashboard Design Principles                               │
│                                                                              │
│  레이아웃 구조                                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │  ┌─────────────────────────────────────────────────────────────────┐    ││
│  │  │  Header: 제목, 필터, 날짜 범위                                  │    ││
│  │  └─────────────────────────────────────────────────────────────────┘    ││
│  │                                                                          ││
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐                   ││
│  │  │   KPI    │ │   KPI    │ │   KPI    │ │   KPI    │                   ││
│  │  │  매출   │ │  주문수  │ │  고객수  │ │  전환율  │                   ││
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘                   ││
│  │                                                                          ││
│  │  ┌────────────────────────────────┐ ┌────────────────────────────────┐  ││
│  │  │                                │ │                                │  ││
│  │  │     매출 추이 (Line Chart)     │ │   지역별 매출 (Bar Chart)      │  ││
│  │  │                                │ │                                │  ││
│  │  └────────────────────────────────┘ └────────────────────────────────┘  ││
│  │                                                                          ││
│  │  ┌────────────────────────────────┐ ┌────────────────────────────────┐  ││
│  │  │                                │ │                                │  ││
│  │  │   카테고리별 비중 (Pie)        │ │   상세 데이터 (Table)          │  ││
│  │  │                                │ │                                │  ││
│  │  └────────────────────────────────┘ └────────────────────────────────┘  ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
│  설계 원칙:                                                                  │
│  • 5초 규칙: 핵심 정보를 5초 내에 파악                                      │
│  • 위에서 아래로: 요약 → 상세                                               │
│  • 왼쪽에서 오른쪽으로: 중요도 순                                           │
│  • 일관된 색상 체계                                                         │
│  • 적절한 여백                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Do's

  • ✅ KPI를 상단에 배치
  • ✅ 일관된 색상 사용
  • ✅ 적절한 차트 유형 선택
  • ✅ 필터로 인터랙티브하게
  • ✅ 모바일 반응형 고려

Don'ts

  • ❌ 너무 많은 차트
  • ❌ 3D 차트 사용
  • ❌ 불필요한 장식
  • ❌ 일관성 없는 색상
  • ❌ 레이블 없는 축

9.3 계산 필드

QuickSight 계산 필드

// 자주 사용하는 계산 필드

// 1. 전년 대비 성장률
yoy_growth = 
  (sum(revenue) - sum(revenue, [{order_date}], PRE_FILTER)) 
  / sum(revenue, [{order_date}], PRE_FILTER) * 100

// 2. 이동 평균 (7일)
moving_avg_7d = 
  avgOver(sum(revenue), [{order_date}], [-6, 0], PRE_FILTER)

// 3. 누적 합계
cumulative_revenue = 
  runningSum(sum(revenue), [{order_date}], ASC)

// 4. 순위
sales_rank = 
  rank([sum(revenue) DESC], [{product_category}], PRE_FILTER)

// 5. 비율
category_percentage = 
  sum(revenue) / sumOver(sum(revenue), [], PRE_FILTER) * 100

// 6. 조건부 계산
profit_status = 
  ifelse(
    sum(profit) > 0, "Profitable",
    sum(profit) < 0, "Loss",
    "Break-even"
  )

// 7. 날짜 계산
days_since_last_order = 
  dateDiff(max(order_date), now(), "DD")

// 8. 문자열 처리
customer_segment = 
  concat(
    toString(ntile(4, [sum(revenue) DESC], [])),
    "-",
    ifelse(count(order_id) > 10, "Frequent", "Occasional")
  )

// 9. 전월 대비
mom_change = 
  sum(revenue) - lag(sum(revenue), [{truncDate("MM", order_date)}], 1, PRE_FILTER)

// 10. 목표 달성률
target_achievement = 
  sum(revenue) / sum(target) * 100

시각화 선택 가이드

데이터 유형목적권장 차트
시계열추세 파악Line, Area
카테고리비교Bar, Column
비율구성Pie, Donut, Treemap
분포패턴Histogram, Box Plot
관계상관관계Scatter, Bubble
지리위치 기반Map, Filled Map

10. 종합 핵심 요약

쿼리 & 분석 서비스 종합

Amazon Athena

  • • 서버리스 SQL 쿼리 (S3 데이터)
  • • Presto/Trino 엔진 기반
  • • 스캔한 데이터 양 기준 과금
  • • 파티셔닝 + Parquet = 비용 90% 절감
  • • Federated Query로 다양한 소스 연결

Amazon QuickSight

  • • 서버리스 BI 서비스
  • • SPICE: 인메모리 캐시 엔진
  • • ML Insights: 이상 탐지, 예측
  • • 임베디드 대시보드 지원
  • • 행 수준 보안 (RLS)

Amazon OpenSearch

  • • 검색 + 로그 분석 + 시각화
  • • Elasticsearch 호환
  • • UltraWarm: 저비용 웜 스토리지
  • • ISM: 인덱스 수명 주기 관리
  • • OpenSearch Dashboards (Kibana 대체)

시각화 원칙

  • • 목적에 맞는 차트 유형 선택
  • • 데이터-잉크 비율 최대화
  • • 색상은 의미 있게 사용
  • • 대시보드는 5-7개 시각화로 제한

🎯 시험 포인트

Athena 최적화

파티셔닝 + 컬럼나 포맷 + 압축

SPICE

QuickSight 인메모리 캐시, 빠른 응답

OpenSearch UltraWarm

S3 기반 저비용 웜 스토리지

Federated Query

Athena에서 RDS, DynamoDB 등 직접 쿼리

서비스 선택 가이드

요구사항권장 서비스
S3 데이터 Ad-hoc 쿼리Athena
대시보드 + BIQuickSight
로그 검색 + 분석OpenSearch
복잡한 분석 + MLRedshift
실시간 대시보드OpenSearch + Kinesis