<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sang_dosa.log</title>
        <link>https://velog.io/</link>
        <description>조용한 개발자</description>
        <lastBuildDate>Sat, 06 Sep 2025 07:41:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. sang_dosa.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sang_dosa" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Kubernetes] 클러스터의 이해와 k8s 구조 및 역할]]></title>
            <link>https://velog.io/@sang_dosa/Kubernetes-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-k8s-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%97%AD%ED%95%A0</link>
            <guid>https://velog.io/@sang_dosa/Kubernetes-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-k8s-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%97%AD%ED%95%A0</guid>
            <pubDate>Sat, 06 Sep 2025 07:41:11 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-앞서서">들어가기 앞서서..</h2>
<p>k8s에 대한 완전히 무지한 상황에서 업무에 투입이 되다 보니 이해력이 없이 무작정 가이드대로만 진행한 상황이다. 그렇기에 기본적인 서비스에 대한 이해, 구조 파악을 위해서 해당 내용을 작성합니다. </p>
<h1 id="클러스터">클러스터</h1>
<p>✅ 클러스터(Cluster)란?</p>
<p>여러 대의 컴퓨터(노드)를 하나로 묶어, 하나의 시스템처럼 동작하게 만든 집합체.</p>
<p>Kubernetes에서는 클러스터가 기본 단위.
이 클러스터 안에서 앱을 배포, 실행, 스케일링, 관리 가능</p>
</br>

<p>✅ 클러스터의 구조</p>
<pre><code>Kubernetes Cluster
├── Control Plane (1개 이상) -- 클러스터를 관리하고 조율하는 역할
│    ├── API Server
│    ├── Scheduler
│    └── Controller Manager
│
└── Worker Nodes (1개 이상) -- 실제 프로그램을 실행하는 컴퓨터들
     ├── kubelet
     ├── Container Runtime (예: containerd, Docker)
     └── Pods (앱 실행) -- 실제 실행되는 프로그램들
</code></pre><h1 id="kubernetesk8s">Kubernetes(k8s)</h1>
<p>✅ Kubernetes(k8s)란?
설정을 통해 클러스터 위에서 어떤 앱을 실행할지 어떻게 설정할지에 대한 것들을 자동으로 관리해주는 오케스트레이션</p>
<pre><code>        Kubernetes (k8s)
        ────────────────
        - 클러스터 상태 감시
        - 앱 자동 배포/복구
        - 스케일링 제어
        - 로드밸런싱

                ↓

       ┌────────────────────┐
       │   Kubernetes       │
       │     Cluster        │
       │                    │
       │  ┌──────────────┐  │
       │  │ Control Plane│  │ ← 매니저
       │  └──────────────┘  │
       │  ┌──────────────┐  │
       │  │ Worker Node 1│  │ ← 일꾼
       │  │   └─ Pod     │  │
       │  └──────────────┘  │
       │  ┌──────────────┐  │
       │  │ Worker Node 2│  │ ← 일꾼
       │  │   └─ Pod     │  │
       │  └──────────────┘  │
       └────────────────────┘
</code></pre><h2 id="실습">실습</h2>
<p>Kind를 활용하여 cluster를 생성하고 그 클러스에 nginx 배포</p>
<pre><code>실습 구조
┌────────────────────────────────────────────┐
│                Local Machine               │
│                                            │
│  ┌──────────────────────────────────────┐  │
│  │              kind Cluster            │  │
│  │     (Docker container-based)         │  │
│  │                                      │  │
│  │  ┌────────────┐     ┌────────────┐   │  │
│  │  │  Control   │     │   Worker   │   │  │
│  │  │   Plane    │     │   Node     │   │  │
│  │  └────┬───────┘     └────┬───────┘   │  │
│  │       │                 │            │  │
│  │       │   ┌─────────────────────┐    │  │
│  │       └──▶│      nginx Pod      │    │  │
│  │           │    (container)      │    │  │
│  │           └─────────────────────┘    │  │
│  │                                      │  │
│  └──────────────────────────────────────┘  │
└────────────────────────────────────────────┘
</code></pre><h3 id="필요한-설치-프로그램">필요한 설치 프로그램</h3>
<p>Docker, kind, kubectl </p>
<h4 id="kind-클러스터-생성">kind 클러스터 생성</h4>
<pre><code>* 생성
kind create cluster --name my-cluster

* 확인
kubectl cluster-info --context kind-my-cluster
kubectl get nodes
</code></pre><p><img src="https://velog.velcdn.com/images/sang_dosa/post/52a52687-f696-4838-b749-f47bf207658a/image.png" alt="이미지"></p>
<h4 id="클러스터에-nginx-배포">클러스터에 nginx 배포</h4>
<ol>
<li>delpoyment.yaml 작성</li>
</ol>
<pre><code># nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
</code></pre><ol start="2">
<li>배포 실행<pre><code>실행
kubectl apply -f nginx-deployment.yaml
</code></pre></li>
</ol>
<p>확인
kubectl get deployments
kubectl get pods</p>
<pre><code>![](https://velog.velcdn.com/images/sang_dosa/post/f9a8c3d2-501b-440f-a5bd-9dc0e0ccaf66/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 이중 API 호출을 위한 Cache 처리]]></title>
            <link>https://velog.io/@sang_dosa/Spring-%EC%9D%B4%EC%A4%91-API-%ED%98%B8%EC%B6%9C%EC%9D%84-%EC%9C%84%ED%95%9C-Cache-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@sang_dosa/Spring-%EC%9D%B4%EC%A4%91-API-%ED%98%B8%EC%B6%9C%EC%9D%84-%EC%9C%84%ED%95%9C-Cache-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 22 Jun 2025 11:50:02 GMT</pubDate>
            <description><![CDATA[<h2 id="concurrenthashmap을-통한-cache-처리">ConcurrentHashMap을 통한 Cache 처리</h2>
<ul>
<li>ConcurrentHashMap을 활용해서 Spring 내부에서 서버 메모리 기반의 임시 저장소로 개발</li>
<li>API 실행 시 임시 키 형태의 데이터 저장 및 서비스 마무리 되면 finally에서 key 형태 데이터 삭제</li>
<li>ConcurrentHashMap은 자바 객체이기 때문에, 프로세스가 살아 있는 동안 메모리에 상주</li>
<li>애플리케이션이 실행되고 있는 서버의 메인 메모리(RAM) 에 존재</li>
<li>서버 1대에서만 유효하며, 애플리케이션 종료 시 데이터는 모두 삭제</li>
</ul>
<h3 id="예시">예시</h3>
<ul>
<li><pre><code>import org.springframework.stereotype.Service;
</code></pre></li>
</ul>
<p>import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;</p>
<p>@Service
public class SimpleRequestLockService {</p>
<pre><code>// key: 사용자ID + API명, value: 잠금 만료 시간(Unix epoch millis)
private final Map&lt;String, Long&gt; lockMap = new ConcurrentHashMap&lt;&gt;();

private final long lockDurationMillis = 5000; // 5초 동안 요청 막기

public synchronized boolean tryLock(String key) {
    Long expireTime = lockMap.get(key);
    long now = Instant.now().toEpochMilli();

    if (expireTime == null || expireTime &lt; now) {
        // 잠금 없거나 만료됨 → 새로 잠금 설정
        lockMap.put(key, now + lockDurationMillis);
        return true;
    }

    // 아직 잠금 유효 → 중복 요청임
    return false;
}

public synchronized void releaseLock(String key) {
    lockMap.remove(key);
}</code></pre><p>}</p>
<pre><code>
## Guava Cache
- in-memory 캐시 (JVM 내에 존재)
- TTL 지원 (자동 만료 시간 설정 가능)
- 동시성(Thread-safe) 기본 지원
- LRU(Least Recently Used) 제거 정책 지원
- Spring의 @Cacheable 같은 복잡한 설정 없이 사용 가능


### 예시</code></pre><p>import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Service;</p>
<p>import java.util.concurrent.TimeUnit;</p>
<p>@Service
public class GuavaLockService {</p>
<pre><code>// TTL 60초
private final Cache&lt;String, Boolean&gt; lockCache = CacheBuilder.newBuilder()
        .expireAfterWrite(60, TimeUnit.SECONDS)
        .build();

public boolean tryLock(String key) {
    // 이미 존재하면 중복 요청
    Boolean prev = lockCache.asMap().putIfAbsent(key, true);
    return prev == null;
}

public void releaseLock(String key) {
    lockCache.invalidate(key);
}</code></pre><p>}
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] Could not initialize class sun.awt.X11FontManager]]></title>
            <link>https://velog.io/@sang_dosa/Error-Could-not-initialize-class-sun.awt.X11FontManager</link>
            <guid>https://velog.io/@sang_dosa/Error-Could-not-initialize-class-sun.awt.X11FontManager</guid>
            <pubDate>Sun, 22 Jun 2025 11:00:32 GMT</pubDate>
            <description><![CDATA[<h2 id="오류-내용">오류 내용</h2>
<ul>
<li><p>POI를 활용한 엑셀 생성 개발에서 아래 오류 발생</p>
<pre><code>Could not initialize class sun.awt.X11FontManager</code></pre></li>
<li><p>발생 주요 원인</p>
</li>
</ul>
<ol>
<li>Headless 환경에서 AWT 사용</li>
<li>libfontconfig 등 폰트 관련 리눅스 라이브러리가 없음</li>
<li>Java 버그/설정 문제
==&gt; POI 사용시 AWT를 사용하는데 해당 설정을 해주지 않아서 오류 발생</li>
</ol>
<h2 id="해결-방식">해결 방식</h2>
<ol>
<li>headless 설정 추가</li>
</ol>
<ul>
<li>실행: java.awt.headless=true 설정을 Docker, GitLab 설정 내에 추가</li>
<li>결과: 실제로 POD 내 설정 추가되지 않아 위 오류 지속적으로 발생</li>
</ul>
<ol start="2">
<li>폰트 및 패키지 설치</li>
</ol>
<ul>
<li>실행: Dockerfile 내에 아래 설치 처리 실행<pre><code># 필요 패키지 설치: 폰트, X11 관련
RUN apt-get update &amp;&amp; apt-get install -y \
  fontconfig \
  fonts-dejavu-core \
&amp;&amp; rm -rf /var/lib/apt/lists/*</code></pre></li>
<li>결과: 설치 실행이 되지 않아 지속적으로 오류 발생</li>
</ul>
<ol start="3">
<li>.deb 활용</li>
</ol>
<ul>
<li>실행: .deb 안에 있는 폰트 설치 파일을 통해서 POD 내에 폰트 설치<pre><code># 설치
RUN apt-get update &amp;&amp; apt-get install -y \
  fontconfig \
&amp;&amp; dpkg-deb -x /tmp/my-font.deb /usr/local/share/fonts/ \</code></pre></li>
<li>결과: 폰트 설치 및 오류 해결</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kubernetes] PVC]]></title>
            <link>https://velog.io/@sang_dosa/Kubernetes-PVC</link>
            <guid>https://velog.io/@sang_dosa/Kubernetes-PVC</guid>
            <pubDate>Sun, 22 Jun 2025 10:42:29 GMT</pubDate>
            <description><![CDATA[<h2 id="pvc">PVC</h2>
<p>PVC는 &quot;Persistent Volume Claim&quot;의 약자.</p>
<p>쿠버네티스에서 <strong>컨테이너가 데이터를 저장할 공간(디스크)을 요청</strong>할 때 사용하는 개념</p>
<p>컨테이너는 기본적으로 휘발성이라, 재시작하면 내부 데이터가 날아가는데, PVC를 쓰면 데이터가 지속적으로 유지</p>
<pre><code>예시

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi</code></pre><ul>
<li>관련 용어</li>
</ul>
<ol>
<li>PV: 클러스터 안에 미리 정의된 &quot;스토리지 공간&quot;</li>
<li>Pod: 쿠버네티스에서 돌아가는 실제 컨테이너 묶음</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Docker] Docker 에 대해]]></title>
            <link>https://velog.io/@sang_dosa/Docker-%EC%97%90-%EB%8C%80%ED%95%B4</link>
            <guid>https://velog.io/@sang_dosa/Docker-%EC%97%90-%EB%8C%80%ED%95%B4</guid>
            <pubDate>Fri, 06 Jun 2025 08:48:21 GMT</pubDate>
            <description><![CDATA[<h2 id="도커란">도커란</h2>
<ul>
<li>리눅스os 환경 위에서 컨터이너라는 형태의 공간안에 특정 어플리케이션을 실행하는 것을 뜻함</li>
<li>어플리케이션은 컨테이너 안에서 os를 설치 하는 것이 아닌 host가 가지고 있는 os를 공유 하고 필요로 하는 라이브러리와 다른 파일들을 설치하여 사용</li>
</ul>
<h2 id="도커-설치">도커 설치</h2>
<pre><code>https://docs.docker.com/?_gl=1*lgqfzr*_gcl_au*MjA2NTg3OTMxMy4xNzQ5MTk4MDgw*_ga*NjY0MTkyNzQwLjE3NDkxOTgwODA.*_ga_XJWPQMJYHQ*czE3NDkxOTgwODAkbzEkZzEkdDE3NDkxOTgwODEkajU5JGwwJGgw</code></pre><h2 id="도커-실행-방식">도커 실행 방식</h2>
<ul>
<li>도커 허브 -&gt; pull -&gt; 도커 이미지 설치 -&gt; run -&gt; 이미지를 컨테이너로 생성</li>
</ul>
<h3 id="도커-명령어">도커 명령어</h3>
<ul>
<li>docker images : 도커 이미지 설치 리스트 노출</li>
<li>docker run [option] [이미지 이름] : 설치한 이미지를 컨테이너로 생성</li>
<li>docker ps : 실행중인 컨테이너 리스트 노출</li>
<li>docker stop [컨테이너 이름] : 실행중인 컨테이너 중지</li>
<li>docker start [컨테이너 이름] : 컨테이너 실행</li>
<li>docker log [컨테이너 이름] : 컨테이너 로그 확인</li>
<li>docker rm [컨테이너 이름] : 컨테이너 삭제</li>
<li>docker rmi [이미지 이름] : 이미지 삭제</li>
<li>docker exec [컨테이너 이름] [리눅스 명령어] : 컨테이너 내에 대한 명령어 처리</li>
<li>docker exec -it [컨테이너 이름] /bin/sh : 컨테이너 shell 파일에 접근해 명령어 처리 가능하도록 연결</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MongoDB] Aggregation]]></title>
            <link>https://velog.io/@sang_dosa/MongoDB-Aggregation</link>
            <guid>https://velog.io/@sang_dosa/MongoDB-Aggregation</guid>
            <pubDate>Sat, 01 Mar 2025 07:09:46 GMT</pubDate>
            <description><![CDATA[<p><strong>1. Aggregation의 기본 개념</strong>
Aggregation Framework는 MongoDB에서 데이터를 변형하고 집계할 수 있는 강력한 도구입니다. MongoDB의 Aggregation Pipeline을 사용하여 데이터를 처리할 수 있습니다. 이 파이프라인은 여러 단계의 변환 작업을 순차적으로 적용하여 최종 결과를 도출합니다.</p>
<p>1.1 Aggregation Pipeline
Aggregation은 여러 <strong>단계(Stage)</strong>로 이루어져 있으며, 각 단계는 데이터를 처리하고 변형하는 연산자입니다.
각 단계에서 데이터를 변형하거나 그룹화하고, 최종적으로 결과를 반환합니다.</p>
<p>1.2 Aggregation Stage
Aggregation Pipeline은 여러 단계로 구성됩니다. 각 단계에서 사용되는 주요 연산자는 다음과 같습니다:</p>
<pre><code>$match: 데이터를 필터링합니다.

$project: 필드를 변환하거나, 제거하거나, 새 필드를 추가합니다.

$group: 데이터를 그룹화하고 집계합니다.

$sort: 데이터를 정렬합니다.

$limit: 결과의 개수를 제한합니다.

$skip: 결과에서 특정 개수를 건너뜁니다.

$unwind: 배열을 분해하여 각 원소를 개별 문서로 만듭니다.

$lookup: 다른 컬렉션과 조인을 수행합니다.

$addFields: 필드를 추가하거나 업데이트합니다.

$count: 결과의 개수를 세어 반환합니다.

$sortByCount: 특정 필드로 결과를 집계하여 반환합니다.</code></pre><ol start="2">
<li>각 Aggregation Stage의 상세 설명</li>
</ol>
<pre><code>2.1 $match
목적: 데이터를 필터링하여 조건에 맞는 문서만 다음 단계로 전달합니다.
주요 사용법: 특정 필드가 특정 값인지, 또는 값의 범위가 일정 범위에 속하는지 등을 필터링할 때 사용됩니다.

2.2 $project
목적: 문서의 필드를 선택하거나, 제거하거나, 필드를 새로 생성할 수 있습니다.
주요 사용법: 필드를 포함하거나 제외하고, 필드를 변환하거나 이름을 바꿔서 출력할 때 사용됩니다.

2.3 $group
목적: 문서를 그룹화하고, 각 그룹에 대한 집계를 수행합니다.
주요 사용법: 특정 필드를 기준으로 그룹화하고, 그룹별 합계, 평균, 최소/최대 값 등을 구할 때 사용됩니다.

2.4 $sort
목적: 결과를 정렬합니다.
주요 사용법: 특정 필드를 기준으로 오름차순/내림차순 정렬을 합니다.

2.5 $limit
목적: 결과에서 반환할 문서의 개수를 제한합니다.
주요 사용법: 상위 N개의 결과를 추출할 때 사용됩니다.

2.6 $unwind
목적: 배열을 풀어서 배열 내의 각 요소를 개별 문서로 만듭니다.
주요 사용법: 배열 필드를 갖는 문서에서 각 배열 항목을 별도의 문서로 다루고 싶을 때 사용됩니다.

2.7 $lookup
목적: 다른 컬렉션과 조인합니다.
주요 사용법: 다른 컬렉션의 데이터를 결합하려고 할 때 사용됩니다.

2.8 $count
목적: 결과의 개수를 셉니다.
주요 사용법: 특정 조건을 만족하는 문서 수를 구할 때 사용됩니다.

2.9 $sortByCount
목적: 특정 필드에 대해 집계를 수행하고, 결과를 그 필드의 값으로 정렬합니다.
주요 사용법: 특정 필드의 값을 기준으로 문서를 집계하고 정렬할 때 사용됩니다.</code></pre><ol start="3">
<li>예시와 Spring Data MongoDB 적용</li>
</ol>
<p>3.1 $match 예시
목적: 특정 조건에 맞는 문서만 선택.</p>
<p>예시:</p>
<p>json</p>
<pre><code>db.orders.aggregate([
  { $match: { status: &quot;shipped&quot; } }
])</code></pre><p>Spring Data MongoDB 예시:</p>
<p>java</p>
<pre><code>Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.match(Criteria.where(&quot;status&quot;).is(&quot;shipped&quot;))
);

List&lt;Order&gt; orders = mongoTemplate.aggregate(aggregation, &quot;orders&quot;, Order.class).getMappedResults();</code></pre><p>유의 사항:</p>
<p>match는 조건을 만족하는 데이터만 추출하므로 성능에 중요한 영향을 미칠 수 있습니다. 필요한 조건만 필터링하여 사용해야 합니다.</p>
<p>3.2 $project 예시
목적: 필드 선택, 필드 이름 변경 또는 새 필드 생성.</p>
<p>예시:</p>
<p>json</p>
<pre><code>db.orders.aggregate([
  { $project: { orderId: 1, total: 1, status: 1, discountPrice: { $multiply: [&quot;$price&quot;, 0.9] } } }
])</code></pre><p>Spring Data MongoDB 예시:</p>
<p>java</p>
<pre><code>Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.project(&quot;orderId&quot;, &quot;total&quot;, &quot;status&quot;)
        .andExpression(&quot;price * 0.9&quot;).as(&quot;discountPrice&quot;)
);

List&lt;Order&gt; orders = mongoTemplate.aggregate(aggregation, &quot;orders&quot;, Order.class).getMappedResults();</code></pre><p>유의 사항:</p>
<p>project는 주로 필드를 선택하거나 새 필드를 생성하는 데 사용됩니다. 필드 이름을 변경할 때는 .as() 메서드를 사용해 새 이름을 지정합니다.</p>
<p>3.3 $group 예시
목적: 특정 필드를 기준으로 데이터를 그룹화하고 집계를 수행.</p>
<p>예시:</p>
<p>json</p>
<pre><code>db.orders.aggregate([
  { $group: { _id: &quot;$status&quot;, totalAmount: { $sum: &quot;$amount&quot; } } }
])</code></pre><p>Spring Data MongoDB 예시:</p>
<p>java</p>
<pre><code>Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.group(&quot;status&quot;)
        .sum(&quot;amount&quot;).as(&quot;totalAmount&quot;)
);


List&lt;GroupResult&gt; result = mongoTemplate.aggregate(aggregation, &quot;orders&quot;, GroupResult.class).getMappedResults();</code></pre><p>유의 사항:</p>
<p>group은 그룹화하려는 필드를 기준으로 집계가 이루어집니다. 집계된 결과는 _id 필드에 저장되므로, project 단계에서 _id를 다른 이름으로 변경해야 할 수 있습니다.</p>
<p>3.4 $sort 예시
목적: 결과를 특정 필드를 기준으로 정렬.</p>
<p>예시:</p>
<p>json</p>
<pre><code>db.orders.aggregate([
  { $sort: { totalAmount: -1 } }
])</code></pre><p>Spring Data MongoDB 예시:</p>
<p>java</p>
<pre><code>Aggregation aggregation = Aggregation.newAggregation(
    Aggregation.sort(Sort.by(Sort.Order.desc(&quot;totalAmount&quot;)))
);

List&lt;Order&gt; orders = mongoTemplate.aggregate(aggregation, &quot;orders&quot;, Order.class).getMappedResults();</code></pre><p>유의 사항:</p>
<p>sort는 성능에 영향을 미칠 수 있으므로 필요한 필드만 정렬하는 것이 좋습니다.</p>
<ol start="4">
<li>결과값 예시
예시: $group을 이용해 상태별 총액을 구하는 집계 예시</li>
</ol>
<p>json</p>
<pre><code>db.orders.aggregate([
  { $group: { _id: &quot;$status&quot;, totalAmount: { $sum: &quot;$amount&quot; } } },
  { $sort: { totalAmount: -1 } }
])</code></pre><p>결과값:</p>
<p>json</p>
<pre><code>[
  { &quot;_id&quot;: &quot;shipped&quot;, &quot;totalAmount&quot;: 5000 },
  { &quot;_id&quot;: &quot;pending&quot;, &quot;totalAmount&quot;: 3000 },
  { &quot;_id&quot;: &quot;delivered&quot;, &quot;totalAmount&quot;: 1000 }
]</code></pre><p>Spring Data MongoDB 예시 결과:</p>
<p>java</p>
<pre><code>List&lt;GroupResult&gt; result = mongoTemplate.aggregate(aggregation, &quot;orders&quot;, GroupResult.class).getMappedResults();</code></pre><p>GroupResult 클래스:</p>
<p>java</p>
<pre><code>public class GroupResult {
    private String id; // _id
    private double totalAmount;

    // getters and setters
}</code></pre><ol start="5">
<li>유의사항
성능 최적화: Aggregation 연산은 대규모 데이터셋에서 성능 저하를 일으킬 수 있습니다. match, limit, skip 등을 적절히 사용하여 쿼리 성능을 최적화해야 합니다.
메모리 사용량: 복잡한 집계 연산은 메모리 사용량을 급격히 증가시킬 수 있습니다. Aggregation의 결과가 많을 경우 allowDiskUse(true)를 사용하여 디스크 기반 처리를 할 수 있습니다.
인덱스 활용: match와 같은 필터링 단계는 가능한 한 인덱스를 활용하도록 설계해야 성능이 향상됩니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MongoDB] TimeSeries]]></title>
            <link>https://velog.io/@sang_dosa/TimeSerise</link>
            <guid>https://velog.io/@sang_dosa/TimeSerise</guid>
            <pubDate>Sat, 25 Jan 2025 05:31:37 GMT</pubDate>
            <description><![CDATA[<h2 id="mongodb">MongoDB</h2>
<p>NoSql을 지원하는 데이터 베이스
Bson 기반의 동적 스키마형 문서를 사용
데이터 타입까지 포함되어 이진형태로 인코딩해서 저장</p>
<h3 id="document">Document</h3>
<p>MongoDB의 기본 데이터 단위로 관계형 데이터베이스의 row와 유사
필드와 값 쌍으로 구성</p>
<h3 id="collection">Collection</h3>
<p>Document의 그룹이며 관계형 데이터베이스의 테이블과 유사
컬렉션 내의 Document들은 모두 다른 구조로 저장 가능</p>
<h3 id="crud">CRUD</h3>
<h4 id="create">create</h4>
<p>insertOne(), insertMany()</p>
<p>db.users.inserOne({
    &quot;field&quot; : &quot;value&quot;,
    &quot;field&quot; : &quot;value&quot;
    })</p>
<h4 id="read">read</h4>
<p>find(), findOne()
연산자: $eq, $gt, $lt, $regex </p>
<p>db.users.find({
    &quot;age&quot; : {&quot;$gt&quot;: 30}
    })</p>
<h4 id="update">update</h4>
<p>updateOne(), updateMany(), replaceOne()
연산자: $set, $inc, $push</p>
<p>db.users.updateOne(
    {&quot;_id&quot; : &quot;ObjectId(&quot;id&quot;)&quot;},
    {&quot;$set&quot; : {&quot;email&quot; : &quot;<a href="mailto:new.email@naver.com">new.email@naver.com</a>&quot;}}
    )</p>
<h4 id="delete">delete</h4>
<p>db.users.delete({
    &quot;_id&quot; : &quot;ObjectId(&quot;id&quot;)&quot;
    })</p>
<h3 id="장단점">장단점</h3>
<ol>
<li>장점
1 ) 고성능, 고가용성 및 쉬운 확장성을 제공
2 ) 이진 직렬화, 빠른 처리 속도, 다양한 데이터 유형 지원</li>
<li>단점
1 ) join을 미지원
2 ) 수정, 삭제에 대한 속도가 느림
3 ) 복잡한 트랜잭션을 요구하는 어플리케이션에 어울리지 않음</li>
</ol>
<h2 id="time-series">Time Series</h2>
<h3 id="개념">개념</h3>
<p>시계열 데이터를 기준으로 데이터 저장
MongoDB에서는 TimeSeries Collection을 사용하여 효율적으로 데이터를 저장</p>
<h3 id="생성">생성</h3>
<p>db.collection(&quot;collection_name&quot;{
  timeseries: {
      timeField : &quot;field_name&quot;, &lt;- 필수
    metaField : &quot;value&quot;, &lt;- 옵션
    &quot;field&quot; : &quot;value&quot; ... &lt;- 옵션
  }
})</p>
<h3 id="저장-방식">저장 방식</h3>
<p>TimeField, MetaField 단위로 버킷이 생성되고, 해당 범위 내로 데이터가 들어 올 경우 동일한 버킷에 저장</p>
<hr>
<p>참조
<a href="https://colevelup.tistory.com/45">https://colevelup.tistory.com/45</a>
<a href="https://it-creamstory.tistory.com/entry/MongoDB-60%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Time-Series-Collection-%EC%83%9D%EC%84%B1%EB%B6%80%ED%84%B0-%EC%A1%B0%ED%9A%8C%EA%B9%8C%EC%A7%80">https://it-creamstory.tistory.com/entry/MongoDB-60%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Time-Series-Collection-%EC%83%9D%EC%84%B1%EB%B6%80%ED%84%B0-%EC%A1%B0%ED%9A%8C%EA%B9%8C%EC%A7%80</a>
<a href="https://hello-world.kr/45">https://hello-world.kr/45</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개발] 웹 크롤링을 활용한 네이버 스토어 리뷰 가지고 오기 - 01 ]]></title>
            <link>https://velog.io/@sang_dosa/%EA%B0%9C%EB%B0%9C-%EC%9B%B9-%ED%81%AC%EB%A1%A4%EB%A7%81%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%A6%AC%EB%B7%B0-%EA%B0%80%EC%A7%80%EA%B3%A0-%EC%98%A4%EA%B8%B0-01</link>
            <guid>https://velog.io/@sang_dosa/%EA%B0%9C%EB%B0%9C-%EC%9B%B9-%ED%81%AC%EB%A1%A4%EB%A7%81%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%A6%AC%EB%B7%B0-%EA%B0%80%EC%A7%80%EA%B3%A0-%EC%98%A4%EA%B8%B0-01</guid>
            <pubDate>Fri, 11 Oct 2024 06:36:38 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-개요">프로젝트 개요</h2>
<ul>
<li>웹 크롤링을 활용</li>
<li>리뷰 데이터 저장 및 분석</li>
<li>가장 많이 사용된 단어 및 구매률, 여러 데이터를 노출하는 웹 사이트 개발</li>
</ul>
<h2 id="사용-기술-스택">사용 기술 스택</h2>
<ul>
<li>웹 크롤링<ul>
<li>python</li>
<li>BeautifulSoup</li>
<li>DB: mysql or sqlite</li>
</ul>
</li>
<li>정보 노출 사이트<ul>
<li>java 17</li>
<li>Spring boot 4.1</li>
<li>jpa</li>
<li>프론트: AI 서비스 활용 예정 (<a href="https://vercel.com/">https://vercel.com/</a>, <a href="https://uizard.io/">https://uizard.io/</a>) </li>
<li>DB: mysql or sqlite</li>
</ul>
</li>
</ul>
<blockquote>
<p>참고: <a href="https://velog.io/@onicle/%ED%81%AC%EB%A1%A4%EB%A7%81%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%8A%A4%EB%A7%88%ED%8A%B8%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%A6%AC%EB%B7%B0-%EC%88%98%EC%A7%91#2-5-%EC%88%98%EC%A7%91%ED%95%9C-%EC%A0%95%EB%B3%B4-csv%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0">https://velog.io/@onicle/%ED%81%AC%EB%A1%A4%EB%A7%81%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%8A%A4%EB%A7%88%ED%8A%B8%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%A6%AC%EB%B7%B0-%EC%88%98%EC%A7%91#2-5-%EC%88%98%EC%A7%91%ED%95%9C-%EC%A0%95%EB%B3%B4-csv%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] Callable, Runnable, Future]]></title>
            <link>https://velog.io/@sang_dosa/Spring-Callable-Runnable-Future</link>
            <guid>https://velog.io/@sang_dosa/Spring-Callable-Runnable-Future</guid>
            <pubDate>Mon, 19 Aug 2024 05:01:32 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<p>Spring의 비동기 처리를 위한 인터페이스들</p>
<h2 id="callable">Callable</h2>
<p><code>call()</code> 메서드를 통해서 작업을 실행하며 결과를 반환할 수 있고, 체크 예외를 처리할 수 있음</p>
<h2 id="runnable">Runnable</h2>
<p><code>run()</code> 메서드를 통해 작업을 실행하며 결과를 반환값이 없다.</p>
<h2 id="future">Future</h2>
<ul>
<li>비동기 작업의 결과를 나타내며, 작업이 완료될 때까지 기다리거나 작업이 완료되었는지 확인할 수 있는 메서드를 제공</li>
<li>대부분 Callable과 함께 사용됨</li>
</ul>
<h3 id="사용-메소드">사용 메소드</h3>
<ol>
<li>submit</li>
</ol>
<pre><code>public class FutureExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Callable&lt;String&gt; callable = new MyCallable();

        // callable에 대한 call()을 실행시킴
        Future&lt;String&gt; future = executor.submit(callable);

        // 다른 작업을 수행할 수 있음
        System.out.println(&quot;Doing other tasks...&quot;);

        // 비동기 작업의 결과를 기다림
        String result = future.get();
        System.out.println(&quot;Callable result: &quot; + result);

        executor.shutdown();
    }
}</code></pre><ol start="2">
<li><p>invokeAll</p>
<pre><code>public class InvokeAllExample {
 public static void main(String[] args) throws Exception {
     ExecutorService executor = Executors.newFixedThreadPool(3);

     List&lt;Callable&lt;String&gt;&gt; tasks = Arrays.asList(
         () -&gt; {
             TimeUnit.SECONDS.sleep(2);
             return &quot;Task 1&quot;;
         },
         () -&gt; {
             TimeUnit.SECONDS.sleep(1);
             return &quot;Task 2&quot;;
         },
         () -&gt; {
             TimeUnit.SECONDS.sleep(3);
             return &quot;Task 3&quot;;
         }
     );

     // Callable안에 있는 모든 값들이 한번에 실행됨
     List&lt;Future&lt;String&gt;&gt; futures = executor.invokeAll(tasks);

     for (Future&lt;String&gt; future : futures) {
         // 비동기 작업의 결과를 기다림
         String result = future.get();
         System.out.println(&quot;Result: &quot; + result);
     }

     executor.shutdown();
 }
}</code></pre></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Security] 사용자 관리 ]]></title>
            <link>https://velog.io/@sang_dosa/Spring-Security-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@sang_dosa/Spring-Security-%EC%82%AC%EC%9A%A9%EC%9E%90-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Tue, 30 Jul 2024 05:53:48 GMT</pubDate>
            <description><![CDATA[<h2 id="사용자-관리">사용자 관리</h2>
<ul>
<li>Security를 통해 사용자 관리를 위한 처리를 개발 할 수 있음</li>
<li>사용자 표현 : UserDetails</li>
<li>권한 표현 : GrantedAtuthority</li>
<li>사용자 만들기 및 암호 수정 등의 커스텀 작업 : UserDetailsService, UserDetailsManager</li>
</ul>
<hr>
<h3 id="userdetailsservice">UserDetailsService</h3>
<ul>
<li>사용자의 이름으로 사용자를 검색함 (Read)</li>
</ul>
<hr>
<h3 id="userdetailsmanager">UserDetailsManager</h3>
<ul>
<li>사용자 조회 뿐만 아니라 추가, 수정, 삭제 작업 수행 가능 (Create, Update, Delete)</li>
<li>UserDetailsService을 상속하고 있음</li>
</ul>
<hr>
<h3 id="grantedatuthority">GrantedAtuthority</h3>
<ul>
<li>접속한 사용자가 수행할 수 있는 작업을 나타내는 이용 권리의 집합 정리</li>
</ul>
<hr>
<h3 id="userdetails">UserDetails</h3>
<ul>
<li>권한에 대한 GrantedAtuthority을 포함</li>
<li>UserDetails을 활용하여 사용자 조회, 생성, 삭제 등을 진행(UserDetailsService, UserDetailsManager)</li>
<li>인터페이스이기 때문에 어플리케이션 수준에서 구현 필요</li>
<li>사용자 비활성화 기능 제공<ul>
<li>계정 만료, 계정 잠금, 자격 증명 만료, 계정 비활성화 기능</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 동적계획법]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8F%99%EC%A0%81%EA%B3%84%ED%9A%8D%EB%B2%95</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%8F%99%EC%A0%81%EA%B3%84%ED%9A%8D%EB%B2%95</guid>
            <pubDate>Wed, 17 Jul 2024 06:00:11 GMT</pubDate>
            <description><![CDATA[<h1 id="동적계획법">동적계획법</h1>
<p>프로그램이 실행되는 도중에 실행에 필요한 메모리를 할당하는 방법</p>
<ul>
<li>이전에 구한 값을 기반으로 규칙성을 파악하여 다음 값을 구하는 것</li>
<li>하위 문제의 최적해를 적절히 사용하여 상위 문제를 해결</li>
<li>구현은 분할 정복 알고리즘과 유사<ul>
<li>주어진 문제를 부분 문제로 나누어 각 부분 문제의 답을 계산</li>
<li>부문 문제들을 나누는데에 있어서 분할 후 주어진 부분문제의 정답을 한 번만 계산하고 저장해 두고 추후 해당 부분 문제에 대한 해답을 확인할 때 저장되어 있는 값을 확인</li>
</ul>
</li>
</ul>
<p>예시) 최적 분 문제 구조, 피보나치 수열....</p>
<hr>
<h2 id="피보나치-수열">피보나치 수열</h2>
<p>첫째 항과 둘째 항이 1의 값을 가지고, 그 뒤의 모든 항은 바로 앞 두 항의 합으로 구성된 수열</p>
<p>소스</p>
<pre><code class="language-agsl">작성 언어: java

public static int[] fib;
public static int result = 0;

public static int FibonacciNumber(int A){
    // A번째 수 확인
    fib = new int[A];

    int result = calc(A-1)

    return result;
}

public static int calc(int A){
    if(A &lt; 2){
        return fib[A] = 1;
    }
    if(fib[A] != 0){
        return fib[A];
    }
    else{
        return fib[A] = calc(A-1) + calc(A-2);
    }
} 
</code></pre>
<hr>
<p>참고</p>
<blockquote>
<p><a href="https://namu.wiki/w/%EB%8F%99%EC%A0%81%20%EA%B3%84%ED%9A%8D%EB%B2%95#toc">https://namu.wiki/w/%EB%8F%99%EC%A0%81%20%EA%B3%84%ED%9A%8D%EB%B2%95#toc</a>
<a href="https://sskl660.tistory.com/87">https://sskl660.tistory.com/87</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 분할정복]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5</guid>
            <pubDate>Wed, 17 Jul 2024 02:18:58 GMT</pubDate>
            <description><![CDATA[<h1 id="분할정복">분할정복</h1>
<p>주어진 문제에 대해서 분할하여 각각의 문제를 해결하는 방법으로 하향식 접근 방식</p>
<blockquote>
<ol>
<li>주어진 문제에 대해서 2개 이상의 작은 문제로 분할</li>
<li>분할된 각각의 작은 문제들을 정복해 문제 해결</li>
<li>필요시에 해결된 문제들을 다시 합쳐 원래 문제에 대한 해답을 구함</li>
</ol>
</blockquote>
<p><strong>장점</strong>: 문제를 나눔으로서 어려운 문제를 해결할 수 있는 경우를 생성할 수 있다는 장점이 있고, 이를 위한 효율적인 알고리즘이 다량 존재,</p>
<p><strong>단점</strong>: 함수를 재귀적으로 호출해야 하기 때문에 오버헤드 및 메모리 다량 보관에 대한 스택 오버플로우가 발생할 수 있음</p>
<p>예시) 이진탐색, 합병정렬, 퀵정렬,,,</p>
<hr>
<h2 id="이진탐색">이진탐색</h2>
<p>정렬되어 있는 데이터에 대해서 탐색 방법</p>
<ul>
<li>배열의 가운데 원소와 탐색하려는 값을 비교</li>
<li>성능: O(logN)<blockquote>
<p>탐색값 = 가운데 원소 -&gt; 탐색 성공</p>
<p>탐색값 &lt; 가운데 원소 -&gt; 이진 탐색 왼쪽 부분배열 호출</p>
<p>탐색값 &gt; 가운데 원소 -&gt; 이진 탐색 오른쪽 부분배열 호출</p>
</blockquote>
</li>
</ul>
<p>소스 </p>
<pre><code class="language-agsl">작성 언어: java

public int BinartSearch(int[] A, int left, int right, int B){
    if(left &gt; right) return -1;

    int mid = (int) ((right - left) / 2);

    if( A[mid] == B ) return mid;
    else if( A[mid] &lt; B ){
        BinartSearch(int[] A, mid+1, right, B);
    }
    else{
        BinartSearch(int[] A, left, mid-1, B);
    }
} </code></pre>
<hr>
<h2 id="합병정렬">합병정렬</h2>
<p>배열의 정렬을 위해 분할정복 알고리즘을 사용하여 가장 작은 배열로 분할해서 정렬하고 합병하면서 정렬 처리</p>
<ul>
<li>성능: O(NlogN)<pre><code class="language-agsl">작성 언어: java
</code></pre>
</li>
</ul>
<p>//분할
private void mergeSort(int[] arr, int low, int high) {
       if (low &gt;= high) { //종료 조건
           return;
       }</p>
<pre><code>int mid = low + ((high - low) / 2);
mergeSort(arr, low, mid);//왼쪽
mergeSort(arr, mid + 1, high);//오른쪽

merge(arr, low, mid, high);</code></pre><p>}</p>
<p>//합병
private void merge(int[] arr, int low, int mid, int high) {
    int[] temp = new int[high - low + 1];
    int idx = 0;</p>
<pre><code>int left = low;
int right = mid + 1;
while (left &lt;= mid &amp;&amp; right &lt;= high) {
    if (arr[left] &lt;= arr[right]) {
        temp[idx] = arr[left];
        left++;
    } else {
        temp[idx] = arr[right];
        right++;
    }
    idx++
}

while (left &lt;= mid) { //왼쪽 리스트에 값이 남아 있는 경우
        temp[idx] = arr[left];
        idx++;
        left++;
    }

while (right &lt;= high) { //오른쪽 리스트에 값이 남아 있는 경우
    temp[idx] = arr[right];
    idx++;
    right++;
}

for (int i = low; i &lt;= high; i++) {
    arr[i] = temp[i - low];
}</code></pre><p>}</p>
<p>```</p>
<p>참고</p>
<blockquote>
<p><a href="https://minhamina.tistory.com/127">https://minhamina.tistory.com/127</a></p>
<p><a href="https://cdragon.tistory.com/entry/Algorithm-Merge-Sort%ED%95%A9%EB%B3%91-%EC%A0%95%EB%A0%AC">https://cdragon.tistory.com/entry/Algorithm-Merge-Sort%ED%95%A9%EB%B3%91-%EC%A0%95%EB%A0%AC</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring Security] 기본 지식]]></title>
            <link>https://velog.io/@sang_dosa/Spring-Security-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@sang_dosa/Spring-Security-%EA%B8%B0%EB%B3%B8-%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Tue, 16 Jul 2024 06:02:35 GMT</pubDate>
            <description><![CDATA[<h1 id="string-security">String Security</h1>
<p>HTTP Basic, 아키텍쳐, UserDetailsService, PasswordEncoder</p>
<h2 id="http-basic-인증">HTTP Basic 인증</h2>
<ul>
<li>기본적인 스프링 시큐리티 인증 방식</li>
<li>아이디와 비밀번호를 요청 헤더에 담아 <strong>Base64</strong>로 인코딩</li>
<li>Key : Authorization</li>
<li>Value : 아이디, 비밀번호 인코딩된 값</li>
</ul>
<hr>
<h2 id="아키텍쳐">아키텍쳐</h2>
<h3 id="인증-필터">인증 필터</h3>
<p>클라이언트가 전달한 인증 요청을 가지고 와 인증 관리자에게 전달</p>
<h3 id="보안-컨텍스트">보안 컨텍스트</h3>
<p>인증이 완료된 엔터티에 대한 세부 정보가 컨텍스트에 저장되어 인증 데이터를 유지</p>
<h3 id="인증-관리자">인증 관리자</h3>
<p>전달 받은 요청을 인증 논리를 구현한 인증 공급자를 통해 인증 여부 확인</p>
<h3 id="인증-공급자">인증 공급자</h3>
<p>사용자 세부 정보 및 암호 인코더를 활용해 인증 논리를 구현해 인증 처리를 진행</p>
<h3 id="사용자-세부-정보-서비스userdetailservice">사용자 세부 정보 서비스(UserDetailService)</h3>
<p>요청으로 들어온 사용자를 확인</p>
<h3 id="암호-인코더passwordencoder">암호 인코더(PasswordEncoder)</h3>
<p>요청으로 들어온 암호에 대해서 검증</p>
<hr>
<h2 id="userdetailservice">UserDetailService</h2>
<p>사용자에 대한 정보를 관리</p>
<ul>
<li>기본 구현체는 내부 메모리에 기본 자격 증명을 등록 (사용자 이름 : user)</li>
<li>스프링 컨텍스트가 로드 될 때, 자동으로 생성</li>
</ul>
<hr>
<h2 id="passwordencoder">PasswordEncoder</h2>
<ul>
<li>요청이 들어온 암호를 인코딩</li>
<li>암호가 기존 인코딩과 일치하는지 확인</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 재귀함수]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 15 Jul 2024 04:22:17 GMT</pubDate>
            <description><![CDATA[<h1 id="재귀함수">재귀함수</h1>
<p>함수에서 자기 자신을 다시 호출해 작업을 수행하는 방식
특정한 트리거가 있지 않는 이상 지속적으로 자기 자신을 호출해 작업을 진행해 반복문을 구현할 때 사용한다.</p>
<p>대표적으로 &quot;펙토리얼 함수&quot;, &quot;하노이의 탑&quot; 등을 구현할 때 사용된다.</p>
<p>반복문(for, while)에 들어가야할 변수들을 다수 생성하지 않고, 동일한 매개변수를 통해 반복 작업을 하는 것이기 때문에 변수의 수를 줄일 수 있다.
함수를 호출하게 되면 해당 함수에 대한 지역변수, 매개변수, 반환값을 모두 stack에 저장해야 하는데, 재귀함수에서 자기 자신을 많이 호출하게 되면 그만큼 메모리를 사용하게 되기 때문에 속도 저하가 될 수 있습니다. 또한 복귀를 위한 컨텍스트 스위칭에 비용이 발생하게 됩니다.</p>
<p>▷ 위 단점을 없에기 위해서 <strong>꼬리 재귀</strong>를 사용하는 경우가 많아졌다.</p>
<blockquote>
<p>꼬리 재귀(tail call recursion) :</p>
<p>자기 자신을 기다리며 생기는 메모리 낭비를 최소화 시키기 위해 재귀 호출이 끝나는 시점에서 바로 결과만 반환하도록 함수를 구성하는 것</p>
<p>반환 시점에 함수의 상태유지 및 추가 연산을 하지 않기에 메모리 사용률을 줄일 수 있다.</p>
</blockquote>
<hr>
<p>소스</p>
<pre><code>작성 언어 java

// 펙토리얼 함수
public static int fac(int num){
    if(num == 1){
        return 1;
    }else{
        return num * fac(num - 1);
    }
}

// 펙토리얼 함수(꼬리 재귀)
public static int facTail(int num, int result){
    if(num == 1){
        return 1;
    }else{
        return fac(num - 1, result * num);
    }
}
</code></pre><p>참고</p>
<blockquote>
<p><a href="https://catsbi.oopy.io/dbcc8c79-4600-4655-b2e2-b76eb7309e60">https://catsbi.oopy.io/dbcc8c79-4600-4655-b2e2-b76eb7309e60</a>
<a href="https://wayhome25.github.io/cs/2017/04/15/cs-16-1-recursion/">https://wayhome25.github.io/cs/2017/04/15/cs-16-1-recursion/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 선택정렬]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%84%A0%ED%83%9D%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Mon, 15 Jul 2024 02:15:56 GMT</pubDate>
            <description><![CDATA[<h1 id="선택-정렬">선택 정렬</h1>
<p>리스트 내에 있는 수들 중에서 최솟값을 첫번째에 넣고, 그 다음 값들에 대해서 두번째에 넣고 ..... 하는 행위들을 반복해서 진행하는 정렬 방식</p>
<p>해당 정렬 알고리즘에는 과정이 있다.</p>
<blockquote>
<ol>
<li>주어진 배열 중 최솟값을 찾는다.</li>
<li>그 값을 배열 내에 있는 가장 앞 값과 위치를 변경한다.</li>
<li>가장 앞 값을 제외한 배열 내에서 1~2 과정을 반복한다.</li>
</ol>
</blockquote>
<hr>
<p>소스</p>
<pre><code>작성 언어: java

public static int[] calc(int[] list){
    int list_len = list.length;

    for(int i = 0; i &lt; list_len; i++){
        int now_num = list[i];

        // 최솟값을 찾아야 하는 리스트
        int[] findList = Arrays.copyOfRange(list, i, list_len);

        int min = 1000000;
        int min_count = 0;
        int findList_len = findList.length;

        // 최솟값과 최솟값의 순번 구하기
        for(int j = 0; j &lt; findList_len; j++){
            if(findList[j] &lt; min) {
                min = findList[j];
                min_count = j;
            }
        }

        // 최솟값과 i 값 스왑
        list[i] = list[min_count + i];
        list[min_count + i] = now_num;
    }

    return list;
} </code></pre><hr>
<p>참고</p>
<blockquote>
<p><a href="https://gmlwjd9405.github.io/2018/05/06/algorithm-selection-sort.html">https://gmlwjd9405.github.io/2018/05/06/algorithm-selection-sort.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 몬테카를로 알고리즘]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EB%AA%AC%ED%85%8C%EC%B9%B4%EB%A5%BC%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EB%AA%AC%ED%85%8C%EC%B9%B4%EB%A5%BC%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Fri, 12 Jul 2024 02:20:49 GMT</pubDate>
            <description><![CDATA[<h1 id="몬테카를로-알고리즘">몬테카를로 알고리즘</h1>
<p>무작위 난수를 기반으로 특정 공간에 해당 난수가 존재할 가능성을 확인하는 알고리즘</p>
<p>몬테카를로 알고리즘은 큰 수와 반복적인 작업을 해야 하므로 직접적인 계산 보다는 시스템 및 함수 처리에서 무작위성을 사용하여 알고리즘을 해결한다.</p>
<hr>
<p>특정한 패턴을 따르는 경향이 있다.</p>
<blockquote>
</blockquote>
<ol>
<li>가능한 입력의 도메인을 정의한다.</li>
<li>도메인에 대한 확률 분포에서 임의로 입력을 생성한다.</li>
<li>입력에 대한 결정론적인 계산을 수행한다.</li>
<li>결과를 집계한다.<blockquote>
</blockquote>
</li>
</ol>
<p>몬테카를로 알고리즘을 통한 문제 해결 예시는 대표적으로 원주율을 구하는 방식이다.</p>
<blockquote>
</blockquote>
<ol>
<li>정사각형을 그린 후 그 안에 사분면을 삽입한다.</li>
<li>정사각형 위에 일정한 개수의 점을 균일하게 분포한다.</li>
<li>사분면 내부의 점, 즉 원점으로부터 1만의 개수를 센다.</li>
<li>내부의 개수와 전체 개수의 비율은 두 영역의 비율을 나타내므로 그 값에 4를 곱하여 파이를 만든다.<blockquote>
</blockquote>
</li>
</ol>
<p>원주율을 구하는 실제 코드는 아래와 같다.</p>
<pre><code>작성: java

import java.util.*;

public static double pie(){
    int roofNum = 100000;
    int count = 0;


    while( i != 100000){
        double x = Math.random() * 2 - 1;
        double y = Math.random() * 2 - 1;

        if((x*x) + (y*y) &lt;= 1) {
            count++;
        }

        i++
    }

    double result = 4.0 * count / roofNum;
    return result;
}</code></pre><hr>
<p>참고</p>
<blockquote>
</blockquote>
<p><a href="https://velog.io/@awt0311/%EB%AA%AC%ED%85%8C%EC%B9%B4%EB%A5%BC%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">https://velog.io/@awt0311/%EB%AA%AC%ED%85%8C%EC%B9%B4%EB%A5%BC%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</a></p>
<blockquote>
</blockquote>
<p><a href="https://walking-and-walking.com/entry/Java%EC%97%90%EC%84%9C-%ED%99%95%EB%A5%A0%EC%A0%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%EA%B5%AC%ED%98%84-%EC%9D%B4%EB%A1%A0%EA%B3%BC-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EB%A1%80">https://walking-and-walking.com/entry/Java%EC%97%90%EC%84%9C-%ED%99%95%EB%A5%A0%EC%A0%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%EA%B5%AC%ED%98%84-%EC%9D%B4%EB%A1%A0%EA%B3%BC-%EC%8B%A4%EC%A0%9C-%EC%82%AC%EB%A1%80</a></p>
<blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 유클리드 호제법]]></title>
            <link>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95</link>
            <guid>https://velog.io/@sang_dosa/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95</guid>
            <pubDate>Fri, 12 Jul 2024 01:13:30 GMT</pubDate>
            <description><![CDATA[<h1 id="유클리드-호제법이란">유클리드 호제법이란?</h1>
<p>두 수의 최대 공약수를 구하는 알고리즘</p>
<blockquote>
<p>호제법: 두 수가 서로 상대방 수를 나누어서 결국 원하는 수를 얻는 알고리즘을 말한다.</p>
</blockquote>
<hr>
<p>9와 3에 대한 <strong>최대 공약수</strong> 를 구한다고 가정해 보자</p>
<pre><code>9 = 3 \* 3
3 = 3 \* 1
▷ 9와 3의 최대 공약수는 3</code></pre><p>이렇게 작은 수에 대해서는 빠르게 소인수분해를 통해 최대 공약수를 눈으로 찾을 수 있지만, &quot;1112와 695의 최대 공약수를 찾으시요&quot; 같이 큰 수들의 최대 공약수를 찾는 문제가 나온다면 많은 시간이 소요될 것이다.</p>
<p>이를 위해 유클리드 호제법을 사용해 빠르게 최대 공약수를 찾는다.</p>
<p>유클리드 호제법을 이해하려면 MOD에 대한 부분을 이해해야 한다.</p>
<blockquote>
<p>MOD: 두 값을 나눈 나머지를 구하는 연산</p>
</blockquote>
<pre><code>MOD 예시

1112 mod 695 = 417
</code></pre><p>유클리드 호제법은 위 MOD 연산의 결과 값이 0 이 나올 때 까지 반복하는 것이다.</p>
<pre><code>1112 mod 695 = 417

695 mod 417 = 278

417 mod 278 = 139

278 mod 139 = 0</code></pre><p>&quot;0&quot;이 된 순간의 직전 값 &quot;139&quot; 가 최대 공약수가 된다.</p>
<p>따라서 1112와 695의 최대 공약수는 139이다.</p>
<p>이는 417, 278, 139도 &quot;모두 N의 배수인 수&quot;라는 것과 같은 최대 공약수를 가진다는 것을 알 수 있다.</p>
<hr>
<p>유클리드 호제법을 함수로 표현한다면 아래와 같이 표현할 수 있다.</p>
<pre><code>작성: java

1. 반복문
public static int GCD(){
    int a = 1112;
    int b = 695;

    while(true){
        int c = 0;

        c = a % b;

        if( c == 0 ) {
            return b;
            break;
        }
        else {
            a = b;
            b = c;
        }
    }
}

2. 재귀
public static int GCD(int a, int b){
    return a % b == 0 ? b : GCD(b, a % b);
}
</code></pre><hr>
<p>참고</p>
<blockquote>
<p><a href="https://velog.io/@yerin4847/W1-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95">https://velog.io/@yerin4847/W1-%EC%9C%A0%ED%81%B4%EB%A6%AC%EB%93%9C-%ED%98%B8%EC%A0%9C%EB%B2%95</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] org.springframework.web.multipart.support.MissingServletRequestPartException]]></title>
            <link>https://velog.io/@sang_dosa/Error-org.springframework.web.multipart.support.MissingServletRequestPartException</link>
            <guid>https://velog.io/@sang_dosa/Error-org.springframework.web.multipart.support.MissingServletRequestPartException</guid>
            <pubDate>Thu, 25 Apr 2024 11:54:34 GMT</pubDate>
            <description><![CDATA[<h2 id="오류">오류</h2>
<p>param이 있는 Http 통신 테스트 진행 시 아래 오류 발생</p>
<p>org.springframework.web.multipart.support.MissingServletRequestPartException</p>
<h2 id="해결방안">해결방안</h2>
<p>호출되는 Http method의 param 값이 없을 경우 테스트 오류 발생하기 때문에 param 값이 안들어 왔을 경우에 대한 속성(<strong>required = false</strong>) 추가를 해준다.</p>
<p>AS-IS</p>
<pre><code>    @PostMapping(&quot;/chunk/upload&quot;)
    public ResponseEntity&lt;String&gt; chunkUpload(@RequestParam(name = &quot;chunk&quot;) MultipartFile file,
                                              @RequestParam(&quot;chunkNumber&quot;) int chunkNumber,
                                              @RequestParam(&quot;totalChunks&quot;) int totalChunks) throws IOException {
                                              &#39;&#39;&#39;</code></pre><p>TO-BE</p>
<pre><code>    @PostMapping(&quot;/chunk/upload&quot;)
    public ResponseEntity&lt;String&gt; chunkUpload(@RequestParam(name = &quot;chunk&quot;, required = false) MultipartFile file,
                                              @RequestParam(&quot;chunkNumber&quot;) int chunkNumber,
                                              @RequestParam(&quot;totalChunks&quot;) int totalChunks) throws IOException {
                                              &#39;&#39;&#39;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] " Function() " vs " Const = () => "]]></title>
            <link>https://velog.io/@sang_dosa/JS-Function-vs-Const</link>
            <guid>https://velog.io/@sang_dosa/JS-Function-vs-Const</guid>
            <pubDate>Thu, 25 Apr 2024 11:09:29 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<p>&quot; Function() &quot;, &quot; Const = () =&gt; &quot; 모두 함수 기능을 하는 역할을 한다.</p>
<h3 id="function">Function()</h3>
<p>다른 문장의 가장 상단에서 읽히는 hoisting 이 허용</p>
<h3 id="const---">Const = () =&gt;</h3>
<p>block 내부에서만 구현이 가능한 범위를 가지고 있으며, 다시 선언이 될 수 없다는 특징</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] HTTP Response ]]></title>
            <link>https://velog.io/@sang_dosa/CS-HTTP-Response</link>
            <guid>https://velog.io/@sang_dosa/CS-HTTP-Response</guid>
            <pubDate>Thu, 25 Apr 2024 10:53:46 GMT</pubDate>
            <description><![CDATA[<h2 id="개념">개념</h2>
<h3 id=""></h3>
<p>206(일부 콘텐츠): 서버가 GET 요청의 일부만 성공적으로 처리했다.</p>
]]></description>
        </item>
    </channel>
</rss>