CloudFront 도입 배경과 보안에 대한 설명
  1. 왜 S3의 URL을 직접 노출하면 안 되는가?
    • S3 버킷의 URL이 공개되면, 누구든지 해당 URL을 알고 있는 사람은 데이터를 바로 다운로드하거나 볼 수 있음
    • 이런 식으로 데이터에 쉽게 접근할 수 있으면, 악의적인 사용자가 데이터를 무단으로 이용하거나 시스템을 공격할 가능성이 커짐
    • 따라서 S3의 URL이 외부에 직접적으로 노출되지 않도록 하는 것이 중요
  2. CloudFront는 어떤 역할을 하는가?
    • CloudFront는 S3와 최종 사용자(웹사이트 방문자 또는 애플리케이션 사용자) 사이에서 중개 역할을 함
    • 사용자는 CloudFront의 도메인을 통해 데이터를 요청하게 되고, CloudFront는 S3에서 데이터를 가져와 사용자에게 전달함
    • 이렇게 하면 사용자가 S3의 원본 URL을 알 수 없게 되고, S3 버킷 자체가 외부에 노출되지 않게 됨
    • 결과적으로, 데이터를 안전하게 제공할 수 있는 환경이 만들어짐
  3. CloudFront URL이 노출되면 괜찮은가?
    • 현재 서비스에서는 사진과 같은 콘텐츠만 S3에 저장하고 있고, 유저의 개인정보나 민감한 데이터는 포함하지 않음
    • 이런 경우 CloudFront URL이 노출되더라도 큰 문제가 되지는 않음
    • 하지만 만약 유저의 개인정보나 중요한 데이터를 제공해야 하는 경우라면, CloudFront를 통해 접근 제한 설정을 적용할 수있음
    • 예를 들어, Signed URL을 사용하면 인증된 사용자만 특정 콘텐츠를 볼 수 있도록 제한할 수 있음
  4. CloudFront를 도입한 다른 이유는 무엇인가?
    • 이미지 리사이징과 같은 추가적인 기능을 제공하기 위해서도 CloudFront는 필요했음.

'Cloud' 카테고리의 다른 글

Podman 으로 Zoomoney 프로젝트 배포하기  (0) 2025.03.22
Skopeo  (0) 2025.01.21
Dev-Ops  (0) 2025.01.20
기본적인 DevOps 를 하기위한 리눅스명령어  (0) 2024.11.26
쿠버네티스  (0) 2024.11.19

문제 정의

  • 현상:
    • S3에서 매번 원본 데이터를 직접 가져오는 구조 →  네트워크 성능 저하 및 리소스 낭비
    • 대용량 이미지 파일 사용 → S3 저장공간 비용 및 S3와 클라이언트 간 트래픽 비용 증가
    • S3의 직접적인 URL 노출 → 악성 공격에 취약
  • 해결 방향:
    • CDN(Content Delivery Network)을 활용하여 콘텐츠 전송 최적화.
    • **이미지 포맷 최적화(Webp)와 이미지 리사이징(Thumbnail)**을 통해 네트워크 대역폭 절약 및 저장소 비용 최적화.
    • S3 URL 노출을 방지하고, 안전한 이미지 제공을 위한 접근 제어를 강화
      • CloudFront를 통해 S3의 URL을 감추는 것만으로도 보안적인 장점을 얻을 수 있음

CDN(Content Delivery Network)

  • 📝 정의:
    • CDN은 콘텐츠를 효율적으로 전달하기 위해 설계된 네트워크 시스템으로, 데이터를 사용자와 가까운 서버에 분산 저장하여 빠르게 제공.
  • ⚙️ 동작 원리:
    1. 콘텐츠를 여러 CDN 노드에 분산 저장.
    2. 사용자가 콘텐츠를 요청하면 가장 가까운 노드에서 데이터를 전달받음.
    3. 요청한 데이터가 캐싱되어 있지 않다면, **Origin Server(S3)**에서 콘텐츠를 가져와 노드에 저장한 뒤 사용자에게 전달.
  • 📂 캐싱 방식:
    • Static Caching: 사용자의 요청이 없어도 Origin Server의 콘텐츠를 운영자가 미리 Cache Service에 복사
    • Dynamic Caching: 사용자가 콘텐츠를 요청하면, Origin Server에서 콘텐츠를 가져와 저장
  • 🛠️ AWS에서의 구현: AWS CloudFront를 사용하여 쉽게 CDN 적용 가능.
    • CloudFront는 전 세계에 분산된 엣지 로케이션에서 데이터를 제공, 사용자와의 물리적 거리를 최소화.

이미지 리사이징 방식 비교

  1. 📤 업로드 시 리사이징
    • 작동 방식:
      • S3에 원본 이미지를 업로드할 때 여러 크기의 리사이징된 이미지를 사전에 생성하여 저장.
    • 문제점:
      • 저장소 사용량 증가: 모든 크기의 이미지를 저장해야 하므로 비용 부담이 큼.
      • 유지보수 복잡성: 썸네일 사양이 변경되면 기존 리사이징 이미지를 모두 재생성해야 함.
      • 다양한 디바이스 해상도에 대응하기 어려움.
  2. ⚡ On-demand 리사이징
    • 작동 방식:
      • 클라이언트 요청 시점에 Lambda 함수로 이미지를 리사이징하고 CDN에 캐싱.
    • 장점:
      • 필요한 크기의 이미지만 생성 → S3 저장소 사용량 절감.
      • 네트워크 대역폭 감소 → 사용자 경험 개선
      • WebP와 같은 최신 이미지 포맷을 클라이언트 환경에 맞춰 실시간으로 제공 가능.
    • 단점:
      • 첫 요청 시 리사이징 처리 시간이 발생할 수 있음

On-demand 이미지 리사이징을 선택한 이유

  • 이미 S3에 원본 이미지가 다량으로 업로드된 상황에서, 기존 이미지를 활용하여 추가 작업(썸네일 생성 및 WebP 변환)을 수행하기 위한 최적의 선택
  • 클라이언트 요청 시점에 필요한 크기와 포맷으로 이미지를 생성하므로 기존 원본 데이터를 그대로 유지하면서 새로운 요구사항을 충족 가능

Lambda@Edge를 활용한 이미지 리사이징

  • Lambda@Edge란?
    • AWS의 서버리스 컴퓨팅 기능으로, CloudFront와 통합되어 사용자와 가까운 엣지 서버에서 Lambda 코드를 실행.
    • 특징:
      • 서버 관리 필요 없음.
      • 클라이언트와 가까운 엣지 위치에서 실행되어 응답 속도 향상.
  • 트리거 이벤트:
    • 🟢 Viewer request: 사용자가 CloudFront로 보내는 요청
    • 🟡 Origin request: 캐시 미스 발생 시 CloudFront가 Origin(S3)로 리소스 요청
    • 🟠 Origin response: Origin(S3)의 리소스를 CloundFront에 응답
    • 🔵 Viewer response: Origin의 리소스/cache hit된 응답

아키텍처 변화

  1. 기존 아키텍처:
    • 클라이언트가 S3에서 원본 이미지를 직접 가져옴
    • CDN 적용 후:
      • CloudFront를 통해 캐싱된 이미지 제공 → 데이터 전송 속도 향상.

3.

  • Lambda@Edge 적용 후:
    • 클라이언트 요청 시 Lambda 함수로 이미지를 리사이징하고, 리사이징된 이미지를 CDN에 캐싱.

CloudFront (CDN) 적용 

1. S3 버킷 생성 

{
    "Version": "2012-10-17",
    "Id": "Policy1735204464349",
    "Statement": [
        {
            "Sid": "Stmt1735204460877",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::rentally/*"
        }
    ]
}

-개선전-

<img src="https://projectname.s3.xxxxxx.amazonaws.com/${productlist.category_seq}/${productlist.product_img}"/>

 

 

<크롬 LigthHouse>

문제점

1.S3에서 매번 원본 데이터를 직접 가져오는 구조 →  네트워크 성능 저하 및 리소스 낭비

2.대용량 이미지 파일 사용 → S3 저장공간 비용 및 S3와 클라이언트 간 트래픽 비용 증가

3.S3의 직접적인 URL 노출 → 악성 공격 및 보안에 취약

--개선후--

개선방법

1. CDN(CloudFront) 도입으로 클라이언트 요청시 원본데이터가 아닌 캐싱된 데이터 Load 로 트랙픽 요청 시간 축소

2.S3업로드 시 리사이징 / On-Demand 시 리사이징(Lambda@Edge 사용)

>>Lambda@Edge란?

:AWS의 서버리스 컴퓨팅 기능으로, CloudFront와 통합되어 사용자와 가까운 엣지 서버에서 Lambda 코드를 실행

 

-원본요청시-

 

-Cloud-Front 첫적용- WebP 변환 Thumbnail 적용

-CloudFront Cache Hit 결과-

 

결과

 

1. 청크 전송 인코딩(Chunked Transfer Encoding)란?
  • HTTP/1.1에서 사용하는 데이터 전송 방식 중 하나입니다.
  • 데이터를 한꺼번에 전송하지 않고 작은 청크(chunk) 단위로 나눠 순차적으로 클라이언트에 보냅니다.
  • 마지막 청크는 길이가 0으로 표시되어 전송이 종료되었음을 알립니다.

예:

http
코드 복사
HTTP/1.1 200 OK Transfer-Encoding: chunked 4 Wiki 5 pedia 0
  • 문제 발생: 만약 서버가 전송을 완료하지 못하고 연결이 끊기거나, 마지막 청크(0)가 제대로 전달되지 않으면 클라이언트는 응답이 불완전하다고 판단해 ERR_INCOMPLETE_CHUNKED_ENCODING 에러를 표시합니다.

2. 에러 발생 원인

이 에러는 서버, 클라이언트, 네트워크 환경 등 다양한 요인으로 발생할 수 있습니다. 주요 원인은 다음과 같습니다:

(1) 서버의 응답 중단

  • 서버가 응답을 보내는 도중 오류가 발생하여 전송이 완료되지 않은 경우.
    • 서버 프로세스가 강제 종료되거나 비정상적으로 동작.
    • 네트워크 연결 문제로 응답이 중단.
    • 대용량 데이터를 스트리밍할 때 메모리 부족 등으로 응답이 중단.

(2) HTTP 헤더 문제

  • 서버에서 HTTP 응답 헤더를 잘못 설정한 경우.
    • Transfer-Encoding: chunked 헤더가 누락되거나 잘못 설정.
    • 헤더와 실제 데이터가 일치하지 않는 경우(예: 데이터 크기 오류).

(3) 프록시 서버 문제

  • 클라이언트와 서버 사이의 프록시가 응답을 제대로 처리하지 못했을 때 발생.
    • 프록시가 청크를 분리하거나 잘못된 데이터로 변환.
    • 연결이 중간에 끊어지거나 타임아웃 발생.

(4) 브라우저 문제

  • 클라이언트(웹 브라우저)가 청크 데이터를 제대로 처리하지 못하거나 오류를 발생시키는 경우.

(5) 네트워크 환경 문제

  • 클라이언트와 서버 간 네트워크 연결이 불안정하거나 패킷 손실이 발생하는 경우.

3. 해결 방법

(1) 서버 측 점검

  1. 서버 로그 확인:
    • 서버에서 요청을 처리하는 과정에 오류가 발생했는지 확인.
    • 예: 메모리 부족, 프로세스 중단, 네트워크 타임아웃.
  2. 응답 헤더 설정 확인:
    • Transfer-Encoding: chunked가 올바르게 설정되었는지 확인.
    • 정적 파일이나 대용량 데이터를 전송할 경우, 콘텐츠 크기(Content-Length)를 명시하거나 청크 전송 방식이 제대로 작동하는지 확인.
  3. 마지막 청크 전송 확인:
    • 전송 종료 시 0 크기의 청크가 제대로 추가되고 전송되었는지 확인.
  4. 타임아웃 설정 확인:
    • 서버가 긴 요청을 처리하는 동안 연결이 끊어지지 않도록 타임아웃 설정을 늘림.
    • 예: Nginx, Apache, Node.js와 같은 웹 서버에서 타임아웃 설정을 확인.

(2) 클라이언트 측 해결

  1. 네트워크 연결 점검:
    • 네트워크 연결 상태를 확인하고, 불안정한 네트워크 환경에서 테스트를 반복.
  2. 브라우저 캐시 초기화:
    • 캐시된 데이터가 문제가 될 수 있으므로 브라우저 캐시를 지우고 다시 시도.
  3. 다른 브라우저 사용:
    • 특정 브라우저에서 발생할 수 있는 문제이므로 다른 브라우저에서 테스트.
  4. 프록시 설정 점검:
    • 프록시 서버가 응답을 처리하지 못할 수 있으므로 프록시 설정을 변경하거나 사용하지 않도록 설정.

(3) 프록시 및 로드 밸런서 점검

  • 프록시 서버가 데이터를 손상시키는지 확인.
  • 로드 밸런서가 클라이언트와 서버 간의 연결을 유지하고 있는지 확인.

4. 예방 방법

  • 안정적인 네트워크 환경 유지.
  • 타임아웃 설정 조정.
  • 서버에서 데이터 전송 완료를 보장하도록 철저히 테스트.
  • 대용량 데이터를 전송할 때는 데이터 스트리밍 방식을 최적화.

'SpringBoot > Error' 카테고리의 다른 글

페이징 기능이슈  (0) 2025.01.07
IntelliJ Console 창 문자 깨짐 현상  (0) 2024.05.30

쿼리 수정전

<!--페이징을 위한 카운트 쿼리 수정전-->
    
    <select id="selectTotalProductCount" parameterType="map" resultType="int">
    SELECT COUNT(*) 
    FROM PRODUCT P
    LEFT JOIN
    CATEGORY C ON P.category_seq = C.category_seq
    WHERE 1=1
        <!-- 카테고리 필터 -->
        <if test="category_seq != 0">
            AND P.category_seq = #{category_seq}
        </if>
        
        <!-- 검색어 필터 -->
        <if test="query != null and query != ''">
    AND    (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
        </if>
        
        <!-- 브랜드 필터 -->
        <if test="product_brand != null and product_brand != ''">
            AND P.product_brand = #{product_brand}
        </if>
        
        <!-- 가격대 필터 -->
        <if test="priceRange != null and priceRange != ''">
            AND (
                #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
                OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
                OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
                OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
                OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
                OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
                OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
                OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
            )
        </if>
</select>

쿼리 수정후

 


<if test="query != null and query != ''">
    AND   (  (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
)
</if>

    <!-- 가격대 필터 -->
    <if test="priceRange != null">
        AND (
            #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
            OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
            OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
            OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
            OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
            OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
            OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
            OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
        )
    </if>
   
    <select id="selectTotalProductCount" parameterType="map" resultType="int">
 SELECT COUNT(*) 
    FROM PRODUCT P
    LEFT JOIN
    CATEGORY C ON P.category_seq = C.category_seq
    WHERE 1=1
        <!-- 카테고리 필터 -->
        <if test="category_seq != 0 and category_seq != null">
            AND P.category_seq = #{category_seq}
        </if>
        
        <!-- 검색어 필터 -->
        <if test="query != null and query != ''">
    AND  (  (P.product_serial ILIKE '%' || #{query} || '%')
        OR (P.product_name ILIKE '%' || #{query} || '%')
        OR (P.product_brand ILIKE '%' || #{query} || '%')
        OR (P.product_height ILIKE '%' || #{query} || '%')
        OR (P.product_weight ILIKE '%' || #{query} || '%')
        OR (P.product_wh ILIKE '%' || #{query} || '%')
        OR (P.product_color ILIKE '%' || #{query} || '%')
        OR (P.product_features ILIKE '%' || #{query} || '%')
        OR (C.category_name ILIKE '%' || #{query} || '%')
        )</if>
        
        <!-- 브랜드 필터 -->
        <if test="product_brand != null and product_brand != ''">
            AND P.product_brand = #{product_brand}
        </if>
        
       <!-- 가격대 필터 -->
    <if test="priceRange != null">
        AND (
            #{priceRange} LIKE '%below10k%' AND P.product_pay &lt; 10000
            OR #{priceRange} LIKE '%10kTo20k%' AND P.product_pay BETWEEN 10001 AND 20000
            OR #{priceRange} LIKE '%20kTo30k%' AND P.product_pay BETWEEN 20001 AND 30000
            OR #{priceRange} LIKE '%30kTo40k%' AND P.product_pay BETWEEN 30001 AND 40000
            OR #{priceRange} LIKE '%40kTo50k%' AND P.product_pay BETWEEN 40001 AND 50000
            OR #{priceRange} LIKE '%50kTo70k%' AND P.product_pay BETWEEN 50001 AND 70000
            OR #{priceRange} LIKE '%80kTo100k%' AND P.product_pay BETWEEN 80001 AND 100000
            OR #{priceRange} LIKE '%above100k%' AND P.product_pay > 100000
        )
    </if>
</select>
</mapper>

 

최초 기능 설계시 

검색기능과 필터/정렬 기능을 각각 분리하여 SQL 문을설계 진행 -> 기능을 구현후 테스트 진행시 결과조건은 동일하고 

category_seq 와 query(검색어) 유무에 따른 결과 분리 

 

 

  • currentPage:
    • 설명: param.page가 비어 있으면(예: 첫 진입 시), 기본값으로 1을 설정합니다.
    • 결과: 현재 페이지 번호.
      <c:set var="currentPage" value="${empty param.page ? 1 : param.page}" />
       
  • totalPages:
    • 설명: 전체 페이지 수를 totalPages 변수에 저장합니다. 서버에서 전달된 값을 사용.
       
      <c:set var="totalPages" value="${totalPages}" />

이전 버튼

<li class="page-item ${currentPage == 1 ? 'disabled' : ''}">
    <a class="page-link mx-1" href="javascript:void(0);" aria-label="Previous" data-page="${currentPage > 1 ? currentPage - 1 : 1}">
        <i class="feather-icon icon-chevron-left"></i>
    </a>
</li>
 
  • 클래스 계산:
    • currentPage == 1이면 "disabled" 클래스를 추가해 비활성화.
  • 데이터 속성 계산:
    • data-page: currentPage가 1보다 크면 currentPage - 1(이전 페이지)을 설정.
      그렇지 않으면 1을 유지.

페이지 번호 계산

<c:forEach var="i" begin="${currentPage - 2 > 1 ? currentPage - 2 : 1}" end="${currentPage + 2 < totalPages ? currentPage + 2 : totalPages}">
    <li class="page-item ${currentPage == i ? 'active' : ''}">
        <a class="page-link mx-1" href="javascript:void(0);" data-page="${i}">${i}</a>
    </li>
</c:forEach>
  • begin 시작 값:
    • 설명: 현재 페이지에서 왼쪽으로 최대 2개 표시.
      • 만약 (currentPage - 2)가 1보다 작으면 1로 시작.
        ${currentPage - 2 > 1 ? currentPage - 2 : 1}
  • end 종료 값:
    • 설명: 현재 페이지에서 오른쪽으로 최대 2개 표시.
      • 만약 (currentPage + 2)가 totalPages보다 크면 마지막 페이지(totalPages)를 표시
${currentPage + 2 < totalPages ? currentPage + 2 : totalPages}

 

  • 활성화 클래스:
    • 현재 페이지(currentPage)와 반복 변수(i)가 같으면 active 클래스를 추가해 시각적으로 강조
class="${currentPage == i ? 'active' : ''}"

 '...' 표시

<li class="page-item ${currentPage + 2 < totalPages ? '' : 'd-none'}">
    <a class="page-link mx-1" href="#">...</a>
</li>
 
  • currentPage + 2 < totalPages이면 ... 표시.
    (즉, 현재 페이지 기준으로 오른쪽 2칸 이후에 더 많은 페이지가 있을 때만 표시.)

마지막 페이지 버튼

<li class="page-item ${currentPage == totalPages ? 'disabled' : ''}">
    <a class="page-link mx-1" href="javascript:void(0);" aria-label="Next" data-page="${currentPage < totalPages ? currentPage + 1 : totalPages}">
        <i class="feather-icon icon-chevron-right"></i>
    </a>
</li>

 

  • 클래스 계산:
    • currentPage == totalPages이면 "disabled" 클래스를 추가해 비활성화.
  • 데이터 속성 계산:
    • data-page: currentPage가 totalPages보다 작으면 currentPage + 1(다음 페이지)을 설정.
      그렇지 않으면 마지막 페이지(totalPages)를 유지.

 

1. 금융 분야 리뷰

금융

  • 금융은 자금의 조달과 운용을 다루는 분야
  • 개인, 기업, 정부가 자금을 모으고 사용하며, 투자, 대출, 보험, 자산 관리 등의 활동을 포함

금융 공학

  • 금융 문제를 해결하기 위해 수학, 통계, 경제학, 컴퓨터 과학 등의 방법을 사용하는 학문으로, 파생상품 가격 설정, 리스크 관리, 투자 전략 개발 등에 활용

금융기관

  • 금융기관은 자금의 중개 역할
  • 은행, 보험사, 증권사 등
  • 예금, 대출, 투자, 보험 등의 서비스

2. 주요 금융 자산

주식

  • 기업의 소유권을 나타내는 증권
  • 주식을 보유한 투자자는 해당 기업의 지분을 갖게 됨
  • 주식 시장에서 매매, 배당금과 자본 이익을 통해 수익
  • 효율적 시장 가설(EMH): 전체 투자자에게 공개된 정보가 주가에 즉시 반영되어 주식 시장이 항상 공정하고 효율적으로 작동. 주가는 이미 모든 정보를 반영하며, 지속적 추가 수익은 어렵다고 가정.

채권

  • 정부나 기업이 자금을 조달하기 위해 발행하는 부채 증서
  • 투자자는 이자 수익, 만기 시 발행자는 원금을 상환
  • 안전한 투자 수단, 이자율, 신용 등급, 만기 기간에 따른 가치
  • 신용평가: 차입자의 채무 상환 능력을 평가, 신용 등급 부여. 신용 등급에 따라 대출 조건 등이 영향

파생상품

  • 기초 자산의 가치에 기반한 금융 계약
  • 선물, 옵션, 스왑 등이 포함
  • 파생상품을 통해 리스크를 관리하거나 투기
  • 가격은 기초 자산의 가격 변동에 따라 결정
  • ABS: 자산유동화증권으로 담보 자산에서 발생하는 현금 흐름을 기초로 발행된 증권. 주택담보대출, 자동차대출, 신용카드 채권 등이 있으며, 이 증권을 통해 다양한 자산의 현금 흐름에 투자하며, 발행자는 자금을 조달할 수 있음
  • 리스크 관리: 위험을 식별, 평가한 후, 최소화하기 위한 전략을 수립. 예를 들어 분산 투자, 헤징, 보험, 내부 통제 등

3. 수익율

수익율

  • 수익률: 투자 자산의 초기 가치 대비 변화율, 퍼센트로 표현
  • 로그수익률: 자연 로그를 이용한 수익률, 시간의 흐름에 따른 연속 복리 효과를 반영, log(p(t)/p(t-1))로 표현하며 정규분포를 가정한 금융 모델에 유용, 로그수익률은 서로 더할 수 있어, 여러 기간 수익률 계산에 활용, 상승과 하락 변동이 대칭적으로 나타나서 비대칭성이 줄고 분석이 용이함

복리와 현재 가치

  • 원금에 이자가 붙고 다시 그 이자에 이자가 붙는 방식
  • 시간에 따라 자산이 기하급수적으로 증가하는 효과
  • 현재 가치: 미래의 금액을 현재 시점에서 평가한 가치. 투자 결정과 재무 계획에서 중요한 개념. 할인율을 사용

포트폴리오

  • 다양한 자산의 집합, 분산 투자와 리스크 관리를 통해 수익 극대화 및 손실 최소화 목표
  • 주식, 채권, 현금, 부동산, 파생상품 등 다양한 자산으로 구성
  • 투자 목표, 리스크 허용도, 투자 기간에 따라 구성비율 변화
  • 자산 배분, 리밸런싱, 시장 변화에 대한 대응 등

CAPM

  • CAPM(자본자산가격결정모형)은 자산의 기대수익률을 시장 위험과 무위험 수익률을 기반으로 계산하는 모델
  • E(R(i)) = R(f) + b(i)(E(R(m))-R(f))
  • 자산i의 기대수익율 = 무위험수익율+자산i의 beta X시장위험프리미엄
  • 자산의 리스크와 기대수익율 간 관계, 포트폴리오 구성 및 평가에 사용

 

 

 

ChatController

@Controller
public class ChatController {

	@GetMapping("/chat.do")
	public String chat() {
		return "chat/chatting";
	}
}

 

WebSocketConfig

@Component
public class WebSocketConfig {

		@Bean
		public ServerEndpointExporter serverEndpointExporter() {
			return new ServerEndpointExporter();
		}
}

 

WebSocketServer

//@Service  ...필수아님
//@Service 어노테이션은 해당 클래스를 관리하며,웹 소켓 연결과 관련된 라이프사이클 이벤트를 처리하도록 하는데 필수적인 역할

//웹소켓을 통한 실시간 양방향 통신을 가능하게 하는 서버 사이드의 연결점
@ServerEndpoint("/chatserver") 
public class WebSocketServer {

	// 현재 채팅 서버에 접속한 클라이언트(WebSocket Session) 목록
	private static List<Session> list = new ArrayList<Session>();
	
	@OnOpen //웹 소켓이 연결되면 호출되는 이벤트
	public void handleOpen(Session session) {
		list.add(session); // 접속자 관리		 
	}
	@OnClose
	// 웹 소켓이 닫히면 호출되는 이벤트
	public void handleClose(Session session) {
	    list.remove(session);
	}
	@OnError
	//웹 소켓 에러가 나면 발생하는 이벤트
	public void handleError(Throwable t) {

	}

	@OnMessage //클라이언트에서 서버 측으로 메시지를 보내면 호출되는 이벤트
	public void handleMessage(String msg, Session session) {
		// 로그인할 때: 1#유저명
		// 대화 할 때: 2유저명#메세지
		int index = msg.indexOf("#", 2);
		String no = msg.substring(0, 1);
		String user = msg.substring(2, index);
		String txt = msg.substring(index + 1);
		if (no.equals("1")) {
			// 누군가 접속 > 1#아무개
			for (Session s : list) {
				if (s != session) { // 현재 접속자가 아닌 나머지 사람들
					try {
						s.getBasicRemote().sendText("1#" + user + "#" +txt);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		} else if (no.equals("2")) {
			// 누군가 메세지를 전송
			for (Session s : list) {
				if (s != session) { // 현재 접속자가 아닌 나머지 사람들
					try {
						s.getBasicRemote().sendText("2#" + user + ":" + txt);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	
}

'Spring' 카테고리의 다른 글

Spring Swagger  (0) 2025.02.10
Spring Security  (0) 2025.02.10
WebSocket  (0) 2024.12.05
Controller 와 Restcontroller  (0) 2024.11.28
Spring - AutoWired  (0) 2024.11.27

WebSocketHTML5에서 제공하는 양방향 통신 프로토콜 클라이언트와 서버 간에 **지속적인 연결(persistent connection)**을 유지하면서, 양방향으로 데이터를 실시간으로 주고받을 수 있도록 설계되었습니다. WebSocket은 HTTP와 함께 사용되지만, HTTP와는 다른 독립적인 프로토콜

주요 특징

  1. 양방향 통신 (Full-Duplex):
    • 클라이언트와 서버가 서로 동시에 데이터를 주고받을 수 있습니다.
    • 요청-응답 패턴이 아니라, 필요할 때 자유롭게 메시지를 주고받음.
  2. 지속적인 연결 유지:
    • 클라이언트와 서버가 연결을 맺은 이후에는 지속적으로 연결을 유지합니다.
    • 새로 요청을 보내거나 연결을 다시 설정하지 않아도 실시간 데이터를 주고받을 수 있습니다.
  3. 낮은 오버헤드:
    • HTTP는 요청마다 헤더를 전송해야 하지만, WebSocket은 초기 핸드셰이크 이후 추가적인 헤더 없이 데이터를 주고받습니다.
    • 이를 통해 대역폭 절약지연 시간 감소가 가능합니다.
  4. 이벤트 기반 통신:
    • 데이터가 발생할 때마다 실시간으로 전달 가능.
    • 특히 채팅 애플리케이션, 실시간 알림, 주식 데이터와 같은 실시간성이 중요한 애플리케이션에서 유용합니다.

동작 원리

  1. 핸드셰이크(Handshake):
    • WebSocket은 처음에는 HTTP 프로토콜을 사용하여 연결을 시작합니다.
    • 클라이언트가 서버에 WebSocket 연결을 요청하며, 서버가 이를 승인하면 WebSocket 연결이 성립됩니다.
      1. 지속적인 데이터 통신:
        • 핸드셰이크 이후에는 WebSocket 프로토콜로 전환되어 양방향 데이터 통신이 이루어집니다.
        • 데이터는 프레임(Frame) 단위로 주고받습니다.
      2. 연결 종료:
        • 클라이언트나 서버 중 하나가 명시적으로 연결을 종료하거나 네트워크 문제로 연결이 끊어질 수 있습니다.
    • WebSocket의 구조
      • Opcode: 메시지 타입 (텍스트, 바이너리 등)
      • Payload Length: 메시지 길이
      • Masking Key: 데이터를 암호화하기 위한 키 (클라이언트->서버 데이터에만 적용)
      • Payload Data: 실제 데이터
    • WebSocket 메시지는 다음과 같은 구조로 프레임화됩니다:
    • WebSocket과 HTTP의 비교특징HTTPWebSocket
      통신 방식 요청-응답 (Request-Response) 양방향 (Full-Duplex)
      연결 방식 요청마다 새 연결 연결을 유지 (Persistent Connection)
      오버헤드 요청마다 헤더를 포함 초기 핸드셰이크 이후 헤더 없음
      실시간성 낮음 (폴링 또는 롱 폴링 필요) 높음
      주요 활용 사례 정적인 웹 페이지, API 실시간 채팅, 게임, 알림, IoT 데이터 스트리밍
      WebSocket의 주요 활용 사례
      1. 실시간 채팅:
        • 예: Slack, WhatsApp Web, Discord
      2. 실시간 알림 시스템:
        • 예: 이메일 알림, 쇼핑몰의 실시간 주문 상태 업데이트
      3. 온라인 게임:
        • 실시간 멀티플레이어 게임에서 유용
      4. 금융 데이터 스트리밍:
        • 주식 시장 데이터, 환율 정보 등
      5. IoT(사물 인터넷):
        • 장치 간 실시간 데이터 교환
      6. 화상 회의 및 스트리밍:
        • 예: WebRTC와의 통합
// WebSocket 서버에 연결
const socket = new WebSocket('ws://example.com/socket');

// 연결이 열렸을 때 실행
socket.onopen = () => {
    console.log('WebSocket 연결 성공!');
    socket.send('Hello, Server!');
};

// 메시지를 수신했을 때 실행
socket.onmessage = (event) => {
    console.log('서버로부터 메시지:', event.data);
};

// 연결이 닫혔을 때 실행
socket.onclose = () => {
    console.log('WebSocket 연결 종료');
};

// 오류가 발생했을 때 실행
socket.onerror = (error) => {
    console.error('WebSocket 오류:', error);
};
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (socket) => {
    console.log('클라이언트 연결됨');

    // 클라이언트로부터 메시지 수신
    socket.on('message', (message) => {
        console.log('클라이언트 메시지:', message);
        socket.send('Hello, Client!');
    });

    // 연결 종료
    socket.on('close', () => {
        console.log('클라이언트 연결 종료');
    });
});

console.log('WebSocket 서버가 8080 포트에서 실행 중');

WebSocket의 장단점

장점

  • 실시간 데이터 전송에 적합.
  • 낮은 오버헤드로 효율적 데이터 전송.
  • 서버 푸시(push) 기능 지원.
  • 요청-응답 구조보다 빠른 반응성.

단점

  • 네트워크 환경이 안정적이지 않은 경우 성능 저하.
  • 클라이언트와 서버 간 연결 상태를 관리해야 함.
  • HTTP보다 구현이 복잡.

'Spring' 카테고리의 다른 글

Spring Security  (0) 2025.02.10
WebSocket 예제  (0) 2024.12.05
Controller 와 Restcontroller  (0) 2024.11.28
Spring - AutoWired  (0) 2024.11.27
Spring기본  (0) 2024.11.26

JUnit은 Java 프로그래밍 언어를 위한 단위 테스트(Unit Testing) 프레임워크, 단위 테스트는 소프트웨어의 작은 단위(주로 클래스나 메서드)를 독립적으로 테스트하여 올바르게 동작하는지 확인하는 과정, JUnit은 테스트 자동화를 지원하며, 소프트웨어 개발 과정에서 테스트를 쉽고 체계적으로 실행할 수 있도록 도와줍니다.

주요 특징

  1. 애너테이션 기반 테스트:
    • JUnit은 애너테이션을 활용하여 테스트 메서드를 정의
      • @Test: 테스트 메서드를 나타냅니다.
      • @BeforeEach / @AfterEach: 각 테스트 전에/후에 실행되는 메서드.
      • @BeforeAll / @AfterAll: 모든 테스트 전에/후에 한 번 실행되는 메서드.
  2. 자동화된 테스트:
    • 개발자가 명시한 테스트를 자동으로 실행하고 결과를 확인함
  3. Assertions 지원:
    • assertEquals, assertTrue, assertFalse, assertThrows 등 다양한 메서드로 기대값과 실제값을 비교하여 테스트의 성공 여부를 판단
  4. 통합 도구와의 호환성:
    • JUnit은 Gradle, Maven과 같은 빌드 도구 및 Jenkins와 같은 CI/CD 도구와 통합되어 효과적인 테스트 및 배포 환경을 구축
  5. 테스트 스위트 구성:
    • 여러 테스트 클래스를 묶어서 테스트 스위트를 구성하여, 테스트를 그룹화하고 관리

JUnit의 버전

  • JUnit 4:
    • 애너테이션 기반의 구조가 도입되었으며 널리 사용
  • JUnit 5 (Jupiter):
    • 더 유연하고 확장성이 높은 새로운 아키텍처를 도입.
    • JUnit Platform, JUnit Jupiter, JUnit Vintage로 구성.
    • Java 8 이상의 기능을 활용하여 더 나은 기능 제공.

간단한 예제

1. Maven으로 JUnit 5 의존성 추가

 
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

2. 테스트 코드 작성

@Test
	public void arg4() {
		boardRepo.findByTitleEndingWithAndBnoBetweenOrderByBnoDesc("7", 500L, 600L).forEach(board->{
			log.info(board.toString());
		});
	}

 

JUnit을 사용하는 이유

  1. 코드 품질 향상: 단위 테스트를 통해 잠재적인 버그를 사전에 발견.
  2. 빠른 피드백 루프: 개발 단계에서 코드 수정이 필요한 부분을 빠르게 확인.
  3. 유지보수 용이성: 코드 변경 시 기존 기능이 깨지지 않음을 보장.
  4. 자동화된 테스트: 반복적인 수작업 테스트를 자동화하여 생산성 향상.

'Java' 카테고리의 다른 글

LOG4J  (0) 2024.12.03
JDBC-Controller  (0) 2024.10.25
JDBC-DAO  (0) 2024.10.25
JDBC-DTO  (0) 2024.10.25
JDBC-View  (0) 2024.10.25

Log4j는 Apache Software Foundation에서 제공하는 Java 기반의 로깅 라이브러리입니다. 애플리케이션의 실행 상태를 기록하는 데 사용되며, 디버깅, 오류 분석, 성능 모니터링 등에 유용합니다.

Log4j는 다음과 같은 특징을 가집니다:

  1. 유연한 로깅 레벨: 로그의 심각도에 따라 다양한 레벨(TRACE, DEBUG, INFO, WARN, ERROR, FATAL)을 제공합니다.
  2. 다양한 출력 대상: 로그를 파일, 콘솔, 데이터베이스, 원격 서버 등 다양한 곳에 출력할 수 있습니다.
  3. 구성 파일 지원: XML, JSON 또는 .properties 파일로 설정을 관리할 수 있습니다.
  4. 성능 최적화: 비동기 로깅 및 다양한 최적화 기능 제공.

Log4j의 주요 구성 요소

Log4j는 크게 Logger, Appender, Layout 세 가지 요소로 구성됩니다:

  1. Logger: 로그 메시지를 생성하고 로깅 레벨을 제어하는 역할.
  2. Appender: 로그 메시지의 출력 대상을 결정하는 역할 (예: 콘솔, 파일).
  3. Layout: 로그 메시지의 형식을 지정하는 역할 (예: 날짜, 로그 레벨, 메시지).

Log4j의 로깅 레벨

로그 레벨은 로그의 중요도에 따라 설정됩니다. 각 레벨은 아래와 같습니다:

로그 레벨설명

TRACE 가장 상세한 로그로, 디버깅용 세부 정보.
DEBUG 개발 단계에서 유용한 디버깅 정보.
INFO 일반적인 정보 메시지.
WARN 잠재적인 문제를 경고하는 메시지.
ERROR 오류가 발생했지만 애플리케이션이 계속 실행 가능.
FATAL 심각한 오류로 인해 애플리케이션이 종료될 수 있음.

Log4j 설정 파일 예제

(1) log4j.properties 예제

properties
코드 복사
# Root logger 설정
log4j.rootLogger=DEBUG, console, file

# Console 출력 설정
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n

# File 출력 설정
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n
 

(2) log4j.xml 예제

xml
 
<configuration>
  <appender name="Console" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n"/>
    </layout>
  </appender>

  <appender name="File" class="org.apache.log4j.FileAppender">
    <param name="File" value="app.log"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n"/>
    </layout>
  </appender>

  <root>
    <priority value="DEBUG"/>
    <appender-ref ref="Console"/>
    <appender-ref ref="File"/>
  </root>
</configuration>

Log4j 사용법

1. Logger 생성

Log4j를 사용하려면 Logger 객체를 생성합니다:

java
import org.apache.log4j.Logger;

public class Log4jExample {
    private static final Logger logger = Logger.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.trace("This is a TRACE message");
        logger.debug("This is a DEBUG message");
        logger.info("This is an INFO message");
        logger.warn("This is a WARN message");
        logger.error("This is an ERROR message");
        logger.fatal("This is a FATAL message");
    }
}

2. 주요 메서드

메서드설명

logger.trace(String msg) TRACE 레벨의 로그 메시지를 출력합니다.
logger.debug(String msg) DEBUG 레벨의 로그 메시지를 출력합니다.
logger.info(String msg) INFO 레벨의 로그 메시지를 출력합니다.
logger.warn(String msg) WARN 레벨의 로그 메시지를 출력합니다.
logger.error(String msg) ERROR 레벨의 로그 메시지를 출력합니다.
logger.fatal(String msg) FATAL 레벨의 로그 메시지를 출력합니다.
logger.isDebugEnabled() DEBUG 레벨이 활성화되어 있는지 확인합니다.
logger.isInfoEnabled() INFO 레벨이 활성화되어 있는지 확인합니다.

Log4j와 Log4j 2

  • Log4j 1.x: 위에서 설명한 버전으로 오래된 시스템에서 여전히 사용됩니다.
  • Log4j 2.x: Log4j 1.x의 성능 및 보안 문제를 개선한 최신 버전으로, 비동기 로깅과 동적 로깅 구성이 가능합니다. Log4j 1.x는 더 이상 유지보수되지 않으므로 가능하면 Log4j 2를 사용하는 것이 권장됩니다.

Log4j 관련 보안 이슈

Log4j 2에서 Log4Shell(CVE-2021-44228) 취약점이 발견되었습니다. 공격자가 악의적인 데이터를 이용해 원격 코드 실행(RCE)을 할 수 있는 문제입니다.
해결 방법:

  • Log4j를 2.16.0 이상으로 업그레이드하세요.
  • JNDI 기능을 비활성화하거나 보안 패치를 적용합니다.

'Java' 카테고리의 다른 글

Junit  (0) 2024.12.05
JDBC-Controller  (0) 2024.10.25
JDBC-DAO  (0) 2024.10.25
JDBC-DTO  (0) 2024.10.25
JDBC-View  (0) 2024.10.25

1. SELECT 관련 메서드

    • 결과가 없으면 null을 반환.
    • 결과가 여러 개라면 TooManyResultsException이 발생.<T> T selectOne(String statement)
      지정된 SQL ID를 실행하고 하나의 결과를 반환합니다.
  • <E> List<E> selectList(String statement)
    지정된 SQL ID를 실행하고 여러 개의 결과를 List로 반환합니다.
  • <K, V> Map<K, V> selectMap(String statement, String mapKey)
  • 결과를 Map 형태로 반환하며, mapKey를 기준으로 Map의 키를 설정합니다.
User user = sqlSession.selectOne("UserMapper.selectUserById", 1);
List<User> users = sqlSession.selectList("UserMapper.selectAllUsers");
Map<Integer, User> userMap = sqlSession.selectMap("UserMapper.selectAllUsers", "id");

2. INSERT 관련 메서드

  • int insert(String statement)
    지정된 SQL ID를 실행하여 삽입 작업을 수행하고, 영향을 받은 행(row) 수를 반환합니다.
int rows = sqlSession.insert("UserMapper.insertUser", newUser);

3. UPDATE 관련 메서드

  • int update(String statement)
    지정된 SQL ID를 실행하여 갱신 작업을 수행하고, 영향을 받은 행(row) 수를 반환합니다.
     
int rows = sqlSession.update("UserMapper.updateUser", user);

4. DELETE 관련 메서드

  • int delete(String statement)
    지정된 SQL ID를 실행하여 삭제 작업을 수행하고, 영향을 받은 행(row) 수를 반환합니다
int rows = sqlSession.delete("UserMapper.deleteUserById", 1);

'WEB' 카테고리의 다른 글

SOP  (0) 2025.02.18
3계층 티어  (0) 2025.01.21
SQL Session Factory  (0) 2024.12.02
이미지 파일저장  (0) 2024.11.21
JSP INCLUDE  (0) 2024.11.21

+ Recent posts