<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>codingBird</title>
    <link>https://ddurubird.tistory.com/</link>
    <description>탑을 쌓고 있습니다.</description>
    <language>ko</language>
    <pubDate>Fri, 17 Apr 2026 09:40:22 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>김뚜루</managingEditor>
    <image>
      <title>codingBird</title>
      <url>https://tistory1.daumcdn.net/tistory/5003792/attach/763438ea45144b0092fea6b59a71ee54</url>
      <link>https://ddurubird.tistory.com</link>
    </image>
    <item>
      <title>TIL - AWS 키워드 정리</title>
      <link>https://ddurubird.tistory.com/311</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lambda&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Paas(Platform as a service)&lt;/li&gt;
&lt;li&gt;서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있게 해주는 컴퓨팅 서비스&lt;/li&gt;
&lt;li&gt;Lambda는 고가용성 컴퓨팅 인프라에서 코드를 실행하고 서버와 운영 체제, 유지 관리, 용량 프로비저닝, 코드 및 보안 패치 배포 등 모든 컴퓨팅 리소스를 관리 수행한다.&lt;/li&gt;
&lt;li&gt;Lambda를 사용하면 Lambda가 지원하는 언어 런타임 중 하나로 코드를 제공하면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 처리 &amp;rarr; 업로드 후 S3을 사용해 데이터 처리를 실시간으로 트리거 한다&lt;/li&gt;
&lt;li&gt;웹 어플리케이션 &amp;rarr; Lambda를 다른 AWS 서비스와 결합해 자동으로 스케일 업/다운 할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드는 콘솔, zip, s3을 통해 업로드 할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로비저닝&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 요구에 맞게 시스템 자원을 할당, 배치, 배포해 두었다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ECS&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS에서 제공하는 컨테이너 오케스트레이션 서비스로 여러 어플리케이션 컨테이너를 쉽고 빠르게 실행하고, 컨테이너를 적절하게 분배 및 확장 &amp;amp; 축소 할 수 있도록 도와준다&lt;/li&gt;
&lt;li&gt;ECS 외에도 Docker Swarm, Kubernates, Nomad 등 다양한 오케스트레이션들이 존재한다&lt;/li&gt;
&lt;li&gt;ECS는 클러스터를 관리하기 위한 별도의 인스턴스 구성 &amp;amp; 관리하지 않아도 되며, 클러스터 관리에 대한 추가적인 비용이 없다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Container&lt;/b&gt;: ECS는 컨테이너 오케스트레이션 서비스여서 어플리케이션을 Docker 컨테이너에서 실행 되도록 구성해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker Container &amp;amp; Image:&lt;/b&gt; 컨테이너는 Docker Image라는 읽기 전용 탬플릿을 통해 생성되는 것을 컨테이너라고 하고, 이미지는 컨테이너에 포함되는 모든 구성요소(runtime base Image, 어플리케이션 소스, 라이브러리 등)을 지정해주는 일반 텍스트 파일인 Docker file을 통해 빌드 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker&lt;/b&gt;를 사용하면 더 이상 필요한 런타임 환경 구축을 직접 세팅, 어플리케이션 구동에 필요한 라이브러리 설치 및 기타 세팅을 매번 할 필요가 없고 PC 마다 런타인 환경이나 설치된 라이브러리 버전이 달라 문제가 생기는 것에서 해방될 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cluster&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECS 클러스터는 도커 컨테이너를 실행할 수 있는 논리적인 공간이다. 우리가 사용하는 도커 컨테이너는 도커가 설치된 컨테이너 인스턴스에서 실행되며 이 인스턴스들을 목적에 따라 하나로 묶어주는 것을 클러스터라고 할 수 있다.&lt;/li&gt;
&lt;li&gt;ECS에서는 논리적으로 그룹화된 컨테이너 인스턴스들에게 특정 컨테이너를 띄우고, 증가 시키고 등의 명령을 내려 오케스트레이션을 행할 수 있게 되는 것이다. 그리고 논리적인 공간이다 보니 빈 클러스터 즉 인스턴스가 없는 클러스터도 생성이 가능하다&lt;/li&gt;
&lt;li&gt;컨테이서 인스턴스를 클러스트에 연결 시키고 명령을 전달 즉 오케스트레이션을 하기 위해서는 AWS에서 제공하는 ECS Agent가 컨테이너 인스턴스(EC2)에 설치가 되어있어야 하며, ECS Agent를 통해 ECS 서비스에서의 오케스트레이션 명령 수행이 가능하게 된다.&lt;/li&gt;
&lt;li&gt;즉 클러스터는 컨테이너 인스턴스(EC2)의 논리적인 그룹화이다&lt;/li&gt;
&lt;li&gt;논리적인 공간으로, 컨테이너 인스턴스가 없는 빈 클러스터 생성도 가능하다&lt;/li&gt;
&lt;li&gt;ECS Agent를 통해 논리적인 클러스터에 연결된다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Task &amp;amp; Task Definition&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Task&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task는 컨테이너를 실행하는 최소 단위. 최소 1개 이상의 컨테이너로 구성될 수 있으며 해당 Task 내의 컨테이너는 모두 같은 ECS 클러스터 인스턴스 내에 실행되도록 보장 받는다. 여러 컨테이너를 하나로 묶어서 실행한다고 생각하면 되고, docker-compose와 비슷한 개념이라고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Task Definition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task Definition은 위에서 말한 Task를 저의한 작업 정의이다. Task 실행 시 Task Role, Docker Network Mode, Docker 실행 명령 등 옵션 설정을 할 수 있고, 생성 또는 업데이트된 Task Definitions는 모두 Revision Version이 기록되어 Task를 롤백 등 하고자 할떄 유용하다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Service&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECS Cluster에서 최소 실행 단위는 Task이다. Task를 실행 하는 방식은 직접 Task Definitions를 이용한 실행과 Service를 이용한 실행 두 가지 방식을 제공한다. 직접 Task Definitions를 실행하는 방식은 Task에 대한 관리가 되지 않으며, AWS에서 제공하는 다른 서비스(ELB, AutoScale)등을 이용할 수 없어 대부분 사용하지 않는다&lt;/li&gt;
&lt;li&gt;Service는 Task Definitions를 이용해 Task 실행 유형(EC2 or Fargate), 타입(복제 or 데몬), 테스크가 실행 되어야할 작업 개수, 배포 방식 (롤링 or Blue Green), 배치 전략, AutoScale 설정 및 컨테이너와 ELB(ALB or NLB)등을 설정해 Task를 실행 및 관리하도록 도와준다. ECS Cluster에 Task를 관리하는 상위 그룹 개념이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ELB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;둘 이상의 가용 영역에서 EC2 인스턴스, 컨테이너, IP 주소 등 여러 대상에 걸쳐 수신되는 트래픽을 자동으로 분산한다. 등록된 대상의 상태를 모니터링하면서 상태가 양호한 대상으로만 트래픽을 라우팅한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ALB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드밸런서는 클라이언트에 대한 단일 접점 역할을 수행한다. 로드 밸런서는 여러 가용 영역에서 EC2 인스턴스 같은 여러 대상에 수신 애플리케이션 트래픽을 분산해 애플리케이션 가용성을 향상시킨다. 로드밸런서에는 하나 이상의 리스너를 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;ALB는 OSI 모델의 애플리케이션 계층에서 작동한다. 로드 밸런서는 요청을 받으면 우선 순위에 따라 리스너 규칙을 평가해 적용할 규칙을 결정한 다음 규칙 작업의 대상 그룹에서 대상을 선택한다.&lt;/li&gt;
&lt;li&gt;애플리케이션 트래픽의 컨텐츠를 기반으로 다른 대상 그룹에 요청을 라우팅하도록 리스너 규칙을 구성할 수 있다. 기본 라우팅 알고리즘은 라운드 로빈이다. 초시ㅗ 미해결 요청 라우팅 알고리즘을 지정할 수도 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ALB - Listner&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ALB를 사용하기 전에 리스너를 하나 이상 추가해야 한다. 리스너는 구성한 프로토콜 및 포트를 사용해 연결 요청을 확인하는 프로세스이다. 리스너에 대해 정의한 규칙에 따라 로드 밸런서가 등록된 대상으로 요청을 라우팅하는 방법이 결정된다.&lt;/li&gt;
&lt;li&gt;리스너는 HTTP, HTTPS , 포트 1-65535 를 지원한다&lt;/li&gt;
&lt;li&gt;리스너 프로토콜이 HTTPS인 경우 리스너에 한 개 이상의 SSL 서버 인증서를 배포해야 한다.&lt;/li&gt;
&lt;li&gt;규칙 작업 &amp;rarr; 규칙 작업에는 작업을 수행하는데 필요한 유형, 순서 및 정보가 있다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;authenticate-cognito: Https리스너 Amazon Cognito를 사용해 사용자를 인증한다&lt;/li&gt;
&lt;li&gt;foward: 요청을 지정된 대상 그룹으로 전달한다.&lt;/li&gt;
&lt;li&gt;redirect: 요청을 다른 URL로 리디렉션 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ALB - Listner Rule&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 리스너는 기본 규칙을 가지고 있고, 선택에 따라 추가 규칙을 정의할 수 있다. 각 규칙은 우선 순위, 하나 이상의 작업, 하나 이상의 조건으로 구성된다.&lt;/li&gt;
&lt;li&gt;기본 규칙 &amp;rarr; 리스너를 생성할 때 기본 규칙에 대한 작업을 정의한다. 기본 규칙은 조건을 가질 수 없고, 리스너 규칙에 대해 조건이 충족되지 않으면 기본 규칙에 대해 작업이 수행된다.&lt;/li&gt;
&lt;li&gt;규칙 우선 순위 &amp;rarr; 규칙마다 우선 순위가 있다. 가장 낮은 값에서 가장 높은 값에 이르기까지 운선 순위에 따라 평가된다.&lt;/li&gt;
&lt;li&gt;규칙 조건
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host-header: 호스트 이름을 기반으로 라우팅한다&lt;/li&gt;
&lt;li&gt;http-header: http header 기반으로 라우팅한다&lt;/li&gt;
&lt;li&gt;http-request-method: 요청 메서드 기반으로 라우팅한다&lt;/li&gt;
&lt;li&gt;path-pattern: 요청 URL의 경로 패턴을 기반으로 라우팅한다&lt;/li&gt;
&lt;li&gt;query-string: 쿼리 문자열의 키/값 페어 또는 값을 기반으로 라우팅한다&lt;/li&gt;
&lt;li&gt;source-ip: 요청의 소스ip 주소를 기반으로 라우팅한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Target Group (대상 그룹)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대상 그룹은 지정한 프로토콜과 포트 번호를 사용해 EC2 인스턴스 같은 하나 이상의 등록된 대상으로 요청을 라우팅한다. 여러 대상 그룹에 대상을 등록할 수 있다. 로드 밸런서의 리스너 규칙에서 지정한 대상 그룹에 등록된 모든 대상에서 상태검사가 수행된다&lt;/li&gt;
&lt;li&gt;각 대상 그룹은 하나 이상의 등록된 대상에 요청을 라우팅하는 데 사용된다. 각 리스너 규칙을 생성할 때 대상 그룹 및 조건을 지정한다. 규칙 조건이 충족되면 해당하는 대상 그룹으로 트래픽이 전달된다. 서로 다른 유형의 요청에 대해 서로 다른 대상 그룹을 생성할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 요청의 경우 하나의 대상 그룹을 생성하고 애플리케이션에 대한 마이크로 서비스의 요청인 경우 다른 대상 그룹을 생성한다.&lt;/li&gt;
&lt;li&gt;하나의 로드 밸런서에만 각 대상 그룹을 사용할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Target type&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대상 그룹을 생성할 때 대상 유형을 지정한다. 이 값에 따라 대상 그룹에 대상을 등록할 때 지정하는 대상의 유형이 결정된다. 생성 후 변경 불가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;instance &amp;rarr; 대상이 인스턴스 id에 의해 지정된다&lt;/li&gt;
&lt;li&gt;ip &amp;rarr; ip주소&lt;/li&gt;
&lt;li&gt;lambda &amp;rarr; 대상이 lambda 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;등록된 대상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로드 밸런서는 클라이언트에 대해 단일 접점의 역할을 하며 정상적으로 등록된 대상 간에 수신 트래픽을 자동으로 분산한다. 하나 이상의 대상 그룹에 각 대상을 등록할 수 있다.&lt;/li&gt;
&lt;li&gt;애플리케이션에 대한 요규가 증가하면 이를 처리하기 위해 하나 이상의 대상 그룹에 추가 대상을 등록할 수 있다.&lt;/li&gt;
&lt;li&gt;인스턴스 ID로 대상을 등록하는 경우 Auto Scaling 그룹에 로드 밸런서를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;VPC(Virtual Private Cloud)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VPC를 사용하면 정의한 논리적으로 격리된 가상 네트워크에서 AWS 리소스를 시작할 수 있다.&lt;/li&gt;
&lt;li&gt;가상 네트워크는 AWS의 확장 가능한 인프라를 사용한다는 이점과 함께 고객의 자체 데이터 센터에서 운영하는 기존 네트워크와 유사하다.&lt;/li&gt;
&lt;li&gt;VPC에는 리전의 각 가용성 영역에 하나의 서브넷이 있고, 각 서브넷에 EC2 인스턴스가 있고, VPC의 리소스와 인터넷 간의 통신을 허용하는 인터넷 게이트웨이가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서브넷&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서브넷은 VPC의 IP 주소 범위이다. 특정 서브넷에서 EC2 인스턴스와 같은 AWS 리소스를 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;단일 가용 영역 내에서만 존재해야 하며, 여러 영역으로 확장할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보안그룹&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안 그룹은 연결된 리소스에 도달하고 나갈 수 있는 트래픽을 제어한다. 예를 들어 보안 그룹을 EC2 인스턴스와 연결하면 인스턴스에 대한 인바운드 및 아웃바운드 트래픽을 제어한다. 보안 그룹은 해당 보안 그룹이 생성된 VPC 리소스에만 연결할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code Deploy&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CodeDeploy는 EC2, 온프레미스 인스턴스, Lambda 함수 또는 ECS 서비스로 애플리케이션 배포를 자동화하는 배포 서비스이다.&lt;/li&gt;
&lt;li&gt;다양한 애플리케이션 컨텐츠를 무제한으로 배포할 수 있다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드&lt;/li&gt;
&lt;li&gt;Lambda 함수&lt;/li&gt;
&lt;li&gt;웹 및 구성 파일&lt;/li&gt;
&lt;li&gt;패키지&lt;/li&gt;
&lt;li&gt;스크립트&lt;/li&gt;
&lt;li&gt;멀티미디어 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CodeDeploy는 서버에서 실행되고 Amazon S3 버킷, Github 리포지토리 또는 Bitbucket 리포지토리에 저장되는 애플리케이션 컨텐츠를 배포할 수 있고 람다도 배포할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새 기능 신속하게 출시&lt;/li&gt;
&lt;li&gt;람다 함수 버전 업데이트&lt;/li&gt;
&lt;li&gt;애플리케이션 배포 시 가동 중지 방지&lt;/li&gt;
&lt;li&gt;구성관리 및 CI/CD 을 위해 사용하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blue / Green 배포&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블루/그린 배포는 새 애플리케이션 버전의 변경으로 인한 중단을 최소화하면서 애플리케이션을 업데이트하는 데 사용한다. CodeDeploy는 프로덕션 트래픽을 다시 라우팅하기 전에 이전 버전과 함께 새 애플리케이션 버전을 프로비저닝 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 새 대체 환경에서 설치 및 테스트하고 트래픽을 다시 라우팅해 프러덕션에 간단히 배포 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS Lambda , ECS 에서 블루/그린 배포
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lambda 또는 ECS 컴퓨팅 플랫폼을 사용하는 경우 원래 람다 함수 또느 ECS 태스크 집합에서 새 함수 또는 태스크 집합으로 트래픽을 어떻게 이동할지 지정해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카나리(Canary), 리니어(Linear), All-at-once 방법이 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카나리&lt;/b&gt; &amp;rarr; 트래픽이 두 증분으로 나위어 이동한다. 첫 번째 증분에서 업데이트된 람다 함수 또는 ECS 태스크로 이동할 트래픽의 백분율 및 두 번째 증분에서 나머지 트래픽의 이동을 시작하기 전까지 간격을 지정하는 사전 정의된 카나리 옵션 중에서 선택할 수 있다.&lt;/li&gt;
&lt;li&gt;리니어: 트래픽이 증분 이동하며 각 증분 간에 시간 간격이 동일하다.&lt;/li&gt;
&lt;li&gt;한 번에 모두: 한 번에 이동한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/311</guid>
      <comments>https://ddurubird.tistory.com/311#entry311comment</comments>
      <pubDate>Sun, 3 Sep 2023 12:37:05 +0900</pubDate>
    </item>
    <item>
      <title>TIL - Jib</title>
      <link>https://ddurubird.tistory.com/310</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;Jib - Java library for building containers&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GMxTJ/btssPyoCSPP/tu9sSDGGYDxJAWPMteDkD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GMxTJ/btssPyoCSPP/tu9sSDGGYDxJAWPMteDkD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GMxTJ/btssPyoCSPP/tu9sSDGGYDxJAWPMteDkD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGMxTJ%2FbtssPyoCSPP%2Ftu9sSDGGYDxJAWPMteDkD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;203&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile을 작성하지 않고 Docker daemon 없이 Docker 이미지를 빌드할 수 있게 도와주는 라이브러리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지 레이어 캐싱을 통해 변경사항만 다시 빌드해 속도가 빠르고 Jar를 가지고 있지 않아 상당히 용량이 작다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 작성하고 gradle jib 명령어를 실행하면 도커 이미지를 build.grade jib 에 설정한 대로 굽는다.&lt;/p&gt;
&lt;pre id=&quot;code_1693580643101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
	id(&quot;com.google.cloud.tools.jib&quot;) version &quot;3.1.4&quot;
}

jib {
	from {
		image = &quot;openjdk:17&quot;
		platforms {
			platform {
				architecture = &quot;arm64&quot;
				os = &quot;linux&quot;
			}
		}
	}
	to {
		image = &quot;docker.io/${project.findProperty(&quot;DOCKERHUB_USERNAME&quot;)}/jib-practice&quot;
		tags = setOf(&quot;latest&quot;)
		auth {
			username = project.findProperty(&quot;DOCKERHUB_USERNAME&quot;).toString()
			password = project.findProperty(&quot;DOCKERHUB_PASSWORD&quot;).toString()
			//프로젝트 루트에 docker.propreties 생성
		}
	}
	container {
		creationTime = &quot;USE_CURRENT_TIMESTAMP&quot;
		jvmFlags = listOf(&quot;-Dspring.profiles.active=local&quot;, &quot;-XX:+UseContainerSupport&quot;, &quot;-Dserver.port=8080&quot;, &quot;-Dfile.encoding=UTF-8&quot;)
		enviroment = [
		...환경설정
					&quot;SPRING_PROFILES_ACTIVE&quot;: &quot;dev&quot;
		]
	}
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/310</guid>
      <comments>https://ddurubird.tistory.com/310#entry310comment</comments>
      <pubDate>Sat, 2 Sep 2023 00:05:26 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 9일 차. JwtExceptionFilter</title>
      <link>https://ddurubird.tistory.com/309</link>
      <description>&lt;div id=&quot;SE-5996d965-4a42-4133-8dce-c0077ec326d9&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXY34w/btssAGmY8iR/btjkCl6FDooTjvkvc4pYi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXY34w/btssAGmY8iR/btjkCl6FDooTjvkvc4pYi1/img.png&quot; data-alt=&quot;분명 CorsFilter가 정상적으로 동작하고 있을텐데&amp;amp;hellip;?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXY34w/btssAGmY8iR/btjkCl6FDooTjvkvc4pYi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXY34w%2FbtssAGmY8iR%2FbtjkCl6FDooTjvkvc4pYi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;132&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분명 CorsFilter가 정상적으로 동작하고 있을텐데&amp;hellip;?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-2e358d53-0c78-4da0-a37c-4207b968b8a6&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-61701918-9df7-49dc-9b6f-f959e24caab5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;첫 출근을 기다리며 여유롭게 코테, SQL, 프로젝트 마이그레이션을 진행하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-9973702e-c6e2-49bb-ad37-27da6f308a54&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;인생의 낙원이 있다면 이게 아닐까 생각하는 요즘이다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-8cf87815-74b7-456a-a645-66d9139cab72&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기능 대부분은 마이그레이션 완료했으니 가능하면 출근 전에 선착순 상품 구현해보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-cfa1b940-7567-4d2f-94e5-70f0730fdbe4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-2118aa97-37d4-4fad-b80b-9fe977950262&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Filter 에서 발생하는 예외 처리&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-56732914-8c9a-4bcd-806e-aefd8e17e5a4&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;1098&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/umGFg/btssGiemnVY/jEJ9dDRwzjDjovXIEV4XQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/umGFg/btssGiemnVY/jEJ9dDRwzjDjovXIEV4XQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/umGFg/btssGiemnVY/jEJ9dDRwzjDjovXIEV4XQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FumGFg%2FbtssGiemnVY%2FjEJ9dDRwzjDjovXIEV4XQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;468&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;1098&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-aee3f16a-2f6a-4e5a-8b96-bca2eeb4fed5&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-ddeb4e99-578f-4171-9790-75aa7e495530&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;WebFlux는 Spring Security Interceptor가 없어 Filter에서 토큰 검증을 수행하고 decode된 값을 Handler Function의 Attribute로 넣어주고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-6fb7cd35-c3fc-4d92-979f-e7dda0938f10&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존 Interceptor는 DispatcherServelet과 Handler 사이에 위치해서 토큰 검증 시 예외가 발생하면 ControllerAdvice에서 처리할 수 있었지만 Filter는 보다 밖에 위치한 컴포넌트여서 에러처리를 위한 Filter를 구현해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c63c4387-cf2b-461b-95ae-3eeba7f0c42a&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-50d54efc-9af2-422e-b9fd-7f048cc74bd4&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;Ordered&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-5a0ee2f8-5047-4a3e-b04c-fd2656948e01&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/59mQv/btssHCQWKAT/ZyiVh4J3DzvYMex12embWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/59mQv/btssHCQWKAT/ZyiVh4J3DzvYMex12embWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/59mQv/btssHCQWKAT/ZyiVh4J3DzvYMex12embWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F59mQv%2FbtssHCQWKAT%2FZyiVh4J3DzvYMex12embWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;186&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-041566d4-f37a-4462-9bd1-d43d418b0594&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-be4019ab-97ae-43fb-99d9-99784f6e3360&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하지만 필터에서 설정된 에러응답은 Filter의 Order에 따라 CORS를 처리하기 위한 응답헤더가 설정되지 않을 수도 있다. 이렇게 되면 브라우저 SOP 정책으로 CORS 가 발생하고 서버응답 또한 무시되니 Axios.Interceptor에서 응답코드 별로 구현한 로직이 정상적으로 동작하지 않게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ef284626-bc47-4eb9-91d0-dacd5a635ae5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;원인을 파악하고 CorsFilter를 아래와 같이 Ordered를 최우선으로 설정하니 CORS가 해결되어 Axios.Interceptor가 정상적으로 동작할 수 있게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-c7555261-c6bb-4418-bdec-439bfb172598&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S1ItB/btssCtHhMxE/lpq6EggXtIcqiqvGcMmet1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S1ItB/btssCtHhMxE/lpq6EggXtIcqiqvGcMmet1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S1ItB/btssCtHhMxE/lpq6EggXtIcqiqvGcMmet1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS1ItB%2FbtssCtHhMxE%2Flpq6EggXtIcqiqvGcMmet1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;234&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-ee8e743f-43d5-4ca0-965d-8084a181a594&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-554553ac-baf4-447d-9b5a-1041000c6561&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/309</guid>
      <comments>https://ddurubird.tistory.com/309#entry309comment</comments>
      <pubDate>Wed, 30 Aug 2023 18:59:55 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 8일 차. Fetch 그리고 이모저모</title>
      <link>https://ddurubird.tistory.com/308</link>
      <description>&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div id=&quot;SE-48b1577f-70b4-4750-bdbd-1dab09d4a32f&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzWqs7/btsrR5BA9HL/D9S7Dl7NefoFpba1nk1VCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzWqs7/btsrR5BA9HL/D9S7Dl7NefoFpba1nk1VCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzWqs7/btsrR5BA9HL/D9S7Dl7NefoFpba1nk1VCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzWqs7%2FbtsrR5BA9HL%2FD9S7Dl7NefoFpba1nk1VCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;258&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-c0362447-3dbb-48e1-bc42-8dcdbb381f8d&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-7c61c05b-1558-4f44-8efb-f28b132736b5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;JDSL를 통해 엔티티를 찾을 때 쿼리 조건에 해당하는 엔티티가 없으면 null이나 optional을 반환하지 않고 바로 예외를 발생시킨다. 따라서 CustomException을 발생시키거나 다른 로직을 구현하기 위해 Repository를 만들어 한 번 감싸서 사용했었다. &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-05fa37bc-7faf-41e8-9215-df2ed5dc1b46&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsZUm4/btsrYLWmEBo/ApMan1brTRJkf4jHuooQ91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsZUm4/btsrYLWmEBo/ApMan1brTRJkf4jHuooQ91/img.png&quot; data-alt=&quot;cartItems가 없는 상황이라면 빈 컬렉션이 있는 게 맞지 않나&amp;amp;amp;hellip;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsZUm4/btsrYLWmEBo/ApMan1brTRJkf4jHuooQ91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsZUm4%2FbtsrYLWmEBo%2FApMan1brTRJkf4jHuooQ91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;228&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cartItems가 없는 상황이라면 빈 컬렉션이 있는 게 맞지 않나&amp;amp;hellip;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-05cb227c-8202-4a98-9588-d88de499cd8b&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-a59bf03a-805d-4179-a516-7fcf193fb417&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;연관관계가 없는 경우 위의 코드로도 로직 구현이 가능하지만 위와 같이 Cart의 cartItems가 없는 경우에도fetch(Cart::cartItems)를 할 때 연관관계가 있는 데이터를 찾을 수 없어 동일한 예외가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-2112646b-60d7-4134-b3db-06858e620b4c&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-cac2b6ef-20a7-48b6-b195-c93f873f48a3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;SQL Booster 스터디&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;SE-9b25dc41-2055-4bd7-a911-08cb14ec7b80&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0wuva/btsr4KITplq/VQWfPxIeVmZLPkv7Kgx0E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0wuva/btsr4KITplq/VQWfPxIeVmZLPkv7Kgx0E1/img.png&quot; data-alt=&quot;부찌 국물에서 샤브샤브 맛이 나는데 5점을 줄 수 있다고&amp;amp;amp;hellip;?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0wuva/btsr4KITplq/VQWfPxIeVmZLPkv7Kgx0E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0wuva%2Fbtsr4KITplq%2FVQWfPxIeVmZLPkv7Kgx0E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;131&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;부찌 국물에서 샤브샤브 맛이 나는데 5점을 줄 수 있다고&amp;amp;hellip;?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-293d3fc0-b609-4cff-81c3-7fbff3e0bac7&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-79287417-bff3-4163-b2eb-0ef5d464a2b5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;처음 SQL 을 봤을 때 상당히 귀찮은 애라고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-528b6317-ca63-4f18-a27f-6461dd15415d&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가뜩이나 Java, Javascript, TDD 어쩌고 저쩌고를 머리에 욱여넣을 때 만나서 더욱 그렇게 느껴졌던 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-263fac34-960c-4fc4-ac51-ada37ce671ad&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;당시 과제는 JPA 기본 API를 사용하면 되는 수준이기도 해서 필요성을 느끼지 못해서도 그랬고.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-75ae1dff-527c-4ebc-ac62-85f14b59623a&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-070306d5-1940-4161-a7aa-e6793c44f12b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;시간이 지나 SQL 필요성을 느껴 스터디를 꾸려 학습하고 있는데 다시 보니 이렇게 정직한 애는 또 없다고 생각이 든다. 그때 귀찮더라도 바로 공부를 시작했다면 지금쯤이면 웬만한 쿼리는 바로 작성 가능할텐데. 매우 아쉽다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-033c8aee-c8ad-4305-a26a-56a8c85bfb93&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;암튼 지금이라도 늦지 않았으니 꾸준히 쿼리 작성 능력을 키워 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-09118dd1-8d69-4017-969f-9d7894153f3b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d570567a-b271-4ce0-8dc4-e05f4fbced96&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;Second Run&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-c02dc08c-0399-48a5-8dc7-130e2557d804&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mJQmp/btsrUthu2EC/f3XVKOxtEF1Kpr8G2i7vXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mJQmp/btsrUthu2EC/f3XVKOxtEF1Kpr8G2i7vXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mJQmp/btsrUthu2EC/f3XVKOxtEF1Kpr8G2i7vXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJQmp%2FbtsrUthu2EC%2Ff3XVKOxtEF1Kpr8G2i7vXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;193&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-1228a76a-9234-40ea-925e-2244880f197b&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-88ab365a-ab1b-4b43-8384-f9bad4a0aa9f&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;자격 요건은 되지 않지만 혹시나 해서 지원했던 회사로부터 합격 통보를 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c41d6914-79fe-4f14-bf9e-c27b945fb9a2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1차 면접 때 면접관 세 분 모두 내 블로그 글이나 프로젝트 코드를 평가하고 서류합격을 시켰다는 말을 듣고 상당히 감동했고, 이런 분들과 같이 업무를 하고 싶어 입사 희망 욕구가 폭발했었는데 운이 매우 좋았던 거 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-73fb65fa-adc0-46df-a311-cf466374cb09&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어려운 시기에 기회가 주어진 만큼 다시 열심히 뛰어 보자.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/308</guid>
      <comments>https://ddurubird.tistory.com/308#entry308comment</comments>
      <pubDate>Wed, 23 Aug 2023 14:31:42 +0900</pubDate>
    </item>
    <item>
      <title>TIL - m1에서 Oracle DB 사용하기</title>
      <link>https://ddurubird.tistory.com/307</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bECtgA/btsq5dGLKqQ/n39gGliCub93DeeerTVDW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bECtgA/btsq5dGLKqQ/n39gGliCub93DeeerTVDW0/img.png&quot; data-alt=&quot;주 고객이 대규모 기업여서 Mac 지원에 소홀한게 아닌가 싶다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bECtgA/btsq5dGLKqQ/n39gGliCub93DeeerTVDW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbECtgA%2Fbtsq5dGLKqQ%2Fn39gGliCub93DeeerTVDW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;155&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주 고객이 대규모 기업여서 Mac 지원에 소홀한게 아닌가 싶다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL BOOSTER 라는 책을 추천 받아서 구입했더니 예제가 Oracle로 되어 있어 Oracle DB를 설치해야만 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Oracle DB는 공식적으로 mac 환경 그러니까 arm64 x86을 지원하지 않아서 일반적으로는 실행할 수 없는 상황.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 colima 라는 container runtimes on macOS가 있어서 Oracle DB를 사용할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. colima 설치&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692004056765&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install colima&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. docker 설치 (설치하지 않았다면)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. colima 실행&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692004129732&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;colima start --memory 4 --arch x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 만약 colima 실행이 안된다면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692004199031&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;colima delete 또는 ~/.lima 에서 colima 폴더를 삭제 
colima start --memory 4 --arch x86_64 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. docker 이미지 다운 및 실행&lt;/p&gt;
&lt;pre id=&quot;code_1692004222200&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --restart unless-stopped --name oracle -e ORACLE_PASSWORD=pass -p 1521:1521 -d gvenzl/oracle-xe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. docker -ps 로 컨테이너 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2564&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwkucP/btsrdiAtD0U/prY3nfNTDMDHS886BiEAC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwkucP/btsrdiAtD0U/prY3nfNTDMDHS886BiEAC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwkucP/btsrdiAtD0U/prY3nfNTDMDHS886BiEAC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwkucP%2FbtsrdiAtD0U%2FprY3nfNTDMDHS886BiEAC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2564&quot; height=&quot;92&quot; data-origin-width=&quot;2564&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 기타 이모저모&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;system이 아닌 username은 sys as sysdba , password는 기존에 설정한 pass로 connection을 맺어야지 가능한 기능들이 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;table 데이터를 저장할 tablespace를 먼저 확보하고 거기에 table 데이터를 저장하는 형식.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오라클...불편하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/307</guid>
      <comments>https://ddurubird.tistory.com/307#entry307comment</comments>
      <pubDate>Mon, 14 Aug 2023 18:11:27 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 7일 차. 값, 값 검증을 보자.</title>
      <link>https://ddurubird.tistory.com/306</link>
      <description>&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;SE-211d66aa-da79-40cf-bddd-42b309132e73&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BHz7J/btsqWeE9hRZ/IUneh4B7F9OZ7ba53fkVL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BHz7J/btsqWeE9hRZ/IUneh4B7F9OZ7ba53fkVL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BHz7J/btsqWeE9hRZ/IUneh4B7F9OZ7ba53fkVL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBHz7J%2FbtsqWeE9hRZ%2FIUneh4B7F9OZ7ba53fkVL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;371&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-89da6762-ad67-48e5-a75e-7604ff314bc0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-2db6db70-3a4f-4907-a25e-d4372ddf5379&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기분 좋은 1차 면접 합격 소식이 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-2e540499-fb6f-4468-b9ab-2439edb4f2da&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;면접에 대해 조금 말하자면 상당히 만족스럽고 감동까지 받았던 면접이라고 말할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-a67797c7-b54d-4d2b-b4dd-81cde73fe5a4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;면접에는 팀장을 포함해 총 3명의 면접관이 있었는데, 모두 내 블로그에 작성된 글을 하나도 빠짐없이 읽었고 심지어 프로젝트 클론까지 떠서 꼼꼼히 검토했다는 말씀을 해주셨기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-0d168dd4-d6ef-493f-bc73-98d117d3b7ec&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;집에 돌아오는 길에 이런 회사에 합류할 수 있다면 더 이상 소원은 없을 것 같다고 생각하기도 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c97ed264-5cbb-43e1-abf7-bdd5ed4c9474&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-167fd7c0-8a54-4e82-8ef8-65bb667ec2f9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 남은 건 2차 임원 면접.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-bcf2b5c0-daa5-4ddc-b321-60300e2cf3db&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어떻게 될지 모르지만 최선을 다해보자.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-c924695f-8e30-45d7-a359-412e012ff5fb&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;중첩된 data class로 발생했던 오류.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1691730898409&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
data class A(
   @ElementCollection
   val b: List&amp;lt;B&amp;gt;
)

@Embeddable
data class B(
   @Embedded
   val c: C
)

@Embeddable
data class C(
   val dateWithACrush: LocalDate
)

@Entity
data class A(
   @ElementCollection
   val c: List&amp;lt;C&amp;gt; // like a not nested embeddable in Entity
)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-377d1e00-b3a2-4d15-9f50-3f63219113a7&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-37284ff2-7a87-4371-93b5-c22f8c04a6a8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코드를 작성하고 빌드를 하려고 하는데 영문 모를 Could not locate setter method for property 에러가 발생했다. 찾아보니 에러가 발생한 원인은 ElementalCollection에 중첩된 embeddable에 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ac4fc94e-7790-4980-b0b5-e4205f56ed41&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-5b05e6dd-e5c9-4831-a911-d19da3581b0e&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Hibernate는 기본적으로 setter를 이용해 필드 접근을 하는데 val로 설정되어 있으니 setter가 없어 접근하지 못하는 상황. (&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://youtrack.jetbrains.com/issue/KT-26938&quot;&gt;https://youtrack.jetbrains.com/issue/KT-26938&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-604369f0-e7ae-44a5-a927-340362596f87&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c38134ed-3b27-45b3-a082-a6aae8456664&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해결 방법으로는 필드를 var로 변경하거나 @Access(AccessType.FIELD)로 설정하면 되는데, data class 불변성을 유지하기 위해 @Access(AccessType.FILED) 어노테이션을 사용해 해결했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-a5a63adb-fa11-43f4-9fae-d4ee7b243166&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;자바에서는 이런 문제가 발생하지 않았는데, JPA는 여러모로 Kotlin과 사이가 좋지 않은 느낌적인 느낌이다&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-71e4dedb-3eed-4d65-a380-fbb15011fa77&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-a8f91a08-d9e0-42ef-8109-9275b9e92a28&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;정규식을 한 곳으로 모아보자.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1691730917533&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Embeddable
data class Email(
    val value: String
) {
    init {
        val emailRegex = Regex(&quot;^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$&quot;)

        if (!value.matches(emailRegex)) {
            throw InvalidEmailException()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;SE-7cfd3f48-5dc0-4110-a32e-53a7fba78cce&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-3a828048-b98f-4aa1-84f6-841fba2df836&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 코드는 값 객체가 스스로 유효성 검증을 하는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-54f411fa-66b9-4a1c-a134-9c8db97ce9c2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;v1을 진행할 때는 몰랐지만 다시 보니 유효성 검증에 사용될 정규식을 init 블럭에서 선언하고 사용하는 모습이 상당히 불편하게 보였다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-347f4d69-9297-4150-9a1f-01018c034786&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-f89aa58f-5121-46a6-8eae-ee5181b59f42&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여러 값 객체의 정규식을 수정해야 하는 상황이 발생하면 수정이 필요한 모든 값 객체 파일을 하나씩 수정해야 되어서 현재 방식보단 좀 더 편한 방식이 있을거라 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-bb23c691-3c11-481c-a583-7be38a95c3e9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-63af9412-2775-4ec0-bbda-9b0be81a831d&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그렇게 조금 고민하다 RegExp 객체를 제공하는 RegExpProvider 클래스를 작성하고, enum으로 작성한 정규식을 통해 Regex 객체를 돌려주는 방식으로 수정해 보았다. 코드는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1691730948500&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Embeddable
data class Email(
    val value: String
) {
    init {
        if (!value.matches(RegExpProvider.email())) {
            throw InvalidEmailException()
        }
    }
}

@Component
class RegExpProvider {
    companion object {
        fun email(): Regex {
            return Regex(Regexp.EMAIL.value)
        }
    }
}

enum class RegExp(
    val value: String
) {
    EMAIL(&quot;^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$&quot;),
    USERNAME(&quot;^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$&quot;),
    NAME(&quot;^[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]{2,}$&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-dc712236-53e9-42fb-8d86-e4cfdd96bd00&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-7c8e448e-1764-4eef-a3ec-b56a887d6529&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그런데 성공적으로 값 객체에서 정규식을 추출해서 한 곳에서 관리하도록 변경한건 좋았지만, &lt;/span&gt;&lt;span&gt;값 객체 하나 추가할 때 RegExpProvider 의 메서드도 추가해야 하고 RegExp에 enum도 추가해야 되고&amp;hellip;&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-73ab34b9-233f-4a3a-941b-cb61734ce837&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;작성해야 되는 코드 량이 배로 증가해 과연 잘한 것일까 심히 고민이다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/306</guid>
      <comments>https://ddurubird.tistory.com/306#entry306comment</comments>
      <pubDate>Fri, 11 Aug 2023 14:16:13 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 6일 차. Interceptor는 이제 없어.</title>
      <link>https://ddurubird.tistory.com/305</link>
      <description>&lt;div id=&quot;SE-04aa7c93-c6d4-4449-8dfd-aa5a529e43da&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA296W/btsp8XDuwVs/rKect7TIwDoEz3kfiNFXH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA296W/btsp8XDuwVs/rKect7TIwDoEz3kfiNFXH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA296W/btsp8XDuwVs/rKect7TIwDoEz3kfiNFXH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA296W%2Fbtsp8XDuwVs%2FrKect7TIwDoEz3kfiNFXH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;406&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-22ddd661-0935-4380-a64c-62a9031bb00d&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-36d70b80-356a-45a9-a9d0-7942d4b41188&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;v1 &amp;rarr; v2 프로젝트 6일 차. 익숙하지 않은 스타일과 서버 스펙이 달라 아예 새롭게 구현해야 되는 부분 때문에 그동안 기능 구현 속도가 나오지 않았지만 이제 스타일에 어느 정도 적응이 끝나 슬슬 구현 속도가 올라오는 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-29a86b75-3ec8-4d52-a974-763ddf2a89cd&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;큰 문제가 없다면 다음 주 주말에는 웬만한 기능은 완성되어 있을거라 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-2f64be53-8d99-472d-a54b-c65e5b63d5d3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-64e15662-ebfa-4cca-858e-484ab54ad617&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;전역 에러처리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-4d72f282-3f0c-4cd9-af5a-0449e84af3c1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NXBqV/btsp1ktIvOb/q2NKN6S9NuJvBfJzUBJBbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NXBqV/btsp1ktIvOb/q2NKN6S9NuJvBfJzUBJBbK/img.png&quot; data-alt=&quot;ResponseStatusException을 상속해야 errorAttributes.getErrorAttributes를 통해 예외 정보를 얻을 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NXBqV/btsp1ktIvOb/q2NKN6S9NuJvBfJzUBJBbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNXBqV%2Fbtsp1ktIvOb%2Fq2NKN6S9NuJvBfJzUBJBbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;261&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ResponseStatusException을 상속해야 errorAttributes.getErrorAttributes를 통해 예외 정보를 얻을 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-55622660-e19f-4155-9985-9e51a530934b&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-f54a4320-3f63-4fd7-a1ff-815d1f3d83df&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;webMVC를 사용할 땐 ControllerAdvice를 통해 편하게 전역 에러처리가 가능했다, 하지만 WebFlux는 ControllerAdvice를 지원하지 않아 전역 에러처리를 하고 싶다면 AbstractErrorWebExceptionHandler 를 직접 구현해야만 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-4f105e4d-6865-41a5-be2b-6917d02a7cd6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래는 직접 구현한 GlobalErrorWebExceptionHandler의 일부. errorAttributes에서 에러 정보를 추출해 적절한 응답으로 전환해 주는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691153519402&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// GlobalErrorWebExceptionHandle.class 
...
private fun renderErrorResponse(
        request: ServerRequest,
        errorAttributes: ErrorAttributes
    ): Mono&amp;lt;ServerResponse&amp;gt; {
        val errorProperties = errorAttributes.getErrorAttributes(request, ErrorAttributeOptions.defaults())
        val message = errorAttributes.getError(request).message
        val status = errorProperties[&quot;status&quot;].toString().toInt()
        errorProperties[&quot;message&quot;] = message

        return ServerResponse.status(HttpStatus.valueOf(status))
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(errorProperties))
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;SE-46332c2c-1684-4f1a-b692-f88fe0f84ca8&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div style=&quot;background-color: #fdfdfd;&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-29cafe58-2ab7-4f51-9712-b1952b68b028&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-541a901a-1888-4594-b36f-99b02d5f75f1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;전체 모듈 테스트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-cd8c11f3-debf-4b4d-bb05-1ec9bca0c8b1&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWSZZV/btsp8ABECAH/rQpQRQLkh83H7CKqfa40i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWSZZV/btsp8ABECAH/rQpQRQLkh83H7CKqfa40i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWSZZV/btsp8ABECAH/rQpQRQLkh83H7CKqfa40i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWSZZV%2Fbtsp8ABECAH%2FrQpQRQLkh83H7CKqfa40i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;560&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-607bafe1-5423-4f98-a438-08a000af0f76&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-4f2a3ff2-c48d-49b0-9e57-842236139cd0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기능 하나 구현을 끝내면 모든 테스트를 돌려보는 습관이 있는데 멀티 모듈 프로젝트를 진행하다 보니 모듈마다 한 번씩 test task를 실행시켜 줘야 해서 귀찮음을 느끼고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-a7d67d0e-2abc-4fcb-a532-833e3ad5b8ec&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;혹시 편하게 전체 모듈 테스트를 할 수 있을까 설정을 찾아보던 도중 Run/debug configurations에서 Force test tasks execution 을 발견. 설정 활성화를 하니 성공적으로 하나의 Gradle task에서 모든 모듈의 test tasks를 실행시킬 수 있었다&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-f291ed6e-f893-47e6-8d0a-dc2a9d12b20f&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c8ef1e21-d26b-4e88-a4ea-57dbb266baff&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;Interceptor는 이제 없어&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-1ed390ef-5fed-4dc3-b4e2-94fc9c396d0a&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;​&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-53449439-ce04-40a8-b5a2-238b0d48c739&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;WebFlux는 Dispatcher servlet이 존재하지 않아 Servlet으로 향하는 요청과 Servlet응답을 가로챌 수 있는 Interceptor를 사용하지 않는다. v1에서 Interceptor를 이용해 Authorization 헤더에 있는 값을 디코딩해서 Controller에 넣어 주는 방식을 변경해야 된다는 의미다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-26764ae4-edf7-4244-80de-2ffca0dfd3d5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Interceptor가 없으니 JWT 디코딩 로직을 Handler에서 수행해야 되나 고민했지만, 모든 Handler에 JWT 디코딩 로직을 넣을 순 없는 노릇. WebFilter를 찾아본 결과 기존 Interceptor와 동일한 로직 구현이 가능해 아래와 같이 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1691153557018&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
class RequestContextWebFilter(
    private val jwtUtil: JwtUtil
) : WebFilter {
    companion object {
        private const val BEARER_PREFIX = &quot;Bearer &quot;
    }

    override fun filter(
        exchange: ServerWebExchange,
        chain: WebFilterChain
    ): Mono&amp;lt;Void&amp;gt; {
        val accessToken = exchange.request.headers.getFirst(&quot;authorization&quot;) ?: return chain.filter(exchange)

        if (!accessToken.startsWith(BEARER_PREFIX)) {
            return chain.filter(exchange)
        }

        val username = accessToken.toUsername()

        exchange.attributes[&quot;username&quot;] = username

        return chain.filter(exchange)
    }

    private fun String.toUsername(): Username {
        return Username(
            this.substring(BEARER_PREFIX.length).let { jwtUtil.decode(it) }
        )
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TIL</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/305</guid>
      <comments>https://ddurubird.tistory.com/305#entry305comment</comments>
      <pubDate>Fri, 4 Aug 2023 21:52:45 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 5일 차. Pageable쿼리는 fetch를 지원하지 않아.</title>
      <link>https://ddurubird.tistory.com/304</link>
      <description>&lt;div id=&quot;SE-fe6f9342-7923-40a5-8765-06b28af2e3df&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D07y3/btspPaixVjr/YRE2VYAxBHZLVzv8p5cpyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D07y3/btspPaixVjr/YRE2VYAxBHZLVzv8p5cpyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D07y3/btspPaixVjr/YRE2VYAxBHZLVzv8p5cpyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD07y3%2FbtspPaixVjr%2FYRE2VYAxBHZLVzv8p5cpyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;980&quot; height=&quot;494&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-d537198d-779d-4745-bc99-0764ad1af32b&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-96e31531-f10d-4fa0-878c-3194d6332f48&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1. @ElementCollection의 기본 fetctType은 lazy이다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-9987e1d7-4358-4155-b173-3d20f386b6b0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2. JPA를 사용하면 @Treansactional 범위에서 엔티티 속성에 접근할 수 있어 크게 문제가 되지 않지만 JDSL-Reactive는 Hibernate-Reactive를 사용하면 @Transactional 지원을 하지 않아 session 범위에서 수동으로 fetch를 하거나 쿼리를 나눠야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d716c417-00d7-46d0-98d2-153d730e41bd&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;3. JDSL - PageableQueryDSL에서 session 범위에서 fetch(Boo::bars)를 했는데 여전히 Lazy initialize exception이 발생했다. 원인은 PageableQueryDSL에서 fetch를 지원하지 않기 때문.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-60ff46ba-db50-4c68-ab4e-22c989e3b3ad&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;4. 참고로 JDSL은 @ElementalCollection의 fetchType 지원하지 않기 때문 @OneToMany(fetchType = FetcyType.Eager)을 사용해야만 했다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-4b60c6da-ada0-4bb1-81e4-a6a18ceb1516&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj0CSc/btspFMb5Nbq/YkTbH54lEyYHaOB3cKYhF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj0CSc/btspFMb5Nbq/YkTbH54lEyYHaOB3cKYhF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj0CSc/btspFMb5Nbq/YkTbH54lEyYHaOB3cKYhF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj0CSc%2FbtspFMb5Nbq%2FYkTbH54lEyYHaOB3cKYhF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;505&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-77cc6136-4637-48b3-a9f0-07041b05cf65&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-b6d96d1f-bb63-4c74-9ebd-c5c120fe581b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;5. 위 이미지를 통해 비슷한 조건에서 페이징쿼리가 아닌 경우 정상적으로 fetch가 되는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/304</guid>
      <comments>https://ddurubird.tistory.com/304#entry304comment</comments>
      <pubDate>Tue, 1 Aug 2023 20:39:54 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) v1 &amp;rarr; v2 4일 차. 프로젝트 초기세팅</title>
      <link>https://ddurubird.tistory.com/303</link>
      <description>&lt;div id=&quot;SE-15da4c05-0aaa-4b9b-b9e7-8d268b50fe78&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;737&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lkeGc/btspg03LbX3/l3lpnOXIUmWKUmqZgzJ0e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lkeGc/btspg03LbX3/l3lpnOXIUmWKUmqZgzJ0e0/img.png&quot; data-alt=&quot;상품 카테고리 조회로직 마이그레이션 완료...!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lkeGc/btspg03LbX3/l3lpnOXIUmWKUmqZgzJ0e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlkeGc%2Fbtspg03LbX3%2Fl3lpnOXIUmWKUmqZgzJ0e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;288&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;737&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상품 카테고리 조회로직 마이그레이션 완료...!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-04d1ca4a-ed6a-44b5-b025-bdba80d8e74a&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-fc31c991-97e6-4591-9983-70bb2315ca12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;spring initializr에서 스프링부트 버전 선택과 의존성 몇 개만 추가하면 거의 세팅이 끝났던 v1과 다르게 v2은 spring initializr에서 제공하지 않는 라이브러리도 많고 버전 호환도 하나하나 신경써야 되어서 이전과 다르게 프로젝트 세팅에만 상당한 시간이 필요했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d5be9eca-3660-4aff-900e-bc79ac197892&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Version x.y.z 에서 x도 아닌 y부분에서 숫자 하나 낮다고 빌드가 안 되는 경험은 매우 고통스러웠지만 여러 시도 끝에 결국 프로젝트가 성공적으로 빌드되자 꽤 큰 성취감을 느꼈다.&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-6fd868df-9cc7-466d-aac6-956300daf7d8&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;멀티모듈&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-a4bf7e2a-ae69-4963-9d7e-dd4fc7cae6a7&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FQmTS/btsplP8wHeI/SaV6ltKWkRkr4fnKmKgG5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FQmTS/btsplP8wHeI/SaV6ltKWkRkr4fnKmKgG5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FQmTS/btsplP8wHeI/SaV6ltKWkRkr4fnKmKgG5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFQmTS%2FbtsplP8wHeI%2FSaV6ltKWkRkr4fnKmKgG5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;208&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-cc88ded0-693a-41cb-a8fa-ba8700864cbc&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-95cdd1f9-7963-44b1-b5f7-f2e170d265f5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;v2는 v1때 구현하지 못 했던 어드민을 개발하고 싶어 멀티모듈 프로젝트로 구성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ada44c5e-5f5e-4dfb-9c7f-28331bbd0b09&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로젝트의 크기가 멀티 저장소를 필요한 크기도 아니고 단일 저장소에서 멀티 모듈로 프로젝트를 구현할 경우 하나의 저장소만 관리하면 되니 관리가 편해지고 모듈 의존성만 추가하면 코드를 복붙하지 않고 동일한 코드를 재사용할 수 있기 때문.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-09ff442f-8d53-4cc8-a616-839276d31a31&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;테스트인데 401 Unauthorized...?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-7ad18c68-cf73-4515-812f-3557661e5f20&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ASvtc/btspsGoTJEc/gBNsPXMVg9C8HkYf19e0j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ASvtc/btspsGoTJEc/gBNsPXMVg9C8HkYf19e0j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ASvtc/btspsGoTJEc/gBNsPXMVg9C8HkYf19e0j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FASvtc%2FbtspsGoTJEc%2FgBNsPXMVg9C8HkYf19e0j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;370&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-bffeb050-9794-41d3-9345-925a001a1705&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-288545fd-afce-4fd5-ab0c-038e6b3c2132&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Handler테스트 코드를 작성하고 실행하니 401 Unauthorized에러가 발생하는 문제가 있었다, 원인은 ****SpringContextHolder에 Authentication Object이 설정되지 않아 인증이 되지 않았던 것.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ca9acbb8-3074-4469-b566-9079c3b94ab9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위와 같이 mutateWith메서드에 mockUser()를 추가해주니 쉽게 해결할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;SE-07a1ae13-a05e-428f-9a0d-5b4937e1f7a2&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;공통된 부분은 추상화로 코드 중복을 없애보자&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-04525bca-aba7-433d-86ca-afb49ccf6465&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;785&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S716o/btspmLLmTEM/dGqt700T7QmIAA6or8dcpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S716o/btspmLLmTEM/dGqt700T7QmIAA6or8dcpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S716o/btspmLLmTEM/dGqt700T7QmIAA6or8dcpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS716o%2FbtspmLLmTEM%2FdGqt700T7QmIAA6or8dcpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;785&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;785&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-28116dc1-9ff4-49fe-8e91-72a007bcaf7e&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-018c623c-a64a-46d4-826a-74524f7ac940&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;엔티티를 생성할 때 매번 id와 createdAt, updatedAt필드를 작성하고 equals와 hashCode작성하는 것이 번거로웠는데 id와 createdAt 그리고 updatedAt 을 가진 BaseEntity 를 상속시킴으로 번거로움을 제거할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-6143a01d-5ccb-4193-bfd5-c205e7720eae&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-da33df90-5018-4deb-84af-b4cfe98ff402&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;레포지토리도 인터페이스만 선언하고 세부 구현은 BaseRepository를 상속받는 방식으로 구현해 보고 싶었는데 생각보다 쉽지 않아 아래와 같이 구현 해놓은 상태. 더 좋은 방법은 무엇이 있을까 생각해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-17fd1131-0ebe-4f6a-9c89-3d3833e61834&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1690710497122&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
class CategoryService(
    private val categoryRepository: CategoryRepository
) {
    suspend fun findAll(): List&amp;lt;CategoryDto&amp;gt; {
        return categoryRepository.findAll().map {
            category -&amp;gt; CategoryDto.from(category)
        }
    }
}

interface BaseRepository&amp;lt;T&amp;gt; {
    suspend fun findAll(): List&amp;lt;T&amp;gt;
}

@Repository
class CategoryRepository(
    private val sessionFactory: SessionFactory,
    private val queryFactory: SpringDataHibernateMutinyReactiveQueryFactory
) : BaseRepository&amp;lt;Category&amp;gt; {
    override suspend fun findAll(): List&amp;lt;Category&amp;gt; {
        return queryFactory.listQuery {
            select(entity(Category::class))
            from(entity(Category::class))
            orderBy(col(Category::id).asc())
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/303</guid>
      <comments>https://ddurubird.tistory.com/303#entry303comment</comments>
      <pubDate>Sun, 30 Jul 2023 18:48:29 +0900</pubDate>
    </item>
    <item>
      <title>TIL - 쇼핑몰 프로젝트(Plain Old) 마이그레이션 3일 차. Kotlin JDSL Reactive</title>
      <link>https://ddurubird.tistory.com/302</link>
      <description>&lt;div id=&quot;SE-269efac7-08d1-49a2-89c3-626b6cc39626&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFmih/btspk8sP3Ar/2Kpeb5mH7b8nuDnvYtnDD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFmih/btspk8sP3Ar/2Kpeb5mH7b8nuDnvYtnDD0/img.png&quot; data-alt=&quot;SpringBoot 3.x.x + MySQL + Kotlin-JDSL-Reactive 구현 성공!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFmih/btspk8sP3Ar/2Kpeb5mH7b8nuDnvYtnDD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFmih%2Fbtspk8sP3Ar%2F2Kpeb5mH7b8nuDnvYtnDD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;376&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SpringBoot 3.x.x + MySQL + Kotlin-JDSL-Reactive 구현 성공!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;SE-5802a0e7-e66c-4499-8502-dae4eb5c2dad&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p id=&quot;SE-27abf385-a946-4e9c-85ac-447366127723&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;어제 Hibernate Reactive 2.0을 구현하면서 불편한 점들을 매우 간편하게 해결해 주는 오픈소스 프로젝트 Kotlin JDSL Reactive를 아샬님을 통해 알게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-db8e13c3-57bd-4004-a357-66317ca7ced6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-5248f832-7aa3-430e-b2d5-915db40fcef4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Kotlin JDSL Reactive는 아래와 같은 편리함을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-0c6e138f-f250-4b48-b9bd-0fb1e54852a7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- CompletedFuture 또는 flatMap을 사용해 코드 깊이가 깊어지는 문제를 해결하기 위해 Kotlin JDSL Reactive는 Kotlin의 coroutine을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-a56569fc-f899-4c87-9288-736751ba7b2b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- persistence.xml 작성의 번거로움을 해결하기 위해 Kotlin JDSL Reactive는 자체 DSL을 제공한다. 이를 통해 데이터베이스 설정을 Kotlin 코드 내에서 직접 처리할 수 있어 설정 작업이 비교적 간단하다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-5d074d10-1eef-44fd-892a-ca7a993719c7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- Kotlin coroutine을 활용해 비동기 코드를 동기 코드와 비슷하게 작성할 수 있다는 점은 매우 마음에 든다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-84a0faa9-a58e-49cc-86f4-686ecc73dc0a&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-612183ee-e95f-4583-ba12-faab69f5507b&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;물론 H2 데이터베이스 외에 다른 데이터베이스 설정 예제도 함께 제공했다면 더 좋았겠지만, MySQL 연동은 어찌저찌 성공했으니 매우 만족스럽다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d1048469-5efa-4cee-b485-01ef60f34f24&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-9ca037a1-2972-41ad-8d3a-d24ee74ce049&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 큰 고난이었던 데이터베이스 관련 부분을 해결했으니 내일 부터는 프로젝트 세팅을 시작해보자!&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>TIL</category>
      <category>til</category>
      <author>김뚜루</author>
      <guid isPermaLink="true">https://ddurubird.tistory.com/302</guid>
      <comments>https://ddurubird.tistory.com/302#entry302comment</comments>
      <pubDate>Fri, 28 Jul 2023 20:00:21 +0900</pubDate>
    </item>
  </channel>
</rss>