<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>jaylab-log</title><link>https://jaylab.link/</link><description>Recent content on jaylab-log</description><generator>Hugo</generator><language>ko-kr</language><lastBuildDate>Thu, 07 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://jaylab.link/index.xml" rel="self" type="application/rss+xml"/><item><title>배포할 때마다 끊기던 서비스를 무중단으로: Jenkins Rolling Deployment에서 내린 네 가지 결정</title><link>https://jaylab.link/jenkins-rolling-deploy-pipeline/</link><pubDate>Thu, 07 Aug 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/jenkins-rolling-deploy-pipeline/</guid><description>타겟 그룹 전체를 동시에 재시작하던 Jenkins 파이프라인을 서버별 순차 배포와 LB 제어로 바꾸며 내린 네 가지 결정과 아직 남은 한계</description></item><item><title>기존 JDK를 지우지 않고 JDK 11로 빌드하기: SDKMAN이 PATH를 가로채는 원리</title><link>https://jaylab.link/jdk-version-manager-sdkman/</link><pubDate>Thu, 24 Jul 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/jdk-version-manager-sdkman/</guid><description>로컬은 Corretto 21인데 빌드 테스트는 JDK 11이 필요할 때, SDKMAN이 기존 JDK를 건드리지 않고 PATH 우선순위만 가로채 세션 단위로 버전을 바꾸는 원리와 기존 JDK를 처리하는 세 가지 선택</description></item><item><title>쉘 스크립트 대신 Dockerode를 고른 이유: 멀티 테넌트 프로비저닝의 롤백과 동시성</title><link>https://jaylab.link/multi-tenant-provisioning-system/</link><pubDate>Mon, 14 Jul 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/multi-tenant-provisioning-system/</guid><description>docker run을 감싼 쉘로는 실패 원인도 롤백도 다룰 수 없어 Dockerode와 스키마 분리로 테넌트 생성을 자동화한 과정</description></item><item><title>Nginx Proxy Manager API로 Proxy Host 자동으로 붙이기</title><link>https://jaylab.link/npm-proxy-host-dynamic-creating/</link><pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/npm-proxy-host-dynamic-creating/</guid><description>테넌트가 생길 때마다 손으로 하던 도메인 연결을 Nginx Proxy Manager API로 Proxy Host를 만들어 자동화했다.</description></item><item><title>프록시가 webserver를 찾지 못한 이유: Docker 네트워크 격리와 컨테이너명 DNS</title><link>https://jaylab.link/docker-network-proxy-traffic-resolve/</link><pubDate>Thu, 26 Jun 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-network-proxy-traffic-resolve/</guid><description>NPM과 webserver가 서로 다른 Docker 네트워크에 있어 프록시가 끊긴 원인을 찾고, 네트워크 연결로 컨테이너 간 직접 통신을 복구한 기록</description></item><item><title>타 부서 제안서의 아키텍처 파트를 맡았다</title><link>https://jaylab.link/architecture-strategy-proposal/</link><pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><guid>https://jaylab.link/architecture-strategy-proposal/</guid><description>타 부서 제안서의 아키텍처·구축 전략 파트를 며칠 만에 맡아, 모호한 요구사항을 AWS 구조도로 정리한 기록</description></item><item><title>SonarQube를 brew에서 Docker로 옮겼다</title><link>https://jaylab.link/brew-sonarqube-install-and-sast/</link><pubDate>Fri, 20 Dec 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/brew-sonarqube-install-and-sast/</guid><description>SonarQube Java 분석의 .class 요구 에러를 빌드로 풀고, brew 없이도 돌도록 서버와 Scanner를 Docker로 옮긴 기록</description></item><item><title>Ansible 첫 ping은 publickey에서 막혔다</title><link>https://jaylab.link/ansible-simple-playbook-practice/</link><pubDate>Tue, 19 Nov 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/ansible-simple-playbook-practice/</guid><description>첫 Ansible ping이 publickey 오류로 막혔고, 인벤토리에 키를 지정해 풀었다. 이어서 ad-hoc 명령과 첫 플레이북까지 돌렸다.</description></item><item><title>Multipass VM 두 대로 Ansible 실습 환경 만들기</title><link>https://jaylab.link/ansible-multipass-environment/</link><pubDate>Wed, 06 Nov 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/ansible-multipass-environment/</guid><description>맥북에 Multipass VM 두 대를 띄워 제어노드에 Ansible을 설치하고, 관리노드로 SSH 키 접속을 여는 과정을 정리했다.</description></item><item><title>Mac에서 컨테이너 IP로 접속이 안 되는 이유</title><link>https://jaylab.link/docker-container-network-arch/</link><pubDate>Sat, 21 Sep 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-container-network-arch/</guid><description>Mac에서 컨테이너 IP가 보이는데도 접속되지 않는 건 Docker 네트워크가 Linux VM 안에만 있어서다.</description></item><item><title>Docker Container SSH 접속 설정</title><link>https://jaylab.link/docker-ssh-setting/</link><pubDate>Mon, 02 Sep 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-ssh-setting/</guid><description>현재 진행 중인 PoC를 위해 SSH 접속 방식 테스트가 필요했다. 그러나 Ubuntu Docker image에는 기본적으로 SSH(즉, openssh-server)가 설치되어있지 않기 때문에 이번에 Docker + SSH 접속 방식을 테스트하고 기록했다.</description></item><item><title>Ubuntu 컨테이너 생성부터 ModSecurity WAF 구성 및 테스트까지</title><link>https://jaylab.link/modsecurity-waf-with-ubuntu-setting/</link><pubDate>Mon, 12 Aug 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/modsecurity-waf-with-ubuntu-setting/</guid><description>앞서 ModSecurity에 대해 간단히 다뤘다. 이번에는 우분투 컨테이너 생성부터 ModSecurity 구성과 테스트까지 진행했고 그 과정을 기록했다.</description></item><item><title>ModSecurity. Opensource Web Application Firewall</title><link>https://jaylab.link/modsecurity-waf-docker-setting/</link><pubDate>Tue, 30 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/modsecurity-waf-docker-setting/</guid><description>부제: Docker를 사용한 ModSecurity WAF 사용기 Web Application Firewall (WAF): 웹 서비스를 통해서 공격을 시도하는 XSS, SQL Injection, Command Execute과 같은 공격을 효과적으로 차단하는 역할을 수행하는 보안 모듈</description></item><item><title>CloudWatch Alarm 활용한 ECS Fargate 스케일아웃</title><link>https://jaylab.link/cloudwatch-alarm-ecs-task-auto-scaling/</link><pubDate>Wed, 24 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/cloudwatch-alarm-ecs-task-auto-scaling/</guid><description>시간이 흐를수록 점점 분석 Batch 프로세스가 오래걸리기 시작하면서 점점 하루를 넘어가기 시작. 이를 해결하기 위해 ECS Service에 CloudWatch를 활용한 오토스케일링을 적용하였고 그 과정을 기록하였다.</description></item><item><title>MyBatis bind tag. 동적 SQL 구현</title><link>https://jaylab.link/mybatis-bind-tag-dynamic-sql-query/</link><pubDate>Thu, 11 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/mybatis-bind-tag-dynamic-sql-query/</guid><description>MyBatis에서 SQL을 동적으로 작성할 때 유용한 &lt;bind&gt;에 대해 간단하게 정리해보았다.</description></item><item><title>Docker ELK 구성에 Metricbeat 추가</title><link>https://jaylab.link/docker-elk-metricbeat/</link><pubDate>Wed, 10 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-elk-metricbeat/</guid><description>앞서 Docker를 활용한 ELK 구성에 이어서, 이번에는 Metricbeat를 붙여보았다</description></item><item><title>Docker를 활용한 ElasticSearch, Logstash, Kibana 구성</title><link>https://jaylab.link/elk-for-docker-setting/</link><pubDate>Sun, 07 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/elk-for-docker-setting/</guid><description>ELK 구성 방법 중에서도 Docker를 활용한 방법으로 진행해보았다.</description></item><item><title>Nginx Proxy Manager - Official Guide &amp; Local Setup for Customization</title><link>https://jaylab.link/npm-official-setup-and-customization/</link><pubDate>Fri, 05 Jul 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/npm-official-setup-and-customization/</guid><description>커스텀을 위해 코드 수정 후 Nginx Proxy Manager를 정상적으로 실행할 수 있어야했는데, 그 어디에서도 이를 위한 가이드를 찾을 수 없었다. 결국 직접 소스코드 분석 후 다음과 같은 내용들을 확인했다.</description></item><item><title>Aurora MySQL 업그레이드 5.7 to 8.0, 작업 전 알아두면 좋았을 주요 포인트</title><link>https://jaylab.link/aurora-mysql-upgrade-8-key-points/</link><pubDate>Tue, 25 Jun 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/aurora-mysql-upgrade-8-key-points/</guid><description>AWS Aurora MySQL 버전 업그레이드 작업 전 알아두면 좋을 내용들을 정리하였다.</description></item><item><title>Docker Buildx를 사용한 멀티 아키텍처 이미지 빌드, Github Actions 적용 사례</title><link>https://jaylab.link/docker-buildx-multiarch-and-git-actions/</link><pubDate>Thu, 13 Jun 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-buildx-multiarch-and-git-actions/</guid><description>Docker Buildx는 다양한 아키텍처에서 실행 가능한 이미지를 빌드할 수 있는 Docker CLI이다. 이를 활용하면 특정 아키텍쳐에서 지원하는 이미지로 빌드할 수 있다.</description></item><item><title>Poetry를 활용한 Python 프로젝트 관리</title><link>https://jaylab.link/python-poetry-basic/</link><pubDate>Sat, 04 May 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/python-poetry-basic/</guid><description>패키지 의존성 관리와 가상 환경 생성 및 관리를 위한 방법.</description></item><item><title>[ArgoCD] Error: Unable to create ‘..git/index.lock’: File exists</title><link>https://jaylab.link/argocd-index-lock-error/</link><pubDate>Thu, 02 May 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/argocd-index-lock-error/</guid><description>&lt;h4 id="argocd-application-생성시-에러-발생"&gt;ArgoCD Application 생성시 에러 발생&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;▼ Error message&lt;/strong&gt;
Unable to create application: application spec for saassss is invalid: InvalidSpecError: Unable to get app details: rpc error: code = Internal desc = Failed to checkout FETCH_HEAD: &lt;code&gt;git checkout --force FETCH_HEAD&lt;/code&gt; failed exit status 128: fatal: Unable to create ‘..git/index.lock’: File exists. Another git process seems to be running in this repository, e.g. an editor opened by ‘git commit’. Please make sure all processes are terminated then try again. If it still fails, a git process may have crashed in this repository earlier: remove the file manually to continue.&lt;/p&gt;</description></item><item><title>[CI/CD] Github Actions: build, push, update another repo file</title><link>https://jaylab.link/git-actions-build-push-update/</link><pubDate>Fri, 19 Apr 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-actions-build-push-update/</guid><description>Github Actions을 사용하여 멀티플랫폼이 지원되는 이미지 빌드 방법과 Docker Hub 혹은 ECR에 Push하는 방법. 그리고 제 3의 Git Repo 파일 내용 변경 방법을 정리해보았다.</description></item><item><title>[Terraform] DB Instance 삭제된 VPC 참조 현상</title><link>https://jaylab.link/terraform-error-create-intance-by-old-vpc-id/</link><pubDate>Thu, 11 Apr 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/terraform-error-create-intance-by-old-vpc-id/</guid><description>Terraform으로 DB Instance 생성시 존재하지 않는 VPC를 참조하는 현상에 대하여</description></item><item><title>Github Actions &amp; ArgoCD CI/CD (for K8S, Java App)</title><link>https://jaylab.link/git-actions-argocd-cicd-minikube/</link><pubDate>Fri, 05 Apr 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-actions-argocd-cicd-minikube/</guid><description>Github Actions를 활용해서 애플리케이션 Build &amp;amp; Image push. 애플리케이션과 Kubernetes manifest 파일 변경점 발생시 ArgoCD를 통하여 로컬 Minikube 환경에 자동배포 환경 구축하기</description></item><item><title>ArgoCD 활용 Minikube 환경에 배포(feat. AWS ECR)</title><link>https://jaylab.link/argocd-ecr-image-pull-error/</link><pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/argocd-ecr-image-pull-error/</guid><description>ArgoCD를 통해 내 로컬에 Minikube 클러스터 환경이 구성되도록 만들었다. 그리고 Dockerhub Public image를 가져오던 기존방식에서 AWS ECR의 image를 pull 하는 방식으로 바꿔보았고, 이 과정에서 발생한 문제에 대해 알게된 원인과 해결방법을 기록해보았다.</description></item><item><title>What to do about maven import errors</title><link>https://jaylab.link/maven-build-error-not-found/</link><pubDate>Wed, 20 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/maven-build-error-not-found/</guid><description>io.opentelemetry:opentelemetry-exporter-zipkin:jar:unknown was not found in &lt;a href="https://repo.maven.apache.org/maven2"&gt;https://repo.maven.apache.org/maven2&lt;/a&gt; during a previous attempt. This failure was cached in the local repository and resolution is not reattempted until the update interval of central has elapsed or updates are forced</description></item><item><title>VM Proxy 설정에 대한 HttpURLConnection &amp; HttpClient 차이</title><link>https://jaylab.link/httpurlconnection-httpclient-compare/</link><pubDate>Sun, 17 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/httpurlconnection-httpclient-compare/</guid><description>프록시 서버 구성을 하는 과정에서 알게된 Java 클래스 몇가지를 비교하여 정리하였다.</description></item><item><title>About Netcat and Netstat</title><link>https://jaylab.link/netcat-and-netstat/</link><pubDate>Fri, 15 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/netcat-and-netstat/</guid><description>&lt;h2 id="netcat"&gt;netcat&lt;/h2&gt;
&lt;p&gt;netcat: 네트워크 연결을 테스트하고 다른 서버와 포트의 통신 상태를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;netcat&lt;/code&gt;은 많은 Linux 배포판에서 사용할 수 있는 네트워킹 유틸리티로, 다양한 패키지 매니저를 통해 설치할 수 있다.&lt;/p&gt;
&lt;p&gt;주요 Linux 배포판별로 &lt;code&gt;netcat&lt;/code&gt;을 설치하고 사용하는 방법을 다음과 같이 정리했다&lt;/p&gt;
&lt;h4 id="1-debian-ubuntu-apt-사용"&gt;&lt;strong&gt;1. Debian, Ubuntu (apt 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt install netcat-openbsd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="2-fedora-centos-dnf-또는-yum-사용"&gt;&lt;strong&gt;2. Fedora, CentOS (dnf 또는 yum 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 또는 CentOS에서는 yum이 보편적인 방식&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo yum install nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="3-arch-linux-pacman-사용"&gt;&lt;strong&gt;3. Arch Linux (pacman 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pacman -Syu gnu-netcat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="4-opensuse-zypper-사용"&gt;&lt;strong&gt;4. openSUSE (zypper 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo zypper install netcat-openbsd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="5-alpine-linux-apk-사용"&gt;&lt;strong&gt;5. Alpine Linux (apk 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add netcat-openbsd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="6-amazon-linux-yum-또는-dnf-사용"&gt;&lt;strong&gt;6. Amazon Linux (yum 또는 dnf 사용)&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Amazon Linux 2의 경우:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo yum install nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Amazon Linux 2023의 경우:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv 10.244.0.74 &lt;span class="m"&gt;9092&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ss"&gt;ss&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;netstat -tuln&lt;/code&gt;:
시스템의 네트워크 연결, 라우팅 테이블, 인터페이스 통계, masquerade 연결, 멀티캐스트 멤버십 등을 보여준다.&lt;/p&gt;</description></item><item><title>Local K8S 환경 Kafka Application 구성(feat. Minikube)</title><link>https://jaylab.link/minikube-build-kafka-app/</link><pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/minikube-build-kafka-app/</guid><description>&lt;h4 id="구축-환경"&gt;구축 환경&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Mac Apple Sillicon (arm64)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기존 docker-compose.yaml 파일을 k8s 클러스터 구성하기 위한 yaml 파일로 convert하기 위해 &lt;strong&gt;kompose&lt;/strong&gt; 툴 사용&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install kompose
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kompose convert -f docker-compose.yml --out converted.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;converted.yaml&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;minikube, 위의 converted.yaml 활용하여 로컬에 k8s 클러스터 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="minikube-사용하여-기존-kafka-application-구축"&gt;minikube 사용하여 기존 Kafka Application 구축&lt;/h3&gt;
&lt;h4 id="minikube-설치"&gt;minikube 설치&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install minikube
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="minikube-클러스터-시작"&gt;minikube 클러스터 시작&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;minikube start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading="lazy" src="https://jaylab.link/minikube-build-kafka-app/image-1.png"&gt;&lt;/p&gt;
&lt;p&gt;(설치하고나면 ~/.kube/config 파일에 클러스터와 컨텍스트 관련 정보 자동으로 설정 완료)&lt;/p&gt;
&lt;h4 id="option-사용할-namespace-설정"&gt;(option) 사용할 Namespace 설정&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl create ns kafka-app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl config set-context --current --namespace&lt;span class="o"&gt;=&lt;/span&gt;kafka-app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="로컬-image-사용하도록-설정"&gt;로컬 image 사용하도록 설정&lt;/h4&gt;
&lt;p&gt;Registry에 올리지 않은, 로컬에만 있는 image를 사용하기 위해서는 다음과 같은 커맨드 실행이 필요하다.&lt;/p&gt;</description></item><item><title>Quick Guide: How to Identify Your Package Manager</title><link>https://jaylab.link/quick-guide-identify-package-manager/</link><pubDate>Mon, 04 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/quick-guide-identify-package-manager/</guid><description>Linux를 사용하다보면 그 안에서 사용 가능한 패키지 관리자 확인이 필요할 때가 있다. 쿠버네티스나 도커를 사용하면 image를 통해 pod container를 편리하게 구성할 수 있지만, 그 image가 구성된 OS가 어떤 배보판인지 확인하고 적합한 패키지관리자를 찾아야했다.</description></item><item><title>Spring Optimization: Refactoring Object-to-Map Conversion Methods</title><link>https://jaylab.link/spring-refactoring-object-to-map/</link><pubDate>Fri, 01 Mar 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/spring-refactoring-object-to-map/</guid><description>기존 Reflection 사용 방식 Object to Map 컨버팅 메서드의 성능 및 유지보수성 개선하기 위한 방법</description></item><item><title>Docker Pull Error: 두 가지 해결 방법</title><link>https://jaylab.link/docker-pull-error-two-effective-solutions/</link><pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/docker-pull-error-two-effective-solutions/</guid><description>로컬 쿠버네티스 환경에 애플리케이션 배포를 하면서 겪은 Docker Image Pull 에러. 이것을 해결할 수 있었던 몇 가지 방법에 대한 기록.</description></item><item><title>[AWS] Terraform으로 이해하는 LoadBalancer(feat. ASG)</title><link>https://jaylab.link/aws-tf-as-lb/</link><pubDate>Thu, 22 Feb 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/aws-tf-as-lb/</guid><description>LoadBalancer, Listener, Listener Rule 그리고 Health Check에 대하여</description></item><item><title>MySQL SET - Change Default Port, Public Key Error</title><link>https://jaylab.link/mysql-set-default-port-and-public-key-error/</link><pubDate>Sun, 11 Feb 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/mysql-set-default-port-and-public-key-error/</guid><description>기본 포트 설정과 Public Key Retrieval is not allowed 에러에 대하여</description></item><item><title>[IntelliJ] 프로젝트 파일 안보이는 현상 및 해결</title><link>https://jaylab.link/intellij-invisible-file/</link><pubDate>Tue, 06 Feb 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/intellij-invisible-file/</guid><description>IntelliJ에서만 보여야할 작업 디렉토리 및 파일들이 안보이는 현상과 조치방법</description></item><item><title>[AWS] Getting Metadata on Amazon Linux 2023</title><link>https://jaylab.link/aws-get-metadata-al2023/</link><pubDate>Fri, 02 Feb 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/aws-get-metadata-al2023/</guid><description>Amazon Linux 2023 Machine Image 기반 서버에서 메타데이터 조회, MySQL 연결 방법</description></item><item><title>[Nuxt] How to build a static website</title><link>https://jaylab.link/how-to-build-static-web-nuxt/</link><pubDate>Mon, 22 Jan 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/how-to-build-static-web-nuxt/</guid><description>Nuxt Framework 애플리케이션을 배포하기 위해 알아야할 기본적인 커맨드</description></item><item><title>Errors when changing the "nvm" version</title><link>https://jaylab.link/nvm-error-action/</link><pubDate>Mon, 15 Jan 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/nvm-error-action/</guid><description>nvm -v 실행시 발생했던 에러에 대한 조치방법</description></item><item><title>[Nx] Monorepo structure design</title><link>https://jaylab.link/front-monorepo/</link><pubDate>Wed, 10 Jan 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/front-monorepo/</guid><description>하나의 repository가 있다. 이 저장소의 코드는 A라는 기존 템플릿 코드에 B 템플릿 코드를 합친 형태다. 하나의 도메인 안에서 A와 B 화면이 유기적으로 사용되어야했고, A와 B에 각각에 있는 스타일이나 기능 코드를 쉽게 가져다 쓰기 위해 이렇게 병합된 형태가 되었다. 그런데 애플리케이션의 사이즈가 커지면서 점점 패키지 충돌이 발생하게 되었다. 그래서 처음부터 다시 설계하여 분리하는것이 적절하다고 판단했고, 이 때 Monolithic Repository 구조를 적용하게되었다.</description></item><item><title>[Git] Upstream commit history merge</title><link>https://jaylab.link/git-upstream-merge/</link><pubDate>Thu, 04 Jan 2024 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-upstream-merge/</guid><description>어느 Github 저장소를 Fork하여 나의 repository로 옮긴 후 이 코드를 가지고 몇번의 commit &amp;amp; push 작업을 해왔다. 어느날 메인(Fork했던 대상 Repository) 저장소에서 그 동안 이루어진 수정내역을 나의 repository에 반영해줘야할 시기가 왔다. 이 때 vs code로 진행했던 작업과정을 순서대로 기록해보았다.</description></item><item><title>[IntelliJ] CRLF/LF 개행 변경 문제 해결</title><link>https://jaylab.link/git-crlf-lf-conflict/</link><pubDate>Wed, 20 Dec 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-crlf-lf-conflict/</guid><description>인텔리제이를 통한 Git 작업시 종종 겪을 수 있는 CRLF/LF 개행 변경 관련 문제와 그 해결법</description></item><item><title>[Nuxt3] HTML to PDF</title><link>https://jaylab.link/nuxt-html-to-pdf/</link><pubDate>Tue, 12 Dec 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/nuxt-html-to-pdf/</guid><description>&lt;h2 id="nuxt3에서-html-내용-일부를-pdf-파일로-추출하는-방법"&gt;Nuxt3에서 HTML 내용 일부를 PDF 파일로 추출하는 방법&lt;/h2&gt;
&lt;p&gt;📌 기능구현에 사용된 패키지 : &amp;ldquo;html2pdf&amp;rdquo;&lt;/p&gt;
&lt;h4 id="패키지-설치"&gt;패키지 설치&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save html2pdf.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;위 명령을 입력하면 package.json 파일의 dependencies 안에 &lt;code&gt;&amp;quot;html2pdf.js&amp;quot;: &amp;quot;^0.10.1&amp;quot;,&lt;/code&gt; 한줄 추가된 것을 확인할 수 있다.&lt;/p&gt;
&lt;h4 id="html-to-pdf-code"&gt;HTML to PDF Code&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;PDF로 다운로드하고자 하는 특정 HTML 요소의 id를 html-to-pdf 라고 가정&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// PDF 다운로드 버튼 추가 예시
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;generatePdf&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; PDF Download&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;PDF 다운로드 기능 js&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;html2pdf&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;html2pdf.js&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generatePdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;html-to-pdf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;myfile.pdf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;jpeg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.98&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;html2canvas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;jsPDF&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;in&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;letter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orientation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;portrait&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// New Promise-based usage:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;html2pdf&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>[Git] 소스코드 Push 무한로딩 현상</title><link>https://jaylab.link/git-push-infinite-loading/</link><pubDate>Thu, 07 Dec 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-push-infinite-loading/</guid><description>Source Push infinite loading</description></item><item><title>[Flutter] BottomNavigationBar 구현시 메모리 고려사항</title><link>https://jaylab.link/flutter-bottom-nav-memory/</link><pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/flutter-bottom-nav-memory/</guid><description>&lt;h3 id="기존-코드"&gt;기존 코드&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dart" data-lang="dart"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;package:flutter/material.dart&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;package:meals_clone/screens/categories.dart&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// import &amp;#39;package:riverpod/riverpod.dart&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TabScreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_TabScreenState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_TabScreenState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;categoriesScreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CategoriesScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Placeholder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;activePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CategoriesScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_selectPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;activePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;categoriesScreen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;activePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Categories&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;centerTitle:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// body: const CategoriesScreen(),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;activePage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;bottomNavigationBar:&lt;/span&gt; &lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_meal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;카테고리&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;star&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;즐겨찾기&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;showSelectedLabels:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 선택된 레이블 숨기기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;showUnselectedLabels:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 선택되지 않은 레이블 숨기기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;currentIndex:&lt;/span&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="n"&gt;_selectPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="수정된-코드"&gt;수정된 코드&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dart" data-lang="dart"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;package:flutter/material.dart&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;package:meals_clone/screens/categories.dart&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// import &amp;#39;package:riverpod/riverpod.dart&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TabScreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_TabScreenState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_TabScreenState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TabScreen&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_selectPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 현재 선택된 페이지에 따라 적절한 위젯을 동적으로 선택합니다.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;activePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CategoriesScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Placeholder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Categories&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;centerTitle:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;activePage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;bottomNavigationBar:&lt;/span&gt; &lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_meal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;카테고리&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;star&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;즐겨찾기&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;showSelectedLabels:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 선택된 레이블 숨기기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;showUnselectedLabels:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 선택되지 않은 레이블 숨기기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;currentIndex:&lt;/span&gt; &lt;span class="n"&gt;_selectedPageIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="n"&gt;_selectPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="수정된-내용"&gt;수정된 내용&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;불필요한 인스턴스화 제거:
&lt;code&gt;categoriesScreen&lt;/code&gt;과 &lt;code&gt;placeholder&lt;/code&gt; 위젯을 미리 인스턴스화할 필요가 없다.
이들은 필요할 때 생성될 수 있으며, 위와 같이 수정하면 메모리 사용량을 줄이고 성능을 향상시킬 수 있다.&lt;/p&gt;</description></item><item><title>[Flutter] 에러 내용 “MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider)”</title><link>https://jaylab.link/flutter-missing-plugin-exception/</link><pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/flutter-missing-plugin-exception/</guid><description>&lt;h3 id="에러-발생"&gt;에러 발생&lt;/h3&gt;
&lt;p&gt;개발 중 다음과 같은 로그가 발생했다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Hot Restart&lt;/code&gt;를 해도 동일하게 에러가 출력.&lt;/p&gt;
&lt;p&gt;그러나 동작에는 문제가 없는 상황&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;h3 id="해결-"&gt;해결 💢&lt;/h3&gt;
&lt;p&gt;단순히 IDE와 시뮬레이터 재부팅 혹은 &lt;code&gt;flutter clean&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Flutter 공식 이슈이기도 한 문제 ⇒ &lt;a href="https://github.com/flutter/flutter/issues/10912"&gt;https://github.com/flutter/flutter/issues/10912&lt;/a&gt;&lt;/p&gt;</description></item><item><title>[Git] Private Repo Clone 방법 및 RSA 키에 대한 이해</title><link>https://jaylab.link/git-clone-private-repo/</link><pubDate>Tue, 07 Nov 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/git-clone-private-repo/</guid><description>&lt;div class="callout callout-gray"&gt;&lt;div class="callout-body"&gt;본 내용과 관련해서는 더 간단하게 설명된 다른 블로그 글들이 많이 있다.
그러나 그 각각의 내용이 무엇인지, 그것들이 어떻게 동작되는지 궁금해서
이 기회에 이것저것 찾고 알게된 내용들을 정리해보았다.&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;목차 -&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="사전-지식"&gt;사전 지식&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;RSA 키 쌍의 의미와 사용 목적&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RSA 키 쌍은 공개 키와 개인 키로 구성되며, 이들은 서로 수학적으로 연관되어 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;공개 키(public key)&lt;/strong&gt;: 이 키는 안전하게 공유될 수 있으며, Git 서버에 등록된다. 서버는 이 공개 키를 사용하여 사용자의 인증 정보를 확인한다.&lt;/p&gt;</description></item><item><title>[Nuxt3] Layout 설정 및 동적 변경</title><link>https://jaylab.link/nuxt-layout-setting/</link><pubDate>Tue, 31 Oct 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/nuxt-layout-setting/</guid><description>&lt;p&gt;Nuxt3에서는 vue 파일에 특별한 설정이 없다면 해당 페이지의 레이아웃은 “&lt;code&gt;default&lt;/code&gt;” 레이아웃으로 설정된다.&lt;/p&gt;
&lt;h4 id="default-레이아웃-예시"&gt;default 레이아웃 예시&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="실무에서-사용중인-레이아웃-예시"&gt;실무에서 사용중인 레이아웃 예시&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;~/layouts/&lt;/code&gt; 경로에 &lt;code&gt;default.vue&lt;/code&gt; 파일을 추가하여 설정할 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;page&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;page font--jakarta&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;HeaderLayout1&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;FooterLayout1&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;*default.vue *&lt;/p&gt;
&lt;h4 id="-참고"&gt;※ 참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;slot&lt;/code&gt; 태그는 페이지를 렌더링하는 특수한 태그&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;해당 태그가 존재하지 않을 경우, 이상 동작할 수 있으므로 반드시 포함 필요&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;단, 페이지 영역을 숨겨할 경우는 &lt;code&gt;v-show&lt;/code&gt; 등을 사용하여 제어해야 하며, &lt;code&gt;v-show&lt;/code&gt;는 반드시 slot이 아닌 상위 Element에 위치해야 한다&lt;/p&gt;</description></item><item><title>[Nuxt3] $fetch 함수에 대하여(feat. 일반적인 fetch와의 차이)</title><link>https://jaylab.link/nuxt-about-fetch/</link><pubDate>Mon, 23 Oct 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/nuxt-about-fetch/</guid><description>&lt;p&gt;우선 fetch와 $fetch를 사용한 각각의 예시코드부터 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fetch 사용 예시&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAsyncData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nuxt3&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAsyncData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://localhost:8080/api/user/1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;$fetch 사용 예시&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;useAsyncData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;mountains&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;$fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://api.nuxtjs.dev/mountains&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="fetch-fetch-차이점"&gt;fetch, $fetch 차이점&lt;/h3&gt;
&lt;p&gt;위의 코드를 살펴보면 둘 다 &lt;code&gt;useAsyncData&lt;/code&gt;의 두 번째 매개변수로 함수를 전달하고 있다. 이 함수는 데이터를 가져오는 데 필요한 비동기 작업을 수행하며, 이 함수가 반환하는 Promise의 결과값이 &lt;code&gt;useAsyncData&lt;/code&gt;의 &lt;code&gt;data&lt;/code&gt;로 설정된다.&lt;/p&gt;
&lt;p&gt;이 중 첫 번째 코드는 &lt;code&gt;fetch&lt;/code&gt; 함수를 직접 사용하여 HTTP 요청을 보낸 후 &lt;code&gt;.then&lt;/code&gt; 메서드를 사용하여 응답을 처리하는 내용이다. 그리고 응답값을 JSON 형식으로 파싱하기 위해 &lt;code&gt;response.json()&lt;/code&gt;을 직접 호출하고있다. &lt;code&gt;response.json()&lt;/code&gt;은 Fetch API에서 제공하는 메서드로, 응답값을 JSON 형식으로 파싱한다.
두 번째 코드에서 &lt;code&gt;$fetch&lt;/code&gt;를 사용하면 이 메서드가 자동으로 적용된다. &lt;code&gt;$fetch&lt;/code&gt;는 내부적으로 &lt;code&gt;fetch&lt;/code&gt;를 사용하되, 추가적인 처리를 수행한다. 그 중 하나가 바로 응답을 자동으로 JSON 형식으로 파싱하는것이기 때문에 &lt;code&gt;$fetch&lt;/code&gt;를 사용하면 &lt;code&gt;response.json()&lt;/code&gt;을 호출하지 않아도 된다.&lt;/p&gt;</description></item><item><title>[Nuxt3] useFetch, useAsyncData의 차이</title><link>https://jaylab.link/nuxt-usefetch-useasyncdata/</link><pubDate>Thu, 12 Oct 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/nuxt-usefetch-useasyncdata/</guid><description>&lt;p&gt;Nuxt3에서는 &lt;code&gt;useAsyncData&lt;/code&gt;와 &lt;code&gt;useFetch&lt;/code&gt;라는 훅을 지원한다. 이 둘은 코드 구성 형태가 비슷하기 때문에 차이점은 무엇인지 정리해보았다.&lt;/p&gt;
&lt;h4 id="usefetch"&gt;useFetch&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useFetch&lt;/code&gt;는 created 단계 이후에 실행 (즉 ,새로운 Vue 인스턴스가 만들어진 후 데이터가 준비된 후에 실행)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이렇게 하면 &lt;code&gt;useFetch&lt;/code&gt;가 실행되는 시점에는 컴포넌트의 데이터가 이미 준비되어 있으므로, 이 데이터를 기반으로 서버로부터 필요한 데이터를 요청하거나 필요한 작업을 수행할 수 있다. 이는 비동기적으로 데이터를 처리하는 웹 애플리케이션에서 매우 중요한 부분&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="useasyncdata"&gt;useAsyncData&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useAsyncData&lt;/code&gt;는 &lt;code&gt;created&lt;/code&gt; 단계 이전에 즉, 첫 렌더링이 시작되기 전에 실행&lt;/p&gt;</description></item><item><title>Redis 세션 클러스터링 설정 (feat. Redisson)</title><link>https://jaylab.link/redis-session-clustering/</link><pubDate>Tue, 03 Oct 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/redis-session-clustering/</guid><description>How to Enable Session Clustering in &amp;ldquo;Low-level Spring Framework&amp;rdquo; and &amp;ldquo;Redis with Encryption”</description></item><item><title>Delimiter를 통한 다중 검색 처리</title><link>https://jaylab.link/delimiter-multi-searching/</link><pubDate>Mon, 25 Sep 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/delimiter-multi-searching/</guid><description>&lt;h3 id="들어가며"&gt;들어가며&lt;/h3&gt;
&lt;p&gt;현재 투입되어있는 프로젝트의 서비스 중에는 특정 데이터를 조회할 수 있는 화면이 있습니다.&lt;/p&gt;
&lt;p&gt;그런데 이 화면을 사용하게되는 업무 특성상 데이터를 일일이 하나씩 조회하기가 번거롭다는 불편사항이있어&lt;/p&gt;
&lt;p&gt;이를 한번에 다중 검색 가능하도록 수정 조치하였고, 당시 작업내용을 핵심부분만 간략하게 정리하였습니다.&lt;/p&gt;
&lt;h3 id="기존-방식"&gt;기존 방식&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;mapper.xml&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ticketSearch&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;(method_info != null and method_info != &amp;#39;&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;method_id == &amp;#39;gkId&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GK_ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method_info&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;method_id == &amp;#39;parentTicketId&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRNT_TKT_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method_info&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ESCAPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;method_id == &amp;#39;ticketId&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TKT_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method_info&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ESCAPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;method_id == &amp;#39;guid&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GUID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method_info&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;list&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameterType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;map&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resultMap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ticketVO&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;컬럼&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;컬럼&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;컬럼&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;테이블&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEL_YN_FG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;N&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;refid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ticketSearch&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;▲ 화면에서 입력한 문자열 1개를 포함하고있는 데이터를 “like” 검색을 통한 단일 조회 방식&lt;/p&gt;</description></item><item><title>Autoscaling 적용을 위한 스케줄(@Scheduled) 로직 변경</title><link>https://jaylab.link/scheduler-transition/</link><pubDate>Thu, 21 Sep 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/scheduler-transition/</guid><description>&lt;h4 id="들어가며"&gt;들어가며&lt;/h4&gt;
&lt;p&gt;현재 투입되어있는 프로젝트에서는 최근 기존의 인프라 구성에서 많은 부분 전환된 일정이 있었습니다.
그 당시 Autoscaling을 적용하기 위해서 예상되는 문제점은 무엇이 있었는지, 그리고 그것을 어떻게 극복했는지 다음과 같이 정리하였습니다.&lt;/p&gt;
&lt;h4 id="기존-환경"&gt;기존 환경&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Autoscaling이 적용되어있지 않은 was EC2 2개 운영&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;해당 서버의 Spring Framework 애플리케이션 내에는 &lt;code&gt;@Scheduled&lt;/code&gt; 어노테이션을 통한 스케줄잡 사용&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일부 스케줄 관련 코드 :&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;0 30 9 ? * *&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;GMT+9:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;notifyLongTermNoUseAdmin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GkConstant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WAS_NUMBER&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// STG, PRD 의 1번 서버인 경우에만 Batch 실행&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LongTermNoUseAdminVO&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifyAdminDataList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;longTermNoUseAdminSvc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNotifyAdminDataList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LongTermNoUseAdminVO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notifyAdminDataList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mailReceiverIDList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Arrays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccountId&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 메일 수신인 리스트에 장기 미사용자의 이메일 주소만 추출해 추가&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 대상자별 mail template parameter 가 다르기 때문에 for 문을 통해 개별 메일 발송&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awssesClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mailReceiverIDList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;[&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ospAppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGkServerType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MailEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NOTIFICATION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMailSubject&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setMailHtmlBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MailEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NOTIFICATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;##### SUCCESS Sending Notification Email : {}, {}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSaGuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccountId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;##### FAIL Sending Notification Email : {}, {}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSaGuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;voNotifyAdmin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccountId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;##### SUCCESS notifyLongTermNoUseAdmin Batch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;##### FAIL notifyLongTermNoUseAdmin Batch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;위와 같이 “코드가 실행되는 두대의 서버 중 vm option의 특정 시스템 프로퍼티가 ‘1’로 되어있는 서버에서만 동작”하도록 조건이 설정 (&lt;code&gt;if(&amp;quot;1&amp;quot;.equals(GkConstant.WAS_NUMBER))&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="문제점"&gt;문제점&lt;/h4&gt;
&lt;p&gt;Autoscaling을 적용하게되면 서버가 동적으로 생성 및 삭제되기 때문에 어느서버가 1번 서버인지 기준이 사라지는 상황. 따라서 기존의 Scheduled 로직이 여러 서버에서 중복으로 실행되거나 혹은 전혀 실행될 수 없는 로직&lt;/p&gt;</description></item><item><title>Open vscode with the Terminal 'code .' command</title><link>https://jaylab.link/vscode-open-command/</link><pubDate>Thu, 03 Aug 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/vscode-open-command/</guid><description>&lt;h3 id="명령어를-통해-visual-studio-code로-특정-경로-열기"&gt;명령어를 통해 &lt;strong&gt;Visual Studio Code&lt;/strong&gt;로 특정 경로 열기&lt;/h3&gt;
&lt;p&gt;터미널에서 작업을 하다보면 종종 특정 경로를 “&lt;strong&gt;Visual Studio Code&lt;/strong&gt;”로 바로 열고싶을 때가 있다.
이럴때 사용할 수 있는 방법으로, 터미널에서 &lt;code&gt;code .&lt;/code&gt; 명령을 통해서 현재 위치를 vscode로 열거나
&lt;code&gt;code ~/열고싶은 디렉토리 경로&lt;/code&gt; 명령으로 열 수 있는데, 이 커맨드를 사용하기 위해서는 다음과 같은 과정이 필요하다.&lt;/p&gt;
&lt;h3 id="code-명령-설치"&gt;‘code’ 명령 설치&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Visual Studio Code 열고 &lt;code&gt;cmd + shift + p&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;code&lt;/code&gt; 입력 후 &lt;code&gt;Shell Command: Install ‘code’ command in PATH&lt;/code&gt; 클릭&lt;/p&gt;</description></item><item><title>[JS] input box 사용자 편의성에 대한 고민</title><link>https://jaylab.link/js-input-upper-lower-case/</link><pubDate>Tue, 01 Aug 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/js-input-upper-lower-case/</guid><description>Thinking about user usability for &amp;lsquo;input box’</description></item><item><title>[AWS] 콘솔과 CLI 접속시 특정 IP 외 접근 차단 정책</title><link>https://jaylab.link/aws-access-deny-policy/</link><pubDate>Wed, 26 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/aws-access-deny-policy/</guid><description>Policy to block certain IP access to the console or CLI</description></item><item><title>[JS] document.cookie 사용 이슈 및 해결 방법</title><link>https://jaylab.link/js-cookie/</link><pubDate>Fri, 21 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/js-cookie/</guid><description>&lt;h2 id="documentcookie"&gt;document.cookie&lt;/h2&gt;
&lt;hr&gt;
&lt;h4 id="들어가며"&gt;들어가며&lt;/h4&gt;
&lt;p&gt;Frontend에서 Cookie를 다룰 때, 일반적으로 javascript의 &lt;code&gt;document.cookie&lt;/code&gt; 를 통해 get, set, delete 할 수 있다. 이러한 cookie는 같은 브라우저 내에서 값을 저장, 전달, 공유할 수 있다는 점에서 유용하게 사용된다. 현재 투입되어있는 프로젝트에서도 &lt;code&gt;document.cookie&lt;/code&gt;로 쿠키가 관리되고있으며, 다음과 같은 용도로 사용되고있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;다른 화면으로 파라미터를 넘기기 위해&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;뒤로가기 시 이전의 검색조건 값을 다시 불러오기 위해&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;window.open&lt;/code&gt; 을 통한 팝업 창에서 부모 창으로 값을 전달하기 위해&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="문제점"&gt;문제점&lt;/h4&gt;
&lt;p&gt;이러한 &lt;code&gt;document.cookie&lt;/code&gt;를 단순하게 사용하기에는 한 가지 문제가 있었다.
사용자가 목록페이지에서 “각기 다른 여러 글”을 &lt;code&gt;Ctrl&lt;/code&gt;을 누른채 클릭하여 새로운 탭으로 띄울때, 그리고 이 과정이 짧은 시간에 이루어질때 몇가지 문제가 발생했다. A, B, C 글을 짧은시간에 ‘거의’ 동시에 띄울경우 A, B, C 글에 대한 각각의 탭이 열리는게 아닌, C의 글만 3개가 띄워진 것. 이는 A탭이 열린 직 후 A에 대한 파라미터를 담은 쿠키를 조회하기 전, C에 대한 쿠키로 덮어쓰기되어 발생된 현상이었다.&lt;/p&gt;</description></item><item><title>[DB] redis-cli 명령어 종류 및 자료구조 정리</title><link>https://jaylab.link/redis-cli-and-data-structure/</link><pubDate>Fri, 14 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/redis-cli-and-data-structure/</guid><description>&amp;lsquo;Commands used in Redis and summary of &amp;lsquo;data structure&amp;rsquo;</description></item><item><title>[AWS] ElastiCache for Redis 생성 및 접속</title><link>https://jaylab.link/redis-create-access/</link><pubDate>Mon, 10 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/redis-create-access/</guid><description>&lt;div class="callout callout-yellow"&gt;&lt;span class="callout-icon"&gt;📌&lt;/span&gt;&lt;div class="callout-body"&gt;테스트 환경 : Mac OS M1&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="redis-클러스터-생성-설정값"&gt;Redis 클러스터 생성 설정값&lt;/h3&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;‘클러스터 모드’ &lt;strong&gt;비활성화&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AWS ElastiCache for Redis 생성시 default로 잡히는 포트는 &lt;code&gt;6379&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그러나 보안을 위해 default 포트는 피해서 &lt;code&gt;6480&lt;/code&gt;으로 설정&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;암호화는 ‘저장 중 암호화, 전송 중 암호화’ 모두 활성화하여 생성&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="redis-접근"&gt;Redis 접근&lt;/h3&gt;
&lt;hr&gt;
&lt;h4 id="redis-접근-조건"&gt;&lt;strong&gt;Redis 접근 조건&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;로컬에서 AWS Redis에 cli로 접근하기 위해서는 “로컬 → Redis”로 다이렉트로 접근할 수 없음
외부 접근 비허용. 같은 VPC 내부의 인스턴스를 통해서만 접근할 수 있음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이러한 특징 때문에 로컬에서 Redis에 접속하기 위해서는 Bastion Host가 필요&lt;/p&gt;</description></item><item><title>[AWS] ElastiCache for Redis 구성요소</title><link>https://jaylab.link/redis-component/</link><pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/redis-component/</guid><description>Components of &amp;lsquo;ElastiCache for Redis’</description></item><item><title>[DB] Redis란 무엇인가? 기본적인 개념</title><link>https://jaylab.link/redis-basic-concepts/</link><pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/redis-basic-concepts/</guid><description>Basic concepts about &amp;lsquo;Redis’</description></item><item><title>[IntelliJ] 정적 리소스 변경사항 자동 반영</title><link>https://jaylab.link/intellij-resource-build/</link><pubDate>Mon, 26 Jun 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/intellij-resource-build/</guid><description>Automatically reflect static resource changes</description></item><item><title>[IntelliJ] java 파일 서버 재실행 없이 변경사항 적용 (HotSwap)</title><link>https://jaylab.link/injellij-hotswap/</link><pubDate>Wed, 21 Jun 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/injellij-hotswap/</guid><description>Apply changes to java files without restarting the server</description></item><item><title>Custom Domain and Create DNS</title><link>https://jaylab.link/aws-dns/</link><pubDate>Tue, 06 Jun 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/aws-dns/</guid><description>&lt;p&gt;SSL 서버인증서&lt;/p&gt;
&lt;p&gt;HTTP : 암호화되지 않은상태&lt;/p&gt;
&lt;p&gt;HTTPS : 암호화된 상태&lt;/p&gt;
&lt;p&gt;브라우저로부터 어떤 웹서버로 보내진 암호화된 데이터는 사이트 운영자가 가진 비밀키를 이용해 원래 데이터로 되돌릴 수 있다. 그러나 운영자 외 악의를 가진 누군가가 운영자로 위장한 (피싱) 사이트를 구축해 데이터를 훔칠 가능성도 있다.&lt;/p&gt;
&lt;p&gt;이 경우, 위장하지 않았음을 보장해주는 회사들을 **‘인증기관’**이라 부르며, 이 보장을 증명하는 것이 &lt;strong&gt;SSL 서버 인증서(인증서)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;브라우저 주소창에서 열쇠아이콘을 클릭하면 볼수 있는 것이 &lt;strong&gt;SSL 서버 인증서&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Route53의 두 가지 기능&lt;/p&gt;</description></item><item><title>Using AWS to Build Infrastructure</title><link>https://jaylab.link/aws-basic-infrastructure/</link><pubDate>Mon, 05 Jun 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/aws-basic-infrastructure/</guid><description>&lt;p&gt;&lt;strong&gt;VPC&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;vpc cidr : 10.0.0.0/16&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;서브넷&lt;/strong&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Subnet 간단 개념&lt;/summary&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;개념 : VPC의 IP 주소 범위를 나누는 단위.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;역할분리 - 외부에 공개하느 리소스 여부를 구별&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;기기 분리 - AWS 안에서의 물리적인 이중화(다중화)를 수행&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;p&gt;subnet cidr :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;10.0.0.0/20(public1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;10.0.16.0/20(public2)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;10.0.64.0/20(private1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;10.0.80.0/20(private2)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Internet Gateway&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;internet gateway 생성 후 vpc에 연결&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NAT Gateway&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NAT Gateway : private subnet 2개에 각각 대응해줄 2개의 NAT Gateway 생성 후
“퍼블릭 서브넷”에 1개씩 연결
( + eip 할당 )&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;라우팅 테이블&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Tomcat 배포 방식의 올바른 이해와 구조적 분석</title><link>https://jaylab.link/tomcat-improve-cicd-pipeline/</link><pubDate>Thu, 11 May 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/tomcat-improve-cicd-pipeline/</guid><description>&lt;p&gt;Tomcat 서버의 애플리케이션 배포는 웹 애플리케이션 운영에서 가장 기본적인 영역이면서도, 실제로는 많은 개발자들이 정확한 동작 원리를 놓치고 있는 부분이다. 최근 고객사 운영 서버의 &lt;code&gt;server.xml&lt;/code&gt; 설정을 분석하던 중, &lt;code&gt;&amp;lt;Context&amp;gt;&lt;/code&gt; 태그와 &lt;code&gt;&amp;lt;Host&amp;gt;&lt;/code&gt;의 &lt;code&gt;appBase&lt;/code&gt; 속성이 혼재된 구성에서 WAR 파일 배포 과정에 대한 오해를 발견했다.&lt;/p&gt;
&lt;h3 id="기존의-배포-설정"&gt;기존의 배포 설정&lt;/h3&gt;
&lt;p&gt;운영 중인 서버의 &lt;code&gt;server.xml&lt;/code&gt; 설정:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;Host&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;appBase=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/home/manager/eps/webapps&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;unpackWARs=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;autoDeploy=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;Context&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;docBase=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/home/manager/eps/webapps/legacy&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;reloadable=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;Context&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/v5&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;docBase=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/home/manager/eps/webapps/ui5&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;reloadable=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- ... AccessLogValve 설정 ... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/Host&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 설정에서 명시적인 &lt;code&gt;&amp;lt;Context&amp;gt;&lt;/code&gt; 배포와 &lt;code&gt;appBase&lt;/code&gt;를 통한 자동 배포가 공존하고 있었음.. 그런데 여기서 문제가 발생. 새로운 버전의 &lt;code&gt;ui5.war&lt;/code&gt; 파일을 배포하려 할 때, 기존 관리자는 다음과 같은 절차를 따르고 있었다.&lt;/p&gt;</description></item><item><title>Try Python's argparse</title><link>https://jaylab.link/python-argparse/</link><pubDate>Sun, 23 Apr 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/python-argparse/</guid><description>&lt;h2 id="argparse"&gt;Argparse&lt;/h2&gt;
&lt;p&gt;argparse를 이용하면 파이썬 스크립트 실행시 사용자 임의의 Argument값을 넘겨서 활용할 수 있다.&lt;/p&gt;
&lt;h3 id="유용한-argparse에-대하여-간단한-예제"&gt;&lt;strong&gt;유용한 argparse에 대하여 간단한 예제&lt;/strong&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 인자값을 받을 수 있는 인스턴스 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;사용법 테스트입니다.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 입력받을 인자값 등록&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-testArg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;testDest&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python argparse.py -testArg=&lt;/span&gt;&lt;span class="si"&gt;{입력값}&lt;/span&gt;&lt;span class="s2"&gt; 형태로 입력하세요~&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 입력받은 인자값을 args에 저장 (type: namespace)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testDest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;결과값: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testDest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="사용예시--"&gt;**사용예시 : **&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;» python argparseTest.py --help
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usage: argparseTest.py &lt;span class="o"&gt;[&lt;/span&gt;-h&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;-testArg TESTDEST&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;사용법 테스트입니다.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;optional arguments:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -h, --help show this &lt;span class="nb"&gt;help&lt;/span&gt; message and &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -testArg TESTDEST python argparse.py -testArg&lt;span class="o"&gt;={&lt;/span&gt;입력값&lt;span class="o"&gt;}&lt;/span&gt; 형태로 입력하세요~
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="결과값-"&gt;결과값 :&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;» python argparseTest.py -testArg&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;123&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;결과값: 123&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>How to separate Python's development environment (venv)</title><link>https://jaylab.link/python-venv/</link><pubDate>Sat, 22 Apr 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/python-venv/</guid><description>&lt;h3 id="venv가상환경-사용-이유"&gt;venv(가상환경) 사용 이유&lt;/h3&gt;
&lt;p&gt;파이썬을 다루다보면 다양한 패키지들을 사용하게 된다.&lt;/p&gt;
&lt;p&gt;그렇게, &lt;code&gt;pip3 install {패키지명}&lt;/code&gt;을 무분별하게 사용하다보면 로컬에 있는 다른 프로젝트 간 패키지 버전 충돌 혹은 패키지(라이브러리)간 의존성 문제가 발생하기도 한다.&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 ‘&lt;code&gt;venv&lt;/code&gt;’라는 파이썬 모듈로 패키지 환경 분리가 가능하며, 작업환경이 바뀌더라도 필요한 패키지들을 동일한 버전으로 설치해 작업할 수도 있다.&lt;/p&gt;
&lt;h3 id="현재-설치되어있는-파이썬-패키지-목록-확인"&gt;현재 설치되어있는 파이썬 패키지 목록 확인&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="가상환경-생성"&gt;&lt;strong&gt;가상환경 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m venv myenv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;venv&lt;/code&gt; 라는 &lt;code&gt;모듈&lt;/code&gt;을 사용하겠다는 의미&lt;/p&gt;
&lt;p&gt;&lt;code&gt;myenv&lt;/code&gt;는 사용자 임의의 가상환경 별칭&lt;/p&gt;</description></item><item><title>Defense against injection attacks</title><link>https://jaylab.link/python-injection/</link><pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/python-injection/</guid><description>&lt;div class="callout callout-yellow"&gt;&lt;span class="callout-icon"&gt;❓&lt;/span&gt;&lt;div class="callout-body"&gt;&lt;em&gt;&lt;strong&gt;Injetion : 보안 취약점을 이용하여, 임의의 SQL 문을 주입하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위&lt;/strong&gt;&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;** 📜 Content**&lt;/p&gt;
&lt;h3 id="injection-공격-예시"&gt;🗡️ Injection 공격 예시&lt;/h3&gt;
&lt;hr&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;/*&lt;/span&gt; &lt;span class="err"&gt;어떠한&lt;/span&gt; &lt;span class="err"&gt;POST방식&lt;/span&gt; &lt;span class="err"&gt;API에&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;아래와&lt;/span&gt; &lt;span class="err"&gt;같은&lt;/span&gt; &lt;span class="err"&gt;인자값을&lt;/span&gt; &lt;span class="err"&gt;넘기는&lt;/span&gt; &lt;span class="err"&gt;것이&lt;/span&gt; &lt;span class="err"&gt;정상적인&lt;/span&gt; &lt;span class="err"&gt;방법일&lt;/span&gt; &lt;span class="err"&gt;때*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;testKey&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;testValue&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;/*&lt;/span&gt; &lt;span class="err"&gt;악의적인&lt;/span&gt; &lt;span class="err"&gt;사용자는&lt;/span&gt; &lt;span class="err"&gt;다음과&lt;/span&gt; &lt;span class="err"&gt;같이&lt;/span&gt; &lt;span class="err"&gt;인자값을&lt;/span&gt; &lt;span class="err"&gt;설정하여&lt;/span&gt; &lt;span class="err"&gt;쿼리를&lt;/span&gt; &lt;span class="err"&gt;조작할&lt;/span&gt; &lt;span class="err"&gt;수&lt;/span&gt; &lt;span class="err"&gt;있다.&lt;/span&gt; &lt;span class="err"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;testKey&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;testValue&amp;#39; and &amp;#39;해킹&amp;#39;=&amp;#39;해킹&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;위 인자값이 SQL Query에 적용될 때 정상적인 인자값을 넘겨받은 쿼리&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testTable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;testValue&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Injection 공격을 시도된 쿼리&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testTable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;testValue&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;해킹&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;해킹&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="python에-대한-injection-방어-코드"&gt;🛡️ &lt;strong&gt;python에 대한 injection 방어 코드&lt;/strong&gt;&lt;/h3&gt;
&lt;hr&gt;
&lt;h4 id="실무에서-사용된-db-호출-코드-방식-injection-대응-전"&gt;&lt;strong&gt;실무에서 사용된 DB 호출 코드 방식 (Injection 대응 전)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;psycopg2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;psycopg2.extras&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RealDictCursor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 연결 준비&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dbname=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; user=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; host=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; password=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_pass&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 호출함수 선언&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RealDictCursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# SQL 실행&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# DB조회 결과값 담기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SQL 쿼리 선언&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;select pjt_no, item_type from testTable &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;where pjt_no = &amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pjt_no&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#39; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;and item_type = &amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;type_cd2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 호출함수 실행&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="injection-대응-type-1"&gt;&lt;strong&gt;Injection 대응 (Type 1)&lt;/strong&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;psycopg2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;psycopg2.extras&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RealDictCursor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 연결 준비&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;dbname=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; user=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; host=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; password=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_pass&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 호출함수 선언&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RealDictCursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# SQL 실행&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# DB조회 결과값 담기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SQL 쿼리 선언&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;select pjt_no, item_type from testTable &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;where pjt_no = &lt;/span&gt;&lt;span class="si"&gt;%(pjt_no)s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;and item_type = &lt;/span&gt;&lt;span class="si"&gt;%(item_type)s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sql_arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;pjt_no&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pjt_no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;item_type&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DB 호출함수 실행&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sql_arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="injection-대응-type-2"&gt;&lt;strong&gt;Injection 대응 (Type 2)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;(&lt;strong&gt;딕셔너리 타입(X) → tuple 타입으로 넘기는 것도 가능)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Hello world!</title><link>https://jaylab.link/about/</link><pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate><guid>https://jaylab.link/about/</guid><description>&lt;div class="callout callout-gray"&gt;&lt;span class="callout-icon"&gt;✅&lt;/span&gt;&lt;div class="callout-body"&gt;The blog works by importing posts written in Notion!&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Hello, I am &lt;code&gt;jay&lt;/code&gt;, the owner of the blog. 🙂&lt;/p&gt;
&lt;p&gt;If there is anything wrong or if you have any questions, please feel free to contact us by email. thank you! 😎&lt;/p&gt;
&lt;figure&gt;
&lt;img loading="lazy" src="image-1.png"
alt="Blhamz wallpaper"/&gt; &lt;figcaption&gt;
&lt;p&gt;Blhamz wallpaper&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;GITHUB.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jay-lab"&gt;https://github.com/jay-lab&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Resume.</title><link>https://jaylab.link/resume/</link><pubDate>Sat, 25 Jun 2022 00:00:00 +0000</pubDate><guid>https://jaylab.link/resume/</guid><description>&lt;h2 id="morethanmin-software-engineer"&gt;&lt;strong&gt;morethanmin, Software Engineer&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://jaylab.link/resume/image-1.png"&gt;&lt;/p&gt;
&lt;h4 id="contact"&gt;Contact.&lt;/h4&gt;
&lt;p&gt;Email. &lt;a href="mailto:morethanmin.dev@gamil.com"&gt;morethanmin.dev@gamil.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Phone. 010-1234-1234&lt;/p&gt;
&lt;h4 id="channel"&gt;Channel.&lt;/h4&gt;
&lt;p&gt;GitHub. &lt;a href="https://github.com/morethanmin"&gt;https://github.com/morethanmin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Blog. &lt;a href="https://morethanmin.web.app/"&gt;https://morethanmin.web.app&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="introduce"&gt;Introduce.&lt;/h2&gt;
&lt;p&gt;hello? I am a developer who loves to code. hello? I am a developer who loves to code. This is the requirements test page. This is the requirements test page. This is the requirements test page. This is the requirements test page. This is the requirements test page.&lt;/p&gt;
&lt;p&gt;The cat is doing the coding for me. The cat is doing the coding for me.&lt;/p&gt;</description></item></channel></rss>