<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_030702.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 14 Mar 2026 14:21:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>_030702.log</title>
            <url>https://velog.velcdn.com/images/_030702/profile/4c268cb3-eab3-4616-9e56-2cbef1947349/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. _030702.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_030702" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Ubuntu 리눅스 쿠버네티스 클러스터 구축하기]]></title>
            <link>https://velog.io/@_030702/Ubuntu-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@_030702/Ubuntu-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 14 Mar 2026 14:21:42 GMT</pubDate>
            <description><![CDATA[<h2 id="0-쿠버네티스란">0. 쿠버네티스란?</h2>
<p>쿠버네티스 클러스터는 컨테이너화 된 애플리케이션을 실행하는 컴퓨팅 노드이다.</p>
<p>컨테이너화 된 애플리케이션의 대규모 배포, 스케일링 및 관리를 간편하게 만들어주며, 오픈 소스 기반의 컨테이너 오케스트레이션이다.</p>
<pre><code>오케스트레이션(Orchestration)은 
컨테이너, 서버, 애플리케이션, 데이터 파이프라인 등 
여러 IT 구성 요소와 자동화된 태스크들을 연동하여 
복잡한 워크플로우를 효율적으로 관리 및 조율하는 기술을 의미한다.</code></pre><p>도커는 애플리케이션의 프로세스 격리 기술을 사용해 컨테이너로 관리할 수 있는 도구라면, </p>
<p>쿠버네티스는 그런 컨테이너를 간편하게 관리할 수 있게 도와준다고 보면 된다.</p>
<h2 id="1-준비사항">1. 준비사항</h2>
<p>Ubuntu 24.04.1 LTS / VMware workstation Pro</p>
<ul>
<li><p>Master Node 1대 (CPU 2 Core, RAM 2GB)  </p>
</li>
<li><p>Worker Node 2대 (CPU 1 Core, RAM 1GB)</p>
</li>
</ul>
<p>사양 확인</p>
<ul>
<li><p>코어 nproc</p>
</li>
<li><p>메모리 free -h</p>
</li>
<li><p>ifconfig -a</p>
</li>
<li><p>uuid sudo cat /sys/class/dmi/id/product_uuid</p>
</li>
</ul>
<p>모든 명령어 입력은 root 계정에서 진행한다.</p>
<ul>
<li>sudo su</li>
</ul>
<h2 id="2-순서">2. 순서</h2>
<ol>
<li><p>쿠버네티스 설치 준비</p>
</li>
<li><p>컨테이너 런타임 설치</p>
</li>
<li><p>쿠버네티스 설치</p>
</li>
<li><p>Master Node 설정</p>
</li>
<li><p>Worker Node 등록</p>
</li>
<li><p>최종 확인</p>
</li>
</ol>
<h2 id="3-swap-비활성화">3. swap 비활성화</h2>
<p>쿠버네티스의 대표적인 역할은 스케줄링(어느 노드에 어떤 컨테이너 (=Pod)를 배치할까)</p>
<p>스케줄러는 각 노드의 가용자원(CPU, Memory)를 정확히 파악해서 Pod를 배치함</p>
<p>만약 swap이 켜져있으면, 노드의 실제 물리 메모리가 부족할 때 디스크의 일부를 메모리처럼 사용하게 됨 </p>
<p>이럴 경우, Pod를 계속 보낼 수 있지만, 실제로는 디스크 I/O 성능이 급격히 저하될 수 있음</p>
<p><strong>따러서, 스케줄러가 자원 상태를 100% 예측 가능하게 관리하기 위해서 변수가 되는 스왑을 제거하는 것임.</strong></p>
<pre><code># 영구 비활성화
sudo sed -i &#39;/swap/s/^/#/&#39; /etc/fstab

# 메모리 상태확인
sudo free -m

# swap 메모리 상태 확인 (출력값 없으면 비활성화 상태)
sudo swapon -a</code></pre><p> <img src="https://velog.velcdn.com/images/_030702/post/2270e998-e87f-4343-8449-d833b3bb00b9/image.png" alt=""></p>
<h2 id="4-방화벽-설정">4. 방화벽 설정</h2>
<p>쿠버네티스 포트만 개방 / 방화벽 비활성화 두가지 방법 중 하나를 선택해서 진행함.</p>
<p>필자의 경우 전자를 선택해서 진행. </p>
<pre><code># 방화벽 설치 및 예외 설정 
sudo apt-get install -y firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/9538d210-d623-42b8-974c-64c00ac2fb06/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/bd2256cf-2520-4e04-84b3-d67ea083d1d5/image.png" alt=""></p>
<h3 id="master-node-인-경우">Master node 인 경우</h3>
<p>TCP</p>
<ul>
<li><p>6443 Kubernetes API Server</p>
</li>
<li><p>2379-2380 etcd server API (DB)</p>
</li>
<li><p>10250 Kubernetes API</p>
</li>
<li><p>10251 kube-scheduler</p>
</li>
<li><p>10252 kube-controller-manager </p>
</li>
</ul>
<p>UDP</p>
<ul>
<li><p>CNI (Container network interface) 용도</p>
</li>
<li><p>8285 Flannel (UDP)</p>
</li>
<li><p>8472 Flannel (VXLAN)</p>
</li>
</ul>
<pre><code>sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=2379-2380/tcp
sudo firewall-cmd --permanent --add-port=10250-10252/tcp
sudo firewall-cmd --permanent --add-port=8285/udp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --reload</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/6d3beb0a-5259-4921-bf09-663c58bb5bd2/image.png" alt=""></p>
<h3 id="worker-node-인-경우">Worker node 인 경우</h3>
<p>TCP</p>
<ul>
<li><p>10250 kubelet API</p>
</li>
<li><p>30000-32767 Nodeport services (외부에서 쿠버네티스 내부 서비스에 접속할 때 사용함</p>
</li>
</ul>
<p>UDP</p>
<ul>
<li>8285/8472 Flannel </li>
</ul>
<pre><code>sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --permanent --add-port=30000-32767/tcp
sudo firewall-cmd --permanent --add-port=8285/udp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --permanent --add-port=26443/tcp
sudo firewall-cmd --reload</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/d4e02d92-eaea-47ea-83de-e0114a34bf5e/image.png" alt=""></p>
<h3 id="방화벽-오픈-상태-확인">방화벽 오픈 상태 확인</h3>
<pre><code>#열린 포트 확인
sudo firewall-cmd --list-all</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/646a1d2a-c272-46b2-96ad-9c9382b41100/image.png" alt="">👆 worker node 
<img src="https://velog.velcdn.com/images/_030702/post/6bee8421-57b7-46f2-9e98-16c1b36d7c76/image.png" alt="">👆 master node</p>
<h2 id="5-네트워크-옵션-설정">5. 네트워크 옵션 설정</h2>
<p>리눅스 서버의 네트워크 기능을 쿠버네티스용 (= 컨테이너 통신용)으로 바꾸는 과정</p>
<p><code>br_netfilter</code></p>
<ul>
<li><p>리눅스 서버는 기본적으로 물리적인 통신을 담당</p>
</li>
<li><p>하지만, 쿠버네티스는 서버 안에 가상 네트워크를 만들고, 그 안에서 Pod들이 통신하도록 함</p>
</li>
<li><p>br_netfilter는 리눅스 커널의 bridge 레이어를 통과하는 패킷들을 iptables(방화벽/네트워크 규칙)이 검사할 수 있게 함</p>
</li>
</ul>
<p><code>k8s.conf</code></p>
<ul>
<li>컨테이너 간의 통신이 리눅스 방화벽 규칙을 따르도록 강제하는 설정 </li>
</ul>
<pre><code># /etc/modules-load.d/k8s.conf 파일 생성
sudo cat &lt;&lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
# /etc/sysctl.d/k8s.conf 파일 생성
sudo cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# 시스템 재시작 없이 stysctl 파라미터 반영
sudo sysctl --system</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/db2352e4-8f94-4fdc-b071-35874a0d4b82/image.png" alt=""></p>
<h2 id="6-컨테이너-런타임-설치">6. 컨테이너 런타임 설치</h2>
<p>쿠버네티스는 컨테이너를 관리하는 오케스트레이션 플랫폼이기 때문에, 컨테이너 런타임을 별도로 설치해야한다.</p>
<p>세가지 방법이 존재하는데, 두번째 방법 (containerd만 설치해서 사용하는 방법을 선택했다)</p>
<ol>
<li><p>docker 설치 시 Depends로 함께 설치되는 containerd 패키지 사용</p>
</li>
<li><p>containerd만 설치 사용</p>
</li>
<li><p>docker engine + cri-dockerd </p>
</li>
</ol>
<h3 id="containerd-설치-후-사용">Containerd 설치 후 사용</h3>
<h4 id="apt-업데이트-후-필수-패키지-설치">apt 업데이트 후 필수 패키지 설치</h4>
<pre><code>sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/f0f899d2-6438-4a6f-9e75-8c2c95cbc07d/image.png" alt=""></p>
<h4 id="공개키-다운로드-및-저장소-등록">공개키 다운로드 및 저장소 등록</h4>
<p>공개키는 인터넷에서 파일을 다운로드할때, 해커가 심어놓은 악성코드인지 혹은 진짜 Docker인지 확인하기 위해서 사용함</p>
<pre><code>#공개키 다운로드
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 저장소 등록
echo &quot;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable&quot; | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/d69f9f7d-a249-4190-af9e-6344016d3bab/image.png" alt=""></p>
<h4 id="containerd-패키지-설치">containerd 패키지 설치</h4>
<p>쿠버네티스의 표준 런타임 </p>
<pre><code>sudo apt-get update
#containerd 패키지 설치
sudo apt-get install containerd
#설치 확인
sudo systemctl status containerd</code></pre><h4 id="containerd-config-설정">containerd config 설정</h4>
<p>리눅스의 주인은 systemd 인데, 쿠버네티스의 kubelet도 자원관리를 위해 이 systemd를 사용하길 바람.</p>
<p>이 설정을 안해주면 컨테이너 런타임과 시스템이 충돌하게 됨.</p>
<pre><code>#containerd 구성 파일 생성
sudo mkdir -p /etc/containerd
#containerd 기본 설정값으로 config.toml 생성
sudo containerd config default | sudo tee /etc/containerd/config.toml
#config.toml 파일 수정 SystemCgroup 설정을 true로 바꿈
vi /etc/containerd/config.toml
#수정사항 적용 및 재실행
sudo systemctl restart containerd</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/e4cfd685-ddd4-4a70-9f7a-c03ae6c3558e/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/2d051d7c-f2a2-4185-b067-4052dfa78705/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/224cbe77-3dca-4528-8b67-1f5ad1767d53/image.png" alt=""></p>
<h2 id="7-쿠버네티스-클러스터-설치">7. 쿠버네티스 클러스터 설치</h2>
<h4 id="필수-패키지-설치-및-구글-클라우드-공개키-다운로드-및-쿠버네티스-레포지토리-추가">필수 패키지 설치 및 구글 클라우드 공개키 다운로드 및 쿠버네티스 레포지토리 추가</h4>
<pre><code>sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
#공개 키 다운로드
curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://dl.k8s.io/apt/doc/apt-key.gpg
#apt 저장소에 쿠버네티스 저장소 추가
sudo echo &quot;deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main&quot; | sudo tee /etc/apt/sources.list.d/kubernetes.list</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/bf3e058c-3b6f-49c1-8195-8fe50cb5843d/image.png" alt=""></p>
<h4 id="쿠버네티스-패키지-설치">쿠버네티스 패키지 설치</h4>
<p>kubeadm : 클러스터를 구축(init, join)하는 도구</p>
<p>kubelet : 각 노드에서 컨테이너를 실제로 실행/관리함</p>
<p>kubectl : 사용자가 쿠버네티스에 명령을 내리는 도구</p>
<pre><code>#저장소 추가되었기 때문에 apt 업데이트
sudo apt-get update
#쿠버네티스 패키지 설치
sudo apt-get install -y kubelet kubeadm kubectl
#쿠버네티스 패키지 버전 고정
sudo apt-mark hold kubelet kubeadm kubectl
#쿠버네티스 설치 버전 조회
kubelet --version
kubeadm version
kubectl version
#kubelet service 확인
sudo systemctl status kubelet.service</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/42ad9550-6d93-4b79-a4d0-0d2aff912957/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/c6a99a27-6108-4fb4-b9a0-03bab5be487d/image.png" alt=""></p>
<h4 id="문제-발생">문제 발생</h4>
<p>→ kubelet, kubeadm, kubectl 모두 설치됨</p>
<p>→ 하지만 kubelet.services가 안열림 </p>
<p><strong><em>대안책</em></strong></p>
<p>Docker 저장소 정보 삭제하고 재설치</p>
<p><img src="https://velog.velcdn.com/images/_030702/post/26abb6cc-d33a-4d66-a245-b67ec24bcede/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/899f9ff1-3eac-41b7-ad69-26ff5966c034/image.png" alt=""></p>
<p>제대로 작동함을 확인 ! Master Node 구성</p>
<h2 id="8-control-plane-구성-및-cni-설정">8. Control-Plane 구성 및 CNI 설정</h2>
<p>Calico 기본 설정값 <code>192.168.0.0/16</code></p>
<pre><code># default
kubeadm init
# calico
sudo kubeadm init --pod-network-cidr=192.168.0.0/16</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/36bf4a1b-d184-4cd1-b46c-ba5cbcba9d28/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/32e6a70a-058a-4940-8b2a-985335d9cbfd/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/7b56d931-521d-40d6-9f57-f6a24d57cfe5/image.png" alt=""></p>
<p>netstat 보냈는데 답이 없음 = API 서버 죽어있음 </p>
<p><img src="https://velog.velcdn.com/images/_030702/post/343560f3-b7c6-4bd4-8809-d0225c7074a0/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/a6a4f622-24e8-47a0-a5a5-72340a25cdca/image.png" alt=""></p>
<p>API 서버가 죽어있어서, kubectl 설정 삭제하고 다시 작성함.</p>
<p>후에, CNI(container network interface) 설정 진행</p>
<pre><code>*CNI (container network interface)

컨테이너 간의 네트워크를 제어할 수 있는 플러그인으로 컨테이너 런타임에서 컨테이너의 네트워크를 사용하게 하는 인터페이스임.</code></pre><h2 id="9-최종-확인">9. 최종 확인</h2>
<pre><code># connection
nc -vz [ip] [port]
# master node의 kubeadm token create --print-join-command
# 알아온 ip, port, token, hash 값을 worker node에서 실행
# master node와 연동됨
sudo kubeadm join [ip] [port] [token] [hash]
# master node에서 연결된 node 확인
hostname -I</code></pre><p><img src="https://velog.velcdn.com/images/_030702/post/ab53d960-82aa-452c-99ae-c8766036b6ac/image.png" alt="">👆 worker node
<img src="https://velog.velcdn.com/images/_030702/post/aaf268b3-2297-4b1d-953d-fa9d84394b27/image.png" alt="">👆 master node</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vSphere, vCenter, ESXi, VMFS]]></title>
            <link>https://velog.io/@_030702/vSphere-vCenter-ESXi-VMFS</link>
            <guid>https://velog.io/@_030702/vSphere-vCenter-ESXi-VMFS</guid>
            <pubDate>Sat, 14 Mar 2026 14:11:00 GMT</pubDate>
            <description><![CDATA[<h3 id="esxi">ESXi</h3>
<ul>
<li>VMware에서 개발한 Hypervisor( 가상화 환경을 만들 수 있도록 최소한의 기능만을 지원하는 축소버전의 OS)</li>
<li>Type1 Hypervisor (bare-metal, os에 종속되지 않고 하드웨어에 직접 설치되는 구조)</li>
</ul>
<h3 id="vcenter">vCenter</h3>
<ul>
<li><p>가상화환경을 만드는 가장 큰 이유는 여러 컴퓨터를 하나의 큰 성능과 용량의 컴퓨터처럼 사용하기 위함</p>
<p>  ⇒ 이 기능을 지원하는 것이 vCenter</p>
</li>
<li><p>vCenter는 가상화 환경 구축을 위한 핵심 소프트웨어 ESXi가 설치된 다수의 머신을 하나의 가상화환경으로 만들어주는 기능을 제공함</p>
</li>
<li><p>vCenter를 이용해서 여러 ESXi 호스트들을 쉽게 추가/제거 할 수 있고, 그 외에도 vMotion, DRS&amp;DPM과 같은 기능을 제공함</p>
</li>
</ul>
<h3 id="vsphere">vSphere</h3>
<ul>
<li><p>VMware의 정의에 따르면 엔터프라이즈 급 가상화 플랫폼임</p>
</li>
<li><p>위에 언급한 소프트웨어들을 전부 포함하고 있는 소프트웨어 패키지를 의미</p>
<ul>
<li>Excel, Word= ESXi, vCenter</li>
<li>Microsoft Offic = vSphere</li>
</ul>
</li>
<li><p>소프트웨어 정의 데이터센터 (SDDC)</p>
<ul>
<li>소프트웨어-정의 데이터 센터는 서버, 네트워크, 스토리지 그리고 기반 설비 등의 모든 인프라가 가상화됨</li>
<li>데이터 센터의 제어 또한 소프트웨어에 의해 자동화 되는 것을 뜻함</li>
<li>vSphere은 가상 인프라 계층에서 ESXi 하이퍼바이저를 통해 서버 가상화 환경을 구현하고, 기능을 제공하는 분산 소프트웨어 시스템임</li>
<li>단순히 호스트를 분할하여 사용하는 것이 아니라, 인프라 자원을 하나의 거대한 가상 컴퓨터로 통합해 자원을 효율성있게 분할하여 사용함</li>
</ul>
</li>
<li><p>클라우드 환경</p>
<ul>
<li>vSphere은 규모에 맞는 간편하고 효율적인 관리, 원활한 하이브리드 클라우드 환경을 제공하여 향상된 애플리케이션 성능을 제공하고 모든 클라우드의 기반을 만들어줌</li>
<li>이를 통해서 IT 조직은 유연하고 안정적인 IT 서비스를 제공할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="vsphere-환경을-사전에-관리하는-방법">vSphere 환경을 사전에 관리하는 방법</h3>
<ul>
<li><p>VMware Skyline</p>
<ul>
<li><p>잠재적인 문제를 사전에 식별하고 해결 시간을 단축하기 위해 고객별 제품 사용 데이터를 자동으로 안전하게 수집, 집계 및 분석을 하는 기술</p>
</li>
<li><p>VMware Skyline Collector 라는 독립형 어플라이언스 기기를 설치해야함</p>
<ul>
<li>문제사항회피 : 잠재적인 문제를 사전에 식별하고, 문제가 발생하기 전에 해결해서 환경에 대한 신뢰성과 안정성 향상을 도모함</li>
<li>문제해결시간단축 : 환경 별 데이터 기반 분석으로 문제 해결 시간을 단축함</li>
<li>사용자 맞춤 추천 : 사용자 환경에 따른 해결방안을 제시함.</li>
</ul>
<p>⇒ vSphere 구독시 기본으로 제공함</p>
</li>
</ul>
</li>
</ul>
<h3 id="vsphere-vmfs-가상-시스템-파일-시스템">vSphere VMFS (가상 시스템 파일 시스템)</h3>
<ul>
<li>클러스터 파일 시스템<ul>
<li>클러스터 : 여러대의 컴퓨터들이 연결되어 하나의 시스템처럼 동작하는 집합</li>
</ul>
</li>
<li>여러 ESXi 호스트가 공유 스토리지를 동시에 읽거나 쓸 수 있음을 뜻함 ⇒ 분산 스토리지 구조 제공</li>
</ul>
<h3 id="vsphere가-cpu-메모리-네트워크-disk와-어떤-방식으로-상호작용하는가">vSphere가 CPU, 메모리, 네트워크, Disk와 어떤 방식으로 상호작용하는가?</h3>
<ul>
<li>물리적 구성요소를 소프트웨어 구성 요소로 추상화 ⇒ 가상화 기술을 통해 상호작용</li>
</ul>
<p>vCenter Server system과 ESXi hosts에 연결하기위한 사용자 인터페이스는 무엇이 있는가?</p>
<ul>
<li>vCenter Server System → vSphere Client</li>
<li>ESXi hosts → VMware Host Client<ul>
<li>vCenter Server를 사용할 수 없을 때, ESXi 호스트를 직접 관리하는데 사용할 수 있는 HTML5 기반 사용자 인터페이스</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[가상화, 컨테이너, 하이퍼바이저]]></title>
            <link>https://velog.io/@_030702/%EA%B0%80%EC%83%81%ED%99%94-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80</link>
            <guid>https://velog.io/@_030702/%EA%B0%80%EC%83%81%ED%99%94-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80</guid>
            <pubDate>Sat, 14 Mar 2026 14:10:02 GMT</pubDate>
            <description><![CDATA[<h3 id="가상화">가상화</h3>
<p>회사에서는 하나의 물리서버를 보다 효율적으로 사용하기를 바람
이런 필요에 의해 가상화 기술이 발전하게 됨 ⇒ VM이나 컨테이너와 같은 기술이 생김
물리적인 하드웨어를 논리적으로 구분하는 것 = 가상화
하나의 하드웨어에서 여러개의 시스템을 효율적으로 사용할 수 있게 해줌</p>
<h3 id="가상화의-유형">가상화의 유형</h3>
<p>가상머신 vm - 하이퍼바이저를 이용해 리소스 전체를 가상화하는 방법</p>
<p>컨테이너 container - OS 수준에서 프로세스를 컨테이너 형태로 격리하는 방법</p>
<p>⇒ virtual machine 은 물리적인 컴퓨터와 동일한 기능을 제공하는 소프트웨어 컴퓨터임</p>
<p><img src="https://velog.velcdn.com/images/_030702/post/60bd4a52-ecdc-4720-acad-c8eb30b72451/image.png" alt=""></p>
<h3 id="각-os-마다-커널이-존재함">각 OS 마다 커널이 존재함</h3>
<p>하드웨어를 제어하기 위한 명령어 : 커널</p>
<p>애플리케이션이 OS 커널에 요청을 보내는 방식이 다르고 (시스템 콜), 이를 하드웨어가 이해할 수 있는 CPU 명령어 셋으로 변환하는 과정이 필요함</p>
<p>Hypervisor은?
    -&gt; 이때 !!! 각 OS 마다 말하는 명령을 하드웨어가 이해할 수 있게 하나의 명령어로 번역해줌</p>
<pre><code>Windows - NT Kernel

Linux - Linux Kernel

mac - Darwin</code></pre><p>프로그램과 하드웨어(CPU) 사이에 <strong>CPU에 직접적으로 명령을 실행</strong>하기 위한 함수 리스트가 존재함 (ex. Linux  system call)</p>
<pre><code>만약 프로그램에서 파일을 연다고 생각해보자.

프로그램 파일 열기 클릭

Kernel 파일 Open 함수 실행

CPU 내부적으로 파일을 여닫고 하는 과정들에 포함되는 명령어 셋들을 실행

=&gt; OS 마다 차이가 발생하는 부분은 2번 과정

3번은 명령어 셋들이 호출되는 과정이 다를 뿐 최종적으로 CPU에 전달되는 명령어는 동일함</code></pre><h3 id="hypervisor">Hypervisor</h3>
<p>VM과 하드웨어 간의 I/O명령을 처리하는 인터페이스</p>
<p>물리적인 하드웨어를 논리적으로 가상화하는 것을 담당하는 기술이 하이퍼 바이저</p>
<p>가상머신을 생성하고 구동하는 프로그램 (=가상머신 모니터) → 다수의 운영체제를 동시에 실행하기 위한 논리적 플랫폼</p>
<p>host(=ESXI에 리소스를 제공하는 물리적 컴퓨터)에서 여러 가상의 GuestOS(=VM에서 실행되는 운영체제)를 실행하기 위한 플랫폼</p>
<p>VM 위에 올라간 OS들의 명령을 하드웨어가 이해할 수 있게 하나의 명령어로 번역해주고, 반대로 각 OS들에게 하드웨어의 리소스들을 나눠주면서 조율해줌 (운용역할도 진행)</p>
<p>⇒ VMware를 설치하고 CentOS를 올렸다면, CentOS에서 명령들을 하드웨어와 중재하는 역할을 한다고 이해하면 될듯</p>
<p>추가로, PC에서 핸드폰용 Android Emulator, iOS Emulator을 돌릴때 혹은 x86_64 OS에서 ARM / ARM64 OS를 테스트하고 싶을 때 사용하기도 함.</p>
<h3 id="hypervisor-type">Hypervisor Type</h3>
<p><img src="https://velog.velcdn.com/images/_030702/post/80c971e8-6710-4b1b-b11b-0c8a51285ba5/image.png" alt=""></p>
<h3 id="컨테이너">컨테이너</h3>
<p>컨테이너의 시작은 화물을 어떻게 하면 효율적으로 옮길 수 있을까 → 표준화된 컨테이너 박스를 만들고, 무엇을 넣어도 표준화된 운송선만 있으면 똑같이 세워서 효율적으로 운송할 수 있음</p>
<p>이 컨셉을 IT에 적용한 것이 컨테이너</p>
<p>소프트웨어 코드, 애플리케이션 코드가 작동하기 위해 필요한 컴포넌트와 함께 표준화된 방식으로 패키징해서, 컨테이너 엔진만 있으면 돌아갈 수 있도록 하는 구조를 컨테이너라고 함.</p>
<p>⇒ 컴퓨팅 환경 간의 이식성이 높아짐</p>
<p>가상화 레이어 없어서 실행 속도가 프로세스 실행 수준으로 빠름</p>
<p>리눅스 커널의 기능인 Namespace(자원 격리), cgroups(자원 제한)을 사용해 프로세스 단위로 격리함 </p>
<p>하이퍼 바이저로 구동된 OS는 각자 다른 Kernel을 가질 수 있음</p>
<p>컨테이너는 호스트와 동일한 커널을 사용함 </p>
<pre><code>하드웨어 → 하이퍼바이저 → 가상머신 (Guest OS Kernel + App)

하드웨어 → HostOS (Kernel) → container 엔진 (Docker) → Application</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Kubernetes k8s 접근 구조]]></title>
            <link>https://velog.io/@_030702/Kubernetes-k8s-%EC%A0%91%EA%B7%BC-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@_030702/Kubernetes-k8s-%EC%A0%91%EA%B7%BC-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sat, 14 Mar 2026 14:04:28 GMT</pubDate>
            <description><![CDATA[<pre><code>Credential(Kubeconfig/Token) 배포영역

    ㄴ Cluster

        ㄴ Namespace

            ㄴ Pod

                ㄴ container

사용자가 인증해서

    ㄴ 어느 클러스터에

        ㄴ 어느 공간에

            ㄴ 어떤 실행단위에

                ㄴ 어떤 컨테이너에 접근하느냐
</code></pre><ul>
<li><p>credential</p>
<ul>
<li><p>PaaS에서 접근할 때 가장 먼저 필요함</p>
</li>
<li><p>kubernetes API 서버에 접속 인증 정보</p>
</li>
</ul>
</li>
<li><p>cluster</p>
<ul>
<li><p>kubernetes가 실제로 돌아가는 전체 인프라</p>
</li>
<li><p>노드 (Contol Plane+Worker)의 집합<br>→ API Server/ Scheduler / Controller Manager / Nodes</p>
</li>
</ul>
</li>
<li><p>Namespace</p>
<ul>
<li><p>클러스터 내부 논리적 격리 공간</p>
</li>
<li><p>멀티팀/ 멀티서비스 분리<br>→ 이름충돌방지/ 권한 분리</p>
</li>
</ul>
</li>
<li><p>Pod</p>
<ul>
<li><p>Kubernetes의 최소 배포 단위</p>
</li>
<li><p>1개 이상의 컨테이너 묶음 
→ 동일 IP 공유, storage 공유, 같이 스케줄링 됨</p>
</li>
</ul>
</li>
</ul>
<h4 id=""></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cloud 종류 (Public, Private, Hybrid)]]></title>
            <link>https://velog.io/@_030702/Cloud-%EC%A2%85%EB%A5%98-Public-Private-Hybrid</link>
            <guid>https://velog.io/@_030702/Cloud-%EC%A2%85%EB%A5%98-Public-Private-Hybrid</guid>
            <pubDate>Sat, 14 Mar 2026 13:59:50 GMT</pubDate>
            <description><![CDATA[<h3 id="개요">개요</h3>
<p>Public Cloud, Private Cloud, Hybrid Cloud에 대해서 알아보자</p>
<h3 id="public-cloud">Public Cloud</h3>
<ul>
<li><p>누구나 함께 이용할 수 있도록 구축된 대규모 클라우드 서비스</p>
</li>
<li><p>사용자는 필요한 때에 필요한 만큼의 클라우드 자원을 할당받아 이용할 수 있도록 제공하는 서비스 방식</p>
</li>
<li><p>필요한 서버를 즉시 생성하여 이용할 수 있음</p>
</li>
<li><p>트래픽과 자원 사용량 증가 시 바로 확장이 가능한 확장성</p>
</li>
<li><p>저렴한 서비스 비용으로 인한 경제성</p>
</li>
<li><p>시스템 유지보수나 장애 대응을 위한 효율성과 같은 클라우드 서비스 장점에 최적화 된 서비스</p>
</li>
<li><p>KT cloud, Amazon AWS, IBM SoftLayer과 같이 대중적인 클라우드 서비스를 제공하는 업체들의 서비가 공용클라우드에 속하며, 일반적인 웹 서비스나 게임, 동영상 서비스가 주로 공용 클라우드 환경으로 구성됨</p>
</li>
</ul>
<h3 id="private-cloud">Private Cloud</h3>
<ul>
<li>자사 내부에 직접 클라우드 인프라를 구축한 형태</li>
<li>클라우드 인프라에 대한 직접적인 통제 권한을 가진 클라우드 서비스 구축 방식을 말함</li>
<li>사설 클라우드를 구축하는 경우에는 보안을 위해 외부의 인터넷 망과 IT 인프라를 분리하거나, 운영 회사에서 원하는 형태로 보안 시스템과 내부 IT 인프라를 구성할 수 있음</li>
<li>그러나, 공용 클라우드와 달리 구성된 여유자원이 한정되어있어 자원의 필요량이 증가하면 확장에 제약이 있을 수 있음</li>
<li>추가로, 직접 구축을 해야하기 때문에, 클라우드 인프라를 관리하기 위한 관리 인력이 필요함</li>
<li>많은 기업과 공공기관들이 보안에 대한 민감도가 높은 IT 시스템을 운영하기 때문에 사설 클라우드를 구축함</li>
<li>주로 외부의 접속이 필요없이 회사 내부 서비스를 위한 그룹웨어, ERP와 같은 서비스들이 주로 사설 클라우드 환경임</li>
</ul>
<h3 id="hybrid-cloud">Hybrid cloud</h3>
<ul>
<li>내부에 사설 클라우드를 구축하여 운영하다가 필요에 따라 외부의 공용 클라우드를 함께 이용하는 것을 일반적으로 하이브리드 클라우드라고 함.</li>
<li>여기에 기존의 물리 서버를 포함해 비즈니스 환경에 따라 공용, 사설, 물리 서버 중 두가지 이상의 클라우드를 혼용하여 구성하기도 함.</li>
<li>공용 서비스를 위한 웹, WAS 서버는 공용 클라우드를 이용하여 클라우드의 장점(비용과 확장성)에 최적화 시키고, 보안이 중요한 서버 (database와 같은 경우)들은 사설 클라우드 환경을 구성하여 외부의 직접적인 접속을 차단함</li>
<li>기존에 운영중이거나, 높은 성능을 필요로 하는 물리 서버들과 연동해 사용자의 요구에 맞는 최적의 인프라를 설계할 수 있는 장점 존재</li>
<li>외부 서비스와 보안이 모두 중요한 금융, 개인정보 취급 웹서비스, 교육, 시스템들이 하이브리드 클라우드 환경으로 구성됨</li>
</ul>
<p><img src="https://velog.velcdn.com/images/_030702/post/dfefcc92-7f49-48f3-9418-1060089a3e28/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OpenStack, Kubernetes]]></title>
            <link>https://velog.io/@_030702/OpenStack-Kubernetes</link>
            <guid>https://velog.io/@_030702/OpenStack-Kubernetes</guid>
            <pubDate>Sat, 14 Mar 2026 13:58:04 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>Kubernetes</p>
<ul>
<li>cmp의 실행 기반으로 사용됨</li>
<li>cmp 솔루션 자체가 복잡한 MSA로 되어있어 cmp 소프트웨어를 안정적으로 돌리기 위한 인프라로 OKD나 K8s를 사용하게 함<ul>
<li>k8s : 가볍고, 표준적이지만 운영환경 구축에 손이 많이 감</li>
<li>OKD : 빌드 도구(S21) 통합 모니터링, 향상된 보안 정책이 기본 포함되어있어서 CMP같은 복잡한 시스템을 올리기에 운영 부담이 적음</li>
</ul>
</li>
<li>cmp의 관리대상으로 포함됨<ul>
<li>cmp의 주 목적인 멀티 클라우드 관리 항목 중 하나로, k8s 클러스터나 okd를 클러스터를 등록해 제어하는 경우</li>
<li>cmp 화면 하나에서 aws ec2도 관리하고, 내부 서버의 okd 노드 상태도 체크하며 자원을 배분하는 형태</li>
</ul>
</li>
<li>okd vs k8s<ul>
<li>두 기술은 뿌리는 같으나 패키징이 다름</li>
<li>kubernetes (k8s) = 컨테이너 오케스트레이션의 순수 엔진<ul>
<li>엔진만 있어서 모니터링, 로깅, 보안, CI CD를 위해 별도의 오픈소스 도구를 직접 조합해야함</li>
</ul>
</li>
<li>OKD (Openshift community Distribution)<ul>
<li>k8s라는 엔진 위에 redhat이 편의도구들을 미리 다 조립해놓은 완성차 같은 플랫폼</li>
<li>Openshift의 오픈소스 버전이며, 기업의 ocp로 가기전의 상위 프로젝트</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>openstack vs kubernetes</p>
<ul>
<li>무엇을 가상화하느냐?<ul>
<li>openstack (IaaS) : 컴퓨터(VM) 자체를 빌려줌<ul>
<li>CPU 4코어, 램 16GB, 윈도우 설치된 가상 컴퓨터 한대를 통째로 만드는 도구</li>
<li>지금 업무에서의 VMWare와 역할이 거의 같음</li>
<li>VMWare 유료, OpenStack 무료</li>
</ul>
</li>
<li>쿠버네티스(PaaS/Container Platform) : 컨테이너인 Pod를 빌려줌<ul>
<li>OS가 깔린 컴퓨터를 통째로 만드는게 아니라, 그 위에 돌아가는 도커 컨테이너만 띄워줌</li>
</ul>
</li>
<li>정리하자면 오픈스택은 운동장(컴퓨터 자원)을 만들고 쿠버네티스는 그 운동장  위에서 축구시합이 돌아가게 관리한다고 이해하면 될듯</li>
</ul>
</li>
<li>사용 목적<ul>
<li>쿠버네티스를 사용하는 주된 이유는 도커 컨테이너를 관리하고 옮기기 위해서가 맞음</li>
<li>오픈스택은 목적이 조금 더 넓음</li>
<li>쿠버네티스 = 도커 컨테이너 (소스코드와 디비)를 어느서버에 띄울까? 서버가 죽으면 옆으로 옮겨야지</li>
<li>오픈스택 = 쿠버네티스가 돌아갈 수 있는 가상서버 10대를 만들어야지 or DB를 컨테이너가 아니라 성능을 위해 가상서버에 직접 설치해야지</li>
<li>현업에서<ul>
<li>물리서버 → 오픈스택으로 가상 서버 → 그 가상 서버 위에 쿠버네티스 설치 → 그 위에 도커 배포</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring에서 실제 Client IP를 가져오는 방법 (getRemoteAddr를 쓰면 안 되는 이유)]]></title>
            <link>https://velog.io/@_030702/Spring%EC%97%90%EC%84%9C-%EC%8B%A4%EC%A0%9C-Client-IP%EB%A5%BC-%EA%B0%80%EC%A0%B8%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95-getRemoteAddr%EB%A5%BC-%EC%93%B0%EB%A9%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@_030702/Spring%EC%97%90%EC%84%9C-%EC%8B%A4%EC%A0%9C-Client-IP%EB%A5%BC-%EA%B0%80%EC%A0%B8%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95-getRemoteAddr%EB%A5%BC-%EC%93%B0%EB%A9%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sun, 08 Mar 2026 13:44:13 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>회사 코드에서 Client의 IP 주소를 가져오는 공통 유틸리티 메서드를 발견했다.
처음에는 단순히 request.getRemoteAddress()를 사용하면 클라이언트 IP를 얻을 수 있을 것이라고 생각했지만, 실제 코드를 살펴보니 여러 HTTP 헤더를 확인하는 비교적 복잡한 로직이 구현되어 있었다.</p>
<p>이러한 구현 방식이 필요한 이유를 살펴보던 중, 프록시 서버나 로드밸런서를 거치는 환경에서는 getRemoteAddress()만으로는 실제 사용자 IP를 확인하기 어렵다는 사실을 알게 되었다.</p>
<p>특히 최근의 서비스 환경에서는 <code>Reverse Proxy</code>, <code>Load Balancer</code>, <code>API Gateway</code> 등 여러 네트워크 계층을 거쳐 요청이 전달되기 때문에, 실제 클라이언트 IP를 확인하기 위해서는 추가적인 처리가 필요하다.</p>
<p>따라서 이번 글에서는 다음 내용을 정리해보고자 한다.</p>
<ol>
<li><p>request.getRemoteAddress()로 실제 사용자 IP를 알 수 없는 이유</p>
</li>
<li><p>Proxy / Load Balancer 환경에서 요청이 전달되는 방식</p>
</li>
<li><p>X-Forwarded-For 헤더를 활용한 실제 Client IP 확인 방법</p>
</li>
<li><p>실무에서 Client 정보를 처리하는 일반적인 방식</p>
</li>
</ol>
<h3 id="왜-getremoteaddress를-사용하지-않을까">왜 getRemoteAddress()를 사용하지 않을까?</h3>
<p>보통의 클라이언트 IP를 가져올때는 <code>request.getRemoteAddress()</code>를 사용한다.
하지만 실제 서비스 환경에서는 이 방법만으로는 사용자의 실제 IP를 확인하기 어려운 경우가 많다.</p>
<p>그 이유는 서비스 구조 사이에 여러 네트워크 장비가 존재하기 때문이다.</p>
<p>예를 들어 다음과 같은 구조를 생각해볼 수 있다.</p>
<pre><code>Client 
   ↓
Load Balancer (L4/L7)
   ↓
Nginx (Reverse Proxy)
   ↓
Spring Boot Server</code></pre><p>이 상태에서 서버가 <code>request.getRemoteAddress()</code>를 호출하면 실제 사용자 IP가 아니라 바로 앞 단계 장비의 IP가 반환된다.</p>
<p>즉 다음 중 하나가 반환될 가능성이 높다.</p>
<ul>
<li><p><code>Load Balancer IP</code></p>
</li>
<li><p><code>Reverse Proxy IP</code></p>
</li>
</ul>
<p>따라서 서버 입장에서는 실제 사용자 IP를 직접 알 수 없다.</p>
<h3 id="그렇다면-실제-client-ip는-어디에-있을까">그렇다면, 실제 Client IP는 어디에 있을까?</h3>
<p>프록시나 로드밸런서를 거칠 경우, 실제 사용자 <code>IP</code>는 <code>HTTP Header</code>를 통해 전달된다.</p>
<p>대표적으로 다음과 같은 헤더들이 사용된다.</p>
<h4 id="주요-client-ip-관련-헤더">주요 <code>Client IP</code> 관련 헤더</h4>
<ul>
<li>X-Forwarded-For</li>
<li><blockquote>
<p>가장 표준적으로 사용되는 헤더이며, 프록시 서버를 거칠 때마다 IP가 누적된다.</p>
</blockquote>
</li>
</ul>
<pre><code>X-Forwarded-For: clientIP, proxy1, proxy2

Proxy-Client-IP : Apache 등 특정 프록시 서버에서 사용되는 헤더

WL-Proxy-Client-IP : WebLogic 서버에서 사용하는 헤더

HTTP_CLIENT_IP : PHP 환경에서 사용되는 경우가 있음

마지막으로 위 헤더에 값이 없는 경우에만

request.getRemoteAddress()를 fallback 방식으로 사용하는 것이 일반적이다.</code></pre><p>📌 ClientUtil 같은 유틸리티가 필요한 이유</p>
<p>이러한 이유 때문에 실무에서는 단순히 <code>getRemoteAddress()</code>를 호출하기보다
여러 헤더를 순차적으로 확인하는 공통 유틸리티 메서드를 사용하는 경우가 많다.</p>
<p>예를 들어 다음과 같은 방식이다.</p>
<pre><code>X-Forwarded-For

Proxy-Client-IP

WL-Proxy-Client-IP

HTTP_CLIENT_IP

request.getRemoteAddress()</code></pre><p>이 순서대로 값을 확인하여 실제 사용자 IP를 찾아내는 방식이다.</p>
<h3 id="client-정보-파싱-ip--os--browser">Client 정보 파싱 (IP / OS / Browser)</h3>
<p>일부 서비스에서는 사용자 IP뿐 아니라 <code>OS</code>, <code>Browser</code>, <code>Device</code>와 같은 정보도 함께 파싱하기도 한다.</p>
<p><strong><em>하지만</em></strong> 최근 실무에서는 이러한 로직을 직접 구현하기보다는 다른 방식으로 처리하는 경우가 많다.</p>
<p>브라우저와 디바이스 종류가 계속 증가하기 때문에
개발자가 직접 if-else 로직으로 관리하기에는 유지보수 비용이 너무 크기 때문에 그렇다고 한다.</p>
<h3 id="실무에서의-일반적인-처리-방식">실무에서의 일반적인 처리 방식</h3>
<p>최근 서비스 환경에서는 다음과 같은 방식이 많이 사용된다.</p>
<pre><code>1. API Gateway에서 처리

Kong API Gateway
Spring Cloud Gateway

Gateway에서 Client 정보를 파싱하여 전달</code></pre><pre><code>2. WAF (Web Application Firewall)

AWS WAF

보안 장비에서 Client 정보를 분석</code></pre><pre><code>3. 전문 라이브러리 사용

uap-java

User-Agent 파싱을 전문적으로 처리</code></pre><h3 id="정리">정리</h3>
<p>로드밸런서나 프록시 서버가 존재하는 환경에서는
<code>request.getRemoteAddress()</code>만으로 실제 사용자 IP를 확인하기 어렵다.</p>
<p>이러한 환경에서는 요청이 전달되는 과정에서 <code>HTTP Header</code>에 실제 <code>Client IP</code>가 포함되기 때문에
<code>X-Forwarded-For</code>와 같은 헤더를 활용하여 <code>IP</code>를 확인해야 한다.</p>
<p>따라서 실무에서는 다양한 헤더를 순차적으로 확인하는 공통 유틸리티 메서드를 사용하거나,
<code>API Gateway</code>나 보안 장비에서 해당 정보를 처리하는 방식이 일반적으로 사용된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MSA에서 HttpSession을 사용하면 안 되는 이유]]></title>
            <link>https://velog.io/@_030702/MSA-JWT-token-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@_030702/MSA-JWT-token-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Sun, 08 Mar 2026 13:35:33 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>회사 프로젝트의 코드를 살펴보던 중 <code>Service</code>에서 <code>HttpSession</code>을 사용해 RSA 키를 생성하고 관리하는 로직을 발견했다.
하지만 현재 프로젝트는 <code>Microservices Architecture(MSA)</code> 구조로 구성되어 있으며, MSA에서는 일반적으로 Stateless(무상태성)을 지향한다는 점에서 의문이 생겼다.</p>
<p><code>HttpSession</code>은 특정 서버의 메모리에 사용자 상태를 저장하는 방식인데, 서버가 여러 대로 구성된 환경에서는 요청이 다른 서버로 전달될 수 있기 때문에 세션 기반 로직이 정상적으로 동작하지 않을 가능성이 있다.</p>
<p>이러한 의문을 계기로 다음과 같은 내용을 정리해보고자 한다.</p>
<ol>
<li><p>MSA 환경에서 HttpSession 사용이 왜 문제가 될 수 있는지</p>
</li>
<li><p>세션 기반 인증과 JSON Web Token(JWT) 기반 인증의 차이</p>
</li>
<li><p>MSA 환경에서 인증 및 암호화 키 관리 대안</p>
</li>
<li><p>RSA 키를 세션 단위로 생성하는 방식의 한계와 대안</p>
</li>
</ol>
<p>이를 통해 현재 코드 구조가 어떤 환경을 가정하고 작성되었는지 이해하고, MSA 환경에서는 어떤 방식으로 개선할 수 있을지 정리해보고자 한다.</p>
<h3 id="기술적-고민을-시작한-이유">기술적 고민을 시작한 이유</h3>
<p>현재 회사 코드를 살펴보면서 한 가지 공통적인 설계 방향을 발견할 수 있었다.</p>
<p><code>인터페이스 / Impl 분리</code>
→ 구현체를 분리하여 유지보수와 확장성을 높이기 위한 객체지향 설계</p>
<p><code>MyBatis의 BaseVO / Interceptor 사용</code>
→ 반복되는 로직을 줄이고 공통 기능을 재사용하기 위한 구조</p>
<p><code>Stateless 구조 지향</code>
→ 서비스 확장성과 장애 대응을 고려한 MSA 아키텍처의 핵심 원칙</p>
<p>결국 이러한 기술적 선택들은 단순한 구현 방식의 차이가 아니라,
“어떻게 하면 시스템을 더 쉽게 유지보수하고, 안정적으로 확장할 수 있을까?”
라는 고민에서 출발한다고 볼 수 있다.</p>
<p>따라서 나도 동일한 관점을 갖고, 발전시키고 싶은 부분에 대해 고민을 시작하였다.</p>
<h3 id="msa-환경에서-httpsession-사용이-왜-문제가-될-수-있는지">MSA 환경에서 HttpSession 사용이 왜 문제가 될 수 있는지</h3>
<p>현재 <code>Service</code> 코드에서는 <code>HttpSession</code>을 사용하여 RSA 키를 생성하고 관리하고 있다.
하지만 현재 프로젝트는 <code>Microservices Architecture(MSA)</code> 구조로 구성되어 있으며, MSA는 기본적으로 Stateless(무상태성)을 지향한다.</p>
<p><code>Stateless</code> 구조에서는 특정 서버가 사용자 상태를 기억하지 않는다. 
즉, 어떤 서버가 요청을 처리하더라도 동일한 결과를 반환할 수 있어야 한다.</p>
<p>하지만 <code>HttpSession</code>은 서버 메모리에 사용자 상태를 저장하는 방식이기 때문에, 서버가 여러 대로 구성된 환경에서는 문제가 발생할 수 있다. 
예를 들어, 사용자의 요청이 로드밸런서를 통해 다른 서버로 전달될 경우 해당 서버에는 기존 세션 정보가 존재하지 않기 때문에 인증이나 암호화 과정에서 오류가 발생할 수 있다.</p>
<p>이러한 이유로 <code>MSA</code> 환경에서는 <code>HttpSession과</code> 같은 서버 의존적인 상태 관리 방식보다는, <code>JSON Web Token(JWT)</code>이나 공통 저장소 기반의 상태 관리 방식을 사용하는 경우가 많다. 
예를 들어 인증 정보나 암호화 키를 <code>Redis</code>와 같은 중앙 저장소에서 관리하면 어떤 서버가 요청을 처리하더라도 동일한 데이터를 참조할 수 있다. 
또한 보안 키 관리가 필요한 경우에는 <code>AWS Key Management Service(KMS)</code>와 같은 키 관리 시스템을 사용하는 방법도 있다.</p>
<p>현재 코드에서 <code>HttpSession</code>을 사용하는 이유는 몇 가지 가능성을 생각해볼 수 있다.</p>
<ol>
<li><p>초기에는 단일 서버 환경을 가정하고 작성된 로직일 가능성</p>
</li>
<li><p><code>MSA</code>로 전환되는 과정에서 레거시 코드가 그대로 유지되었을 가능성</p>
</li>
</ol>
<p><code>MSA</code> 환경에서 안정적으로 확장하기 위해서는 세션 기반 상태 관리보다는 공통 저장소 기반의 상태 관리 구조로 전환하는 것이 필요하다.</p>
<h3 id="세션-기반-인증과-json-web-tokenjwt-기반-인증의-차이">세션 기반 인증과 JSON Web Token(JWT) 기반 인증의 차이</h3>
<p><code>SESSION</code>은 서버에서 기억해야한다면, <code>JWT</code>는 클라이언트가 기억하는것이다.</p>
<h3 id="msa-환경에서-인증-및-암호화-키-관리-대안">MSA 환경에서 인증 및 암호화 키 관리 대안</h3>
<h4 id="예시-1-서버-다중화-환경에서의-세션-문제">예시 1. 서버 다중화 환경에서의 세션 문제</h4>
<p>현재 서버가 다중화된 환경이라고 가정해보자.
단일 서버 환경이라면 세션에 RSA 키를 저장하더라도 언제든지 동일한 서버에서 해당 키를 사용할 수 있다.</p>
<p>하지만 <code>Microservices Architecture(MSA) 환경</code>에서는 상황이 달라진다.</p>
<p>예를 들어 1번 서버에서 <code>generateRsaKey</code>를 호출하여 세션에 RSA 키를 저장했다고 가정해보자.
이후 암호화 요청이 발생했을 때 로드밸런서에 의해 요청이 2번 서버로 전달될 수 있다.
이 경우 2번 서버에는 해당 세션 정보가 존재하지 않기 때문에 RSA 키를 찾을 수 없고, 인증 또는 복호화 과정이 실패할 수 있다.</p>
<p>이 문제를 해결하기 위해 다음과 같은 방법을 사용할 수 있다.</p>
<ul>
<li><p><code>Sticky Session</code></p>
</li>
<li><p><code>Session Clustering</code></p>
</li>
</ul>
<p>하지만 이러한 방식은 특정 서버에 요청이 고정되거나 세션 동기화가 필요해지기 때문에 <code>MSA</code>의 유연성과 확장성을 떨어뜨릴 수 있다.</p>
<h4 id="예시-2-서비스-간-세션-공유-문제">예시 2. 서비스 간 세션 공유 문제</h4>
<p>또 다른 문제는 서비스 간 세션 공유가 어렵다는 점이다.</p>
<p>예를 들어 인증 서비스에서 <code>HttpSession</code>에 RSA 키를 저장했다고 가정하자.
하지만 해당 키는 다른 서비스에서 접근할 수 없다.
각 서비스는 서로 다른 물리적 서버나 컨테이너에서 실행되기 때문이다.</p>
<p>결과적으로 서비스 간에 암호화된 데이터를 주고받아야 하는 상황에서는 세션 기반 키 관리 방식이 의미를 잃게 된다.</p>
<h4 id="msa에서-권장되는-방식">MSA에서 권장되는 방식</h4>
<p>이러한 이유로 <code>MSA</code>에서는 서버 세션에 의존하는 방식 대신 <code>무상태(Stateless)</code> 구조를 지향한다.</p>
<p>무상태 구조에서는 서버가 사용자 상태를 기억하지 않기 때문에
어떤 서버가 요청을 처리하더라도 동일한 결과를 반환할 수 있어야 한다.</p>
<p>따라서 인증 및 상태 관리를 위해 다음과 같은 방식이 사용된다.</p>
<p><code>JSON Web Token(JWT)</code>
→ 인증 정보를 서버 세션이 아닌 토큰 자체에 포함하여 클라이언트가 보관</p>
<p><code>Redis 기반 중앙 저장소</code>
→ 인증 정보나 암호화 키를 모든 서버가 접근할 수 있는 공통 저장소에 관리</p>
<h3 id="rsa-암호화가-필요한-경우">RSA 암호화가 필요한 경우</h3>
<p>만약 서비스 로직에서 <code>RSA 암호화</code>가 필요한 경우에는
<code>HttpSession</code> 대신 공유 저장소 기반 구조를 사용하는 것이 바람직하다.</p>
<p>예를 들어 Redis 기반 공유 세션, 공통 데이터베이스 기반 키 저장 또는 <code>Key Management System(KMS)</code> 과 같은 방식을 활용하면 어떤 서버가 요청을 처리하더라도 동일한 키에 접근할 수 있다.</p>
<h3 id="rsa-키를-세션-단위로-생성하는-방식의-한계와-대안">RSA 키를 세션 단위로 생성하는 방식의 한계와 대안</h3>
<p>현재 코드에서는 <code>HttpSession</code>을 사용하여 RSA 키를 생성하고 세션에 저장하는 방식으로 암호화를 처리하고 있다.
하지만 이러한 방식은 MSA 환경에서는 여러 가지 한계를 가진다.</p>
<p>먼저, 세션 기반으로 RSA 키를 관리할 경우 특정 서버의 메모리에 키가 저장되기 때문에 서버가 여러 대로 구성된 환경에서는 동일한 키를 공유하기 어렵다.
사용자의 요청이 로드밸런서를 통해 다른 서버로 전달될 경우 해당 서버에는 기존 세션 정보가 존재하지 않기 때문에 암호화 또는 복호화 과정에서 문제가 발생할 수 있다.</p>
<p>또한 세션마다 RSA 키를 생성하는 방식은 성능 측면에서도 비효율적일 수 있다.
RSA 키 생성은 비교적 비용이 큰 연산이기 때문에 요청이 많아질수록 서버에 불필요한 부하가 발생할 수 있다.</p>
<p>실제 서비스 환경에서는 애플리케이션 레벨에서 RSA 암호화를 직접 처리하기보다는, <code>HTTPS(SSL/TLS)</code>와 같은 표준 보안 프로토콜을 활용하는 것이 일반적이다.
<code>HTTPS</code>는 전송 구간에서 이미 강력한 암호화를 제공하기 때문에 대부분의 경우 애플리케이션에서 별도의 RSA 키를 생성하여 관리할 필요가 없다.</p>
<p>만약 서비스 내부 로직에서 암호화 키 관리가 필요하다면 다음과 같은 방법을 고려할 수 있다.</p>
<p>공통 저장소 기반 키 관리
→ <code>Redis</code>와 같은 중앙 저장소를 활용하여 여러 서버가 동일한 키에 접근할 수 있도록 구성</p>
<p>키 관리 시스템(KMS) 활용
→ 보안 키를 전문적으로 관리하는 시스템을 통해 안전하게 키를 저장하고 사용할 수 있음</p>
<p>결과적으로, MSA 환경에서는 특정 서버의 세션에 의존하는 방식보다는 모든 서버가 동일한 정보를 공유할 수 있는 구조를 사용하는 것이 중요하다.
또한 가능하다면 애플리케이션 코드에서 직접 암호화를 구현하기보다는 인프라 레벨의 표준 보안 프로토콜을 활용하는 것이 더 안정적이고 효율적인 방법이라고 볼 수 있다.</p>
<h3 id="정리">정리</h3>
<p>이번 내용을 정리해보면, MSA 환경에서는 서버 세션에 의존하는 방식이 확장성과 안정성 측면에서 한계를 가질 수 있다. 
따라서 인증 정보나 암호화 키는 특정 서버에 종속되지 않는 방식으로 관리하는 것이 중요하며, 
<code>Redis</code>와 같은 공통 저장소나 <code>KMS</code>와 같은 키 관리 시스템을 활용하는 방법을 고려할 수 있다. 
또한 대부분의 경우 애플리케이션 레벨의 암호화보다는 <code>HTTPS와 같은 표준 보안 프로토콜</code>을 활용하는 것이 더 효율적인 선택이 될 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Nexus]]></title>
            <link>https://velog.io/@_030702/Nexus</link>
            <guid>https://velog.io/@_030702/Nexus</guid>
            <pubDate>Sun, 08 Mar 2026 13:15:14 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>회사에서 IntelliJ 환경을 초기 설정한 후 프로젝트를 빌드하는 과정에서 Nexus에 접근하지 못해 빌드가 실패하는 문제가 발생했다.</p>
<p>의존성 라이브러리를 내려받는 과정에서 문제가 발생한 것으로 보였고, 이를 계기로 Sonatype Nexus Repository(Nexus)가 어떤 역할을 하는지 알아보게 되었다.</p>
<h3 id="nexus-repository란">Nexus Repository란?</h3>
<ul>
<li>메이븐 사용하면 pom.xml 파일로 jar 파일 다운받아옴</li>
<li>Nexus는 메이븐에서 사용할 수 있는 Repository.</li>
<li>외부 의존을 덜고, local nexus(cache)로 사용함으로써 빠르게 라이브러리 끌어올 수 있음.<ul>
<li>한번 다운로드받은 dependency는 로컬에 저장되나, 컴퓨터를 포맷하거나 새롭게 시작하는 상황에서는 설정을 해야함</li>
</ul>
</li>
</ul>
<h3 id="nexus-repository-특징">Nexus Repository 특징</h3>
<ul>
<li>회사/단체의 화이트 리스트로 인해 외부 repository에 접속하기 어려운 프록스 역할</li>
<li>비상시 외부 인터넷이 느리거나 repository가 다운되는 상황에서도 빠른 다운 가능</li>
<li>현재 maven에 올라와있지 않은 자료들을 효율적으로 관리 가능</li>
<li>서버에도 동일한 설정을 해줘야함으로 서버 구조 복잡하면 잔업도 늘어남</li>
<li>예외 파일로 인한 설정이 줄어들어 일관성 증가함</li>
</ul>
<h3 id="주의사항">주의사항</h3>
<p>한번 다운 받은 Nexus는 로컬에 반영구적으로 저장되나, 새롭게 시작하는 상황에서는 설정 필요</p>
<h3 id="참고자료">참고자료</h3>
<p><a href="https://m.blog.naver.com/qhdqhdekd261/221827574154">https://m.blog.naver.com/qhdqhdekd261/221827574154</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA & MyBatis]]></title>
            <link>https://velog.io/@_030702/JPA-MyBatis</link>
            <guid>https://velog.io/@_030702/JPA-MyBatis</guid>
            <pubDate>Sun, 08 Mar 2026 13:13:07 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>지금까지 프로젝트를 진행하면서 데이터베이스를 사용할 때는 주로 Jakarta Persistence API(JPA)를 사용해왔다. 
하지만 이번 프로젝트에서는 처음으로 MyBatis를 사용하게 되었다.
MyBatis는 SQL을 직접 작성하여 데이터베이스와 연동하는 방식이라는 점에서 JPA와 차이가 있다. 
이번 글에서는 MyBatis의 기본 개념과 동작 방식에 대해 정리해보고자 한다.</p>
<h3 id="mybatis란-">MyBatis란 ?</h3>
<ul>
<li><p>자바 애플리케이션에서 데이터 베이스와 상호작용하는데 사용되지만, 접근방식과 특징이 많이 다름</p>
</li>
<li><p>여기서의 상호작용 : 저장/조회</p>
</li>
<li><p>SQL Mapper 기술 </p>
</li>
<li><p>개발자가 작성한 SQL 실행 결과를 객체에 매핑</p>
<pre><code>💡 Mybatis</code></pre></li>
<li><p>SQL Mapper 프레임 워크</p>
</li>
<li><p>직접 SQL 작성</p>
</li>
<li><p>반복적인 SQL 작성 필요</p>
</li>
<li><p>복잡한 SQL 제어에 유리</p>
<pre><code></code></pre></li>
</ul>
<h3 id="jpa란-">JPA란 ?</h3>
<ul>
<li><p>ORM (Object Relational Mapping) </p>
</li>
<li><p>DB에 자동으로 매핑해주는 프레임 워크</p>
</li>
</ul>
<pre><code>💡 JPA

- Java persistence API, 객체 - 관계 매핑 표준
- JPQL 또는 메서드 이름 기반 쿼리
- 영속성 컨텍스트, JPQL 학습 필요
- CRUD 자동화로 생산성 높음
- 객체 중심 설계에 적합</code></pre><h3 id="jpa-vs-mybatis-아키텍처-비교">JPA vs MyBatis 아키텍처 비교</h3>
<pre><code class="language-java">[JPA]
Controller (REST API 받음)
    ↓
Service (비즈니스 로직)
    ↓
Repository (인터페이스만, 구현체 자동 생성)
    ↓
Entity (객체 ↔ 테이블 자동 매핑)
    ↓
Database</code></pre>
<pre><code>[MyBatis]
Controller (REST API 받음)
    ↓
Service (비즈니스 로직)
    ↓
Mapper (인터페이스) + XML (SQL 작성)
    ↓
DTO/VO (데이터 전달 객체) -&gt; model/도메인/dto, vo 존재 
* DAO는 DB접근담당이고
* DTO는 계층간 데이터 전달용
    ↓
Database</code></pre><h3 id="jpa-특징">JPA 특징</h3>
<ul>
<li>JPA = Java Persistence API는 자바 표준 ORM(Object - Relational Mapping) 기술</li>
<li>객체지향 프로그래밍 방식으로 데이터베이스와 상호작용 할 수 있도록 설계</li>
<li>JPA를 사용하면 SQL 대신 <code>Entity</code>를 통해 데이터 처리 가능</li>
</ul>
<ul>
<li>대표 구현체 : Hibernate, EclipseLink, OpenJPA</li>
<li>특징 :<ul>
<li>데이터베이스 테이블과 자바 객체를 매핑해 객체 지향적으로 데이터 관리</li>
<li>JPQL(Java Persistence Query Language) 라는 쿼리 언어 사용</li>
<li>영속성 컨텍스트 (Persistence Context) 를 통해 데이터 변경 사항 자동으로 추적</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Entity 
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
}</code></pre>
<h3 id="mybatis-특징">MyBatis 특징</h3>
<ul>
<li>SQL Mapper 프레임 워크, SQL 쿼리를 직접 작성하면서도 자바 객체와 매핑할 수 있는 기능 제공</li>
<li>SQL 작성의 자유도 보장, 복잡한 쿼리나 데이터베이스 특화 기능을 활용하기 좋음</li>
</ul>
<aside>

<ul>
<li>SQL을 직접 제어할 수 있어서 복잡한 쿼리 작성에 유리</li>
<li>XML 혹은 어노테이션 기반으로 SQL 관리</li>
<li>동적 쿼리를 지원하여 조건에 따라 SQL 유연하게 변경할 수 있음</aside>

</li>
</ul>
<pre><code class="language-java">&lt;select id=&quot;findUserById&quot; parameterType=&quot;Long&quot; resultType=&quot;User&quot;&gt;
    SELECT * FROM users WHERE id = #{id}
&lt;/select&gt;</code></pre>
<ul>
<li>JDBC 프로그래밍 단순화하고 불필요한 Boilerplate 코드 제거하고 Java 소스코드에서 SQL문을 별도의 XML로 저장해 이를 연결함</li>
</ul>
<ul>
<li>동적 SQL 생성 기능<ul>
<li>MyBatis 는 Boilerplate 코드 제거 및 SQL 문 분리 외에도 동적인 SQL 생성 기능을 제공해 프로그램 실행 중에 입력되는 파라미터에 따라 서로 다른 SQL문을 동적으로 생성함</li>
</ul>
</li>
</ul>
<h3 id="mybatis-사용-순서와-예시">MyBatis 사용 순서와 예시</h3>
<ol>
<li><p>스프링 프로젝트 생성 후 필요한 의존성 추가 </p>
<ul>
<li>아마 미리 추가 되어있었을 것</li>
<li>spring-core</li>
<li>spring-jdbc</li>
<li>mybatis</li>
<li>mybatis-spring</li>
</ul>
</li>
<li><p>DB 설정</p>
<ul>
<li><p>JDBC로 DB연결을 하기에 DB 연결 정보 설정해야함</p>
</li>
<li><p>properties나 XML에 DataSource 등록</p>
<pre><code class="language-java">&lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
 &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.jdbc.Driver&quot;/&gt; // mysql 드라이버
 &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/mydatabase&quot;/&gt; // mysql 사용
 &lt;property name=&quot;username&quot; value=&quot;username&quot;/&gt; // 아이디 
 &lt;property name=&quot;password&quot; value=&quot;password&quot;/&gt; // 비밀번호 
&lt;/bean&gt;</code></pre>
</li>
</ul>
</li>
<li><p>MyBatis 설정</p>
<ul>
<li><p>MyBatis XML 설정 파일 만들고 필요한 설정 추가</p>
<pre><code class="language-java">&lt;bean id=&quot;sqlSessionFactory&quot; class=&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;&gt;
 &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;
 &lt;property name=&quot;configLocation&quot; value=&quot;classpath:mybatis-config.xml&quot;/&gt;
&lt;/bean&gt;

&lt;bean id=&quot;sqlSession&quot; class=&quot;org.mybatis.spring.SqlSessionTemplate&quot;&gt;
 &lt;constructor-arg ref=&quot;sqlSessionFactory&quot;/&gt;
&lt;/bean&gt;</code></pre>
</li>
</ul>
</li>
<li><p>MyBatis Mapper 작성과 등록</p>
<ul>
<li><p>Mapper = DB 쿼리 ↔ 자바 메서드()를 매핑하는 역할</p>
</li>
<li><p>데이터 베이스에 접근하기 위한 SQL 쿼리를 작성하고, 이를 실행하는 자바 메서드 정의함</p>
<p>⇒ 정리하자면 XML에 SQL 쿼리들을 작성할 건데 그 SQL이 연결된 메서드를 정의할 파일 </p>
<p>ex) CRUD 쿼리문을 XML에 작성하고. InsertItems(), Getitems(), modifyItems(), DeleteItems() 메서드가 있는 인터페이스 만들거임. 그리고 그 클래스를 Mapper라는 명칭으로 부르자 !</p>
<pre><code class="language-java">@Mapper // 등록 
// 작성
public interface UserMapper {
 User getUserById(int id);
 void insertUser(User user);
 void updateUser(User user);
 void deleteUser(int id);
}</code></pre>
</li>
</ul>
</li>
<li><p>Mapper 인터페이스와 연결될 XML 작성</p>
<ul>
<li><p>Mapper 메서드() 별로 SQL 쿼리문을 작성함</p>
<pre><code class="language-xml">&lt;mapper namespace=&quot;com.exmaple.mapper.UserMapper&quot;&gt;
  &lt;select id=&quot;getUserById&quot; parameterTytpe=&quot;int&quot; resultType=&quot;com.example.model.User&quot;&gt;
  SELECT * FROM users WHERE id = #{id}
  &lt;/select&gt;
  &lt;insert id=&quot;insertUser&quot; parameterType=&quot;com.example.model.User&quot;&gt;    
  INSERT INTO users (name, email) VALUES (#{name}, #{email}) 
  &lt;/insert&gt;  

  &lt;!-- 다른 메서드들에 대한 쿼리도 추가할 수 있다 --&gt;

&lt;/mapper&gt;</code></pre>
</li>
</ul>
</li>
<li><p>Mapper 사용</p>
<ul>
<li><p>Mapper 인터페이스를 스프링 Bean으로 등록하고 주입받아 사용함</p>
<pre><code class="language-java">@Service
public class UserSerivce {
  priate final UserMapper userMapper;

  public UserService(UserMapper userMapper){
      this.userMapper = userMapper;
  }

// 유저 조회
public User getUsedById(int id){
   return userMapper.getUserById(id);
}
// 유저 등록
public User insertUser(User user){
   return userMapper.insertUser(user);
}</code></pre>
</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Maven & Gradle]]></title>
            <link>https://velog.io/@_030702/Maven-Gradle</link>
            <guid>https://velog.io/@_030702/Maven-Gradle</guid>
            <pubDate>Sun, 08 Mar 2026 13:02:06 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>현재까지 프로젝트를 진행하며 프로젝트 관리 도구로 Gradle만 사용해봤는데, 현재 회사 프로젝트에서는 Maven으로 프로젝트 관리 도구를 사용하기에 Gradle, Maven이 무슨 역할을 하는지 알아보고, 이 둘을 비교해보려고 한다.</p>
<h3 id="maven-이란">Maven 이란?</h3>
<ul>
<li>자바용 프로젝트 관리 도구 → 프로젝트를 빌드, 패키지, 배포등의 역할을 수행하고 각종 라이브러리를 관리함</li>
<li>아파치 라이센스로 배포되는 오픈소스 소프트웨어 (Ant 대안)</li>
<li>2개의 설정 파일을 통해 관리 가능 → <code>setting.xml</code> <code>pom.xml</code><ul>
<li>setting.xml 은 메이븐 툴과 관련 설정 파일</li>
<li>pom.xml<ul>
<li>자바 프로젝트의 빌드 툴로 메이븐을 설정했다면 생성될 것</li>
<li>프로젝트에 필요한 라이브러리를 정의하는 공간 (해당 라이브러리가 작동하는데 필요한 다른 라이브러리 자동으로 설치</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="maven-빌드-순서-life-cycle">Maven 빌드 순서 (Life Cycle)</h4>
<ul>
<li>메이븐은 미리 정의하고 있는 빌드 순서가 있음. → <code>라이프 사이클</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/_030702/post/1a03d70f-dbeb-44e5-9337-a91154ea0f0b/image.png" alt=""></p>
<ol>
<li>Default(=Build) : 소스코드 컴파일. 컴파일 성공하면 target/classes 폴더가 만들어지고 컴파일 된 class 파일 생성<ul>
<li>target 폴더 = 빌드 결과물이 위치</li>
<li>즉 최종적으로 배포 가능한 WAR/JAR 폴더가 생성됨 (6번 참고)</li>
</ul>
</li>
<li>Clean : 빌드 후 생성된 target 삭제 → 생성된 파일 삭제한다</li>
<li>Validate : 프로젝트가 올바른지 확인하고, 필요한 모든 정보를 사용할 수 있는지 확인</li>
<li>Compile : 프로젝트의 소스코드를 컴파일 하는 단계</li>
<li>Test : 유닛 (단위) 테스트를 수행하는 단계 (테스트 실패시 빌드로 처리, 스킵 가능)</li>
<li>Package : 실제 컴파일 된 소스코드와 리소스들을 <code>jar</code>, <code>war</code> 과 같은 파일등의 배포를 위한 패키지로 만드는 단계 → 타켓 directory 만듦</li>
<li>Verify : 통합 테스트 결과에 대한 검사를 실행하여 품질 기준을 충족하는지 확인</li>
<li>Install : 패키지를 로컬 저장소에 설치하는 단계, Package와 다른 점은 로컬 저장소에 있는 다른 프로젝트 들이 접근이 가능하다.</li>
<li>Site : 프로젝트 문서와 사이트 작성, 생성하는 단계</li>
<li>Deploy : 만들어진 package를 원격 저장소에 release 하는 단계</li>
</ol>
<p>→ 코드를 수정한 후 package하면 오래걸림 </p>
<p>→ 오래걸리니 target 파일의 코드를 바꾸게 하면 package를 할 필요가 없음</p>
<p>→ 무지성 clean → package</p>
<h4 id="maven-라이브러리-저장-장소-pomxml">Maven 라이브러리 저장 장소 (pom.xml)</h4>
<ul>
<li>maven은 필요한 라이브러리를 <code>pom.xml</code> 에 저장함 ⇒ 프로젝트 모델링</li>
</ul>
<pre><code class="language-xml">&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
&lt;groupId&gt;com.회사이름&lt;/groupId&gt;
&lt;artifactId&gt;모듈이름&lt;/artifactId&gt; &lt;!-- jar, war 파일 이름을 의미 --&gt;
&lt;version&gt;artifact의 버전&lt;/version&gt; &lt;!-- 0.0.1-SNAPSHOT = 아직 개발 중 --&gt;
&lt;packaging&gt;artifact의 포맷&lt;/packaging&gt;

&lt;properties&gt;...&lt;/properties&gt; &lt;!-- 값을 가진 상수 ${propertyname} 이런식으로 참조 가능  --&gt;

&lt;repositories&gt;...&lt;/repositories&gt; &lt;!-- 라이브러리 받아올 저장소 지정  --&gt;

&lt;dependencies&gt;...&lt;/dependencies&gt;
&lt;!-- 의존성은 POM에서 가장 중요함 (일관성 있는 프로젝트에 필수)
Maven은 dependencies 요소에 명시된 의존성을 자동 다운하고,
해당 의존성이 의존하는 또다른 의존성(transitive dependencies) 까지 가져 옴
외부에서 jar 파일을 일일이 다운받아 관리하던 것에비해 매우 개선됨--&gt;

&lt;build&gt;...&lt;/build&gt; &lt;!-- 플러그인 목록  --&gt;</code></pre>
<h3 id="그렇다면-빌드란-무엇인가-">그렇다면 빌드란 무엇인가 ?</h3>
<p>Maven이 자바 기반 프로젝트를 관리하는 도구라는 것을 알게 되었다. 
Maven은 프로젝트를 빌드하고, 패키징하며, 배포하는 과정을 관리하고 필요한 라이브러리 의존성까지 효율적으로 관리해준다. 
그렇다면 여기서 말하는 빌드(Build)란 무엇일까?</p>
<p><img src="https://velog.velcdn.com/images/_030702/post/aaeb153b-dcd8-48f8-a9dd-dbea44d14e38/image.png" alt=""></p>
<ul>
<li>빌드<ul>
<li>소스코드 혹은 프로젝트에 쓰인 각각의 파일 및 자원을 JVM이나 WAS가 인식할 수 있는 구조로 패키징하는 과정 및 결과물을 의미</li>
</ul>
</li>
<li>빌드 관리 도구 (Build Tool)<ul>
<li>프로젝트 생성, 테스트 빌드, 배포 → 를 위한 전용 프로그램</li>
<li>소스코드를 실행 가능한 어플리케이션으로 만들어주는 도구</li>
<li>if 라이브러리가 계속 늘어나거나, 라이브러리 버전 동기화가 어렵다 ? → build tool이 해소</li>
<li>다시 말해 빌드 과정을 자동화해서 관리해주는 역할</li>
</ul>
</li>
<li>특징<ul>
<li>빌드 도구의 특징은 개발자가 스크립트를 작성해 다양한 작업을 수행할 수 있게 함</li>
<li>소스코드를 컴파일, 패키징 할 수 있으며 테스트를 자동으로 수행하거나 의존성 주입 및 배포 작업을 할 수 있음</li>
<li>종류에 따라 스크립트 작성시 사용하는 문법이 다르긴 하나 일반적으로 <code>XML</code> 혹은 특정 도메인 언어를 사용함</li>
<li>빌드 도구 종류에는 <code>Ant</code> <code>Maven</code> <code>Gradle</code> 이 있음</li>
</ul>
</li>
</ul>
<pre><code>💡 빌드 자동화란 ?

1. 종속성 다운로드

2. 소스코드를 바이너리 코드로 컴파일

3. 바이너리 코드를 패키징

4. 테스트 실행

5. 프로덕션 시스템에 배포

를 스크립팅하거나 자동화하는 행위 </code></pre><h3 id="gradle이란-">Gradle이란 ?</h3>
<ul>
<li>Maven 을 대체할 수 있는 프로젝트 구성 관리 및 범용 빌드 툴</li>
<li>Ant Builder 와 Groovy script를 기반으로 구축되어 기존 Ant 역할 + 배포 스크립 기능 모두 사용 가능</li>
<li>스프링 부트와 안드로이드에 사용</li>
<li>빌드 속도가 Maven에 비해 10~100배 빠름, Java, C/C++, Python을 지원</li>
<li>XML 사용안함, JVM위에서 동작하는 Groovy나 Kotlin을 통해 작성함</li>
<li>Maven의 pom.xml은 선언형으로 설정하는 정적인 문서인 반면, Gradle의 build.gradle은 스크립트로 작성하는 동적인 소스 파일</li>
<li>즉, Maven은 정적인 문서와 엄격한 규칙으로 인해 어떤 설정이 필요할 때 한계가 있으나 Gradle은 로직을 넣을 수 있으니 한계가 필요없음. → 필요하다면 로직에 플러그인을 호출하거나 직접 코드를 짜면됨</li>
</ul>
<pre><code>정리하자면,

Groovy는 JVM에서 실행되는 스크립트 언어

JVM에서 동작하지만, 소스코드를 컴파일 할 필요없음

Java와 호환되며, Java class file을 Groovy class로 사용 가능함

Java와 문법 유사 </code></pre><h4 id="gradle-특징">Gradle 특징</h4>
<pre><code>1. 가독성이 좋다 : 코딩에 의한 **간결한 정의**가 가능하므로 가독성이 좋다.
2. 재사용에 용이 : 설정 주입 방식(Configuration Injection)을 사용하므로 재사용에 용이하다.
3. 구조적인 장점 : Build Script를 Groovy 기반의 DSL(Domail Specific Language)를 사용하여 코드로서 설정 정보를 구성하므로 구조적인 장점이 있다.
4. 편리함 : Gradle 설치 없이 Gradle wrapper를 이용하여 빌드를 지원한다.
5. 멀티 프로젝트 : Gradle은 멀티 프로젝트 빌드를 지원하기 위해 설계된 빌드 관리 도구이다.
6. 지원: Maven을 완전 지원한다.</code></pre><h3 id="maven-vs-gradle">Maven vs Gradle</h3>
<p><img src="https://velog.velcdn.com/images/_030702/post/a398abb1-9956-40fd-9df1-632162fe7920/image.png" alt=""></p>
<ol>
<li><p>스크립트 길이와 가독성 면에서는 Gradle이 우세하다</p>
</li>
<li><p>Build와 테스트 실행 결과 Gradle이 더 빠르다</p>
</li>
</ol>
<ul>
<li>Gradle은 캐시를 사용하기 때문에 테스트 반복 시 실행 결과 시간의 차이가 더 커진다</li>
</ul>
<ol start="3">
<li>의존성이 늘어날 수 록 스크립트의 품질의 차이가 커진다</li>
</ol>
<ul>
<li>Maven은 멀티 프로젝트에서 특정 설정을 다른 모듈에서 사용하려면 상속 받아야하지만, Gradle은 설정 주입 방식을 사용하므로 멀티 프로젝트에 적합하다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[마이크로서비스 아키텍처 (MSA)]]></title>
            <link>https://velog.io/@_030702/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-MSA</link>
            <guid>https://velog.io/@_030702/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-MSA</guid>
            <pubDate>Sun, 08 Mar 2026 12:49:52 GMT</pubDate>
            <description><![CDATA[<h3 id="목적">목적</h3>
<p>지금까지 진행했던 프로젝트는 대부분 동아리나 개인 프로젝트 형태였기 때문에, 하나의 애플리케이션 중심으로 개발을 진행하는 경우가 많았다.
하지만 현재 회사에서 진행 중인 프로젝트는 각 기능을 모듈 단위로 분리하여 여러 개의 애플리케이션으로 구성되어 있다. 
이러한 구조를 Microservices Architecture(MSA) 라고 한다. 
이번 글에서는 MSA의 개념과 특징에 대해 정리해보고자 한다.</p>
<h3 id="msa-기반-모듈이란">MSA 기반 모듈이란?</h3>
<ul>
<li>Microservices Architecture</li>
<li>마이크로 서비스 아키텍처로 설계된 독립적인 서비스 단위를 의미</li>
<li>하나의 큰 애플리케이션을 작고 독립적인 서비스들로 쪼개는 아키텍처 방식을 의미함</li>
</ul>
<h3 id="모놀리식-vs-msa">모놀리식 vs MSA</h3>
<pre><code class="language-java">[모놀리식 - 전통 방식]
┌─────────────────────────┐
│  하나의 큰 애플리케이션      │
│  - 회원 관리              │
│  - 주문 처리              │
│  - 결제 처리              │
│  - 상품 관리              │
└─────────────────────────┘
→ 한 부분만 수정해도 전체 재배포

[MSA - 마이크로서비스]
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│회원 서비스 │  │주문 서비스 │  │결제 서비스 │  │상품 서비스 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
→ 각각 독립적으로 개발/배포/운영</code></pre>
<h3 id="msa-특징">MSA 특징</h3>
<p>각 모듈(서비스)은:</p>
<ul>
<li><strong>독립적인 데이터베이스</strong> 보유</li>
<li><strong>독립적인 배포</strong> 가능</li>
<li><strong>다른 언어/기술</strong> 사용 가능 (회원은 Java, 결제는 Python 등)</li>
<li><strong>API로 통신</strong> (REST API, gRPC 등)</li>
<li><strong>독립적인 확장</strong> (주문 서비스만 서버 추가 가능)</li>
</ul>
<h3 id="실제-예시">실제 예시</h3>
<pre><code class="language-java">쿠팡 같은 서비스:
- 회원 모듈 (로그인, 회원가입)
- 상품 모듈 (상품 검색, 상세)
- 주문 모듈 (장바구니, 주문)
- 결제 모듈 (결제 처리)
- 배송 모듈 (배송 추적)
- 리뷰 모듈 (상품 리뷰)

→ 각각 별도 팀이 개발/관리</code></pre>
<h3 id="장단점">장단점</h3>
<p><strong>장점:</strong></p>
<ul>
<li>부분 수정/배포 용이</li>
<li>장애 격리 (결제 오류가 검색에 영향 X)</li>
<li>기술 선택 자유</li>
<li>팀별 독립 개발</li>
</ul>
<p><strong>단점:</strong></p>
<ul>
<li>복잡도 증가</li>
<li>분산 시스템 관리 어려움</li>
<li>서비스 간 통신 오버헤드<ul>
<li>처리 시간 및 메모리가 추가적으로 사용되는 현상을 의미<ul>
<li>원래 A처리는 3초걸리는데 안정성을 위해 B처리 10초로 진행</li>
<li>이때의 오버헤드는 7초</li>
</ul>
</li>
<li>기존의 단일 Application인 Monolithic과 MSA의 정보 교환 방식은 근본적으로 다름</li>
<li>모놀리식 → 같은 메모리 공간을 쓰기 때문에, 단순히 함수를 호출하고 데이터가 담긴 메모리 주소값만 넘기면 됨.</li>
<li>MSA → 서비스가 서로 다른 서버에 존재. 정보를 주려면 데이터를 패킷으로 쪼개서 네트워크(HTTP, gRPC)를 타고 옆 동네로 넘어가야함<ul>
<li>추가로 다른 애플리케이션이기때문에 인증 및 인가도 매번 증명해야하며,</li>
<li>데이터 일관성을 유지하는데에 어려움 → 트랜잭션 하나로는 안됨. 2PC, Saga패턴과 같은 복잡한 로직을 사용해야함</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 스터디 2주차] Spring Boot의 코어개념]]></title>
            <link>https://velog.io/@_030702/UMC-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%A3%BC%EC%B0%A8-Spring-Boot%EC%9D%98-%EC%BD%94%EC%96%B4%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@_030702/UMC-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%A3%BC%EC%B0%A8-Spring-Boot%EC%9D%98-%EC%BD%94%EC%96%B4%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 22 Sep 2025 07:21:06 GMT</pubDate>
            <description><![CDATA[<h2 id="spring이란">Spring이란?</h2>
<p><img src="https://velog.velcdn.com/images/_030702/post/02b01212-061d-40f8-9078-4d890a71f932/image.png" alt=""></p>
<h4 id="기업에서-사용하는-규모가-큰-자바-프로그램을-더-쉽게-만들-수-있게-도와주는-가볍고-오픈소스로-제공되는-미리-만들어준-코드의-뼈대를-제공하는-프로그램-개발-도구-프레임워크">기업에서 사용하는 규모가 큰 자바 프로그램을 더 쉽게 만들 수 있게 도와주는, 가볍고 오픈소스로 제공되는 미리 만들어준 코드의 뼈대를 제공하는 프로그램 개발 도구 (프레임워크)</h4>
<p>-&gt; Spring을 사용한다는건 Frame안에서 work를 한다고 할 수 있음</p>
<h2 id="api란">API란?</h2>
<p>프레임워크가 미리 구성된 것을 가져와서 사용한다는 의미에서 API와도, 라이브러리와도 비슷한 느낌이 들어 셋의 정확한 개념을 알고 있는 것이 중요하다.</p>
<h4 id="api는-애플리케이션-프로그래밍-인터페이스의-약자로-interface라는-이름에서-알-수-있듯-2개-이상의-소프트웨어-컴포넌트-사이에서-상호작용-할-수-있도록-정의된-인터페이스를-의미함">API는 <code>애플리케이션 프로그래밍 인터페이스</code>의 약자로 interface라는 이름에서 알 수 있듯 2개 이상의 소프트웨어 컴포넌트 사이에서 상호작용 할 수 있도록 정의된 인터페이스를 의미함</h4>
<p>-&gt; 즉, 소프트웨어 애플리케이션이 서로 데이터를 주고 받으며 통신할 수 있도록 만들어진 일련의 규칙이자 약속이라 할 수 있음. </p>
<h2 id="라이브러리란">라이브러리란?</h2>
<h4 id="라이브러리는-프로그램-개발에-필요한-함수-클래스-객체들을-모아둔-도구-상자와-같음">라이브러리는 프로그램 개발에 필요한 함수, 클래스, 객체들을 모아둔 도구 상자와 같음</h4>
<p>-&gt; 라이브러리의 제어권은 개발자에게 있음. 단순히 필요한 기능을 제공할 뿐임</p>
<hr/>

<h2 id="ioc">IoC?</h2>
<h4 id="inversion-of-control">Inversion of Control</h4>
<p>-&gt; 라이브러리의 제어권은 개발자에게 있음
-&gt; but, 프레임워크의 제어권은 프레임워크에 있음</p>
<p>따라서, 개발자가 프레임워크가 정해둔 규칙과 구조에 맞게 코드를 작성하고 삽입하면, 실제 실행 흐름은 프레임워크가 관리함</p>
<p>이를 우리는 <code>제어의 역전</code>이라고 부름</p>
<p>즉, <code>spring</code>은 <code>제어의 역전</code>에 해당하는 프레임워크임</p>
<h3 id="제어의-역전">제어의 역전</h3>
<p>그렇다면 우리가 왜 제어의 역전에 대해 이해하고 있어야하는지 궁금할 것 같음</p>
<p>위에 계속 언급했듯, 프로그램의 제어 흐름을 개발자가 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전이라고 함</p>
<p>일반적인 프로그램을 작성할 때는 객체의 생성, 설정, 초기화, 메서드 호출, 소멸까지의 모든 과정을 개발자가 직접 제어해야 함. 
-&gt; <code>애플리케이션의 실행 흐름, 생명주기는 모두 전적으로 개발자가 관리하는 구조임</code></p>
<p>But, 프레임 워크를 사용하면 객체의 생명 주기를 모두 프레임 워크에 위임함.</p>
<p>제어의 역전에서는 오브젝트가 스스로 사용할 오브젝트를 결정하지도 생성하지도 않음</p>
<h2 id="di란">DI란?</h2>
<h4 id="dependency-injection--의존성-주입">Dependency Injection = 의존성 주입</h4>
<p>-&gt; IoC가 제어권을 외부로 넘긴다면, 이를 실행하기 위해 DI를 사용함</p>
<p>DI는 객체가 필요로 하는 것(=종속성)을 직접 만들지 않고 외부에서 주입받는 방식을 의미함</p>
<blockquote>
<p>토비의 스프링에서는 다음의 세 가지 조건을 충족하는 작업을 의존관계 주입이라고 말함.
    1. 클래스 모델이나 코드에는 런타임 시점과 의존관계가 들어나지 않음. 즉, 인터페이스에만 의존하고 있어야 함.
    2. 런타임 시점의 의존관게는 컨테이너나 팩토리와 같은 제 3의 존재가 결정함.
    3. 의존관게는 사용할 오브젝트에 대한 래퍼런스를 외부에서 주입해주며 만들어짐.</p>
</blockquote>
<p>여기서 객체를 생성하고 관리하며 의존관계를 연결해주는 컨테이너를 우리는 IoC 컨테이너 혹은 DI 컨테이너라고 부름
-&gt; 객체가 생성자 인수, 메서드에 대한 팩토리 메서드에서 생성되거나 반환된 후에 객체 인스턴스에 설정된 속성을 통해서만 종속성을 정의하는 프로세스라고 보면 됨.</p>
<p>정리해보면, </p>
<ol>
<li>객체가 자기 안에서 필요한 것들을 직접 만들지 않고,</li>
<li>생성자 인자, 메서드, 혹은 팩토리 메서드를 통해 외부에서 만들어진 것을 전달받아 사용함</li>
<li>전달 받은 후에는 객체 안에서 설정된 속성을 통해서만 종속성을 사용함</li>
</ol>
<h2 id="그렇다면-종속성은-무엇인가--">그렇다면 종속성은 무엇인가 .. ?</h2>
<h4 id="의존성이란-한-객체가-다른-객체에-기능이나-데이터를-필요로-하는-관계를-의미---즉-한-객체는-다른-객체없이-제대로-동작할-수-없는-상태">의존성이란 한 객체가 다른 객체에 기능이나 데이터를 필요로 하는 관계를 의미 -&gt; 즉 한 객체는 다른 객체없이 제대로 동작할 수 없는 상태</h4>
<h2 id="외부로부터-의존성-주입하는-것이-왜-필요한가-">외부로부터 의존성 주입하는 것이 왜 필요한가 ?</h2>
<p>개발 상황에서는 객체를 마주했을때 서로 의존되어있는 경우가 대다수임.
독립적으로 존재하는 객체도 존재할 수 있으나, 로직을 구현하면서 필연적으로 서로 의존성을 지닌 경우가 많을 것임</p>
<p>ex1) RegularCoffeeBean을 커피 원두로 사용 -&gt; <code>원두 일체형</code></p>
<pre><code>public static class RegularCoffeeBean{
        public void grind() {
        System.out.println(&quot;기본 원두로 갈고 있습니다&quot;);
    }
}

public static class CoffeeMachine {
    private RegularCoffeeBean regularCoffeeBean;

    public CoffeeMachine() {
        this.regularCoffeeBean = new RegularCoffeeBean();
    }

    public void brew() {
        regularCoffeeBean.grind();
        System.out.println(&quot;기본 원두로 커피를 내립니다.&quot;);
    }
}</code></pre><p>ex2) <code>원두 분리형</code></p>
<pre><code>// 인터페이스
public interface CoffeeBeans {
    void grind();
}

// 인터페이스 구현하는 구현체 클래스
@Component(&quot;regularBean&quot;)
public static class RegularCoffeeBeans implements CoffeeBeans {
      @Override
    public void grind() {
        System.out.println(&quot;기본 원두로 갈고 있습니다&quot;);
    }
}

// 원두 종류 바꾸고싶을땐? 구현체 클래스를 또 만들면 됨
@Component(&quot;decafBean&quot;)
public static class DecafCoffeeBeans implements CoffeeBeans {

      @Override
    public void grind() {
        System.out.println(&quot;디카페인 원두로 갈고 있습니다&quot;);
    }
}

// 커피머신
@Component
public static class CoffeeMachine {

        @Autowired
        @Qualifier(&quot;decafBean&quot;)
    private CoffeeBeans coffeeBeans;

    public void brew() {
        coffeeBeans.grind();
        System.out.println(&quot;원두로 커피를 내립니다.&quot;);
    }
}
</code></pre><p>원두일체형은 <code>강한 결합</code> 원두분리형은 <code>느슨한 결합</code>이라고 할 수 있음
-&gt; 각 객체끼리 의존성이 존재할 때 의존성을 외부에서 주입받는 것이 바람직 함.</p>
<hr/>

<h2 id="서블릿">서블릿</h2>
<h4 id="자바-서블릿은-웹페이지를-동적으로-생성하는-서버-측-프로그램을-의미함">자바 서블릿은 웹페이지를 동적으로 생성하는 서버 측 프로그램을 의미함</h4>
<p>기존의 서버는 정적인 자료만을 주고 받고 있었음. 
웹이 다양한 기능을 요구하게 되면서 사용자의 요구에 맞춘 동적인 페이지를 만들 필요가 생김 -&gt; 이를 위해 만들어진 것이 서블릿 !</p>
<pre><code>1. WS (Web Server)
- 웹서버를 뜻하며 주로 정적인 컨텐츠 (HTML, img ..)를 클라이언트에 전달함
- 요청 -&gt; 응답 (대부분 HTTP) 
- 정적인 웹페이지 처리에 최적화
- 동적인 처리 (데이터 베이스 접근 불가) 
- Apache HTTP Server, Nginx 존재

2. WAS (Web Application)
- WAS는 웹 애플리케이션 서버라고 부르며, 동적인 웹 서비스를 처리함
- 클라이언트 요청 -&gt; 서버 측에서 로직 처리 -&gt; 결과 응답
- DB 접근, 비즈니스 로직 실행 가능
- 주로 Servlet, JSP, Spring 등 동적인 웹 서비스 제공 가능
- Tomcat, JBoss, WebLogic, Spring Boot embedded Tomcat 존재</code></pre><h2 id="서블릿-동작-방식--범용">서블릿 동작 방식 : 범용</h2>
<p><img src="https://velog.velcdn.com/images/_030702/post/25517aaa-9164-484a-9e98-1100ad7fc7fb/image.png" alt=""></p>
<h2 id="서블릿-동작-방식--스프링">서블릿 동작 방식 : 스프링</h2>
<p><img src="https://velog.velcdn.com/images/_030702/post/b6f17d10-087c-4e86-8006-0494830bd85a/image.png" alt=""></p>
<hr/>

<h2 id="servlet-container란">Servlet Container란?</h2>
<h4 id="서블릿-객체를-생성-초기화-호출-종료하는-생명주기의-담당함">서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기의 담당함.</h4>
<p>Servlet이 어떤 역할을 수행하는 메뉴얼이라면, 
Servlet Container는 클라이언트의 요청을 받아주고 응답할 수 있게 웹 서버과 Socket으로 통신하며 직접 핸들링한다고 볼 수 있음.</p>
<pre><code>클라이언트의 요청이 들어오면 ...
서블릿 컨테이너는 web.xml을 기반으로 사용자가 요청한 URL이 어느 서블릿에 대한 요청인지 찾음

그렇다면 web.xml은 무엇인가?
-&gt; 웹 애플리케이션의 배치 파일로 서블릿, 필터, 리스너 등 웹 구성 요소의 설정 정보를 정의하는 환경설정 파일임
-&gt; 여기서 정의된 서블릿과 URL 매핑 정보를 확인하고, 매핑된 서블릿이 있으면 해당 서블릿으로 전달하고, 서블릿이 처리한 응답을 브라우저로 보내줌.</code></pre><p>본래 네트워크를 통한 소통을 위해서는 <code>Socket</code>을 만들고 <code>listen()</code>, <code>accept()</code>, <code>connect()</code>를 통해 구현해야함.
하지만 servlet 컨테이너는 이러한 기능을 api로 제공하여 간편화 함</p>
<p>-&gt; 우리는 servlet으로 구현해야 할 비즈니스 로직에 대해서만 집중하면 되는 것임 !!!</p>
<hr/>

<h2 id="스프링에서의-di-ioc-servlet">스프링에서의 DI, IoC, Servlet</h2>
<h4 id="bean">Bean</h4>
<p>빈은 Spring 컨테이너가 관리하는 자바의 객체를 의미함.
Spring은 Bean을 통해 객체를 인스턴스화한 후, 객체 간의 의존관계를 관리함.
객체가 의존관계를 등록할 때, Spring 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만들게 됨.</p>
<h3 id="일반적인-자바를-사용하는-경우">일반적인 자바를 사용하는 경우</h3>
<pre><code>public class UMC9thMember {
        private int age;
        private String nickname;
        private String school;
        private String studyPart;

        public UMC9thMember(){            
        }

        public UMC9thMember(int age, String nickname, String school, String studayPart) {
                this.age = age;
                this.nickname = nickname;
                this.school = school;
                this.studyPart = studyPart;
        }
}

// 여기서 객체를 생성하기 위해서는 
// new 생성자 사용 -&gt; 개발자가 전적으로 생명주기, 의존성 관리 진행

UMC9thMember umc9thMember = new UMC9thMember(23, &quot;마크&quot;,&quot;상명대학교&quot;, &quot;Server(SpringBoot)&quot;);</code></pre><h3 id="bean을-사용하는-경우">Bean을 사용하는 경우</h3>
<p><code>@Component</code> → <code>@Autowired</code> : <strong>묵시적 빈 정의</strong></p>
<ul>
<li>클래스에 어노테이션을 추가하고, Autowired로 다른 클래스에서 해당 Bean을 끌어온다.</li>
<li>@Controller, @Service, @Repository를 사용하면 그 자체로 묵시적 빈 정의를 한 것이라고 볼 수 있음.</li>
</ul>
<p><code>@Configuration</code> → <code>@Bean</code> : <strong>명시적 빈 정의</strong> </p>
<ul>
<li>Spring 설정 파일에 Configuration 어노테이션을 추가하고, Bean 어노테이션을 붙여 명시적으로 빈을 지정한다.</li>
<li>스프링 라이브러리에 없는 외부 라이브러리에서도 Bean 생명주기를 사용할 수 있음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[초록스터디] Spring core ]]></title>
            <link>https://velog.io/@_030702/%EC%B4%88%EB%A1%9D%EC%8A%A4%ED%84%B0%EB%94%94-Spring-core</link>
            <guid>https://velog.io/@_030702/%EC%B4%88%EB%A1%9D%EC%8A%A4%ED%84%B0%EB%94%94-Spring-core</guid>
            <pubDate>Wed, 25 Jun 2025 08:34:54 GMT</pubDate>
            <description><![CDATA[<p>시작에 앞서
Spring을 초록스터디를 통해 처음 공부해보았는데, 첫번째 방탈출 미션을 드디어 완료하였다. JAVA 미션을 진행할때는 잘 몰랐던 것을 배울 수 있어서 좋았다. 특히, 멘토 오찌님이 공부하고 생각할 거리를 많이 주셔서 배울 수 있었던 것 같다. 이번 core 미션은 Spring context를 이해하는데에 초점을 맞추어 공부했으나, 어렵다 . . . 따라서, 이번 미션을 진행하며 고민해보았거나, 오찌님이 질문해주신 부분들을 정리해두려 한다.</p>
<h2 id="1-spring-context란">1. Spring Context란?</h2>
<blockquote>
<p><strong>Spring Context</strong> 란 빈(Bean) 객체들을 생성하고 관리하는 스프링의 핵심 컨테이너이다.</p>
</blockquote>
<ul>
<li>Bean: 스프링이 관리하는 객체 (Controller, Service, Repository)</li>
<li>Container: Bean의 생명주기, 의존성 주입 등을 관리하는 스프링 내부 시스템</li>
<li>Context: Bean 등록, 조회, 설정 등을 처리하는 객체 = ApplicationContext</li>
</ul>
<h2 id="2-예외처리-위치">2. 예외처리 위치</h2>
<blockquote>
<p>예외처리는 도메인에서 처리하는 것이 좋을까, 서비스에서 처리하는 것이 좋을까</p>
</blockquote>
<p>도메인에서 처리할 수 있는 예외는 도메인에서, 그 외에는 서비스에서 처리하는 것이 좋다.
-&gt; 최대한 더 낮은 계층에서 처리하는 것을 원칙으로 삼자.</p>
<p>시간 객체의 시간 값에 null이 들어왔다 -&gt; 도메인에서 예외처리
시간 객체의 시간 값은 중복이면 안된다 -&gt; 도메인 객체만으로는 안되고 저장된 다른 객체를 봐야함 -&gt; 서비스에서 예외처리</p>
<h2 id="3-controller---repository-직접-의존">3. Controller -&gt; Repository 직접 의존</h2>
<p>[Controller]
↓ 요청 전달 / 응답 반환 [DTO]
[Service]
↓ 비즈니스 로직 처리
[Repository]
↓ DB 접근
[Domain]
↔ DB 매핑</p>
<p>+) [Exception] : 예외처리
+) [View] : 화면에 보이는 것</p>
<p>이와 같은 관계를 갖고 애플리케이션은 설계되는데, 
Controller 는 요청/응답 처리에 집중해야하며, Repository는 DB 접근에 집중해야함.
또한, 비즈니스 로직은 Service에서 진행해야함.</p>
<p>-&gt; 이 부분 제발 좀 ... 지키자 희정아 ...
✅ 설계 시작 전에 계층별 책임 적어보기    &quot;이건 Service 책임인가?&quot; 먼저 생각하기
✅ 단위 테스트 작성    비즈니스 로직은 Service에서 테스트할 수 있어야 함
✅ 객체 간 의존 구조 그려보기    간단한 UML, 화살표 그리기 등으로 시각화
✅ Controller에서 로직이 길어지면 &quot;서비스로 옮길 수 있지 않을까?&quot; 되묻기</p>
<h2 id="4-autowired-없이도-작동-">4. <code>@Autowired</code> 없이도 작동 ?</h2>
<pre><code>public class ReservationController {
    private final ReservationService reservationService;

    @Autowired
    public ReservationController(ReservationService reservationService) {
        this.reservationService = reservationService;
    } </code></pre><p>-&gt; 이 코드에서는 <code>@Autowired</code> 없이도 작동 가능함.
Spring 4.3부터 클래스에 생성자가 하나 뿐이라면, 자동으로 주입가능한 Bean을 찾아서 주입함.
당연히, 생성자 2개 이상일 때는 생략하지 않아도 됨.</p>
<h2 id="5-상태코드-메서드">5. 상태코드 메서드</h2>
<pre><code>ResponseEntity
.status(HttpStatus.CREATED)
.location(location)
.body(ReservationResponse.from(savedReservation));</code></pre><p>↓</p>
<pre><code>ResponseEntity
.created(location)
.body(ReservationResponse.from(savedReservation));</code></pre><p>로 축약할 수 있음. </p>
<p>+) 
ok = 200
created(URI) = 201
accepted() = 202
noContent() = 204
badRequest() = 400
notFound() = 404</p>
<h2 id="6-삭제를-의도했는데-삭제가-되지-않은-상황의-예외처리는-서비스에서-">6. 삭제를 의도했는데 삭제가 되지 않은 상황의 예외처리는 서비스에서 !</h2>
<ul>
<li>TimeController<pre><code>boolean deleted = timeService.deleteTime(id);
      if (!deleted) {
          return ResponseEntity.badRequest().build();
      }</code></pre>본래 Controller에서 단순 삭제 성공 여부만 판단하고 있다고 생각하여 Controller에 예외처리 로직을 작성하였는데, <code>삭제 실패와 같은 비즈니스 로직 실패는 Service</code>에서 다루는 것이 맞는 방식이다.</li>
</ul>
<p>따라서, 말씀해주신대로,
삭제 실패 -&gt; Service
성공 흐름 -&gt; Controller
나머지 예외는 exception에서 처리하는 것으로 수정하였다. </p>
<p>&lt;수정 후&gt;</p>
<ol>
<li>Controller는 성공한 흐름만 다룸 <pre><code>@DeleteMapping(&quot;/{id}&quot;)
 public ResponseEntity&lt;Void&gt; deleteTime(@PathVariable Long id) {
     timeService.deleteTime(id);
     return ResponseEntity.noContent().build();
 }</code></pre></li>
</ol>
<p>2.Service에서 예외 표현함(비즈니스 로직 예외)</p>
<pre><code>public boolean deleteTime(Long id) {
        if(!timeRepository.deleteById(id)) {
            throw new InvalidTimeException(&quot;없는 시간입니다.&quot; + id);
        }
        return timeRepository.deleteById(id);
    }</code></pre><h2 id="7-도메인에-setter">7. 도메인에 setter</h2>
<p>도메인 객체에 setter를 열어두는 것은 객체 상태를 마음대로 바꿀 수 있어, 캡슐화에 어긋나고, 객체 불변성 유지에 어려워짐.</p>
<p>보통 도메인 객체를 수정해야하는 경우
서비스 클래스에서 도메인 객체를 받아
서비스 클래스에서 상태 변경을 수행하고,
변경된 도메인 객체를 Repository에 저장하는 방식을 사용할 것 !</p>
<h2 id="8-validation-꼭-유효성-검증-꼬옥-">8. validation 꼭. 유효성 검증 꼬옥 !!!</h2>
<p>까먹지 말긔 ... </p>
<h2 id="9-localtime-vs-string">9. LocalTime vs String</h2>
<ol>
<li>LocalTime</li>
</ol>
<ul>
<li>타입 안정성: 시간 정보를 명확한 타입으로 표현해서, 시간 연산(더하기, 빼기 등)을 안전하게 수행 가능</li>
<li>유효성 검사 내장: 잘못된 시간 값(예: 25:00)은 생성 자체가 안 됨</li>
<li>표준 API 지원: 포맷팅, 파싱, 비교, 계산 등 편리한 메서드 제공</li>
<li>명확한 의미 전달: 변수 타입만 보고도 시간 정보임을 알 수 있음</li>
<li>DB 매핑 주의 필요: JDBC에서 LocalTime 직접 매핑이 안 될 수 있어, java.sql.Time으로 변환 필요</li>
<li>직렬화/역직렬화 설정 필요: JSON 직렬화 시 기본 String 포맷 지정 필요(예: HH:mm:ss)</li>
</ul>
<ol start="2">
<li>String</li>
</ol>
<ul>
<li>단순 저장/전송: 포맷을 문자열로 자유롭게 표현 가능 (예: &quot;10:00&quot;, &quot;10시&quot;, &quot;10:00 AM&quot;)</li>
<li>빠른 구현: 복잡한 타입 변환 없이 바로 사용 가능</li>
<li>유효성 검사 어려움: &quot;25:00&quot;, &quot;abc&quot; 같은 잘못된 시간 값도 들어갈 수 있음</li>
<li>시간 연산 불가: 시간 더하기 빼기 등 로직 작성 시 문자열 파싱 필요</li>
<li>타입 의미 모호: 그냥 문자열이어도 시간으로 처리 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[소프트웨어 설계 패턴 ( MVC)]]></title>
            <link>https://velog.io/@_030702/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%84%A4%EA%B3%84-%ED%8C%A8%ED%84%B4-MVC</link>
            <guid>https://velog.io/@_030702/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%84%A4%EA%B3%84-%ED%8C%A8%ED%84%B4-MVC</guid>
            <pubDate>Wed, 25 Jun 2025 06:41:51 GMT</pubDate>
            <description><![CDATA[<p><strong>📌 기본 MVC 구성</strong></p>
<ol>
<li>Model </li>
</ol>
<ul>
<li>애플리케이션의 핵심 비즈니스 데이터와 로직을 담은 영역</li>
<li>도메인 객체 또는 Entity 클래스가 위치</li>
</ul>
<ol start="2">
<li>View</li>
</ol>
<ul>
<li>사용자에게 데이터를 보여주는 부분 (ex) JSON</li>
<li>Controller로 받은 데이터를 시각적으로 표현</li>
</ul>
<ol start="3">
<li>Controller </li>
</ol>
<ul>
<li>사용자에게 요청을 받아서 처리하고, 응답을 반환함</li>
<li>주로 Service를 호출해서 비즈니스 로직을 수행</li>
</ul>
<p>-&gt; 여기까지가 MVC 설계 패턴
but, 스프링과 같은 현대 프레임워크에서는 구조를 더욱 세분화해서 유지보수성과 확장성을 높임.
<code>Service</code> <code>Repository</code> <code>DTO</code> <code>Domain</code> <code>Exception</code> 을 알아볼 것.</p>
<ol start="4">
<li>Service</li>
</ol>
<ul>
<li>비즈니스 로직을 담당</li>
<li>Controller, Repository 사이의 중간다리 연결</li>
<li>트랜잭션 처리, 여러 Repository 조합 호출 등의 로직 수행</li>
</ul>
<ol start="5">
<li>Repository</li>
</ol>
<ul>
<li>DB와 직접적으로 통신함 (DAO 역할)</li>
</ul>
<ol start="6">
<li>DTO</li>
</ol>
<ul>
<li>Data Transfer Object</li>
<li>데이터 전달용 객체</li>
<li>주로 Controller &lt;-&gt; Service 사이에서 사용</li>
<li>Domain과 분리해 API 요청/응답용 포맷을 정리함</li>
<li>Validation 이나 보안 목적에서도 중요</li>
</ul>
<ol start="7">
<li>Domain(또는 Entity)</li>
</ol>
<ul>
<li>애플리케이션의 핵심 객체</li>
<li>불변성, 도메인 규칙을 중심으로 구성</li>
</ul>
<ol start="8">
<li>Exception</li>
</ol>
<ul>
<li>예외를 세분화해서 정리</li>
<li>예외를 명확하게 분리하여 에러메세지 처리와 로깅을 쉽게 함</li>
</ul>
<p>[Controller]
    ↓ 요청 전달 / 응답 반환
[Service]
    ↓ 비즈니스 로직 처리
[Repository]
    ↓ DB 접근
[Domain]
    ↔ DB 매핑</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도메인, MVC (Model - view - controller) model]]></title>
            <link>https://velog.io/@_030702/%EB%8F%84%EB%A9%94%EC%9D%B8-MVC-Model-view-controller-model</link>
            <guid>https://velog.io/@_030702/%EB%8F%84%EB%A9%94%EC%9D%B8-MVC-Model-view-controller-model</guid>
            <pubDate>Sat, 24 May 2025 15:41:02 GMT</pubDate>
            <description><![CDATA[<p><strong>### 도메인이란?</strong></p>
<p>소프트웨어로 해결하고자 하는 문제 영역을 의미</p>
<p>ex) 온라인 서점 : 소프트웨어로 해결하고자 하는 문제 영역
-&gt; 이 도메인에서는 하위 도메인으로 상품조회, 구매, 결제, 배송 추적의 기능을 제공
-&gt; 하위 도메인은 다른 하위 도메인과 연결되어 완전한 기능을 제공
-&gt; 도메인이 제공할 모든 기능을 구현해야 하는 것은 아님 (결제와 같은 기능은 외주 가능)</p>
<h4 id="즉-도메인은-우리가-만들고자-하는-대상이-되는-소프트웨어문제영역이며-도메인-설계는-우리가-만들고자-하는-소프트웨어에-어떤-기능을-제공할지-어떠한-데이터-구성을-가질지-각-기능간의-관계를-어떻게-할지를-설계하는-것을-의미함">즉, &#39;도메인&#39;은 우리가 만들고자 하는 대상이 되는 소프트웨어(문제영역)이며, 도메인 설계는 우리가 만들고자 하는 소프트웨어에 어떤 기능을 제공할지, 어떠한 데이터 구성을 가질지, 각 기능간의 관계를 어떻게 할지를 설계하는 것을 의미함.</h4>
<h3 id="mvc"><strong>MVC</strong></h3>
<p>MVC 모델은 웹 애플리케이션에서 요청과 응답을 효과적으로 처리하는 패턴
클라이언트의 요청을 Controller -&gt; Service -&gt; Repository -&gt; Database의 흐름으로 처리하고, 
결과를 View로 반환해 클라이언트에게 보여준다고 볼 수 있음</p>
<h3 id="mvc-아키텍처"><strong>MVC 아키텍처</strong></h3>
<ol>
<li>Model</li>
</ol>
<ul>
<li>애플리케이션의 데이터 및 비즈니스 로직을 담당</li>
<li>데이터 베이스와 직접적으로 연결되며, Service 계층을 통해 Controller에서 데이터를 가져옴</li>
<li>데이터와 데이터가 동작하는 방식을 제공</li>
</ul>
<ol start="2">
<li>View</li>
</ol>
<ul>
<li>클라이언트에게 보여줄 화면을 담당</li>
<li>HTML, JSP, Thymeleaf, React를 사용해 데이터를 표현</li>
<li>데이터의 표현 (화면에 보여줌)</li>
</ul>
<ol start="3">
<li>Controller </li>
</ol>
<ul>
<li>사용자의 요청을 받아 처리하고, 적절한 데이터를 Model에서 가져와 View로 전달</li>
<li>@Controller @RestController를 사용해 구현</li>
<li>모델과 뷰의 연결고리 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/_030702/post/9c152b01-553c-42f0-bba4-61b2f6fb4b0f/image.png" alt=""></p>
<p>예를들면, </p>
<ol>
<li>사용자가 애플리케이션과 컨트롤러에 최초 처리를 요청</li>
<li>모델에서 데이터 갱신, 생성, 삭제 조작함</li>
<li>모델은 결과를 컨트롤러에 전달</li>
<li>컨트롤러는 뷰에 결과를 전달하여 결과적으로는 뷰가 사용자에게 응답 보여줌</li>
</ol>
<p>-&gt; 사용자가 요청을 하면 모델에서 조작하고 결과를 데이터 컨트롤러에 전달. 컨트롤러는 뷰에 전달해 응답 보여줌.</p>
<h3 id="책임과-의존성"><strong>책임과 의존성</strong></h3>
<p>유연성 있는 소프트웨어를 개발해야함. (다양한 요구사항의 변화에 대처하기 위해)
이는 책임과 의존성과 깊은 연관이 있음.</p>
<p>책임 : 객체가 수행해야하는 역할이나 기능을 의미
의존성 : 어떤 객체가 다른 객체에 기능을 위임하거나 사용할 때 발생</p>
<p>ex) class A 가 class B에 기능을 요청할 경우, A는 B에 의존하고, B는 특정 기능 수행에 대한 책임을 가짐. 좋은 설계란, 책임을 잘 나누고, 동시에 의존성을 낮추는 구조를 의미함.</p>
<h3 id="그렇다면-도메인과-뷰를-왜-분리해야하는가"><strong>그렇다면 도메인과 뷰를 왜 분리해야하는가?</strong></h3>
<p>-&gt; 관심사의 분리 때문에</p>
<p>도메인은 <code>무엇을 할 것인가</code>를 담당하고,
뷰는 <code>어떻게 보여줄 것인가</code>를 담당.</p>
<p>분리를 통해 재사용, 단위 테스트의 용이성, 유지보수하기 편리, 뷰를 여러가지로 확장해도 도메인을 사용가능해 확장성, 개발 업무의 영역이 분리되어 협업의 효율이 올라간다는 장점이 있음.</p>
<h3 id="뷰로-도메인이-넘어간다면"><strong>뷰로 도메인이 넘어간다면?</strong></h3>
<p>뷰에서 핵심 도메인을 처리한다면 다양한 문제 발생 가능함.</p>
<ol>
<li>로직 중복 : 뷰마다 로직을 구현하면 여러 곳에 같은 로직 반복</li>
<li>버그 발생 가능성 증가 : 로직이 뷰마다 다르게 동작할 수 있음.</li>
<li>테스트 어려움 : 로직이 UI에 묻혀있어 테스트 불가능 하거나 복잡.</li>
<li>유지보수 어려움 : 뷰가 바뀔때마다 로직 수정해야함.</li>
</ol>
<h3 id="도메인이-뷰를-참조하는-것은-괜찮은가"><strong>도메인이 뷰를 참조하는 것은 괜찮은가?</strong></h3>
<p>도메인은 뷰를 몰라야 함.</p>
<p>-&gt; 도메인은 순수한 소프트웨어 로직이기 때문에 어떠한 UI에서도 독립적으로 존재해야 함.
-&gt; 도메인이 뷰를 알게되면, UI변경시 도메인에 영향 줌.
-&gt; 도메인 로직을 재사용할 수 없고, 결합도가 높아져 유지보수 어려움.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cross-Site Request Forgery (CSRF)]]></title>
            <link>https://velog.io/@_030702/Cross-Site-Request-Forgery-CSRF</link>
            <guid>https://velog.io/@_030702/Cross-Site-Request-Forgery-CSRF</guid>
            <pubDate>Mon, 19 May 2025 10:44:17 GMT</pubDate>
            <description><![CDATA[<p>쿠키에 있는 이용자의 식별 정보는 클라이언트에서 보내진 요청으로부터 생기며, 이는 이용자에게 권한이 부여될 수 있음을 의미합니다. 이 부분과 관련된 범죄도 증가하고 있고, 많은 서비스가 우리의 정보와 밀접하게 연관되어 있기에 더욱 조심해야합니다. 이러한 정보를 날조, 위조하는 것을 CSRF 교차사이트 요청 위조라고 정의합니다.</p>
<p><strong>CSRF</strong>
이용자를 속여서, 의도하지 않은 요청에 동의하게 만드는 공격을 의미합니다. 그럴듯한 웹 페이지를 만들어서 이용자의 입력을 유도하고, 이용자가 값을 입력하면 이를 은행이나 중요한 포털사이트로 전송해 스스로 이동한 것 같은 요청을 발생시킵니다. 다시 정리해서 말하면 <code>임의의 이용자의 권한을 이용해 임의의 주소에 HTTP 요청을 보낼 수 있는 취약점입니다.</code> </p>
<p>이용자가 HTTP 요청을 보내도록 하는 방법은 다양하게 존재합니다.</p>
<p><strong>CSRF동작</strong>
CSRF 공격에 성공하기 위해서는 공격자가 작성한 악성 스크립트를 이용자가 실행해야합니다. 이는 공격자가 이용자에게 메일을 보내거나, 게시판에 글을 작성해 이용자가 조회하도록 유도합니다.
HTML 혹은 Javascript을 통해 공격 스크립트를 작성할 수 있으며 아래 사진 및 코드는 HTML로 작성한 스크립트의 예시입니다. <img> 혹은 <form> 태그를 사용하는 방법으로 HTTP에 요청을 보내면 HTTP 헤더 쿠키에 사용자의 정보가 포홤됩니다.</p>
<p><img src="https://velog.velcdn.com/images/_030702/post/0d79adb7-ef1c-496e-ad47-8d4f2402dc74/image.png" alt=""></p>
<p><strong>XSS와 CSRF의 차이</strong>
XSS, CSRF 모두 스크립트를 웹페이지에 작성해 공격한다는 점에서 매우 유사합니다. 두 취약점의 공통점과 차이점을 비교해보자면, 우선 두 취약점 모두 클라이언트를 대상으로 하는 공격이며, 이용자가 악성 스크립트가 포함된 페이지에 접속하도록 유도해야합니다. 반면 차이점은 두 취약점의 목적에 있습니다. XSS는 인증 정보인 세션과 쿠키를 탈취하는데에 있으며, CSRF는 이용자가 임의의 페이지에 HTTP 요청을 보내는 것을 목적으로 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] XSS-1, XSS-2]]></title>
            <link>https://velog.io/@_030702/Dreamhack-XSS-1-XSS-2</link>
            <guid>https://velog.io/@_030702/Dreamhack-XSS-1-XSS-2</guid>
            <pubDate>Mon, 19 May 2025 09:59:02 GMT</pubDate>
            <description><![CDATA[<p><strong>XSS1</strong></p>
<p>문제에서 제시하는 페이지는 총 4가지 입니다.</p>
<ol>
<li>/ : 인덱스 페이지</li>
<li>/vuln : 이용자가 입력한 값 출력</li>
<li>/memo : 이용자에게 메모를 남길 수 있고, 출력도 가능합니다</li>
<li>/flag : 전달된 URL에 임의의 이용자가 접속하게 하여, <em>해당 이용자의 쿠기</em>에는 FLAG가 존재함을 확인합니다.</li>
</ol>
<blockquote>
<p>취약점 분석
vuln, memo의 엔드포인트에는 이용자의 입력값을 받아 출력합니다.
memo는 render_template를 이용해 memo.html를 이용해 출력하는데 render_template함수는 전달된 템플릿 변수를 기록할 때 HTMl 코드로 변환하여 저장하기 때문에 XSS가 발생하지 않습니다. 하지만, vuln은 이용자가 입력한 값을 페이지에 그대로 노출시키기 때문에 XSS가 발생합니다.</p>
</blockquote>
<blockquote>
<p>익스플로잇
/vuln 엔드포인트에서 발생하는 XSS 취약점을 통해 임의 이용자 쿠키를 탈취해야합니다. 탈취한 쿠키를 전달받기 위해서는 외부에서 접근 가능한 웹서버 혹은 memo 엔드포인트를 사용할 수 있습니다.</p>
</blockquote>
<p>location.href : 전체 url 반환 혹은 업데이트 가능
document.cookie : 해당 페이지에서 사용하는 쿠키를 읽고 씀</p>
<blockquote>
<p>쿠키탈취
memo 페이지 사용 
<code>&lt;script&gt;location.href = &quot;/memo?memo=&quot; + document.cookie;&lt;/script&gt;</code>
웹 서버 사용
<code>&lt;script&gt;location.href = &quot;/memo?memo=&quot; + document.cookie;&lt;/script&gt;</code></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/_030702/post/a5ade7db-4c8a-414a-9428-814620563090/image.png" alt="">
파라미터로 쿠키탈취를 위한 코드를 작성하면, flag 값이 메모에 나온다.</p>
<p><strong>XSS2</strong>
문제에서 제시하는 페이지는 동일하게 4가지입니다.
다만, 1번 문제와 다르게 vuln 페이지에서 param으로 전달받은 값을 html 코드로 변환하기 때문에, 1번 처럼 문제를 풀 수는 없습니다.</p>
<script> 태그를 사용할 수 없는 대신, <img> 태그를 사용한다면 XSS 공격을 할 수 있습니다.]]></description>
        </item>
        <item>
            <title><![CDATA[Cross Site Scripting (XSS)]]></title>
            <link>https://velog.io/@_030702/Cross-Site-Scripting-XSS</link>
            <guid>https://velog.io/@_030702/Cross-Site-Scripting-XSS</guid>
            <pubDate>Mon, 19 May 2025 09:37:30 GMT</pubDate>
            <description><![CDATA[<p><code>클라이언트 사이드</code>의 가장 큰 취약점은 이용자를 식별하기 위한 세션 및 쿠키 정보를 탈취하고 해당 계정으로 임의의 기능을 수행할 수 있다는 점 입니다.</p>
<p>이번에는 클라이언트 사이드 취약점을 활용한 대표적인 공격인 <code>Cross Site Scripting(XSS)</code>의 종류와 어떤 상황에서 발생하며, 어떻게 활용되는지를 확인해보겠습니다.</p>
<blockquote>
<p>Cross Site Scripting의 약어는 CSS라고 불리우는게 맞으나, html css js에서 사용되는 스타일시트 css와 혼동될 수 있어 XSS로 부른다.</p>
</blockquote>
<p>*<em>Cross Site Scripting (XSS) *</em>
XSS는 클라이언트 사이드 취약점 중 하나로, 공격자가 웹 리소스에 악성 스크립트를 삽입해 이용자의 웹 브라우저에서 해당 스크립트를 실행할 수 있습니다. 또한 이 취약점을 이용해 계정의 세션 정보를 탈취하고, 해당 계정으로 임의의 행동을 수행할 수 있습니다. 해당 취약점은 SOP 보안 정책이 등장하며 이전에 비해 힘들어졌습니다. 다만, 이를 우회하는 다양한 기술이 소개되며 XSS 공격은 지속되고 있습니다.</p>
<p><strong>Same-Origin Policy(SOP)</strong>
SOP는 동일 출처 정책이라는 뜻으로 동일한 출처의 리소스만 상호작용을 허용하는 정책이다. 두 URL의 프로토콜, 호스트, 포트가 모두 같아야 동일한 출처로 인정되며 웹 사이트 샌드박스화하여 잠재적인 보안 위협으로부터 보호합니다. </p>
<p><img src="https://velog.velcdn.com/images/_030702/post/bbcd61f6-67be-491d-9640-30264eabe631/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/615ed6c6-0dcc-4947-aa7a-e409357caf4e/image.png" alt=""></p>
<p><strong>XSS의 종류</strong>
XSS는 이용자가 삽입한 내용을 출력하는 기능에서 발생합니다. 에를들면 로그인 시 출력되는 &quot;안녕하세요, 00회원님&quot;과 같은 문장에서 말입니다. XSS의 종류는 발생 형태에 따라 구분되며, 아래의 내용을 확인해주세요.
<img src="https://velog.velcdn.com/images/_030702/post/47d001b4-f99f-4f71-b153-ceb1c18bb9d8/image.png" alt=""></p>
<blockquote>
<p>Stored XSS
서버의 데이터 베이스 또는 파일 등의 형태로 저장된 악성 스크립트를 조회할 때 발생합니다. 예를들면, 게시물과 댓글에 악성 스크립트를 포함해 업로드 하는 방식이 있습니다. 게시물은 불특정 다수에게 보여지기 때문에, 해당 기능에서 XSS 취약점이 존재할 경우 많은 피해를 볼 수 있습니다.</p>
</blockquote>
<blockquote>
<p>Reflected XSS
서버가 악성 스크립트가 담긴 요청을 출력할 때 발생합니다. 대표적으로 게시판 서비스에 작성된 게시물을 조회하기 위한 검색창에 스크립트를 포함해 검색하는 방식이 있습니다. 검색시 검색 문자열에 악성 스크립트가 포함되어 있다면 발생할 수 있습니다. 또한, Reflected XSS는 다른 이용자를 악성 스크립트가 포함된 링크에 접속하도록 유도해야 합니다. 이용자에게 링크를 전달하기 위해 Click Jacking, Open Redirect등 다른 취약점과 연계하여 사용합니다.
<em>이용자의 요청에 악성 스크립트가 포함되어있음</em></p>
</blockquote>
<blockquote>
<p>ClickJacking
사용자가 자신이 클릭하는 것과 다른 것을 클릭하도록 속여 악의적인 행동을 유도하는 웹 보안 공격입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] WebHacking Session-basic 문제풀이]]></title>
            <link>https://velog.io/@_030702/Dreamhack-WebHacking-Session-basic-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@_030702/Dreamhack-WebHacking-Session-basic-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Mon, 07 Apr 2025 10:08:54 GMT</pubDate>
            <description><![CDATA[<p><a href="https://dreamhack.io/wargame/challenges/409">https://dreamhack.io/wargame/challenges/409</a></p>
<ol>
<li><p>문제 파일 다운로드 후 서버 생성하고 VM에 접속함
<img src="https://velog.velcdn.com/images/_030702/post/0c257696-6742-4055-af60-99a1c2957f4c/image.png" alt=""></p>
</li>
<li><p>admin 계정에 로그인하기 위해 cookie 문제와 동일하게 시도
<img src="https://velog.velcdn.com/images/_030702/post/11e0f678-c41e-438f-9688-f422e34fc3c9/image.png" alt=""></p>
<blockquote>
<p>변화 없음 </p>
</blockquote>
</li>
</ol>
<h3 id="index-페이지-코드">index 페이지 코드</h3>
<pre><code>@app.route(&#39;/&#39;) # / 페이지 라우팅 
def index():
    session_id = request.cookies.get(&#39;sessionid&#39;, None) # 쿠키에서 sessionid 조회
    try:
        username = session_storage[session_id] # session_storage에서 해당 sessionid를 통해 username 조회
    except KeyError:
        return render_template(&#39;index.html&#39;)

    return render_template(&#39;index.html&#39;, text=f&#39;Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}&#39;) </code></pre><p>를 확인해보면 <code>sessionid</code>의 값을 통해 <code>session_storage</code>에서 해당 <code>sessionid</code>의 <code>username</code>을 조회해 <code>admin</code>인지 확인하고 있음.
<em><strong>따라서, <code>admin</code>의 <code>sessionid</code>를 알아야함.</strong></em></p>
<ol start="3">
<li><p>admin의 sessionid값을 알기 위해 url+/admin 접속
<img src="https://velog.velcdn.com/images/_030702/post/f117241c-115f-406a-8164-8d9602295487/image.png" alt=""></p>
</li>
<li><p>guest로 로그인 진행
<img src="https://velog.velcdn.com/images/_030702/post/c6d6ca03-2eac-44b6-96e3-1e8288102136/image.png" alt="">
<img src="https://velog.velcdn.com/images/_030702/post/b24fcaf6-23cc-46a6-a1d6-5c7b0a341d86/image.png" alt=""></p>
</li>
</ol>
<ol start="5">
<li>guest의 sessionid 변조 진행
<img src="https://velog.velcdn.com/images/_030702/post/46d52bcc-213d-4712-bca6-5d22f13a5dcc/image.png" alt=""></li>
</ol>
]]></description>
        </item>
    </channel>
</rss>