반응형

서로 다른 계정 간 S3 To S3 복사작업 하는 방법 with php laravel

다른 언어도 동일하게 copyObject API를 사용하면 가능하다.

사전 데이터

aws-sdk-php
A 계정으로 만든 버킷
B 계정으로 만든 버킷
A,B 버킷에 접근할 iam 계정

사전 작업

AWS 콘솔에서 iam 계정 번호를 준비해, 사용할 버킷 A, B에 아래와 같이 your_iam_ID 위치에 권한을, your_bucket에 버킷 이름을 넣는다.

  • ListBucket : 디렉토리 복사코드를 준비하기 위한 리스트 조회 권한
  • GetObject : 파일을 다운로드할 수 있는 조회 권한
  • PutObject : 파일을 업로드 할 수 있는 권한

해당 위치 접근 방법은 Amazon S3 > 버킷 > 권한 > 버킷 정책

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowAccountyour_iam_IDAccess",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::your_iam_ID:root"
            },
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::your_bucket",
                "arn:aws:s3:::your_bucket/*"
            ]
        }
    ]
}

CopyObject API 구현

CopyObject API는 파일 단위로 A에서 B로 옮기는 작업이다. 따라서 단일 건에 대해서는 권한만 잘 오픈해준다면, 어렵지 않은 작업이다.

단일 파일 복사 작업

use Aws\\S3\\S3Client;

class S3ToS3Transfer{

    public function fileCopy(){
        $targetS3Client = new S3Client([
            'credentials' => [
                'key' => iam_key,
                'secret' => iam_secret_Key,
            ],
            'region' => 'ap-northeast-2',
            'version' => 'latest',
        ]);

        //source 위치에 있는 파일을 key 위치로 복사
        $targetS3Client->copyObject([
            'Bucket'     => 'target_bucket',
            'Key'        => 'target_path',
            'CopySource' => urlencode('source_bucket/' . $sourcePath),
        ]);

    }

CopySource의 버킷을 명시해줘야 정상 동작 가능

디렉토리 복사 작업

디렉토리 복사는 파일의 목록을 읽어오는 ListBucket API를 활용해야 한다.

흐름은 대략 이렇다

  1. Source S3 스토리지 리스트 불러오기(ListObjectsV2 API)
  2. 불러온 리스트에서 size가 0인 디렉토리 제외하고 모두 배열에 저장(1000개 이상일 경우 페이지네이션 처리)
  3. foreach로 각 object를 돌며 단일 copyObject API 요청
    public function directoryCopy(array $data){
        $sourceS3Client = new S3Client([
            'credentials' => [
                'key' => iam_key,
                'secret' => iam_secret_key,
            ],
            'region' => 'ap-northeast-2',
            'version' => 'latest',
        ]);

        $targetS3Client = new S3Client([
            'credentials' => [
                'key' => iam_key,
                'secret' => iam_secret_key,
            ],
            'region' => 'ap-northeast-2',
            'version' => 'latest',
        ]);

        // source S3 디렉토리 탐색 시작(1000개 단위 페이징 되어있음)
        $requestParams = [
                'Bucket' => $sourceStorageInfo->virtual_path,
                'Prefix' => $sourcePath, // 조회할 디렉토리 지정
            ];

        do{
            $objects = $sourceS3Client->listObjectsV2($requestParams);

            // 다음 페이지 존재확인 IsTruncated : 1 or 0
            if ($objects['IsTruncated']){
                $requestParams = [
                    'Bucket' => $sourceStorageInfo->virtual_path,
                    'Prefix' => $sourcePath, // 조회할 디렉토리 지정
                    'ContinuationToken' => $objects['NextContinuationToken'] // 다음 페이지 토큰
                ];

                $objectsNextPageFlag = true;

            } else {
                $objectsNextPageFlag = false;
            }

            //오브젝트 리스트 파싱
            if (isset($objects['Contents'])) {
                foreach ($objects['Contents'] as $object) {
                    // 디렉토리 오브젝트 제외
                    if ($object['Size'] == 0) {
                        continue;
                    }

                    $notIncludeDirectoryObjects[] = $object;
                }
            }
            
        } while($objectsNextPageFlag); // 페이징 끝날 때까지 반복

        //배열 전체 탐색해 target S3에 파일 복사
        foreach ($notIncludeDirectoryObjects as $object) {
            $objectKey = $object['Key']; // 버킷 제외 파일 경로
            $fileNameWithExt = basename($objectKey); // 파일명 추출

            //source 위치에 있는 파일을 key 위치로 복사
            $targetS3Client->copyObject([
                'Bucket'     => 'target_bucket',
                'Key'        => 'target_path',
                'CopySource' => urlencode('source_bucket/' . $sourcePath), // 버킷 + 소스 위치
            ]);
        }
    }

리스트 조회한 데이터를 그대로 복사요청 진행하면 되는 로직

반응형

'공부 > Laravel' 카테고리의 다른 글

Laravel 5.8 TestCode  (0) 2024.06.21
Pusher를 활용한 Laravel5.8 + Javascript 소켓통신  (0) 2024.06.20
반응형

@Async 어노테이션은 Spring Framework에서 비동기 처리를 지원하기 위해 사용되는 기능

주로 시간이 오래 걸리는 작업을 별도의 쓰레드에서 실행하여 메인 쓰레드의 응답성을 향상시키기 위해 사용

기본 개념

  • @Async는 메서드를 비동기로 실행하게 만들어주는 어노테이션
  • Spring이 관리하는 TaskExecutor를 사용해 별도의 쓰레드에서 메서드를 실행
  • 메서드를 호출한 쪽은 즉시 반환되며, 실제 작업은 백그라운드에서 진행

사용 방법

  1. 설정 활성화
@Configuration
@EnableAsync
public class AsyncConfig {
    // 커스텀 TaskExecutor 설정도 가능
}
  1. 비동기로 실행할 메서드에 @Async 추가
@Service
public class MyService {

    @Async
    public void asyncMethod() {
        // 오래 걸리는 작업
        System.out.println("비동기 작업 수행 중...");
    }
}
  1. 호출 예시
@RestController
public class MyController {

    @Autowired
    private MyService myService;

    @GetMapping("/start")
    public String startAsyncTask() {
        myService.asyncMethod(); // 비동기 호출
        return "요청 처리 완료 (비동기 작업 중)";
    }
}

반환값이 있는 경우

  • Future<T>, CompletableFuture<T> 또는 ListenableFuture<T>로 감싸서 리턴 가능
@Async
public CompletableFuture<String> asyncReturnMethod() {
    return CompletableFuture.completedFuture("비동기 결과");
}

주의사항

  • @Async 메서드는 프록시 기반이므로 같은 클래스 내부에서 호출하면 비동기로 동작하지 않음
  • → 자기 자신을 @Autowired로 주입받아 사용하거나 구조 분리를 권장
  • 기본 쓰레드 풀은 제한적이므로 커스텀 TaskExecutor를 정의해주는 것이 좋음
  • @Async는 public 메서드에서만 제대로 작동함

커스텀 Executor 왜 사용?

기본 Executor는 SimpleAsyncTaskExecutor를 사용

  • Spring은 @Async를 사용할 때 별도 설정이 없으면 SimpleAsyncTaskExecutor를 사용
  • 이 Executor는 쓰레드 풀을 사용하지 않고, 호출할 때마다 새로운 쓰레드를 생성

⇒ 따라서 쓰레드 새로 무한정 생성 및 재사용 불가능

  • 쓰레드 재사용이 안 됨
  • 많은 요청이 들어오면 OutOfMemoryError 위험
  • 성능과 안정성 모두 낮음

커스텀 Executor 사용 예시

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("AsyncExecutor-");
        executor.initialize();
        return executor;
    }
}
반응형
반응형

HLS(HTTP Live Streaming)는 Apple이 개발한 비디오 스트리밍 프로토콜로, 주로 인터넷을 통해 오디오 및 비디오 콘텐츠를 실시간 또는 주문형(on-demand)으로 전달하는 데 사용


핵심 개념

  • HTTP 기반
  • 콘텐츠를 일반 HTTP 서버를 통해 전송하므로 방화벽, CDN, 캐시 서버와 잘 호환
  • Segment(조각) 방식 전송
  • 전체 비디오를 작게 분할(보통 2~10초)한 .ts 파일로 쪼개어 순차적으로 전송
  • M3U8 플레이리스트 사용
  • .m3u8 형식의 플레이리스트 파일을 사용해 어떤 세그먼트를 재생할지 정의
  • 적응형 비트레이트 스트리밍(ABR)
  • 네트워크 상태에 따라 자동으로 품질(해상도/비트레이트)을 조정해 버퍼링을 최소화

HLS 구성 요소

  1. Master Playlist (m3u8)
  2. 여러 해상도/비트레이트 버전을 링크한 상위 목록
  3. Media Playlist (m3u8)
  4. 특정 품질의 세그먼트 목록 (ex: segment1.ts, segment2.ts...)
  5. Media Segments (.ts)
  6. 실제 오디오/비디오 데이터가 들어 있는 짧은 MPEG-TS 파일들

작동 방식 요약

  1. 클라이언트가 .m3u8 플레이리스트 요청
  2. 플레이리스트에 있는 세그먼트(.ts) 파일들을 순차 다운로드
  3. 다운로드한 세그먼트를 재생
  4. 네트워크 상태 따라 다른 품질의 플레이리스트로 전환 가능

어디에 사용되나?

  • YouTube, Twitch, Netflix 등 OTT 서비스
  • 모바일/웹 브라우저 기반 스트리밍
  • 라이브 방송 시스템 (뉴스, 스포츠 등)

장점

  • 범용성: 브라우저와 모바일 기기에서 바로 재생 가능
  • HTTP 사용: 별도 스트리밍 서버 필요 없음
  • 적응형 스트리밍: 다양한 네트워크 환경에서 안정적 재생

M3U8

m3u8은 HLS(HTTP Live Streaming)에서 사용되는 플레이리스트 파일 형식으로, UTF-8 인코딩된 M3U 파일. 쉽게 말해, 재생할 미디어 파일(.ts 세그먼트 등)의 목록과 스트리밍 정보를 담고 있는 텍스트 파일


m3u8 파일이 하는 역할

  • 스트리밍할 미디어 세그먼트(.ts) 들의 경로를 나열
  • 각 세그먼트의 길이, 순서 정보를 포함
  • 경우에 따라 해상도별 스트림 목록(Master Playlist) 역할도 함

m3u8의 두 종류

  1. Master Playlist
    #EXTM3U
    #EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
    low.m3u8
    #EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=1280x720
    mid.m3u8
    #EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1920x1080
    high.m3u8
    
    
    사용자의 네트워크 상황에 따라 적절한 화질의 Media Playlist를 선택
  2. 여러 화질 버전의 Media Playlist를 포함한 상위 목록
  3. Media Playlist
    #EXTM3U
    #EXT-X-TARGETDURATION:10
    #EXT-X-VERSION:3
    #EXTINF:9.0,
    segment1.ts
    #EXTINF:10.0,
    segment2.ts
    #EXTINF:8.5,
    segment3.ts
    #EXT-X-ENDLIST
    
    
    위 순서대로 .ts 파일을 다운로드하고 재생
  4. 실제 비디오 세그먼트 목록을 나열

주요 태그 설명

#EXTM3U m3u8 파일의 시작을 알림
#EXTINF:<duration> 각 세그먼트의 재생 길이(초)
#EXT-X-STREAM-INF 마스터 재생 목록에서 각 해상도 스트림 정의
#EXT-X-ENDLIST 리스트가 종료되었음을 의미 (VOD에서 사용)
#EXT-X-TARGETDURATION 세그먼트의 최대 길이

m3u8 파일은 어떻게 쓰이나?

  • 웹 플레이어에서 .m3u8 주소를 불러와 스트리밍 시작
  • 라이브 방송은 .m3u8이 실시간으로 업데이트됨
  • 동영상 다운로드 도구에서 .m3u8을 분석해 전체 영상 다운로드 가능

.HLS에서 .ts 파일이란?

HLS 스트리밍에서 .ts는 MPEG-TS (MPEG Transport Stream) 형식의 비디오 세그먼트 파일

  • 비디오와 오디오 데이터를 포함한 실시간 전송용 포맷
  • .ts = Transport Stream
  • 스트리밍을 위해 짧게 나뉜 (예: 5~10초) 미디어 조각들

예시로 비교

파일 종류 확장자 의미

스트리밍 비디오 세그먼트 .ts MPEG Transport Stream
웹 프로그래밍 언어 파일 .ts TypeScript 소스코드

스트리밍에서 .ts 사용 예시

#EXTINF:10.0,
video1.ts
#EXTINF:10.0,
video2.ts
#EXTINF:10.0,
video3.ts

여기서 video1.ts, video2.ts 등은 비디오 데이터 조각들이고, 순서대로 스트리밍

 
반응형

'공부 > CS' 카테고리의 다른 글

WAF, ZTN  (1) 2025.04.27
API Gateway  (0) 2025.03.24
워터마크 기술 개념  (0) 2025.03.03
아키텍처 간략 정리  (0) 2025.01.05
언어별 연산 속도  (0) 2024.12.30
반응형

단일 장애 지점(Single Point Of Failure)

SPOF 문제는 개발 인프라를 구성할 때 빼 놓을 수 없는 고려점이다.

SPOF(Single Point of Failure)는 단일 구성 요소에 장애가 발생했을 때, 전체 시스템이 영향을 받아 서비스를 이용할 수 없게 되는 상황을 의미한다 예를 들어, 클라이언트가 요청을 보내는 서버가 하나뿐이라면, 해당 서버에 장애가 발생할 경우 서비스를 이용할 수 없게 된다.

다음은 웹 요청의 일반적인 흐름을 SPOF 관점에서 단순화한 예시이다.

모든 인프라에서 SPOF를 고려하기 위해 위와 같은 예시를 들었으며, 각 계층별로 SPOF는 반드시 고려해봐야 할 문제이다.

클라이언트가 서버에 요청을 진행하면, L4 로드밸런서가 요청을 받아 각 Proxy Server에 데이터를 넘기며, Proxy 서버는 요청을 내부의 WAS로 전달한다. WAS에서는 Database에 있는 값을 확인해 생성된 데이터를 Client에게 반환합니다.

L4 Load Balancer

L4 로드밸런서는 TCP/UDP 레벨에서 요청을 분산 처리하는 역할을 한다.

해당 계층에서 SPOF를 방지하기 위한 방법은 다음과 같다.

  • 이중화 구성 (Active-Active 또는 Active-Passive)
  • VRRP(Virtual Router Redundancy Protocol)를 통한 고가용성 구성
  • 클라우드 환경에서는 관리형 L4 (예: AWS NLB, Azure Load Balancer)를 활용해 내결함성 확보

SPOF 예방 방안

  • L4 LB를 두 대 이상 배치
  • 헬스 체크 기반의 자동 장애 전환(failover) 구성

Proxy Server

Proxy 서버는 클라이언트 요청을 받아 내부 WAS로 전달하거나 캐싱, 인증, 보안 처리 등의 역할을 한다.

프록시가 하나일 경우 SPOF가 발생하므로 수평 확장 및 L4 앞단 분산이 필수다.

SPOF 예방 방안

  • 여러 대의 프록시 서버를 L4 로드밸런서를 통해 분산
  • Stateless 설계로 프록시 간 장애 영향을 최소화
  • Proxy 레벨에서의 Failover 및 헬스 체크 기능 사용

WAS

WAS는 실제 비즈니스 로직이 실행되는 계층이다. 하나의 WAS 인스턴스에 모든 처리를 맡길 경우, 서비스 전체 장애가 발생할 수 있다.

SPOF 예방 방안

  • WAS 인스턴스를 다중 구성(Auto Scaling 포함)
  • Proxy 또는 L7 Load Balancer에서 로드밸런싱
  • 세션 클러스터링 또는 세션 스토리지 분리(Redis 등)를 통해 무상태 아키텍처 구현

Database

데이터베이스는 서비스의 핵심 데이터를 저장하므로, 장애 시 영향도가 가장 크다.

DB는 전통적으로 SPOF가 발생하기 쉬운 계층이므로, 고가용성 구성이 필수이다.

SPOF 예방 방안

  • Master-Slave 또는 Master-Master 복제 구성
  • 리더-팔로워 구조 + 자동 Failover (예: RDS Multi-AZ, MySQL with MHA)
  • 샤딩 또는 분산 DB 도입 고려
  • 정기 백업 및 장애 복구 절차 수립

SPOF는 모든 인프라 구성에서 반드시 고려해야 할 요소이며, 계층별로 고가용성을 확보하는 구조 설계가 안정적인 시스템 운영의 핵심이다.

반응형
반응형

S3에 장기보관하기 위한 목적을 가진 S3스토리지 클래스 DEEP_ARCHIVE는 Amazon S3 Glacier Deep Archive에서 사용되는 S3의 가장 저렴한 스토리지 클래스로, 자주 사용하지 않는 데이터, 장기 보관(Long-term Archival)을 위한 데이터 저장에 적합하다.

주요 특징

  1. 용도
    • 수 년에서 수십 년 동안 거의 접근하지 않지만, 규제나 컴플라이언스 목적으로 반드시 보관해야 하는 데이터를 저장할 때 사용
    • 예: 법적 기록, 의료 기록, 장기 백업 데이터
  2. 비용
    • S3 스토리지 클래스 중 가장 낮은 GB당 저장 비용
    • 대신, 검색(복원) 요청 및 검색 데이터 전송 비용이 상대적으로 높음
  3. 지연 시간
    • 복원 시간이 수 시간(12시간 이내) 소요
    • (2가지 옵션 제공: Standard - 최대 12시간, Bulk - 최대 48시간, Express - 수 분)
  4. 가용성/내구성
    • 내구성은 S3 표준과 동일한 99.999999999% (11 9's)
    • 가용성이 낮기에 자주 사용하지 않는 데이터에 대해서만 사용
  5. 액세스 방식
    • 데이터를 바로 읽을 수 없으며, 먼저 복원 요청을 해야 함.
    • 복원 요청이 완료되면 설정된 유지 기간 동안 임시적으로 표준 S3처럼 접근 가능.
  6. 적용 사례
    • 규제 준수 저장
    • 대규모 비활성 로그 데이터
    • 법적 증거자료 백업
    • 장기 백업 및 DR(Disaster Recovery) 데이터

정리

  • 최저 비용
  • 수년간 거의 액세스하지 않는 데이터
  • 즉시 액세스 불가 (복원 요청 필요)
  • 복원까지 수 시간 소요
반응형
반응형

MySQL의 시간 표현형식인 DATETIME, TIMESTAMP
두 표현형식에 대해서 알아보자

* 해당 글에서 말하는 local time이란 서버컴퓨터에 설정되어 있는 날짜 기준 시각을 의미

항목 TIMESTAMP DATETIME
용량 4바이트 8바이트
범위 1970-01-01 ~ 2038-01-19 1000-01-01 ~ 9999-12-31
시간대 영향 있음 (타임존 변환 자동 적용) 없음 (그대로 저장)
저장 방식 UNIX timestamp (초 단위) 구조화된 날짜/시간 필드
주의 사항 2038 문제, 시간대 혼동 가능 타임존 변환 로직 수동 구현 필요

TIMESTAMP

  • 저장 용량 : 4Byte
  • 저장 범위 : 1970-01-01 ~ 2038-01-19
  • 시간대 영향 : 서버 Timezone에 따라 변환
  • 내부 저장 방식 : UNIX 시간(초) 기반
  • 주의 사항 : 2038년 문제, timezone 혼동

특징

자동 시간대 관리

저장 : 시간 저장할 때 서버의 timezone → UTC로 변환되어 저장

조회 : 저장된 UTC → 현재 세션/서버의 timezone 기준으로 보여줌

장점

글로벌 서비스에서 사용자 시간대별로 시간 값을 표시할 때, TIMESTAMP는 자동으로 변환되므로 별도의 시간대 계산 로직 없이도 사용자에게 맞는 현지 시간을 보여줄 수 있다.

2038년 문제

TIMESTAMP는 4바이트 날짜 표현 데이터로써 초 단위로 저장하기에, 표현 가능한 범위가 -2147483548 ~ 2147483647이다. 해당 값의 최대값이 2038-01-19 03:14:07 UTC 이기에, 이후 시간 표현에 대해서 문제가 생긴다.

timezone 혼동

  • 서버로 요청하는 클라이언트의 local time이 변경되었을 경우

    • ex) 2025-05-05 10:00:00 → 2025-05-05 01:00:00

    • Asia/Seoul UTC +9* 타임존을 사용하고 있는 서버에서 서버의 TIMEZONE 설정을 UTC로 변경

      ⇒ 기존에 +9시간으로 나오던 시간이 +9가 안된 채로 보여줌

  • 같은 TIMESTAMP 값을 서로 다른 환경에서 비교할 때 오류

    • ex) A 서버 (Asia/Seoul)와 B 서버 (UTC)가 서로 TIMESTAMP 데이터를 주고받는 상황
    • A 서버 : 2025-05-05 12:00:00 UTC +9
    • B 서버 : 2025-05-05 03:00:00 UTC

DATETIME

  • 저장 용량 : 8Byte
  • 저장 범위 : 1000-01-01 ~ 9999-12-31
  • 시간대 영향 : 입력된 값 그대로 저장
  • 내부 저장 방식 : 구조화된 날짜.시간 필드
  • 주의 사항 : UTC 저장이 필요하면 직접 처리해야함

특징

8바이트 구조화된 방식으로 날짜·시간 정보를 별도 필드에 저장하며, 값 변환 없이 그대로 저장된다. 예: 9999-12-31과 같은 값도 안정적으로 표현 가능 (약 47비트 수준이지만 여유 있게 8바이트 확보)

장점

  • 서버 환경, 클라이언트 지역, 타임존 설정과 무관하게 항상 일정한 값 유지
  • TIMESTAMP와 다르게 8바이트의 넓은 범위 저장 가능
  • 타임존에 영향받지 않으며, 타임존 로직은 애플리케이션에서 명시적으로 제어 가능

기준 시간 지정 시 별도의 로직 필요

글로벌 서비스에서 DATETIME을 사용할 경우, 사용자 시간대에 맞게 표시하려면 애플리케이션에서 명시적인 기준 시간(예: UTC) 변환 로직이 필요하다.

반응형

'공부 > Database' 카테고리의 다른 글

RDBMS 쿼리 성능 개선 기초 ( 다수의 WHERE절 쿼리)  (0) 2024.07.24
반응형

보안설계에서 중요한 키워드 WAF, Zero Trust Network


1. WAF (Web Application Firewall)

정의

웹 애플리케이션 계층(L7)의 보안 방화벽

HTTP 요청/응답을 검사해서 악의적인 공격을 차단하는 역할


WAF가 막아주는 공격 유형:

공격 설명

SQL Injection 쿼리에 악성 코드 삽입 시도
XSS (크로스 사이트 스크립팅) 자바스크립트 삽입 공격
CSRF 인증된 사용자의 권한 도용
경로 탐색(Path Traversal) 파일 시스템 침투 시도
봇 트래픽 차단 스크래퍼, 크롤러, 무차별 대입 공격 차단 등

WAF는 어디에 위치하냐면:

[클라이언트]
   ↓
[WAF] ← 모든 HTTP 요청 검사
   ↓
[웹 서버 / 프록시 서버]
   ↓
[WAS]


WAF 종류

구분 예시

하드웨어 F5 ASM, Imperva
소프트웨어 ModSecurity (Apache/Nginx), NAXSI
클라우드형 AWS WAF, Cloudflare WAF, Azure WAF

2. Zero Trust Network (제로 트러스트 네트워크)

정의

"절대 아무도 신뢰하지 마라.

네트워크 내부든 외부든 모든 요청을 검증하고 최소한의 권한만 허용하라."


핵심 개념:

개념 의미

No implicit trust 내부망이라고 무조건 믿지 않음
모든 요청 인증/인가 사용자, 장치, 앱 단위 인증 필수
최소 권한 원칙 필요한 자원에만 접근 가능
세션 기반 보안 로그인 이후에도 지속적 확인 (MFA, 토큰 검사 등)

Zero Trust 흐름 예시:

[사용자 또는 디바이스 요청]
      ↓
[인증 시스템 (IDP, MFA 등)]
      ↓
[Zero Trust 게이트웨이 (ZTNA Proxy)]
      ↓
[애플리케이션]


구성 요소 예시:

요소 예시

ID 관리 Okta, Azure AD
접근 제어 Zscaler, Cloudflare Zero Trust, Tailscale
정책 엔진 Google BeyondCorp, Istio (서비스메쉬)

WAF vs Zero Trust 비교

항목 WAF Zero Trust

방어 대상 웹 애플리케이션 네트워크 전체 (사용자/디바이스/앱 등)
작동 계층 L7 (HTTP) 전 계층 (ID, 네트워크, 앱, 세션 등)
보안 방식 패턴 매칭, 룰 기반 인증, 정책, 신뢰 검증
목적 공격 차단 (XSS, SQLi 등) 접근 제한 + 인증 기반 통제
설치 위치 웹 서버 앞단 네트워크 게이트웨이, 클라이언트 단 등

한 줄 정리

용어 요약

WAF 웹 공격 막는 L7 보안 필터
Zero Trust "내부망도 믿지 마" 철학으로 모든 요청을 지속적으로 검증
반응형

'공부 > CS' 카테고리의 다른 글

HLS 프로토콜  (1) 2025.06.02
API Gateway  (0) 2025.03.24
워터마크 기술 개념  (0) 2025.03.03
아키텍처 간략 정리  (0) 2025.01.05
언어별 연산 속도  (0) 2024.12.30
반응형

volatile은 자바에서 멀티스레딩 환경에서 자주 사용되는 키워드로, 변수에 대한 가시성(visibility)을 보장해 주는 기능


volatile이란?

volatile 키워드는 변수 선언 앞에 붙여서 사용되며, 해당 변수가 여러 스레드에서 동시에 접근될 수 있음을 나타냅니다.


private volatile boolean running = true;

필요한 상황

자바에서 멀티스레딩을 할 때, 각 스레드는 자신만의 캐시에 변수 값을 저장할 수 있음

이때, 한 스레드가 변경한 값이 다른 스레드에 보이도록 캐시 업데이트 진행

예시:

class Example implements Runnable {
    private boolean running = true;

    public void run() {
        while (running) {
            // do something
        }
    }

    public void stop() {
        running = false;
    }
}

이 코드는 stop() 메서드에서 runningfalse로 바꾸더라도, run() 내부의 스레드는 true만 계속 보고 멈추지 않음


private volatile boolean running = true;

volatile의 효과

  1. 가시성 보장 (Visibility)
    • 어떤 스레드가 값을 바꾸면, 다른 모든 스레드는 그 변경을 즉시 볼 수 있습니다.
  2. 원자성은 보장하지 않음 (No Atomicity)
    • volatile읽기/쓰기 자체는 원자적이지만, 복합 연산(++, += 등)은 원자적이지 않아요.
    • 예: count++volatile로도 안전하지 않습니다.

volatile vs synchronized

특징 volatile synchronized
가시성 O O
원자성 X O
성능 빠름 상대적으로 느림 (락 걸림)
용도 단순 플래그, 읽기/쓰기 변수 복잡한 상태 공유, 연산 등

🔸 사용 예시

public class FlagExample {
    private volatile boolean flag = false;

    public void setFlagTrue() {
        flag = true;
    }

    public void waitForFlag() {
        while (!flag) {
            // 대기 중
        }
        System.out.println("Flag detected!");
    }
}

요약

  • volatile은 멀티 스레드 환경에서 변수 값의 변경이 다른 스레드에 보이지 않는 문제(가시성 문제)를 해결하기 위해 사용된다.
  • 일반적으로 CPU는 변수 값을 자신의 캐시에 저장하고 사용하기 때문에, 한 스레드에서 변경한 값이 다른 스레드의 캐시에 반영되지 않아 변경이 전파되지 않는 문제가 발생할 수 있다.
  • volatile 키워드를 사용하면, 변수의 값을 항상 메인 메모리에서 읽고 쓰게 되어 최신 값이 모든 스레드에 보이도록 보장할 수 있다.

  • Thread A는 여전히 true인 줄 알고 계속 루프를 돔
  • Thread B는 메모리에서 false로 바꿨지만, A는 캐시된 값을 사용 중

  • Thread B가 false로 바꾸면
  • Thread A도 즉시 최신 값을 보고 루프 종료
반응형
반응형

https://spody.tistory.com/216

https://spody.tistory.com/217

두 글을 정리요약했습니다.

발생 현상

  • AUIGrid(자바스크립트 라이브러리)를 사용하는 엑셀 다운로드 시 깨진 파일을 다운받음
  • 그러나 백엔드 서버에서 해당 파일을 저장해둔 위치에 가서 파일을 열어보면 정상적으로 열리는 상태
  • 다운로드 받은 파일(깨진 파일)과 정상 파일을 메모장으로 열어서 데이터 비교했을 때 파일이 쓰이다 만 것처럼 데이터가 들어가 있음 (깨진 파일은 데이터가 중간에 끊김)

발생 원인

  • 파일이 중간에 깨진 이유는 디스크 쓰기 작업이 완료(flush)되기 전에 파일을 반환했기 때문에 발생
  • 백엔드 서버의 로직을 봤을 때 해당 코드는 동기적으로 처리되어야 정상인데 왜 문제가 발생했는가?
    • AIP 서버에서 파일 데이터를 수신하면, 해당 데이터는 먼저 메모리 내 커널 페이지 캐시에 저장
    • 이 시점에서 쉘 스크립트 또는 PHP는 파일 쓰기 작업이 완료된 것으로 간주하고, 바로 다음 단계로 넘어감
    • 하지만 flush가 디스크에 완전히 반영되기 전 프론트엔드에서 해당 파일을 다운로드 받아 문제가 발생
    • 즉, 쓰기 지연(write-back) 상태에서 read가 먼저 발생해 깨진 파일을 받게 됨

해결 방안

  • 3초 딜레이 적용
    • AUIGrid에서 사용하는 백엔드 호출 로직(DrmService.php)에 sleep(3)을 추가하여 디스크 flush가 완료되도록 유도
  • 쉘 스크립트 내에서 파일 디스크 flush 유도
    • 반환받은 파일 데이터를 .tmp 확장자로 저장한 후, mv file.xlsx.tmp file.xlsx 방식으로 최종 이름 변경
    • mv는 atomic rename 동작이기 때문에 flush 유도에 효과적
  • Python fsync() 시스템콜 직접 호출
    • python3 -c "f = open('${ORIGINAL_PATH}', 'rb+'); import os; os.fsync(f.fileno()); f.close()"
    • 명시적으로 flush 요청을 보냄 (단, NFS 환경에서는 보장되지 않을 수 있음)

고려 사항

  • 해당 문제는 운영 환경에서만 발생하며, 개발 환경에서는 재현되지 않음
  • 운영 서버에서 직접 코드를 수정하거나 임시 로직을 삽입해 테스트할 필요가 있음
  • 백엔드에서 생성하는 다른 엑셀 암호화 파일들도 동일한 문제의 가능성이 있음
    • 단, 해당 로직은 후처리 과정이 존재해 자연스러운 딜레이가 발생했기 때문에 flush가 완료되었음
  • Content-Length 기반 .done + mv 전략은, 상대 서버가 전송하는 파일이 반드시 바이너리 형식이며 Content-Length를 정확하게 명시한다는 전제를 필요로 한다. 하지만 상대 서버가 우리가 직접 구현한 것이 아니라면, 전송 형식이 base64, gzip, chunked encoding 등으로 바뀌거나 Content-Length가 부정확할 수 있기 때문에, 이 전략은 실질적으로 적용이 불가능하다고 판단하여 제외함.

NFS(Network File System)를 곁들인 저장소

쉘 스크립트나 Python을 통해 flush를 직접 유도했음에도 불구하고, 운영 환경에서는 여전히 깨진 파일이 다운로드되었음.

이를 확인한 결과, 해당 파일이 저장된 위치는 NFS 마운트 스토리지였다.

NFS 환경 특성

  • NFS export 옵션 중 sync, async가 있음
  • 옵션이 명시되지 않으면 기본값은 async → flush 지연

NFS export 옵션 설명

  • sync:
    • 클라이언트의 write() 요청마다 NFS 서버가 디스크에 즉시 flush한 후 OK 응답 반환
    • 안정성 높음, 성능 낮음
  • async (기본값):
    • write() → 클라이언트 커널 캐시에 저장 → 일정 시간 후 NFS 서버에 전송
    • 서버에서도 메모리에 저장 후 나중에 flush (2단계 지연)
    • fsync() 호출 → 클라이언트는 COMMIT 요청을 보내지만, 서버 flush 시점은 커널이 결정
    • 이로 인해 flush 전에 장애 발생 시 데이터 손실 가능성 있음

NFS 환경에서 flush 요약

  • 백엔드 서버는 NFS 마운트 스토리지에 파일을 저장하고 있음
  • NFS의 async 설정 때문에 flush 요청이 디스크에 즉시 반영되지 않음
  • flush()나 fsync(), mv 등을 해도 NFS 서버의 커널 스케줄러가 flush 시점을 결정하기 때문에 클라이언트 입장에서 flush가 즉시 완료되었는지는 보장되지 않음

최종 해결 방안

  • NFS export 옵션을 sync로 바꾸는 건 성능 리스크가 커서 적용 어려움
  • 엑셀 파일은 사용자가 다운로드 받기만 하면 되므로, 3초 대기 정도는 큰 무리가 없음
  • 따라서 엑셀 path를 반환하기 전 sleep 3을 넣는 것이 현재 환경에선 가장 현실적인 대응
반응형

'LTF(learn through failure)' 카테고리의 다른 글

파일 flush 문제 공유 (NFS)  (0) 2025.04.13
파일 flush 문제 공유  (0) 2025.04.05
반응형

https://spody.tistory.com/216

직전에 작성한 글로부터 이어집니다.

NFS(Network File System)를 곁들인 저장소

쉘 스크립트나 Python을 통해 flush를 직접 유도했음에도 불구하고, 운영 환경에서는 여전히 깨진 파일이 다운로드되었음. 이를 확인한 결과, 해당 파일이 저장된 위치는 NFS 마운트 스토리지였다.

NFS 환경 특성

  • NFS export 옵션 중 sync, async가 있음
  • 옵션이 명시되지 않으면 기본값은 async → flush 지연

NFS export 옵션 설명

  • sync:
    • 클라이언트의 write() 요청마다 NFS 서버가 디스크에 즉시 flush한 후 OK 응답 반환
    • 안정성 높음, 성능 낮음
  • async (기본값):
    • write() → 클라이언트 커널 캐시에 저장 → 일정 시간 후 NFS 서버에 전송
    • 서버에서도 메모리에 저장 후 나중에 flush (2단계 지연)
    • fsync() 호출 → 클라이언트는 COMMIT 요청을 보내지만, 서버 flush 시점은 커널이 결정
    • 이로 인해 flush 전에 장애 발생 시 데이터 손실 가능성 있음

NFS 환경에서 flush 요약

  • 백엔드 서버는 NFS 마운트 스토리지에 파일을 저장하고 있음
  • NFS의 async 설정 때문에 flush 요청이 디스크에 즉시 반영되지 않음
  • flush()나 fsync(), mv 등을 해도 NFS 서버의 커널 스케줄러가 flush 시점을 결정하기 때문에 클라이언트 입장에서 flush가 즉시 완료되었는지는 보장되지 않음

최종 해결 방안

  • NFS export 옵션을 sync로 바꾸는 건 성능 리스크가 커서 적용 어려움
  • 엑셀 파일은 사용자가 다운로드 받기만 하면 되므로, 3초 대기 정도는 큰 무리가 없음
  • 따라서 엑셀 path를 반환하기 전 sleep 3을 넣는 것이 현재 환경에선 가장 현실적인 대응
반응형

'LTF(learn through failure)' 카테고리의 다른 글

파일 flush 문제 해결 전략  (1) 2025.04.13
파일 flush 문제 공유  (0) 2025.04.05

+ Recent posts