diff --git a/_posts/2023-08-10-spring-data-dynamodb-localdatetime.md b/_posts/2023-08-10-spring-data-dynamodb-localdatetime.md
index 94bd755..789e20e 100644
--- a/_posts/2023-08-10-spring-data-dynamodb-localdatetime.md
+++ b/_posts/2023-08-10-spring-data-dynamodb-localdatetime.md
@@ -1,7 +1,7 @@
---
title: LocalDateTime 저장 에러
date: 2023-08-10 18:20:00 +/-TTTT
-categories: [Spring, Spring Data DynamoDB]
+categories: [Project, POCHAK]
tags: [spring, dynamodb, troubleshooting] # TAG names should always be lowercase
---
diff --git a/_posts/2023-08-10-spring-data-dynamodb-partitionkey-and-sortkey.md b/_posts/2023-08-10-spring-data-dynamodb-partitionkey-and-sortkey.md
index 3df448f..bd27ee3 100644
--- a/_posts/2023-08-10-spring-data-dynamodb-partitionkey-and-sortkey.md
+++ b/_posts/2023-08-10-spring-data-dynamodb-partitionkey-and-sortkey.md
@@ -1,7 +1,7 @@
---
title: Partition Key와 Sort Key를 같이 사용할 때
date: 2023-08-10 18:20:00 +/-TTTT
-categories: [Spring, Spring Data DynamoDB]
+categories: [Project, POCHAK]
tags: [spring, dynamodb, troubleshooting] # TAG names should always be lowercase
---
diff --git a/_posts/2023-08-16-spring-data-dynamodb-query.md b/_posts/2023-08-16-spring-data-dynamodb-query.md
index 419aca0..6034094 100644
--- a/_posts/2023-08-16-spring-data-dynamodb-query.md
+++ b/_posts/2023-08-16-spring-data-dynamodb-query.md
@@ -1,7 +1,7 @@
---
title: Spring Data DynamoDB 쿼리 작업 정리
date: 2023-08-16 18:20:00 +/-TTTT
-categories: [Spring, Spring Data DynamoDB]
+categories: [Project, POCHAK]
tags: [spring, dynamodb] # TAG names should always be lowercase
---
diff --git a/_posts/2023-09-11-spring-data-dynamodb-paging.md b/_posts/2023-09-11-spring-data-dynamodb-paging.md
index f64371d..8ac1fce 100644
--- a/_posts/2023-09-11-spring-data-dynamodb-paging.md
+++ b/_posts/2023-09-11-spring-data-dynamodb-paging.md
@@ -1,7 +1,7 @@
---
title: Spring Data DynamoDB 페이징 정리
date: 2023-09-11 18:20:00 +/-TTTT
-categories: [Spring, Spring Data DynamoDB]
+categories: [Project, POCHAK]
tags: [spring, dynamodb] # TAG names should always be lowercase
---
diff --git a/_posts/2024-08-25-cloud-sql-migration.md b/_posts/2024-08-25-cloud-sql-migration.md
new file mode 100644
index 0000000..528a050
--- /dev/null
+++ b/_posts/2024-08-25-cloud-sql-migration.md
@@ -0,0 +1,102 @@
+---
+title: AWS RDS에서 GCP Cloud SQL로 마이그레이션 작업
+date: 2024-08-25 18:30:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, migration, cloudsql, gcp] # TAG names should always be lowercase
+---
+
+# Cloud SQL로 데이터베이스를 변경한 이유
+
+창업지원단의 지원금을 받고 있기에 대부분의 서비스를 AWS에서 GCP로 옮기고 있는 상황입니다.
+
+따라서 기존에 사용하던 AWS의 RDS에서 GCP의 Cloud SQL로 마이그레이션을 하기로 결정하였습니다.
+
+# 작업 과정
+## GCP Database Migration API 활성화하기
+
+GCP에서 제공해주는 Database Migration API를 사용하여 작업을 시작해보겠습니다.
+먼저 Database Migration API를 검색하여 활성화를 시켜주어야 합니다.
+
+아래 이미지는 이미 활성화된 모습입니다.
+
+![img](/assets/img/2024-08-25-cloud-sql-migration/1.png)
+
+## 연결 프로필 생성
+
+그 뒤, 연결 프로필 `Connection profiles` 을 생성해주어야 합니다.
+
+> 연결 프로필은 소스 데이터베이스 인스턴스(예: MySQL용 Amazon RDS)에 대한 정보를 저장하고,
+> Database Migration Service에서 대상 Cloud SQL 데이터베이스 인스턴스로 데이터를 마이그레이션하는 데 사용됩니다.
+{: .prompt-tip }
+
+
+
+`데이터베이스 마이그레이션 > 연결 프로필`에서
+상단의 `+ 프로필 만들기` 버튼을 눌러 새로운 프로필을 만들어줍시다.
+
+![img](/assets/img/2024-08-25-cloud-sql-migration/2.png)
+
+
+다음과 같이 세팅해주었습니다.
+
+1. 데이터베이스 엔진 : `MySQL`
+2. 연결 프로필 이름 : `mysql-rds` (편한 이름으로 변경 가능)
+3. 연결 프로필 ID : `mysql-rds` (편한 아이디로 변경 가능)
+4. 호스트 이름 또는 IP 주소 : {RDS IP 주소}
+5. 포트 : `3306` (MySQL 기본 포트 - 설정에 따라 변경될 수 있습니다.)
+6. 사용자 이름 : {MySQL 사용자 이름}
+7. 비밀번호 : {MySQL 비밀번호}
+8. 리전 : `asia-northeast3 (서울)` (저는 서울로 설정하였습니다.)
+
+## 마이그레이션 작업 생성
+
+`데이터베이스 마이그레이션 > 마이그레이션 작업`에서
+상단의 `+ 마이그레이션 작업 만들기` 버튼을 눌러 새로운 마이그레이션 작업을 만들어줍시다.
+
+![img](/assets/img/2024-08-25-cloud-sql-migration/3.png)
+
+1. 소스 데이터베이스 엔진 : `MySQL`
+2. 대상 리전: `asia-northeast3 (서울)`
+3. 마이그레이션 작업 유형: 일회성
+
+으로 설정하고 `저장하고 계속하기` 버튼을 클릭해줍니다.
+
+
+
+다음으로 소스 연결 프로필로 아까 생성한 `mysql-rds`를 선택해줍니다.
+다른건 기본 세팅으로 두고 진행하였습니다.
+
+
+
+마이그레이션 대상 데이터베이스를 생성합니다.
+비용을 위해 최소 사양으로 설정하였습니다!
+- `Enterprise`
+- 공유 코어 vCPU 1개, `0.614GB`
+- `HDD`, `10GB`
+- `MySQL 8.0.31`
+
+
+
+대상 데이터베이스 생성이 완료되면, 다음으로 넘어가 연결 방법을 선택합니다.
+
+연결 방법은 `IP 허용 목록` 으로 설정하고, `Destination IP Address`를 복사합니다.
+복사한 AWS RDS 인스턴스의 보안그룹에 추가하여 접근이 가능하도록 설정해주어야 합니다.
+
+
+
+모두 설정해준 뒤, 마이그레이션 작업을 시작해줍니다.
+- 설정에 이상이 없다면 마이그레이션 작업이 시작됩니다.
+
+![img](/assets/img/2024-08-25-cloud-sql-migration/4.png)
+_설정은 이 사진을 참고해주세요!_
+
+약 13분 정도 작업이 진행되었고,
+이후에 `Cloud SQL > (대상 인스턴스) > 작업`에 들어가서
+마이그레이션 작업 과정은 다음과 같이 확인할 수 있었습니다.
+
+![img](/assets/img/2024-08-25-cloud-sql-migration/5.png)
+
+
+
+# 참고자료
+- [Migrating to Cloud SQL from Amazon RDS for MySQL Using Database Migration Service](https://www.cloudskillsboost.google/focuses/17696?catalog_rank=%7B%22rank%22:1,%22num_filters%22:0,%22has_search%22:true%7D&parent=catalog&search_id=22626074)
\ No newline at end of file
diff --git a/_posts/2024-08-28-docker.sock-permission-error.md b/_posts/2024-08-28-docker.sock-permission-error.md
new file mode 100644
index 0000000..d763109
--- /dev/null
+++ b/_posts/2024-08-28-docker.sock-permission-error.md
@@ -0,0 +1,158 @@
+---
+title: Jenkins에서 docker build시, docker.sock 권한 에러 발생 해결
+date: 2024-08-28 17:00:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, infra, jenkins, troubleshooting] # TAG names should always be lowercase
+---
+
+# 에러 상황
+
+## 발생 위치
+
+Jenkins에서 배포 과정을 실행하며 다음과 같은 오류가 발생하였습니다.
+
+![error](/assets/img/2024-08-28-docker.sock-permission-error/1-error-img.png)
+_에러 발생!!_
+
+- 에러가 발생한 파이프라인의 명렁어
+
+``` bash
+$ docker build -f Dockerfile -t pochakgreen/pochak-dev .
+```
+
+## 원인 확인
+
+아래의 로그로 확인해봤을때, `docker.sock` 실행 과정에서 `permission denied` 권한 에러가 발생하였습니다.
+
+에러 해결과정을 정리해보겠습니다!
+
+- 로그
+
+```
+jenkins@fbfbfcdce8e0:~/workspace/dev-pipeline$ docker build -f Dockerfile -t pochakgreen/pochak-dev .
+DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
+ Install the buildx component to build images with BuildKit:
+ https://docs.docker.com/go/buildx/
+
+permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock:
+
+...
+
+: dial unix /var/run/docker.sock: connect: permission denied
+```
+
+# 환경
+
+## 호스트 환경
+- Ubuntu 20.04.6 LTS
+- Docker version 27.1.2
+- Jenkins Version 2.473 : 도커 컨테이너로 실행
+
+## 도커 컨테이너
+
+다음과 같은 명령어를 통해 도커 소켓을 마운트해서 Jenkins 컨테이너를 실행시키고 있습니다.
+
+``` bash
+$ docker run \
+ -itd --name jenkins \
+ -p 9090:8080 \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ -v /usr/bin/docker:/usr/bin/docker jenkins/jenkins:jdk17
+```
+
+# 해결 과정
+
+## docker.sock의 권한 확인
+
+먼저 docker container로 접속해서 `docker.sock`의 권한을 확인합니다.
+
+```bash
+$ docker exec -it jenkins /bin/bash
+$ ls -al /var/run/
+```
+
+`docker.sock` 파일의 그룹 권한이 이름이 없고 GID `998`로 설정되어 있는 것을 확인할 수 있습니다.
+
+![check-permission](/assets/img/2024-08-28-docker.sock-permission-error/2-check-permission.png)
+
+> 따라서 앞으로의 해결 과정은
+> 1. `docker` 그룹을 만들어서
+> 2. `jenkins` 유저를 `docker` 그룹에 넣어주고,
+> 3. `docker` 그룹에 `docker.sock` 실행권한을 부여해줄 예정입니다.
+{: .prompt-tip }
+
+## `docker` 그룹 설정
+
+먼저 젠킨스 컨테이너에 root 권한으로 접속합니다.
+
+```bash
+$ docker exec -itu0 jenkins /bin/bash # root로 접속
+```
+
+
+
+저는 기존에 docker 그룹이 없었기 때문에 새로 생성해 주었습니다.
+- 이미 존재할 경우 새로 생성할 필요 없음!
+
+```bash
+$ groupadd docker
+```
+
+
+
+그 뒤, `jenkins` 유저를 그룹에 넣어주고,
+docker 그룹에 `docker.sock` 실행권한을 부여하였습니다.
+
+```bash
+$ usermod -aG docker jenkins
+$ chown root:docker /var/run/docker.sock
+```
+
+![group-setting](/assets/img/2024-08-28-docker.sock-permission-error/3-group-setting.png)
+
+## `docker.sock` 실행권한 확인
+
+다시 젠킨스로 접속하여 `docker.sock`의 권한을 확인하면,
+docker 그룹에 실행 권한이 생긴걸 확인할 수 있습니다.
+
+```bash
+$ docker exec -itu0 jenkins /bin/bash # 다시 jenkins로 접속
+$ ls -al /var/run/
+```
+
+![permission](/assets/img/2024-08-28-docker.sock-permission-error/4-permission.png)
+
+
+
+또한 아래의 명령어를 통해 `docker` 그룹 안에 `jenkins` 유저가 있는 것도 확인할 수 있습니다.
+
+```bash
+$ cat /etc/group | grep docker
+```
+
+![img](/assets/img/2024-08-28-docker.sock-permission-error/5-group.png)
+
+## jenkins 재시작
+
+이렇게 권한을 모두 확인하였음에도 "바로" jenkins 파이프라인을 실행시키면, 동일한 오류가 발생합니다.
+
+
+
+재시작을 하지 않고, 파이프라인에 `id` 명령어를 통해 확인해보니, `jenkins` 유저에 `docker` 그룹이 빠져있는 것을 확인할 수 있었습니다.
+
+![img](/assets/img/2024-08-28-docker.sock-permission-error/6-id.png)
+
+> 참고로 터미널에서 jenkins 컨테이너에 접속해서 확인해봤을 때는 docker 그룹도 잘 나오는 상태였습니다.
+> ![img](/assets/img/2024-08-28-docker.sock-permission-error/7-terminal.png)
+{: .prompt-info }
+
+
+
+따라서 jenkins 컨테이너를 재시작하고 다시 파이프라인을 실행하면,
+다음과 같이 `docker` 그룹도 확인할 수 있고,
+배포 과정도 정상적으로 수행됩니다!
+
+![img](/assets/img/2024-08-28-docker.sock-permission-error/8-id.png)
+
+![img](/assets/img/2024-08-28-docker.sock-permission-error/9-success.png)
+_해결 완료!_
\ No newline at end of file
diff --git a/_posts/2024-08-31-dns-setting.md b/_posts/2024-08-31-dns-setting.md
new file mode 100644
index 0000000..11d140c
--- /dev/null
+++ b/_posts/2024-08-31-dns-setting.md
@@ -0,0 +1,132 @@
+---
+title: GCP Cloud DNS를 통한 도메인 설정과 SSL 인증서 설치
+date: 2024-08-31 17:00:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, infra, dns] # TAG names should always be lowercase
+---
+
+# 새로 도메인을 설정한 이유
+POCHAK의 기존 도메인이 만료됨에 따라 기존 도메인 연장 혹은 새로운 도메인으로의 변경이 필요한 상태였습니다.
+팀원들과의 논의 결과, 무엇보다도 기존 도메인의 연장비용이.. 굉장히 부담스러웠기에 비교적으로 저렴한 `pochak.app` 이라는 새로운 도메인을 구매하게 됩니다.
+
+> 창업지원단의 지원금을 받고 있기에 대부분의 서비스를 AWS에서 GCP로 옮기고 있었고, 따라서 AWS의 Route53이 아닌 GCP의 Cloud DNS라는 서비스를 통해 도메인을 설정하였습니다.
+{: .prompt-info }
+
+# 환경
+
+## 호스트 환경
+- GCP
+- Ubuntu 20.04.6 LTS
+- nginx/1.18.0 (Ubuntu)
+
+# 도메인 설정 with GCP
+> 가비아 구매 과정은 생략하겠습니다!
+
+## Cloud DNS 영역 만들기
+![img](/assets/img/2024-08-31-dns-setting/1-img.png)
+_사진에서는 pochak--zone으로 캡처하였지만, 실제로는 pochak-zone으로 설정함_
+
+다음과 같이 영역 이름을 `pochak-zone`, DNS 이름을 `pochak.app`으로 설정해주었습니다.
+
+## 새로운 레코드 추가하기
+![img](/assets/img/2024-08-31-dns-setting/2-img.png)
+
+`표준 추가` 버튼을 눌러 새로운 레코드를 추가해줍니다.
+
+
+
+![img](/assets/img/2024-08-31-dns-setting/3-img.png)
+
+위와 같이 `A` 유형, 데이터엔 연결될 서버의 `IP주소`를 넣어줍니다.
+
+## 가비아에서 GCP 네임 서버 설정
+
+이제 다음과 같이 도메인 구매처의 관리 페이지에 들어가, GCP 네임 서버를 설정해주면 됩니다.
+
+![img](/assets/img/2024-08-31-dns-setting/4-img.png)
+
+네임 서버 정보는 콘솔에서 `NS` 레코드에서 확인할 수 있습니다.
+
+![img](/assets/img/2024-08-31-dns-setting/5-img.png)
+
+# SSL 인증서 설치
+> .app 도메인은 SSL 인증서 설치가 필수입니다!
+{: .prompt-info }
+
+## certbot 설치
+
+```bash
+$ sudo apt install certbot
+$ sudo apt install python3-certbot-nginx
+$ sudo certbot --nginx
+```
+
+여기서 `sudo certbot --nginx` 명령어는 certbot이 nginx 설정을 자동으로 조정하게 하는 명령어입니다.
+
+![img](/assets/img/2024-08-31-dns-setting/6-img.png)
+
+1. 먼저 도메인 `pochak.app`을 입력하였고,
+2. 2번 `Redirect`로 설정을 해주었습니다.
+
+
+
+`/etc/nginx/sites-enabled/default` 파일을 확인해보면
+certbot이 다음과 같이 자동으로 세팅을 해둔 것을 확인할 수 있습니다.
+
+![img](/assets/img/2024-08-31-dns-setting/7-img.png)
+
+## Ubuntu 방화벽 확인
+
+- 80 (http)
+- 443 (https)
+- 22 (ssh)
+
+포트가 모두 열려있는지 확인합니다.
+
+```bash
+$ sudo ufw status
+```
+
+![img](/assets/img/2024-08-31-dns-setting/8-img.png)
+
+## SSL 자동 갱신 확인
+
+```bash
+$ sudo certbot renew --dry-run
+```
+
+위 명령어를 통해 SSL 인증서 갱신 시뮬레이션을 실행할 수 있습니다.
+
+![img](/assets/img/2024-08-31-dns-setting/9-img.png)
+
+위와 같이 `Congratulations ~~` 이 뜬다면 성공!
+
+## 사이트 보안 등급 확인
+https://www.ssllabs.com/ssltest/analyze.html
+
+위 사이트로 접속하여 도메인을 입력하면 사이트 보안 등급을 확인할 수 있습니다.
+
+![img](/assets/img/2024-08-31-dns-setting/10-img.png)
+
+
+## 설정 완료
+
+https://pochak.app 에 정상적으로 서버로 접속할 수 있는 걸 확인할 수 있습니다!
+
+![img](/assets/img/2024-08-31-dns-setting/11-img.png)
+
+# 트러블슈팅
+
+## 자동 갱신 과정에서 발생한 에러
+
+```bash
+$ sudo certbot certonly -d "*.pochak.app" --manual --preferred-challenges dns
+```
+
+> 위와 같이 --manual 을 사용하여 SSL 인증서를 발급받았다면,
+> 중간에 TXT 레코드 편집이 필요합니다.
+> 하지만, certbot이 레코드 편집까지 자동으로 수행해주는 것은 불가능하기 때문에, 에러가 발생하는 것입니다.
+> [참고: GitHub Issue](https://github.com/certbot/certbot/issues/6280#issuecomment-451361955)
+{: .prompt-warning }
+
+위 --manual 사용은 *(와일드 카드) 사용에 유리한 면이 있지만, 아직 포착은 도메인을 다양하게 사용하고 있지 않기에 위와 같은 방법을 사용하였습니다.
\ No newline at end of file
diff --git a/_posts/2024-09-28-block-querydsl.md b/_posts/2024-09-28-block-querydsl.md
new file mode 100644
index 0000000..26f3a18
--- /dev/null
+++ b/_posts/2024-09-28-block-querydsl.md
@@ -0,0 +1,217 @@
+---
+title: 차단 로직을 Querydsl로 리팩토링한 과정
+date: 2024-09-28 10:45:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, springboot, querydsl] # TAG names should always be lowercase
+---
+
+# 기존 코드
+
+## 구현해내고자 했던 기능
+
+포착은 소셜 네트워킹 서비스로 앱스토어와 플레이스토어의 규정상 "차단" 기능이 필수적으로 필요하였습니다.
+
+팀원들과 상의하며 규정한 차단 로직은 다음과 같습니다.
+
+1. 사용자A가 사용자B를 차단한다.
+2. 사용자A는 사용자B를 조회할수도, 사용자B가 포함된 게시물을 확인할 수 없다.
+ - 여기서 사용자B가 포함된 게시물이란 사용자B가 포착하였거나, 포착된 게시물 전부
+3. 사용자B도 마찬가지로 사용자A 조회가 불가능하며, 사용자A가 포함된 게시물을 확인할 수 없다.
+4. 사용자A는 사용자B가 포함된 게시물이 아니더라도, 다른 게시물에 사용자B가 남긴 댓글을 확인할 수 없다. (사용자B도 마찬가지)
+5. 차단한 순간 사용자A와 사용자B 사이의 팔로우 상태는 전부 끊긴다.
+6. 차단한 순간 사용자A와 사용자B가 함께 포함된 게시물은 전부 INACTIVE한 상태가 된다.
+
+"태깅" 기능이 있는 포착의 특성상 굉장히 어렵게.. 구현이 완료되었습니다.
+
+## 리팩토링을 진행한 기존 쿼리
+
+위의 차단 로직 중에서 2, 3번에 해당하는 "차단된 혹은 차단한 사용자가 포함된 게시물을 확인할 수 없다"는 부분을 구현한 기존 쿼리입니다.
+
+- 기존 쿼리
+```java
+@Query("""
+ select p from Post p
+ join fetch p.owner
+ where p.id = :postId and p.status = 'ACTIVE'
+ and p.owner not in (select b.blockedMember from Block b where b.blocker = :loginMember)
+ and :loginMember not in (select b.blockedMember from Block b where b.blocker = p.owner)
+ and not exists (select t.member from Tag t where t.post = p intersect select b.blockedMember from Block b where b.blocker = :loginMember)
+ and :loginMember not in (select b.blockedMember from Block b where b.blocker in (select t.member from Tag t where t.post = p))
+ """)
+Optional findById(
+ @Param("postId") final Long postId,
+ @Param("loginMember") final Member loginMember
+);
+```
+
+### 이전 차단 기능 구현 방식
+
+일단 각 조건인 and에 걸려있는 서브 쿼리인 `in (select ~ )` 절이 굉장히 많았습니다.
+
+확인해야 할 부분이 총 4가지였습니다.
+1. 현재 로그인한 사람이 조회하고자 하는 게시물의 포차커를 차단하였는가?
+2. 현재 로그인한 사람이 조회하고자 하는 게시물의 포차키 중 한명이라도 차단하였는가?
+3. 조회하고자 하는 게시물의 포차커가 현재 로그인한 사람을 차단하였는가?
+4. 조회하고자 하는 게시물의 포차키 중 한 명이라도 현재 로그인한 사람을 차단하였는가?
+
+> 여기서 포차커는 "포착해준 사람" 포차키는 "포착당한 사람"으로 정의됩니다.
+{: .prompt-info }
+
+### 문제점
+
+문제는 이 조건을 모두 in (select 서브 쿼리) 절, 3번과 4번 조건의 경우엔 2번의 서브 쿼리가 필요했습니다.
+- 총 6번의 서브 쿼리가 구현에 사용되었습니다.
+
+불필요한 서브 쿼리를 남발하고 있었기에 (특히나 위 쿼리는 중복되는 쿼리 역시도 존재했기에) 개선이 굉장히 시급했습니다.
+
+> 물론 POCHAK은 최신 버전의 MySQL(8.0.31)을 사용하고 있기 때문에, 서브쿼리의 성능이 JOIN에 비해 크게 떨어지지 않습니다. (이후에 나올 성능 측정에서 역시도 큰 차이를 보이고 있지 않습니다.)
+> 하지만, String으로 작성해야하는 JPQL의 특성상 재사용성이 굉장히 떨어지고, 직관적이지 못해 유지보수성 역시도 좋다고 느껴지지 않았습니다.
+> 따라서 앞으로 포착 애플리케이션의 유지보수를 위해서라도 복잡한 쿼리는 QueryDSL을 통해 개선하고자한 의도도 있습니다!
+{: .prompt-tip }
+
+# 환경
+
+## 개발 환경 정보
+
+- SpringBoot 3.2.1 (Java 17)
+- MySQL (8.0.31)
+- Querydsl 5.0.0
+
+# 리팩토링 결과
+
+## 개선된 쿼리
+
+- 개선한 쿼리
+
+```java
+public Optional findByIdWithoutBlockPost(
+ final Long postId,
+ final Long loginMemberId
+) {
+ return Optional.ofNullable(
+ query.selectFrom(post)
+ .join(post.owner).fetchJoin()
+ .join(tag).on(tag.post.eq(post))
+ .leftJoin(block).on(
+ checkOwnerOrTaggedMemberBlockLoginMember(loginMemberId)
+ .or(checkLoginMemberBlockOwnerOrTaggedMember(loginMemberId))
+ )
+ .groupBy(post)
+ .having(block.id.count().eq(0L))
+ .where(
+ post.id.eq(postId),
+ post.status.eq(BaseEntityStatus.ACTIVE)
+ )
+ .fetchOne()
+ );
+}
+
+private BooleanExpression checkOwnerOrTaggedMemberBlockLoginMember(final Long loginMemberId) {
+ return (block.blocker.eq(tag.member).or(block.blocker.eq(post.owner)))
+ .and(block.blockedMember.id.eq(loginMemberId));
+}
+
+private BooleanExpression checkLoginMemberBlockOwnerOrTaggedMember(final Long loginMemberId) {
+ return (block.blocker.id.eq(loginMemberId))
+ .and(block.blockedMember.eq(tag.member).or(block.blockedMember.eq(post.owner)));
+}
+```
+> 메소드 명은 저도 유감입니다.. 하지만 최선이었어요🙃
+
+### 구현 방식
+
+서브쿼리를 줄일 수 있는 방법에 대해 고민하였고, JOIN의 형태로 해결하는 방법에 대해 고민했습니다.
+- 그림에서 post.owner 페치조인의 경우는 제외하였습니다
+
+![img](/assets/img/2024-09-28-block-querydsl/img0.jpeg)
+
+1. 먼저, 기준테이블인 post 테이블에 tag 테이블을 inner join을 합니다. 여기서 만약 게시글에 여러명을 태그했을 경우, 위 그림과 같이 데이터 뻥튀기가 발생할 수 있지만 이는 나중에 groupby 절을 통해 해결해줄 예정입니다.
+2. 그렇게 산출된 결과테이블을 기준테이블로 block을 LEFT JOIN 해줍니다. 조건은 다음과 같습니다.
+ 1. 태그된(촬영된) 유저 혹은 게시물을 업로드한 유저가 현재 로그인한 사람이 차단했을 경우
+ 2. 또는, 현재 로그인한 유저가 태그된 유저 혹은 게시물을 업로드한 유저를 차단했을 경우
+ - ⇒ 해당 조건에 충족한다면 block 데이터가, 아니라면 null로 행에 추가됩니다.
+3. `where` 절을 통해 현재 상태가 `ACTIVE`하고, 사용자가 요청한 `게시물 id`값이 반환될 수 있도록 필터링 해줍니다.
+4. 그 다음 1. 2. 과정에서 일어날 수 있는 데이터 뻥튀기를 방지하기 위하여 `.groupBy(post)` 를 통해 중복 데이터를 막아줍니다.
+5. 또한, `.having(block.id.count().eq(0L))`을 통해 만약 2. 과정의 조건에 걸려 null이 아닌 하나의 block id라도 행에 표기되어 있다면 필터링을 통해 결과에 반영되지 않도록 설정하였습니다.
+
+### 결과
+
+일단 자바 코드로 작성한 쿼리이기에 가독성이 좋아졌습니다. `checkOwnerOrTaggedMemberBlockLoginMember()` 메소드 등의 사용을 통해 조건절에 대한 의도 전달이 명확해졌습니다.
+
+또한, `in 서브쿼리` 방식에서 `join` 사용으로 변경함으로써 성능 향상을 기대할 수 있습니다.
+
+## 성능 비교 및 마무리
+
+결과적으로 개선이 되긴 되었지만, 드라마틱하게 실행 시간이 줄어들진 않았습니다. [이 곳](https://smwu-pochak.github.io/posts/test-query-performance/)에서 자세한 내용을 확인할 수 있습니다!
+
+또한 Querydsl 도입 이후, CustomRepository를 테스팅하는 방법을 [이 곳](ttps://smwu-pochak.github.io/posts/testing-repository/)에서 정리하였습니다!
+
+# [10/2 내용 추가] 리팩토링 V2
+
+## V2 리팩토링 결과
+
+### 개선된 쿼리
+
+```java
+private Optional findByIdWithoutBlockPost(
+ final Long postId,
+ final Long loginMemberId
+) {
+return Optional.ofNullable(
+ query.selectFrom(post)
+ .join(post.owner).fetchJoin()
+ .join(tag).on(tag.post.eq(post).and(post.id.eq(postId)))
+ .leftJoin(block).on(
+ checkOwnerOrTaggedMemberBlockLoginMember(loginMemberId)
+ .or(checkLoginMemberBlockOwnerOrTaggedMember(loginMemberId))
+ )
+ .groupBy(post)
+ .having(block.id.count().eq(0L))
+ .where(post.status.eq(BaseEntityStatus.ACTIVE))
+ .fetchOne()
+);
+}
+```
+
+### 개선한 부분
+
+기존에 where 절에 포함되어 있던 `post.id.eq(postId)` 조건을 초반에 tag를 inner join할 때 on절의 조건으로 이동시켰습니다.
+
+[기존 쿼리](https://smwu-pochak.github.io/posts/block-querydsl/#%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D)의 구현방식에서 첫번째 `기준테이블인 post 테이블에 tag 테이블을 inner join` 해주는 과정에 on 조건절에 `post.id.eq(postId)` 를 추가하였습니다.
+
+원래 `where(post.id.eq(postId))`를 통하여 Tag와의 JOIN, Block과의 LEFT JOIN이 끝난 뒤 where문을 통해 post id를 필터링하는 방식에서 미리 join 전에 필터링을 거는 방식으로 변경하였습니다.
+
+
+## JOIN - ON 과 WHERE의 성능 차이
+
+### 주된 차이점
+
+- `ON` : join 전에 조건을 필터링
+- `WHERE` : join 후에 조건을 필터링
+
+inner join의 경우, 조건을 `on`과 `where` 어디에 위치시켜도 성능은 동일합니다.
+하지만 outer join일 경우 달라지게 됩니다.
+
+무엇보다도 기존 테이블의 행을 줄여서 left join 전에 필요한 데이터 양을 미리 줄이는 것입니다.
+
+### 개선 결과
+
+이전 방식은 다음 그림과 같습니다. Post 테이블과 Tag 테이블을 JOIN 하기에 다음과 같은 결과가 산출됩니다.
+
+![img](/assets/img/2024-09-28-block-querydsl/img1.jpeg)
+
+따라서 산출된 결과 테이블에서 Block을 LEFT JOIN 해주기에,
+정말 찾으려는 id를 가진 Post 뿐만 아니라 다른 게시물 데이터까지 Block과 Left JOIN을 해주어야 한다는 한계가 있었습니다.
+
+
+
+따라서 Tag와 inner join을 할 때 on 조건절에 `post.id.eq(postId)` 를 추가하여 찾고자하는 id를 가진 게시물만 결과값으로 나올 수 있도록 수정하였습니다.
+
+![img](/assets/img/2024-09-28-block-querydsl/img2.jpeg)
+
+
+## 성능 비교
+
+기존, 개선한 V1, V2 쿼리 각각의 성능 측정은 [이 글](https://smwu-pochak.github.io/posts/test-query-performance/#2%EC%B0%A8-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%ED%95%B4%EB%B4%85%EC%8B%9C%EB%8B%A4)에서 자세히 측정하였습니다.
+
+근소하지만 성능 향상의 결과를 얻을 수 있었습니다.
diff --git a/_posts/2024-09-29-test-query-performance.md b/_posts/2024-09-29-test-query-performance.md
new file mode 100644
index 0000000..5ab7c0d
--- /dev/null
+++ b/_posts/2024-09-29-test-query-performance.md
@@ -0,0 +1,285 @@
+---
+title: 쿼리 성능 테스트
+date: 2024-09-29 13:45:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, springboot, test, performance] # TAG names should always be lowercase
+---
+
+# 🧐 들어가며
+
+최근 차단 쿼리를 `in (서브쿼리)` 방식에서 `left join`을 사용한 방식으로 개선하고 있습니다. 물론 쿼리 가독성을 위해 querydsl도 도입하며 다양한 방식을 배울 수도 있었는데요! 과연 제가 개선한 쿼리가 이전 쿼리보다 얼마나 나아졌을까에 대한 궁금증이 생겼습니다.😆
+
+# ⚒️ 테스팅 시나리오 정리
+
+## 환경
+클라우드 환경에서 테스팅도 좋겠지만 일단 비용이 굉장히 걱정되고, 순수한 "쿼리" 테스팅을 위해 로컬에서 테스트를 진행할 예정입니다!
+- SpringBoot 3.2.1 (Java 17)
+- H2 Database 2.3.232
+
+## 데이터 세팅
+각각 테스트 코드로 작성해서 H2 데이터베이스에 더미데이터를 넣어주었습니다
+- 게시물 더미 데이터 40000개
+- 멤버 더미 데이터 30000개
+- 태그 더미 데이터 20000개
+- 차단 더미 데이터 120000개
+
+![img](/assets/img/2024-09-29-test-query-performance/img0.png)
+_설정 완료!_
+
+## 테스팅할 메소드
+
+자세한 리팩토링 과정은 [이곳](https://smwu-pochak.github.io/posts/block-querydsl/)에서 확인할 수 있습니다.
+
+- 기존 쿼리
+```java
+@Query("""
+ select p from Post p
+ join fetch p.owner
+ where p.id = :postId and p.status = 'ACTIVE'
+ and p.owner not in (select b.blockedMember from Block b where b.blocker = :loginMember)
+ and :loginMember not in (select b.blockedMember from Block b where b.blocker = p.owner)
+ and not exists (select t.member from Tag t where t.post = p intersect select b.blockedMember from Block b where b.blocker = :loginMember)
+ and :loginMember not in (select b.blockedMember from Block b where b.blocker in (select t.member from Tag t where t.post = p))
+ """)
+Optional findById(
+ @Param("postId") final Long postId,
+ @Param("loginMember") final Member loginMember
+);
+```
+
+- 개선한 쿼리
+```java
+public Optional findByIdWithoutBlockPost(
+ final Long postId,
+ final Long loginMemberId
+) {
+ return Optional.ofNullable(
+ query.selectFrom(post)
+ .join(post.owner).fetchJoin()
+ .join(tag).on(tag.post.eq(post))
+ .leftJoin(block).on(
+ checkOwnerOrTaggedMemberBlockLoginMember(loginMemberId)
+ .or(checkLoginMemberBlockOwnerOrTaggedMember(loginMemberId))
+ )
+ .groupBy(post)
+ .having(block.id.count().eq(0L))
+ .where(
+ post.id.eq(postId),
+ post.status.eq(BaseEntityStatus.ACTIVE)
+ )
+ .fetchOne()
+ );
+}
+```
+
+## 테스팅 방법
+1. 최대한 많은 요청을 각 v1, v2 api에 전송한다.
+2. 동일한 환경에서 전송한다. (로컬 환경에서 테스팅할 예정!)
+3. 전송 후 응답 시간 등 결과를 확인한다.
+
+# 🛠️ 테스트
+## Apache Bench
+
+Apache AB (Apache Benchmark)는 Apache HTTP Server의 성능을 테스트하고 측정하기 위한 명령줄 도구입니다.
+
+저는 맥북 프로를 사용하고 있기 때문에, 따로 세팅은 필요하지 않았고, 터미널을 통해 명령어를 보내서 바로 테스팅할 수 있었습니다!
+
+### 세팅
+
+아래의 명령어를 사용하여 서버에 "게시물 조회" 요청을 보내보겠습니다!
+
+```
+ab -n 12000 -c 100 -H 'Authorization: Bearer ey~~' http://localhost:8080/api/v2/posts/{postId}
+```
+
+- `-n` : 요청의 개수
+- `-c` : 동시 접속자
+- `-H` : 헤더 추가 -H 'Authorization: Bearer ey~~' 형태로 전송!
+
+저는 요청은 12000개, 동시 접속자는 100명으로 설정을 해주었고, 서버 자체의 인증때문에 access token을 뽑아서 함께 전송해주었습니다.
+
+> 사실 요청의 개수나 접속자는 각 쿼리마다 동일하게 세팅을 해주는 게 중요하지, 숫자 자체가 크게 유의미한 것 같지는 않아서 적당히 큰 숫자로 설정을 해주었습니다!
+{: .prompt-info }
+
+### 이전 쿼리
+먼저, 이전에 작성한 쿼리의 결과를 확인해보겠습니다.
+
+![img](/assets/img/2024-09-29-test-query-performance/img1.png)
+_이전 쿼리 결과_
+
+- `1초당 처리한 응답의 개수` Requests per second: 1068.08 [#/sec] (mean)
+- `요청 1건당 처리된 시간` Time per request: 93.626 [ms] (mean)
+
+다만 여러번 보낼때마다 처리 시간이 변동되기에 5번 정보 보내보고 평균적인 응답 개수 및 처리 시간은 다음과 같습니다.
+
+- `1초당 처리한 응답의 개수` Requests per second: 1185.2 [#/sec] (mean)
+- `요청 1건당 처리된 시간` Time per request: 89.2736 [ms] (mean)
+
+
+### 현재 쿼리
+그리고 개선된 쿼리의 결과를 확인해보겠습니다.
+
+![img](/assets/img/2024-09-29-test-query-performance/img2.png)
+_개선된 쿼리 결과_
+
+- `1초당 처리한 응답의 개수` Requests per second: 1118.62 [#/sec] (mean)
+- `요청 1건당 처리된 시간` Time per request: 89.396 [ms] (mean)
+
+이 역시도 5번 정도 보낸 뒤 계산된 평균적인 응답 개수 및 처리 시간은 다음과 같습니다.
+
+- `1초당 처리한 응답의 개수` Requests per second: 1047.412 [#/sec] (mean)
+- `요청 1건당 처리된 시간` Time per request: 76.906 [ms] (mean)
+
+## JMeter
+
+Apache JMeter는 서버가 제공하는 성능 및 부하를 측정할 수 있는 테스트 도구입니다.
+
+따로 다운을 받아야 해서 Apache보다는 조금 귀찮았지만, 세팅과정의 UI가 직관적이고, 제공하는 리포트 등을 통해 결과 역시도 상세히 확인할 수 있었기에 굉장히 편했습니다!
+
+### JMeter 세팅
+
+먼저 스레드 그룹을 설정해주었습니다.
+
+![img](/assets/img/2024-09-29-test-query-performance/img3.png)
+
+Number of Threads는 유저 수, Loop Count는 각 유저가 보낼 요청 수를 의미합니다.
+
+- Number of Threads : 100
+- Ramp-up period : 10
+- Loop Count : 100
+
+저는 100명의 유저가 100번의 요청을 10초 동안 보내도록 세팅을 해주었습니다.
+
+
+
+HTTP Request 정보를 다음과 같이 세팅해주었습니다.
+![img](/assets/img/2024-09-29-test-query-performance/img4.png)
+
+- Server Name or IP : localhost
+- Port Number : 8080
+- Http Request : [GET] /api/v2/posts/12
+
+
+
+추가적인 헤더 정보는 Header Manager를 추가해주었습니다.
+![img](/assets/img/2024-09-29-test-query-performance/img5.png)
+
+
+### 기존 쿼리
+
+기존 쿼리로 세팅해준 뒤 생성된 Report를 확인해주면 다음과 같습니다.
+![img](/assets/img/2024-09-29-test-query-performance/img6.png)
+
+- Average : 4
+- Median : 4
+- 90% Line : 5
+- 95% Line : 6
+- 99% Line : 9
+
+### 개선한 쿼리
+
+기존 쿼리로 세팅해준 뒤 생성된 Report를 확인해주면 다음과 같습니다.
+![img](/assets/img/2024-09-29-test-query-performance/img7.png)
+
+- Average : 4
+- Median : 4
+- 90% Line : 5
+- 95% Line : 5
+- 99% Line : 7
+
+개선된 쿼리와 기존 쿼리가 평균 시간이나 중간값에서는 유사함을 보여주었지만, 기존 쿼리에서 95%, 99% Line 값이 높게 나온 것을 확인할 수 있었습니다.
+
+# 결론
+## 1차 결과: 드라마틱한 개선은 없었다!
+
+기존에 통상적으로 알려져있는 `in (서브쿼리)` 보다 `join` 사용이 더 유리하다고 알고 있었는데, 생각보다 극적으로 쿼리가 개선되진 않았습니다. 또한, 툴 각각의 소요 시간 역시도 천차만별이었습니다...!
+
+그 이유 중 하나가 테스트 데이터 생성의 한계라고 생각하는데, 무작위가 아닌 반복문으로 생성된 데이터이다보니 한정적인 케이스로 쌓인 데이터에 대해서만 테스트가 가능했다는 점이 이번 테스팅의 한계점인 것 같습니다.😓 데이터를 "쌓는 것" 자체에만 너무 집중한 것 같네요.
+
+> post에 tag는 2개씩, member는 3명씩 차단하고 있는 상태였습니다. 각 member가 3명 이상의 더 많은 유저들을 차단하고 있었다면, 혹은 아예 차단하고 있지 않았다면 등등의 모든 케이스를 아우르는 무작위 데이터였다면 또 다른 결과를 확인했을 수도 있을 것 같습니다.
+{: .prompt-warning }
+
+# [10/2 내용 추가]
+
+## 2차 테스트를 해봅시다!
+
+> 위 쿼리를 다시 개선한 V2 쿼리를 다시 작성하였습니다! 따라서 기존 쿼리, V1 쿼리, V2 쿼리의 성능 비교를 해보도록 하겠습니다!
+[자세한 내용](https://smwu-pochak.github.io/posts/block-querydsl/#join---on-%EA%B3%BC-where%EC%9D%98-%EC%84%B1%EB%8A%A5-%EC%B0%A8%EC%9D%B4)
+{: .prompt-info }
+
+### 2차 테스팅 방법
+
+먼저, Apache Bench, JMeter를 사용하는 방법도 좋지만! 쿼리만 테스팅하기엔 두 개의 툴 모두 정확히 말하면 쿼리 응답 시간이 아닌 평균 서버 응답 시간을 측정해주는 도구이기에 쿼리 개선 외의 변수가 많다고 생각했습니다.
+테스트를 돌릴 때마다 하나의 쿼리가 일정하게 우세하게 나오는 것이 아닌 비정기적으로 결과가 뒤집어질 때도 있었구요.
+
+따라서, 데이터베이스 자체에서 제공하는 기능을 통해 쿼리 실행시간을 확인하는 방법에 대해 알아보고자 합니다.
+
+### 사전 준비
+
+1차에서는 로컬 H2 데이터베이스를 활용하여 테스트를 진행하였지만, 이번엔 운영 데이터베이스와 동일한 MySQL에 테스트 데이터를 넣어서 진행해주었습니다.
+
+## MySQL Profiling
+
+MySQL 에서는 쿼리가 처리되는 동안 각 단계별 작업에 시간이 얼마나 걸렸는지 확인할 수 있는 기능을 제공하며 이를 `쿼리 프로파일링(Query Profiling)` 기능이라고 합니다.
+
+SQL로 번역된 기존 JPQL 쿼리들을 직접 MySQL에 실행시켜 쿼리 프로파일링 기능을 통해 실행 시간을 확인해보겠습니다.
+
+### Profiling 옵션 확인하기
+
+먼저 쿼리 프로파일링 옵션이 활성화 되어있는지 확인해보겠습니다.
+
+> 쿼리 프로파일링 기능은 기본적으로 비활성화 되어 있습니다.
+{: .prompt-info }
+
+```sql
+SELECT @@profiling;
+```
+
+![img](/assets/img/2024-09-29-test-query-performance/img8.png)
+
+
+### 옵션 활성화
+
+`SET profiling=1;` 명령어를 통해 쿼리 프로파일링 옵션을 활성화하고 확인하면 결과값이 1이 됩니다.
+
+```sql
+-- 옵션 활성화
+SET profiling=1;
+
+-- 활성화 확인
+SELECT @@profiling;
+```
+
+![img](/assets/img/2024-09-29-test-query-performance/img9.png)
+
+### 실행 결과
+
+그 뒤 각각 기존 쿼리, V1 쿼리, V2 쿼리를 실행하고, `show profiles;` 를 통해 쿼리 목록을 확인할 수 있습니다.
+
+```sql
+show profiles;
+```
+
+![img](/assets/img/2024-09-29-test-query-performance/img10.png)
+
+
+`Duration`을 확인하면 기존, V1, V2쿼리 각각 실행시간 역시도 확인할 수 있습니다.
+- 기존 쿼리 : 0.0414810
+- V1 쿼리 : 0.0065200
+- V2 쿼리 : **0.0027180**
+
+## Datagrip
+
+평소에 개발할 땐 Jetbrains사의 DataGrip을 사용하는데, DataGrip에서도 실행 시간을 제공합니다!
+
+### 실행 결과
+
+![img](/assets/img/2024-09-29-test-query-performance/img11.png)
+
+- 기존 쿼리 : 34 ms
+- V1 쿼리 : 24 ms
+- V2 쿼리 : **10 ms**
+
+## 2차 결과
+
+물론 MySQL 프로파일링 기능이나 Datagrip으로 확인한 결과는 여러번 돌리고 확인한 실행시간은 아니기에 정확한 결과는 아니었다고 생각합니다. 하지만 근소하더라도 결과적으로 향상된 결과를 얻을 수 있었고, 앞으로 서버 테스팅에서 사용하면서 더 효율적이고 유의미한 작업을 해낼 수 있도록 사용할 수 있을 것 같아 만족합니다☺️
\ No newline at end of file
diff --git a/_posts/2024-10-01-ubuntu-ufw.md b/_posts/2024-10-01-ubuntu-ufw.md
new file mode 100644
index 0000000..abc0ef3
--- /dev/null
+++ b/_posts/2024-10-01-ubuntu-ufw.md
@@ -0,0 +1,132 @@
+---
+title: Ubuntu UFW 방화벽 확인하기
+date: 2024-10-01 01:30:00 +0900
+categories: [Project, POCHAK]
+tags: [backend, infra, firewall, error] # TAG names should always be lowercase
+---
+
+# 오류 발생 상황
+
+## 수상한 점
+
+포착은 배포를 위해 docker hub를 사용하고 있습니다! private registry에 빌드 후 푸시한 이미지를 각각 개발 서버와 운영 서버에서 pull을 받아와서 사용하고 있는데요!
+
+앱을 이전하고 애플로그인 설정 변경을 위해 서버에 새로운 키 값을 넣어주고, 이를 운영 서버에 반영하고자 하였습니다.
+
+하지만 이상하게도 분명 개발 서버와 운영 서버는 설정 값의 차이일 뿐 거의 동일한 버전의 이미지를 pull 받아서 사용하게 했는데.. 🤷♀️ 운영 서버에서만 jdbc connection error가 발생하였습니다.
+
+> 심지어 docker hub에 올린 이미지를 로컬에서 실행시켰을 때도 에러가 발생하지 않았어요....;;😂
+{: .prompt-danger }
+
+![img](/assets/img/2024-10-01-ubuntu-ufw/img0.png)
+_킹받게 잘돌아가는 개발 서버_
+
+### 오류 메세지
+
+제가 확인했던 오류 메세지입니다.
+
+```
+com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
+
+The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
+ at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:175) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:819) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:440) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:239) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:188) ~[mysql-connector-j-8.1.0.jar!/:8.1.0]
+ ...
+ at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) ~[HikariCP-5.0.1.jar!/:na]
+ at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) ~[spring-jdbc-6.1.2.jar!/:6.1.2]
+ at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) ~[spring-jdbc-6.1.2.jar!/:6.1.2]
+ ...
+Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
+
+```
+
+> `com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure`
+> `Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure`
+{: .prompt-info }
+
+가장 첫 줄과 마지막 줄을 확인해보면 알겠지만 JDBC가 데이터베이스와 연결하지 못하는 이슈가 발생하고 있었습니다.
+
+## 해결의 실마리
+
+로컬, 개발 서버에서는 모두 정상적으로 작동되고 있었기에 불현듯이.. 스쳐지나가는 단어가 있었습니다ㅎㅎ
+
+바로 `방화벽`.. 보통 클라우드 환경이 로컬 환경과 차이가 난다면 92.45%로 방화벽 문제일 확률이 매우 크다고 생각했기에! GCP Cloud SQL의 인바운드 설정에서 문제점을 먼저 찾고자 하였습니다.
+
+# 환경
+
+## 호스트 환경
+- GCP Compute Engine (Ubuntu 20.04.6 LTS)
+- GCP Cloud SQL (MySQL 8.0.31)
+- Docker version 27.1.2
+
+# GCP 방화벽 확인하기
+
+## Cloud SQL 방화벽
+
+먼저 데이터베이스 서버를 설정해둔 Cloud SQL의 방화벽 규칙을 먼저 확인하였습니다.
+
+![img](/assets/img/2024-10-01-ubuntu-ufw/img1.png)
+_왼쪽 "연결" 클릭_
+
+![img](/assets/img/2024-10-01-ubuntu-ufw/img2.png)
+_"네트워킹" 탭 확인_
+
+위 사진처럼 `연결` > `네트워킹` 으로 접속하면 Cloud SQL이 허용하고 있는 IP주소 목록을 확인할 수 있습니다.
+
+하지만 절망적이게도 철저한 제가 Cloud SQL 방화벽 설정은 철저히 해둔 상태였기에.. 다른 방화벽 혹은 compute engine 내부에서 설정한 방화벽을 확인하게 됩니다.
+
+# Ubuntu 방화벽 확인하기
+
+## 개발 서버와 운영 서버의 방화벽 세팅 비교
+UFW는 데비안 계열 및 다양한 리눅스 환경에서 작동되는 사용하기 쉬운 방화벽 관리 프로그램입니다!
+
+운영 서버에서는 포트 관리를 위해 http(80), https(443), ssh(22)번을 열어주었는데, 여기서 제 착각이 있었습니다.
+
+DNS 설정 당시, [이렇게](https://smwu-pochak.github.io/posts/dns-setting/#ubuntu-%EB%B0%A9%ED%99%94%EB%B2%BD-%ED%99%95%EC%9D%B8) 방화벽 설정을 해주었는데, 생각해보니 MySQL 서버 연결을 위해 3306 포트가 필요하다는 생각을 못했던 것입니다.. 🤦♀️🤦♀️
+
+
+
+개발 서버는 따로 방화벽 설정을 해주지 않아 다음과 같이 ufw status를 확인했을 때 inactive로 설정이 되어있었습니다.
+(애초에 방화벽이 없었으니 데이터베이스 서버 접근도 정상적으로 이루어졌던겁니다 .. 이런~)
+
+![img](/assets/img/2024-10-01-ubuntu-ufw/img3.png)
+
+## 방화벽이 문제의 원인!
+
+먼저 방화벽을 inactive하게 설정해주고, 컨테이너가 정상적으로 실행되는지부터 확인해보겠습니다.
+
+```sh
+# 방화벽 비활성화
+sudo ufw disable
+
+# 방화벽 상태 확인
+sudo ufw status
+```
+
+위와 같이 방화벽 비활성화를 실행시키면 개발 서버 처럼 `status: inactive`를 확인할 수 있습니다.
+그리고 다시 도커 컨테이너를 실행시켜주면, 정상적으로 작동되는 모습을 확인할 수 있습니다.
+
+![img](/assets/img/2024-10-01-ubuntu-ufw/img4.png)
+
+## 방화벽 설정하기
+
+일단 이대로 port를 전부 열어두는 것은 분명 편하지만 보안이 굉장히 위험해지기 때문에, 다시 폐쇄적인 정책으로 되돌려 주겠습니다.
+
+원래대로 방화벽을 다시 활성화 해주고, 80, 443, 22, 3306 포트를 열어준 뒤 다시 도커 컨테이너를 실행시켜 주겠습니다.
+
+```sh
+# 방화벽 활성화
+sudo ufw enable
+
+# 방화벽 3306 규칙 추가
+sudo ufw allow 3306
+
+# 방화벽 상태 확인
+sudo ufw status
+```
+
+그 뒤 다시 도커 컨테이너를 실행시켜주면, 이번에는 정상적으로 JDBC 커넥션 연결이 완료되는 모습을 확인할 수 있습니다!
\ No newline at end of file
diff --git a/_posts/2025-01-13-adjust-docker-container-order.md b/_posts/2025-01-13-adjust-docker-container-order.md
index 2f3815b..e5f5260 100644
--- a/_posts/2025-01-13-adjust-docker-container-order.md
+++ b/_posts/2025-01-13-adjust-docker-container-order.md
@@ -1,7 +1,7 @@
---
title: 도커 컨테이너 실행 순서 조정하기
date: 2025-1-13 00:20:00 +/-TTTT
-categories: [Infra, Docker]
+categories: [Project, COASTER]
tags: [docker, infra, troubleshooting]
math: true
---
diff --git a/_posts/2025-01-13-connect-localhost-inside-container.md b/_posts/2025-01-13-connect-localhost-inside-container.md
index 9072f14..4c61fe5 100644
--- a/_posts/2025-01-13-connect-localhost-inside-container.md
+++ b/_posts/2025-01-13-connect-localhost-inside-container.md
@@ -1,7 +1,7 @@
---
title: 도커 컨테이너 내부에서 localhost에 접근하기
date: 2025-1-13 02:51:00 +/-TTTT
-categories: [Infra, Docker]
+categories: [Project, COASTER]
tags: [docker, infra, springboot, troubleshooting]
math: true
---
diff --git a/assets/img/2024-08-25-cloud-sql-migration/1.png b/assets/img/2024-08-25-cloud-sql-migration/1.png
new file mode 100644
index 0000000..e6a9a0b
Binary files /dev/null and b/assets/img/2024-08-25-cloud-sql-migration/1.png differ
diff --git a/assets/img/2024-08-25-cloud-sql-migration/2.png b/assets/img/2024-08-25-cloud-sql-migration/2.png
new file mode 100644
index 0000000..2be1d4d
Binary files /dev/null and b/assets/img/2024-08-25-cloud-sql-migration/2.png differ
diff --git a/assets/img/2024-08-25-cloud-sql-migration/3.png b/assets/img/2024-08-25-cloud-sql-migration/3.png
new file mode 100644
index 0000000..5af9a53
Binary files /dev/null and b/assets/img/2024-08-25-cloud-sql-migration/3.png differ
diff --git a/assets/img/2024-08-25-cloud-sql-migration/4.png b/assets/img/2024-08-25-cloud-sql-migration/4.png
new file mode 100644
index 0000000..3093c4e
Binary files /dev/null and b/assets/img/2024-08-25-cloud-sql-migration/4.png differ
diff --git a/assets/img/2024-08-25-cloud-sql-migration/5.png b/assets/img/2024-08-25-cloud-sql-migration/5.png
new file mode 100644
index 0000000..ffbfb1d
Binary files /dev/null and b/assets/img/2024-08-25-cloud-sql-migration/5.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/1-error-img.png b/assets/img/2024-08-28-docker.sock-permission-error/1-error-img.png
new file mode 100644
index 0000000..fcf7754
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/1-error-img.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/2-check-permission.png b/assets/img/2024-08-28-docker.sock-permission-error/2-check-permission.png
new file mode 100644
index 0000000..132c559
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/2-check-permission.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/3-group-setting.png b/assets/img/2024-08-28-docker.sock-permission-error/3-group-setting.png
new file mode 100644
index 0000000..cabdd1d
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/3-group-setting.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/4-permission.png b/assets/img/2024-08-28-docker.sock-permission-error/4-permission.png
new file mode 100644
index 0000000..af61075
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/4-permission.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/5-group.png b/assets/img/2024-08-28-docker.sock-permission-error/5-group.png
new file mode 100644
index 0000000..0782d50
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/5-group.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/6-id.png b/assets/img/2024-08-28-docker.sock-permission-error/6-id.png
new file mode 100644
index 0000000..cb77e4b
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/6-id.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/7-terminal.png b/assets/img/2024-08-28-docker.sock-permission-error/7-terminal.png
new file mode 100644
index 0000000..c766908
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/7-terminal.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/8-id.png b/assets/img/2024-08-28-docker.sock-permission-error/8-id.png
new file mode 100644
index 0000000..68a427b
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/8-id.png differ
diff --git a/assets/img/2024-08-28-docker.sock-permission-error/9-success.png b/assets/img/2024-08-28-docker.sock-permission-error/9-success.png
new file mode 100644
index 0000000..b651126
Binary files /dev/null and b/assets/img/2024-08-28-docker.sock-permission-error/9-success.png differ
diff --git a/assets/img/2024-08-31-dns-setting/1-img.png b/assets/img/2024-08-31-dns-setting/1-img.png
new file mode 100644
index 0000000..96b627a
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/1-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/10-img.png b/assets/img/2024-08-31-dns-setting/10-img.png
new file mode 100644
index 0000000..26297f9
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/10-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/11-img.png b/assets/img/2024-08-31-dns-setting/11-img.png
new file mode 100644
index 0000000..5fee979
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/11-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/2-img.png b/assets/img/2024-08-31-dns-setting/2-img.png
new file mode 100644
index 0000000..1d6533b
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/2-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/3-img.png b/assets/img/2024-08-31-dns-setting/3-img.png
new file mode 100644
index 0000000..e8e61ed
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/3-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/4-img.png b/assets/img/2024-08-31-dns-setting/4-img.png
new file mode 100644
index 0000000..b87f20b
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/4-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/5-img.png b/assets/img/2024-08-31-dns-setting/5-img.png
new file mode 100644
index 0000000..7abbc80
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/5-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/6-img.png b/assets/img/2024-08-31-dns-setting/6-img.png
new file mode 100644
index 0000000..deb9bc3
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/6-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/7-img.png b/assets/img/2024-08-31-dns-setting/7-img.png
new file mode 100644
index 0000000..6da617b
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/7-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/8-img.png b/assets/img/2024-08-31-dns-setting/8-img.png
new file mode 100644
index 0000000..59a3c10
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/8-img.png differ
diff --git a/assets/img/2024-08-31-dns-setting/9-img.png b/assets/img/2024-08-31-dns-setting/9-img.png
new file mode 100644
index 0000000..2de2c6f
Binary files /dev/null and b/assets/img/2024-08-31-dns-setting/9-img.png differ
diff --git a/assets/img/2024-09-28-block-querydsl/img0.jpeg b/assets/img/2024-09-28-block-querydsl/img0.jpeg
new file mode 100644
index 0000000..1a63824
Binary files /dev/null and b/assets/img/2024-09-28-block-querydsl/img0.jpeg differ
diff --git a/assets/img/2024-09-28-block-querydsl/img1.jpeg b/assets/img/2024-09-28-block-querydsl/img1.jpeg
new file mode 100644
index 0000000..30e79ef
Binary files /dev/null and b/assets/img/2024-09-28-block-querydsl/img1.jpeg differ
diff --git a/assets/img/2024-09-28-block-querydsl/img2.jpeg b/assets/img/2024-09-28-block-querydsl/img2.jpeg
new file mode 100644
index 0000000..ede2657
Binary files /dev/null and b/assets/img/2024-09-28-block-querydsl/img2.jpeg differ
diff --git a/assets/img/2024-09-29-test-query-performance/img0.png b/assets/img/2024-09-29-test-query-performance/img0.png
new file mode 100644
index 0000000..92cbc3e
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img0.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img1.png b/assets/img/2024-09-29-test-query-performance/img1.png
new file mode 100644
index 0000000..a05f41d
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img1.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img10.png b/assets/img/2024-09-29-test-query-performance/img10.png
new file mode 100644
index 0000000..64d933e
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img10.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img11.png b/assets/img/2024-09-29-test-query-performance/img11.png
new file mode 100644
index 0000000..bc212af
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img11.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img2.png b/assets/img/2024-09-29-test-query-performance/img2.png
new file mode 100644
index 0000000..d0c0e4b
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img2.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img3.png b/assets/img/2024-09-29-test-query-performance/img3.png
new file mode 100644
index 0000000..bfcdffc
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img3.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img4.png b/assets/img/2024-09-29-test-query-performance/img4.png
new file mode 100644
index 0000000..71015a4
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img4.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img5.png b/assets/img/2024-09-29-test-query-performance/img5.png
new file mode 100644
index 0000000..8b5f5cf
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img5.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img6.png b/assets/img/2024-09-29-test-query-performance/img6.png
new file mode 100644
index 0000000..8d36b52
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img6.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img7.png b/assets/img/2024-09-29-test-query-performance/img7.png
new file mode 100644
index 0000000..2647cf6
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img7.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img8.png b/assets/img/2024-09-29-test-query-performance/img8.png
new file mode 100644
index 0000000..755e1d0
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img8.png differ
diff --git a/assets/img/2024-09-29-test-query-performance/img9.png b/assets/img/2024-09-29-test-query-performance/img9.png
new file mode 100644
index 0000000..82354a7
Binary files /dev/null and b/assets/img/2024-09-29-test-query-performance/img9.png differ
diff --git a/assets/img/2024-10-01-ubuntu-ufw/img0.png b/assets/img/2024-10-01-ubuntu-ufw/img0.png
new file mode 100644
index 0000000..206a8d4
Binary files /dev/null and b/assets/img/2024-10-01-ubuntu-ufw/img0.png differ
diff --git a/assets/img/2024-10-01-ubuntu-ufw/img1.png b/assets/img/2024-10-01-ubuntu-ufw/img1.png
new file mode 100644
index 0000000..ae967ee
Binary files /dev/null and b/assets/img/2024-10-01-ubuntu-ufw/img1.png differ
diff --git a/assets/img/2024-10-01-ubuntu-ufw/img2.png b/assets/img/2024-10-01-ubuntu-ufw/img2.png
new file mode 100644
index 0000000..bb14a32
Binary files /dev/null and b/assets/img/2024-10-01-ubuntu-ufw/img2.png differ
diff --git a/assets/img/2024-10-01-ubuntu-ufw/img3.png b/assets/img/2024-10-01-ubuntu-ufw/img3.png
new file mode 100644
index 0000000..eb0278d
Binary files /dev/null and b/assets/img/2024-10-01-ubuntu-ufw/img3.png differ
diff --git a/assets/img/2024-10-01-ubuntu-ufw/img4.png b/assets/img/2024-10-01-ubuntu-ufw/img4.png
new file mode 100644
index 0000000..e557e9d
Binary files /dev/null and b/assets/img/2024-10-01-ubuntu-ufw/img4.png differ