반응형

발생 현상

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

발생 원인

  • 파일이 중간에 깨진 이유는 디스크 쓰기 작업이 완료(flush)되기 전에 파일을 반환했기 때문에 발생
  • 백엔드 서버의 로직을 봤을 때 해당 코드는 동기적으로 처리되어야 정상인데 왜 문제가 발생했는가?
    • AIP 서버에서 파일 데이터를 수신하면, 해당 데이터는 먼저 메모리에 존재하는 파일 스트림에 저장됨. 이 시점에서 쉘 스크립트는 파일 쓰기 작업이 완료된 것으로 간주(php 코드도 동일)하고, 바로 다음 단계로 넘어간다. 하지만 파일 스트림의 내용이 실제 디스크에 완전히 flush되기 전에 프론트에서 해당 파일을 다운로드받아 문제가 생기고 있었음
  • 엑셀 다운로드 흐름

해결 방안

  • 3초 딜레이 적용
    • AUIGrid에서 사용하는 백엔드 호출 로직(DrmService.php)에 sleep(3)을 추가하여 디스크 flush가 완료되도록 유도
  • 쉘 스크립트 내에서 파일 디스크 flush 유도
    • mv 명령어 사용 시 OS 스케줄러의 디스크 flush를 유도하는 커널 레벨 특성을 이용
    • 반환받은 파일 데이터를 .tmp 확장자로 저장한 후, mv file.xlsx.tmp file.xlsl 방식으로 이름 변경 처리
    • 이 과정을 통해 flush가 완료된 시점에만 최종 파일로 접근 가능하게 만듦
  • python flush() 시스템콜 직접 호출
    • 아래의 파이썬 호출 코드를 쉘 스크립트 에서 실행 python3 -c "f = open('${ORIGINAL_PATH}', 'rb+'); import os; os.fsync(f.fileno()); f.close()"

고려 사항

  • 해당 문제는 운영 환경에서만 발생하며, 개발 환경에서는 재현되지 않기 때문에 정확한 원인 분석이 제한적
  • 운영 서버에서 직접 코드를 수정하거나 임시 로직을 삽입해 테스트할 필요가 있음
  • 백엔드에서 생성하는 다른 엑셀 암호화 파일들도 같은 문제의 가능성이 존재
    • 다만, 해당 로직은 후처리 과정이 존재하기 때문에 자연스러운 딜레이가 발생해 flush가 완료되고, 문제 없이 다운로드가 가능했던 것으로 보임. 따라서 AIP 암호화 처리시 반드시 호출하는 쉘 스크립트 내에서 해결하는 것이 Best

요약

반응형

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

파일 flush 문제 해결 전략  (1) 2025.04.13
파일 flush 문제 공유 (NFS)  (0) 2025.04.13
반응형

목적

API Gateway의 목적은 클라이언트와 백엔드 서비스 간의 중간 계층 역할을 하면서 다양한 기능들을 수행하여 시스템의 효율성과 보안성을 높이는 것

특징

1. 단일 진입점 제공 (Single Entry Point)

  • 클라이언트는 여러 백엔드 서비스를 직접 호출하지 않고, API Gateway 하나만 호출하면 됨.
  • 마이크로서비스 아키텍처에서 특히 유용.

2. 요청 라우팅 (Request Routing)

  • 들어오는 요청을 적절한 백엔드 서비스로 라우팅
  • 예: /user → 사용자 서비스, /order → 주문 서비스

3. 보안 관리

  • 인증(Authentication), 인가(Authorization)를 중앙에서 처리
  • JWT 토큰 검사, API 키 체크 등

4. 로깅 및 모니터링

  • 요청 로그를 수집하거나, 트래픽 분석 등 관측 기능을 제공
  • 성능 모니터링, 트래픽 제어 등도 가능

5. 요청/응답 변환

  • 요청 또는 응답의 포맷을 변환 (예: XML ↔ JSON)
  • 백엔드는 단순하게 유지하고, 클라이언트 맞춤 응답 제공 가능

6. 로드 밸런싱 및 장애 처리

  • 여러 백엔드 인스턴스 사이에서 트래픽 분산
  • 장애 발생 시 다른 인스턴스로 우회

7. 속도 제한 및 트래픽 제어 (Rate Limiting)

  • 남용 방지를 위해 호출 빈도 제한
  • 과도한 요청에 대한 보호

8. 캐싱

  • 빈번한 요청 결과를 캐싱하여 응답 속도 개선 및 서버 부하 감소.

API Gateway는 다음과 같은 역할을 하며 시스템을 더 안전하고, 효율적이며, 확장 가능하게 도움을 준다.

API Gateway의 부하 분산

1. API Gateway가 부하 병목이 될 수 있는 이유

  • 모든 요청을 한 곳(API Gateway)으로 집결시키므로, 트래픽 집중이 발생.
  • 트래픽이 급증하면, API Gateway가 요청을 처리하는 데 시간이 오래 걸릴 수 있음.

2. 부하를 분산시키는 방법

2.1 수평 확장(Scaling Out)

  • API Gateway 서버를 여러 대 운영하고, 그 앞단에 로드 밸런서(Load Balancer) 를 두어 부하를 분산
  • 예: Nginx, AWS ALB, Google Cloud Load Balancing 등

2.2 캐싱(Caching) 활용

  • 자주 요청되는 결과를 캐싱하면, Gateway가 각 요청마다 백엔드까지 직접 호출할 필요가 없어짐
  • 응답 속도 향상 및 서버 부하 감소

2.3 비동기 처리(Async)와 메시지 큐(Message Queue)

  • 어떤 요청은 즉시 처리가 필요하지 않을 수 있습니다(예: 이메일 전송, 비동기 트랜잭션 등)
  • 메시지 큐(Kafka, RabbitMQ 등)를 통해 처리할 수 있도록 하여 Gateway가 동기 처리 부담을 완화

2.4 서버리스(Serverless) 사용

  • AWS API Gateway + Lambda 조합 등으로 필요한 시점에만 자동 확장이 가능하도록 설계
  • 초당 수백만 건의 요청도 일정 수준까지는 감당하도록 구성 가능

2.5 API Gateway 경량화

  • Gateway 레이어에 지나치게 많은 기능(인증/인가, 변환, 로깅 등)을 몰아넣으면 부하가 커지기에 잘 고려해야함
  • 꼭 필요한 기능만 Gateway에서 처리하고, 나머지는 별도 마이크로서비스 혹은 다른 계층에서 담당하게 하여 Gateway의 역할을 경량화

3. 단일 장애 지점을 피하는 고가용성(HA) 구성

  • API Gateway가 멈추면 모든 서비스가 사실상 중단될 가능성이 있음
  • 따라서, 멀티 AZ(가용 영역), 멀티 리전 또는 다중 Gateway 인스턴스 구성을 통해 장애 발생 시에도 서비스 중단이 최소화되도록 하는 구성이 필요

4. 결론

  • API Gateway가 모든 트래픽의 단일 진입점으로 동작하므로, 제대로 설계하고 확장 전략을 마련하지 않으면 병목 및 장애 지점이 될 수 있다.
  • 하지만 수평 확장, 캐싱, 비동기 처리, 고가용성 구성 등을 통해 충분히 분산 설계가 가능함
  • 즉, 잘못 설계하면 병목이 되지만, 올바른 인프라/아키텍처 구성과 모니터링을 통해 API Gateway의 이점(중앙 집중 관리, 보안, 모니터링 등)을 극대화하면서도 부하 문제 완화가능
반응형

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

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

백준 포도주 시식

https://www.acmicpc.net/problem/2156

접근 방식

조건 : 연속으로 놓여 있는 3잔을 모두 마실 수는 없음

최초에 값을 초기화할 때 앞의 2개의 잔을 모두 마실 필요가 있나? x

마셨을 때 : 1

마시지 않았을 때 0

기준으로 표현하자면,

경우의 수는 110, 101, 011로 좁혀지고, 010은 고려대상이 아님


구현 방법

  • DP배열에서 위 접근방식을 적용하면 해결됨

  • 바텀업으로 값을 채워나감
  • 최초 값은 index 2까지 초기화
    • 0 : 1
    • 1 : 11
    • 2 : 101, 110, 011
  • 이후 점화식을 코드로 구현
    • i index 기준으로 봤을 때 각 경우의 수는 아래와 같이 표현됨
    • 110 : dp[i-1]
    • 101 : dp[i-2] + arr[i]
    • 011 : dp[i-3] + arr[i-1] + arr[i]

풀이

import java.io.*;
import java.util.*;

public class p2156 {
    static int[] arr;
    static int[] dp;

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());

        arr = new int[n];
        dp = new int[n];

        for(int i = 0; i < n; i++){
            arr[i] = Integer.parseInt(br.readLine());
        }

        if(n == 1){
            System.out.println(arr[0]);
            return;
        }

        if(n == 2){
            System.out.println(arr[0] + arr[1]);
            return;
        }

        dp[0] = arr[0];
        dp[1] = arr[0] + arr[1];
        dp[2] = Integer.max(dp[1],
                Integer.max(dp[0] + arr[2], // 101
                        arr[1] + arr[2])); // 011

        for(int i = 3; i < n; i++){
            dp[i] = Integer.max(dp[i-1], // 110
                    Integer.max(dp[i-2] + arr[i], // 101
                            dp[i-3] + arr[i-1] + arr[i])); // 011
        }

        System.out.println(dp[n-1]);
    }
}


후기

점화식을 세워서 진행하는 바텀업 DP 문제는 경우의 수를 어떻게 잘 구현 해야할지 머릿속에서 잘 구상해야하는데 이게 아직도 어려운 것 같다.

반응형
반응형

워터마크(Watermark)란?

워터마크(Watermark)는 디지털 콘텐츠(이미지, 영상, 오디오, 문서 등)에 저작권 보호, 출처 표기, 위변조 방지 등의 목적으로 삽입되는 표식이다. 워터마크는 가시적(Visible)비가시적(Invisible) 방식으로 나뉜다.


1. 가시적 워터마크 (Visible Watermark)

  • 정의

이미지, 영상, 문서 등에 눈에 보이는 형태로 삽입된 워터마크를 의미한다.

  • 특징
    • 사람이 직접 볼 수 있음 (예: 로고, 텍스트, 투명한 마크)
    • 저작권 보호 및 출처 표시 목적
    • 복제 방지를 위한 억제 효과
    • 그러나 이미지 편집 기술(포토샵 등)로 제거 가능
  • 예시
    • 사진에 삽입된 "© 2025 PhotographerName" 표시
    • 영상의 한쪽 구석에 있는 방송국 로고 (예: "KBS", "MBC")
    • PDF 문서의 배경에 투명하게 삽입된 회사명

2. 비가시적 워터마크 (Invisible Watermark)

  • 정의

사람의 눈에는 보이지 않지만, 디지털 신호(이미지, 영상, 오디오, 문서)에 숨겨진 형태로 삽입된 워터마크를 의미한다.

  • 특징
    • 사람이 직접 볼 수 없음 (특정 소프트웨어나 알고리즘을 통해 확인 가능)
    • 저작권 보호 및 원본 출처 확인
    • 변형, 편집 후에도 검출 가능 (강인성에 따라 다름)
    • 이미지, 오디오, 영상에 널리 사용됨
  • 기술적 기법
    • 공간 영역 기법: 픽셀값에 미세한 변화 삽입
    • 주파수 영역 기법: 푸리에 변환(DFT), 웨이브렛 변환(DWT) 등을 활용해 삽입
    • 양자화 지수 변조(QIM): 특정 패턴을 신호에 삽입하여 탐지
  • 예시
    • 사진 속 특정 픽셀에 디지털 서명 숨기기
    • 영화나 음악 파일에 저작권 정보 삽입
    • 문서에 특정한 패턴을 추가해 출처 확인

3. 가시적 vs 비가시적 워터마크 비교

비교 항목 가시적 워터마크 비가시적 워터마크
눈으로 보이는지 O (보임) X (보이지 않음)
저작권 보호 효과 높음 (사람들이 인식) 높음 (탐지 필요)
위변조 방지 제거 가능성 높음 강인한 방식은 제거 어려움
출처 추적 육안으로 확인 가능 디지털 분석 필요
사용 예시 사진 로고, 방송국 마크, PDF 배경 디지털 인증, 영상·음원 추적

결론

  • 가시적 워터마크는 저작권 보호를 강조하고 쉽게 알아볼 수 있는 장점이 있지만 제거될 가능성이 있다.
  • 비가시적 워터마크는 콘텐츠의 무결성을 유지하면서도 추적이 가능하지만, 분석 도구가 필요하다.
  • 두 가지 방법을 함께 사용하면 보안성과 저작권 보호 효과를 극대화할 수 있다.
반응형

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

WAF, ZTN  (1) 2025.04.27
API Gateway  (0) 2025.03.24
아키텍처 간략 정리  (0) 2025.01.05
언어별 연산 속도  (0) 2024.12.30
Context Switching(문맥 교환)  (0) 2024.09.19
반응형

백준 감소하는 수(줄어드는 수)

https://www.acmicpc.net/problem/1174

https://www.acmicpc.net/problem/1038

접근 방식

최초엔 n까지 범위의 수를 생각했는데, 잘못 생각한 것이였고, 앞자리부터 점점 감소하는 수의 모든 경우의 수 중 몇번째인지 n으로 입력받아 찾는 문제


구현 방법

  • 감소하는 수에 대해 모든 조합을 찾은 후 정렬해 수행
  • 이 때 최대 수의 범위까지 1씩 더해가며 찾는 것이 아닌, 값이 들어갈 수 있는 범위에 대해서만 찾기

  • 9876543210 까지가 최대 수
  • 재귀에서 자릿수(depth)를 받아서 depth 범위만큼 foreach돈다.
  • 기본적으로 for문 돌 때 자기보다 낮은 수에 대해서만 수행
  • 재귀 안에서 count가 같으면 return - count는 static으로 갖고있기에 종료가능
  • for문이 다 돌면 재귀호출 시행

풀이

package Baekjoon.gold;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

public class p1174 {

    static Stack<Long> st = new Stack<>();
    static ArrayList<Long> al = new ArrayList<>();

    static int count = 0;
    static int n;
    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        n = Integer.parseInt(br.readLine());

        /*
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9
         10 | 20, 21 | 30, 31, 32 | 40, 41, 42, 43 | 50, 51, 52, 53, 54 |
         60, 61, 62, 63, 64, 65 | 70, 71, 72, 73, 74, 75, 76 |
         80, 81, 82, 83, 84, 85, 86, 87 | 90, 91, 92, 93, 94, 95, 96, 97, 98 |
         210 |320, 321, 310 | 410, 420, 421, 430, 431, 432
         */

        //9 -> 8 7 6 5 4 3 2 1 0
        //8 -> 7 6 5 4 3 2 1 0
        //7 -> 6 5 4 3 2 1 0
        //6 -> 5 4 3 2 1 0
        //5 -> 4 3 2 1 0
        //4 -> 3 2 1 0
        //3 -> 2 1 0
        //2 -> 1 0
        //1 -> 0

        // 10 20 21 30 31 32
        //9876543210 까지가 최대 수
        //재귀에서 자릿수(depth)를 받아서 depth 범위만큼 foreach돈다.
        //기본적으로 for문 돌 때 자기보다 낮은 수에 대해서만 수행
        //재귀 안에서 count가 같으면 return - count는 static으로 갖고있기에 종료가능
        //for문이 다 돌면 재귀호출 시행

        if(n <= 10){
            System.out.println(n-1);
            return;
        }
        recur(10);
        List<Long> list = al.stream().sorted().collect(Collectors.toList());

        long result = -1;
        if(list.size() >= n){
            result = list.get(n-1);
        }

        System.out.println(result);
    }

    private static void recur(long nowNum){
        for(long i = 0; i < nowNum; i++){
            count++;
            st.push(i);
            st.forEach(sb::append);
            al.add(Long.valueOf(sb.toString()));
            sb.setLength(0);
            recur(i);
            st.pop();
        }
    }
}

후기

최초에 문제를 잘못 읽었고, 이후엔 전체 탐색을 효율적으로 하기위한 노력(가지치기)을 하는 데 시간을 많이 썼음

반응형
반응형

AtomicInteger.class의 메서드를 탐방하다 발견하게 된 native 키워드 탐방

@IntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);

native 키워드는 Java에서 네이티브 코드를 사용해 자바 코드로는 직접 수행하기 어려운 작업을 해결하기 위해 사용됨. Java는 기본적으로 플랫폼 독립성을 목표로 설계된 언어지만, 외부에 작업 수행을 맡기기도 한다.

native 키워드의 역할 및 특징

운영체제, 하드웨어 종속작업 처리

Java는 플랫폼 독립성을 가진 언어이지만, 운영 체제나 하드웨어에 종속적인 작업을 처리해야할 때 C/C++로 작성된 네이티브 코드를 호출해 운영 체제의 API나 하드웨어를 직접 제어해야 한다.

  • 예:
    • 파일 시스템 접근 (FileInputStream, FileOutputStream)
    • 네트워크 소켓 관리
    • 그래픽 처리를 위한 GUI 라이브러리 (AWT/Swing)
    • 시스템 시간 가져오기 (System.currentTimeMillis())
    • concurrent(동시성) 관련 코드

성능 최적화

Java는 일반적으로 JVM 위에서 동작하므로 C/C++로 작성된 네이티브 코드에 비해 약간의 오버헤드가 존재. 특히, 자바의 네이티브 메서드는 속도가 중요한 저수준 작업에서 성능을 극대화하기 위해 사용.

  • 예:
    • AtomicInteger와 같은 CAS (Compare-And-Swap) 연산은 C/C++로 구현된 네이티브 코드를 호출하여 고성능을 보장.
    • arraycopy()는 배열 복사를 네이티브 코드로 처리하여 높은 속도를 보장.

하드웨어 제어

Java는 추상화된 언어로, 하드웨어를 직접 제어하는 데 적합하지 않다. 하지만 네이티브 코드를 사용하면 Java 프로그램이 특정 하드웨어 장치를 제어하거나 센서 데이터를 읽을 수 있다.

  • 예:
    • 임베디드 시스템에서 Java와 네이티브 코드를 조합해 사용.
    • 특정 하드웨어 기능 호출.

멀티스레딩 및 동기화

Java는 스레드 관리와 동기화 관련 기능을 제공하지만, JVM의 성능을 극대화하거나 플랫폼 종속적인 동기화 메커니즘을 사용하기 위해 네이티브 메서드가 필요할 수 있다.

  • 예:
    • Thread.sleep()와 같은 메서드는 네이티브 코드를 통해 구현되어 운영 체제의 스레드 관리 기능을 호출.
    • AtomicIntegercompareAndSet() 같은 원자적 연산은 하드웨어 명령을 활용한 네이티브 코드로 구현.
반응형
반응형

멀티스레드 문제 예시 - 은행 계좌 잔액 갱신 문제

class BankAccount {
    private int balance = 100;

    public void withdraw(int amount) {
        if (balance >= amount) { // 조건 검사
            balance -= amount;  // 잔액 갱신
        }
    }
}

의도된 동작

  1. 스레드 A가 50을 출금.
  2. 스레드 B가 50을 출금.
  3. 결과적으로 잔액은 0이 되어야 함.

문제 상황

  • 스레드 A와 스레드 B가 동시에 withdraw(50) 메서드를 호출하면, 잔액 조건 검사와 갱신 작업이 중첩될 수 있음.
  • 이로 인해 두 스레드가 잔액 조건을 동시에 검사한 후, 동시에 출금을 진행.
  • 결과적으로, 잔액이 50으로 잘못 계산됨.

 

**정상적인 단일 스레드 실행**

-------
스레드 A 실행:
[검사] 잔액: 100 >= 50  -> True
[갱신] 잔액 -= 50       -> 잔액: 50

**멀티스레드에서의 충돌**
스레드 A 실행:                          스레드 B 실행:
[검사] 잔액: 100 >= 50  -> True        [검사] 잔액: 100 >= 50  -> True
[갱신] 잔액 -= 50       -> 잔액: 50    [갱신] 잔액 -= 50       -> 잔액: 50

결과: 두 스레드가 모두 출금을 완료했지만, 최종 잔액은 50으로 계산됨 (잘못된 결과).

멀티스레드 환경의 위험 요소

  1. Race Condition (경쟁 상태)
    • 여러 스레드가 동시에 동일한 자원에 접근하고 이를 수정할 때, 실행 순서에 따라 결과가 달라질 수 있음.
    • 예: 위의 은행 계좌 예제에서 스레드 간 동기화가 없을 경우 발생.
  2. Deadlock (교착 상태)
    • 두 스레드가 서로 자원을 기다리며 무한히 대기하는 상황.
    • 예: 스레드 A가 자원 1을 점유하고 자원 2를 기다리는 동안, 스레드 B는 자원 2를 점유하고 자원 1을 기다림.
  3. Data Corruption (데이터 손상)
    • 여러 스레드가 동일한 메모리 위치를 수정하면, 결과값이 예기치 않게 손상될 수 있음.
    • 예: 공유 데이터 구조의 상태가 불완전하거나 잘못된 상태로 유지됨.
  4. Thread Interleaving (스레드 중첩 실행)
    • 스레드가 실행 중에 문맥 교환(Context Switching)으로 인해 실행 흐름이 중첩됨.
    • 예: 스레드 A와 B가 교차 실행되며 연산이 중간 상태에서 중단됨.

해결 방법

1. 동기화 사용

  • synchronized 키워드: 공유 자원에 한 번에 하나의 스레드만 접근하도록 보장.
  • ReentrantLock: 더 세밀한 락 제어 가능.
public synchronized void withdraw(int amount) {
    if (balance >= amount) {
        balance -= amount;
    }
}

2. 원자적 연산 사용

  • Java의 AtomicInteger 또는 AtomicLong과 같은 클래스 사용.
  • 내부적으로 CAS(Compare-And-Swap)를 활용하여 동기화 문제를 해결.
import java.util.concurrent.atomic.AtomicInteger;

class BankAccount {
    private AtomicInteger balance = new AtomicInteger(100);

    public void withdraw(int amount) {
        balance.addAndGet(-amount); // 원자적 연산
    }
}

3. 동시성 제어 도구

  • CountDownLatch, Semaphore, CyclicBarrier 등 사용.
  • 특정 조건에서 스레드의 실행을 제어하여 동기화 문제를 방지.
반응형
반응형

AtomicInteger.class

Java에서 사용하는 클래스로 멀티 스레드 환경에서 안전하게 사용되기 위한 원자성을 보장받는 자료형이다

클래스의 주요 기능 및 특징

  • 원자적 연산
    • 다른 스레드가 해당 연산의 중간 상태를 볼 수 없음
  • 스레드 안전
    • 여러 스레드에서 동시에 접근하더라도 데이터 경합 없이 안전하게 값을 읽고 쓸 수 있음
  • 락 없이 동작
    • 기존의 동기화 방식(synchronized 키워드)은 락을 사용하지만, 아토믹 연산은 CPU가 제공하는 하드웨어 수준의 락-프리(Lock-Free) 매커니즘을 사용
  • CAS 알고리즘
    내부적으로 CAS(Compare-And-Swap) 알고리즘을 사용하여 값을 업데이트
    • 특정 메모리 위치 값이 예상값과 같은지 확인한 후, 같다면 새 값으로 교체
    • 이 과정에서 충돌을 감지

주요 메서드

  • get()
  • set(int newValue)
  • getAndIncrement()
    • 현재 값 반환 후 1 증가
  • incrementAndGet()
    • 값을 1 증가시키고 증가된 값 반환
  • compareAndSet(int expect, int update)
    • 현재 값이 expect와 같으면 update 값으로 변경 (CAS 연산)

사용 고려 시점

  • 동시성 프로그래밍에서 데이터를 안전하게 업데이트해야 하는 경우
    • 멀티 스레드 환경에서 카운터 증가
    • 통계 집계
  • 락 기반 동기화보다 가벼운 대안
  • 성능이 중요한 환경에서 데이터 무결성을 유지해야 할 때

❓CAS (Compare-And-Swap)

CAS는 값의 업데이트가 특정 조건에서만 이루어지도록 보장하는 하드웨어 지원 원자적 연산
따라서 자바에만 있는 것이 아니라, C 계열의 언어에서도 std::atmoic 클래스와 같이 사용되고 있음

CAS 작동 방식

  • 특정 메모리 위치에 대해, 세 값을 비교 및 업데이트
    • 현재 값 (current): 현재 메모리에 저장된 값
    • 기대 값 (expected): 우리가 예상하는 값
    • 새 값 (new): 우리가 저장하려는 값
  • CAS 연산의 과정
    • 메모리의 현재 값이 기대 값(expected)과 동일하면, 새 값으로 업데이트
    • 메모리의 현재 값이 기대 값과 다르면 실패를 반환

CAS 장점

  • 스케일링 가능성: 여러 스레드가 동시에 접근해도 성능 저하가 적다.
  • 스레드가 블로킹되지 않으므로 데드락 위험성이 없다

CAS 단점

  • Busy-waiting:
    • 실패할 경우 재시도하는 루프를 사용하므로 CPU 자원을 소비할 수 있다.
    • 특히 충돌이 빈번하면 성능 저하가 발생할 수 있다.
  • ABA 문제:
    • 메모리 값이 A -> B -> A로 변경된 경우, CAS는 값이 바뀌지 않았다고 판단할 수 있다.
    • 이 문제는 태그(tag) 또는 버전 관리를 통해 해결

코드 예시 - 과정만 참고

public final int incrementAndGet() {
    for (;;) {
        int current = get(); // 현재 값 읽기
        int next = current + 1; // 새로운 값 계산
        if (compareAndSet(current, next)) // CAS 연산
            return next; // 성공 시 반환
    }
}

CPU의 원자적 명령어

CPU는 CAS 연산을 지원하기 위해 특정 명령어(기계어)를 제공

  • x86 아키텍처 - CMPXCHG(Compare and Exchange)
  • ARM 아키텍처 - LDREX/STREX

메모리 값을 읽고 수정하는 작업을 한 번에 처리하므로, synchronized 키워드 없이 안전하게 업데이트


반응형

'호기심 천국 > Java' 카테고리의 다른 글

멀티스레드 문제 상황 예시 1  (1) 2025.01.20
StringBuilder vs String 문자열합치기  (0) 2023.09.19
반응형
  • 컨트롤러 테스트에서 사용한 기법

    • Mocking을 통해 Controller 단위 테스트 시행
    • 통합테스트가 아닌 단위 테스트를 진행할 때는 어노테이션에 주의해야합니다.
    • 이번 테스트에 사용된 UserControllerTest.java 파일에서 사용된 WebMvcTest를 명시해 사용하면, 컨트롤러 레이어의 단위테스트 형태를 갖출 수 있습니다
  • Mocking 기법 예시

    • Mocking을 통해 Controller 레이어의 단위테스트가 목적
    • 필요없는 내부동작을 임의로 동작한 것으로 치고 반환
    • Controller에서 필요없는 로직인 UserService안의 로직을 Mocking하여, Controller 레이어에서 사용되는 Request, Response, Valid 등을 테스트할 수 있음

@Slf4j
@WebMvcTest(UserController.class) // WebMvcTest는 컨트롤러 레이어만 테스트하기 위해 스프링의 WebMvc 관련 빈만 로드
@MockBean(JpaMetamodelMappingContext.class) // jpa동작하지않도록
@AutoConfigureMockMvc(addFilters = false) // 시큐리티 필터 제외
@ActiveProfiles("test")
class UserControllerTest {
    ...... 중략
    @Autowired
    private MockMvc mockMvc; //실제 서블릿 컨테이너 없이도 HTTP 요청과 응답을 테스트하기 위해 사용

    @Autowired
    private ObjectMapper objectMapper; // Response 매핑을 위한 객체

    @MockBean
    private UserService userService; // @MockBean을 활용하여 UserService를 Mocking


    private final User mockUser = User.builder()
                .id(UUID.randomUUID())
                .email("mockUser@naver.com")
                .nickname("mockUser")
                .password("abcd1234!")
                .isMarketing(true)
                .isAlarm(true)
                .state(State.ACTIVE)
                .build();

    @Test
    @DisplayName("회원가입 성공 response 테스트")
    void testCreateUserSuccess() throws Exception {
    CreateUserReq createUserReq = CreateUserReq.testCreate(); // 임의의 UserReq 생성

    /** CreateUserReq 타입에 맞는 객체가 UserService.create에 들어가면 mockUser를 반환하도록
    *    given 메서드는 BDD 스타일 테스트에서 사용되는 Mockito의 메서드
    *    mocking객체(UserService)안의 파라미터에 any(), eq() 두 메서드를 통해 값을 넣어줄 수 있고
    *    any(CreateUserReq.class) 선언 시 CreateUserReq 타입이 userService.create() 안에 들어가야 모킹된 mockUser가 반환된다
    **/
    given(userService.create(any(CreateUserReq.class))).willReturn(mockUser);

    /**
    *  "/users"의 위치로 헤더에 MediaType 타입에 APPLICATION_JSON, body에 createUserReq의 값을 담아 요청
    *  andExpect() 안에서 값이 어떻게 반환되는지 확인 현재 예시에서는 201반환하는지 확인
    **/
    mockMvc.perform(post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(createUserReq)))
            .andExpect(status().isCreated());
    ...... 중략
}

❗구현이슈

  • @AutoConfigureMockMvc(addFilters = false) // 시큐리티 필터 제외
  • 임의의 시큐리티 필터를 생성했을 시엔, 해당 어노테이션을 넣어줘야 한다.
  • 넣어주지 않을 시 실제 시큐리티 필터를 타서 문제 발생
반응형

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

SQL Injection  (0) 2023.09.30
스프링부트 Lombok @RequiredArgsConstructor잘 알고쓰자  (0) 2023.07.26
반응형

아키텍처 간략 정리

1. MVC (Model-View-Controller)

구성 요소

  • Model: 데이터와 비즈니스 로직을 관리합니다.
  • View: 사용자에게 정보를 시각적으로 표시합니다.
  • Controller: 사용자 요청을 처리하고 Model과 View를 연결합니다.

특징

  • 역할이 분리되어 코드의 가독성과 재사용성이 높습니다.
  • 사용자가 View와 상호작용하면 Controller가 이를 처리하고 Model을 업데이트한 후, View에 결과를 표시합니다.

활용

  • 웹 애플리케이션(스프링 MVC, Ruby on Rails 등)
  • 데스크톱 애플리케이션

2. MVVM (Model-View-ViewModel)

구성 요소

  • Model: 데이터와 비즈니스 로직.
  • View: UI 요소.
  • ViewModel: View와 Model 사이의 중간 계층으로, View의 상태를 관리하고, 데이터 바인딩을 지원합니다.

특징

  • 양방향 데이터 바인딩을 통해 View와 ViewModel의 동기화를 자동으로 처리합니다.
  • View는 데이터에 대한 의존성을 최소화합니다.

활용

  • WPF(Windows Presentation Foundation)
  • Angular, Vue.js, React + Redux/MobX와 같은 SPA(Single Page Application) 프레임워크

3. MVP (Model-View-Presenter)

구성 요소

  • Model: 데이터와 비즈니스 로직.
  • View: 사용자 인터페이스(UI).
  • Presenter: View의 로직을 담당하며 Model과 상호작용한 후 데이터를 View에 전달합니다.

특징

  • View는 단순히 UI를 표시하는 역할만 하고, 모든 로직은 Presenter에서 처리됩니다.
  • View와 Presenter는 1:1 관계를 갖습니다.

활용

  • 안드로이드 애플리케이션
  • 레거시 데스크톱 애플리케이션

4. Clean Architecture

구성 요소

  • Entities: 애플리케이션의 비즈니스 규칙을 캡슐화.
  • Use Cases: 애플리케이션의 특정 작업이나 기능을 정의.
  • Interface Adapters: 데이터와 UI의 변환 계층.
  • Frameworks & Drivers: 데이터베이스, UI, 외부 API 등.

특징

  • 의존성 규칙: 안쪽 계층은 바깥쪽 계층을 알지 못합니다.
  • 비즈니스 로직이 프레임워크와 독립적이어서 확장성과 유지보수가 용이합니다.

활용

  • 대규모 시스템
  • 멀티 플랫폼 애플리케이션

5. Layered Architecture (계층형 아키텍처)

구성 요소

  • Presentation Layer: 사용자 인터페이스.
  • Application Layer: 비즈니스 로직.
  • Data Layer: 데이터베이스 및 데이터 액세스.

특징

  • 계층 간 명확한 분리가 이루어집니다.
  • 단순하고 이해하기 쉬운 구조지만, 복잡한 시스템에서 성능 저하가 있을 수 있습니다.

활용

  • 전통적인 웹 애플리케이션(3-tier 아키텍처)
  • 엔터프라이즈 애플리케이션

6. Microservices Architecture

구성 요소

  • 애플리케이션을 독립적으로 배포 가능한 작은 서비스들로 나눕니다.
  • 각 서비스는 고유한 비즈니스 기능을 담당하며 API로 통신합니다.

특징

  • 독립적인 배포와 확장이 가능합니다.
  • 서비스 간 통신 오버헤드가 있고, 분산 시스템의 복잡성이 증가할 수 있습니다.

활용

  • 대규모 분산 시스템
  • 클라우드 네이티브 애플리케이션

7. Event-Driven Architecture

구성 요소

  • Event Producer: 이벤트를 생성하고 발행.
  • Event Consumer: 이벤트를 처리.
  • Event Broker: 이벤트를 전달하거나 중개.

특징

  • 비동기 통신을 기반으로 설계되어 높은 확장성을 제공합니다.
  • 복잡한 이벤트 흐름을 관리하기 위한 설계가 필요합니다.

활용

  • IoT 시스템
  • 실시간 애플리케이션(채팅, 트랜잭션 시스템)

8. Hexagonal Architecture (Ports and Adapters)

구성 요소

  • Core (Application Logic): 애플리케이션의 핵심 비즈니스 로직.
  • Ports: 외부와의 상호작용을 정의하는 인터페이스.
  • Adapters: 특정 구현을 제공하여 Core와 통신.

특징

  • 비즈니스 로직과 외부 시스템 간의 의존성을 최소화합니다.
  • 높은 테스트 가능성과 확장성을 제공합니다.

활용

  • 복잡한 도메인 로직이 있는 시스템
  • 시스템 간 통합이 필요한 애플리케이션

9. Serverless Architecture

구성 요소

  • 클라우드 기반 서비스와 함수(Function-as-a-Service)를 이용하여 애플리케이션을 실행.
  • 서버를 관리하지 않고도 클라우드 제공자가 모든 인프라를 처리.

특징

  • 확장성과 비용 효율성이 높습니다.
  • 서버 환경에 대한 제어가 제한적입니다.

활용

  • 이벤트 중심 애플리케이션
  • 간단한 API 백엔드

10. CQRS (Command Query Responsibility Segregation)

구성 요소

  • Command: 데이터 변경 작업(쓰기).
  • Query: 데이터 읽기 작업.

특징

  • 읽기와 쓰기를 분리하여 각 작업을 최적화합니다.
  • 복잡한 시스템에서 동시성과 확장성을 지원합니다.

활용

  • 이벤트 소싱 시스템
  • 고성능 읽기/쓰기 작업이 필요한 시스템

11. Pipe and Filter Architecture

구성 요소

  • Filters: 데이터를 처리하는 독립적인 컴포넌트.
  • Pipes: 필터 간 데이터를 전달.

특징

  • 데이터 흐름을 직관적으로 표현할 수 있습니다.
  • 처리 단계가 많아지면 성능 저하가 발생할 수 있습니다.

활용

  • 데이터 처리 파이프라인
  • 이미지 또는 오디오 프로세싱

12. Service-Oriented Architecture (SOA)

구성 요소

  • 독립적인 서비스들로 구성되며, 각 서비스는 특정 비즈니스 기능을 제공합니다.
  • 서비스는 표준 프로토콜(예: SOAP, REST)을 통해 통신합니다.

특징

  • 서비스 간 의존성이 낮아 확장성과 재사용성이 높습니다.
  • 복잡한 통합과 관리가 필요합니다.

활용

  • 엔터프라이즈 애플리케이션
  • 분산 시스템

CSR에서 자바 스프링 백엔드 아키텍처

  1. 계층형 아키텍처 사용 (Layered Architecture)
  2. RESTful API 서버로 프론트엔드와 데이터 통신
  3. JPA + Hibernate를 통한 데이터베이스 연동
  4. JWT를 통한 보안 및 인증 처리
  5. 마이크로서비스 및 API Gateway 적용 가능 (Spring Cloud Gateway)
  6. 비즈니스 로직, 예외 처리, 로깅, 모니터링 구성

➡️ CSR 환경에서는 프론트엔드가 주로 UI를 렌더링하고, 자바 스프링 백엔드는 데이터 제공, 비즈니스 로직 처리, 보안, 데이터베이스 연동을 주로 담당합니다.

아키텍처 패턴 선택 가이드

  1. 프로젝트 규모:
    • 작은 프로젝트: MVC, MVP
    • 대규모 프로젝트: Microservices, Clean Architecture
  2. 프론트엔드 및 백엔드 협업:
    • 단일 애플리케이션: MVVM, MVC
    • 분리된 환경: RESTful API 기반 MC
  3. 확장성 요구:
    • 높은 확장성: Microservices, Serverless
  4. 실시간 처리:
    • 실시간 데이터: Event-Driven, CQRS
반응형

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

API Gateway  (0) 2025.03.24
워터마크 기술 개념  (0) 2025.03.03
언어별 연산 속도  (0) 2024.12.30
Context Switching(문맥 교환)  (0) 2024.09.19
해시 - 솔트치기  (0) 2024.08.25

+ Recent posts