From db5491ae0f52268c89493365be1e38184a411828 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 21:46:00 +0900
Subject: [PATCH 01/47] =?UTF-8?q?refactor(Slack):=20=EC=A0=A0=ED=82=A8?=
 =?UTF-8?q?=EC=8A=A4=20=EB=B9=8C=EB=93=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?=
 =?UTF-8?q?=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 84 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 82 insertions(+), 2 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index f0ce1ac78..1cd3f349e 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -134,13 +134,13 @@ pipeline {
     post {
         failure {
             script {
-                sendSlackNotification("${env.GIT_CHANGELOG}\n:scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE)
+                sendSlackNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE, true)
             }
         }
 
         success {
             script {
-                sendSlackNotification("${env.GIT_CHANGELOG}\n:rocket: Deployment completed successfully.", env.SLACK_COLOR_SUCCESS)
+                sendSlackNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS, false)
             }
         }
     }
@@ -165,6 +165,77 @@ def sendSlackNotification(message, color) {
     }
 }
 
+def sendSlackNotification(String message, String color, boolean isFailure = false) {
+    def jobUrl = "${env.BUILD_URL}"
+    def consoleOutputUrl = "${jobUrl}console"
+    def buildLog = isFailure ? getBuildLog(10) : ""
+
+    def payload = [
+        blocks: [
+            [
+                type: "section",
+                text: [
+                    type: "mrkdwn",
+                    text: message
+                ]
+            ]
+        ],
+        attachments: [
+            [
+                color: color,
+                blocks: [
+                    [
+                        type: "section",
+                        text: [
+                            type: "mrkdwn",
+                            text: "*Change Log:*\n${env.GIT_CHANGELOG}"
+                        ]
+                    ],
+                    isFailure ? [
+                        type: "section",
+                        text: [
+                            type: "mrkdwn",
+                            text: "*Reason:*\n```${buildLog}```"
+                        ]
+                    ] : null,
+                    [
+                        type: "actions",
+                        elements: [
+                            [
+                                type: "button",
+                                text: [
+                                    type: "plain_text",
+                                    text: "Job",
+                                    emoji: true
+                                ],
+                                url: jobUrl,
+                                value: "click_job"
+                            ],
+                            [
+                                type: "button",
+                                text: [
+                                    type: "plain_text",
+                                    text: "Console Output",
+                                    emoji: true
+                                ],
+                                url: consoleOutputUrl,
+                                value: "click_console_output"
+                            ]
+                        ]
+                    ]
+                ].findAll { it != null }
+            ]
+        ]
+    ]
+
+    withEnv(["SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"]) {
+        def payloadJson = groovy.json.JsonOutput.toJson(payload)
+        sh """
+            curl -X POST -H 'Content-type: application/json' --data '${payloadJson}' ${SLACK_WEBHOOK_URL}
+        """
+    }
+}
+
 def getChangeLog() {
     def previousCommit = env.GIT_PREVIOUS_SUCCESSFUL_COMMIT ?: 'HEAD~1'
     def currentCommit = env.GIT_COMMIT ?: 'HEAD'
@@ -182,6 +253,15 @@ def getChangeLog() {
     return changeLog
 }
 
+def getBuildLog(lines) {
+    def log = sh(
+        script: "tail -n ${lines} ${env.WORKSPACE}/target/build.log",
+        returnStdout: true
+    ).trim()
+
+    return log.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')
+}
+
 def backupPostgres() {
     def BACKUP_FILE = "postgres_backup_${new Date().format('yyyy-MM-dd_HH-mm-ss')}.sql"
     withEnv([

From a815832b99197259aa728b2609eff6c4cfd0e9f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 21:51:50 +0900
Subject: [PATCH 02/47] =?UTF-8?q?fix(Slack):=20=EC=8A=AC=EB=9E=99=20?=
 =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=A0=84=EC=86=A1=20=EB=A9=94?=
 =?UTF-8?q?=EC=86=8C=EB=93=9C=EB=AA=85=20=EC=A4=91=EB=B3=B5=EC=97=90=20?=
 =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 1cd3f349e..54b273b14 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -134,13 +134,13 @@ pipeline {
     post {
         failure {
             script {
-                sendSlackNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE, true)
+                sendSlackBuildNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE, true)
             }
         }
 
         success {
             script {
-                sendSlackNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS, false)
+                sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS, false)
             }
         }
     }
@@ -165,7 +165,7 @@ def sendSlackNotification(message, color) {
     }
 }
 
-def sendSlackNotification(String message, String color, boolean isFailure = false) {
+def sendSlackBuildNotification(String message, String color, boolean isFailure = false) {
     def jobUrl = "${env.BUILD_URL}"
     def consoleOutputUrl = "${jobUrl}console"
     def buildLog = isFailure ? getBuildLog(10) : ""

From 7547ef1714b7b76bb6db347b424cfe3706cc9c42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 22:00:19 +0900
Subject: [PATCH 03/47] =?UTF-8?q?fix(Slack):=20=EC=8A=AC=EB=9E=99=20?=
 =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=A0=84=EC=86=A1=20=EB=A9=94?=
 =?UTF-8?q?=EC=86=8C=EB=93=9C=EB=AA=85=20=EC=A4=91=EB=B3=B5=20=EB=B0=8F=20?=
 =?UTF-8?q?base=20url=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=98=A4=EB=A5=98?=
 =?UTF-8?q?=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 54b273b14..4e9ad44ed 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -2,6 +2,8 @@ pipeline {
     agent any
 
     environment {
+        JENKINS_DOMAIN = credentials('jenkins_domain')
+
         SLACK_WEBHOOK_URL = credentials('slack_webhook_url')
         SLACK_COLOR_SUCCESS = credentials('slack_color_success')
         SLACK_COLOR_FAILURE = credentials('slack_color_failure')
@@ -166,8 +168,8 @@ def sendSlackNotification(message, color) {
 }
 
 def sendSlackBuildNotification(String message, String color, boolean isFailure = false) {
-    def jobUrl = "${env.BUILD_URL}"
-    def consoleOutputUrl = "${jobUrl}console"
+    def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}"
+    def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console"
     def buildLog = isFailure ? getBuildLog(10) : ""
 
     def payload = [

From 38d7d2139bfe05d719686fb72104e49ad79bcb10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 22:13:09 +0900
Subject: [PATCH 04/47] =?UTF-8?q?refactor(Jenkins):=20=ED=97=AC=EC=8A=A4?=
 =?UTF-8?q?=20=EC=B2=B4=ED=81=AC=20=EC=8B=9C=EA=B0=84=204=EB=B6=84->2.5?=
 =?UTF-8?q?=EB=B6=84=20=EC=B6=95=EC=86=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 4e9ad44ed..585256fc9 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -401,7 +401,7 @@ def performHealthCheck() {
         echo "Public IP address: ${PUBLIC_IP}"
 
         def start_time = System.currentTimeMillis()
-        def timeout = start_time + 240000  // 4 minutes
+        def timeout = start_time + 150000  // 2.5 minutes
 
         while (System.currentTimeMillis() < timeout) {
             def elapsed = (System.currentTimeMillis() - start_time) / 1000

From 97be3b239f4cc4798bbefb1cf7c26c4f5df4d3e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 23:00:26 +0900
Subject: [PATCH 05/47] =?UTF-8?q?refactor(Slack):=20=EC=A0=A0=ED=82=A8?=
 =?UTF-8?q?=EC=8A=A4=20=EB=B9=8C=EB=93=9C=20=EB=A1=9C=EA=B7=B8=20=EB=8C=80?=
 =?UTF-8?q?=EC=8B=A0=20=EC=8B=A4=ED=8C=A8=20=EB=8B=A8=EA=B3=84=EB=A5=BC=20?=
 =?UTF-8?q?=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?=
 =?UTF-8?q?=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 585256fc9..3846792f1 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -197,7 +197,7 @@ def sendSlackBuildNotification(String message, String color, boolean isFailure =
                         type: "section",
                         text: [
                             type: "mrkdwn",
-                            text: "*Reason:*\n```${buildLog}```"
+                            text: "*Failed at stage:*\n```${env.CURRENT_STAGE}```"
                         ]
                     ] : null,
                     [
@@ -255,15 +255,6 @@ def getChangeLog() {
     return changeLog
 }
 
-def getBuildLog(lines) {
-    def log = sh(
-        script: "tail -n ${lines} ${env.WORKSPACE}/target/build.log",
-        returnStdout: true
-    ).trim()
-
-    return log.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')
-}
-
 def backupPostgres() {
     def BACKUP_FILE = "postgres_backup_${new Date().format('yyyy-MM-dd_HH-mm-ss')}.sql"
     withEnv([

From 7689d12be395c8477d96c411d67ddb9351bc851a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 23:23:50 +0900
Subject: [PATCH 06/47] =?UTF-8?q?refactor(Slack):=20=EC=A0=A0=ED=82=A8?=
 =?UTF-8?q?=EC=8A=A4=20=EB=B9=8C=EB=93=9C=20=EB=A1=9C=EA=B7=B8=20=EB=8C=80?=
 =?UTF-8?q?=EC=8B=A0=20=EC=8B=A4=ED=8C=A8=20=EB=8B=A8=EA=B3=84=EB=A5=BC=20?=
 =?UTF-8?q?=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?=
 =?UTF-8?q?=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 3846792f1..82057d28a 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -136,13 +136,13 @@ pipeline {
     post {
         failure {
             script {
-                sendSlackBuildNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE, true)
+                sendSlackBuildNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE)
             }
         }
 
         success {
             script {
-                sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS, false)
+                sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS)
             }
         }
     }
@@ -167,10 +167,9 @@ def sendSlackNotification(message, color) {
     }
 }
 
-def sendSlackBuildNotification(String message, String color, boolean isFailure = false) {
+def sendSlackBuildNotification(String message, String color) {
     def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}"
     def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console"
-    def buildLog = isFailure ? getBuildLog(10) : ""
 
     def payload = [
         blocks: [
@@ -193,13 +192,6 @@ def sendSlackBuildNotification(String message, String color, boolean isFailure =
                             text: "*Change Log:*\n${env.GIT_CHANGELOG}"
                         ]
                     ],
-                    isFailure ? [
-                        type: "section",
-                        text: [
-                            type: "mrkdwn",
-                            text: "*Failed at stage:*\n```${env.CURRENT_STAGE}```"
-                        ]
-                    ] : null,
                     [
                         type: "actions",
                         elements: [

From 75535d5b0fd342a851638fdbd1e80541ed553d6f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 19 Jun 2024 23:44:50 +0900
Subject: [PATCH 07/47] =?UTF-8?q?refactor(Slack):=20=ED=8E=98=EC=9D=B4?=
 =?UTF-8?q?=EB=A1=9C=EB=93=9C=EB=A5=BC=20JSON=20=EA=B0=9D=EC=B2=B4?=
 =?UTF-8?q?=EB=A1=9C=20=EC=A0=95=EC=9D=98=ED=95=98=EC=97=AC=20=EC=89=BD?=
 =?UTF-8?q?=EA=B2=8C=20=EB=B3=80=EA=B2=BD=20=EA=B0=80=EB=8A=A5=ED=95=98?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=95=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 82057d28a..ffe491337 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -148,25 +148,25 @@ pipeline {
     }
 }
 
-def sendSlackNotification(message, color) {
-    withEnv([
-        "SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"
-    ]) {
-        def payload = """{
-            "attachments": [
-                {
-                    "color": "${color}",
-                    "text": "${message.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')}"
-                }
+def sendSlackNotification(String message, String color) {
+    def payload = [
+        attachments: [
+            [
+                color: color,
+                text: message.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')
             ]
-        }"""
+        ]
+    ]
 
+    withEnv(["SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"]) {
+        def payloadJson = groovy.json.JsonOutput.toJson(payload)
         sh """
-            curl -X POST --data-urlencode 'payload=${payload}' ${SLACK_WEBHOOK_URL}
+            curl -X POST -H 'Content-type: application/json' --data '${payloadJson}' ${SLACK_WEBHOOK_URL}
         """
     }
 }
 
+
 def sendSlackBuildNotification(String message, String color) {
     def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}"
     def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console"

From 2288a52c2e1bf351054f6b99fac8077cc925c5e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Thu, 20 Jun 2024 00:55:22 +0900
Subject: [PATCH 08/47] =?UTF-8?q?fix(Jenkins):=20Jenkinsfile=EC=97=90=20?=
 =?UTF-8?q?=ED=8A=B8=EB=A6=AC=EA=B1=B0=EB=A5=BC=20=EC=B6=94=EA=B0=80?=
 =?UTF-8?q?=ED=95=98=EC=97=AC=20Github=20Webhook=EC=9D=B4=20=EC=A0=95?=
 =?UTF-8?q?=EC=83=81=20=EC=9E=91=EB=8F=99=ED=95=98=EB=8F=84=EB=A1=9D=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index ffe491337..eaee06c49 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -1,6 +1,10 @@
 pipeline {
     agent any
 
+    triggers {
+        githubPush()
+    }
+
     environment {
         JENKINS_DOMAIN = credentials('jenkins_domain')
 

From 05be4564450a04dc11b313b4d8fc836bb2a7e31f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Thu, 20 Jun 2024 01:02:05 +0900
Subject: [PATCH 09/47] Webhook test

---
 jenkins/Jenkinsfile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index eaee06c49..5ef0b7782 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -5,6 +5,7 @@ pipeline {
         githubPush()
     }
 
+
     environment {
         JENKINS_DOMAIN = credentials('jenkins_domain')
 

From dc0be0fa3e5d66734791b893c2f4e71fd3c6f649 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Thu, 20 Jun 2024 01:13:06 +0900
Subject: [PATCH 10/47] =?UTF-8?q?=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 jenkins/Jenkinsfile | 1 -
 1 file changed, 1 deletion(-)

diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile
index 5ef0b7782..eaee06c49 100644
--- a/jenkins/Jenkinsfile
+++ b/jenkins/Jenkinsfile
@@ -5,7 +5,6 @@ pipeline {
         githubPush()
     }
 
-
     environment {
         JENKINS_DOMAIN = credentials('jenkins_domain')
 

From 8a3f65bc018a2e5180e0c3a951a6238082c1758d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?=
 <85067003+limehee@users.noreply.github.com>
Date: Fri, 21 Jun 2024 16:18:22 +0900
Subject: [PATCH 11/47] =?UTF-8?q?=EC=8A=AC=EB=9E=99=20=EC=95=8C=EB=A6=BC?=
 =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?=
 =?UTF-8?q?=EC=9C=A0=EB=8F=99=EC=A0=81=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?=
 =?UTF-8?q?=EC=A0=95=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?=
 =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=99=84=EB=A3=8C=20(#386)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(Slack): 슬랙 알림을 발행-구독 패턴으로 변경 및 알림을 On/Off 가능한 구조로 변경

* feat(Slack): 슬랙 알림 설정 변경 API 추가

* feat(Slack): 슬랙 알림 조회 API 추가

* refactor(Slack): AlertType 예외 수정

* refactor(Slack): 코드 컨벤션 수정

* feat(Slack): 새 게시글 알림 추가

* refactor(Slack): 새 게시글 알림 이모지 변경
---
 .../application/ApplicationService.java       |   3 +-
 .../board/application/BoardService.java       |   4 +
 .../clab/api/domain/board/domain/Board.java   |   1 +
 .../api/NotificationSettingController.java    |  46 +++
 .../NotificationSettingService.java           |  48 +++
 .../slack/application/SlackService.java       | 254 ++-------------
 .../slack/application/SlackServiceHelper.java | 293 ++++++++++++++++++
 .../dao/NotificationSettingRepository.java    |  13 +
 .../global/common/slack/domain/AlertType.java |   9 +
 .../slack/domain/AlertTypeConverter.java      |  43 +++
 .../slack/domain/AlertTypeResolver.java       |  23 ++
 .../common/slack/domain/GeneralAlertType.java |  19 ++
 .../slack/domain/NotificationSetting.java     |  43 +++
 .../slack/domain/SecurityAlertType.java       |   2 +-
 .../NotificationSettingUpdateRequestDto.java  |  20 ++
 .../NotificationSettingResponseDto.java       |  22 ++
 .../common/slack/event/NotificationEvent.java |  24 ++
 .../exception/AlertTypeNotFoundException.java |   9 +
 .../slack/listener/NotificationListener.java  |  30 ++
 src/main/resources/messages.properties        |   2 +
 20 files changed, 669 insertions(+), 239 deletions(-)
 create mode 100644 src/main/java/page/clab/api/global/common/slack/api/NotificationSettingController.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/application/NotificationSettingService.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/dao/NotificationSettingRepository.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/domain/AlertType.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/domain/AlertTypeConverter.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/domain/AlertTypeResolver.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/domain/GeneralAlertType.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/domain/NotificationSetting.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/dto/request/NotificationSettingUpdateRequestDto.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/dto/response/NotificationSettingResponseDto.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/event/NotificationEvent.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/exception/AlertTypeNotFoundException.java
 create mode 100644 src/main/java/page/clab/api/global/common/slack/listener/NotificationListener.java

diff --git a/src/main/java/page/clab/api/domain/application/application/ApplicationService.java b/src/main/java/page/clab/api/domain/application/application/ApplicationService.java
index 81fa9b55d..9fbabd6e2 100644
--- a/src/main/java/page/clab/api/domain/application/application/ApplicationService.java
+++ b/src/main/java/page/clab/api/domain/application/application/ApplicationService.java
@@ -1,6 +1,5 @@
 package page.clab.api.domain.application.application;
 
-import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.data.domain.Page;
@@ -44,7 +43,7 @@ public String createApplication(ApplicationRequestDto requestDto) {
 
         notificationService.sendNotificationToAdmins(requestDto.getStudentId() + " " +
                 requestDto.getName() + "님이 동아리에 지원하였습니다.");
-        slackService.sendApplicationNotification(requestDto);
+        slackService.sendNewApplicationNotification(requestDto);
         return applicationRepository.save(application).getStudentId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardService.java b/src/main/java/page/clab/api/domain/board/application/BoardService.java
index 5462632f4..4c9427fda 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardService.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardService.java
@@ -24,6 +24,7 @@
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.file.application.UploadedFileService;
 import page.clab.api.global.common.file.domain.UploadedFile;
+import page.clab.api.global.common.slack.application.SlackService;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
 import page.clab.api.global.validation.ValidationService;
@@ -43,6 +44,8 @@ public class BoardService {
 
     private final ValidationService validationService;
 
+    private final SlackService slackService;
+
     private final BoardRepository boardRepository;
 
     private final BoardLikeRepository boardLikeRepository;
@@ -59,6 +62,7 @@ public String createBoard(BoardRequestDto requestDto) throws PermissionDeniedExc
         if (board.shouldNotifyForNewBoard()) {
             notificationService.sendNotificationToMember(currentMember, "[" + board.getTitle() + "] 새로운 공지사항이 등록되었습니다.");
         }
+        slackService.sendNewBoardNotification(board);
         return boardRepository.save(board).getCategory().getKey();
     }
 
diff --git a/src/main/java/page/clab/api/domain/board/domain/Board.java b/src/main/java/page/clab/api/domain/board/domain/Board.java
index 99847c588..44db8437b 100644
--- a/src/main/java/page/clab/api/domain/board/domain/Board.java
+++ b/src/main/java/page/clab/api/domain/board/domain/Board.java
@@ -69,6 +69,7 @@ public class Board extends BaseEntity {
 
     private String imageUrl;
 
+    @Getter
     @Column(nullable = false)
     private boolean wantAnonymous;
 
diff --git a/src/main/java/page/clab/api/global/common/slack/api/NotificationSettingController.java b/src/main/java/page/clab/api/global/common/slack/api/NotificationSettingController.java
new file mode 100644
index 000000000..e9757bb3d
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/api/NotificationSettingController.java
@@ -0,0 +1,46 @@
+package page.clab.api.global.common.slack.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import page.clab.api.global.common.dto.ApiResponse;
+import page.clab.api.global.common.slack.application.NotificationSettingService;
+import page.clab.api.global.common.slack.dto.request.NotificationSettingUpdateRequestDto;
+import page.clab.api.global.common.slack.dto.response.NotificationSettingResponseDto;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/notification-settings")
+@RequiredArgsConstructor
+@Tag(name = "Notification Setting", description = "알림 설정")
+public class NotificationSettingController {
+
+    private final NotificationSettingService notificationSettingService;
+
+    @Operation(summary = "[S] 슬랙 알림 조회", description = "ROLE_SUPER 이상의 권한이 필요함")
+    @Secured({"ROLE_SUPER"})
+    @GetMapping("")
+    public ApiResponse<Object> getNotificationSettings() {
+        List<NotificationSettingResponseDto> notificationSettings = notificationSettingService.getNotificationSettings();
+        return ApiResponse.success(notificationSettings);
+    }
+
+    @Operation(summary = "[S] 슬랙 알림 설정 변경", description = "ROLE_SUPER 이상의 권한이 필요함")
+    @Secured({"ROLE_SUPER"})
+    @PutMapping("")
+    public ApiResponse<Object> updateNotificationSetting(
+            @Valid @RequestBody NotificationSettingUpdateRequestDto requestDto
+    ) {
+        notificationSettingService.updateNotificationSetting(requestDto.getAlertType(), requestDto.isEnabled());
+        return ApiResponse.success();
+    }
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/application/NotificationSettingService.java b/src/main/java/page/clab/api/global/common/slack/application/NotificationSettingService.java
new file mode 100644
index 000000000..16aad7c4d
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/application/NotificationSettingService.java
@@ -0,0 +1,48 @@
+package page.clab.api.global.common.slack.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.global.common.slack.dao.NotificationSettingRepository;
+import page.clab.api.global.common.slack.domain.AlertType;
+import page.clab.api.global.common.slack.domain.AlertTypeResolver;
+import page.clab.api.global.common.slack.domain.NotificationSetting;
+import page.clab.api.global.common.slack.dto.response.NotificationSettingResponseDto;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class NotificationSettingService {
+
+    private final AlertTypeResolver alertTypeResolver;
+
+    private final NotificationSettingRepository settingRepository;
+
+    @Transactional(readOnly = true)
+    public List<NotificationSettingResponseDto> getNotificationSettings() {
+        return settingRepository.findAll().stream()
+                .map(NotificationSettingResponseDto::toDto)
+                .toList();
+    }
+
+    @Transactional
+    public void updateNotificationSetting(String alertTypeName, boolean enabled) {
+        AlertType alertType = alertTypeResolver.resolve(alertTypeName);
+        NotificationSetting setting = getOrCreateDefaultSetting(alertType);
+        setting.updateEnabled(enabled);
+        settingRepository.save(setting);
+    }
+
+    @Transactional
+    public NotificationSetting getOrCreateDefaultSetting(AlertType alertType) {
+        return settingRepository.findByAlertType(alertType)
+                .orElseGet(() -> createAndSaveDefaultSetting(alertType));
+    }
+
+    private NotificationSetting createAndSaveDefaultSetting(AlertType alertType) {
+        NotificationSetting defaultSetting = NotificationSetting.createDefault(alertType);
+        return settingRepository.save(defaultSetting);
+    }
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
index 48c76eeb3..8963b6f54 100644
--- a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
@@ -1,267 +1,49 @@
 package page.clab.api.global.common.slack.application;
 
-import com.slack.api.Slack;
-import com.slack.api.model.Attachment;
-import static com.slack.api.model.block.Blocks.actions;
-import static com.slack.api.model.block.Blocks.section;
-import com.slack.api.model.block.LayoutBlock;
-import static com.slack.api.model.block.composition.BlockCompositions.markdownText;
-import static com.slack.api.model.block.composition.BlockCompositions.plainText;
-import static com.slack.api.model.block.element.BlockElements.asElements;
-import static com.slack.api.model.block.element.BlockElements.button;
-import com.slack.api.webhook.Payload;
-import com.slack.api.webhook.WebhookResponse;
-import io.ipinfo.api.model.IPResponse;
-import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
 import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.jetbrains.annotations.NotNull;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.context.event.EventListener;
-import org.springframework.core.env.Environment;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Service;
 import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
+import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.member.domain.Member;
+import page.clab.api.global.common.slack.domain.GeneralAlertType;
 import page.clab.api.global.common.slack.domain.SecurityAlertType;
-import page.clab.api.global.config.SlackConfig;
-import page.clab.api.global.util.HttpReqResUtil;
+import page.clab.api.global.common.slack.event.NotificationEvent;
 
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryMXBean;
-import java.lang.management.MemoryUsage;
-import java.lang.management.OperatingSystemMXBean;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
-
-@Service
 @Slf4j
+@Service
+@RequiredArgsConstructor
 public class SlackService {
 
-    private final Slack slack;
-
-    private final String webhookUrl;
-
-    private final String webUrl;
-
-    private final String apiUrl;
-
-    private final String color;
-
-    private final Environment environment;
-
-    private final AttributeStrategy attributeStrategy;
-
-    public SlackService(SlackConfig slackConfig, Environment environment, AttributeStrategy attributeStrategy) {
-        this.slack = slackConfig.slack();
-        this.webhookUrl = slackConfig.getWebhookUrl();
-        this.webUrl = slackConfig.getWebUrl();
-        this.apiUrl = slackConfig.getApiUrl();
-        this.color = slackConfig.getColor();
-        this.environment = environment;
-        this.attributeStrategy = attributeStrategy;
-    }
+    private final ApplicationEventPublisher eventPublisher;
 
     public void sendServerErrorNotification(HttpServletRequest request, Exception e) {
-        List<LayoutBlock> blocks = createErrorBlocks(request, e);
-        sendSlackMessageWithBlocks(blocks);
+        eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.SERVER_ERROR, request, e));
     }
 
     public void sendSecurityAlertNotification(HttpServletRequest request, SecurityAlertType alertType, String additionalMessage) {
-        List<LayoutBlock> blocks = createSecurityAlertBlocks(request, alertType, additionalMessage);
-        sendSlackMessageWithBlocks(blocks);
+        eventPublisher.publishEvent(new NotificationEvent(this, alertType, request, additionalMessage));
     }
 
     public void sendAdminLoginNotification(HttpServletRequest request, Member loginMember) {
-        List<LayoutBlock> blocks = createAdminLoginBlocks(request, loginMember);
-        sendSlackMessageWithBlocks(blocks);
+        eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.ADMIN_LOGIN, request, loginMember));
     }
 
-    public void sendApplicationNotification(ApplicationRequestDto applicationRequestDto) {
-        List<LayoutBlock> blocks = createApplicationBlocks(applicationRequestDto);
-        sendSlackMessageWithBlocks(blocks);
+    public void sendNewApplicationNotification(ApplicationRequestDto applicationRequestDto) {
+        eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.APPLICATION_CREATED, null, applicationRequestDto));
     }
 
-    @EventListener(ContextRefreshedEvent.class)
-    public void sendServerStartNotification() {
-        List<LayoutBlock> blocks = createServerStartBlocks();
-        sendSlackMessageWithBlocks(blocks);
-    }
-
-    private CompletableFuture<Boolean> sendSlackMessageWithBlocks(List<LayoutBlock> blocks) {
-        return CompletableFuture.supplyAsync(() -> {
-            Payload payload = Payload.builder()
-                    .blocks(List.of(blocks.getFirst()))
-                    .attachments(Collections.singletonList(
-                            Attachment.builder()
-                                    .color(color)
-                                    .blocks(blocks.subList(1, blocks.size()))
-                                    .build()
-                    )).build();
-            try {
-                WebhookResponse response = slack.send(webhookUrl, payload);
-                if (response.getCode() == 200) {
-                    return true;
-                } else {
-                    log.error("Slack notification failed: {}", response.getMessage());
-                    return false;
-                }
-            } catch (IOException e) {
-                log.error("Failed to send Slack message: {}", e.getMessage(), e);
-                return false;
-            }
-        });
-    }
-
-    private List<LayoutBlock> createErrorBlocks(HttpServletRequest request, Exception e) {
-        String httpMethod = request.getMethod();
-        String requestUrl = request.getRequestURI();
-        String username = getUsername();
-
-        String errorMessage = e.getMessage() == null ? "No error message provided" : e.getMessage();
-        String detailedMessage = extractMessageAfterException(errorMessage);
-        log.error("Server Error: {}", detailedMessage);
-        return Arrays.asList(
-                section(section -> section.text(markdownText(":firecracker: *Server Error*"))),
-                section(section -> section.fields(Arrays.asList(
-                        markdownText("*User:*\n" + username),
-                        markdownText("*Endpoint:*\n[" + httpMethod + "] " + requestUrl)
-                ))),
-                section(section -> section.text(markdownText("*Error Message:*\n" + detailedMessage))),
-                section(section -> section.text(markdownText("*Stack Trace:*\n```" + getStackTraceSummary(e) + "```")))
-        );
+    public void sendNewBoardNotification(Board board) {
+        eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.BOARD_CREATED, null, board));
     }
 
-    private List<LayoutBlock> createSecurityAlertBlocks(HttpServletRequest request, SecurityAlertType alertType, String additionalMessage) {
-        String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
-        String requestUrl = request.getRequestURI();
-        String username = getUsername(request);
-        String location = getLocation(request);
-
-        return Arrays.asList(
-                section(section -> section.text(markdownText(String.format(":imp: *%s*", alertType.getTitle())))),
-                section(section -> section.fields(Arrays.asList(
-                        markdownText("*User:*\n" + username),
-                        markdownText("*IP Address:*\n" + clientIpAddress),
-                        markdownText("*Location:*\n" + location),
-                        markdownText("*Endpoint:*\n" + requestUrl)
-                ))),
-                section(section -> section.text(markdownText("*Details:*\n" + alertType.getDefaultMessage() + "\n" + additionalMessage)))
-        );
-    }
-
-    private List<LayoutBlock> createAdminLoginBlocks(HttpServletRequest request, Member loginMember) {
-        String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
-        String location = getLocation(request);
-
-        return Arrays.asList(
-                section(section -> section.text(markdownText(String.format(":mechanic: *%s Login*", loginMember.getRole().getDescription())))),
-                section(section -> section.fields(Arrays.asList(
-                        markdownText("*User:*\n" + loginMember.getId() + " " + loginMember.getName()),
-                        markdownText("*IP Address:*\n" + clientIpAddress),
-                        markdownText("*Location:*\n" + location)
-                )))
-        );
-    }
-
-    private List<LayoutBlock> createApplicationBlocks(ApplicationRequestDto requestDto) {
-        List<LayoutBlock> blocks = new ArrayList<>();
-
-        blocks.add(section(section -> section.text(markdownText(":sparkles: *New Application*"))));
-        blocks.add(section(section -> section.fields(Arrays.asList(
-                markdownText("*Type:*\n" + requestDto.getApplicationType().getDescription()),
-                markdownText("*Student ID:*\n" + requestDto.getStudentId()),
-                markdownText("*Name:*\n" + requestDto.getName()),
-                markdownText("*Grade:*\n" + requestDto.getGrade() + "학년"),
-                markdownText("*Interests:*\n" + requestDto.getInterests())
-        ))));
-
-        if (requestDto.getGithubUrl() != null && !requestDto.getGithubUrl().isEmpty()) {
-            blocks.add(actions(actions -> actions.elements(asElements(
-                    button(b -> b.text(plainText(pt -> pt.emoji(true).text("Github")))
-                            .url(requestDto.getGithubUrl())
-                            .actionId("click_github"))
-            ))));
-        }
-        return blocks;
-    }
-
-    private List<LayoutBlock> createServerStartBlocks() {
-        String osInfo = System.getProperty("os.name") + " " + System.getProperty("os.version");
-        String jdkVersion = System.getProperty("java.version");
-
-        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
-        int availableProcessors = osBean.getAvailableProcessors();
-        double systemLoadAverage = osBean.getSystemLoadAverage();
-        double cpuUsage = ((systemLoadAverage / availableProcessors) * 100);
-
-        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
-        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
-        String memoryInfo = formatMemoryUsage(heapMemoryUsage);
-
-        return Arrays.asList(
-                section(section -> section.text(markdownText("*:battery: Server Started*"))),
-                section(section -> section.fields(Arrays.asList(
-                        markdownText("*Environment:* \n" + environment.getProperty("spring.profiles.active")),
-                        markdownText("*OS:* \n" + osInfo),
-                        markdownText("*JDK Version:* \n" + jdkVersion),
-                        markdownText("*CPU Usage:* \n" + String.format("%.2f%%", cpuUsage)),
-                        markdownText("*Memory Usage:* \n" + memoryInfo)
-                ))),
-                actions(actions -> actions.elements(asElements(
-                        button(b -> b.text(plainText(pt -> pt.emoji(true).text("Web")))
-                                .url(webUrl)
-                                .value("click_web")),
-                        button(b -> b.text(plainText(pt -> pt.emoji(true).text("Swagger")))
-                                .url(apiUrl)
-                                .value("click_swagger"))
-                )))
-        );
-    }
-
-    private String extractMessageAfterException(String message) {
-        String exceptionIndicator = "Exception:";
-        int exceptionIndex = message.indexOf(exceptionIndicator);
-        return exceptionIndex == -1 ? message : message.substring(exceptionIndex + exceptionIndicator.length()).trim();
-    }
-
-    private String getStackTraceSummary(Exception e) {
-        return Arrays.stream(e.getStackTrace())
-                .limit(10)
-                .map(StackTraceElement::toString)
-                .collect(Collectors.joining("\n"));
-    }
-
-    private String formatMemoryUsage(MemoryUsage memoryUsage) {
-        long usedMemory = memoryUsage.getUsed() / (1024 * 1024);
-        long maxMemory = memoryUsage.getMax() / (1024 * 1024);
-        return String.format("%dMB / %dMB (%.2f%%)", usedMemory, maxMemory, ((double) usedMemory / maxMemory) * 100);
-    }
-
-    private static String getUsername() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        return (authentication == null || authentication.getName() == null) ? "anonymous" : authentication.getName();
-    }
-
-    private @NotNull String getUsername(HttpServletRequest request) {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        return Optional.ofNullable(request.getAttribute("member"))
-                .map(Object::toString)
-                .orElseGet(() -> Optional.ofNullable(authentication)
-                        .map(Authentication::getName)
-                        .orElse("anonymous"));
-    }
-
-    private @NotNull String getLocation(HttpServletRequest request) {
-        IPResponse ipResponse = attributeStrategy.getAttribute(request);
-        return ipResponse == null ? "Unknown" : ipResponse.getCountryName() + ", " + ipResponse.getCity();
+    @EventListener(ContextRefreshedEvent.class)
+    public void sendServerStartNotification() {
+        eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.SERVER_START, null, null));
     }
 
 }
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
new file mode 100644
index 000000000..3bd9c0e6d
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
@@ -0,0 +1,293 @@
+package page.clab.api.global.common.slack.application;
+
+import com.slack.api.Slack;
+import com.slack.api.model.Attachment;
+import static com.slack.api.model.block.Blocks.actions;
+import static com.slack.api.model.block.Blocks.section;
+import com.slack.api.model.block.LayoutBlock;
+import static com.slack.api.model.block.composition.BlockCompositions.markdownText;
+import static com.slack.api.model.block.composition.BlockCompositions.plainText;
+import static com.slack.api.model.block.element.BlockElements.asElements;
+import static com.slack.api.model.block.element.BlockElements.button;
+import com.slack.api.webhook.Payload;
+import com.slack.api.webhook.WebhookResponse;
+import io.ipinfo.api.model.IPResponse;
+import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.core.env.Environment;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
+import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.global.common.slack.domain.AlertType;
+import page.clab.api.global.common.slack.domain.GeneralAlertType;
+import page.clab.api.global.common.slack.domain.SecurityAlertType;
+import page.clab.api.global.config.SlackConfig;
+import page.clab.api.global.util.HttpReqResUtil;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.MemoryUsage;
+import java.lang.management.OperatingSystemMXBean;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+@Component
+@Slf4j
+public class SlackServiceHelper {
+
+    private final Slack slack;
+
+    private final String webhookUrl;
+
+    private final String webUrl;
+
+    private final String apiUrl;
+
+    private final String color;
+
+    private final Environment environment;
+
+    private final AttributeStrategy attributeStrategy;
+
+    public SlackServiceHelper(SlackConfig slackConfig, Environment environment, AttributeStrategy attributeStrategy) {
+        this.slack = slackConfig.slack();
+        this.webhookUrl = slackConfig.getWebhookUrl();
+        this.webUrl = slackConfig.getWebUrl();
+        this.apiUrl = slackConfig.getApiUrl();
+        this.color = slackConfig.getColor();
+        this.environment = environment;
+        this.attributeStrategy = attributeStrategy;
+    }
+
+    public CompletableFuture<Boolean> sendSlackMessage(AlertType alertType, HttpServletRequest request, Object additionalData) {
+        List<LayoutBlock> blocks = createBlocks(alertType, request, additionalData);
+
+        return CompletableFuture.supplyAsync(() -> {
+            Payload payload = Payload.builder()
+                    .blocks(List.of(blocks.getFirst()))
+                    .attachments(Collections.singletonList(
+                            Attachment.builder()
+                                    .color(color)
+                                    .blocks(blocks.subList(1, blocks.size()))
+                                    .build()
+                    )).build();
+            try {
+                WebhookResponse response = slack.send(webhookUrl, payload);
+                if (response.getCode() == 200) {
+                    return true;
+                } else {
+                    log.error("Slack notification failed: {}", response.getMessage());
+                    return false;
+                }
+            } catch (IOException e) {
+                log.error("Failed to send Slack message: {}", e.getMessage(), e);
+                return false;
+            }
+        });
+    }
+
+    public List<LayoutBlock> createBlocks(AlertType alertType, HttpServletRequest request, Object additionalData) {
+        if (alertType instanceof SecurityAlertType) {
+            return createSecurityAlertBlocks(request, alertType, additionalData.toString());
+        } else if (alertType instanceof GeneralAlertType) {
+            switch ((GeneralAlertType) alertType) {
+                case ADMIN_LOGIN:
+                    if (additionalData instanceof Member) {
+                        return createAdminLoginBlocks(request, (Member) additionalData);
+                    }
+                    break;
+                case APPLICATION_CREATED:
+                    if (additionalData instanceof ApplicationRequestDto) {
+                        return createApplicationBlocks((ApplicationRequestDto) additionalData);
+                    }
+                    break;
+                case BOARD_CREATED:
+                    if (additionalData instanceof Board) {
+                        return createBoardBlocks((Board) additionalData);
+                    }
+                    break;
+                case SERVER_START:
+                    return createServerStartBlocks();
+                case SERVER_ERROR:
+                    if (additionalData instanceof Exception) {
+                        return createErrorBlocks(request, (Exception) additionalData);
+                    }
+                    break;
+                default:
+                    log.error("Unknown alert type: {}", alertType);
+                    return List.of();
+            }
+        }
+        return List.of();
+    }
+
+    private List<LayoutBlock> createErrorBlocks(HttpServletRequest request, Exception e) {
+        String httpMethod = request.getMethod();
+        String requestUrl = request.getRequestURI();
+        String username = getUsername();
+
+        String errorMessage = e.getMessage() == null ? "No error message provided" : e.getMessage();
+        String detailedMessage = extractMessageAfterException(errorMessage);
+        log.error("Server Error: {}", detailedMessage);
+        return Arrays.asList(
+                section(section -> section.text(markdownText(":firecracker: *Server Error*"))),
+                section(section -> section.fields(Arrays.asList(
+                        markdownText("*User:*\n" + username),
+                        markdownText("*Endpoint:*\n[" + httpMethod + "] " + requestUrl)
+                ))),
+                section(section -> section.text(markdownText("*Error Message:*\n" + detailedMessage))),
+                section(section -> section.text(markdownText("*Stack Trace:*\n```" + getStackTraceSummary(e) + "```")))
+        );
+    }
+
+    private List<LayoutBlock> createSecurityAlertBlocks(HttpServletRequest request, AlertType alertType, String additionalMessage) {
+        String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
+        String requestUrl = request.getRequestURI();
+        String username = getUsername(request);
+        String location = getLocation(request);
+
+        return Arrays.asList(
+                section(section -> section.text(markdownText(String.format(":imp: *%s*", alertType.getTitle())))),
+                section(section -> section.fields(Arrays.asList(
+                        markdownText("*User:*\n" + username),
+                        markdownText("*IP Address:*\n" + clientIpAddress),
+                        markdownText("*Location:*\n" + location),
+                        markdownText("*Endpoint:*\n" + requestUrl)
+                ))),
+                section(section -> section.text(markdownText("*Details:*\n" + alertType.getDefaultMessage() + "\n" + additionalMessage)))
+        );
+    }
+
+    private List<LayoutBlock> createAdminLoginBlocks(HttpServletRequest request, Member loginMember) {
+        String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
+        String location = getLocation(request);
+
+        return Arrays.asList(
+                section(section -> section.text(markdownText(String.format(":mechanic: *%s Login*", loginMember.getRole().getDescription())))),
+                section(section -> section.fields(Arrays.asList(
+                        markdownText("*User:*\n" + loginMember.getId() + " " + loginMember.getName()),
+                        markdownText("*IP Address:*\n" + clientIpAddress),
+                        markdownText("*Location:*\n" + location)
+                )))
+        );
+    }
+
+    private List<LayoutBlock> createApplicationBlocks(ApplicationRequestDto requestDto) {
+        List<LayoutBlock> blocks = new ArrayList<>();
+
+        blocks.add(section(section -> section.text(markdownText(":sparkles: *New Application*"))));
+        blocks.add(section(section -> section.fields(Arrays.asList(
+                markdownText("*Type:*\n" + requestDto.getApplicationType().getDescription()),
+                markdownText("*Student ID:*\n" + requestDto.getStudentId()),
+                markdownText("*Name:*\n" + requestDto.getName()),
+                markdownText("*Grade:*\n" + requestDto.getGrade() + "학년"),
+                markdownText("*Interests:*\n" + requestDto.getInterests())
+        ))));
+
+        if (requestDto.getGithubUrl() != null && !requestDto.getGithubUrl().isEmpty()) {
+            blocks.add(actions(actions -> actions.elements(asElements(
+                    button(b -> b.text(plainText(pt -> pt.emoji(true).text("Github")))
+                            .url(requestDto.getGithubUrl())
+                            .actionId("click_github"))
+            ))));
+        }
+        return blocks;
+    }
+
+    private List<LayoutBlock> createBoardBlocks(Board board) {
+        List<LayoutBlock> blocks = new ArrayList<>();
+        String username = board.isWantAnonymous() ?
+                board.getNickname() : board.getMember().getId() + " " + board.getMember().getName();
+
+        blocks.add(section(section -> section.text(markdownText(":writing_hand: *New Board*"))));
+        blocks.add(section(section -> section.fields(Arrays.asList(
+                markdownText("*Title:*\n" + board.getTitle()),
+                markdownText("*Category:*\n" + board.getCategory().getDescription()),
+                markdownText("*User:*\n" + username)
+        ))));
+        return blocks;
+    }
+
+    private List<LayoutBlock> createServerStartBlocks() {
+        String osInfo = System.getProperty("os.name") + " " + System.getProperty("os.version");
+        String jdkVersion = System.getProperty("java.version");
+
+        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
+        int availableProcessors = osBean.getAvailableProcessors();
+        double systemLoadAverage = osBean.getSystemLoadAverage();
+        double cpuUsage = ((systemLoadAverage / availableProcessors) * 100);
+
+        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
+        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
+        String memoryInfo = formatMemoryUsage(heapMemoryUsage);
+
+        return Arrays.asList(
+                section(section -> section.text(markdownText("*:battery: Server Started*"))),
+                section(section -> section.fields(Arrays.asList(
+                        markdownText("*Environment:* \n" + environment.getProperty("spring.profiles.active")),
+                        markdownText("*OS:* \n" + osInfo),
+                        markdownText("*JDK Version:* \n" + jdkVersion),
+                        markdownText("*CPU Usage:* \n" + String.format("%.2f%%", cpuUsage)),
+                        markdownText("*Memory Usage:* \n" + memoryInfo)
+                ))),
+                actions(actions -> actions.elements(asElements(
+                        button(b -> b.text(plainText(pt -> pt.emoji(true).text("Web")))
+                                .url(webUrl)
+                                .value("click_web")),
+                        button(b -> b.text(plainText(pt -> pt.emoji(true).text("Swagger")))
+                                .url(apiUrl)
+                                .value("click_swagger"))
+                )))
+        );
+    }
+
+    private String extractMessageAfterException(String message) {
+        String exceptionIndicator = "Exception:";
+        int exceptionIndex = message.indexOf(exceptionIndicator);
+        return exceptionIndex == -1 ? message : message.substring(exceptionIndex + exceptionIndicator.length()).trim();
+    }
+
+    private String getStackTraceSummary(Exception e) {
+        return Arrays.stream(e.getStackTrace())
+                .limit(10)
+                .map(StackTraceElement::toString)
+                .collect(Collectors.joining("\n"));
+    }
+
+    private String formatMemoryUsage(MemoryUsage memoryUsage) {
+        long usedMemory = memoryUsage.getUsed() / (1024 * 1024);
+        long maxMemory = memoryUsage.getMax() / (1024 * 1024);
+        return String.format("%dMB / %dMB (%.2f%%)", usedMemory, maxMemory, ((double) usedMemory / maxMemory) * 100);
+    }
+
+    private static String getUsername() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return (authentication == null || authentication.getName() == null) ? "anonymous" : authentication.getName();
+    }
+
+    private @NotNull String getUsername(HttpServletRequest request) {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return Optional.ofNullable(request.getAttribute("member"))
+                .map(Object::toString)
+                .orElseGet(() -> Optional.ofNullable(authentication)
+                        .map(Authentication::getName)
+                        .orElse("anonymous"));
+    }
+
+    private @NotNull String getLocation(HttpServletRequest request) {
+        IPResponse ipResponse = attributeStrategy.getAttribute(request);
+        return ipResponse == null ? "Unknown" : ipResponse.getCountryName() + ", " + ipResponse.getCity();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/dao/NotificationSettingRepository.java b/src/main/java/page/clab/api/global/common/slack/dao/NotificationSettingRepository.java
new file mode 100644
index 000000000..a66b8d5cd
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/dao/NotificationSettingRepository.java
@@ -0,0 +1,13 @@
+package page.clab.api.global.common.slack.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import page.clab.api.global.common.slack.domain.AlertType;
+import page.clab.api.global.common.slack.domain.NotificationSetting;
+
+import java.util.Optional;
+
+public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, Long> {
+
+    Optional<NotificationSetting> findByAlertType(AlertType alertType);
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/AlertType.java b/src/main/java/page/clab/api/global/common/slack/domain/AlertType.java
new file mode 100644
index 000000000..971d10449
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/domain/AlertType.java
@@ -0,0 +1,9 @@
+package page.clab.api.global.common.slack.domain;
+
+public interface AlertType {
+
+    String getTitle();
+
+    String getDefaultMessage();
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeConverter.java b/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeConverter.java
new file mode 100644
index 000000000..c96092da1
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeConverter.java
@@ -0,0 +1,43 @@
+package page.clab.api.global.common.slack.domain;
+
+import jakarta.persistence.AttributeConverter;
+import jakarta.persistence.Converter;
+import page.clab.api.global.common.slack.exception.AlertTypeNotFoundException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Converter(autoApply = true)
+public class AlertTypeConverter implements AttributeConverter<AlertType, String> {
+
+    private static final Map<String, AlertType> CACHE = new HashMap<>();
+
+    static {
+        for (GeneralAlertType type : GeneralAlertType.values()) {
+            CACHE.put(type.getTitle(), type);
+        }
+        for (SecurityAlertType type : SecurityAlertType.values()) {
+            CACHE.put(type.getTitle(), type);
+        }
+    }
+
+    @Override
+    public String convertToDatabaseColumn(AlertType alertType) {
+        if (alertType == null) {
+            return null;
+        }
+        return alertType.getTitle();
+    }
+
+    @Override
+    public AlertType convertToEntityAttribute(String dbData) {
+        if (dbData == null || dbData.isEmpty()) {
+            return null;
+        }
+        AlertType alertType = CACHE.get(dbData);
+        if (alertType == null) {
+            throw new AlertTypeNotFoundException(dbData);
+        }
+        return alertType;
+    }
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeResolver.java b/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeResolver.java
new file mode 100644
index 000000000..a0c8d101d
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/domain/AlertTypeResolver.java
@@ -0,0 +1,23 @@
+package page.clab.api.global.common.slack.domain;
+
+import org.springframework.stereotype.Service;
+import page.clab.api.global.common.slack.exception.AlertTypeNotFoundException;
+
+@Service
+public class AlertTypeResolver {
+
+    public AlertType resolve(String alertTypeName) {
+        for (GeneralAlertType type : GeneralAlertType.values()) {
+            if (type.getTitle().equals(alertTypeName)) {
+                return type;
+            }
+        }
+        for (SecurityAlertType type : SecurityAlertType.values()) {
+            if (type.getTitle().equals(alertTypeName)) {
+                return type;
+            }
+        }
+        throw new AlertTypeNotFoundException(alertTypeName);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/GeneralAlertType.java b/src/main/java/page/clab/api/global/common/slack/domain/GeneralAlertType.java
new file mode 100644
index 000000000..42444236e
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/domain/GeneralAlertType.java
@@ -0,0 +1,19 @@
+package page.clab.api.global.common.slack.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum GeneralAlertType implements AlertType {
+
+    ADMIN_LOGIN("관리자 로그인", "Admin login."),
+    APPLICATION_CREATED("새 지원서", "New application has been submitted."),
+    BOARD_CREATED("새 게시글", "New board has been created."),
+    SERVER_START("서버 시작", "Server has been started."),
+    SERVER_ERROR("서버 에러", "Server error occurred.");
+
+    private final String title;
+    private final String defaultMessage;
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/NotificationSetting.java b/src/main/java/page/clab/api/global/common/slack/domain/NotificationSetting.java
new file mode 100644
index 000000000..a6b8ff8da
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/domain/NotificationSetting.java
@@ -0,0 +1,43 @@
+package page.clab.api.global.common.slack.domain;
+
+import jakarta.persistence.Convert;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@Entity
+public class NotificationSetting {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Convert(converter = AlertTypeConverter.class)
+    private AlertType alertType;
+
+    private boolean enabled;
+
+    public static NotificationSetting createDefault(AlertType alertType) {
+        return NotificationSetting.builder()
+                .alertType(alertType)
+                .enabled(true)
+                .build();
+    }
+
+    public void updateEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/domain/SecurityAlertType.java b/src/main/java/page/clab/api/global/common/slack/domain/SecurityAlertType.java
index 587de828d..a0921ba64 100644
--- a/src/main/java/page/clab/api/global/common/slack/domain/SecurityAlertType.java
+++ b/src/main/java/page/clab/api/global/common/slack/domain/SecurityAlertType.java
@@ -5,7 +5,7 @@
 
 @Getter
 @AllArgsConstructor
-public enum SecurityAlertType {
+public enum SecurityAlertType implements AlertType {
 
     ABNORMAL_ACCESS("비정상적인 접근", "Unexpected access pattern detected."),
     REPEATED_LOGIN_FAILURES("지속된 로그인 실패", "Multiple consecutive failed login attempts."),
diff --git a/src/main/java/page/clab/api/global/common/slack/dto/request/NotificationSettingUpdateRequestDto.java b/src/main/java/page/clab/api/global/common/slack/dto/request/NotificationSettingUpdateRequestDto.java
new file mode 100644
index 000000000..ed1343b02
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/dto/request/NotificationSettingUpdateRequestDto.java
@@ -0,0 +1,20 @@
+package page.clab.api.global.common.slack.dto.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class NotificationSettingUpdateRequestDto {
+
+    @NotNull(message = "{notNull.notificationSetting.alertType}")
+    @Schema(description = "알림 타입", example = "서버 시작")
+    private String alertType;
+
+    @NotNull(message = "{notNull.notificationSetting.enabled}")
+    @Schema(description = "알림 활성화 여부", example = "true")
+    private boolean enabled;
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/dto/response/NotificationSettingResponseDto.java b/src/main/java/page/clab/api/global/common/slack/dto/response/NotificationSettingResponseDto.java
new file mode 100644
index 000000000..25bb27b3f
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/dto/response/NotificationSettingResponseDto.java
@@ -0,0 +1,22 @@
+package page.clab.api.global.common.slack.dto.response;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.global.common.slack.domain.NotificationSetting;
+
+@Getter
+@Builder
+public class NotificationSettingResponseDto {
+    
+    private String alertType;
+
+    private boolean enabled;
+    
+    public static NotificationSettingResponseDto toDto(NotificationSetting setting) {
+        return NotificationSettingResponseDto.builder()
+                .alertType(setting.getAlertType().getTitle())
+                .enabled(setting.isEnabled())
+                .build();
+    }
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/event/NotificationEvent.java b/src/main/java/page/clab/api/global/common/slack/event/NotificationEvent.java
new file mode 100644
index 000000000..5ac93ee69
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/event/NotificationEvent.java
@@ -0,0 +1,24 @@
+package page.clab.api.global.common.slack.event;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+import page.clab.api.global.common.slack.domain.AlertType;
+
+@Getter
+public class NotificationEvent extends ApplicationEvent {
+
+    private final AlertType alertType;
+
+    private final HttpServletRequest request;
+
+    private final Object additionalData;
+
+    public NotificationEvent(Object source, AlertType alertType, HttpServletRequest request, Object additionalData) {
+        super(source);
+        this.alertType = alertType;
+        this.request = request;
+        this.additionalData = additionalData;
+    }
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/exception/AlertTypeNotFoundException.java b/src/main/java/page/clab/api/global/common/slack/exception/AlertTypeNotFoundException.java
new file mode 100644
index 000000000..e7ea55bf6
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/exception/AlertTypeNotFoundException.java
@@ -0,0 +1,9 @@
+package page.clab.api.global.common.slack.exception;
+
+public class AlertTypeNotFoundException extends RuntimeException {
+
+    public AlertTypeNotFoundException(String alertTypeName) {
+        super("Unknown alert type: " + alertTypeName);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/global/common/slack/listener/NotificationListener.java b/src/main/java/page/clab/api/global/common/slack/listener/NotificationListener.java
new file mode 100644
index 000000000..9dac2e8fe
--- /dev/null
+++ b/src/main/java/page/clab/api/global/common/slack/listener/NotificationListener.java
@@ -0,0 +1,30 @@
+package page.clab.api.global.common.slack.listener;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+import page.clab.api.global.common.slack.application.NotificationSettingService;
+import page.clab.api.global.common.slack.application.SlackServiceHelper;
+import page.clab.api.global.common.slack.domain.AlertType;
+import page.clab.api.global.common.slack.domain.NotificationSetting;
+import page.clab.api.global.common.slack.event.NotificationEvent;
+
+@Component
+@RequiredArgsConstructor
+public class NotificationListener {
+
+    private final NotificationSettingService settingService;
+
+    private final SlackServiceHelper slackServiceHelper;
+
+    @EventListener
+    public void handleNotificationEvent(NotificationEvent event) {
+        AlertType alertType = event.getAlertType();
+        NotificationSetting setting = settingService.getOrCreateDefaultSetting(alertType);
+
+        if (setting.isEnabled()) {
+            slackServiceHelper.sendSlackMessage(alertType, event.getRequest(), event.getAdditionalData());
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index d42cc8b92..6970588fd 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -157,6 +157,8 @@ notNull.news.source=출처는 필수 입력 항목입니다.
 notNull.news.date=날짜는 필수 입력 항목입니다.
 notNull.notification.memberId=학번은 필수 입력 항목입니다.
 notNull.notification.content=내용은 필수 입력 항목입니다.
+notNull.notificationSetting.alertType=알림 타입은 필수 입력 항목입니다.
+notNull.notificationSetting.enabled=알림 활성화 여부는 필수 입력 항목입니다.
 notNull.product.name=제품명은 필수 입력 항목입니다.
 notNull.product.description=설명은 필수 입력 항목입니다.
 notNull.recruitment.startDate=시작 날짜는 필수 입력 항목입니다.

From 2725396882401572b04bb848bc5a547e8f6c5bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=86=A1=EC=9E=AC=ED=9B=88?=
 <128021502+SongJaeHoonn@users.noreply.github.com>
Date: Tue, 25 Jun 2024 23:42:54 +0900
Subject: [PATCH 12/47] =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B3=B5=EA=B3=A0=20?=
 =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9E=90=EB=8F=99=EB=B3=80=EA=B2=BD=20?=
 =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1=20=EC=99=84=EB=A3=8C=20?=
 =?UTF-8?q?(#389)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 모집공고 상태 나타내는 enum 도입 #353

* feat: 모집 기간에 따라 자동으로 모집 상태 변경하는 로직 작성 #353

* refactor: 모집 상태 구현 로직 변경에 따른 DTO 수정 #353

* feat: 트랜잭션을 추상화한 인터페이스 빈으로 등록 #353

* refactor: 모집 상태 문자열 사이즈 관련 message 삭제 #353

* refactor: 모집 공고 수정 시에도 즉각적으로 상태 반영되도록 수정 #353

* refactor: 띄어쓰기 수정 #353

* refactor(Recruitment): 띄어쓰기 수정

---------

Co-authored-by: 한관희 <noop103@naver.com>
---
 .../application/RecruitmentService.java       | 35 +++++++++++++++++++
 .../recruitment/domain/Recruitment.java       |  7 ++--
 .../recruitment/domain/RecruitmentStatus.java | 17 +++++++++
 .../dto/request/RecruitmentRequestDto.java    |  5 ---
 .../request/RecruitmentUpdateRequestDto.java  |  3 --
 .../dto/response/RecruitmentResponseDto.java  |  3 +-
 .../api/global/config/TransactionConfig.java  | 16 +++++++++
 src/main/resources/messages.properties        |  1 -
 8 files changed, 74 insertions(+), 13 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/recruitment/domain/RecruitmentStatus.java
 create mode 100644 src/main/java/page/clab/api/global/config/TransactionConfig.java

diff --git a/src/main/java/page/clab/api/domain/recruitment/application/RecruitmentService.java b/src/main/java/page/clab/api/domain/recruitment/application/RecruitmentService.java
index a6275550c..c4c01507d 100644
--- a/src/main/java/page/clab/api/domain/recruitment/application/RecruitmentService.java
+++ b/src/main/java/page/clab/api/domain/recruitment/application/RecruitmentService.java
@@ -1,13 +1,19 @@
 package page.clab.api.domain.recruitment.application;
 
+import jakarta.persistence.EntityManager;
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.domain.recruitment.dao.RecruitmentRepository;
 import page.clab.api.domain.recruitment.domain.Recruitment;
+import page.clab.api.domain.recruitment.domain.RecruitmentStatus;
 import page.clab.api.domain.recruitment.dto.request.RecruitmentRequestDto;
 import page.clab.api.domain.recruitment.dto.request.RecruitmentUpdateRequestDto;
 import page.clab.api.domain.recruitment.dto.response.RecruitmentResponseDto;
@@ -15,6 +21,7 @@
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.validation.ValidationService;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 @Service
@@ -27,9 +34,16 @@ public class RecruitmentService {
 
     private final RecruitmentRepository recruitmentRepository;
 
+    private final PlatformTransactionManager transactionManager;
+
+    private final EntityManager entityManager;
+
+    private final TransactionDefinition transactionDefinition;
+
     @Transactional
     public Long createRecruitment(RecruitmentRequestDto requestDto) {
         Recruitment recruitment = RecruitmentRequestDto.toEntity(requestDto);
+        updateRecruitmentStatusByRecruitment(recruitment);
         validationService.checkValid(recruitment);
         notificationService.sendNotificationToAllMembers("새로운 모집 공고가 등록되었습니다.");
         return recruitmentRepository.save(recruitment).getId();
@@ -55,6 +69,7 @@ public Long updateRecruitment(Long recruitmentId, RecruitmentUpdateRequestDto re
         Recruitment recruitment = getRecruitmentByIdOrThrow(recruitmentId);
         recruitment.update(requestDto);
         validationService.checkValid(recruitment);
+        updateRecruitmentStatusByRecruitment(recruitment);
         return recruitmentRepository.save(recruitment).getId();
     }
 
@@ -69,4 +84,24 @@ public Recruitment getRecruitmentByIdOrThrow(Long recruitmentId) {
                 .orElseThrow(() -> new NotFoundException("해당 모집 공고가 존재하지 않습니다."));
     }
 
+    @Scheduled(cron = "0 * * * * *")
+    public void updateRecruitmentStatus(){
+        List<Recruitment> recruitments = recruitmentRepository.findAll();
+        recruitments.forEach(this::updateRecruitmentStatusByRecruitment);
+    }
+
+    public void updateRecruitmentStatusByRecruitment(Recruitment recruitment){
+        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
+        LocalDateTime now = LocalDateTime.now();
+        RecruitmentStatus newStatus = RecruitmentStatus.OPEN;
+        if (now.isBefore(recruitment.getStartDate())) {
+            newStatus = RecruitmentStatus.UPCOMING;
+        } else if(now.isAfter(recruitment.getEndDate())) {
+            newStatus = RecruitmentStatus.CLOSED;
+        }
+        recruitment.updateStatus(newStatus);
+        entityManager.merge(recruitment);
+        transactionManager.commit(transactionStatus);
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/recruitment/domain/Recruitment.java b/src/main/java/page/clab/api/domain/recruitment/domain/Recruitment.java
index 7f689027c..a114029e6 100644
--- a/src/main/java/page/clab/api/domain/recruitment/domain/Recruitment.java
+++ b/src/main/java/page/clab/api/domain/recruitment/domain/Recruitment.java
@@ -52,15 +52,16 @@ public class Recruitment extends BaseEntity {
     private String target;
 
     @Column(nullable = false)
-    @Size(min = 1, message = "{size.recruitment.status}")
-    private String status;
+    @Enumerated(EnumType.STRING)
+    private RecruitmentStatus status;
 
     public void update(RecruitmentUpdateRequestDto recruitmentUpdateRequestDto) {
         Optional.ofNullable(recruitmentUpdateRequestDto.getStartDate()).ifPresent(this::setStartDate);
         Optional.ofNullable(recruitmentUpdateRequestDto.getEndDate()).ifPresent(this::setEndDate);
         Optional.ofNullable(recruitmentUpdateRequestDto.getApplicationType()).ifPresent(this::setApplicationType);
         Optional.ofNullable(recruitmentUpdateRequestDto.getTarget()).ifPresent(this::setTarget);
-        Optional.ofNullable(recruitmentUpdateRequestDto.getStatus()).ifPresent(this::setStatus);
     }
 
+    public void updateStatus(RecruitmentStatus status) { this.status = status; }
+
 }
diff --git a/src/main/java/page/clab/api/domain/recruitment/domain/RecruitmentStatus.java b/src/main/java/page/clab/api/domain/recruitment/domain/RecruitmentStatus.java
new file mode 100644
index 000000000..bd77d7f86
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/recruitment/domain/RecruitmentStatus.java
@@ -0,0 +1,17 @@
+package page.clab.api.domain.recruitment.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum RecruitmentStatus {
+
+    UPCOMING("UPCOMING", "모집 예정"),
+    OPEN("OPEN", "모집중"),
+    CLOSED("CLOSED", "모집 종료");
+
+    private String key;
+    private String description;
+
+}
diff --git a/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentRequestDto.java b/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentRequestDto.java
index 7cbf3eff6..3dbe53273 100644
--- a/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentRequestDto.java
+++ b/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentRequestDto.java
@@ -29,17 +29,12 @@ public class RecruitmentRequestDto {
     @Schema(description = "대상", example = "2~3학년", required = true)
     private String target;
 
-    @NotNull(message = "{notNull.recruitment.status}")
-    @Schema(description = "상태", example = "종료", required = true)
-    private String status;
-
     public static Recruitment toEntity(RecruitmentRequestDto requestDto) {
         return Recruitment.builder()
                 .startDate(requestDto.getStartDate())
                 .endDate(requestDto.getEndDate())
                 .applicationType(requestDto.getApplicationType())
                 .target(requestDto.getTarget())
-                .status(requestDto.getStatus())
                 .build();
     }
 
diff --git a/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentUpdateRequestDto.java b/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentUpdateRequestDto.java
index 0a41652e8..a7579a794 100644
--- a/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentUpdateRequestDto.java
+++ b/src/main/java/page/clab/api/domain/recruitment/dto/request/RecruitmentUpdateRequestDto.java
@@ -23,7 +23,4 @@ public class RecruitmentUpdateRequestDto {
     @Schema(description = "대상", example = "2~3학년")
     private String target;
 
-    @Schema(description = "상태", example = "종료")
-    private String status;
-
 }
diff --git a/src/main/java/page/clab/api/domain/recruitment/dto/response/RecruitmentResponseDto.java b/src/main/java/page/clab/api/domain/recruitment/dto/response/RecruitmentResponseDto.java
index f619b6b93..18f42adc6 100644
--- a/src/main/java/page/clab/api/domain/recruitment/dto/response/RecruitmentResponseDto.java
+++ b/src/main/java/page/clab/api/domain/recruitment/dto/response/RecruitmentResponseDto.java
@@ -4,6 +4,7 @@
 import lombok.Getter;
 import page.clab.api.domain.application.domain.ApplicationType;
 import page.clab.api.domain.recruitment.domain.Recruitment;
+import page.clab.api.domain.recruitment.domain.RecruitmentStatus;
 
 import java.time.LocalDateTime;
 
@@ -21,7 +22,7 @@ public class RecruitmentResponseDto {
 
     private String target;
 
-    private String status;
+    private RecruitmentStatus status;
 
     private LocalDateTime updatedAt;
 
diff --git a/src/main/java/page/clab/api/global/config/TransactionConfig.java b/src/main/java/page/clab/api/global/config/TransactionConfig.java
new file mode 100644
index 000000000..74753ce61
--- /dev/null
+++ b/src/main/java/page/clab/api/global/config/TransactionConfig.java
@@ -0,0 +1,16 @@
+package page.clab.api.global.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+
+@Configuration
+public class TransactionConfig {
+
+    @Bean
+    public TransactionDefinition transactionDefinition() {
+        return new DefaultTransactionDefinition();
+    }
+
+}
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 6970588fd..5d1a5a4ad 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -44,7 +44,6 @@ size.product.name=서비스명은 최소 {min}글자 이상이어야 합니다.
 size.product.description=설명은 최소 {min}글자에서 {max}글자 사이어야 합니다.
 size.recruitment.applicationType=신청 유형은 최소 {min}자 이상이어야 합니다.
 size.recruitment.target=대상은 최소 {min}자 이상이어야 합니다.
-size.recruitment.status=상태는 최소 {min}자 이상이어야 합니다.
 size.review.content=내용은 {min}자 이상 {max}자 이하여야 합니다.
 size.sharedAccount.username=유저명은 {min}자 이상이어야 합니다.
 size.sharedAccount.password=비밀번호는 {min}자 이상이어야 합니다.

From 3d206acf6cf0f99d79b4621868d249c46757791d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 04:36:03 +0900
Subject: [PATCH 13/47] =?UTF-8?q?refactor(Member):=20MemberLookupService?=
 =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../accuse/application/AccuseService.java     |  8 +-
 .../ActivityGroupAdminService.java            | 18 ++---
 .../ActivityGroupBoardService.java            | 16 ++--
 .../ActivityGroupMemberService.java           | 10 +--
 .../ActivityGroupReportService.java           | 10 +--
 .../application/AttendanceService.java        | 16 ++--
 .../award/application/AwardService.java       | 12 +--
 .../domain/blog/application/BlogService.java  | 14 ++--
 .../board/application/BoardService.java       | 16 ++--
 .../application/BookLoanRecordService.java    | 12 +--
 .../comment/application/CommentService.java   | 20 ++---
 .../donation/application/DonationService.java | 12 +--
 .../application/AccountLockInfoService.java   | 15 ++--
 .../login/application/LoginService.java       | 12 +--
 .../application/MemberLookupService.java      | 28 +++++++
 .../application/MemberLookupServiceImpl.java  | 78 ++++++++++++++++++
 .../member/application/MemberService.java     | 81 +++----------------
 .../domain/member/dao/MemberRepository.java   |  2 +-
 .../application/MembershipFeeService.java     | 14 ++--
 .../application/NotificationService.java      | 25 +++---
 .../position/application/PositionService.java |  8 +-
 .../review/application/ReviewService.java     | 16 ++--
 .../schedule/application/ScheduleService.java | 12 +--
 .../SharedAccountUsageService.java            |  8 +-
 .../application/WorkExperienceService.java    | 14 ++--
 .../email/application/EmailService.java       |  8 +-
 .../common/file/application/FileService.java  | 14 ++--
 27 files changed, 268 insertions(+), 231 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
 create mode 100644 src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index ee3454436..668bf358c 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -23,7 +23,7 @@
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.comment.application.CommentService;
 import page.clab.api.domain.comment.domain.Comment;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.domain.review.application.ReviewService;
@@ -39,7 +39,7 @@
 @Slf4j
 public class AccuseService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final BoardService boardService;
 
@@ -59,7 +59,7 @@ public class AccuseService {
     public Long createAccuse(AccuseRequestDto requestDto) {
         TargetType type = requestDto.getTargetType();
         Long targetId = requestDto.getTargetId();
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
 
         validateAccuseRequest(type, targetId, currentMember);
 
@@ -84,7 +84,7 @@ public PagedResponseDto<AccuseResponseDto> getAccusesByConditions(TargetType typ
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AccuseMyResponseDto> getMyAccuses(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Accuse> accuses = accuseRepository.findByMember(currentMember, pageable);
         return new PagedResponseDto<>(accuses.map(AccuseMyResponseDto::toDto));
     }
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
index 1c716c78b..6a9182b35 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
@@ -21,7 +21,7 @@
 import page.clab.api.domain.activityGroup.dto.request.ActivityGroupUpdateRequestDto;
 import page.clab.api.domain.activityGroup.dto.response.ActivityGroupMemberWithApplyReasonResponseDto;
 import page.clab.api.domain.activityGroup.dto.response.ActivityGroupResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -37,7 +37,7 @@
 @RequiredArgsConstructor
 public class ActivityGroupAdminService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ActivityGroupMemberService activityGroupMemberService;
 
@@ -53,7 +53,7 @@ public class ActivityGroupAdminService {
 
     @Transactional
     public Long createActivityGroup(ActivityGroupRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = ActivityGroupRequestDto.toEntity(requestDto);
         validationService.checkValid(activityGroup);
         activityGroupRepository.save(activityGroup);
@@ -68,7 +68,7 @@ public Long createActivityGroup(ActivityGroupRequestDto requestDto) {
 
     @Transactional
     public Long updateActivityGroup(Long activityGroupId, ActivityGroupUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         if (!isMemberGroupLeaderRole(activityGroup, currentMember)) {
             throw new PermissionDeniedException("해당 활동을 수정할 권한이 없습니다.");
@@ -117,7 +117,7 @@ public Long deleteActivityGroup(Long activityGroupId) {
 
     @Transactional
     public Long updateProjectProgress(Long activityGroupId, Long progress) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         if (!isMemberGroupLeaderRole(activityGroup, currentMember)) {
             throw new PermissionDeniedException("해당 활동을 수정할 권한이 없습니다.");
@@ -129,7 +129,7 @@ public Long updateProjectProgress(Long activityGroupId, Long progress) throws Pe
 
     @Transactional
     public Long addSchedule(Long activityGroupId, List<GroupScheduleDto> scheduleDtos) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         if (!isMemberGroupLeaderRole(activityGroup, currentMember)) {
             throw new PermissionDeniedException("해당 일정을 등록할 권한이 없습니다.");
@@ -143,7 +143,7 @@ public Long addSchedule(Long activityGroupId, List<GroupScheduleDto> scheduleDto
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ActivityGroupMemberWithApplyReasonResponseDto> getGroupMembersWithApplyReason(Long activityGroupId, Pageable pageable) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         if (!(isMemberGroupLeaderRole(activityGroup, currentMember) || currentMember.isAdminRole())) {
             throw new PermissionDeniedException("해당 활동의 멤버를 조회할 권한이 없습니다.");
@@ -168,13 +168,13 @@ public PagedResponseDto<ActivityGroupMemberWithApplyReasonResponseDto> getGroupM
 
     @Transactional
     public String manageGroupMemberStatus(Long activityGroupId, String memberId, GroupMemberStatus status) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         if (!isMemberGroupLeaderRole(activityGroup, currentMember)) {
             throw new PermissionDeniedException("해당 활동의 신청 멤버를 조회할 권한이 없습니다.");
         }
 
-        Member member = memberService.getMemberByIdOrThrow(memberId);
+        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         GroupMember groupMember = activityGroupMemberService.getGroupMemberByActivityGroupAndMemberOrThrow(activityGroup, member);
         groupMember.validateAccessPermission();
         groupMember.updateStatus(status);
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
index c5f6ca403..e855dfcd9 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
@@ -21,9 +21,7 @@
 import page.clab.api.domain.activityGroup.dto.response.AssignmentSubmissionWithFeedbackResponseDto;
 import page.clab.api.domain.activityGroup.dto.response.FeedbackResponseDto;
 import page.clab.api.domain.activityGroup.exception.InvalidParentBoardException;
-import page.clab.api.domain.award.domain.Award;
-import page.clab.api.domain.award.dto.response.AwardResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -43,7 +41,7 @@ public class ActivityGroupBoardService {
 
     private final ActivityGroupBoardRepository activityGroupBoardRepository;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ActivityGroupAdminService activityGroupAdminService;
 
@@ -57,7 +55,7 @@ public class ActivityGroupBoardService {
 
     @Transactional
     public Long createActivityGroupBoard(Long parentId, Long activityGroupId, ActivityGroupBoardRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = activityGroupAdminService.getActivityGroupByIdOrThrow(activityGroupId);
         if (!activityGroupMemberService.isGroupMember(activityGroup, currentMember)) {
             throw new PermissionDeniedException("활동 그룹 멤버만 게시글을 등록할 수 있습니다.");
@@ -99,7 +97,7 @@ public PagedResponseDto<ActivityGroupBoardResponseDto> getActivityGroupBoardByCa
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ActivityGroupBoardChildResponseDto> getActivityGroupBoardByParent(Long parentId, Pageable pageable) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroupBoard parentBoard = getActivityGroupBoardByIdOrThrow(parentId);
         Long activityGroupId = parentBoard.getActivityGroup().getId();
 
@@ -113,7 +111,7 @@ public PagedResponseDto<ActivityGroupBoardChildResponseDto> getActivityGroupBoar
 
     @Transactional(readOnly = true)
     public List<AssignmentSubmissionWithFeedbackResponseDto> getMyAssignmentsWithFeedbacks(Long parentId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroupBoard parentBoard = getActivityGroupBoardByIdOrThrow(parentId);
         List<ActivityGroupBoard> mySubmissions = activityGroupBoardRepository.findMySubmissionsWithFeedbacks(parentId, currentMember.getId());
         return mySubmissions.stream()
@@ -129,7 +127,7 @@ public List<AssignmentSubmissionWithFeedbackResponseDto> getMyAssignmentsWithFee
 
     @Transactional
     public ActivityGroupBoardUpdateResponseDto updateActivityGroupBoard(Long activityGroupBoardId, ActivityGroupBoardUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroupBoard board = getActivityGroupBoardByIdOrThrow(activityGroupBoardId);
         board.validateAccessPermission(currentMember);
 
@@ -140,7 +138,7 @@ public ActivityGroupBoardUpdateResponseDto updateActivityGroupBoard(Long activit
     }
 
     public Long deleteActivityGroupBoard(Long activityGroupBoardId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroupBoard board = getActivityGroupBoardByIdOrThrow(activityGroupBoardId);
         board.validateAccessPermission(currentMember);
         activityGroupBoardRepository.delete(board);
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
index 9def6f236..1a29b50c0 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
@@ -31,7 +31,7 @@
 import page.clab.api.domain.activityGroup.dto.response.GroupMemberResponseDto;
 import page.clab.api.domain.activityGroup.exception.AlreadyAppliedException;
 import page.clab.api.domain.activityGroup.exception.InvalidCategoryException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -44,7 +44,7 @@
 @Slf4j
 public class ActivityGroupMemberService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final NotificationService notificationService;
 
@@ -69,7 +69,7 @@ public PagedResponseDto<ActivityGroupResponseDto> getActivityGroups(Pageable pag
     @Transactional(readOnly = true)
     public Object getActivityGroup(Long activityGroupId) {
         ActivityGroupDetails details = activityGroupDetailsRepository.fetchActivityGroupDetails(activityGroupId);
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
 
         boolean isOwner = details.getGroupMembers().stream()
                 .anyMatch(groupMember -> groupMember.isOwnerAndLeader(currentMember));
@@ -85,7 +85,7 @@ public Object getActivityGroup(Long activityGroupId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ActivityGroupResponseDto> getMyActivityGroups(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         List<GroupMember> groupMembers = getGroupMemberByMember(currentMember);
 
         List<ActivityGroupResponseDto> activityGroups = groupMembers.stream()
@@ -134,7 +134,7 @@ public PagedResponseDto<GroupMemberResponseDto> getActivityGroupMembers(Long act
 
     @Transactional
     public Long applyActivityGroup(Long activityGroupId, ApplyFormRequestDto formRequestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupByIdOrThrow(activityGroupId);
         activityGroup.validateForApplication();
         if (isGroupMember(activityGroup, currentMember)) {
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupReportService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupReportService.java
index d8cf467e3..95f21cce5 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupReportService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupReportService.java
@@ -14,7 +14,7 @@
 import page.clab.api.domain.activityGroup.dto.request.ActivityGroupReportUpdateRequestDto;
 import page.clab.api.domain.activityGroup.dto.response.ActivityGroupReportResponseDto;
 import page.clab.api.domain.activityGroup.exception.DuplicateReportException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
@@ -27,7 +27,7 @@ public class ActivityGroupReportService {
 
     private final ActivityGroupAdminService activityGroupAdminService;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
@@ -35,7 +35,7 @@ public class ActivityGroupReportService {
 
     @Transactional
     public Long writeReport(ActivityGroupReportRequestDto requestDto) throws PermissionDeniedException, IllegalAccessException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Long activityGroupId = requestDto.getActivityGroupId();
         ActivityGroup activityGroup = activityGroupAdminService.validateAndGetActivityGroupForReporting(activityGroupId, currentMember);
         ActivityGroupReport report = validateReportCreationPermission(requestDto, activityGroup);
@@ -59,7 +59,7 @@ public ActivityGroupReportResponseDto searchReport(Long activityGroupId, Long tu
 
     @Transactional
     public Long updateReport(Long reportId, Long activityGroupId, ActivityGroupReportUpdateRequestDto requestDto) throws PermissionDeniedException, IllegalAccessException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = activityGroupAdminService.getActivityGroupByIdOrThrow(activityGroupId);
         validateReportUpdatePermission(activityGroupId, currentMember, activityGroup);
         ActivityGroupReport report = getReportByIdOrThrow(reportId);
@@ -69,7 +69,7 @@ public Long updateReport(Long reportId, Long activityGroupId, ActivityGroupRepor
     }
 
     public Long deleteReport(Long reportId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroupReport report = validateReportDeletionPermission(reportId, currentMember);
         activityGroupReportRepository.delete(report);
         return report.getId();
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/AttendanceService.java b/src/main/java/page/clab/api/domain/activityGroup/application/AttendanceService.java
index bda17cced..02698ce0d 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/AttendanceService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/AttendanceService.java
@@ -23,7 +23,7 @@
 import page.clab.api.domain.activityGroup.exception.DuplicateAttendanceException;
 import page.clab.api.domain.login.dao.RedisQRKeyRepository;
 import page.clab.api.domain.login.domain.RedisQRKey;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.file.application.FileService;
@@ -43,7 +43,7 @@
 @Slf4j
 public class AttendanceService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final FileService fileService;
 
@@ -63,7 +63,7 @@ public class AttendanceService {
 
     @Transactional
     public String generateAttendanceQRCode(Long activityGroupId) throws IOException, WriterException, PermissionDeniedException, IllegalAccessException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = validateAttendanceQRCodeGeneration(activityGroupId, currentMember);
 
         String nowDateTime = LocalDateTime.now().format(dateTimeFormatter);
@@ -82,7 +82,7 @@ public String generateAttendanceQRCode(Long activityGroupId) throws IOException,
 
     @Transactional
     public Long checkMemberAttendance(AttendanceRequestDto requestDto) throws IllegalAccessException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = validateMemberForAttendance(currentMember, requestDto.getActivityGroupId());
         validateQRCodeData(requestDto.getQRCodeSecretKey());
         Attendance attendance = Attendance.create(currentMember, activityGroup, LocalDate.now());
@@ -91,7 +91,7 @@ public Long checkMemberAttendance(AttendanceRequestDto requestDto) throws Illega
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AttendanceResponseDto> getMyAttendances(Long activityGroupId, Pageable pageable) throws IllegalAccessException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = validateGroupAndMemberForAttendance(activityGroupId, currentMember);
         Page<Attendance> attendances = getAttendanceByMember(activityGroup, currentMember, pageable);
         return new PagedResponseDto<>(attendances.map(AttendanceResponseDto::toDto));
@@ -99,7 +99,7 @@ public PagedResponseDto<AttendanceResponseDto> getMyAttendances(Long activityGro
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AttendanceResponseDto> getGroupAttendances(Long activityGroupId, Pageable pageable) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupWithValidPermissions(activityGroupId, currentMember);
         Page<Attendance> attendances = getAttendanceByActivityGroup(activityGroup, pageable);
         return new PagedResponseDto<>(attendances.map(AttendanceResponseDto::toDto));
@@ -107,7 +107,7 @@ public PagedResponseDto<AttendanceResponseDto> getGroupAttendances(Long activity
 
     @Transactional
     public Long writeAbsentExcuse(AbsentRequestDto requestDto) throws IllegalAccessException, DuplicateAbsentExcuseException {
-        Member absentee = memberService.getMemberByIdOrThrow(requestDto.getAbsenteeId());
+        Member absentee = memberLookupService.getMemberByIdOrThrow(requestDto.getAbsenteeId());
         ActivityGroup activityGroup = getValidActivityGroup(requestDto.getActivityGroupId());
         validateAbsentExcuseConditions(absentee, activityGroup, requestDto.getAbsentDate());
         Absent absent = AbsentRequestDto.toEntity(requestDto, absentee, activityGroup);
@@ -116,7 +116,7 @@ public Long writeAbsentExcuse(AbsentRequestDto requestDto) throws IllegalAccessE
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AbsentResponseDto> getActivityGroupAbsentExcuses(Long activityGroupId, Pageable pageable) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = getActivityGroupWithPermissionCheck(activityGroupId, currentMember);
         Page<Absent> absents = absentRepository.findAllByActivityGroup(activityGroup, pageable);
         return new PagedResponseDto<>(absents.map(AbsentResponseDto::toDto));
diff --git a/src/main/java/page/clab/api/domain/award/application/AwardService.java b/src/main/java/page/clab/api/domain/award/application/AwardService.java
index fadd17df8..cfa5a2260 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardService.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardService.java
@@ -10,7 +10,7 @@
 import page.clab.api.domain.award.dto.request.AwardRequestDto;
 import page.clab.api.domain.award.dto.request.AwardUpdateRequestDto;
 import page.clab.api.domain.award.dto.response.AwardResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
@@ -21,7 +21,7 @@
 @RequiredArgsConstructor
 public class AwardService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
@@ -29,7 +29,7 @@ public class AwardService {
 
     @Transactional
     public Long createAward(AwardRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Award award = AwardRequestDto.toEntity(requestDto, currentMember);
         validationService.checkValid(award);
         return awardRepository.save(award).getId();
@@ -43,14 +43,14 @@ public PagedResponseDto<AwardResponseDto> getAwardsByConditions(String memberId,
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AwardResponseDto> getMyAwards(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Award> awards = getAwardByMember(pageable, currentMember);
         return new PagedResponseDto<>(awards.map(AwardResponseDto::toDto));
     }
 
     @Transactional
     public Long updateAward(Long awardId, AwardUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Award award = getAwardByIdOrThrow(awardId);
         award.validateAccessPermission(currentMember);
         award.update(requestDto);
@@ -59,7 +59,7 @@ public Long updateAward(Long awardId, AwardUpdateRequestDto requestDto) throws P
     }
 
     public Long deleteAward(Long awardId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Award award = getAwardByIdOrThrow(awardId);
         award.validateAccessPermission(currentMember);
         awardRepository.delete(award);
diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index 0abc22dac..1119a5c6d 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -11,7 +11,7 @@
 import page.clab.api.domain.blog.dto.request.BlogUpdateRequestDto;
 import page.clab.api.domain.blog.dto.response.BlogDetailsResponseDto;
 import page.clab.api.domain.blog.dto.response.BlogResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
@@ -22,7 +22,7 @@
 @RequiredArgsConstructor
 public class BlogService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
@@ -30,7 +30,7 @@ public class BlogService {
 
     @Transactional
     public Long createBlog(BlogRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = BlogRequestDto.toEntity(requestDto, currentMember);
         validationService.checkValid(blog);
         return blogRepository.save(blog).getId();
@@ -44,7 +44,7 @@ public PagedResponseDto<BlogResponseDto> getBlogsByConditions(String title, Stri
 
     @Transactional(readOnly = true)
     public BlogDetailsResponseDto getBlogDetails(Long blogId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         boolean isOwner = blog.isOwner(currentMember);
         return BlogDetailsResponseDto.toDto(blog, isOwner);
@@ -52,7 +52,7 @@ public BlogDetailsResponseDto getBlogDetails(Long blogId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Blog> blogs = blogRepository.findAllByIsDeletedTrue(pageable);
         return new PagedResponseDto<>(blogs
                 .map(blog -> BlogDetailsResponseDto.toDto(blog, blog.isOwner(currentMember))));
@@ -60,7 +60,7 @@ public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageabl
 
     @Transactional
     public Long updateBlog(Long blogId, BlogUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         blog.validateAccessPermission(currentMember);
         blog.update(requestDto);
@@ -69,7 +69,7 @@ public Long updateBlog(Long blogId, BlogUpdateRequestDto requestDto) throws Perm
     }
 
     public Long deleteBlog(Long blogId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         blog.validateAccessPermission(currentMember);
         blogRepository.delete(blog);
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardService.java b/src/main/java/page/clab/api/domain/board/application/BoardService.java
index 4c9427fda..8d4b27eaf 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardService.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardService.java
@@ -18,7 +18,7 @@
 import page.clab.api.domain.board.dto.response.BoardListResponseDto;
 import page.clab.api.domain.board.dto.response.BoardMyResponseDto;
 import page.clab.api.domain.comment.dao.CommentRepository;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -36,7 +36,7 @@
 @RequiredArgsConstructor
 public class BoardService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final NotificationService notificationService;
 
@@ -54,7 +54,7 @@ public class BoardService {
 
     @Transactional
     public String createBoard(BoardRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         List<UploadedFile> uploadedFiles = uploadedFileService.getUploadedFilesByUrls(requestDto.getFileUrlList());
         Board board = BoardRequestDto.toEntity(requestDto, currentMember, uploadedFiles);
         board.validateAccessPermissionForCreation(currentMember);
@@ -74,7 +74,7 @@ public PagedResponseDto<BoardListResponseDto> getBoards(Pageable pageable) {
 
     @Transactional(readOnly = true)
     public BoardDetailsResponseDto getBoardDetails(Long boardId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Board board = getBoardByIdOrThrow(boardId);
         boolean hasLikeByMe = checkLikeStatus(board, currentMember);
         boolean isOwner = board.isOwner(currentMember);
@@ -83,7 +83,7 @@ public BoardDetailsResponseDto getBoardDetails(Long boardId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardMyResponseDto> getMyBoards(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Board> boards = getBoardByMember(pageable, currentMember);
         return new PagedResponseDto<>(boards.map(BoardMyResponseDto::toDto));
     }
@@ -96,7 +96,7 @@ public PagedResponseDto<BoardCategoryResponseDto> getBoardsByCategory(BoardCateg
 
     @Transactional
     public String updateBoard(Long boardId, BoardUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Board board = getBoardByIdOrThrow(boardId);
         board.validateAccessPermission(currentMember);
         board.update(requestDto);
@@ -106,7 +106,7 @@ public String updateBoard(Long boardId, BoardUpdateRequestDto requestDto) throws
 
     @Transactional
     public Long toggleLikeStatus(Long boardId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Board board = getBoardByIdOrThrow(boardId);
         Optional<BoardLike> boardLikeOpt = boardLikeRepository.findByBoardIdAndMemberId(board.getId(), currentMember.getId());
         if (boardLikeOpt.isPresent()) {
@@ -128,7 +128,7 @@ public PagedResponseDto<BoardListResponseDto> getDeletedBoards(Pageable pageable
     }
 
     public String deleteBoard(Long boardId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Board board = getBoardByIdOrThrow(boardId);
         board.validateAccessPermission(currentMember);
         boardRepository.delete(board);
diff --git a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
index 352214c4c..900781678 100644
--- a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
+++ b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
@@ -16,7 +16,7 @@
 import page.clab.api.domain.book.dto.response.BookLoanRecordResponseDto;
 import page.clab.api.domain.book.exception.BookAlreadyAppliedForLoanException;
 import page.clab.api.domain.book.exception.MaxBorrowLimitExceededException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -30,7 +30,7 @@ public class BookLoanRecordService {
 
     private final BookService bookService;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final NotificationService notificationService;
 
@@ -43,7 +43,7 @@ public class BookLoanRecordService {
     @Transactional
     public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOptimisticLockingFailureException {
         try {
-            Member borrower = memberService.getCurrentMember();
+            Member borrower = memberLookupService.getCurrentMember();
             borrower.checkLoanSuspension();
 
             validateBorrowLimit(borrower);
@@ -63,7 +63,7 @@ public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOp
 
     @Transactional
     public Long returnBook(BookLoanRecordRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
         book.returnBook(currentMember);
         bookRepository.save(book);
@@ -78,7 +78,7 @@ public Long returnBook(BookLoanRecordRequestDto requestDto) {
 
     @Transactional
     public Long extendBookLoan(BookLoanRecordRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
 
         book.validateCurrentBorrower(currentMember);
@@ -93,7 +93,7 @@ public Long extendBookLoan(BookLoanRecordRequestDto requestDto) {
 
     @Transactional
     public Long approveBookLoan(Long bookLoanRecordId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         BookLoanRecord bookLoanRecord = getBookLoanRecordByIdOrThrow(bookLoanRecordId);
         Book book = bookService.getBookByIdOrThrow(bookLoanRecord.getBook().getId());
 
diff --git a/src/main/java/page/clab/api/domain/comment/application/CommentService.java b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
index f2c579e72..575f5ee78 100644
--- a/src/main/java/page/clab/api/domain/comment/application/CommentService.java
+++ b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
@@ -1,6 +1,5 @@
 package page.clab.api.domain.comment.application;
 
-import java.util.Comparator;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.hibernate.Hibernate;
@@ -19,7 +18,7 @@
 import page.clab.api.domain.comment.dto.response.CommentMyResponseDto;
 import page.clab.api.domain.comment.dto.response.CommentResponseDto;
 import page.clab.api.domain.comment.dto.response.DeletedCommentResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
@@ -27,6 +26,7 @@
 import page.clab.api.global.exception.PermissionDeniedException;
 import page.clab.api.global.validation.ValidationService;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -38,7 +38,7 @@ public class CommentService {
 
     private final BoardService boardService;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final NotificationService notificationService;
 
@@ -57,7 +57,7 @@ public Long createComment(Long parentId, Long boardId, CommentRequestDto request
 
     @Transactional(readOnly = true)
     public PagedResponseDto<CommentResponseDto> getAllComments(Long boardId, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Comment> comments = getCommentByBoardIdAndParentIsNull(boardId, pageable);
         comments.forEach(comment -> {
             Hibernate.initialize(comment.getChildren());
@@ -69,7 +69,7 @@ public PagedResponseDto<CommentResponseDto> getAllComments(Long boardId, Pageabl
 
     @Transactional(readOnly = true)
     public PagedResponseDto<CommentMyResponseDto> getMyComments(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Comment> comments = getCommentByWriter(currentMember, pageable);
         List<CommentMyResponseDto> dtos = comments
                 .map(comment -> toCommentMyResponseDto(comment, currentMember))
@@ -81,14 +81,14 @@ public PagedResponseDto<CommentMyResponseDto> getMyComments(Pageable pageable) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<DeletedCommentResponseDto> getDeletedComments(Long boardId, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Comment> comments = commentRepository.findAllByIsDeletedTrueAndBoardId(boardId, pageable);
         return new PagedResponseDto<>(comments.map(comment -> DeletedCommentResponseDto.toDto(comment, currentMember.getId())));
     }
 
     @Transactional
     public Long updateComment(Long commentId, CommentUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Comment comment = getCommentByIdOrThrow(commentId);
         comment.validateAccessPermission(currentMember);
         comment.update(requestDto);
@@ -98,7 +98,7 @@ public Long updateComment(Long commentId, CommentUpdateRequestDto requestDto) th
     }
 
     public Long deleteComment(Long commentId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Comment comment = getCommentByIdOrThrow(commentId);
         comment.validateAccessPermission(currentMember);
         comment.updateIsDeleted();
@@ -108,7 +108,7 @@ public Long deleteComment(Long commentId) throws PermissionDeniedException {
 
     @Transactional
     public Long toggleLikeStatus(Long commentId) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Comment comment = getCommentByIdOrThrow(commentId);
         Optional<CommentLike> commentLikeOpt = commentLikeRepository.findByCommentIdAndMemberId(comment.getId(), currentMember.getId());
         if (commentLikeOpt.isPresent()) {
@@ -136,7 +136,7 @@ private Page<Comment> getCommentByWriter(Member member, Pageable pageable) {
     }
 
     private Comment createAndStoreComment(Long parentId, Long boardId, CommentRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Board board = boardService.getBoardByIdOrThrow(boardId);
         Comment parent = findParentComment(parentId);
         Comment comment = CommentRequestDto.toEntity(requestDto, board, currentMember, parent);
diff --git a/src/main/java/page/clab/api/domain/donation/application/DonationService.java b/src/main/java/page/clab/api/domain/donation/application/DonationService.java
index 398afb924..5073239e2 100644
--- a/src/main/java/page/clab/api/domain/donation/application/DonationService.java
+++ b/src/main/java/page/clab/api/domain/donation/application/DonationService.java
@@ -10,7 +10,7 @@
 import page.clab.api.domain.donation.dto.request.DonationRequestDto;
 import page.clab.api.domain.donation.dto.request.DonationUpdateRequestDto;
 import page.clab.api.domain.donation.dto.response.DonationResponseDto;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
@@ -23,7 +23,7 @@
 @RequiredArgsConstructor
 public class DonationService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
@@ -31,7 +31,7 @@ public class DonationService {
 
     @Transactional
     public Long createDonation(DonationRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Donation donation = DonationRequestDto.toEntity(requestDto, currentMember);
         validationService.checkValid(donation);
         return donationRepository.save(donation).getId();
@@ -45,7 +45,7 @@ public PagedResponseDto<DonationResponseDto> getDonationsByConditions(String mem
 
     @Transactional(readOnly = true)
     public PagedResponseDto<DonationResponseDto> getMyDonations(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Donation> donations = getDonationsByDonor(currentMember, pageable);
         return new PagedResponseDto<>(donations.map(DonationResponseDto::toDto));
     }
@@ -58,7 +58,7 @@ public PagedResponseDto<DonationResponseDto> getDeletedDonations(Pageable pageab
 
     @Transactional
     public Long updateDonation(Long donationId, DonationUpdateRequestDto donationUpdateRequestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Donation donation = getDonationByIdOrThrow(donationId);
         validateDonationUpdatePermission(currentMember);
         donation.update(donationUpdateRequestDto);
@@ -67,7 +67,7 @@ public Long updateDonation(Long donationId, DonationUpdateRequestDto donationUpd
     }
 
     public Long deleteDonation(Long donationId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Donation donation = getDonationByIdOrThrow(donationId);
         validateDonationUpdatePermission(currentMember);
         donationRepository.delete(donation);
diff --git a/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java b/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
index f372ec533..49cdebda2 100644
--- a/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
+++ b/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
@@ -13,7 +13,7 @@
 import page.clab.api.domain.login.dto.response.AccountLockInfoResponseDto;
 import page.clab.api.domain.login.exception.LoginFaliedException;
 import page.clab.api.domain.login.exception.MemberLockedException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.slack.application.SlackService;
@@ -26,7 +26,7 @@
 @Slf4j
 public class AccountLockInfoService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final SlackService slackService;
 
@@ -42,7 +42,7 @@ public class AccountLockInfoService {
 
     @Transactional
     public Long banMemberById(HttpServletRequest request, String memberId) {
-        Member member = memberService.getMemberById(memberId);
+        Member member = memberLookupService.getMemberById(memberId);
         AccountLockInfo accountLockInfo = ensureAccountLockInfo(member);
         accountLockInfo.banPermanently();
         redisTokenService.deleteRedisTokenByMemberId(memberId);
@@ -52,7 +52,7 @@ public Long banMemberById(HttpServletRequest request, String memberId) {
 
     @Transactional
     public Long unbanMemberById(HttpServletRequest request, String memberId) {
-        Member member = memberService.getMemberById(memberId);
+        Member member = memberLookupService.getMemberById(memberId);
         AccountLockInfo accountLockInfo = ensureAccountLockInfo(member);
         accountLockInfo.unban();
         slackService.sendSecurityAlertNotification(request, SecurityAlertType.MEMBER_UNBANNED, "ID: " + member.getId() + ", Name: " + member.getName());
@@ -76,7 +76,7 @@ public void handleAccountLockInfo(String memberId) throws MemberLockedException,
 
     @Transactional
     public void handleLoginFailure(HttpServletRequest request, String memberId) throws MemberLockedException, LoginFaliedException {
-        Member member = memberService.getMemberById(memberId);
+        Member member = memberLookupService.getMemberById(memberId);
         AccountLockInfo accountLockInfo = ensureAccountLockInfoForMemberId(memberId);
         validateAccountLockStatus(accountLockInfo);
         accountLockInfo.incrementLoginFailCount();
@@ -99,7 +99,10 @@ private AccountLockInfo ensureAccountLockInfo(Member member) {
     }
 
     private AccountLockInfo ensureAccountLockInfoForMemberId(String memberId) throws LoginFaliedException {
-        Member member = memberService.getMemberByIdOrThrowLoginFailed(memberId);
+        Member member = memberLookupService.getMemberById(memberId);
+        if (member == null) {
+            throw new LoginFaliedException();
+        }
         return ensureAccountLockInfo(member);
     }
 
diff --git a/src/main/java/page/clab/api/domain/login/application/LoginService.java b/src/main/java/page/clab/api/domain/login/application/LoginService.java
index e076d494a..f9a243ef0 100644
--- a/src/main/java/page/clab/api/domain/login/application/LoginService.java
+++ b/src/main/java/page/clab/api/domain/login/application/LoginService.java
@@ -20,7 +20,7 @@
 import page.clab.api.domain.login.dto.response.TokenInfo;
 import page.clab.api.domain.login.exception.LoginFaliedException;
 import page.clab.api.domain.login.exception.MemberLockedException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.auth.exception.TokenForgeryException;
 import page.clab.api.global.auth.exception.TokenMisuseException;
@@ -43,7 +43,7 @@ public class LoginService {
 
     private final AccountLockInfoService accountLockInfoService;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final LoginAttemptLogService loginAttemptLogService;
 
@@ -57,7 +57,7 @@ public class LoginService {
     public LoginResult login(HttpServletRequest request, LoginRequestDto requestDto) throws LoginFaliedException, MemberLockedException {
         authenticateAndCheckStatus(request, requestDto);
         logLoginAttempt(request, requestDto.getId(), true);
-        Member loginMember = memberService.getMemberByIdOrThrow(requestDto.getId());
+        Member loginMember = memberLookupService.getMemberByIdOrThrow(requestDto.getId());
         loginMember.updateLastLoginTime();
         return generateLoginResult(loginMember);
     }
@@ -65,7 +65,7 @@ public LoginResult login(HttpServletRequest request, LoginRequestDto requestDto)
     @Transactional
     public LoginResult authenticator(HttpServletRequest request, TwoFactorAuthenticationRequestDto twoFactorAuthenticationRequestDto) throws LoginFaliedException, MemberLockedException {
         String memberId = twoFactorAuthenticationRequestDto.getMemberId();
-        Member loginMember = memberService.getMemberById(memberId);
+        Member loginMember = memberLookupService.getMemberById(memberId);
         String totp = twoFactorAuthenticationRequestDto.getTotp();
 
         accountLockInfoService.handleAccountLockInfo(memberId);
@@ -82,7 +82,7 @@ public String resetAuthenticator(String memberId) {
     }
 
     public String revoke(String memberId) {
-        Member member = memberService.getMemberById(memberId);
+        Member member = memberLookupService.getMemberById(memberId);
         redisTokenService.deleteRedisTokenByMemberId(memberId);
         return member.getId();
     }
@@ -165,7 +165,7 @@ private void sendAdminLoginNotification(HttpServletRequest request, Member login
 
     private void validateMemberExistence(Authentication authentication) {
         String id = authentication.getName();
-        Member member = memberService.getMemberById(id);
+        Member member = memberLookupService.getMemberById(id);
         if (member == null) {
             throw new TokenForgeryException("존재하지 않는 회원에 대한 토큰입니다.");
         }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
new file mode 100644
index 000000000..a578dad11
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -0,0 +1,28 @@
+package page.clab.api.domain.member.application;
+
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.response.MemberResponseDto;
+
+import java.util.List;
+
+public interface MemberLookupService {
+
+    boolean existsMemberById(String memberId);
+
+    Member getMemberById(String memberId);
+
+    Member getMemberByIdOrThrow(String memberId);
+
+    Member getMemberByEmail(String email);
+
+    Member getCurrentMember();
+
+    List<MemberResponseDto> getMembers();
+
+    List<Member> findAllMembers();
+
+    List<Member> getAdmins();
+
+    List<Member> getSuperAdmins();
+
+}
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
new file mode 100644
index 000000000..07e4bf5d3
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -0,0 +1,78 @@
+package page.clab.api.domain.member.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import page.clab.api.domain.member.dao.MemberRepository;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.response.MemberResponseDto;
+import page.clab.api.global.auth.util.AuthUtil;
+import page.clab.api.global.exception.NotFoundException;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class MemberLookupServiceImpl implements MemberLookupService {
+
+    private final MemberRepository memberRepository;
+
+    @Override
+    public boolean existsMemberById(String memberId) {
+        return memberRepository.existsById(memberId);
+    }
+
+    @Override
+    public Member getMemberById(String memberId) {
+        return memberRepository.findById(memberId)
+                .orElse(null);
+    }
+
+    @Override
+    public Member getMemberByIdOrThrow(String memberId) {
+        return memberRepository.findById(memberId)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
+    @Override
+    public Member getMemberByEmail(String email) {
+        return memberRepository.findByEmail(email)
+                .orElseThrow(() -> new NotFoundException("해당 이메일을 사용하는 멤버가 없습니다."));
+    }
+
+    @Override
+    public Member getCurrentMember() {
+        String memberId = AuthUtil.getAuthenticationInfoMemberId();
+        return memberRepository.findById(memberId)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
+    @Override
+    public List<MemberResponseDto> getMembers() {
+        List<Member> members = memberRepository.findAll();
+        return members.stream()
+                .map(MemberResponseDto::toDto)
+                .toList();
+    }
+
+    @Override
+    public List<Member> findAllMembers() {
+        return memberRepository.findAll();
+    }
+
+    @Override
+    public List<Member> getAdmins() {
+        return memberRepository.findAll()
+                .stream()
+                .filter(Member::isAdminRole)
+                .toList();
+    }
+
+    @Override
+    public List<Member> getSuperAdmins() {
+        return memberRepository.findAll()
+                .stream()
+                .filter(Member::isSuperAdminRole)
+                .toList();
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberService.java b/src/main/java/page/clab/api/domain/member/application/MemberService.java
index 4de503bcb..0efd76d84 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberService.java
@@ -2,8 +2,6 @@
 
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.security.crypto.password.PasswordEncoder;
@@ -12,7 +10,6 @@
 import page.clab.api.domain.application.dao.ApplicationRepository;
 import page.clab.api.domain.application.domain.Application;
 import page.clab.api.domain.application.exception.NotApprovedApplicationException;
-import page.clab.api.domain.login.exception.LoginFaliedException;
 import page.clab.api.domain.member.dao.MemberRepository;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.request.MemberRequestDto;
@@ -27,7 +24,6 @@
 import page.clab.api.domain.position.dao.PositionRepository;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
-import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.email.application.EmailService;
 import page.clab.api.global.common.file.application.FileService;
@@ -49,11 +45,13 @@
 @Slf4j
 public class MemberService {
 
+    private final MemberLookupService memberLookupService;
+
     private final VerificationService verificationService;
 
     private final ValidationService validationService;
 
-    private EmailService emailService;
+    private final EmailService emailService;
 
     private final ApplicationRepository applicationRepository;
 
@@ -63,17 +61,7 @@ public class MemberService {
 
     private final PasswordEncoder passwordEncoder;
 
-    private  FileService fileService;
-
-    @Autowired
-    public void setEmailService(@Lazy EmailService emailService) {
-        this.emailService = emailService;
-    }
-
-    @Autowired
-    public void setFileServie(@Lazy FileService fileService) {
-        this.fileService = fileService;
-    }
+    private final FileService fileService;
 
     @Transactional
     public String createMember(MemberRequestDto requestDto) {
@@ -100,13 +88,6 @@ public String createMemberByRecruitmentId(Long recruitmentId, String memberId) {
         return createMemberFromApplication(application);
     }
 
-    public List<MemberResponseDto> getMembers() {
-        List<Member> members = memberRepository.findAll();
-        return members.stream()
-                .map(MemberResponseDto::toDto)
-                .toList();
-    }
-
     @Transactional(readOnly = true)
     public PagedResponseDto<MemberResponseDto> getMembersByConditions(String id, String name, Pageable pageable) {
         Page<Member> members = memberRepository.findByConditions(id, name, pageable);
@@ -115,7 +96,7 @@ public PagedResponseDto<MemberResponseDto> getMembersByConditions(String id, Str
 
     @Transactional(readOnly = true)
     public MyProfileResponseDto getMyProfile() {
-        Member currentMember = getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         return MyProfileResponseDto.toDto(currentMember);
     }
 
@@ -127,8 +108,8 @@ public PagedResponseDto<MemberBirthdayResponseDto> getBirthdaysThisMonth(int mon
 
     @Transactional
     public String updateMemberInfo(String memberId, MemberUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = getCurrentMember();
-        Member member = getMemberByIdOrThrow(memberId);
+        Member currentMember = memberLookupService.getCurrentMember();
+        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         member.validateAccessPermission(currentMember);
         updateMember(requestDto, member);
         validationService.checkValid(member);
@@ -146,47 +127,17 @@ public String requestResetMemberPassword(MemberResetPasswordRequestDto requestDt
 
     @Transactional
     public String verifyResetMemberPassword(VerificationRequestDto requestDto) {
-        Member member = getMemberByIdOrThrow(requestDto.getMemberId());
+        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getMemberId());
         Verification verification = verificationService.validateVerificationCode(requestDto, member);
         updateMemberPasswordWithVerificationCode(verification.getVerificationCode(), member);
         return member.getId();
     }
 
-    public Member getMemberById(String memberId) {
-        return memberRepository.findById(memberId)
-                .orElse(null);
-    }
-
-    public Member getMemberByIdOrThrow(String memberId) {
-        return memberRepository.findById(memberId)
-                .orElseThrow(() -> new NotFoundException("해당 멤버가 없습니다."));
-    }
-
     private Application getApplicationByRecruitmentIdAndStudentIdOrThrow(Long recruitmentId, String memberId) {
         return applicationRepository.findByRecruitmentIdAndStudentId(recruitmentId, memberId)
                 .orElseThrow(() -> new NotFoundException("존재하지 않는 지원서입니다."));
     }
 
-    public Member getMemberByIdOrThrowLoginFailed(String memberId) throws LoginFaliedException {
-        return memberRepository.findById(memberId)
-                .orElseThrow(LoginFaliedException::new);
-    }
-
-    public Member getCurrentMember() {
-        String memberId = AuthUtil.getAuthenticationInfoMemberId();
-        return memberRepository.findById(memberId)
-                .orElseThrow(() -> new NotFoundException("해당 멤버가 없습니다."));
-    }
-
-    public Member getMemberByEmail(String email) {
-        return (Member)memberRepository.findByEmail(email)
-                .orElseThrow(() -> new NotFoundException("해당 이메일을 사용하는 멤버가 없습니다."));
-    }
-
-    public List<Member> findAll() {
-        return memberRepository.findAll();
-    }
-
     private void setupMemberPassword(Member member) {
         if (member.getPassword().isEmpty()) {
             setRandomPasswordAndSendEmail(member);
@@ -246,7 +197,7 @@ public void createPositionByMember(Member member) {
     }
 
     private Member validateResetPasswordRequest(MemberResetPasswordRequestDto requestDto) {
-        Member member = getMemberByIdOrThrow(requestDto.getId());
+        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getId());
         if (!member.isSameName(requestDto.getName()) || !member.isSameEmail(requestDto.getEmail())) {
             throw new InvalidInformationException("올바르지 않은 정보입니다.");
         }
@@ -267,18 +218,4 @@ private void updateMember(MemberUpdateRequestDto requestDto, Member member) thro
         }
     }
 
-    public List<Member> getAdmins() {
-        return memberRepository.findAll()
-                .stream()
-                .filter(Member::isAdminRole)
-                .toList();
-    }
-
-    public List<Member> getSuperAdmins() {
-        return memberRepository.findAll()
-                .stream()
-                .filter(Member::isSuperAdminRole)
-                .toList();
-    }
-
 }
diff --git a/src/main/java/page/clab/api/domain/member/dao/MemberRepository.java b/src/main/java/page/clab/api/domain/member/dao/MemberRepository.java
index 2a24bdbf3..4f5696d10 100644
--- a/src/main/java/page/clab/api/domain/member/dao/MemberRepository.java
+++ b/src/main/java/page/clab/api/domain/member/dao/MemberRepository.java
@@ -16,7 +16,7 @@ public interface MemberRepository extends JpaRepository<Member, String>, MemberR
 
     boolean existsByEmail(String email);
 
-    Optional<Object> findByEmail(String email);
+    Optional<Member> findByEmail(String email);
 
     Page<Member> findAllByOrderByCreatedAtDesc(Pageable pageable);
 
diff --git a/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java b/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
index 292930d26..50466da4c 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
@@ -5,7 +5,7 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.membershipFee.dao.MembershipFeeRepository;
 import page.clab.api.domain.membershipFee.domain.MembershipFee;
@@ -23,7 +23,7 @@
 @RequiredArgsConstructor
 public class MembershipFeeService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final NotificationService notificationService;
 
@@ -33,7 +33,7 @@ public class MembershipFeeService {
 
     @Transactional
     public Long createMembershipFee(MembershipFeeRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         MembershipFee membershipFee = MembershipFeeRequestDto.toEntity(requestDto, currentMember);
         validationService.checkValid(membershipFee);
         notificationService.sendNotificationToAdmins("새로운 회비 내역이 등록되었습니다.");
@@ -42,7 +42,7 @@ public Long createMembershipFee(MembershipFeeRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<MembershipFeeResponseDto> getMembershipFeesByConditions(String memberId, String memberName, String category, MembershipFeeStatus status, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         boolean isAdminOrSuper = currentMember.isAdminRole();
         Page<MembershipFee> membershipFeesPage = membershipFeeRepository.findByConditions(memberId, memberName, category, status, pageable);
         return new PagedResponseDto<>(membershipFeesPage.map(membershipFee -> MembershipFeeResponseDto.toDto(membershipFee, isAdminOrSuper)));
@@ -50,7 +50,7 @@ public PagedResponseDto<MembershipFeeResponseDto> getMembershipFeesByConditions(
 
     @Transactional(readOnly = true)
     public PagedResponseDto<MembershipFeeResponseDto> getDeletedMembershipFees(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         boolean isAdminOrSuper = currentMember.isAdminRole();
         Page<MembershipFee> membershipFees = membershipFeeRepository.findAllByIsDeletedTrue(pageable);
         return new PagedResponseDto<>(membershipFees.map(membershipFee -> MembershipFeeResponseDto.toDto(membershipFee, isAdminOrSuper)));
@@ -58,7 +58,7 @@ public PagedResponseDto<MembershipFeeResponseDto> getDeletedMembershipFees(Pagea
 
     @Transactional
     public Long updateMembershipFee(Long membershipFeeId, MembershipFeeUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         MembershipFee membershipFee = getMembershipFeeByIdOrThrow(membershipFeeId);
         membershipFee.validateAccessPermission(currentMember);
         membershipFee.update(requestDto);
@@ -67,7 +67,7 @@ public Long updateMembershipFee(Long membershipFeeId, MembershipFeeUpdateRequest
     }
 
     public Long deleteMembershipFee(Long membershipFeeId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         MembershipFee membershipFee = getMembershipFeeByIdOrThrow(membershipFeeId);
         membershipFee.validateAccessPermission(currentMember);
         membershipFeeRepository.delete(membershipFee);
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
index 607c2fccc..bfc98daba 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
@@ -1,13 +1,11 @@
 package page.clab.api.domain.notification.application;
 
 import lombok.RequiredArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.dao.NotificationRepository;
 import page.clab.api.domain.notification.domain.Notification;
@@ -25,20 +23,15 @@
 @RequiredArgsConstructor
 public class NotificationService {
 
-    private MemberService memberService;
+    private MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
     private final NotificationRepository notificationRepository;
 
-    @Autowired
-    public void setMemberService(@Lazy MemberService memberService) {
-        this.memberService = memberService;
-    }
-
     @Transactional
     public Long createNotification(NotificationRequestDto requestDto) {
-        Member member = memberService.getMemberByIdOrThrow(requestDto.getMemberId());
+        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getMemberId());
         Notification notification = NotificationRequestDto.toEntity(requestDto, member);
         validationService.checkValid(notification);
         return notificationRepository.save(notification).getId();
@@ -46,7 +39,7 @@ public Long createNotification(NotificationRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<NotificationResponseDto> getNotifications(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Notification> notifications = getNotificationByMember(currentMember, pageable);
         return new PagedResponseDto<>(notifications.map(NotificationResponseDto::toDto));
     }
@@ -58,7 +51,7 @@ public PagedResponseDto<NotificationResponseDto> getDeletedNotifications(Pageabl
     }
 
     public Long deleteNotification(Long notificationId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Notification notification = getNotificationByIdOrThrow(notificationId);
         notification.validateAccessPermission(currentMember);
         notificationRepository.delete(notification);
@@ -66,7 +59,7 @@ public Long deleteNotification(Long notificationId) throws PermissionDeniedExcep
     }
 
     public void sendNotificationToAllMembers(String content) {
-        List<Notification> notifications = memberService.findAll().stream()
+        List<Notification> notifications = memberLookupService.findAllMembers().stream()
                 .map(member -> Notification.create(member, content))
                 .toList();
         notificationRepository.saveAll(notifications);
@@ -85,17 +78,17 @@ public void sendNotificationToMembers(List<Member> members, String content) {
     }
 
     public void sendNotificationToMember(String memberId, String content) {
-        Member member = memberService.getMemberByIdOrThrow(memberId);
+        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         Notification notification = Notification.create(member, content);
         notificationRepository.save(notification);
     }
 
     public void sendNotificationToAdmins(String content) {
-        sendNotificationToSpecificRole(memberService::getAdmins, content);
+        sendNotificationToSpecificRole(memberLookupService::getAdmins, content);
     }
 
     public void sendNotificationToSuperAdmins(String content) {
-        sendNotificationToSpecificRole(memberService::getSuperAdmins, content);
+        sendNotificationToSpecificRole(memberLookupService::getSuperAdmins, content);
     }
 
     private void sendNotificationToSpecificRole(Supplier<List<Member>> memberSupplier, String content) {
diff --git a/src/main/java/page/clab/api/domain/position/application/PositionService.java b/src/main/java/page/clab/api/domain/position/application/PositionService.java
index 8459720d4..22eb51f6a 100644
--- a/src/main/java/page/clab/api/domain/position/application/PositionService.java
+++ b/src/main/java/page/clab/api/domain/position/application/PositionService.java
@@ -5,7 +5,7 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.position.dao.PositionRepository;
 import page.clab.api.domain.position.domain.Position;
@@ -22,13 +22,13 @@
 @RequiredArgsConstructor
 public class PositionService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final PositionRepository positionRepository;
 
     @Transactional
     public Long createPosition(PositionRequestDto requestDto) {
-        Member member = memberService.getMemberByIdOrThrow(requestDto.getMemberId());
+        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getMemberId());
         return positionRepository.findByMemberAndYearAndPositionType(member, requestDto.getYear(), requestDto.getPositionType())
                 .map(Position::getId)
                 .orElseGet(() -> {
@@ -45,7 +45,7 @@ public PagedResponseDto<PositionResponseDto> getPositionsByConditions(String yea
 
     @Transactional(readOnly = true)
     public PositionMyResponseDto getMyPositionsByYear(String year) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         List<Position> positions = getPositionsByMemberAndYear(currentMember, year);
         if (positions.isEmpty()) {
             throw new NotFoundException("해당 멤버의 " + year + "년도 직책이 존재하지 않습니다.");
diff --git a/src/main/java/page/clab/api/domain/review/application/ReviewService.java b/src/main/java/page/clab/api/domain/review/application/ReviewService.java
index 28d593eb2..cf3fadd3d 100644
--- a/src/main/java/page/clab/api/domain/review/application/ReviewService.java
+++ b/src/main/java/page/clab/api/domain/review/application/ReviewService.java
@@ -11,7 +11,7 @@
 import page.clab.api.domain.activityGroup.domain.ActivityGroupRole;
 import page.clab.api.domain.activityGroup.domain.GroupMember;
 import page.clab.api.domain.activityGroup.exception.ActivityGroupNotFinishedException;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.domain.review.dao.ReviewRepository;
@@ -30,7 +30,7 @@
 @Slf4j
 public class ReviewService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ActivityGroupMemberService activityGroupMemberService;
 
@@ -42,7 +42,7 @@ public class ReviewService {
 
     @Transactional
     public Long createReview(ReviewRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = activityGroupMemberService.getActivityGroupByIdOrThrow(requestDto.getActivityGroupId());
         validateReviewCreationPermission(activityGroup, currentMember);
         Review review = ReviewRequestDto.toEntity(requestDto, currentMember, activityGroup);
@@ -53,28 +53,28 @@ public Long createReview(ReviewRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ReviewResponseDto> getReviewsByConditions(String memberId, String memberName, Long activityId, Boolean isPublic, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Review> reviews = reviewRepository.findByConditions(memberId, memberName, activityId, isPublic, pageable);
         return new PagedResponseDto<>(reviews.map(review -> ReviewResponseDto.toDto(review, currentMember)));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ReviewResponseDto> getMyReviews(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Review> reviews = getReviewByMember(currentMember, pageable);
         return new PagedResponseDto<>(reviews.map(review -> ReviewResponseDto.toDto(review, currentMember)));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ReviewResponseDto> getDeletedReviews(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Review> reviews = reviewRepository.findAllByIsDeletedTrue(pageable);
         return new PagedResponseDto<>(reviews.map(review -> ReviewResponseDto.toDto(review, currentMember)));
     }
 
     @Transactional
     public Long updateReview(Long reviewId, ReviewUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Review review = getReviewByIdOrThrow(reviewId);
         review.validateAccessPermission(currentMember);
         review.update(requestDto);
@@ -83,7 +83,7 @@ public Long updateReview(Long reviewId, ReviewUpdateRequestDto requestDto) throw
     }
 
     public Long deleteReview(Long reviewId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Review review = getReviewByIdOrThrow(reviewId);
         review.validateAccessPermission(currentMember);
         reviewRepository.delete(review);
diff --git a/src/main/java/page/clab/api/domain/schedule/application/ScheduleService.java b/src/main/java/page/clab/api/domain/schedule/application/ScheduleService.java
index 87353be65..a1a5fc133 100644
--- a/src/main/java/page/clab/api/domain/schedule/application/ScheduleService.java
+++ b/src/main/java/page/clab/api/domain/schedule/application/ScheduleService.java
@@ -11,7 +11,7 @@
 import page.clab.api.domain.activityGroup.domain.ActivityGroup;
 import page.clab.api.domain.activityGroup.domain.ActivityGroupRole;
 import page.clab.api.domain.activityGroup.domain.GroupMember;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.schedule.dao.ScheduleRepository;
 import page.clab.api.domain.schedule.domain.Schedule;
@@ -34,7 +34,7 @@
 @Slf4j
 public class ScheduleService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ActivityGroupMemberService activityGroupMemberService;
 
@@ -44,7 +44,7 @@ public class ScheduleService {
 
     @Transactional
     public Long createSchedule(ScheduleRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         ActivityGroup activityGroup = resolveActivityGroupForSchedule(requestDto, currentMember);
         Schedule schedule = ScheduleRequestDto.toEntity(requestDto, currentMember, activityGroup);
         schedule.validateAccessPermissionForCreation(currentMember);
@@ -53,7 +53,7 @@ public Long createSchedule(ScheduleRequestDto requestDto) throws PermissionDenie
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ScheduleResponseDto> getSchedulesWithinDateRange(LocalDate startDate, LocalDate endDate, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         List<GroupMember> groupMembers = activityGroupMemberService.getGroupMemberByMember(currentMember);
         List<ActivityGroup> myGroups = getMyActivityGroups(groupMembers);
         Page<Schedule> schedules = scheduleRepository.findByDateRangeAndMember(startDate, endDate, myGroups, pageable);
@@ -71,7 +71,7 @@ public ScheduleCollectResponseDto getCollectSchedules() {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<ScheduleResponseDto> getActivitySchedules(LocalDate startDate, LocalDate endDate, Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Schedule> schedules = scheduleRepository.findActivitySchedulesByDateRangeAndMember(startDate, endDate, currentMember, pageable);
         return new PagedResponseDto<>(schedules.map(ScheduleResponseDto::toDto));
     }
@@ -83,7 +83,7 @@ public PagedResponseDto<ScheduleResponseDto> getDeletedSchedules(Pageable pageab
     }
 
     public Long deleteSchedule(Long scheduleId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Schedule schedule = getScheduleById(scheduleId);
         schedule.validateAccessPermission(currentMember);
         scheduleRepository.delete(schedule);
diff --git a/src/main/java/page/clab/api/domain/sharedAccount/application/SharedAccountUsageService.java b/src/main/java/page/clab/api/domain/sharedAccount/application/SharedAccountUsageService.java
index 9ec4b30d5..7c45b6155 100644
--- a/src/main/java/page/clab/api/domain/sharedAccount/application/SharedAccountUsageService.java
+++ b/src/main/java/page/clab/api/domain/sharedAccount/application/SharedAccountUsageService.java
@@ -9,7 +9,7 @@
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.sharedAccount.dao.SharedAccountUsageRepository;
 import page.clab.api.domain.sharedAccount.domain.SharedAccount;
@@ -33,7 +33,7 @@ public class SharedAccountUsageService {
 
     private final SharedAccountService sharedAccountService;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final SharedAccountUsageRepository sharedAccountUsageRepository;
 
@@ -130,7 +130,7 @@ private SharedAccountUsage prepareSharedAccountUsage(SharedAccountUsageRequestDt
         LocalDateTime startTime = sharedAccountUsageRequestDto.getStartTime() != null ? sharedAccountUsageRequestDto.getStartTime() : currentTime;
         LocalDateTime endTime = sharedAccountUsageRequestDto.getEndTime();
 
-        String memberId = memberService.getCurrentMember().getId();
+        String memberId = memberLookupService.getCurrentMember().getId();
         SharedAccountUsage sharedAccountUsage = SharedAccountUsage.create(sharedAccount, memberId, startTime, endTime);
 
         sharedAccountUsage.validateUsageTimes(currentTime);
@@ -166,7 +166,7 @@ private void validateReservedUsages(Long sharedAccountId, LocalDateTime startTim
     }
 
     private void updateUsageStatus(SharedAccountUsage sharedAccountUsage, SharedAccountUsageStatus status) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         sharedAccountUsage.updateStatus(status, currentMember);
         sharedAccountUsageRepository.save(sharedAccountUsage);
         sharedAccountUsage.getSharedAccount().updateIsInUse(false);
diff --git a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
index 76bd74959..7c094dd6a 100644
--- a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
+++ b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
@@ -5,7 +5,7 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.workExperience.dao.WorkExperienceRepository;
 import page.clab.api.domain.workExperience.domain.WorkExperience;
@@ -21,7 +21,7 @@
 @RequiredArgsConstructor
 public class WorkExperienceService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 
@@ -29,7 +29,7 @@ public class WorkExperienceService {
 
     @Transactional
     public Long createWorkExperience(WorkExperienceRequestDto requestDto) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         WorkExperience workExperience = WorkExperienceRequestDto.toEntity(requestDto, currentMember);
         validationService.checkValid(workExperience);
         return workExperienceRepository.save(workExperience).getId();
@@ -37,14 +37,14 @@ public Long createWorkExperience(WorkExperienceRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<WorkExperienceResponseDto> getMyWorkExperience(Pageable pageable) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<WorkExperience> workExperiences = workExperienceRepository.findAllByMember(currentMember, pageable);
         return new PagedResponseDto<>(workExperiences.map(WorkExperienceResponseDto::toDto));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<WorkExperienceResponseDto> getWorkExperiencesByConditions(String memberId, Pageable pageable) {
-        Member member = memberService.getMemberByIdOrThrow(memberId);
+        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         Page<WorkExperience> workExperiences = workExperienceRepository.findAllByMember(member, pageable);
         return new PagedResponseDto<>(workExperiences.map(WorkExperienceResponseDto::toDto));
     }
@@ -57,7 +57,7 @@ public PagedResponseDto<WorkExperienceResponseDto> getDeletedWorkExperiences(Pag
 
     @Transactional
     public Long updateWorkExperience(Long workExperienceId, WorkExperienceUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         WorkExperience workExperience = getWorkExperienceByIdOrThrow(workExperienceId);
         workExperience.validateAccessPermission(currentMember);
         workExperience.update(requestDto);
@@ -66,7 +66,7 @@ public Long updateWorkExperience(Long workExperienceId, WorkExperienceUpdateRequ
     }
 
     public Long deleteWorkExperience(Long workExperienceId) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         WorkExperience workExperience = getWorkExperienceByIdOrThrow(workExperienceId);
         workExperience.validateAccessPermission(currentMember);
         workExperienceRepository.deleteById(workExperienceId);
diff --git a/src/main/java/page/clab/api/global/common/email/application/EmailService.java b/src/main/java/page/clab/api/global/common/email/application/EmailService.java
index 2532e4d02..d0a133fe1 100644
--- a/src/main/java/page/clab/api/global/common/email/application/EmailService.java
+++ b/src/main/java/page/clab/api/global/common/email/application/EmailService.java
@@ -9,7 +9,7 @@
 import org.springframework.web.multipart.MultipartFile;
 import org.thymeleaf.context.Context;
 import org.thymeleaf.spring6.SpringTemplateEngine;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.global.common.email.domain.EmailTemplateType;
@@ -28,7 +28,7 @@
 @Slf4j
 public class EmailService {
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final SpringTemplateEngine springTemplateEngine;
 
@@ -46,7 +46,7 @@ public List<String> broadcastEmail(EmailDto emailDto, List<MultipartFile> multip
 
         emailDto.getTo().parallelStream().forEach(address -> {
             try {
-                Member recipient = memberService.getMemberByEmail(address);
+                Member recipient = memberLookupService.getMemberByEmail(address);
                 String emailContent = generateEmailContent(emailDto, recipient.getName());
                 emailAsyncService.sendEmailAsync(address, emailDto.getSubject(), emailContent, convertedFiles, emailDto.getEmailTemplateType());
                 successfulAddresses.add(address);
@@ -61,7 +61,7 @@ public List<String> broadcastEmailToAllMember(EmailDto emailDto, List<MultipartF
         List<File> convertedFiles = multipartFiles != null && !multipartFiles.isEmpty() ?
                 convertMultipartFiles(multipartFiles) : null;
 
-        List<MemberResponseDto> memberList = memberService.getMembers();
+        List<MemberResponseDto> memberList = memberLookupService.getMembers();
 
         List<String> successfulEmails = Collections.synchronizedList(new ArrayList<>());
 
diff --git a/src/main/java/page/clab/api/global/common/file/application/FileService.java b/src/main/java/page/clab/api/global/common/file/application/FileService.java
index 77e3142d2..edc32625b 100644
--- a/src/main/java/page/clab/api/global/common/file/application/FileService.java
+++ b/src/main/java/page/clab/api/global/common/file/application/FileService.java
@@ -10,7 +10,7 @@
 import page.clab.api.domain.activityGroup.dao.ActivityGroupRepository;
 import page.clab.api.domain.activityGroup.dao.GroupMemberRepository;
 import page.clab.api.domain.member.application.MemberCloudService;
-import page.clab.api.domain.member.application.MemberService;
+import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.file.domain.UploadedFile;
 import page.clab.api.global.common.file.dto.request.DeleteFileRequestDto;
@@ -35,7 +35,7 @@ public class FileService {
 
     private final FileHandler fileHandler;
 
-    private final MemberService memberService;
+    private final MemberLookupService memberLookupService;
 
     private final MemberCloudService memberCloudService;
 
@@ -57,7 +57,7 @@ public class FileService {
     private String maxFileSize;
 
     public String saveQRCodeImage(byte[] QRCodeImage, String path, long storagePeriod, String nowDateTime) throws IOException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         String extension = "png";
         String originalFileName = path.replace(File.separator, "-") + nowDateTime;
         String saveFilename = fileHandler.makeFileName(extension);
@@ -80,7 +80,7 @@ public List<UploadedFileResponseDto> saveFiles(List<MultipartFile> multipartFile
     }
 
     public UploadedFileResponseDto saveFile(MultipartFile multipartFile, String path, long storagePeriod) throws IOException, PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
 
         validatePathVariable(path);
         validateMemberCloudUsage(multipartFile, path);
@@ -96,7 +96,7 @@ public UploadedFileResponseDto saveFile(MultipartFile multipartFile, String path
     }
 
     public String deleteFile(DeleteFileRequestDto deleteFileRequestDto) throws PermissionDeniedException {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         UploadedFile uploadedFile = uploadedFileService.getUploadedFileByUrl(deleteFileRequestDto.getUrl());
         String filePath = uploadedFile.getSavedPath();
         File storedFile = new File(filePath);
@@ -109,7 +109,7 @@ public String deleteFile(DeleteFileRequestDto deleteFileRequestDto) throws Permi
     }
 
     public String buildPath(String baseDirectory, Long... additionalSegments) {
-        Member currentMember = memberService.getCurrentMember();
+        Member currentMember = memberLookupService.getCurrentMember();
         StringBuilder pathBuilder = new StringBuilder(baseDirectory);
         for (Long segment : additionalSegments) {
             pathBuilder.append(File.separator).append(segment);
@@ -123,7 +123,7 @@ public void validatePathVariable(String path) throws AssignmentFileUploadFailExc
             Long activityGroupId = Long.parseLong(path.split(Pattern.quote(File.separator))[1]);
             Long activityGroupBoardId = Long.parseLong(path.split(Pattern.quote(File.separator))[2]);
             String memberId = path.split(Pattern.quote(File.separator))[3];
-            Member assignmentWriter = memberService.getMemberById(memberId);
+            Member assignmentWriter = memberLookupService.getMemberById(memberId);
             if (!activityGroupRepository.existsById(activityGroupId)) {
                 throw new AssignmentFileUploadFailException("해당 활동은 존재하지 않습니다.");
             }

From 67a712615508abc17e69c4d9bbb8f78550532000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 11:46:35 +0900
Subject: [PATCH 14/47] =?UTF-8?q?feat(Member):=20=EB=8F=84=EB=A9=94?=
 =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=B2=A4=ED=8A=B8,=20=EA=B3=B5=ED=86=B5?=
 =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=EB=84=88,=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?=
 =?UTF-8?q?=20=EB=94=94=EC=8A=A4=ED=8C=A8=EC=B2=98=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../member/event/MemberDeletedEvent.java      | 16 +++++++++++++
 .../member/event/MemberEventDispatcher.java   | 24 +++++++++++++++++++
 .../member/event/MemberEventProcessor.java    |  9 +++++++
 .../event/MemberEventProcessorRegistry.java   | 22 +++++++++++++++++
 .../member/event/MemberUpdatedEvent.java      | 16 +++++++++++++
 5 files changed, 87 insertions(+)
 create mode 100644 src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
 create mode 100644 src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
 create mode 100644 src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
 create mode 100644 src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
 create mode 100644 src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java

diff --git a/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
new file mode 100644
index 000000000..c9d40fa5e
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
@@ -0,0 +1,16 @@
+package page.clab.api.domain.member.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+public class MemberDeletedEvent extends ApplicationEvent {
+
+    private final String memberId;
+
+    public MemberDeletedEvent(Object source, String memberId) {
+        super(source);
+        this.memberId = memberId;
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
new file mode 100644
index 000000000..76746915d
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
@@ -0,0 +1,24 @@
+package page.clab.api.domain.member.event;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class MemberEventDispatcher {
+
+    private final List<MemberEventProcessor> processors;
+
+    @EventListener
+    public void handleMemberDeletedEvent(MemberDeletedEvent event) {
+        processors.forEach(processor -> processor.processMemberDeleted(event.getMemberId()));
+    }
+
+    @EventListener
+    public void handleMemberUpdatedEvent(MemberUpdatedEvent event) {
+        processors.forEach(processor -> processor.processMemberUpdated(event.getMemberId()));
+    }
+}
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
new file mode 100644
index 000000000..f3d34ca62
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
@@ -0,0 +1,9 @@
+package page.clab.api.domain.member.event;
+
+public interface MemberEventProcessor {
+
+    void processMemberDeleted(String memberId);
+
+    void processMemberUpdated(String memberId);
+
+}
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
new file mode 100644
index 000000000..90f9098a5
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
@@ -0,0 +1,22 @@
+package page.clab.api.domain.member.event;
+
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Component
+public class MemberEventProcessorRegistry {
+
+    private final List<MemberEventProcessor> processors = new ArrayList<>();
+
+    public void registerProcessor(MemberEventProcessor processor) {
+        processors.add(processor);
+    }
+
+    public List<MemberEventProcessor> getProcessors() {
+        return Collections.unmodifiableList(processors);
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
new file mode 100644
index 000000000..b1bc7b142
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
@@ -0,0 +1,16 @@
+package page.clab.api.domain.member.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+public class MemberUpdatedEvent extends ApplicationEvent {
+
+    private final String memberId;
+
+    public MemberUpdatedEvent(Object source, String memberId) {
+        super(source);
+        this.memberId = memberId;
+    }
+
+}

From de8b01ead35dea3e4472bb398d13f000b6057aea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 12:05:25 +0900
Subject: [PATCH 15/47] =?UTF-8?q?feat(Member):=20=EC=88=98=EC=A0=95,=20?=
 =?UTF-8?q?=EC=82=AD=EC=A0=9C=EC=8B=9C=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?=
 =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=EA=B0=80=20=EB=B0=9C=EC=83=9D?=
 =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/member/api/MemberController.java   | 20 +++++++++++++------
 .../member/application/MemberService.java     | 16 ++++++++++++++-
 .../clab/api/domain/member/domain/Member.java |  4 ++++
 3 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/member/api/MemberController.java b/src/main/java/page/clab/api/domain/member/api/MemberController.java
index 2a6c2b1f7..c046de494 100644
--- a/src/main/java/page/clab/api/domain/member/api/MemberController.java
+++ b/src/main/java/page/clab/api/domain/member/api/MemberController.java
@@ -3,12 +3,11 @@
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
-import java.util.Optional;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.security.access.annotation.Secured;
+import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PatchMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -17,7 +16,6 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
-import page.clab.api.domain.application.domain.Application;
 import page.clab.api.domain.member.application.MemberService;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.request.MemberRequestDto;
@@ -26,16 +24,16 @@
 import page.clab.api.domain.member.dto.response.MemberBirthdayResponseDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.response.MyProfileResponseDto;
-import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.dto.ApiResponse;
+import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.verification.dto.request.VerificationRequestDto;
 import page.clab.api.global.exception.InvalidColumnException;
 import page.clab.api.global.exception.PermissionDeniedException;
-
-import java.util.List;
 import page.clab.api.global.exception.SortingArgumentException;
 import page.clab.api.global.util.PageableUtils;
 
+import java.util.List;
+
 @RestController
 @RequestMapping("/api/v1/members")
 @RequiredArgsConstructor
@@ -147,4 +145,14 @@ public ApiResponse<String> verifyResetMemberPassword(
         return ApiResponse.success(id);
     }
 
+    @Operation(summary = "[S] 멤버 정보 삭제", description = "ROLE_SUPER 이상의 권한이 필요함")
+    @Secured({"ROLE_SUPER"})
+    @DeleteMapping("/{memberId}")
+    public ApiResponse<String> deleteMember(
+            @PathVariable(name = "memberId") String memberId
+    ) {
+        String id = memberService.deleteMember(memberId);
+        return ApiResponse.success(id);
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberService.java b/src/main/java/page/clab/api/domain/member/application/MemberService.java
index 0efd76d84..189b5b9a4 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberService.java
@@ -2,6 +2,7 @@
 
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.security.crypto.password.PasswordEncoder;
@@ -18,6 +19,8 @@
 import page.clab.api.domain.member.dto.response.MemberBirthdayResponseDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.response.MyProfileResponseDto;
+import page.clab.api.domain.member.event.MemberDeletedEvent;
+import page.clab.api.domain.member.event.MemberUpdatedEvent;
 import page.clab.api.domain.member.exception.DuplicateMemberContactException;
 import page.clab.api.domain.member.exception.DuplicateMemberEmailException;
 import page.clab.api.domain.member.exception.DuplicateMemberIdException;
@@ -63,6 +66,8 @@ public class MemberService {
 
     private final FileService fileService;
 
+    private final ApplicationEventPublisher eventPublisher;
+
     @Transactional
     public String createMember(MemberRequestDto requestDto) {
         checkMemberUniqueness(requestDto);
@@ -113,7 +118,9 @@ public String updateMemberInfo(String memberId, MemberUpdateRequestDto requestDt
         member.validateAccessPermission(currentMember);
         updateMember(requestDto, member);
         validationService.checkValid(member);
-        return memberRepository.save(member).getId();
+        memberRepository.save(member);
+        eventPublisher.publishEvent(new MemberUpdatedEvent(this, member.getId()));
+        return member.getId();
     }
 
     @Transactional
@@ -133,6 +140,13 @@ public String verifyResetMemberPassword(VerificationRequestDto requestDto) {
         return member.getId();
     }
 
+    public String deleteMember(String memberId) {
+        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
+        memberRepository.delete(member);
+        eventPublisher.publishEvent(new MemberDeletedEvent(this, member.getId()));
+        return member.getId();
+    }
+
     private Application getApplicationByRecruitmentIdAndStudentIdOrThrow(Long recruitmentId, String memberId) {
         return applicationRepository.findByRecruitmentIdAndStudentId(recruitmentId, memberId)
                 .orElseThrow(() -> new NotFoundException("존재하지 않는 지원서입니다."));
diff --git a/src/main/java/page/clab/api/domain/member/domain/Member.java b/src/main/java/page/clab/api/domain/member/domain/Member.java
index f8656db93..bdb172ee8 100644
--- a/src/main/java/page/clab/api/domain/member/domain/Member.java
+++ b/src/main/java/page/clab/api/domain/member/domain/Member.java
@@ -16,6 +16,8 @@
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.SQLRestriction;
 import org.hibernate.validator.constraints.URL;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -38,6 +40,8 @@
 @Builder
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
+@SQLDelete(sql = "UPDATE member SET is_deleted = true WHERE id = ?")
+@SQLRestriction("is_deleted = false")
 public class Member extends BaseEntity implements UserDetails {
 
     @Id

From fb52e86a1ddbd1672b2262a995e2a60d1dfed25b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 12:23:32 +0900
Subject: [PATCH 16/47] =?UTF-8?q?fix(Notification):=20MemberLookupService?=
 =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20=EC=98=A4?=
 =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/notification/application/NotificationService.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
index bfc98daba..49b4e3fa8 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
@@ -23,7 +23,7 @@
 @RequiredArgsConstructor
 public class NotificationService {
 
-    private MemberLookupService memberLookupService;
+    private final MemberLookupService memberLookupService;
 
     private final ValidationService validationService;
 

From 59d79d3feb14a123febe107254718f199b939d13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 12:53:47 +0900
Subject: [PATCH 17/47] =?UTF-8?q?refactor(Accuse):=20Accuse=EC=97=90?=
 =?UTF-8?q?=EC=84=9C=20Member=EB=A5=BC=20=EC=99=B8=EB=9E=98=ED=82=A4?=
 =?UTF-8?q?=EB=A1=9C=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../accuse/application/AccuseService.java     | 30 +++++++++++--------
 .../domain/accuse/dao/AccuseRepository.java   |  5 ++--
 .../clab/api/domain/accuse/domain/Accuse.java |  6 ++--
 .../accuse/dto/request/AccuseRequestDto.java  |  5 ++--
 .../accuse/dto/response/AccuseMemberInfo.java | 17 ++++-------
 .../clab/api/domain/board/domain/Board.java   |  4 +++
 .../application/MemberLookupService.java      |  2 ++
 .../application/MemberLookupServiceImpl.java  |  5 ++++
 .../clab/api/domain/review/domain/Review.java |  4 +++
 9 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index 668bf358c..852c56220 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -60,14 +60,15 @@ public Long createAccuse(AccuseRequestDto requestDto) {
         TargetType type = requestDto.getTargetType();
         Long targetId = requestDto.getTargetId();
         Member currentMember = memberLookupService.getCurrentMember();
+        String memberId = currentMember.getId();
 
-        validateAccuseRequest(type, targetId, currentMember);
+        validateAccuseRequest(type, targetId, memberId);
 
         AccuseTarget target = getOrCreateAccuseTarget(requestDto, type, targetId);
         validationService.checkValid(target);
         accuseTargetRepository.save(target);
 
-        Accuse accuse = findOrCreateAccuse(requestDto, currentMember, target);
+        Accuse accuse = findOrCreateAccuse(requestDto, memberId, target);
         validationService.checkValid(accuse);
 
         notificationService.sendNotificationToMember(currentMember, "신고하신 내용이 접수되었습니다.");
@@ -84,8 +85,8 @@ public PagedResponseDto<AccuseResponseDto> getAccusesByConditions(TargetType typ
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AccuseMyResponseDto> getMyAccuses(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Accuse> accuses = accuseRepository.findByMember(currentMember, pageable);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<Accuse> accuses = accuseRepository.findByMemberId(currentMemberId, pageable);
         return new PagedResponseDto<>(accuses.map(AccuseMyResponseDto::toDto));
     }
 
@@ -103,23 +104,23 @@ private AccuseTarget getAccuseTargetByIdOrThrow(TargetType type, Long targetId)
                 .orElseThrow(() -> new NotFoundException("존재하지 않는 신고 대상입니다."));
     }
 
-    private void validateAccuseRequest(TargetType type, Long targetId, Member currentMember) {
+    private void validateAccuseRequest(TargetType type, Long targetId, String currentMemberId) {
         switch (type) {
             case BOARD:
                 Board board = boardService.getBoardByIdOrThrow(targetId);
-                if (board.isOwner(currentMember)) {
+                if (board.isOwner(currentMemberId)) {
                     throw new AccuseTargetTypeIncorrectException("자신의 게시글은 신고할 수 없습니다.");
                 }
                 break;
             case COMMENT:
                 Comment comment = commentService.getCommentByIdOrThrow(targetId);
-                if (comment.isOwner(currentMember)) {
+                if (comment.isOwner(currentMemberId)) {
                     throw new AccuseTargetTypeIncorrectException("자신의 댓글은 신고할 수 없습니다.");
                 }
                 break;
             case REVIEW:
                 Review review = reviewService.getReviewByIdOrThrow(targetId);
-                if (review.isOwner(currentMember)) {
+                if (review.isOwner(currentMemberId)) {
                     throw new AccuseTargetTypeIncorrectException("자신의 리뷰는 신고할 수 없습니다.");
                 }
                 break;
@@ -133,15 +134,15 @@ private AccuseTarget getOrCreateAccuseTarget(AccuseRequestDto requestDto, Target
                 .orElseGet(() -> AccuseRequestDto.toTargetEntity(requestDto));
     }
 
-    private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, Member currentMember, AccuseTarget target) {
-        return accuseRepository.findByMemberAndTarget(currentMember, target)
+    private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, String memberId, AccuseTarget target) {
+        return accuseRepository.findByMemberIdAndTarget(memberId, target)
                 .map(existingAccuse -> {
                     existingAccuse.updateReason(requestDto.getReason());
                     return existingAccuse;
                 })
                 .orElseGet(() -> {
                     target.increaseAccuseCount();
-                    return AccuseRequestDto.toEntity(requestDto, currentMember, target);
+                    return AccuseRequestDto.toEntity(requestDto, memberId, target);
                 });
     }
 
@@ -150,7 +151,10 @@ private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget>
         return accuseTargets.stream()
                 .map(accuseTarget -> {
                     List<Accuse> accuses = accuseRepository.findByTargetOrderByCreatedAtDesc(accuseTarget);
-                    List<AccuseMemberInfo> members = AccuseMemberInfo.create(accuses);
+                    List<AccuseMemberInfo> members = accuses.stream()
+                            .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
+                            .map(AccuseMemberInfo::create)
+                            .toList();
                     return AccuseResponseDto.toDto(accuses.getFirst(), members);
                 })
                 .toList();
@@ -158,7 +162,7 @@ private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget>
 
     private void sendStatusUpdateNotifications(AccuseStatus status, AccuseTarget target) {
         List<Member> members = accuseRepository.findByTarget(target).stream()
-                .map(Accuse::getMember)
+                .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
                 .toList();
         notificationService.sendNotificationToMembers(members, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
     }
diff --git a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
index 3359e7cb0..b309861b3 100644
--- a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
+++ b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
@@ -6,7 +6,6 @@
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.accuse.domain.Accuse;
 import page.clab.api.domain.accuse.domain.AccuseTarget;
-import page.clab.api.domain.member.domain.Member;
 
 import java.util.List;
 import java.util.Optional;
@@ -16,11 +15,11 @@ public interface AccuseRepository extends JpaRepository<Accuse, Long> {
 
     Page<Accuse> findAllByOrderByCreatedAtDesc(Pageable pageable);
 
-    Optional<Accuse> findByMemberAndTarget(Member member, AccuseTarget target);
+    Optional<Accuse> findByMemberIdAndTarget(String memberId, AccuseTarget target);
 
     List<Accuse> findByTargetOrderByCreatedAtDesc(AccuseTarget accuseTarget);
 
-    Page<Accuse> findByMember(Member currentMember, Pageable pageable);
+    Page<Accuse> findByMemberId(String memberId, Pageable pageable);
 
     List<Accuse> findByTarget(AccuseTarget target);
 
diff --git a/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java b/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
index 90f5024d0..fa1734cf7 100644
--- a/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
+++ b/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
@@ -16,7 +16,6 @@
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
 
 @Entity
@@ -32,9 +31,8 @@ public class Accuse extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @ManyToOne
     @JoinColumns({
diff --git a/src/main/java/page/clab/api/domain/accuse/dto/request/AccuseRequestDto.java b/src/main/java/page/clab/api/domain/accuse/dto/request/AccuseRequestDto.java
index 34f60c50d..94d841548 100644
--- a/src/main/java/page/clab/api/domain/accuse/dto/request/AccuseRequestDto.java
+++ b/src/main/java/page/clab/api/domain/accuse/dto/request/AccuseRequestDto.java
@@ -8,7 +8,6 @@
 import page.clab.api.domain.accuse.domain.AccuseStatus;
 import page.clab.api.domain.accuse.domain.AccuseTarget;
 import page.clab.api.domain.accuse.domain.TargetType;
-import page.clab.api.domain.member.domain.Member;
 
 @Getter
 @Setter
@@ -26,9 +25,9 @@ public class AccuseRequestDto {
     @Schema(description = "신고 사유", example = "부적절한 게시글입니다.", required = true)
     private String reason;
 
-    public static Accuse toEntity(AccuseRequestDto requestDto, Member member, AccuseTarget target) {
+    public static Accuse toEntity(AccuseRequestDto requestDto, String memberId, AccuseTarget target) {
         return Accuse.builder()
-                .member(member)
+                .memberId(memberId)
                 .target(target)
                 .reason(requestDto.getReason())
                 .build();
diff --git a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
index 139747c6d..a7b019664 100644
--- a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
+++ b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
@@ -2,10 +2,9 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import page.clab.api.domain.accuse.domain.Accuse;
+import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
-import java.util.List;
 
 @Getter
 @Builder
@@ -17,17 +16,11 @@ public class AccuseMemberInfo {
 
     private LocalDateTime createdAt;
 
-    public static List<AccuseMemberInfo> create(List<Accuse> accuses) {
-        return accuses.stream()
-                .map(AccuseMemberInfo::create)
-                .toList();
-    }
-
-    public static AccuseMemberInfo create(Accuse accuse) {
+    public static AccuseMemberInfo create(Member member) {
         return AccuseMemberInfo.builder()
-                .memberId(accuse.getMember().getId())
-                .name(accuse.getMember().getName())
-                .createdAt(accuse.getCreatedAt())
+                .memberId(member.getId())
+                .name(member.getName())
+                .createdAt(member.getCreatedAt())
                 .build();
     }
 
diff --git a/src/main/java/page/clab/api/domain/board/domain/Board.java b/src/main/java/page/clab/api/domain/board/domain/Board.java
index 44db8437b..9b2e171bb 100644
--- a/src/main/java/page/clab/api/domain/board/domain/Board.java
+++ b/src/main/java/page/clab/api/domain/board/domain/Board.java
@@ -109,6 +109,10 @@ public boolean isOwner(Member member) {
         return this.member.isSameMember(member);
     }
 
+    public boolean isOwner(String memberId) {
+        return this.member.isSameMember(memberId);
+    }
+
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
         if (!isOwner(member) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정할 권한이 없습니다.");
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index a578dad11..ae6e581ed 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -17,6 +17,8 @@ public interface MemberLookupService {
 
     Member getCurrentMember();
 
+    String getCurrentMemberId();
+
     List<MemberResponseDto> getMembers();
 
     List<Member> findAllMembers();
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 07e4bf5d3..1fd2b004a 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -46,6 +46,11 @@ public Member getCurrentMember() {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public String getCurrentMemberId() {
+        return AuthUtil.getAuthenticationInfoMemberId();
+    }
+
     @Override
     public List<MemberResponseDto> getMembers() {
         List<Member> members = memberRepository.findAll();
diff --git a/src/main/java/page/clab/api/domain/review/domain/Review.java b/src/main/java/page/clab/api/domain/review/domain/Review.java
index 1dce023a0..57eabe2f2 100644
--- a/src/main/java/page/clab/api/domain/review/domain/Review.java
+++ b/src/main/java/page/clab/api/domain/review/domain/Review.java
@@ -64,6 +64,10 @@ public boolean isOwner(Member member) {
         return this.member.isSameMember(member);
     }
 
+    public boolean isOwner(String memberId) {
+        return this.member.isSameMember(memberId);
+    }
+
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
         if (!isOwner(member) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 후기를 수정/삭제할 권한이 없습니다.");

From d986c99b7eb065f16f46b00b3f1b72482d29a710 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 13:15:30 +0900
Subject: [PATCH 18/47] =?UTF-8?q?feat(Accuse):=20Member=EA=B0=80=20?=
 =?UTF-8?q?=EC=82=AD=EC=A0=9C=EB=90=98=EB=A9=B4,=20Accuse=EB=8F=84=20?=
 =?UTF-8?q?=EA=B0=99=EC=9D=B4=20=EC=82=AD=EC=A0=9C=EB=90=98=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/AccuseEventProcessor.java     | 37 +++++++++++++++++++
 .../domain/accuse/dao/AccuseRepository.java   |  2 +
 2 files changed, 39 insertions(+)
 create mode 100644 src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
new file mode 100644
index 000000000..ded661eab
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
@@ -0,0 +1,37 @@
+package page.clab.api.domain.accuse.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import page.clab.api.domain.accuse.dao.AccuseRepository;
+import page.clab.api.domain.accuse.domain.Accuse;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class AccuseEventProcessor implements MemberEventProcessor {
+
+    private final AccuseRepository accuseRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    public void processMemberDeleted(String memberId) {
+        List<Accuse> accuses = accuseRepository.findByMemberId(memberId);
+        accuseRepository.deleteAll(accuses);
+    }
+
+    @Override
+    public void processMemberUpdated(String memberId) {
+        // do nothing
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
index b309861b3..52e3e9366 100644
--- a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
+++ b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
@@ -21,6 +21,8 @@ public interface AccuseRepository extends JpaRepository<Accuse, Long> {
 
     Page<Accuse> findByMemberId(String memberId, Pageable pageable);
 
+    List<Accuse> findByMemberId(String memberId);
+
     List<Accuse> findByTarget(AccuseTarget target);
 
 }

From a88a474930e08de11a9e986fec0ac7f764992109 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Sun, 23 Jun 2024 13:54:05 +0900
Subject: [PATCH 19/47] =?UTF-8?q?feat(Accuse):=20=EC=86=8C=ED=94=84?=
 =?UTF-8?q?=ED=8A=B8=20=EB=94=9C=EB=A6=AC=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../accuse/application/AccuseEventProcessor.java |  5 ++++-
 .../domain/accuse/application/AccuseService.java | 11 ++++++++---
 .../api/domain/accuse/dao/AccuseRepository.java  | 16 ++++++++++------
 .../clab/api/domain/accuse/domain/Accuse.java    |  8 ++++++++
 4 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
index ded661eab..88a7bb49e 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
@@ -3,6 +3,7 @@
 import jakarta.annotation.PostConstruct;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.accuse.dao.AccuseRepository;
 import page.clab.api.domain.accuse.domain.Accuse;
 import page.clab.api.domain.member.event.MemberEventProcessor;
@@ -24,9 +25,11 @@ public void init() {
     }
 
     @Override
+    @Transactional
     public void processMemberDeleted(String memberId) {
         List<Accuse> accuses = accuseRepository.findByMemberId(memberId);
-        accuseRepository.deleteAll(accuses);
+        accuses.forEach(Accuse::delete);
+        accuseRepository.saveAll(accuses);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index 852c56220..8393637f9 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -33,6 +33,7 @@
 import page.clab.api.global.validation.ValidationService;
 
 import java.util.List;
+import java.util.Objects;
 
 @Service
 @RequiredArgsConstructor
@@ -135,7 +136,7 @@ private AccuseTarget getOrCreateAccuseTarget(AccuseRequestDto requestDto, Target
     }
 
     private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, String memberId, AccuseTarget target) {
-        return accuseRepository.findByMemberIdAndTarget(memberId, target)
+        return accuseRepository.findByMemberIdAndTarget(memberId, target.getTargetType(), target.getTargetReferenceId())
                 .map(existingAccuse -> {
                     existingAccuse.updateReason(requestDto.getReason());
                     return existingAccuse;
@@ -150,18 +151,22 @@ private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, String memberId,
     private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget> accuseTargets) {
         return accuseTargets.stream()
                 .map(accuseTarget -> {
-                    List<Accuse> accuses = accuseRepository.findByTargetOrderByCreatedAtDesc(accuseTarget);
+                    List<Accuse> accuses = accuseRepository.findByTargetOrderByCreatedAtDesc(accuseTarget.getTargetType(), accuseTarget.getTargetReferenceId());
+                    if (accuses.isEmpty()) {
+                        return null;
+                    }
                     List<AccuseMemberInfo> members = accuses.stream()
                             .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
                             .map(AccuseMemberInfo::create)
                             .toList();
                     return AccuseResponseDto.toDto(accuses.getFirst(), members);
                 })
+                .filter(Objects::nonNull)
                 .toList();
     }
 
     private void sendStatusUpdateNotifications(AccuseStatus status, AccuseTarget target) {
-        List<Member> members = accuseRepository.findByTarget(target).stream()
+        List<Member> members = accuseRepository.findByTarget(target.getTargetType(), target.getTargetReferenceId()).stream()
                 .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
                 .toList();
         notificationService.sendNotificationToMembers(members, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
diff --git a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
index 52e3e9366..324aea80a 100644
--- a/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
+++ b/src/main/java/page/clab/api/domain/accuse/dao/AccuseRepository.java
@@ -3,9 +3,10 @@
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.accuse.domain.Accuse;
-import page.clab.api.domain.accuse.domain.AccuseTarget;
+import page.clab.api.domain.accuse.domain.TargetType;
 
 import java.util.List;
 import java.util.Optional;
@@ -13,16 +14,19 @@
 @Repository
 public interface AccuseRepository extends JpaRepository<Accuse, Long> {
 
-    Page<Accuse> findAllByOrderByCreatedAtDesc(Pageable pageable);
+    @Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId")
+    Optional<Accuse> findByMemberIdAndTarget(String memberId, TargetType targetType, Long targetReferenceId);
 
-    Optional<Accuse> findByMemberIdAndTarget(String memberId, AccuseTarget target);
-
-    List<Accuse> findByTargetOrderByCreatedAtDesc(AccuseTarget accuseTarget);
+    @Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId ORDER BY a.createdAt DESC")
+    List<Accuse> findByTargetOrderByCreatedAtDesc(TargetType targetType, Long targetReferenceId);
 
+    @Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId ORDER BY a.createdAt DESC")
     Page<Accuse> findByMemberId(String memberId, Pageable pageable);
 
+    @Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId")
     List<Accuse> findByMemberId(String memberId);
 
-    List<Accuse> findByTarget(AccuseTarget target);
+    @Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId")
+    List<Accuse> findByTarget(TargetType targetType, Long targetReferenceId);
 
 }
diff --git a/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java b/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
index fa1734cf7..10e48b4fe 100644
--- a/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
+++ b/src/main/java/page/clab/api/domain/accuse/domain/Accuse.java
@@ -16,6 +16,8 @@
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.SQLRestriction;
 import page.clab.api.global.common.domain.BaseEntity;
 
 @Entity
@@ -25,6 +27,8 @@
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
 @EqualsAndHashCode(callSuper = false)
+@SQLDelete(sql = "UPDATE accuse SET is_deleted = true WHERE id = ?")
+@SQLRestriction("is_deleted = false")
 public class Accuse extends BaseEntity {
 
     @Id
@@ -49,4 +53,8 @@ public void updateReason(String reason) {
         this.reason = reason;
     }
 
+    public void delete() {
+        this.isDeleted = true;
+    }
+
 }

From 61bb43a48c952064acbdb4b3a2dc50e887a11bf3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Tue, 25 Jun 2024 14:35:53 +0900
Subject: [PATCH 20/47] =?UTF-8?q?feat(Award):=20Award=EC=97=90=EC=84=9C=20?=
 =?UTF-8?q?Member=EB=A5=BC=20=EC=99=B8=EB=9E=98=ED=82=A4=EB=A1=9C=20?=
 =?UTF-8?q?=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/AwardEventProcessor.java      | 39 +++++++++++++++++++
 .../award/application/AwardService.java       | 12 +++---
 .../api/domain/award/dao/AwardRepository.java |  7 +++-
 .../domain/award/dao/AwardRepositoryImpl.java |  6 +--
 .../clab/api/domain/award/domain/Award.java   | 17 ++++----
 .../award/dto/request/AwardRequestDto.java    |  5 +--
 6 files changed, 64 insertions(+), 22 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
new file mode 100644
index 000000000..8d2ef818f
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
@@ -0,0 +1,39 @@
+package page.clab.api.domain.award.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.award.dao.AwardRepository;
+import page.clab.api.domain.award.domain.Award;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class AwardEventProcessor implements MemberEventProcessor {
+
+    private final AwardRepository awardRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(String memberId) {
+        List<Award> awards = awardRepository.findByMemberId(memberId);
+        awards.forEach(Award::delete);
+        awardRepository.saveAll(awards);
+    }
+
+    @Override
+    public void processMemberUpdated(String memberId) {
+        // do nothing
+    }
+}
diff --git a/src/main/java/page/clab/api/domain/award/application/AwardService.java b/src/main/java/page/clab/api/domain/award/application/AwardService.java
index cfa5a2260..cfb50f6cc 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardService.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardService.java
@@ -29,8 +29,8 @@ public class AwardService {
 
     @Transactional
     public Long createAward(AwardRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Award award = AwardRequestDto.toEntity(requestDto, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Award award = AwardRequestDto.toEntity(requestDto, currentMemberId);
         validationService.checkValid(award);
         return awardRepository.save(award).getId();
     }
@@ -43,8 +43,8 @@ public PagedResponseDto<AwardResponseDto> getAwardsByConditions(String memberId,
 
     @Transactional(readOnly = true)
     public PagedResponseDto<AwardResponseDto> getMyAwards(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Award> awards = getAwardByMember(pageable, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<Award> awards = getAwardByMemberId(pageable, currentMemberId);
         return new PagedResponseDto<>(awards.map(AwardResponseDto::toDto));
     }
 
@@ -77,8 +77,8 @@ private Award getAwardByIdOrThrow(Long awardId) {
                 .orElseThrow(() -> new NotFoundException("해당 수상 이력이 존재하지 않습니다."));
     }
 
-    private Page<Award> getAwardByMember(Pageable pageable, Member member) {
-        return awardRepository.findAllByMember(member, pageable);
+    private Page<Award> getAwardByMemberId(Pageable pageable, String memberId) {
+        return awardRepository.findByMemberId(memberId, pageable);
     }
 
 }
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/domain/award/dao/AwardRepository.java b/src/main/java/page/clab/api/domain/award/dao/AwardRepository.java
index 7037c5531..dac66260f 100644
--- a/src/main/java/page/clab/api/domain/award/dao/AwardRepository.java
+++ b/src/main/java/page/clab/api/domain/award/dao/AwardRepository.java
@@ -7,14 +7,17 @@
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.award.domain.Award;
-import page.clab.api.domain.member.domain.Member;
+
+import java.util.List;
 
 @Repository
 public interface AwardRepository extends JpaRepository<Award, Long>, AwardRepositoryCustom, QuerydslPredicateExecutor<Award> {
 
-    Page<Award> findAllByMember(Member member, Pageable pageable);
+    Page<Award> findByMemberId(String memberId, Pageable pageable);
 
     @Query(value = "SELECT a.* FROM award a WHERE a.is_deleted = true", nativeQuery = true)
     Page<Award> findAllByIsDeletedTrue(Pageable pageable);
 
+    List<Award> findByMemberId(String memberId);
+
 }
diff --git a/src/main/java/page/clab/api/domain/award/dao/AwardRepositoryImpl.java b/src/main/java/page/clab/api/domain/award/dao/AwardRepositoryImpl.java
index ba1221246..409030277 100644
--- a/src/main/java/page/clab/api/domain/award/dao/AwardRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/award/dao/AwardRepositoryImpl.java
@@ -10,10 +10,10 @@
 import page.clab.api.domain.award.domain.Award;
 import page.clab.api.domain.award.domain.QAward;
 import page.clab.api.domain.member.domain.QMember;
+import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.time.LocalDate;
 import java.util.List;
-import page.clab.api.global.util.OrderSpecifierUtil;
 
 @Repository
 @RequiredArgsConstructor
@@ -27,7 +27,7 @@ public Page<Award> findByConditions(String memberId, Long year, Pageable pageabl
         QMember qMember = QMember.member;
 
         BooleanBuilder builder = new BooleanBuilder();
-        if (memberId != null) builder.and(qAward.member.id.eq(memberId));
+        if (memberId != null) builder.and(qAward.memberId.eq(memberId));
         if (year != null) {
             LocalDate startOfYear = LocalDate.of(year.intValue(), 1, 1);
             LocalDate endOfYear = LocalDate.of(year.intValue(), 12, 31);
@@ -35,7 +35,7 @@ public Page<Award> findByConditions(String memberId, Long year, Pageable pageabl
         }
 
         List<Award> awards = queryFactory.selectFrom(qAward)
-                .leftJoin(qAward.member, qMember)
+                .leftJoin(qMember).on(qAward.memberId.eq(qMember.id))
                 .where(builder)
                 .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, qAward))
                 .offset(pageable.getOffset())
diff --git a/src/main/java/page/clab/api/domain/award/domain/Award.java b/src/main/java/page/clab/api/domain/award/domain/Award.java
index d9f7646e5..e54d92e3c 100644
--- a/src/main/java/page/clab/api/domain/award/domain/Award.java
+++ b/src/main/java/page/clab/api/domain/award/domain/Award.java
@@ -5,8 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -53,9 +51,8 @@ public class Award extends BaseEntity {
     @Column(nullable = false)
     private LocalDate awardDate;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id")
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     public void update(AwardUpdateRequestDto requestDto) {
         Optional.ofNullable(requestDto.getCompetitionName()).ifPresent(this::setCompetitionName);
@@ -64,12 +61,16 @@ public void update(AwardUpdateRequestDto requestDto) {
         Optional.ofNullable(requestDto.getAwardDate()).ifPresent(this::setAwardDate);
     }
 
-    public boolean isOwner(Member member) {
-        return this.member.isSameMember(member);
+    public void delete() {
+        this.isDeleted = true;
+    }
+
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+        if (!isOwner(member.getId()) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정/삭제할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/award/dto/request/AwardRequestDto.java b/src/main/java/page/clab/api/domain/award/dto/request/AwardRequestDto.java
index 80ce4fe91..31f6173fc 100644
--- a/src/main/java/page/clab/api/domain/award/dto/request/AwardRequestDto.java
+++ b/src/main/java/page/clab/api/domain/award/dto/request/AwardRequestDto.java
@@ -5,7 +5,6 @@
 import lombok.Getter;
 import lombok.Setter;
 import page.clab.api.domain.award.domain.Award;
-import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDate;
 
@@ -29,13 +28,13 @@ public class AwardRequestDto {
     @Schema(description = "수상일", example = "2023-08-18", required = true)
     private LocalDate awardDate;
 
-    public static Award toEntity(AwardRequestDto requestDto, Member member) {
+    public static Award toEntity(AwardRequestDto requestDto, String memberId) {
         return Award.builder()
                 .competitionName(requestDto.getCompetitionName())
                 .organizer(requestDto.getOrganizer())
                 .awardName(requestDto.getAwardName())
                 .awardDate(requestDto.getAwardDate())
-                .member(member)
+                .memberId(memberId)
                 .build();
     }
 

From 3a63c93b92fa3681af5c952fb01522a1073a3aa1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Tue, 25 Jun 2024 15:14:11 +0900
Subject: [PATCH 21/47] =?UTF-8?q?refactor(Blog):=20Blog=EC=97=90=EC=84=9C?=
 =?UTF-8?q?=20Member=EB=A5=BC=20=EC=99=B8=EB=9E=98=ED=82=A4=EB=A1=9C=20?=
 =?UTF-8?q?=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../blog/application/BlogEventProcessor.java  | 39 +++++++++++++++++++
 .../domain/blog/application/BlogService.java  |  8 ++--
 .../api/domain/blog/dao/BlogRepository.java   |  4 ++
 .../domain/blog/dao/BlogRepositoryImpl.java   |  6 +--
 .../clab/api/domain/blog/domain/Blog.java     | 13 ++++---
 .../blog/dto/request/BlogRequestDto.java      |  5 +--
 .../dto/response/BlogDetailsResponseDto.java  |  7 ++--
 7 files changed, 63 insertions(+), 19 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java b/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
new file mode 100644
index 000000000..e27ec84a8
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
@@ -0,0 +1,39 @@
+package page.clab.api.domain.blog.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.blog.dao.BlogRepository;
+import page.clab.api.domain.blog.domain.Blog;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class BlogEventProcessor implements MemberEventProcessor {
+
+    private final BlogRepository blogRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(String memberId) {
+        List<Blog> blogs = blogRepository.findByMemberId(memberId);
+        blogs.forEach(Blog::delete);
+        blogRepository.saveAll(blogs);
+    }
+
+    @Override
+    public void processMemberUpdated(String memberId) {
+        // do nothing
+    }
+}
diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index 1119a5c6d..5f98d352a 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -30,8 +30,8 @@ public class BlogService {
 
     @Transactional
     public Long createBlog(BlogRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Blog blog = BlogRequestDto.toEntity(requestDto, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Blog blog = BlogRequestDto.toEntity(requestDto, currentMemberId);
         validationService.checkValid(blog);
         return blogRepository.save(blog).getId();
     }
@@ -47,7 +47,7 @@ public BlogDetailsResponseDto getBlogDetails(Long blogId) {
         Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         boolean isOwner = blog.isOwner(currentMember);
-        return BlogDetailsResponseDto.toDto(blog, isOwner);
+        return BlogDetailsResponseDto.toDto(blog, currentMember, isOwner);
     }
 
     @Transactional(readOnly = true)
@@ -55,7 +55,7 @@ public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageabl
         Member currentMember = memberLookupService.getCurrentMember();
         Page<Blog> blogs = blogRepository.findAllByIsDeletedTrue(pageable);
         return new PagedResponseDto<>(blogs
-                .map(blog -> BlogDetailsResponseDto.toDto(blog, blog.isOwner(currentMember))));
+                .map(blog -> BlogDetailsResponseDto.toDto(blog, currentMember, blog.isOwner(currentMember))));
     }
 
     @Transactional
diff --git a/src/main/java/page/clab/api/domain/blog/dao/BlogRepository.java b/src/main/java/page/clab/api/domain/blog/dao/BlogRepository.java
index 57c5f8720..6c8465e72 100644
--- a/src/main/java/page/clab/api/domain/blog/dao/BlogRepository.java
+++ b/src/main/java/page/clab/api/domain/blog/dao/BlogRepository.java
@@ -8,6 +8,8 @@
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.blog.domain.Blog;
 
+import java.util.List;
+
 @Repository
 public interface BlogRepository extends JpaRepository<Blog, Long>, BlogRepositoryCustom, QuerydslPredicateExecutor<Blog> {
 
@@ -16,4 +18,6 @@ public interface BlogRepository extends JpaRepository<Blog, Long>, BlogRepositor
     @Query(value = "SELECT b.* FROM blog b WHERE b.is_deleted = true", nativeQuery = true)
     Page<Blog> findAllByIsDeletedTrue(Pageable pageable);
 
+    List<Blog> findByMemberId(String memberId);
+
 }
diff --git a/src/main/java/page/clab/api/domain/blog/dao/BlogRepositoryImpl.java b/src/main/java/page/clab/api/domain/blog/dao/BlogRepositoryImpl.java
index fdb01257a..6a3f65266 100644
--- a/src/main/java/page/clab/api/domain/blog/dao/BlogRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/blog/dao/BlogRepositoryImpl.java
@@ -10,9 +10,9 @@
 import page.clab.api.domain.blog.domain.Blog;
 import page.clab.api.domain.blog.domain.QBlog;
 import page.clab.api.domain.member.domain.QMember;
+import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.util.List;
-import page.clab.api.global.util.OrderSpecifierUtil;
 
 @Repository
 @RequiredArgsConstructor
@@ -31,7 +31,7 @@ public Page<Blog> findByConditions(String title, String memberName, Pageable pag
         if (memberName != null && !memberName.isBlank()) builder.and(qMember.name.eq(memberName));
 
         List<Blog> blogs = queryFactory.selectFrom(qBlog)
-                .leftJoin(qBlog.member, qMember)
+                .leftJoin(qMember).on(qBlog.memberId.eq(qMember.id))
                 .where(builder)
                 .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, qBlog))
                 .offset(pageable.getOffset())
@@ -40,7 +40,7 @@ public Page<Blog> findByConditions(String title, String memberName, Pageable pag
 
         long count = queryFactory
                 .selectFrom(qBlog)
-                .leftJoin(qBlog.member, qMember)
+                .leftJoin(qMember).on(qBlog.memberId.eq(qMember.id))
                 .where(builder)
                 .fetchCount();
 
diff --git a/src/main/java/page/clab/api/domain/blog/domain/Blog.java b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
index 98994f6fd..6b3ab132d 100644
--- a/src/main/java/page/clab/api/domain/blog/domain/Blog.java
+++ b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
@@ -5,8 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -37,9 +35,8 @@ public class Blog extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @Column(nullable = false)
     @Size(min = 1, max = 100, message = "{size.blog.title}")
@@ -64,8 +61,12 @@ public void update(BlogUpdateRequestDto requestDto) {
         Optional.ofNullable(requestDto.getHyperlink()).ifPresent(this::setHyperlink);
     }
 
+    public void delete() {
+        this.isDeleted = true;
+    }
+
     public boolean isOwner(Member member) {
-        return this.member.isSameMember(member);
+        return this.memberId.equals(member.getId());
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
diff --git a/src/main/java/page/clab/api/domain/blog/dto/request/BlogRequestDto.java b/src/main/java/page/clab/api/domain/blog/dto/request/BlogRequestDto.java
index 6bf9d769d..4cdca912a 100644
--- a/src/main/java/page/clab/api/domain/blog/dto/request/BlogRequestDto.java
+++ b/src/main/java/page/clab/api/domain/blog/dto/request/BlogRequestDto.java
@@ -5,7 +5,6 @@
 import lombok.Getter;
 import lombok.Setter;
 import page.clab.api.domain.blog.domain.Blog;
-import page.clab.api.domain.member.domain.Member;
 
 @Getter
 @Setter
@@ -29,9 +28,9 @@ public class BlogRequestDto {
     @Schema(description = "하이퍼링크", example = "https://www.clab.page")
     private String hyperlink;
 
-    public static Blog toEntity(BlogRequestDto requestDto, Member member) {
+    public static Blog toEntity(BlogRequestDto requestDto, String memberId) {
         return Blog.builder()
-                .member(member)
+                .memberId(memberId)
                 .title(requestDto.getTitle())
                 .subTitle(requestDto.getSubTitle())
                 .content(requestDto.getContent())
diff --git a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
index e646dbce2..c96d9808a 100644
--- a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
@@ -4,6 +4,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.blog.domain.Blog;
+import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 
@@ -32,11 +33,11 @@ public class BlogDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BlogDetailsResponseDto toDto(Blog blog, boolean isOwner) {
+    public static BlogDetailsResponseDto toDto(Blog blog, Member member, boolean isOwner) {
         return BlogDetailsResponseDto.builder()
                 .id(blog.getId())
-                .memberId(blog.getMember().getId())
-                .name(blog.getMember().getName())
+                .memberId(member.getId())
+                .name(member.getName())
                 .title(blog.getTitle())
                 .subTitle(blog.getSubTitle())
                 .content(blog.getContent())

From 6d52b3479b2d43c785870e5ddec007a020117878 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Tue, 25 Jun 2024 18:08:53 +0900
Subject: [PATCH 22/47] =?UTF-8?q?refactor(Board):=20Board=EC=97=90?=
 =?UTF-8?q?=EC=84=9C=20Member=EB=A5=BC=20=EC=99=B8=EB=9E=98=ED=82=A4?=
 =?UTF-8?q?=EB=A1=9C=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/BoardEventProcessor.java      | 39 +++++++++++++++++++
 .../board/application/BoardService.java       | 37 ++++++++++--------
 .../api/domain/board/dao/BoardRepository.java |  7 +++-
 .../clab/api/domain/board/domain/Board.java   | 23 +++++------
 .../domain/board/domain/SlackBoardInfo.java   | 25 ++++++++++++
 .../board/dto/request/BoardRequestDto.java    |  5 +--
 .../response/BoardCategoryResponseDto.java    |  6 +--
 .../dto/response/BoardDetailsResponseDto.java |  5 ++-
 .../dto/response/BoardListResponseDto.java    |  5 ++-
 .../dto/response/BoardMyResponseDto.java      |  5 ++-
 .../domain/board/dto/response/WriterInfo.java | 13 ++++---
 .../comment/application/CommentService.java   |  2 +-
 .../slack/application/SlackService.java       |  4 +-
 .../slack/application/SlackServiceHelper.java | 14 +++----
 14 files changed, 130 insertions(+), 60 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
 create mode 100644 src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java

diff --git a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
new file mode 100644
index 000000000..eef69cf84
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
@@ -0,0 +1,39 @@
+package page.clab.api.domain.board.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.board.dao.BoardRepository;
+import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class BoardEventProcessor implements MemberEventProcessor {
+
+    private final BoardRepository boardRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(String memberId) {
+        List<Board> boards = boardRepository.findByMemberId(memberId);
+        boards.forEach(Board::delete);
+        boardRepository.saveAll(boards);
+    }
+
+    @Override
+    public void processMemberUpdated(String memberId) {
+        // do nothing
+    }
+}
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardService.java b/src/main/java/page/clab/api/domain/board/application/BoardService.java
index 8d4b27eaf..fe68c8937 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardService.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardService.java
@@ -11,6 +11,7 @@
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.board.domain.BoardCategory;
 import page.clab.api.domain.board.domain.BoardLike;
+import page.clab.api.domain.board.domain.SlackBoardInfo;
 import page.clab.api.domain.board.dto.request.BoardRequestDto;
 import page.clab.api.domain.board.dto.request.BoardUpdateRequestDto;
 import page.clab.api.domain.board.dto.response.BoardCategoryResponseDto;
@@ -56,20 +57,22 @@ public class BoardService {
     public String createBoard(BoardRequestDto requestDto) throws PermissionDeniedException {
         Member currentMember = memberLookupService.getCurrentMember();
         List<UploadedFile> uploadedFiles = uploadedFileService.getUploadedFilesByUrls(requestDto.getFileUrlList());
-        Board board = BoardRequestDto.toEntity(requestDto, currentMember, uploadedFiles);
+        Board board = BoardRequestDto.toEntity(requestDto, currentMember.getId(), uploadedFiles);
         board.validateAccessPermissionForCreation(currentMember);
         validationService.checkValid(board);
-        if (board.shouldNotifyForNewBoard()) {
+        if (board.shouldNotifyForNewBoard(currentMember)) {
             notificationService.sendNotificationToMember(currentMember, "[" + board.getTitle() + "] 새로운 공지사항이 등록되었습니다.");
         }
-        slackService.sendNewBoardNotification(board);
+        SlackBoardInfo boardInfo = SlackBoardInfo.create(board, currentMember);
+        slackService.sendNewBoardNotification(boardInfo);
         return boardRepository.save(board).getCategory().getKey();
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardListResponseDto> getBoards(Pageable pageable) {
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Board> boards = boardRepository.findAll(pageable);
-        return new PagedResponseDto<>(boards.map(this::mapToBoardListResponseDto));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMember)));
     }
 
     @Transactional(readOnly = true)
@@ -77,21 +80,22 @@ public BoardDetailsResponseDto getBoardDetails(Long boardId) {
         Member currentMember = memberLookupService.getCurrentMember();
         Board board = getBoardByIdOrThrow(boardId);
         boolean hasLikeByMe = checkLikeStatus(board, currentMember);
-        boolean isOwner = board.isOwner(currentMember);
-        return BoardDetailsResponseDto.toDto(board, hasLikeByMe, isOwner);
+        boolean isOwner = board.isOwner(currentMember.getId());
+        return BoardDetailsResponseDto.toDto(board, currentMember, hasLikeByMe, isOwner);
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardMyResponseDto> getMyBoards(Pageable pageable) {
         Member currentMember = memberLookupService.getCurrentMember();
-        Page<Board> boards = getBoardByMember(pageable, currentMember);
-        return new PagedResponseDto<>(boards.map(BoardMyResponseDto::toDto));
+        Page<Board> boards = getBoardByMemberId(pageable, currentMember.getId());
+        return new PagedResponseDto<>(boards.map(board -> BoardMyResponseDto.toDto(board, currentMember)));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardCategoryResponseDto> getBoardsByCategory(BoardCategory category, Pageable pageable) {
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Board> boards = getBoardByCategory(category, pageable);
-        return new PagedResponseDto<>(boards.map(this::mapToBoardCategoryResponseDto));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardCategoryResponseDto(board, currentMember)));
     }
 
     @Transactional
@@ -123,8 +127,9 @@ public Long toggleLikeStatus(Long boardId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardListResponseDto> getDeletedBoards(Pageable pageable) {
+        Member currentMember = memberLookupService.getCurrentMember();
         Page<Board> boards = boardRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(boards.map(this::mapToBoardListResponseDto));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMember)));
     }
 
     public String deleteBoard(Long boardId) throws PermissionDeniedException {
@@ -136,15 +141,15 @@ public String deleteBoard(Long boardId) throws PermissionDeniedException {
     }
 
     @NotNull
-    private BoardListResponseDto mapToBoardListResponseDto(Board board) {
+    private BoardListResponseDto mapToBoardListResponseDto(Board board, Member member) {
         Long commentCount = commentRepository.countByBoard(board);
-        return BoardListResponseDto.toDto(board, commentCount);
+        return BoardListResponseDto.toDto(board, member, commentCount);
     }
 
     @NotNull
-    private BoardCategoryResponseDto mapToBoardCategoryResponseDto(Board board) {
+    private BoardCategoryResponseDto mapToBoardCategoryResponseDto(Board board, Member member) {
         Long commentCount = commentRepository.countByBoard(board);
-        return BoardCategoryResponseDto.toDto(board, commentCount);
+        return BoardCategoryResponseDto.toDto(board, member, commentCount);
     }
 
     public Board getBoardByIdOrThrow(Long boardId) {
@@ -152,8 +157,8 @@ public Board getBoardByIdOrThrow(Long boardId) {
                 .orElseThrow(() -> new NotFoundException("해당 게시글이 존재하지 않습니다."));
     }
 
-    private Page<Board> getBoardByMember(Pageable pageable, Member member) {
-        return boardRepository.findAllByMember(member, pageable);
+    private Page<Board> getBoardByMemberId(Pageable pageable, String memberId) {
+        return boardRepository.findAllByMemberId(memberId, pageable);
     }
 
     private Page<Board> getBoardByCategory(BoardCategory category, Pageable pageable) {
diff --git a/src/main/java/page/clab/api/domain/board/dao/BoardRepository.java b/src/main/java/page/clab/api/domain/board/dao/BoardRepository.java
index 6887d8fa2..11550788a 100644
--- a/src/main/java/page/clab/api/domain/board/dao/BoardRepository.java
+++ b/src/main/java/page/clab/api/domain/board/dao/BoardRepository.java
@@ -7,18 +7,21 @@
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.board.domain.BoardCategory;
-import page.clab.api.domain.member.domain.Member;
+
+import java.util.List;
 
 @Repository
 public interface BoardRepository extends JpaRepository<Board, Long> {
 
     Page<Board> findAll(Pageable pageable);
 
-    Page<Board> findAllByMember(Member member, Pageable pageable);
+    Page<Board> findAllByMemberId(String member, Pageable pageable);
 
     Page<Board> findAllByCategory(BoardCategory category, Pageable pageable);
 
     @Query(value = "SELECT b.* FROM board b WHERE b.is_deleted = true", nativeQuery = true)
     Page<Board> findAllByIsDeletedTrue(Pageable pageable);
 
+    List<Board> findByMemberId(String memberId);
+
 }
diff --git a/src/main/java/page/clab/api/domain/board/domain/Board.java b/src/main/java/page/clab/api/domain/board/domain/Board.java
index 9b2e171bb..8c0be374f 100644
--- a/src/main/java/page/clab/api/domain/board/domain/Board.java
+++ b/src/main/java/page/clab/api/domain/board/domain/Board.java
@@ -9,7 +9,6 @@
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.persistence.OneToMany;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
@@ -22,7 +21,6 @@
 import org.hibernate.annotations.SQLRestriction;
 import page.clab.api.domain.board.dto.request.BoardUpdateRequestDto;
 import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.domain.Role;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.common.file.domain.UploadedFile;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -44,9 +42,8 @@ public class Board extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @Column(nullable = false)
     private String nickname;
@@ -83,6 +80,10 @@ public void update(BoardUpdateRequestDto boardUpdateRequestDto) {
         Optional.of(boardUpdateRequestDto.isWantAnonymous()).ifPresent(this::setWantAnonymous);
     }
 
+    public void delete() {
+        this.isDeleted = true;
+    }
+
     public boolean isNotice() {
         return this.category.equals(BoardCategory.NOTICE);
     }
@@ -91,8 +92,8 @@ public boolean isGraduated() {
         return this.category.equals(BoardCategory.GRADUATED);
     }
 
-    public boolean shouldNotifyForNewBoard() {
-        return !this.member.getRole().equals(Role.USER) && this.category.equals(BoardCategory.NOTICE);
+    public boolean shouldNotifyForNewBoard(Member member) {
+        return member.isAdminRole() && this.category.equals(BoardCategory.NOTICE);
     }
 
     public void incrementLikes() {
@@ -105,16 +106,12 @@ public void decrementLikes() {
         }
     }
 
-    public boolean isOwner(Member member) {
-        return this.member.isSameMember(member);
-    }
-
     public boolean isOwner(String memberId) {
-        return this.member.isSameMember(memberId);
+        return this.memberId.equals(memberId);
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+        if (!isOwner(member.getId()) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java b/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java
new file mode 100644
index 000000000..62c1f4ee8
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java
@@ -0,0 +1,25 @@
+package page.clab.api.domain.board.domain;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.member.domain.Member;
+
+@Getter
+@Builder
+public class SlackBoardInfo {
+
+    private String title;
+
+    private String category;
+
+    private String username;
+
+    public static SlackBoardInfo create(Board board, Member member) {
+        return SlackBoardInfo.builder()
+                .title(board.getTitle())
+                .category(board.getCategory().getDescription())
+                .username(board.isWantAnonymous() ? board.getNickname() : member.getId() + " " + member.getName())
+                .build();
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/board/dto/request/BoardRequestDto.java b/src/main/java/page/clab/api/domain/board/dto/request/BoardRequestDto.java
index eb011dee7..06bfca15a 100644
--- a/src/main/java/page/clab/api/domain/board/dto/request/BoardRequestDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/request/BoardRequestDto.java
@@ -6,7 +6,6 @@
 import lombok.Setter;
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.board.domain.BoardCategory;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.file.domain.UploadedFile;
 import page.clab.api.global.util.RandomNicknameUtil;
 
@@ -38,9 +37,9 @@ public class BoardRequestDto {
     @Schema(description = "익명 사용 여부", example = "false", required = true)
     private boolean wantAnonymous;
 
-    public static Board toEntity(BoardRequestDto requestDto, Member member, List<UploadedFile> uploadedFiles) {
+    public static Board toEntity(BoardRequestDto requestDto, String memberId, List<UploadedFile> uploadedFiles) {
         return Board.builder()
-                .member(member)
+                .memberId(memberId)
                 .nickname(RandomNicknameUtil.makeRandomNickname())
                 .category(requestDto.getCategory())
                 .title(requestDto.getTitle())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
index aaa19f054..c8fdce527 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
@@ -3,7 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.board.domain.BoardCategory;
+import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 
@@ -27,8 +27,8 @@ public class BoardCategoryResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardCategoryResponseDto toDto(Board board, Long commentCount) {
-        WriterInfo writerInfo = WriterInfo.fromBoard(board);
+    public static BoardCategoryResponseDto toDto(Board board, Member member, Long commentCount) {
+        WriterInfo writerInfo = WriterInfo.fromBoard(board, member);
         return BoardCategoryResponseDto.builder()
                 .id(board.getId())
                 .category(board.getCategory().getKey())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
index 10ed09778..6155820ad 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
@@ -4,6 +4,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.file.dto.response.UploadedFileResponseDto;
 
 import java.time.LocalDateTime;
@@ -42,8 +43,8 @@ public class BoardDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardDetailsResponseDto toDto(Board board, boolean hasLikeByMe, boolean isOwner) {
-        WriterInfo writerInfo = WriterInfo.fromBoardDetails(board);
+    public static BoardDetailsResponseDto toDto(Board board, Member member, boolean hasLikeByMe, boolean isOwner) {
+        WriterInfo writerInfo = WriterInfo.fromBoardDetails(board, member);
         return BoardDetailsResponseDto.builder()
                 .id(board.getId())
                 .writerId(writerInfo.getId())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
index eef2f6165..c509454fd 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
@@ -3,6 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 
@@ -28,8 +29,8 @@ public class BoardListResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardListResponseDto toDto(Board board, Long commentCount) {
-        WriterInfo writerInfo = WriterInfo.fromBoard(board);
+    public static BoardListResponseDto toDto(Board board, Member member, Long commentCount) {
+        WriterInfo writerInfo = WriterInfo.fromBoard(board, member);
         return BoardListResponseDto.builder()
                 .id(board.getId())
                 .writerId(writerInfo.getId())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
index 4842a1a6a..f78875737 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
@@ -3,6 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 
@@ -22,11 +23,11 @@ public class BoardMyResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardMyResponseDto toDto(Board board) {
+    public static BoardMyResponseDto toDto(Board board, Member member) {
         return BoardMyResponseDto.builder()
                 .id(board.getId())
                 .category(board.getCategory().getKey())
-                .writerName(board.isWantAnonymous() ? board.getNickname() : board.getMember().getName())
+                .writerName(board.isWantAnonymous() ? board.getNickname() : member.getName())
                 .title(board.getTitle())
                 .imageUrl(board.getImageUrl())
                 .createdAt(board.getCreatedAt())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java b/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
index 9ce553141..b8536da75 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
@@ -2,6 +2,7 @@
 
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.domain.Role;
 
 @Getter
@@ -27,22 +28,22 @@ public WriterInfo(String id, String name, Long roleLevel, String imageUrl) {
         this.imageUrl = imageUrl;
     }
 
-    public static WriterInfo fromBoard(Board board) {
-        if (board.getMember().isAdminRole() && board.isNotice()) {
+    public static WriterInfo fromBoard(Board board, Member member) {
+        if (member.isAdminRole() && board.isNotice()) {
             return new WriterInfo(null, "운영진");
         } else if (board.isWantAnonymous()) {
             return new WriterInfo(null, board.getNickname());
         }
-        return new WriterInfo(board.getMember().getId(), board.getMember().getName());
+        return new WriterInfo(member.getId(), member.getName());
     }
 
-    public static WriterInfo fromBoardDetails(Board board) {
-        if (board.getMember().isAdminRole() && board.isNotice()) {
+    public static WriterInfo fromBoardDetails(Board board, Member member) {
+        if (member.isAdminRole() && board.isNotice()) {
             return new WriterInfo(null, "운영진", Role.ADMIN.toRoleLevel(), null);
         } else if (board.isWantAnonymous()) {
             return new WriterInfo(null, board.getNickname(), null, null);
         }
-        return new WriterInfo(board.getMember().getId(), board.getMember().getName(), board.getMember().getRole().toRoleLevel(), board.getMember().getImageUrl());
+        return new WriterInfo(member.getId(), member.getName(), member.getRole().toRoleLevel(), member.getImageUrl());
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/comment/application/CommentService.java b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
index 575f5ee78..adef5f5f7 100644
--- a/src/main/java/page/clab/api/domain/comment/application/CommentService.java
+++ b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
@@ -153,7 +153,7 @@ private Comment findParentComment(Long parentId) {
 
     private void sendNotificationForNewComment(Comment comment) {
         Board board = comment.getBoard();
-        Member boardOwner = board.getMember();
+        Member boardOwner = memberLookupService.getMemberById(board.getMemberId());
         String notificationMessage = String.format("[%s] %s님이 게시글에 댓글을 남겼습니다.", board.getTitle(), comment.getWriterName());
         notificationService.sendNotificationToMember(boardOwner, notificationMessage);
     }
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
index 8963b6f54..5f2c1a966 100644
--- a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
@@ -8,7 +8,7 @@
 import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
-import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.board.domain.SlackBoardInfo;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.slack.domain.GeneralAlertType;
 import page.clab.api.global.common.slack.domain.SecurityAlertType;
@@ -37,7 +37,7 @@ public void sendNewApplicationNotification(ApplicationRequestDto applicationRequ
         eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.APPLICATION_CREATED, null, applicationRequestDto));
     }
 
-    public void sendNewBoardNotification(Board board) {
+    public void sendNewBoardNotification(SlackBoardInfo board) {
         eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.BOARD_CREATED, null, board));
     }
 
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
index 3bd9c0e6d..67902c100 100644
--- a/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
@@ -21,7 +21,7 @@
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
 import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
-import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.board.domain.SlackBoardInfo;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.slack.domain.AlertType;
 import page.clab.api.global.common.slack.domain.GeneralAlertType;
@@ -113,8 +113,8 @@ public List<LayoutBlock> createBlocks(AlertType alertType, HttpServletRequest re
                     }
                     break;
                 case BOARD_CREATED:
-                    if (additionalData instanceof Board) {
-                        return createBoardBlocks((Board) additionalData);
+                    if (additionalData instanceof SlackBoardInfo) {
+                        return createBoardBlocks((SlackBoardInfo) additionalData);
                     }
                     break;
                 case SERVER_START:
@@ -205,16 +205,14 @@ private List<LayoutBlock> createApplicationBlocks(ApplicationRequestDto requestD
         return blocks;
     }
 
-    private List<LayoutBlock> createBoardBlocks(Board board) {
+    private List<LayoutBlock> createBoardBlocks(SlackBoardInfo board) {
         List<LayoutBlock> blocks = new ArrayList<>();
-        String username = board.isWantAnonymous() ?
-                board.getNickname() : board.getMember().getId() + " " + board.getMember().getName();
 
         blocks.add(section(section -> section.text(markdownText(":writing_hand: *New Board*"))));
         blocks.add(section(section -> section.fields(Arrays.asList(
                 markdownText("*Title:*\n" + board.getTitle()),
-                markdownText("*Category:*\n" + board.getCategory().getDescription()),
-                markdownText("*User:*\n" + username)
+                markdownText("*Category:*\n" + board.getCategory()),
+                markdownText("*User:*\n" + board.getUsername())
         ))));
         return blocks;
     }

From d5e5eba050d482174d9537f5f26fa813a57d79c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Tue, 25 Jun 2024 19:24:47 +0900
Subject: [PATCH 23/47] =?UTF-8?q?refactor(Member):=20Member=20=EC=A0=95?=
 =?UTF-8?q?=EB=B3=B4=20=EB=B3=80=EA=B2=BD=EC=8B=9C=20memberId=EA=B0=80=20?=
 =?UTF-8?q?=EC=95=84=EB=8B=8C=20Member=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?=
 =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A1=9C=20=EB=B3=B4=EB=82=B4?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/accuse/application/AccuseEventProcessor.java    | 7 ++++---
 .../api/domain/award/application/AwardEventProcessor.java  | 7 ++++---
 .../api/domain/blog/application/BlogEventProcessor.java    | 7 ++++---
 .../api/domain/board/application/BoardEventProcessor.java  | 7 ++++---
 .../clab/api/domain/member/application/MemberService.java  | 4 ++--
 .../clab/api/domain/member/event/MemberDeletedEvent.java   | 7 ++++---
 .../api/domain/member/event/MemberEventDispatcher.java     | 4 ++--
 .../clab/api/domain/member/event/MemberEventProcessor.java | 6 ++++--
 .../clab/api/domain/member/event/MemberUpdatedEvent.java   | 7 ++++---
 9 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
index 88a7bb49e..6dab8da72 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
@@ -6,6 +6,7 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.accuse.dao.AccuseRepository;
 import page.clab.api.domain.accuse.domain.Accuse;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -26,14 +27,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(String memberId) {
-        List<Accuse> accuses = accuseRepository.findByMemberId(memberId);
+    public void processMemberDeleted(Member member) {
+        List<Accuse> accuses = accuseRepository.findByMemberId(member.getId());
         accuses.forEach(Accuse::delete);
         accuseRepository.saveAll(accuses);
     }
 
     @Override
-    public void processMemberUpdated(String memberId) {
+    public void processMemberUpdated(Member member) {
         // do nothing
     }
 
diff --git a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
index 8d2ef818f..2372393aa 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
@@ -6,6 +6,7 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.award.dao.AwardRepository;
 import page.clab.api.domain.award.domain.Award;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -26,14 +27,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(String memberId) {
-        List<Award> awards = awardRepository.findByMemberId(memberId);
+    public void processMemberDeleted(Member member) {
+        List<Award> awards = awardRepository.findByMemberId(member.getId());
         awards.forEach(Award::delete);
         awardRepository.saveAll(awards);
     }
 
     @Override
-    public void processMemberUpdated(String memberId) {
+    public void processMemberUpdated(Member member) {
         // do nothing
     }
 }
diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java b/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
index e27ec84a8..bd1a1d131 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
@@ -6,6 +6,7 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.blog.dao.BlogRepository;
 import page.clab.api.domain.blog.domain.Blog;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -26,14 +27,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(String memberId) {
-        List<Blog> blogs = blogRepository.findByMemberId(memberId);
+    public void processMemberDeleted(Member member) {
+        List<Blog> blogs = blogRepository.findByMemberId(member.getId());
         blogs.forEach(Blog::delete);
         blogRepository.saveAll(blogs);
     }
 
     @Override
-    public void processMemberUpdated(String memberId) {
+    public void processMemberUpdated(Member member) {
         // do nothing
     }
 }
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
index eef69cf84..e7ffa7a03 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
@@ -6,6 +6,7 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.board.dao.BoardRepository;
 import page.clab.api.domain.board.domain.Board;
+import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -26,14 +27,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(String memberId) {
-        List<Board> boards = boardRepository.findByMemberId(memberId);
+    public void processMemberDeleted(Member member) {
+        List<Board> boards = boardRepository.findByMemberId(member.getId());
         boards.forEach(Board::delete);
         boardRepository.saveAll(boards);
     }
 
     @Override
-    public void processMemberUpdated(String memberId) {
+    public void processMemberUpdated(Member member) {
         // do nothing
     }
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberService.java b/src/main/java/page/clab/api/domain/member/application/MemberService.java
index 189b5b9a4..820fdb6b2 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberService.java
@@ -119,7 +119,7 @@ public String updateMemberInfo(String memberId, MemberUpdateRequestDto requestDt
         updateMember(requestDto, member);
         validationService.checkValid(member);
         memberRepository.save(member);
-        eventPublisher.publishEvent(new MemberUpdatedEvent(this, member.getId()));
+        eventPublisher.publishEvent(new MemberUpdatedEvent(this, member));
         return member.getId();
     }
 
@@ -143,7 +143,7 @@ public String verifyResetMemberPassword(VerificationRequestDto requestDto) {
     public String deleteMember(String memberId) {
         Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         memberRepository.delete(member);
-        eventPublisher.publishEvent(new MemberDeletedEvent(this, member.getId()));
+        eventPublisher.publishEvent(new MemberDeletedEvent(this, member));
         return member.getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
index c9d40fa5e..692247e07 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
@@ -2,15 +2,16 @@
 
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
+import page.clab.api.domain.member.domain.Member;
 
 @Getter
 public class MemberDeletedEvent extends ApplicationEvent {
 
-    private final String memberId;
+    private final Member member;
 
-    public MemberDeletedEvent(Object source, String memberId) {
+    public MemberDeletedEvent(Object source, Member member) {
         super(source);
-        this.memberId = memberId;
+        this.member = member;
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
index 76746915d..4081107c0 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
@@ -14,11 +14,11 @@ public class MemberEventDispatcher {
 
     @EventListener
     public void handleMemberDeletedEvent(MemberDeletedEvent event) {
-        processors.forEach(processor -> processor.processMemberDeleted(event.getMemberId()));
+        processors.forEach(processor -> processor.processMemberDeleted(event.getMember()));
     }
 
     @EventListener
     public void handleMemberUpdatedEvent(MemberUpdatedEvent event) {
-        processors.forEach(processor -> processor.processMemberUpdated(event.getMemberId()));
+        processors.forEach(processor -> processor.processMemberUpdated(event.getMember()));
     }
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
index f3d34ca62..0bcb8fc4f 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
@@ -1,9 +1,11 @@
 package page.clab.api.domain.member.event;
 
+import page.clab.api.domain.member.domain.Member;
+
 public interface MemberEventProcessor {
 
-    void processMemberDeleted(String memberId);
+    void processMemberDeleted(Member member);
 
-    void processMemberUpdated(String memberId);
+    void processMemberUpdated(Member member);
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
index b1bc7b142..37efe8d1b 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
@@ -2,15 +2,16 @@
 
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
+import page.clab.api.domain.member.domain.Member;
 
 @Getter
 public class MemberUpdatedEvent extends ApplicationEvent {
 
-    private final String memberId;
+    private final Member member;
 
-    public MemberUpdatedEvent(Object source, String memberId) {
+    public MemberUpdatedEvent(Object source, Member member) {
         super(source);
-        this.memberId = memberId;
+        this.member = member;
     }
 
 }

From d1f850b3aa9d182c6d2b9614b6fb92c2ff8f65cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Tue, 25 Jun 2024 19:40:31 +0900
Subject: [PATCH 24/47] =?UTF-8?q?refactor(Member):=20Book=EC=97=90?=
 =?UTF-8?q?=EC=84=9C=20Member=EB=A5=BC=20=EC=99=B8=EB=9E=98=ED=82=A4?=
 =?UTF-8?q?=EB=A1=9C=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../book/application/BookEventProcessor.java  | 34 +++++++++++++++++++
 .../application/BookLoanRecordService.java    | 30 ++++++++--------
 .../domain/book/application/BookService.java  |  5 ++-
 .../book/dao/BookLoanRecordRepository.java    |  3 +-
 .../dao/BookLoanRecordRepositoryImpl.java     | 12 +++----
 .../api/domain/book/dao/BookRepository.java   |  3 +-
 .../domain/book/dao/BookRepositoryImpl.java   |  6 ++--
 .../clab/api/domain/book/domain/Book.java     | 27 ++++++++-------
 .../domain/book/domain/BookLoanRecord.java    | 22 +++++++-----
 .../dto/response/BookDetailsResponseDto.java  |  4 +--
 .../book/dto/response/BookResponseDto.java    |  4 +--
 11 files changed, 95 insertions(+), 55 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java b/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java
new file mode 100644
index 000000000..45af568a8
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java
@@ -0,0 +1,34 @@
+package page.clab.api.domain.book.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+@Component
+@RequiredArgsConstructor
+public class BookEventProcessor implements MemberEventProcessor {
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        // do nothing
+    }
+
+    @Override
+    @Transactional
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
index 900781678..885488eb7 100644
--- a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
+++ b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
@@ -46,10 +46,10 @@ public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOp
             Member borrower = memberLookupService.getCurrentMember();
             borrower.checkLoanSuspension();
 
-            validateBorrowLimit(borrower);
+            validateBorrowLimit(borrower.getId());
 
             Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
-            checkIfLoanAlreadyApplied(book, borrower);
+            checkIfLoanAlreadyApplied(book, borrower.getId());
 
             BookLoanRecord bookLoanRecord = BookLoanRecord.create(book, borrower);
             validationService.checkValid(bookLoanRecord);
@@ -64,41 +64,43 @@ public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOp
     @Transactional
     public Long returnBook(BookLoanRecordRequestDto requestDto) {
         Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = currentMember.getId();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
-        book.returnBook(currentMember);
+        book.returnBook(currentMemberId);
         bookRepository.save(book);
 
         BookLoanRecord bookLoanRecord = getBookLoanRecordByBookAndReturnedAtIsNullOrThrow(book);
-        bookLoanRecord.markAsReturned();
+        bookLoanRecord.markAsReturned(currentMember);
         validationService.checkValid(bookLoanRecord);
 
-        notificationService.sendNotificationToMember(currentMember.getId(), "[" + book.getTitle() + "] 도서 반납이 완료되었습니다.");
+        notificationService.sendNotificationToMember(currentMemberId, "[" + book.getTitle() + "] 도서 반납이 완료되었습니다.");
         return bookLoanRecordRepository.save(bookLoanRecord).getId();
     }
 
     @Transactional
     public Long extendBookLoan(BookLoanRecordRequestDto requestDto) {
         Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = currentMember.getId();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
 
-        book.validateCurrentBorrower(currentMember);
+        book.validateCurrentBorrower(currentMemberId);
         BookLoanRecord bookLoanRecord = getBookLoanRecordByBookAndReturnedAtIsNullOrThrow(book);
-        bookLoanRecord.extendLoan();
+        bookLoanRecord.extendLoan(currentMember);
         validationService.checkValid(bookLoanRecord);
 
-        notificationService.sendNotificationToMember(currentMember.getId(), "[" + book.getTitle() + "] 도서 대출 연장이 완료되었습니다.");
+        notificationService.sendNotificationToMember(currentMemberId, "[" + book.getTitle() + "] 도서 대출 연장이 완료되었습니다.");
 
         return bookLoanRecordRepository.save(bookLoanRecord).getId();
     }
 
     @Transactional
     public Long approveBookLoan(Long bookLoanRecordId) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         BookLoanRecord bookLoanRecord = getBookLoanRecordByIdOrThrow(bookLoanRecordId);
         Book book = bookService.getBookByIdOrThrow(bookLoanRecord.getBook().getId());
 
         book.validateBookIsNotBorrowed();
-        validateBorrowLimit(currentMember);
+        validateBorrowLimit(currentMemberId);
         bookLoanRecord.approve();
 
         validationService.checkValid(bookLoanRecord);
@@ -134,16 +136,16 @@ public BookLoanRecord getBookLoanRecordByBookAndReturnedAtIsNullOrThrow(Book boo
                 .orElseThrow(() -> new NotFoundException("해당 도서 대출 기록이 없습니다."));
     }
 
-    private void validateBorrowLimit(Member borrower) {
-        int borrowedBookCount = bookService.getNumberOfBooksBorrowedByMember(borrower);
+    private void validateBorrowLimit(String borrowerId) {
+        int borrowedBookCount = bookService.getNumberOfBooksBorrowedByMember(borrowerId);
         int maxBorrowableBookCount = 3;
         if (borrowedBookCount >= maxBorrowableBookCount) {
             throw new MaxBorrowLimitExceededException("대출 가능한 도서의 수를 초과했습니다.");
         }
     }
 
-    private void checkIfLoanAlreadyApplied(Book book, Member borrower) {
-        bookLoanRecordRepository.findByBookAndBorrowerAndStatus(book, borrower, BookLoanStatus.PENDING)
+    private void checkIfLoanAlreadyApplied(Book book, String borrowerId) {
+        bookLoanRecordRepository.findByBookAndBorrowerIdAndStatus(book, borrowerId, BookLoanStatus.PENDING)
                 .ifPresent(bookLoanRecord -> {
                     throw new BookAlreadyAppliedForLoanException("이미 대출 신청한 도서입니다.");
                 });
diff --git a/src/main/java/page/clab/api/domain/book/application/BookService.java b/src/main/java/page/clab/api/domain/book/application/BookService.java
index b4e483478..3f5d0db75 100644
--- a/src/main/java/page/clab/api/domain/book/application/BookService.java
+++ b/src/main/java/page/clab/api/domain/book/application/BookService.java
@@ -15,7 +15,6 @@
 import page.clab.api.domain.book.dto.request.BookUpdateRequestDto;
 import page.clab.api.domain.book.dto.response.BookDetailsResponseDto;
 import page.clab.api.domain.book.dto.response.BookResponseDto;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -77,8 +76,8 @@ public BookLoanRecord getBookLoanRecordByBookAndReturnedAtIsNull(Book book) {
                 .orElse(null);
     }
 
-    public int getNumberOfBooksBorrowedByMember(Member member) {
-        return bookRepository.countByBorrower(member);
+    public int getNumberOfBooksBorrowedByMember(String member) {
+        return bookRepository.countByBorrowerId(member);
     }
 
     private LocalDateTime getDueDateForBook(Book book) {
diff --git a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepository.java b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepository.java
index 0998e4554..b65d7ced3 100644
--- a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepository.java
+++ b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepository.java
@@ -4,7 +4,6 @@
 import page.clab.api.domain.book.domain.Book;
 import page.clab.api.domain.book.domain.BookLoanRecord;
 import page.clab.api.domain.book.domain.BookLoanStatus;
-import page.clab.api.domain.member.domain.Member;
 
 import java.util.Optional;
 
@@ -12,6 +11,6 @@ public interface BookLoanRecordRepository extends JpaRepository<BookLoanRecord,
 
     Optional<BookLoanRecord> findByBookAndReturnedAtIsNullAndStatus(Book book, BookLoanStatus bookLoanStatus);
 
-    Optional<Object> findByBookAndBorrowerAndStatus(Book book, Member borrower, BookLoanStatus bookLoanStatus);
+    Optional<Object> findByBookAndBorrowerIdAndStatus(Book book, String borrowerId, BookLoanStatus bookLoanStatus);
 
 }
diff --git a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
index ac79295ff..34e0ed84f 100644
--- a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
@@ -12,10 +12,10 @@
 import page.clab.api.domain.book.domain.QBookLoanRecord;
 import page.clab.api.domain.book.dto.response.BookLoanRecordOverdueResponseDto;
 import page.clab.api.domain.book.dto.response.BookLoanRecordResponseDto;
+import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.time.LocalDateTime;
 import java.util.List;
-import page.clab.api.global.util.OrderSpecifierUtil;
 
 @Repository
 @RequiredArgsConstructor
@@ -29,7 +29,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
 
         BooleanBuilder builder = new BooleanBuilder();
         if (bookId != null) builder.and(bookLoanRecord.book.id.eq(bookId));
-        if (borrowerId != null && !borrowerId.trim().isEmpty()) builder.and(bookLoanRecord.borrower.id.eq(borrowerId));
+        if (borrowerId != null && !borrowerId.trim().isEmpty()) builder.and(bookLoanRecord.borrowerId.eq(borrowerId));
         if (status != null) builder.and(bookLoanRecord.status.eq(status));
 
         List<BookLoanRecordResponseDto> results = queryFactory
@@ -39,8 +39,8 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
                         bookLoanRecord.book.id,
                         bookLoanRecord.book.title,
                         bookLoanRecord.book.imageUrl,
-                        bookLoanRecord.borrower.id,
-                        bookLoanRecord.borrower.name,
+                        bookLoanRecord.borrowerId,
+                        bookLoanRecord.borrowerName,
                         bookLoanRecord.borrowedAt,
                         bookLoanRecord.returnedAt,
                         bookLoanRecord.dueDate,
@@ -73,8 +73,8 @@ public Page<BookLoanRecordOverdueResponseDto> findOverdueBookLoanRecords(Pageabl
                         BookLoanRecordOverdueResponseDto.class,
                         bookLoanRecord.book.id,
                         bookLoanRecord.book.title,
-                        bookLoanRecord.borrower.id,
-                        bookLoanRecord.borrower.name,
+                        bookLoanRecord.borrowerId,
+                        bookLoanRecord.borrowerName,
                         bookLoanRecord.borrowedAt,
                         bookLoanRecord.dueDate,
                         bookLoanRecord.status
diff --git a/src/main/java/page/clab/api/domain/book/dao/BookRepository.java b/src/main/java/page/clab/api/domain/book/dao/BookRepository.java
index 2944b2ec6..03927aa21 100644
--- a/src/main/java/page/clab/api/domain/book/dao/BookRepository.java
+++ b/src/main/java/page/clab/api/domain/book/dao/BookRepository.java
@@ -6,7 +6,6 @@
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import page.clab.api.domain.book.domain.Book;
-import page.clab.api.domain.member.domain.Member;
 
 import java.util.List;
 
@@ -14,7 +13,7 @@ public interface BookRepository extends JpaRepository<Book, Long>, BookRepositor
 
     List<Book> findAllByOrderByCreatedAtDesc();
 
-    int countByBorrower(Member member);
+    int countByBorrowerId(String memberId);
 
     @Query(value = "SELECT b.* FROM book b WHERE b.is_deleted = true", nativeQuery = true)
     Page<Book> findAllByIsDeletedTrue(Pageable pageable);
diff --git a/src/main/java/page/clab/api/domain/book/dao/BookRepositoryImpl.java b/src/main/java/page/clab/api/domain/book/dao/BookRepositoryImpl.java
index a904c02b2..e6bd7e2e2 100644
--- a/src/main/java/page/clab/api/domain/book/dao/BookRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/book/dao/BookRepositoryImpl.java
@@ -10,9 +10,9 @@
 import page.clab.api.domain.book.domain.Book;
 import page.clab.api.domain.book.domain.QBook;
 import page.clab.api.domain.member.domain.QMember;
+import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.util.List;
-import page.clab.api.global.util.OrderSpecifierUtil;
 
 @Repository
 @RequiredArgsConstructor
@@ -33,7 +33,7 @@ public Page<Book> findByConditions(String title, String category, String publish
         if (borrowerName != null) builder.and(borrower.name.eq(borrowerName));
 
         List<Book> books = queryFactory.selectFrom(book)
-                .leftJoin(book.borrower, borrower)
+                .leftJoin(borrower).on(book.borrowerId.eq(borrower.id))
                 .where(builder)
                 .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, book))
                 .offset(pageable.getOffset())
@@ -41,7 +41,7 @@ public Page<Book> findByConditions(String title, String category, String publish
                 .fetch();
 
         long count = queryFactory.selectFrom(book)
-                .leftJoin(book.borrower, borrower)
+                .leftJoin(borrower).on(book.borrowerId.eq(borrower.id))
                 .where(builder)
                 .fetchCount();
 
diff --git a/src/main/java/page/clab/api/domain/book/domain/Book.java b/src/main/java/page/clab/api/domain/book/domain/Book.java
index 03e451f53..640154bed 100644
--- a/src/main/java/page/clab/api/domain/book/domain/Book.java
+++ b/src/main/java/page/clab/api/domain/book/domain/Book.java
@@ -6,8 +6,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.persistence.Version;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -19,7 +17,6 @@
 import page.clab.api.domain.book.dto.request.BookUpdateRequestDto;
 import page.clab.api.domain.book.exception.BookAlreadyBorrowedException;
 import page.clab.api.domain.book.exception.InvalidBorrowerException;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.util.StringJsonConverter;
 
@@ -57,9 +54,11 @@ public class Book extends BaseEntity {
     @Convert(converter = StringJsonConverter.class)
     private List<String> reviewLinks;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id")
-    private Member borrower;
+    @Column(name = "member_id")
+    private String borrowerId;
+
+    @Column(name = "member_name")
+    private String borrowerName;
 
     @Version
     private Long version;
@@ -77,19 +76,23 @@ public void updateIsDeleted(Boolean isDeleted) {
         this.isDeleted = isDeleted;
     }
 
+    public boolean isBorrower(String borrowerId) {
+        return this.borrowerId == null || !this.borrowerId.equals(borrowerId);
+    }
+
     public void validateBookIsNotBorrowed() {
-        if (this.borrower != null) {
+        if (this.borrowerId != null) {
             throw new BookAlreadyBorrowedException("이미 대출 중인 도서입니다.");
         }
     }
 
-    public void returnBook(Member currentMember) {
-        validateCurrentBorrower(currentMember);
-        this.borrower = null;
+    public void returnBook(String borrowerId) {
+        validateCurrentBorrower(borrowerId);
+        this.borrowerId = null;
     }
 
-    public void validateCurrentBorrower(Member currentMember) {
-        if (this.borrower == null || !this.borrower.equals(currentMember)) {
+    public void validateCurrentBorrower(String borrowerId) {
+        if (isBorrower(borrowerId)) {
             throw new InvalidBorrowerException("대출한 도서와 회원 정보가 일치하지 않습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java b/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
index 93aa70f45..aeed93507 100644
--- a/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
+++ b/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
@@ -1,5 +1,6 @@
 package page.clab.api.domain.book.domain;
 
+import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EnumType;
 import jakarta.persistence.Enumerated;
@@ -40,9 +41,11 @@ public class BookLoanRecord extends BaseEntity {
     @JoinColumn(name = "book_id", nullable = false)
     private Book book;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member borrower;
+    @Column(name = "member_id", nullable = false)
+    private String borrowerId;
+
+    @Column(name = "member_name", nullable = false)
+    private String borrowerName;
 
     private LocalDateTime borrowedAt;
 
@@ -58,20 +61,21 @@ public class BookLoanRecord extends BaseEntity {
     public static BookLoanRecord create(Book book, Member borrower) {
         return BookLoanRecord.builder()
                 .book(book)
-                .borrower(borrower)
+                .borrowerId(borrower.getId())
+                .borrowerName(borrower.getName())
                 .loanExtensionCount(0L)
                 .status(BookLoanStatus.PENDING)
                 .build();
     }
 
-    public void markAsReturned() {
+    public void markAsReturned(Member borrower) {
         if (this.returnedAt != null) {
             throw new BookAlreadyReturnedException("이미 반납된 도서입니다.");
         }
         this.returnedAt = LocalDateTime.now();
         if (isOverdue(returnedAt)) {
             long overdueDays = ChronoUnit.DAYS.between(this.dueDate, this.returnedAt);
-            this.borrower.handleOverdueAndSuspension(overdueDays);
+            borrower.handleOverdueAndSuspension(overdueDays);
         }
         this.status = BookLoanStatus.RETURNED;
     }
@@ -80,11 +84,11 @@ private boolean isOverdue(LocalDateTime returnedAt) {
         return returnedAt.isAfter(this.dueDate);
     }
 
-    public void extendLoan() {
+    public void extendLoan(Member borrower) {
         final long MAX_EXTENSIONS = 2;
         LocalDateTime now = LocalDateTime.now();
 
-        if (this.borrower.getLoanSuspensionDate() != null && now.isBefore(this.borrower.getLoanSuspensionDate())) {
+        if (borrower.getLoanSuspensionDate() != null && now.isBefore(borrower.getLoanSuspensionDate())) {
             throw new LoanSuspensionException("대출 정지 중입니다. 연장할 수 없습니다.");
         }
         if (now.isAfter(this.dueDate)) {
@@ -102,7 +106,7 @@ public void approve() {
         if (this.status != BookLoanStatus.PENDING) {
             throw new LoanNotPendingException("대출 신청 상태가 아닙니다.");
         }
-        this.book.setBorrower(this.borrower);
+        this.book.setBorrowerId(this.borrowerId);
         this.status = BookLoanStatus.APPROVED;
         this.borrowedAt = LocalDateTime.now();
         this.dueDate = LocalDateTime.now().plusWeeks(1);
diff --git a/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java b/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
index 7e5c0c459..a5f69adf9 100644
--- a/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
@@ -38,8 +38,8 @@ public class BookDetailsResponseDto {
     public static BookDetailsResponseDto toDto(Book book, LocalDateTime dueDate) {
         return BookDetailsResponseDto.builder()
                 .id(book.getId())
-                .borrowerId(book.getBorrower() == null ? null : book.getBorrower().getId())
-                .borrowerName(book.getBorrower() == null ? null : book.getBorrower().getName())
+                .borrowerId(book.getBorrowerId() == null ? null : book.getBorrowerId())
+                .borrowerName(book.getBorrowerName() == null ? null : book.getBorrowerName())
                 .category(book.getCategory())
                 .title(book.getTitle())
                 .author(book.getAuthor())
diff --git a/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java b/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
index 5920cf05e..df642cb19 100644
--- a/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
+++ b/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
@@ -35,8 +35,8 @@ public class BookResponseDto {
     public static BookResponseDto toDto(Book book, LocalDateTime dueDate) {
         return BookResponseDto.builder()
                 .id(book.getId())
-                .borrowerId(book.getBorrower() == null ? null : book.getBorrower().getId())
-                .borrowerName(book.getBorrower() == null ? null : book.getBorrower().getName())
+                .borrowerId(book.getBorrowerId() == null ? null : book.getBorrowerId())
+                .borrowerName(book.getBorrowerName() == null ? null : book.getBorrowerName())
                 .category(book.getCategory())
                 .title(book.getTitle())
                 .author(book.getAuthor())

From faed6a92f8cd9f3845c88c549221fa79d8b06783 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 00:43:50 +0900
Subject: [PATCH 25/47] =?UTF-8?q?refactor(Award):=20=EC=82=AD=EC=A0=9C=20?=
 =?UTF-8?q?=EB=B0=8F=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?=
 =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../clab/api/domain/award/application/AwardService.java     | 3 ++-
 src/main/java/page/clab/api/domain/award/domain/Award.java  | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/award/application/AwardService.java b/src/main/java/page/clab/api/domain/award/application/AwardService.java
index cfb50f6cc..840dfcf49 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardService.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardService.java
@@ -62,7 +62,8 @@ public Long deleteAward(Long awardId) throws PermissionDeniedException {
         Member currentMember = memberLookupService.getCurrentMember();
         Award award = getAwardByIdOrThrow(awardId);
         award.validateAccessPermission(currentMember);
-        awardRepository.delete(award);
+        award.delete();
+        awardRepository.save(award);
         return award.getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/award/domain/Award.java b/src/main/java/page/clab/api/domain/award/domain/Award.java
index e54d92e3c..f05805376 100644
--- a/src/main/java/page/clab/api/domain/award/domain/Award.java
+++ b/src/main/java/page/clab/api/domain/award/domain/Award.java
@@ -65,12 +65,12 @@ public void delete() {
         this.isDeleted = true;
     }
 
-    public boolean isOwner(String memberId) {
-        return this.memberId.equals(memberId);
+    public boolean isOwner(Member member) {
+        return member.isSameMember(memberId);
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member.getId()) && !member.isAdminRole()) {
+        if (!isOwner(member) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정/삭제할 권한이 없습니다.");
         }
     }

From 86a911cb1aa535ebdf22cf9126dbe7ef848144af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 00:51:34 +0900
Subject: [PATCH 26/47] =?UTF-8?q?refactor(Blog):=20=EC=82=AD=EC=A0=9C=20?=
 =?UTF-8?q?=EB=B0=8F=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?=
 =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../page/clab/api/domain/blog/application/BlogService.java     | 3 ++-
 src/main/java/page/clab/api/domain/blog/domain/Blog.java       | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index 5f98d352a..f5a1090ab 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -72,7 +72,8 @@ public Long deleteBlog(Long blogId) throws PermissionDeniedException {
         Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         blog.validateAccessPermission(currentMember);
-        blogRepository.delete(blog);
+        blog.delete();
+        blogRepository.save(blog);
         return blog.getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/blog/domain/Blog.java b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
index 6b3ab132d..470c8cfc1 100644
--- a/src/main/java/page/clab/api/domain/blog/domain/Blog.java
+++ b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
@@ -66,7 +66,7 @@ public void delete() {
     }
 
     public boolean isOwner(Member member) {
-        return this.memberId.equals(member.getId());
+        return member.isSameMember(memberId);
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {

From 20359e21adcf4e51bb82fc0f82bfde220f659a9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:00:18 +0900
Subject: [PATCH 27/47] =?UTF-8?q?refactor(Blog):=20=EB=8F=84=EB=A9=94?=
 =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EA=B0=80=20=EC=A7=81=EC=A0=91?=
 =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20Member=20=EB=8F=84=EB=A9=94?=
 =?UTF-8?q?=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=B0=B8=EC=A1=B0?=
 =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EB=B3=80?=
 =?UTF-8?q?=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../page/clab/api/domain/blog/application/BlogService.java | 6 +++---
 .../domain/blog/dto/response/BlogDetailsResponseDto.java   | 7 +++----
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index f5a1090ab..8954fb2ea 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -47,15 +47,14 @@ public BlogDetailsResponseDto getBlogDetails(Long blogId) {
         Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
         boolean isOwner = blog.isOwner(currentMember);
-        return BlogDetailsResponseDto.toDto(blog, currentMember, isOwner);
+        return BlogDetailsResponseDto.toDto(blog, currentMember.getId(), currentMember.getName(), isOwner);
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageable) {
         Member currentMember = memberLookupService.getCurrentMember();
         Page<Blog> blogs = blogRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(blogs
-                .map(blog -> BlogDetailsResponseDto.toDto(blog, currentMember, blog.isOwner(currentMember))));
+        return new PagedResponseDto<>(blogs.map(blog -> BlogDetailsResponseDto.toDto(blog, currentMember.getId(), currentMember.getName(), blog.isOwner(currentMember))));
     }
 
     @Transactional
@@ -68,6 +67,7 @@ public Long updateBlog(Long blogId, BlogUpdateRequestDto requestDto) throws Perm
         return blogRepository.save(blog).getId();
     }
 
+    @Transactional
     public Long deleteBlog(Long blogId) throws PermissionDeniedException {
         Member currentMember = memberLookupService.getCurrentMember();
         Blog blog = getBlogByIdOrThrow(blogId);
diff --git a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
index c96d9808a..14a9b179e 100644
--- a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
@@ -4,7 +4,6 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.blog.domain.Blog;
-import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 
@@ -33,11 +32,11 @@ public class BlogDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BlogDetailsResponseDto toDto(Blog blog, Member member, boolean isOwner) {
+    public static BlogDetailsResponseDto toDto(Blog blog, String memberId, String memberName, boolean isOwner) {
         return BlogDetailsResponseDto.builder()
                 .id(blog.getId())
-                .memberId(member.getId())
-                .name(member.getName())
+                .memberId(memberId)
+                .name(memberName)
                 .title(blog.getTitle())
                 .subTitle(blog.getSubTitle())
                 .content(blog.getContent())

From 89002253131cb0491e5e38b12409294dae3ea486 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:09:04 +0900
Subject: [PATCH 28/47] =?UTF-8?q?refactor(Accuse):=20=EC=8B=A0=EA=B3=A0=20?=
 =?UTF-8?q?=EC=95=8C=EB=A6=BC=EC=9D=84=20=EB=B3=B4=EB=82=BC=20=EB=95=8C=20?=
 =?UTF-8?q?memberId=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/accuse/application/AccuseService.java | 16 ++++++++--------
 .../application/NotificationService.java         |  5 +++--
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index 8393637f9..e3f0dbccb 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -34,6 +34,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 @Service
 @RequiredArgsConstructor
@@ -60,8 +61,7 @@ public class AccuseService {
     public Long createAccuse(AccuseRequestDto requestDto) {
         TargetType type = requestDto.getTargetType();
         Long targetId = requestDto.getTargetId();
-        Member currentMember = memberLookupService.getCurrentMember();
-        String memberId = currentMember.getId();
+        String memberId = memberLookupService.getCurrentMemberId();
 
         validateAccuseRequest(type, targetId, memberId);
 
@@ -72,8 +72,8 @@ public Long createAccuse(AccuseRequestDto requestDto) {
         Accuse accuse = findOrCreateAccuse(requestDto, memberId, target);
         validationService.checkValid(accuse);
 
-        notificationService.sendNotificationToMember(currentMember, "신고하신 내용이 접수되었습니다.");
-        notificationService.sendNotificationToSuperAdmins(currentMember.getName() + "님이 신고를 접수하였습니다. 확인해주세요.");
+        notificationService.sendNotificationToMember(memberId, "신고하신 내용이 접수되었습니다.");
+        notificationService.sendNotificationToSuperAdmins(memberId + "님이 신고를 접수하였습니다. 확인해주세요.");
         return accuseRepository.save(accuse).getId();
     }
 
@@ -166,10 +166,10 @@ private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget>
     }
 
     private void sendStatusUpdateNotifications(AccuseStatus status, AccuseTarget target) {
-        List<Member> members = accuseRepository.findByTarget(target.getTargetType(), target.getTargetReferenceId()).stream()
-                .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
-                .toList();
-        notificationService.sendNotificationToMembers(members, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
+        List<String> memberIds = accuseRepository.findByTarget(target.getTargetType(), target.getTargetReferenceId()).stream()
+                .map(Accuse::getMemberId)
+                .collect(Collectors.toList());
+        notificationService.sendNotificationToMembers(memberIds, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
index 49b4e3fa8..ffbed68f6 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
@@ -70,8 +70,9 @@ public void sendNotificationToMember(Member member, String content) {
         notificationRepository.save(notification);
     }
 
-    public void sendNotificationToMembers(List<Member> members, String content) {
-        List<Notification> notifications = members.stream()
+    public void sendNotificationToMembers(List<String> memberIds, String content) {
+        List<Notification> notifications = memberIds.stream()
+                .map(memberLookupService::getMemberByIdOrThrow)
                 .map(member -> Notification.create(member, content))
                 .toList();
         notificationRepository.saveAll(notifications);

From 226962c01180858a18bef5f7e8ebb7953b422aa4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:26:02 +0900
Subject: [PATCH 29/47] =?UTF-8?q?refactor(Accuse):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../accuse/application/AccuseService.java     |  8 +++---
 .../accuse/dto/response/AccuseMemberInfo.java | 27 -------------------
 .../dto/response/AccuseResponseDto.java       |  7 ++---
 .../application/MemberLookupService.java      |  3 +++
 .../application/MemberLookupServiceImpl.java  |  8 ++++++
 .../member/dto/shared/MemberInfoDto.java      | 22 +++++++++++++++
 6 files changed, 40 insertions(+), 35 deletions(-)
 delete mode 100644 src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
 create mode 100644 src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index e3f0dbccb..756333186 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -15,7 +15,7 @@
 import page.clab.api.domain.accuse.domain.AccuseTargetId;
 import page.clab.api.domain.accuse.domain.TargetType;
 import page.clab.api.domain.accuse.dto.request.AccuseRequestDto;
-import page.clab.api.domain.accuse.dto.response.AccuseMemberInfo;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.domain.accuse.dto.response.AccuseMyResponseDto;
 import page.clab.api.domain.accuse.dto.response.AccuseResponseDto;
 import page.clab.api.domain.accuse.exception.AccuseTargetTypeIncorrectException;
@@ -24,7 +24,6 @@
 import page.clab.api.domain.comment.application.CommentService;
 import page.clab.api.domain.comment.domain.Comment;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.domain.review.application.ReviewService;
 import page.clab.api.domain.review.domain.Review;
@@ -155,9 +154,8 @@ private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget>
                     if (accuses.isEmpty()) {
                         return null;
                     }
-                    List<AccuseMemberInfo> members = accuses.stream()
-                            .map(accuse -> memberLookupService.getMemberById(accuse.getMemberId()))
-                            .map(AccuseMemberInfo::create)
+                    List<MemberInfoDto> members = accuses.stream()
+                            .map(accuse -> memberLookupService.getMemberInfoById(accuse.getMemberId()))
                             .toList();
                     return AccuseResponseDto.toDto(accuses.getFirst(), members);
                 })
diff --git a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
deleted file mode 100644
index a7b019664..000000000
--- a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseMemberInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package page.clab.api.domain.accuse.dto.response;
-
-import lombok.Builder;
-import lombok.Getter;
-import page.clab.api.domain.member.domain.Member;
-
-import java.time.LocalDateTime;
-
-@Getter
-@Builder
-public class AccuseMemberInfo {
-
-    private String memberId;
-
-    private String name;
-
-    private LocalDateTime createdAt;
-
-    public static AccuseMemberInfo create(Member member) {
-        return AccuseMemberInfo.builder()
-                .memberId(member.getId())
-                .name(member.getName())
-                .createdAt(member.getCreatedAt())
-                .build();
-    }
-
-}
diff --git a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
index b546afd8c..dc42cea2c 100644
--- a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
+++ b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
@@ -5,6 +5,7 @@
 import page.clab.api.domain.accuse.domain.Accuse;
 import page.clab.api.domain.accuse.domain.AccuseStatus;
 import page.clab.api.domain.accuse.domain.TargetType;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -13,7 +14,7 @@
 @Builder
 public class AccuseResponseDto {
 
-    private List<AccuseMemberInfo> members;
+    private List<MemberInfoDto> members;
 
     private TargetType targetType;
 
@@ -27,7 +28,7 @@ public class AccuseResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static AccuseResponseDto toDto(Accuse accuse, List<AccuseMemberInfo> members) {
+    public static AccuseResponseDto toDto(Accuse accuse, List<MemberInfoDto> members) {
         return AccuseResponseDto.builder()
                 .members(members)
                 .targetType(accuse.getTarget().getTargetType())
@@ -35,7 +36,7 @@ public static AccuseResponseDto toDto(Accuse accuse, List<AccuseMemberInfo> memb
                 .reason(accuse.getReason())
                 .accuseStatus(accuse.getTarget().getAccuseStatus())
                 .accuseCount(accuse.getTarget().getAccuseCount())
-                .createdAt(accuse.getTarget().getCreatedAt())
+                .createdAt(accuse.getCreatedAt())
                 .build();
     }
 
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index ae6e581ed..963e28c98 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -1,6 +1,7 @@
 package page.clab.api.domain.member.application;
 
 import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 
 import java.util.List;
@@ -27,4 +28,6 @@ public interface MemberLookupService {
 
     List<Member> getSuperAdmins();
 
+    MemberInfoDto getMemberInfoById(String memberId);
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 1fd2b004a..743e00f96 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -4,6 +4,7 @@
 import org.springframework.stereotype.Service;
 import page.clab.api.domain.member.dao.MemberRepository;
 import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
@@ -80,4 +81,11 @@ public List<Member> getSuperAdmins() {
                 .toList();
     }
 
+    @Override
+    public MemberInfoDto getMemberInfoById(String memberId) {
+        return memberRepository.findById(memberId)
+                .map(MemberInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java
new file mode 100644
index 000000000..99263561e
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java
@@ -0,0 +1,22 @@
+package page.clab.api.domain.member.dto.shared;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.member.domain.Member;
+
+@Getter
+@Builder
+public class MemberInfoDto {
+
+    private String memberId;
+
+    private String memberName;
+
+    public static MemberInfoDto create(Member member) {
+        return MemberInfoDto.builder()
+                .memberId(member.getId())
+                .memberName(member.getName())
+                .build();
+    }
+
+}

From 4ba27f3e01ad9e60c8dceda54be3eed0a8743bd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:32:43 +0900
Subject: [PATCH 30/47] =?UTF-8?q?refactor(Blog):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../clab/api/domain/blog/application/BlogService.java | 11 ++++++-----
 .../java/page/clab/api/domain/blog/domain/Blog.java   |  6 +++---
 .../blog/dto/response/BlogDetailsResponseDto.java     |  7 ++++---
 .../member/application/MemberLookupService.java       |  4 +++-
 .../member/application/MemberLookupServiceImpl.java   | 10 +++++++++-
 5 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index 8954fb2ea..2f7ee1fcc 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -13,6 +13,7 @@
 import page.clab.api.domain.blog.dto.response.BlogResponseDto;
 import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -44,17 +45,17 @@ public PagedResponseDto<BlogResponseDto> getBlogsByConditions(String title, Stri
 
     @Transactional(readOnly = true)
     public BlogDetailsResponseDto getBlogDetails(Long blogId) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberInfoDto currentMemberInfo = memberLookupService.getCurrentMemberInfo();
         Blog blog = getBlogByIdOrThrow(blogId);
-        boolean isOwner = blog.isOwner(currentMember);
-        return BlogDetailsResponseDto.toDto(blog, currentMember.getId(), currentMember.getName(), isOwner);
+        boolean isOwner = blog.isOwner(currentMemberInfo.getMemberId());
+        return BlogDetailsResponseDto.toDto(blog, currentMemberInfo, isOwner);
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberInfoDto currentMemberInfo = memberLookupService.getCurrentMemberInfo();
         Page<Blog> blogs = blogRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(blogs.map(blog -> BlogDetailsResponseDto.toDto(blog, currentMember.getId(), currentMember.getName(), blog.isOwner(currentMember))));
+        return new PagedResponseDto<>(blogs.map(blog -> BlogDetailsResponseDto.toDto(blog, currentMemberInfo, blog.isOwner(currentMemberInfo.getMemberId()))));
     }
 
     @Transactional
diff --git a/src/main/java/page/clab/api/domain/blog/domain/Blog.java b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
index 470c8cfc1..1384b5982 100644
--- a/src/main/java/page/clab/api/domain/blog/domain/Blog.java
+++ b/src/main/java/page/clab/api/domain/blog/domain/Blog.java
@@ -65,12 +65,12 @@ public void delete() {
         this.isDeleted = true;
     }
 
-    public boolean isOwner(Member member) {
-        return member.isSameMember(memberId);
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
     }
 
     public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+        if (!isOwner(member.getId()) && !member.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정/삭제할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
index 14a9b179e..c611e5e74 100644
--- a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
@@ -4,6 +4,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.blog.domain.Blog;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -32,11 +33,11 @@ public class BlogDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BlogDetailsResponseDto toDto(Blog blog, String memberId, String memberName, boolean isOwner) {
+    public static BlogDetailsResponseDto toDto(Blog blog, MemberInfoDto memberInfo, boolean isOwner) {
         return BlogDetailsResponseDto.builder()
                 .id(blog.getId())
-                .memberId(memberId)
-                .name(memberName)
+                .memberId(memberInfo.getMemberId())
+                .name(memberInfo.getMemberName())
                 .title(blog.getTitle())
                 .subTitle(blog.getSubTitle())
                 .content(blog.getContent())
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 963e28c98..87b185c59 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -1,8 +1,8 @@
 package page.clab.api.domain.member.application;
 
 import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 
 import java.util.List;
 
@@ -30,4 +30,6 @@ public interface MemberLookupService {
 
     MemberInfoDto getMemberInfoById(String memberId);
 
+    MemberInfoDto getCurrentMemberInfo();
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 743e00f96..31d7ac533 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -4,8 +4,8 @@
 import org.springframework.stereotype.Service;
 import page.clab.api.domain.member.dao.MemberRepository;
 import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
+import page.clab.api.domain.member.dto.shared.MemberInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -88,4 +88,12 @@ public MemberInfoDto getMemberInfoById(String memberId) {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public MemberInfoDto getCurrentMemberInfo() {
+        String currentMemberId = getCurrentMemberId();
+        return memberRepository.findById(currentMemberId)
+                .map(MemberInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
 }

From fa0ffc451dd2398a8fd99fccfd8bd5844ecf00ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:48:56 +0900
Subject: [PATCH 31/47] =?UTF-8?q?refactor(Member):=20MemberInfoDto=20->=20?=
 =?UTF-8?q?MemberBasicInfoDto=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20?=
 =?UTF-8?q?=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../api/domain/accuse/application/AccuseService.java   |  6 +++---
 .../domain/accuse/dto/response/AccuseResponseDto.java  |  6 +++---
 .../clab/api/domain/blog/application/BlogService.java  |  6 +++---
 .../blog/dto/response/BlogDetailsResponseDto.java      |  4 ++--
 .../domain/member/application/MemberLookupService.java |  6 +++---
 .../member/application/MemberLookupServiceImpl.java    | 10 +++++-----
 .../{MemberInfoDto.java => MemberBasicInfoDto.java}    |  6 +++---
 7 files changed, 22 insertions(+), 22 deletions(-)
 rename src/main/java/page/clab/api/domain/member/dto/shared/{MemberInfoDto.java => MemberBasicInfoDto.java} (71%)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
index 756333186..fd266c624 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseService.java
@@ -15,7 +15,7 @@
 import page.clab.api.domain.accuse.domain.AccuseTargetId;
 import page.clab.api.domain.accuse.domain.TargetType;
 import page.clab.api.domain.accuse.dto.request.AccuseRequestDto;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.accuse.dto.response.AccuseMyResponseDto;
 import page.clab.api.domain.accuse.dto.response.AccuseResponseDto;
 import page.clab.api.domain.accuse.exception.AccuseTargetTypeIncorrectException;
@@ -154,8 +154,8 @@ private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget>
                     if (accuses.isEmpty()) {
                         return null;
                     }
-                    List<MemberInfoDto> members = accuses.stream()
-                            .map(accuse -> memberLookupService.getMemberInfoById(accuse.getMemberId()))
+                    List<MemberBasicInfoDto> members = accuses.stream()
+                            .map(accuse -> memberLookupService.getMemberBasicInfoById(accuse.getMemberId()))
                             .toList();
                     return AccuseResponseDto.toDto(accuses.getFirst(), members);
                 })
diff --git a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
index dc42cea2c..ae2433284 100644
--- a/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
+++ b/src/main/java/page/clab/api/domain/accuse/dto/response/AccuseResponseDto.java
@@ -5,7 +5,7 @@
 import page.clab.api.domain.accuse.domain.Accuse;
 import page.clab.api.domain.accuse.domain.AccuseStatus;
 import page.clab.api.domain.accuse.domain.TargetType;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -14,7 +14,7 @@
 @Builder
 public class AccuseResponseDto {
 
-    private List<MemberInfoDto> members;
+    private List<MemberBasicInfoDto> members;
 
     private TargetType targetType;
 
@@ -28,7 +28,7 @@ public class AccuseResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static AccuseResponseDto toDto(Accuse accuse, List<MemberInfoDto> members) {
+    public static AccuseResponseDto toDto(Accuse accuse, List<MemberBasicInfoDto> members) {
         return AccuseResponseDto.builder()
                 .members(members)
                 .targetType(accuse.getTarget().getTargetType())
diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogService.java b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
index 2f7ee1fcc..e9ff4bcaa 100644
--- a/src/main/java/page/clab/api/domain/blog/application/BlogService.java
+++ b/src/main/java/page/clab/api/domain/blog/application/BlogService.java
@@ -13,7 +13,7 @@
 import page.clab.api.domain.blog.dto.response.BlogResponseDto;
 import page.clab.api.domain.member.application.MemberLookupService;
 import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -45,7 +45,7 @@ public PagedResponseDto<BlogResponseDto> getBlogsByConditions(String title, Stri
 
     @Transactional(readOnly = true)
     public BlogDetailsResponseDto getBlogDetails(Long blogId) {
-        MemberInfoDto currentMemberInfo = memberLookupService.getCurrentMemberInfo();
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
         Blog blog = getBlogByIdOrThrow(blogId);
         boolean isOwner = blog.isOwner(currentMemberInfo.getMemberId());
         return BlogDetailsResponseDto.toDto(blog, currentMemberInfo, isOwner);
@@ -53,7 +53,7 @@ public BlogDetailsResponseDto getBlogDetails(Long blogId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BlogDetailsResponseDto> getDeletedBlogs(Pageable pageable) {
-        MemberInfoDto currentMemberInfo = memberLookupService.getCurrentMemberInfo();
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
         Page<Blog> blogs = blogRepository.findAllByIsDeletedTrue(pageable);
         return new PagedResponseDto<>(blogs.map(blog -> BlogDetailsResponseDto.toDto(blog, currentMemberInfo, blog.isOwner(currentMemberInfo.getMemberId()))));
     }
diff --git a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
index c611e5e74..959e9794c 100644
--- a/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/blog/dto/response/BlogDetailsResponseDto.java
@@ -4,7 +4,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.blog.domain.Blog;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -33,7 +33,7 @@ public class BlogDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BlogDetailsResponseDto toDto(Blog blog, MemberInfoDto memberInfo, boolean isOwner) {
+    public static BlogDetailsResponseDto toDto(Blog blog, MemberBasicInfoDto memberInfo, boolean isOwner) {
         return BlogDetailsResponseDto.builder()
                 .id(blog.getId())
                 .memberId(memberInfo.getMemberId())
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 87b185c59..7dbf5c6f4 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -2,7 +2,7 @@
 
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 
 import java.util.List;
 
@@ -28,8 +28,8 @@ public interface MemberLookupService {
 
     List<Member> getSuperAdmins();
 
-    MemberInfoDto getMemberInfoById(String memberId);
+    MemberBasicInfoDto getMemberBasicInfoById(String memberId);
 
-    MemberInfoDto getCurrentMemberInfo();
+    MemberBasicInfoDto getCurrentMemberBasicInfo();
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 31d7ac533..cb4c430d9 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -5,7 +5,7 @@
 import page.clab.api.domain.member.dao.MemberRepository;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
-import page.clab.api.domain.member.dto.shared.MemberInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -82,17 +82,17 @@ public List<Member> getSuperAdmins() {
     }
 
     @Override
-    public MemberInfoDto getMemberInfoById(String memberId) {
+    public MemberBasicInfoDto getMemberBasicInfoById(String memberId) {
         return memberRepository.findById(memberId)
-                .map(MemberInfoDto::create)
+                .map(MemberBasicInfoDto::create)
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
     @Override
-    public MemberInfoDto getCurrentMemberInfo() {
+    public MemberBasicInfoDto getCurrentMemberBasicInfo() {
         String currentMemberId = getCurrentMemberId();
         return memberRepository.findById(currentMemberId)
-                .map(MemberInfoDto::create)
+                .map(MemberBasicInfoDto::create)
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberBasicInfoDto.java
similarity index 71%
rename from src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java
rename to src/main/java/page/clab/api/domain/member/dto/shared/MemberBasicInfoDto.java
index 99263561e..b7c27aec8 100644
--- a/src/main/java/page/clab/api/domain/member/dto/shared/MemberInfoDto.java
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberBasicInfoDto.java
@@ -6,14 +6,14 @@
 
 @Getter
 @Builder
-public class MemberInfoDto {
+public class MemberBasicInfoDto {
 
     private String memberId;
 
     private String memberName;
 
-    public static MemberInfoDto create(Member member) {
-        return MemberInfoDto.builder()
+    public static MemberBasicInfoDto create(Member member) {
+        return MemberBasicInfoDto.builder()
                 .memberId(member.getId())
                 .memberName(member.getName())
                 .build();

From ff63cd99096288b792f4332c4f717aa25bed25d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 01:51:49 +0900
Subject: [PATCH 32/47] =?UTF-8?q?feat(Member):=20MemberDetailedInfoDto=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/MemberLookupService.java      |  3 ++
 .../application/MemberLookupServiceImpl.java  |  9 ++++++
 .../dto/shared/MemberDetailedInfoDto.java     | 28 +++++++++++++++++++
 3 files changed, 40 insertions(+)
 create mode 100644 src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java

diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 7dbf5c6f4..d67c6e769 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -3,6 +3,7 @@
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 import java.util.List;
 
@@ -32,4 +33,6 @@ public interface MemberLookupService {
 
     MemberBasicInfoDto getCurrentMemberBasicInfo();
 
+    MemberDetailedInfoDto getCurrentMemberDetailedInfo();
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index cb4c430d9..42985f56e 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -6,6 +6,7 @@
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -96,4 +97,12 @@ public MemberBasicInfoDto getCurrentMemberBasicInfo() {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public MemberDetailedInfoDto getCurrentMemberDetailedInfo() {
+        String currentMemberId = getCurrentMemberId();
+        return memberRepository.findById(currentMemberId)
+                .map(MemberDetailedInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
new file mode 100644
index 000000000..a5116aac6
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
@@ -0,0 +1,28 @@
+package page.clab.api.domain.member.dto.shared;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.member.domain.Member;
+
+@Getter
+@Builder
+public class MemberDetailedInfoDto {
+
+    private String memberId;
+
+    private String memberName;
+
+    private Long roleLevel;
+
+    private String imageUrl;
+
+    public static MemberDetailedInfoDto create(Member member) {
+        return MemberDetailedInfoDto.builder()
+                .memberId(member.getId())
+                .memberName(member.getName())
+                .roleLevel(member.getRole().toRoleLevel())
+                .imageUrl(member.getImageUrl())
+                .build();
+    }
+
+}

From 369e1c3a71e9130493c218220ffa420a5e54e526 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 02:12:49 +0900
Subject: [PATCH 33/47] =?UTF-8?q?refactor(Board):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../board/application/BoardService.java       | 67 ++++++++++---------
 .../clab/api/domain/board/domain/Board.java   | 16 ++---
 .../domain/board/domain/SlackBoardInfo.java   |  6 +-
 .../response/BoardCategoryResponseDto.java    |  6 +-
 .../dto/response/BoardDetailsResponseDto.java |  6 +-
 .../dto/response/BoardListResponseDto.java    |  6 +-
 .../dto/response/BoardMyResponseDto.java      |  6 +-
 .../domain/board/dto/response/WriterInfo.java | 17 +++--
 .../dto/shared/MemberDetailedInfoDto.java     |  7 ++
 9 files changed, 72 insertions(+), 65 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/board/application/BoardService.java b/src/main/java/page/clab/api/domain/board/application/BoardService.java
index fe68c8937..584a14815 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardService.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardService.java
@@ -20,7 +20,8 @@
 import page.clab.api.domain.board.dto.response.BoardMyResponseDto;
 import page.clab.api.domain.comment.dao.CommentRepository;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.file.application.UploadedFileService;
@@ -55,54 +56,54 @@ public class BoardService {
 
     @Transactional
     public String createBoard(BoardRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         List<UploadedFile> uploadedFiles = uploadedFileService.getUploadedFilesByUrls(requestDto.getFileUrlList());
-        Board board = BoardRequestDto.toEntity(requestDto, currentMember.getId(), uploadedFiles);
-        board.validateAccessPermissionForCreation(currentMember);
+        Board board = BoardRequestDto.toEntity(requestDto, currentMemberInfo.getMemberId(), uploadedFiles);
+        board.validateAccessPermissionForCreation(currentMemberInfo);
         validationService.checkValid(board);
-        if (board.shouldNotifyForNewBoard(currentMember)) {
-            notificationService.sendNotificationToMember(currentMember, "[" + board.getTitle() + "] 새로운 공지사항이 등록되었습니다.");
+        if (board.shouldNotifyForNewBoard(currentMemberInfo)) {
+            notificationService.sendNotificationToMember(currentMemberInfo.getMemberId(), "[" + board.getTitle() + "] 새로운 공지사항이 등록되었습니다.");
         }
-        SlackBoardInfo boardInfo = SlackBoardInfo.create(board, currentMember);
+        SlackBoardInfo boardInfo = SlackBoardInfo.create(board, currentMemberInfo);
         slackService.sendNewBoardNotification(boardInfo);
         return boardRepository.save(board).getCategory().getKey();
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardListResponseDto> getBoards(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Page<Board> boards = boardRepository.findAll(pageable);
-        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMember)));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMemberInfo)));
     }
 
     @Transactional(readOnly = true)
     public BoardDetailsResponseDto getBoardDetails(Long boardId) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Board board = getBoardByIdOrThrow(boardId);
-        boolean hasLikeByMe = checkLikeStatus(board, currentMember);
-        boolean isOwner = board.isOwner(currentMember.getId());
-        return BoardDetailsResponseDto.toDto(board, currentMember, hasLikeByMe, isOwner);
+        boolean hasLikeByMe = checkLikeStatus(board, currentMemberInfo);
+        boolean isOwner = board.isOwner(currentMemberInfo.getMemberId());
+        return BoardDetailsResponseDto.toDto(board, currentMemberInfo, hasLikeByMe, isOwner);
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardMyResponseDto> getMyBoards(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Board> boards = getBoardByMemberId(pageable, currentMember.getId());
-        return new PagedResponseDto<>(boards.map(board -> BoardMyResponseDto.toDto(board, currentMember)));
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
+        Page<Board> boards = getBoardByMemberId(pageable, currentMemberInfo.getMemberId());
+        return new PagedResponseDto<>(boards.map(board -> BoardMyResponseDto.toDto(board, currentMemberInfo)));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardCategoryResponseDto> getBoardsByCategory(BoardCategory category, Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Page<Board> boards = getBoardByCategory(category, pageable);
-        return new PagedResponseDto<>(boards.map(board -> mapToBoardCategoryResponseDto(board, currentMember)));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardCategoryResponseDto(board, currentMemberInfo)));
     }
 
     @Transactional
     public String updateBoard(Long boardId, BoardUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Board board = getBoardByIdOrThrow(boardId);
-        board.validateAccessPermission(currentMember);
+        board.validateAccessPermission(currentMemberInfo);
         board.update(requestDto);
         validationService.checkValid(board);
         return boardRepository.save(board).getCategory().getKey();
@@ -110,15 +111,15 @@ public String updateBoard(Long boardId, BoardUpdateRequestDto requestDto) throws
 
     @Transactional
     public Long toggleLikeStatus(Long boardId) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Board board = getBoardByIdOrThrow(boardId);
-        Optional<BoardLike> boardLikeOpt = boardLikeRepository.findByBoardIdAndMemberId(board.getId(), currentMember.getId());
+        Optional<BoardLike> boardLikeOpt = boardLikeRepository.findByBoardIdAndMemberId(board.getId(), currentMemberId);
         if (boardLikeOpt.isPresent()) {
             board.decrementLikes();
             boardLikeRepository.delete(boardLikeOpt.get());
         } else {
             board.incrementLikes();
-            BoardLike newBoardLike = BoardLike.create(currentMember.getId(), board.getId());
+            BoardLike newBoardLike = BoardLike.create(currentMemberId, board.getId());
             validationService.checkValid(newBoardLike);
             boardLikeRepository.save(newBoardLike);
         }
@@ -127,29 +128,29 @@ public Long toggleLikeStatus(Long boardId) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BoardListResponseDto> getDeletedBoards(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Page<Board> boards = boardRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMember)));
+        return new PagedResponseDto<>(boards.map(board -> mapToBoardListResponseDto(board, currentMemberInfo)));
     }
 
     public String deleteBoard(Long boardId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Board board = getBoardByIdOrThrow(boardId);
-        board.validateAccessPermission(currentMember);
+        board.validateAccessPermission(currentMemberInfo);
         boardRepository.delete(board);
         return board.getCategory().getKey();
     }
 
     @NotNull
-    private BoardListResponseDto mapToBoardListResponseDto(Board board, Member member) {
+    private BoardListResponseDto mapToBoardListResponseDto(Board board, MemberDetailedInfoDto memberInfo) {
         Long commentCount = commentRepository.countByBoard(board);
-        return BoardListResponseDto.toDto(board, member, commentCount);
+        return BoardListResponseDto.toDto(board, memberInfo, commentCount);
     }
 
     @NotNull
-    private BoardCategoryResponseDto mapToBoardCategoryResponseDto(Board board, Member member) {
+    private BoardCategoryResponseDto mapToBoardCategoryResponseDto(Board board, MemberDetailedInfoDto memberInfo) {
         Long commentCount = commentRepository.countByBoard(board);
-        return BoardCategoryResponseDto.toDto(board, member, commentCount);
+        return BoardCategoryResponseDto.toDto(board, memberInfo, commentCount);
     }
 
     public Board getBoardByIdOrThrow(Long boardId) {
@@ -165,8 +166,8 @@ private Page<Board> getBoardByCategory(BoardCategory category, Pageable pageable
         return boardRepository.findAllByCategory(category, pageable);
     }
 
-    private boolean checkLikeStatus(Board board, Member member) {
-        return boardLikeRepository.existsByBoardIdAndMemberId(board.getId(), member.getId());
+    private boolean checkLikeStatus(Board board, MemberDetailedInfoDto memberInfo) {
+        return boardLikeRepository.existsByBoardIdAndMemberId(board.getId(), memberInfo.getMemberId());
     }
 
 }
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/domain/board/domain/Board.java b/src/main/java/page/clab/api/domain/board/domain/Board.java
index 8c0be374f..663e9c45f 100644
--- a/src/main/java/page/clab/api/domain/board/domain/Board.java
+++ b/src/main/java/page/clab/api/domain/board/domain/Board.java
@@ -20,7 +20,7 @@
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
 import page.clab.api.domain.board.dto.request.BoardUpdateRequestDto;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.common.file.domain.UploadedFile;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -92,8 +92,8 @@ public boolean isGraduated() {
         return this.category.equals(BoardCategory.GRADUATED);
     }
 
-    public boolean shouldNotifyForNewBoard(Member member) {
-        return member.isAdminRole() && this.category.equals(BoardCategory.NOTICE);
+    public boolean shouldNotifyForNewBoard(MemberDetailedInfoDto memberInfo) {
+        return memberInfo.isAdminRole() && this.category.equals(BoardCategory.NOTICE); // Assuming 2 is Admin role level
     }
 
     public void incrementLikes() {
@@ -110,17 +110,17 @@ public boolean isOwner(String memberId) {
         return this.memberId.equals(memberId);
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member.getId()) && !member.isAdminRole()) {
+    public void validateAccessPermission(MemberDetailedInfoDto memberInfo) throws PermissionDeniedException {
+        if (!isOwner(memberInfo.getMemberId()) && !memberInfo.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정할 권한이 없습니다.");
         }
     }
 
-    public void validateAccessPermissionForCreation(Member currentMember) throws PermissionDeniedException {
-        if (this.isNotice() && !currentMember.isAdminRole()) {
+    public void validateAccessPermissionForCreation(MemberDetailedInfoDto currentMemberInfo) throws PermissionDeniedException {
+        if (this.isNotice() && !currentMemberInfo.isAdminRole()) {
             throw new PermissionDeniedException("공지사항은 관리자만 작성할 수 있습니다.");
         }
-        if (this.isGraduated() && !currentMember.isGraduated()) {
+        if (this.isGraduated() && !currentMemberInfo.isGraduated()) {
             throw new PermissionDeniedException("졸업생 게시판은 졸업생만 작성할 수 있습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java b/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java
index 62c1f4ee8..055409963 100644
--- a/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java
+++ b/src/main/java/page/clab/api/domain/board/domain/SlackBoardInfo.java
@@ -2,7 +2,7 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 @Getter
 @Builder
@@ -14,11 +14,11 @@ public class SlackBoardInfo {
 
     private String username;
 
-    public static SlackBoardInfo create(Board board, Member member) {
+    public static SlackBoardInfo create(Board board, MemberDetailedInfoDto memberInfo) {
         return SlackBoardInfo.builder()
                 .title(board.getTitle())
                 .category(board.getCategory().getDescription())
-                .username(board.isWantAnonymous() ? board.getNickname() : member.getId() + " " + member.getName())
+                .username(board.isWantAnonymous() ? board.getNickname() : memberInfo.getMemberId() + " " + memberInfo.getMemberName())
                 .build();
     }
 
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
index c8fdce527..44074b4cf 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardCategoryResponseDto.java
@@ -3,7 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -27,8 +27,8 @@ public class BoardCategoryResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardCategoryResponseDto toDto(Board board, Member member, Long commentCount) {
-        WriterInfo writerInfo = WriterInfo.fromBoard(board, member);
+    public static BoardCategoryResponseDto toDto(Board board, MemberDetailedInfoDto memberInfo, Long commentCount) {
+        WriterInfo writerInfo = WriterInfo.fromBoard(board, memberInfo);
         return BoardCategoryResponseDto.builder()
                 .id(board.getId())
                 .category(board.getCategory().getKey())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
index 6155820ad..5177b12ab 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardDetailsResponseDto.java
@@ -4,7 +4,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.file.dto.response.UploadedFileResponseDto;
 
 import java.time.LocalDateTime;
@@ -43,8 +43,8 @@ public class BoardDetailsResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardDetailsResponseDto toDto(Board board, Member member, boolean hasLikeByMe, boolean isOwner) {
-        WriterInfo writerInfo = WriterInfo.fromBoardDetails(board, member);
+    public static BoardDetailsResponseDto toDto(Board board, MemberDetailedInfoDto memberInfo, boolean hasLikeByMe, boolean isOwner) {
+        WriterInfo writerInfo = WriterInfo.fromBoardDetails(board, memberInfo);
         return BoardDetailsResponseDto.builder()
                 .id(board.getId())
                 .writerId(writerInfo.getId())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
index c509454fd..2e1577ac2 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardListResponseDto.java
@@ -3,7 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -29,8 +29,8 @@ public class BoardListResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardListResponseDto toDto(Board board, Member member, Long commentCount) {
-        WriterInfo writerInfo = WriterInfo.fromBoard(board, member);
+    public static BoardListResponseDto toDto(Board board, MemberDetailedInfoDto memberInfo, Long commentCount) {
+        WriterInfo writerInfo = WriterInfo.fromBoard(board, memberInfo);
         return BoardListResponseDto.builder()
                 .id(board.getId())
                 .writerId(writerInfo.getId())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java b/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
index f78875737..0fa9997c7 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/BoardMyResponseDto.java
@@ -3,7 +3,7 @@
 import lombok.Builder;
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -23,11 +23,11 @@ public class BoardMyResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static BoardMyResponseDto toDto(Board board, Member member) {
+    public static BoardMyResponseDto toDto(Board board, MemberBasicInfoDto memberInfo) {
         return BoardMyResponseDto.builder()
                 .id(board.getId())
                 .category(board.getCategory().getKey())
-                .writerName(board.isWantAnonymous() ? board.getNickname() : member.getName())
+                .writerName(board.isWantAnonymous() ? board.getNickname() : memberInfo.getMemberName())
                 .title(board.getTitle())
                 .imageUrl(board.getImageUrl())
                 .createdAt(board.getCreatedAt())
diff --git a/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java b/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
index b8536da75..52b4ef268 100644
--- a/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
+++ b/src/main/java/page/clab/api/domain/board/dto/response/WriterInfo.java
@@ -2,8 +2,7 @@
 
 import lombok.Getter;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.domain.Role;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 @Getter
 public class WriterInfo {
@@ -28,22 +27,22 @@ public WriterInfo(String id, String name, Long roleLevel, String imageUrl) {
         this.imageUrl = imageUrl;
     }
 
-    public static WriterInfo fromBoard(Board board, Member member) {
-        if (member.isAdminRole() && board.isNotice()) {
+    public static WriterInfo fromBoard(Board board, MemberDetailedInfoDto memberInfo) {
+        if (memberInfo.isAdminRole() && board.isNotice()) {
             return new WriterInfo(null, "운영진");
         } else if (board.isWantAnonymous()) {
             return new WriterInfo(null, board.getNickname());
         }
-        return new WriterInfo(member.getId(), member.getName());
+        return new WriterInfo(memberInfo.getMemberId(), memberInfo.getMemberName());
     }
 
-    public static WriterInfo fromBoardDetails(Board board, Member member) {
-        if (member.isAdminRole() && board.isNotice()) {
-            return new WriterInfo(null, "운영진", Role.ADMIN.toRoleLevel(), null);
+    public static WriterInfo fromBoardDetails(Board board, MemberDetailedInfoDto memberInfo) {
+        if (memberInfo.isAdminRole() && board.isNotice()) {
+            return new WriterInfo(null, "운영진", memberInfo.getRoleLevel(), null);
         } else if (board.isWantAnonymous()) {
             return new WriterInfo(null, board.getNickname(), null, null);
         }
-        return new WriterInfo(member.getId(), member.getName(), member.getRole().toRoleLevel(), member.getImageUrl());
+        return new WriterInfo(memberInfo.getMemberId(), memberInfo.getMemberName(), memberInfo.getRoleLevel(), memberInfo.getImageUrl());
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
index a5116aac6..be5b1c3c7 100644
--- a/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
@@ -16,13 +16,20 @@ public class MemberDetailedInfoDto {
 
     private String imageUrl;
 
+    private boolean isGraduated;
+
     public static MemberDetailedInfoDto create(Member member) {
         return MemberDetailedInfoDto.builder()
                 .memberId(member.getId())
                 .memberName(member.getName())
                 .roleLevel(member.getRole().toRoleLevel())
                 .imageUrl(member.getImageUrl())
+                .isGraduated(member.isGraduated())
                 .build();
     }
 
+    public boolean isAdminRole() {
+        return roleLevel >= 2;
+    }
+
 }

From dc8107fc99e646a1c93e049ca8aa39274427b154 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 02:15:48 +0900
Subject: [PATCH 34/47] =?UTF-8?q?refactor(Award):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../api/domain/award/application/AwardService.java    | 11 ++++++-----
 .../java/page/clab/api/domain/award/domain/Award.java | 10 +++++-----
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/award/application/AwardService.java b/src/main/java/page/clab/api/domain/award/application/AwardService.java
index 840dfcf49..c46da4931 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardService.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardService.java
@@ -11,7 +11,7 @@
 import page.clab.api.domain.award.dto.request.AwardUpdateRequestDto;
 import page.clab.api.domain.award.dto.response.AwardResponseDto;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -50,18 +50,19 @@ public PagedResponseDto<AwardResponseDto> getMyAwards(Pageable pageable) {
 
     @Transactional
     public Long updateAward(Long awardId, AwardUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Award award = getAwardByIdOrThrow(awardId);
-        award.validateAccessPermission(currentMember);
+        award.validateAccessPermission(currentMemberInfo);
         award.update(requestDto);
         validationService.checkValid(award);
         return awardRepository.save(award).getId();
     }
 
+    @Transactional
     public Long deleteAward(Long awardId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Award award = getAwardByIdOrThrow(awardId);
-        award.validateAccessPermission(currentMember);
+        award.validateAccessPermission(currentMemberInfo);
         award.delete();
         awardRepository.save(award);
         return award.getId();
diff --git a/src/main/java/page/clab/api/domain/award/domain/Award.java b/src/main/java/page/clab/api/domain/award/domain/Award.java
index f05805376..3fd5168c2 100644
--- a/src/main/java/page/clab/api/domain/award/domain/Award.java
+++ b/src/main/java/page/clab/api/domain/award/domain/Award.java
@@ -15,7 +15,7 @@
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
 import page.clab.api.domain.award.dto.request.AwardUpdateRequestDto;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
 
@@ -65,12 +65,12 @@ public void delete() {
         this.isDeleted = true;
     }
 
-    public boolean isOwner(Member member) {
-        return member.isSameMember(memberId);
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+    public void validateAccessPermission(MemberDetailedInfoDto memberInfo) throws PermissionDeniedException {
+        if (!isOwner(memberInfo.getMemberId()) && !memberInfo.isAdminRole()) {
             throw new PermissionDeniedException("해당 게시글을 수정/삭제할 권한이 없습니다.");
         }
     }

From 7601e7150bc5ebc03310b1090d669aea5e86d5ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 03:08:08 +0900
Subject: [PATCH 35/47] =?UTF-8?q?refactor(Book):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/BookLoanRecordService.java    | 32 ++++++-------
 .../domain/book/application/BookService.java  | 20 ++++++---
 .../dao/BookLoanRecordRepositoryImpl.java     | 11 ++++-
 .../clab/api/domain/book/domain/Book.java     |  7 +--
 .../domain/book/domain/BookLoanRecord.java    | 21 ++++-----
 .../dto/response/BookDetailsResponseDto.java  |  4 +-
 .../book/dto/response/BookResponseDto.java    |  4 +-
 .../application/MemberLookupService.java      |  8 +++-
 .../application/MemberLookupServiceImpl.java  | 21 ++++++---
 .../clab/api/domain/member/domain/Member.java | 22 ++-------
 .../dto/shared/BookBorrowerInfoDto.java       | 45 +++++++++++++++++++
 11 files changed, 125 insertions(+), 70 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/member/dto/shared/BookBorrowerInfoDto.java

diff --git a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
index 885488eb7..d4e5cb8a0 100644
--- a/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
+++ b/src/main/java/page/clab/api/domain/book/application/BookLoanRecordService.java
@@ -17,7 +17,7 @@
 import page.clab.api.domain.book.exception.BookAlreadyAppliedForLoanException;
 import page.clab.api.domain.book.exception.MaxBorrowLimitExceededException;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.CustomOptimisticLockingFailureException;
@@ -43,18 +43,18 @@ public class BookLoanRecordService {
     @Transactional
     public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOptimisticLockingFailureException {
         try {
-            Member borrower = memberLookupService.getCurrentMember();
-            borrower.checkLoanSuspension();
+            BookBorrowerInfoDto borrowerInfo = memberLookupService.getCurrentMemberBorrowerInfo();
 
-            validateBorrowLimit(borrower.getId());
+            borrowerInfo.checkLoanSuspension();
+            validateBorrowLimit(borrowerInfo.getMemberId());
 
             Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
-            checkIfLoanAlreadyApplied(book, borrower.getId());
+            checkIfLoanAlreadyApplied(book, borrowerInfo.getMemberId());
 
-            BookLoanRecord bookLoanRecord = BookLoanRecord.create(book, borrower);
+            BookLoanRecord bookLoanRecord = BookLoanRecord.create(book, borrowerInfo);
             validationService.checkValid(bookLoanRecord);
 
-            notificationService.sendNotificationToMember(borrower.getId(), "[" + book.getTitle() + "] 도서 대출 신청이 완료되었습니다.");
+            notificationService.sendNotificationToMember(borrowerInfo.getMemberId(), "[" + book.getTitle() + "] 도서 대출 신청이 완료되었습니다.");
             return bookLoanRecordRepository.save(bookLoanRecord).getId();
         } catch (ObjectOptimisticLockingFailureException e) {
             throw new CustomOptimisticLockingFailureException("도서 대출 신청에 실패했습니다. 다시 시도해주세요.");
@@ -63,29 +63,31 @@ public Long requestBookLoan(BookLoanRecordRequestDto requestDto) throws CustomOp
 
     @Transactional
     public Long returnBook(BookLoanRecordRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        String currentMemberId = currentMember.getId();
+        BookBorrowerInfoDto borrowerInfo = memberLookupService.getCurrentMemberBorrowerInfo();
+        String currentMemberId = borrowerInfo.getMemberId();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
         book.returnBook(currentMemberId);
         bookRepository.save(book);
 
         BookLoanRecord bookLoanRecord = getBookLoanRecordByBookAndReturnedAtIsNullOrThrow(book);
-        bookLoanRecord.markAsReturned(currentMember);
+        bookLoanRecord.markAsReturned(borrowerInfo);
         validationService.checkValid(bookLoanRecord);
 
+        memberLookupService.updateLoanSuspensionDate(borrowerInfo.getMemberId(), borrowerInfo.getLoanSuspensionDate());
+
         notificationService.sendNotificationToMember(currentMemberId, "[" + book.getTitle() + "] 도서 반납이 완료되었습니다.");
         return bookLoanRecordRepository.save(bookLoanRecord).getId();
     }
 
     @Transactional
     public Long extendBookLoan(BookLoanRecordRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        String currentMemberId = currentMember.getId();
+        BookBorrowerInfoDto borrowerInfo = memberLookupService.getCurrentMemberBorrowerInfo();
+        String currentMemberId = borrowerInfo.getMemberId();
         Book book = bookService.getBookByIdOrThrow(requestDto.getBookId());
 
         book.validateCurrentBorrower(currentMemberId);
         BookLoanRecord bookLoanRecord = getBookLoanRecordByBookAndReturnedAtIsNullOrThrow(book);
-        bookLoanRecord.extendLoan(currentMember);
+        bookLoanRecord.extendLoan(borrowerInfo);
         validationService.checkValid(bookLoanRecord);
 
         notificationService.sendNotificationToMember(currentMemberId, "[" + book.getTitle() + "] 도서 대출 연장이 완료되었습니다.");
@@ -95,12 +97,12 @@ public Long extendBookLoan(BookLoanRecordRequestDto requestDto) {
 
     @Transactional
     public Long approveBookLoan(Long bookLoanRecordId) {
-        String currentMemberId = memberLookupService.getCurrentMemberId();
+        String borrowerId = memberLookupService.getCurrentMemberId();
         BookLoanRecord bookLoanRecord = getBookLoanRecordByIdOrThrow(bookLoanRecordId);
         Book book = bookService.getBookByIdOrThrow(bookLoanRecord.getBook().getId());
 
         book.validateBookIsNotBorrowed();
-        validateBorrowLimit(currentMemberId);
+        validateBorrowLimit(borrowerId);
         bookLoanRecord.approve();
 
         validationService.checkValid(bookLoanRecord);
diff --git a/src/main/java/page/clab/api/domain/book/application/BookService.java b/src/main/java/page/clab/api/domain/book/application/BookService.java
index 3f5d0db75..31c017882 100644
--- a/src/main/java/page/clab/api/domain/book/application/BookService.java
+++ b/src/main/java/page/clab/api/domain/book/application/BookService.java
@@ -15,6 +15,8 @@
 import page.clab.api.domain.book.dto.request.BookUpdateRequestDto;
 import page.clab.api.domain.book.dto.response.BookDetailsResponseDto;
 import page.clab.api.domain.book.dto.response.BookResponseDto;
+import page.clab.api.domain.member.application.MemberLookupService;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -24,6 +26,8 @@
 @RequiredArgsConstructor
 public class BookService {
 
+    private final MemberLookupService memberLookupService;
+
     private final BookRepository bookRepository;
 
     private final BookLoanRecordRepository bookLoanRecordRepository;
@@ -42,14 +46,16 @@ public PagedResponseDto<BookResponseDto> getBooksByConditions(String title, Stri
 
     @Transactional(readOnly = true)
     public BookDetailsResponseDto getBookDetails(Long bookId) {
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
         Book book = getBookByIdOrThrow(bookId);
-        return mapToBookDetailsResponseDto(book);
+        return mapToBookDetailsResponseDto(book, currentMemberInfo.getMemberName());
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<BookDetailsResponseDto> getDeletedBooks(Pageable pageable) {
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
         Page<Book> books = bookRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(books.map(this::mapToBookDetailsResponseDto));
+        return new PagedResponseDto<>(books.map((book) -> mapToBookDetailsResponseDto(book, currentMemberInfo.getMemberName())));
     }
 
     @Transactional
@@ -59,9 +65,10 @@ public Long updateBookInfo(Long bookId, BookUpdateRequestDto bookUpdateRequestDt
         return bookRepository.save(book).getId();
     }
 
+    @Transactional
     public Long deleteBook(Long bookId) {
         Book book = getBookByIdOrThrow(bookId);
-        book.updateIsDeleted(true);
+        book.delete();
         bookRepository.save(book);
         return book.getId();
     }
@@ -87,14 +94,15 @@ private LocalDateTime getDueDateForBook(Book book) {
 
     @NotNull
     private BookResponseDto mapToBookResponseDto(Book book) {
+        MemberBasicInfoDto currentMemberInfo = memberLookupService.getCurrentMemberBasicInfo();
         LocalDateTime dueDate = getDueDateForBook(book);
-        return BookResponseDto.toDto(book, dueDate);
+        return BookResponseDto.toDto(book, currentMemberInfo.getMemberName(), dueDate);
     }
 
     @NotNull
-    private BookDetailsResponseDto mapToBookDetailsResponseDto(Book book) {
+    private BookDetailsResponseDto mapToBookDetailsResponseDto(Book book, String borrowerName) {
         LocalDateTime dueDate = getDueDateForBook(book);
-        return BookDetailsResponseDto.toDto(book, dueDate);
+        return BookDetailsResponseDto.toDto(book, borrowerName, dueDate);
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
index 34e0ed84f..902690fe0 100644
--- a/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java
@@ -12,6 +12,7 @@
 import page.clab.api.domain.book.domain.QBookLoanRecord;
 import page.clab.api.domain.book.dto.response.BookLoanRecordOverdueResponseDto;
 import page.clab.api.domain.book.dto.response.BookLoanRecordResponseDto;
+import page.clab.api.domain.member.domain.QMember;
 import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.time.LocalDateTime;
@@ -26,6 +27,7 @@ public class BookLoanRecordRepositoryImpl implements BookLoanRecordRepositoryCus
     @Override
     public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borrowerId, BookLoanStatus status, Pageable pageable) {
         QBookLoanRecord bookLoanRecord = QBookLoanRecord.bookLoanRecord;
+        QMember member = QMember.member;
 
         BooleanBuilder builder = new BooleanBuilder();
         if (bookId != null) builder.and(bookLoanRecord.book.id.eq(bookId));
@@ -40,7 +42,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
                         bookLoanRecord.book.title,
                         bookLoanRecord.book.imageUrl,
                         bookLoanRecord.borrowerId,
-                        bookLoanRecord.borrowerName,
+                        member.name.as("borrowerName"),
                         bookLoanRecord.borrowedAt,
                         bookLoanRecord.returnedAt,
                         bookLoanRecord.dueDate,
@@ -48,6 +50,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
                         bookLoanRecord.status
                 ))
                 .from(bookLoanRecord)
+                .leftJoin(member).on(bookLoanRecord.borrowerId.eq(member.id))
                 .where(builder)
                 .offset(pageable.getOffset())
                 .limit(pageable.getPageSize())
@@ -56,6 +59,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
 
         long total = queryFactory
                 .selectFrom(bookLoanRecord)
+                .leftJoin(member).on(bookLoanRecord.borrowerId.eq(member.id))
                 .where(builder)
                 .fetchCount();
 
@@ -65,6 +69,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
     @Override
     public Page<BookLoanRecordOverdueResponseDto> findOverdueBookLoanRecords(Pageable pageable) {
         QBookLoanRecord bookLoanRecord = QBookLoanRecord.bookLoanRecord;
+        QMember member = QMember.member;
 
         LocalDateTime now = LocalDateTime.now();
 
@@ -74,12 +79,13 @@ public Page<BookLoanRecordOverdueResponseDto> findOverdueBookLoanRecords(Pageabl
                         bookLoanRecord.book.id,
                         bookLoanRecord.book.title,
                         bookLoanRecord.borrowerId,
-                        bookLoanRecord.borrowerName,
+                        member.name.as("borrowerName"),
                         bookLoanRecord.borrowedAt,
                         bookLoanRecord.dueDate,
                         bookLoanRecord.status
                 ))
                 .from(bookLoanRecord)
+                .leftJoin(member).on(bookLoanRecord.borrowerId.eq(member.id))
                 .where(bookLoanRecord.status.eq(BookLoanStatus.APPROVED)
                         .and(bookLoanRecord.dueDate.lt(now)))
                 .offset(pageable.getOffset())
@@ -89,6 +95,7 @@ public Page<BookLoanRecordOverdueResponseDto> findOverdueBookLoanRecords(Pageabl
 
         long total = queryFactory
                 .selectFrom(bookLoanRecord)
+                .leftJoin(member).on(bookLoanRecord.borrowerId.eq(member.id))
                 .where(bookLoanRecord.status.eq(BookLoanStatus.APPROVED)
                         .and(bookLoanRecord.dueDate.lt(now)))
                 .fetchCount();
diff --git a/src/main/java/page/clab/api/domain/book/domain/Book.java b/src/main/java/page/clab/api/domain/book/domain/Book.java
index 640154bed..470fe25b0 100644
--- a/src/main/java/page/clab/api/domain/book/domain/Book.java
+++ b/src/main/java/page/clab/api/domain/book/domain/Book.java
@@ -57,9 +57,6 @@ public class Book extends BaseEntity {
     @Column(name = "member_id")
     private String borrowerId;
 
-    @Column(name = "member_name")
-    private String borrowerName;
-
     @Version
     private Long version;
 
@@ -72,8 +69,8 @@ public void update(BookUpdateRequestDto requestDto) {
         Optional.ofNullable(requestDto.getReviewLinks()).ifPresent(this::setReviewLinks);
     }
 
-    public void updateIsDeleted(Boolean isDeleted) {
-        this.isDeleted = isDeleted;
+    public void delete() {
+        this.isDeleted = true;
     }
 
     public boolean isBorrower(String borrowerId) {
diff --git a/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java b/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
index aeed93507..5888493dd 100644
--- a/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
+++ b/src/main/java/page/clab/api/domain/book/domain/BookLoanRecord.java
@@ -19,7 +19,7 @@
 import page.clab.api.domain.book.exception.LoanNotPendingException;
 import page.clab.api.domain.book.exception.LoanSuspensionException;
 import page.clab.api.domain.book.exception.OverdueException;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
 import page.clab.api.global.common.domain.BaseEntity;
 
 import java.time.LocalDateTime;
@@ -44,9 +44,6 @@ public class BookLoanRecord extends BaseEntity {
     @Column(name = "member_id", nullable = false)
     private String borrowerId;
 
-    @Column(name = "member_name", nullable = false)
-    private String borrowerName;
-
     private LocalDateTime borrowedAt;
 
     private LocalDateTime returnedAt;
@@ -58,24 +55,23 @@ public class BookLoanRecord extends BaseEntity {
     @Enumerated(EnumType.STRING)
     private BookLoanStatus status;
 
-    public static BookLoanRecord create(Book book, Member borrower) {
+    public static BookLoanRecord create(Book book, BookBorrowerInfoDto borrowerInfo) {
         return BookLoanRecord.builder()
                 .book(book)
-                .borrowerId(borrower.getId())
-                .borrowerName(borrower.getName())
+                .borrowerId(borrowerInfo.getMemberId())
                 .loanExtensionCount(0L)
                 .status(BookLoanStatus.PENDING)
                 .build();
     }
 
-    public void markAsReturned(Member borrower) {
+    public void markAsReturned(BookBorrowerInfoDto borrowerInfo) {
         if (this.returnedAt != null) {
             throw new BookAlreadyReturnedException("이미 반납된 도서입니다.");
         }
         this.returnedAt = LocalDateTime.now();
         if (isOverdue(returnedAt)) {
             long overdueDays = ChronoUnit.DAYS.between(this.dueDate, this.returnedAt);
-            borrower.handleOverdueAndSuspension(overdueDays);
+            borrowerInfo.handleOverdueAndSuspension(overdueDays);
         }
         this.status = BookLoanStatus.RETURNED;
     }
@@ -84,13 +80,12 @@ private boolean isOverdue(LocalDateTime returnedAt) {
         return returnedAt.isAfter(this.dueDate);
     }
 
-    public void extendLoan(Member borrower) {
+    public void extendLoan(BookBorrowerInfoDto borrowerInfo) {
         final long MAX_EXTENSIONS = 2;
         LocalDateTime now = LocalDateTime.now();
 
-        if (borrower.getLoanSuspensionDate() != null && now.isBefore(borrower.getLoanSuspensionDate())) {
-            throw new LoanSuspensionException("대출 정지 중입니다. 연장할 수 없습니다.");
-        }
+        borrowerInfo.checkLoanSuspension();
+
         if (now.isAfter(this.dueDate)) {
             throw new LoanSuspensionException("연체 중인 도서는 연장할 수 없습니다.");
         }
diff --git a/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java b/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
index a5f69adf9..2318c623c 100644
--- a/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
+++ b/src/main/java/page/clab/api/domain/book/dto/response/BookDetailsResponseDto.java
@@ -35,11 +35,11 @@ public class BookDetailsResponseDto {
 
     private LocalDateTime updatedAt;
 
-    public static BookDetailsResponseDto toDto(Book book, LocalDateTime dueDate) {
+    public static BookDetailsResponseDto toDto(Book book, String borrowerName, LocalDateTime dueDate) {
         return BookDetailsResponseDto.builder()
                 .id(book.getId())
                 .borrowerId(book.getBorrowerId() == null ? null : book.getBorrowerId())
-                .borrowerName(book.getBorrowerName() == null ? null : book.getBorrowerName())
+                .borrowerName(book.getBorrowerId() == null ? null : borrowerName)
                 .category(book.getCategory())
                 .title(book.getTitle())
                 .author(book.getAuthor())
diff --git a/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java b/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
index df642cb19..8dce5e41b 100644
--- a/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
+++ b/src/main/java/page/clab/api/domain/book/dto/response/BookResponseDto.java
@@ -32,11 +32,11 @@ public class BookResponseDto {
 
     private LocalDateTime updatedAt;
 
-    public static BookResponseDto toDto(Book book, LocalDateTime dueDate) {
+    public static BookResponseDto toDto(Book book, String borrowerName, LocalDateTime dueDate) {
         return BookResponseDto.builder()
                 .id(book.getId())
                 .borrowerId(book.getBorrowerId() == null ? null : book.getBorrowerId())
-                .borrowerName(book.getBorrowerName() == null ? null : book.getBorrowerName())
+                .borrowerName(book.getBorrowerId() == null ? null : borrowerName)
                 .category(book.getCategory())
                 .title(book.getTitle())
                 .author(book.getAuthor())
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index d67c6e769..7bd195939 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -2,15 +2,15 @@
 
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
+import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 public interface MemberLookupService {
 
-    boolean existsMemberById(String memberId);
-
     Member getMemberById(String memberId);
 
     Member getMemberByIdOrThrow(String memberId);
@@ -35,4 +35,8 @@ public interface MemberLookupService {
 
     MemberDetailedInfoDto getCurrentMemberDetailedInfo();
 
+    BookBorrowerInfoDto getCurrentMemberBorrowerInfo();
+
+    void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate);
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 42985f56e..8c993c277 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -5,11 +5,13 @@
 import page.clab.api.domain.member.dao.MemberRepository;
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
+import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 @Service
@@ -18,11 +20,6 @@ public class MemberLookupServiceImpl implements MemberLookupService {
 
     private final MemberRepository memberRepository;
 
-    @Override
-    public boolean existsMemberById(String memberId) {
-        return memberRepository.existsById(memberId);
-    }
-
     @Override
     public Member getMemberById(String memberId) {
         return memberRepository.findById(memberId)
@@ -105,4 +102,18 @@ public MemberDetailedInfoDto getCurrentMemberDetailedInfo() {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public BookBorrowerInfoDto getCurrentMemberBorrowerInfo() {
+        String currentMemberId = getCurrentMemberId();
+        return memberRepository.findById(currentMemberId)
+                .map(BookBorrowerInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
+    public void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate) {
+        Member member = getMemberById(memberId);
+        member.updateLoanSuspensionDate(loanSuspensionDate);
+        memberRepository.save(member);
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/domain/Member.java b/src/main/java/page/clab/api/domain/member/domain/Member.java
index bdb172ee8..e48c2bc4a 100644
--- a/src/main/java/page/clab/api/domain/member/domain/Member.java
+++ b/src/main/java/page/clab/api/domain/member/domain/Member.java
@@ -23,7 +23,6 @@
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.crypto.password.PasswordEncoder;
-import page.clab.api.domain.book.exception.LoanSuspensionException;
 import page.clab.api.domain.member.dto.request.MemberUpdateRequestDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -218,25 +217,12 @@ public void updateLastLoginTime() {
         lastLoginTime = LocalDateTime.now();
     }
 
-    public void clearImageUrl() {
-        this.imageUrl = null;
-    }
-
-    public void checkLoanSuspension() {
-        if (loanSuspensionDate != null && LocalDateTime.now().isBefore(loanSuspensionDate)) {
-            throw new LoanSuspensionException("대출 정지 중입니다. 대출 정지일까지는 책을 대출할 수 없습니다.");
-        }
+    public void updateLoanSuspensionDate(LocalDateTime loanSuspensionDate) {
+        this.loanSuspensionDate = loanSuspensionDate;
     }
 
-    public void handleOverdueAndSuspension(long overdueDays) {
-        if (overdueDays > 0) {
-            LocalDateTime currentDate = LocalDateTime.now();
-            if (loanSuspensionDate == null || loanSuspensionDate.isBefore(currentDate)) {
-                loanSuspensionDate = LocalDateTime.now().plusDays(overdueDays * 7);;
-            } else {
-                loanSuspensionDate = loanSuspensionDate.plusDays(overdueDays * 7);
-            }
-        }
+    public void clearImageUrl() {
+        this.imageUrl = null;
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/BookBorrowerInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/BookBorrowerInfoDto.java
new file mode 100644
index 000000000..fad0b24ce
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/BookBorrowerInfoDto.java
@@ -0,0 +1,45 @@
+package page.clab.api.domain.member.dto.shared;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.book.exception.LoanSuspensionException;
+import page.clab.api.domain.member.domain.Member;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Builder
+public class BookBorrowerInfoDto {
+
+    private String memberId;
+
+    private String memberName;
+
+    private LocalDateTime loanSuspensionDate;
+
+    public static BookBorrowerInfoDto create(Member member) {
+        return BookBorrowerInfoDto.builder()
+                .memberId(member.getId())
+                .memberName(member.getName())
+                .loanSuspensionDate(member.getLoanSuspensionDate())
+                .build();
+    }
+
+    public void handleOverdueAndSuspension(long overdueDays) {
+        if (overdueDays > 0) {
+            LocalDateTime currentDate = LocalDateTime.now();
+            if (loanSuspensionDate == null || loanSuspensionDate.isBefore(currentDate)) {
+                loanSuspensionDate = LocalDateTime.now().plusDays(overdueDays * 7);
+            } else {
+                loanSuspensionDate = loanSuspensionDate.plusDays(overdueDays * 7);
+            }
+        }
+    }
+
+    public void checkLoanSuspension() {
+        if (loanSuspensionDate != null && LocalDateTime.now().isBefore(loanSuspensionDate)) {
+            throw new LoanSuspensionException("대출 정지 중입니다. 대출 정지일까지는 책을 대출할 수 없습니다.");
+        }
+    }
+
+}

From 0ec40565d2c13e378511a4e214bebd68745d7a95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 12:03:28 +0900
Subject: [PATCH 36/47] =?UTF-8?q?refactor(Comment):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../comment/application/CommentService.java   | 100 ++++++++----------
 .../domain/comment/dao/CommentRepository.java |   3 +-
 .../api/domain/comment/domain/Comment.java    |  24 ++---
 .../dto/request/CommentRequestDto.java        |   5 +-
 .../dto/response/CommentMyResponseDto.java    |   8 +-
 .../dto/response/CommentResponseDto.java      |  22 ++--
 .../response/DeletedCommentResponseDto.java   |  17 +--
 .../application/MemberLookupService.java      |   2 +
 .../application/MemberLookupServiceImpl.java  |   7 ++
 9 files changed, 88 insertions(+), 100 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/comment/application/CommentService.java b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
index adef5f5f7..19e233a29 100644
--- a/src/main/java/page/clab/api/domain/comment/application/CommentService.java
+++ b/src/main/java/page/clab/api/domain/comment/application/CommentService.java
@@ -2,8 +2,8 @@
 
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.hibernate.Hibernate;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -19,14 +19,14 @@
 import page.clab.api.domain.comment.dto.response.CommentResponseDto;
 import page.clab.api.domain.comment.dto.response.DeletedCommentResponseDto;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.notification.application.NotificationService;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
 import page.clab.api.global.validation.ValidationService;
 
-import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -57,40 +57,43 @@ public Long createComment(Long parentId, Long boardId, CommentRequestDto request
 
     @Transactional(readOnly = true)
     public PagedResponseDto<CommentResponseDto> getAllComments(Long boardId, Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Page<Comment> comments = getCommentByBoardIdAndParentIsNull(boardId, pageable);
-        comments.forEach(comment -> {
-            Hibernate.initialize(comment.getChildren());
-            sortChildrenComments(comment);
-        });
-        Page<CommentResponseDto> commentDtos = comments.map(comment -> toCommentResponseDto(comment, currentMember));
-        return new PagedResponseDto<>(commentDtos);
+        List<CommentResponseDto> commentDtos = comments.stream()
+                .map(comment -> toCommentResponseDtoWithMemberInfo(comment, currentMemberId))
+                .toList();
+        return new PagedResponseDto<>(new PageImpl<>(commentDtos, pageable, comments.getTotalElements()));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<CommentMyResponseDto> getMyComments(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Comment> comments = getCommentByWriter(currentMember, pageable);
-        List<CommentMyResponseDto> dtos = comments
-                .map(comment -> toCommentMyResponseDto(comment, currentMember))
-                .stream()
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<Comment> comments = getCommentByWriterId(currentMemberId, pageable);
+        List<CommentMyResponseDto> dtos = comments.stream()
+                .map(comment -> toCommentMyResponseDto(comment, currentMemberId))
                 .filter(Objects::nonNull)
                 .toList();
-        return new PagedResponseDto<>(dtos, pageable, dtos.size());
+        return new PagedResponseDto<>(new PageImpl<>(dtos, pageable, comments.getTotalElements()));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<DeletedCommentResponseDto> getDeletedComments(Long boardId, Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Page<Comment> comments = commentRepository.findAllByIsDeletedTrueAndBoardId(boardId, pageable);
-        return new PagedResponseDto<>(comments.map(comment -> DeletedCommentResponseDto.toDto(comment, currentMember.getId())));
+        List<DeletedCommentResponseDto> deletedCommentDtos = comments.stream()
+                .map(comment -> {
+                    MemberDetailedInfoDto memberInfo = memberLookupService.getMemberDetailedInfoById(comment.getWriterId());
+                    return DeletedCommentResponseDto.toDto(comment, memberInfo, comment.isOwner(currentMemberId));
+                })
+                .toList();
+        return new PagedResponseDto<>(new PageImpl<>(deletedCommentDtos, pageable, comments.getTotalElements()));
     }
 
     @Transactional
     public Long updateComment(Long commentId, CommentUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto memberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Comment comment = getCommentByIdOrThrow(commentId);
-        comment.validateAccessPermission(currentMember);
+        comment.validateAccessPermission(memberInfo);
         comment.update(requestDto);
         validationService.checkValid(comment);
         commentRepository.save(comment);
@@ -98,25 +101,25 @@ public Long updateComment(Long commentId, CommentUpdateRequestDto requestDto) th
     }
 
     public Long deleteComment(Long commentId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto memberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Comment comment = getCommentByIdOrThrow(commentId);
-        comment.validateAccessPermission(currentMember);
-        comment.updateIsDeleted();
+        comment.validateAccessPermission(memberInfo);
+        comment.delete();
         commentRepository.save(comment);
         return comment.getBoard().getId();
     }
 
     @Transactional
     public Long toggleLikeStatus(Long commentId) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Comment comment = getCommentByIdOrThrow(commentId);
-        Optional<CommentLike> commentLikeOpt = commentLikeRepository.findByCommentIdAndMemberId(comment.getId(), currentMember.getId());
+        Optional<CommentLike> commentLikeOpt = commentLikeRepository.findByCommentIdAndMemberId(comment.getId(), currentMemberId);
         if (commentLikeOpt.isPresent()) {
             comment.decrementLikes();
             commentLikeRepository.delete(commentLikeOpt.get());
         } else {
             comment.incrementLikes();
-            CommentLike newLike = CommentLike.create(currentMember.getId(), comment.getId());
+            CommentLike newLike = CommentLike.create(currentMemberId, comment.getId());
             commentLikeRepository.save(newLike);
         }
         return comment.getLikes();
@@ -131,15 +134,15 @@ private Page<Comment> getCommentByBoardIdAndParentIsNull(Long boardId, Pageable
         return commentRepository.findAllByBoardIdAndParentIsNull(boardId, pageable);
     }
 
-    private Page<Comment> getCommentByWriter(Member member, Pageable pageable) {
-        return commentRepository.findAllByWriter(member, pageable);
+    private Page<Comment> getCommentByWriterId(String memberId, Pageable pageable) {
+        return commentRepository.findAllByWriterId(memberId, pageable);
     }
 
     private Comment createAndStoreComment(Long parentId, Long boardId, CommentRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Board board = boardService.getBoardByIdOrThrow(boardId);
         Comment parent = findParentComment(parentId);
-        Comment comment = CommentRequestDto.toEntity(requestDto, board, currentMember, parent);
+        Comment comment = CommentRequestDto.toEntity(requestDto, board, currentMemberId, parent);
         if (parent != null) {
             parent.addChildComment(comment);
         }
@@ -153,37 +156,28 @@ private Comment findParentComment(Long parentId) {
 
     private void sendNotificationForNewComment(Comment comment) {
         Board board = comment.getBoard();
-        Member boardOwner = memberLookupService.getMemberById(board.getMemberId());
-        String notificationMessage = String.format("[%s] %s님이 게시글에 댓글을 남겼습니다.", board.getTitle(), comment.getWriterName());
-        notificationService.sendNotificationToMember(boardOwner, notificationMessage);
+        MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(comment.getWriterId());
+        String notificationMessage = String.format("[%s] %s님이 게시글에 댓글을 남겼습니다.", board.getTitle(), memberInfo.getMemberName());
+        notificationService.sendNotificationToMember(board.getMemberId(), notificationMessage);
     }
 
-    private CommentResponseDto toCommentResponseDto(Comment comment, Member currentMember) {
-        Boolean hasLikeByMe = checkLikeStatus(comment.getId(), currentMember.getId());
-        CommentResponseDto responseDto = CommentResponseDto.toDto(comment, currentMember.getId());
-        responseDto.getChildren().forEach(childDto -> setLikeStatusForChildren(childDto, currentMember));
-        if (!responseDto.getIsDeleted()) {
-            responseDto.setHasLikeByMe(hasLikeByMe);
-        }
-        return responseDto;
+    private CommentResponseDto toCommentResponseDtoWithMemberInfo(Comment comment, String currentMemberId) {
+        MemberDetailedInfoDto memberInfo = memberLookupService.getMemberDetailedInfoById(comment.getWriterId());
+        List<CommentResponseDto> childrenDtos = comment.getChildren().stream()
+                .map(child -> toCommentResponseDtoWithMemberInfo(child, currentMemberId))
+                .toList();
+        boolean isOwner = comment.isOwner(currentMemberId);
+        return CommentResponseDto.toDto(comment, memberInfo, isOwner, childrenDtos);
     }
 
     private boolean checkLikeStatus(Long commentId, String memberId) {
         return commentLikeRepository.existsByCommentIdAndMemberId(commentId, memberId);
     }
 
-    private void setLikeStatusForChildren(CommentResponseDto responseDto, Member member) {
-        responseDto.setHasLikeByMe(checkLikeStatus(responseDto.getId(), member.getId()));
-        responseDto.getChildren().forEach(childDto -> setLikeStatusForChildren(childDto, member));
-    }
-
-    private CommentMyResponseDto toCommentMyResponseDto(Comment comment, Member currentMember) {
-        boolean hasLikeByMe = checkLikeStatus(comment.getId(), currentMember.getId());
-        return CommentMyResponseDto.toDto(comment, hasLikeByMe);
-    }
-
-    private void sortChildrenComments(Comment comment) {
-        comment.getChildren().sort(Comparator.comparing(Comment::getCreatedAt));
+    private CommentMyResponseDto toCommentMyResponseDto(Comment comment, String currentMemberId) {
+        boolean hasLikeByMe = checkLikeStatus(comment.getId(), currentMemberId);
+        MemberDetailedInfoDto memberInfo = memberLookupService.getMemberDetailedInfoById(comment.getWriterId());
+        return CommentMyResponseDto.toDto(comment, memberInfo, hasLikeByMe);
     }
 
 }
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/domain/comment/dao/CommentRepository.java b/src/main/java/page/clab/api/domain/comment/dao/CommentRepository.java
index 1a2f2d416..0a34975de 100644
--- a/src/main/java/page/clab/api/domain/comment/dao/CommentRepository.java
+++ b/src/main/java/page/clab/api/domain/comment/dao/CommentRepository.java
@@ -8,14 +8,13 @@
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.comment.domain.Comment;
-import page.clab.api.domain.member.domain.Member;
 
 @Repository
 public interface CommentRepository extends JpaRepository<Comment, Long> {
 
     Page<Comment> findAllByBoardIdAndParentIsNull(Long boardId, Pageable pageable);
 
-    Page<Comment> findAllByWriter(Member member, Pageable pageable);
+    Page<Comment> findAllByWriterId(String memberId, Pageable pageable);
 
     Long countByBoard(Board board);
 
diff --git a/src/main/java/page/clab/api/domain/comment/domain/Comment.java b/src/main/java/page/clab/api/domain/comment/domain/Comment.java
index 87ab10288..2bbfaae42 100644
--- a/src/main/java/page/clab/api/domain/comment/domain/Comment.java
+++ b/src/main/java/page/clab/api/domain/comment/domain/Comment.java
@@ -9,7 +9,6 @@
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.ManyToOne;
 import jakarta.persistence.OneToMany;
-import jakarta.persistence.OrderBy;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -19,7 +18,7 @@
 import lombok.Setter;
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.comment.dto.request.CommentUpdateRequestDto;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
 
@@ -43,9 +42,8 @@ public class Comment extends BaseEntity {
     @JoinColumn(name = "board_id")
     private Board board;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id")
-    private Member writer;
+    @Column(name = "member_id", nullable = false)
+    private String writerId;
 
     @Column(nullable = false)
     private String nickname;
@@ -79,20 +77,12 @@ public void addChildComment(Comment child) {
         child.setParent(this);
     }
 
-    public String getWriterName() {
-        return this.wantAnonymous ? this.nickname : this.writer.getName();
-    }
-
-    public boolean isOwner(Member member) {
-        return this.writer.isSameMember(member);
-    }
-
     public boolean isOwner(String memberId) {
-        return this.writer.isSameMember(memberId);
+        return this.writerId.equals(memberId);
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+    public void validateAccessPermission(MemberDetailedInfoDto memberInfo) throws PermissionDeniedException {
+        if (!isOwner(memberInfo.getMemberId()) && !memberInfo.isAdminRole()) {
             throw new PermissionDeniedException("해당 댓글을 수정/삭제할 권한이 없습니다.");
         }
     }
@@ -107,7 +97,7 @@ public void decrementLikes() {
         }
     }
 
-    public void updateIsDeleted(){
+    public void delete() {
         this.isDeleted = true;
     }
 
diff --git a/src/main/java/page/clab/api/domain/comment/dto/request/CommentRequestDto.java b/src/main/java/page/clab/api/domain/comment/dto/request/CommentRequestDto.java
index 8879451d7..b4e0ef1d6 100644
--- a/src/main/java/page/clab/api/domain/comment/dto/request/CommentRequestDto.java
+++ b/src/main/java/page/clab/api/domain/comment/dto/request/CommentRequestDto.java
@@ -6,7 +6,6 @@
 import lombok.Setter;
 import page.clab.api.domain.board.domain.Board;
 import page.clab.api.domain.comment.domain.Comment;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.util.RandomNicknameUtil;
 
 @Getter
@@ -21,10 +20,10 @@ public class CommentRequestDto {
     @Schema(description = "익명 사용 여부", example = "false", required = true)
     private boolean wantAnonymous;
 
-    public static Comment toEntity(CommentRequestDto requestDto, Board board, Member member, Comment parent) {
+    public static Comment toEntity(CommentRequestDto requestDto, Board board, String writerId, Comment parent) {
         return Comment.builder()
                 .board(board)
-                .writer(member)
+                .writerId(writerId)
                 .nickname(RandomNicknameUtil.makeRandomNickname())
                 .content(requestDto.getContent())
                 .parent(parent)
diff --git a/src/main/java/page/clab/api/domain/comment/dto/response/CommentMyResponseDto.java b/src/main/java/page/clab/api/domain/comment/dto/response/CommentMyResponseDto.java
index 327c2b3b2..e359abfd8 100644
--- a/src/main/java/page/clab/api/domain/comment/dto/response/CommentMyResponseDto.java
+++ b/src/main/java/page/clab/api/domain/comment/dto/response/CommentMyResponseDto.java
@@ -2,8 +2,8 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import page.clab.api.domain.board.domain.BoardCategory;
 import page.clab.api.domain.comment.domain.Comment;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 import java.time.LocalDateTime;
 
@@ -29,7 +29,7 @@ public class CommentMyResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static CommentMyResponseDto toDto(Comment comment, boolean hasLikeByMe) {
+    public static CommentMyResponseDto toDto(Comment comment, MemberDetailedInfoDto memberInfo, boolean hasLikeByMe) {
         if (comment.getBoard() == null || comment.getIsDeleted()) {
             return null;
         }
@@ -37,8 +37,8 @@ public static CommentMyResponseDto toDto(Comment comment, boolean hasLikeByMe) {
                 .id(comment.getId())
                 .boardId(comment.getBoard().getId())
                 .boardCategory(comment.getBoard().getCategory().getKey())
-                .writer(comment.isWantAnonymous() ? comment.getNickname() : comment.getWriter().getName())
-                .writerImageUrl(comment.isWantAnonymous() ? null : comment.getWriter().getImageUrl())
+                .writer(comment.isWantAnonymous() ? comment.getNickname() : memberInfo.getMemberName())
+                .writerImageUrl(comment.isWantAnonymous() ? null : memberInfo.getImageUrl())
                 .content(comment.getContent())
                 .likes(comment.getLikes())
                 .hasLikeByMe(hasLikeByMe)
diff --git a/src/main/java/page/clab/api/domain/comment/dto/response/CommentResponseDto.java b/src/main/java/page/clab/api/domain/comment/dto/response/CommentResponseDto.java
index b11f029f5..966c9d17d 100644
--- a/src/main/java/page/clab/api/domain/comment/dto/response/CommentResponseDto.java
+++ b/src/main/java/page/clab/api/domain/comment/dto/response/CommentResponseDto.java
@@ -5,6 +5,7 @@
 import lombok.Getter;
 import lombok.Setter;
 import page.clab.api.domain.comment.domain.Comment;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -39,33 +40,28 @@ public class CommentResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static CommentResponseDto toDto(Comment comment, String currentMemberId) {
+    public static CommentResponseDto toDto(Comment comment, MemberDetailedInfoDto memberInfo, boolean isOwner, List<CommentResponseDto> children) {
         if (comment.getIsDeleted()) {
             return CommentResponseDto.builder()
                     .id(comment.getId())
                     .isDeleted(true)
-                    .children(comment.getChildren().stream()
-                            .map(child -> CommentResponseDto.toDto(child, currentMemberId))
-                            .toList())
+                    .children(children)
                     .createdAt(comment.getCreatedAt())
                     .build();
         }
         return CommentResponseDto.builder()
                 .id(comment.getId())
                 .isDeleted(false)
-                .writerId(comment.isWantAnonymous() ? null : comment.getWriter().getId())
-                .writerName(comment.isWantAnonymous() ? comment.getNickname() : comment.getWriter().getName())
-                .writerImageUrl(comment.isWantAnonymous() ? null : comment.getWriter().getImageUrl())
-                .writerRoleLevel(comment.isWantAnonymous() ? null : comment.getWriter().getRole().toRoleLevel())
+                .writerId(comment.isWantAnonymous() ? null : comment.getWriterId())
+                .writerName(comment.isWantAnonymous() ? comment.getNickname() : memberInfo.getMemberName())
+                .writerImageUrl(comment.isWantAnonymous() ? null : memberInfo.getImageUrl())
+                .writerRoleLevel(comment.isWantAnonymous() ? null : memberInfo.getRoleLevel())
                 .content(comment.getContent())
-                .children(comment.getChildren().stream()
-                        .map(child -> CommentResponseDto.toDto(child, currentMemberId))
-                        .toList())
+                .children(children)
                 .likes(comment.getLikes())
-                .isOwner(comment.isOwner(currentMemberId))
+                .isOwner(isOwner)
                 .createdAt(comment.getCreatedAt())
                 .build();
-
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/comment/dto/response/DeletedCommentResponseDto.java b/src/main/java/page/clab/api/domain/comment/dto/response/DeletedCommentResponseDto.java
index 2a94dfc4f..d4d9b60b3 100644
--- a/src/main/java/page/clab/api/domain/comment/dto/response/DeletedCommentResponseDto.java
+++ b/src/main/java/page/clab/api/domain/comment/dto/response/DeletedCommentResponseDto.java
@@ -1,12 +1,13 @@
 package page.clab.api.domain.comment.dto.response;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import java.time.LocalDateTime;
-import java.util.List;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.Setter;
 import page.clab.api.domain.comment.domain.Comment;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
+
+import java.time.LocalDateTime;
 
 @Getter
 @Setter
@@ -32,16 +33,16 @@ public class DeletedCommentResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static DeletedCommentResponseDto toDto(Comment comment, String currentMemberId) {
+    public static DeletedCommentResponseDto toDto(Comment comment, MemberDetailedInfoDto memberInfo, boolean isOwner) {
         return DeletedCommentResponseDto.builder()
                 .id(comment.getId())
-                .writerId(comment.isWantAnonymous() ? null : comment.getWriter().getId())
-                .writerName(comment.isWantAnonymous() ? comment.getNickname() : comment.getWriter().getName())
-                .writerImageUrl(comment.isWantAnonymous() ? null : comment.getWriter().getImageUrl())
-                .writerRoleLevel(comment.isWantAnonymous() ? null : comment.getWriter().getRole().toRoleLevel())
+                .writerId(comment.isWantAnonymous() ? null : memberInfo.getMemberId())
+                .writerName(comment.isWantAnonymous() ? comment.getNickname() : memberInfo.getMemberName())
+                .writerImageUrl(comment.isWantAnonymous() ? null : memberInfo.getImageUrl())
+                .writerRoleLevel(comment.isWantAnonymous() ? null : memberInfo.getRoleLevel())
                 .content(comment.getContent())
                 .likes(comment.getLikes())
-                .isOwner(comment.isOwner(currentMemberId))
+                .isOwner(isOwner)
                 .createdAt(comment.getCreatedAt())
                 .build();
     }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 7bd195939..b02a343cf 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -33,6 +33,8 @@ public interface MemberLookupService {
 
     MemberBasicInfoDto getCurrentMemberBasicInfo();
 
+    MemberDetailedInfoDto getMemberDetailedInfoById(String memberId);
+
     MemberDetailedInfoDto getCurrentMemberDetailedInfo();
 
     BookBorrowerInfoDto getCurrentMemberBorrowerInfo();
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 8c993c277..d83eb004a 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -94,6 +94,13 @@ public MemberBasicInfoDto getCurrentMemberBasicInfo() {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public MemberDetailedInfoDto getMemberDetailedInfoById(String memberId) {
+        return memberRepository.findById(memberId)
+                .map(MemberDetailedInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
     @Override
     public MemberDetailedInfoDto getCurrentMemberDetailedInfo() {
         String currentMemberId = getCurrentMemberId();

From 9415198af7620ce9840c4213035d0e53d5044633 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 12:34:14 +0900
Subject: [PATCH 37/47] =?UTF-8?q?feat(Comment):=20EventProcessor=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/CommentEventProcessor.java    | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java b/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java
new file mode 100644
index 000000000..5f0bfcac7
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java
@@ -0,0 +1,34 @@
+package page.clab.api.domain.comment.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+@Component
+@RequiredArgsConstructor
+public class CommentEventProcessor implements MemberEventProcessor {
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        // do nothing
+    }
+
+    @Override
+    @Transactional
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+
+}

From 7f620c9c108f0518954027f14589d61ac683b93f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 12:59:59 +0900
Subject: [PATCH 38/47] =?UTF-8?q?refactor(Donation):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/DonationEventProcessor.java   | 34 ++++++++++++++
 .../donation/application/DonationService.java | 44 +++++++++----------
 .../donation/dao/DonationRepository.java      |  3 +-
 .../donation/dao/DonationRepositoryImpl.java  |  8 ++--
 .../api/domain/donation/domain/Donation.java  | 15 ++++---
 .../dto/request/DonationRequestDto.java       |  5 +--
 .../dto/response/DonationResponseDto.java     | 10 ++---
 .../dto/shared/MemberDetailedInfoDto.java     |  4 ++
 8 files changed, 81 insertions(+), 42 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java b/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java
new file mode 100644
index 000000000..d2cff3110
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java
@@ -0,0 +1,34 @@
+package page.clab.api.domain.donation.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+
+@Component
+@RequiredArgsConstructor
+public class DonationEventProcessor implements MemberEventProcessor {
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        // do nothing
+    }
+
+    @Override
+    @Transactional
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/donation/application/DonationService.java b/src/main/java/page/clab/api/domain/donation/application/DonationService.java
index 5073239e2..136d21b6a 100644
--- a/src/main/java/page/clab/api/domain/donation/application/DonationService.java
+++ b/src/main/java/page/clab/api/domain/donation/application/DonationService.java
@@ -11,7 +11,8 @@
 import page.clab.api.domain.donation.dto.request.DonationUpdateRequestDto;
 import page.clab.api.domain.donation.dto.response.DonationResponseDto;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.exception.NotFoundException;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -31,8 +32,8 @@ public class DonationService {
 
     @Transactional
     public Long createDonation(DonationRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Donation donation = DonationRequestDto.toEntity(requestDto, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Donation donation = DonationRequestDto.toEntity(requestDto, currentMemberId);
         validationService.checkValid(donation);
         return donationRepository.save(donation).getId();
     }
@@ -40,36 +41,45 @@ public Long createDonation(DonationRequestDto requestDto) {
     @Transactional(readOnly = true)
     public PagedResponseDto<DonationResponseDto> getDonationsByConditions(String memberId, String name, LocalDate startDate, LocalDate endDate, Pageable pageable) {
         Page<Donation> donations = donationRepository.findByConditions(memberId, name, startDate, endDate, pageable);
-        return new PagedResponseDto<>(donations.map(DonationResponseDto::toDto));
+        return new PagedResponseDto<>(donations.map(donation -> {
+            MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(donation.getMemberId());
+            return DonationResponseDto.toDto(donation, memberInfo.getMemberName());
+        }));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<DonationResponseDto> getMyDonations(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Donation> donations = getDonationsByDonor(currentMember, pageable);
-        return new PagedResponseDto<>(donations.map(DonationResponseDto::toDto));
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<Donation> donations = donationRepository.findByMemberId(currentMemberId, pageable);
+        return new PagedResponseDto<>(donations.map(donation -> {
+            MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(donation.getMemberId());
+            return DonationResponseDto.toDto(donation, memberInfo.getMemberName());
+        }));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<DonationResponseDto> getDeletedDonations(Pageable pageable) {
         Page<Donation> donations = donationRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(donations.map(DonationResponseDto::toDto));
+        return new PagedResponseDto<>(donations.map(donation -> {
+            MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(donation.getMemberId());
+            return DonationResponseDto.toDto(donation, memberInfo.getMemberName());
+        }));
     }
 
     @Transactional
     public Long updateDonation(Long donationId, DonationUpdateRequestDto donationUpdateRequestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Donation donation = getDonationByIdOrThrow(donationId);
-        validateDonationUpdatePermission(currentMember);
+        donation.validateAccessPermission(currentMemberInfo.isSuperAdminRole());
         donation.update(donationUpdateRequestDto);
         validationService.checkValid(donation);
         return donationRepository.save(donation).getId();
     }
 
     public Long deleteDonation(Long donationId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         Donation donation = getDonationByIdOrThrow(donationId);
-        validateDonationUpdatePermission(currentMember);
+        donation.validateAccessPermission(currentMemberInfo.isSuperAdminRole());
         donationRepository.delete(donation);
         return donation.getId();
     }
@@ -79,14 +89,4 @@ private Donation getDonationByIdOrThrow(Long donationId) {
                 .orElseThrow(() -> new NotFoundException("해당 후원 정보가 존재하지 않습니다."));
     }
 
-    private Page<Donation> getDonationsByDonor(Member member, Pageable pageable) {
-        return donationRepository.findByDonor(member, pageable);
-    }
-
-    private void validateDonationUpdatePermission(Member member) throws PermissionDeniedException {
-        if (!member.isSuperAdminRole()) {
-            throw new PermissionDeniedException("해당 후원 정보를 수정할 권한이 없습니다.");
-        }
-    }
-
 }
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/domain/donation/dao/DonationRepository.java b/src/main/java/page/clab/api/domain/donation/dao/DonationRepository.java
index c6a37ac68..e8c5184eb 100644
--- a/src/main/java/page/clab/api/domain/donation/dao/DonationRepository.java
+++ b/src/main/java/page/clab/api/domain/donation/dao/DonationRepository.java
@@ -7,13 +7,12 @@
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import page.clab.api.domain.donation.domain.Donation;
-import page.clab.api.domain.member.domain.Member;
 
 public interface DonationRepository extends JpaRepository<Donation, Long>, DonationRepositoryCustom, QuerydslPredicateExecutor<Donation> {
 
     Page<Donation> findAllByOrderByCreatedAtDesc(Pageable pageable);
 
-    Page<Donation> findByDonor(Member member, Pageable pageable);
+    Page<Donation> findByMemberId(String memberId, Pageable pageable);
 
     @Query(value = "SELECT d.* FROM donation d WHERE d.is_deleted = true", nativeQuery = true)
     Page<Donation> findAllByIsDeletedTrue(Pageable pageable);
diff --git a/src/main/java/page/clab/api/domain/donation/dao/DonationRepositoryImpl.java b/src/main/java/page/clab/api/domain/donation/dao/DonationRepositoryImpl.java
index 08c8fd1e6..352cec50e 100644
--- a/src/main/java/page/clab/api/domain/donation/dao/DonationRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/donation/dao/DonationRepositoryImpl.java
@@ -10,10 +10,10 @@
 import page.clab.api.domain.donation.domain.Donation;
 import page.clab.api.domain.donation.domain.QDonation;
 import page.clab.api.domain.member.domain.QMember;
+import page.clab.api.global.util.OrderSpecifierUtil;
 
 import java.time.LocalDate;
 import java.util.List;
-import page.clab.api.global.util.OrderSpecifierUtil;
 
 @Repository
 @RequiredArgsConstructor
@@ -27,14 +27,14 @@ public Page<Donation> findByConditions(String memberId, String name, LocalDate s
         QMember member = QMember.member;
 
         BooleanBuilder builder = new BooleanBuilder();
-        if (memberId != null && !memberId.isBlank()) builder.and(donation.donor.id.eq(memberId));
+        if (memberId != null && !memberId.isBlank()) builder.and(donation.memberId.eq(memberId));
         if (name != null && !name.isBlank()) builder.and(member.name.containsIgnoreCase(name));
         if (startDate != null) builder.and(donation.createdAt.goe(startDate.atStartOfDay()));
         if (endDate != null) builder.and(donation.createdAt.loe(endDate.plusDays(1).atStartOfDay()));
 
         List<Donation> results = queryFactory
                 .selectFrom(donation)
-                .leftJoin(donation.donor, member)
+                .leftJoin(member).on(donation.memberId.eq(member.id))
                 .where(builder)
                 .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, donation))
                 .offset(pageable.getOffset())
@@ -43,7 +43,7 @@ public Page<Donation> findByConditions(String memberId, String name, LocalDate s
 
         long count = queryFactory
                 .selectFrom(donation)
-                .leftJoin(donation.donor, member)
+                .leftJoin(member).on(donation.memberId.eq(member.id))
                 .where(builder)
                 .fetchCount();
 
diff --git a/src/main/java/page/clab/api/domain/donation/domain/Donation.java b/src/main/java/page/clab/api/domain/donation/domain/Donation.java
index 26f8c2081..22ce7000a 100644
--- a/src/main/java/page/clab/api/domain/donation/domain/Donation.java
+++ b/src/main/java/page/clab/api/domain/donation/domain/Donation.java
@@ -5,8 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Min;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
@@ -18,8 +16,8 @@
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
 import page.clab.api.domain.donation.dto.request.DonationUpdateRequestDto;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
+import page.clab.api.global.exception.PermissionDeniedException;
 
 import java.util.Optional;
 
@@ -37,9 +35,8 @@ public class Donation extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member donor;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @Column(nullable = false)
     @Min(value = 1, message = "{min.donation.amount}")
@@ -54,4 +51,10 @@ public void update(DonationUpdateRequestDto donationUpdateRequestDto) {
         Optional.ofNullable(donationUpdateRequestDto.getMessage()).ifPresent(this::setMessage);
     }
 
+    public void validateAccessPermission(boolean isSuperAdmin) throws PermissionDeniedException {
+        if (!isSuperAdmin) {
+            throw new PermissionDeniedException("해당 후원 정보를 수정할 권한이 없습니다.");
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/java/page/clab/api/domain/donation/dto/request/DonationRequestDto.java b/src/main/java/page/clab/api/domain/donation/dto/request/DonationRequestDto.java
index b17733320..bf2606fb5 100644
--- a/src/main/java/page/clab/api/domain/donation/dto/request/DonationRequestDto.java
+++ b/src/main/java/page/clab/api/domain/donation/dto/request/DonationRequestDto.java
@@ -5,7 +5,6 @@
 import lombok.Getter;
 import lombok.Setter;
 import page.clab.api.domain.donation.domain.Donation;
-import page.clab.api.domain.member.domain.Member;
 
 @Getter
 @Setter
@@ -19,9 +18,9 @@ public class DonationRequestDto {
     @Schema(description = "후원 메시지", example = "대회 상금 일부 후원", required = true)
     private String message;
 
-    public static Donation toEntity(DonationRequestDto requestDto, Member member) {
+    public static Donation toEntity(DonationRequestDto requestDto, String memberId) {
         return Donation.builder()
-                .donor(member)
+                .memberId(memberId)
                 .amount(requestDto.getAmount())
                 .message(requestDto.getMessage())
                 .build();
diff --git a/src/main/java/page/clab/api/domain/donation/dto/response/DonationResponseDto.java b/src/main/java/page/clab/api/domain/donation/dto/response/DonationResponseDto.java
index f953ee30d..6e871273d 100644
--- a/src/main/java/page/clab/api/domain/donation/dto/response/DonationResponseDto.java
+++ b/src/main/java/page/clab/api/domain/donation/dto/response/DonationResponseDto.java
@@ -12,9 +12,9 @@ public class DonationResponseDto {
 
     private Long id;
 
-    private String donorId;
+    private String memberId;
 
-    private String name;
+    private String memberName;
 
     private Double amount;
 
@@ -22,11 +22,11 @@ public class DonationResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static DonationResponseDto toDto(Donation donation) {
+    public static DonationResponseDto toDto(Donation donation, String memberName) {
         return DonationResponseDto.builder()
                 .id(donation.getId())
-                .donorId(donation.getDonor().getId())
-                .name(donation.getDonor().getName())
+                .memberId(donation.getMemberId())
+                .memberName(memberName)
                 .amount(donation.getAmount())
                 .message(donation.getMessage())
                 .createdAt(donation.getCreatedAt())
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
index be5b1c3c7..7eb443dc2 100644
--- a/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberDetailedInfoDto.java
@@ -32,4 +32,8 @@ public boolean isAdminRole() {
         return roleLevel >= 2;
     }
 
+    public boolean isSuperAdminRole() {
+        return roleLevel == 3;
+    }
+
 }

From fc2414c10291a6b0292987835038323c7cb96a28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 13:54:27 +0900
Subject: [PATCH 39/47] =?UTF-8?q?refactor(AccountLockInfo):=20Member=20?=
 =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?=
 =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20?=
 =?UTF-8?q?=EC=95=8A=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95?=
 =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/AccountLockInfoService.java   | 61 +++++++++++--------
 .../login/dao/AccountLockInfoRepository.java  |  3 +-
 .../domain/login/domain/AccountLockInfo.java  | 13 ++--
 .../response/AccountLockInfoResponseDto.java  |  6 +-
 4 files changed, 46 insertions(+), 37 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java b/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
index 49cdebda2..0a5a02cab 100644
--- a/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
+++ b/src/main/java/page/clab/api/domain/login/application/AccountLockInfoService.java
@@ -14,7 +14,7 @@
 import page.clab.api.domain.login.exception.LoginFaliedException;
 import page.clab.api.domain.login.exception.MemberLockedException;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.global.common.dto.PagedResponseDto;
 import page.clab.api.global.common.slack.application.SlackService;
 import page.clab.api.global.common.slack.domain.SecurityAlertType;
@@ -42,20 +42,20 @@ public class AccountLockInfoService {
 
     @Transactional
     public Long banMemberById(HttpServletRequest request, String memberId) {
-        Member member = memberLookupService.getMemberById(memberId);
-        AccountLockInfo accountLockInfo = ensureAccountLockInfo(member);
+        MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(memberId);
+        AccountLockInfo accountLockInfo = ensureAccountLockInfo(memberInfo.getMemberId());
         accountLockInfo.banPermanently();
         redisTokenService.deleteRedisTokenByMemberId(memberId);
-        slackService.sendSecurityAlertNotification(request, SecurityAlertType.MEMBER_BANNED, "ID: " + member.getId() + ", Name: " + member.getName());
+        sendSlackBanNotification(request, memberId);
         return accountLockInfoRepository.save(accountLockInfo).getId();
     }
 
     @Transactional
     public Long unbanMemberById(HttpServletRequest request, String memberId) {
-        Member member = memberLookupService.getMemberById(memberId);
-        AccountLockInfo accountLockInfo = ensureAccountLockInfo(member);
+        MemberBasicInfoDto memberInfo = memberLookupService.getMemberBasicInfoById(memberId);
+        AccountLockInfo accountLockInfo = ensureAccountLockInfo(memberInfo.getMemberId());
         accountLockInfo.unban();
-        slackService.sendSecurityAlertNotification(request, SecurityAlertType.MEMBER_UNBANNED, "ID: " + member.getId() + ", Name: " + member.getName());
+        sendSlackUnbanNotification(request, memberId);
         return accountLockInfoRepository.save(accountLockInfo).getId();
     }
 
@@ -63,12 +63,16 @@ public Long unbanMemberById(HttpServletRequest request, String memberId) {
     public PagedResponseDto<AccountLockInfoResponseDto> getBanMembers(Pageable pageable) {
         LocalDateTime banDate = LocalDateTime.of(9999, 12, 31, 23, 59);
         Page<AccountLockInfo> banMembers = accountLockInfoRepository.findByLockUntil(banDate, pageable);
-        return new PagedResponseDto<>(banMembers.map(AccountLockInfoResponseDto::toDto));
+        return new PagedResponseDto<>(banMembers.map(accountLockInfo -> {
+            String memberName = memberLookupService.getMemberBasicInfoById(accountLockInfo.getMemberId()).getMemberName();
+            return AccountLockInfoResponseDto.toDto(accountLockInfo, memberName);
+        }));
     }
 
     @Transactional
     public void handleAccountLockInfo(String memberId) throws MemberLockedException, LoginFaliedException {
-        AccountLockInfo accountLockInfo = ensureAccountLockInfoForMemberId(memberId);
+        ensureMemberExists(memberId);
+        AccountLockInfo accountLockInfo = ensureAccountLockInfo(memberId);
         validateAccountLockStatus(accountLockInfo);
         accountLockInfo.unlockAccount();
         accountLockInfoRepository.save(accountLockInfo);
@@ -76,34 +80,32 @@ public void handleAccountLockInfo(String memberId) throws MemberLockedException,
 
     @Transactional
     public void handleLoginFailure(HttpServletRequest request, String memberId) throws MemberLockedException, LoginFaliedException {
-        Member member = memberLookupService.getMemberById(memberId);
-        AccountLockInfo accountLockInfo = ensureAccountLockInfoForMemberId(memberId);
+        ensureMemberExists(memberId);
+        AccountLockInfo accountLockInfo = ensureAccountLockInfo(memberId);
         validateAccountLockStatus(accountLockInfo);
         accountLockInfo.incrementLoginFailCount();
         if (accountLockInfo.shouldBeLocked(maxLoginFailures)) {
             accountLockInfo.lockAccount(lockDurationMinutes);
-            sendSlackMessage(request, member);
+            sendSlackLoginFailureNotification(request, memberId);
         }
         accountLockInfoRepository.save(accountLockInfo);
     }
 
-    public AccountLockInfo createAccountLockInfo(Member member) {
-        AccountLockInfo accountLockInfo = AccountLockInfo.create(member);
+    public AccountLockInfo createAccountLockInfo(String memberId) {
+        AccountLockInfo accountLockInfo = AccountLockInfo.create(memberId);
         accountLockInfoRepository.save(accountLockInfo);
         return accountLockInfo;
     }
 
-    private AccountLockInfo ensureAccountLockInfo(Member member) {
-        return accountLockInfoRepository.findByMember(member)
-                .orElseGet(() -> createAccountLockInfo(member));
+    private AccountLockInfo ensureAccountLockInfo(String memberId) {
+        return accountLockInfoRepository.findByMemberId(memberId)
+                .orElseGet(() -> createAccountLockInfo(memberId));
     }
 
-    private AccountLockInfo ensureAccountLockInfoForMemberId(String memberId) throws LoginFaliedException {
-        Member member = memberLookupService.getMemberById(memberId);
-        if (member == null) {
+    private void ensureMemberExists(String memberId) throws LoginFaliedException {
+        if (memberLookupService.getMemberById(memberId) == null) {
             throw new LoginFaliedException();
         }
-        return ensureAccountLockInfo(member);
     }
 
     private void validateAccountLockStatus(AccountLockInfo accountLockInfo) throws MemberLockedException {
@@ -112,9 +114,20 @@ private void validateAccountLockStatus(AccountLockInfo accountLockInfo) throws M
         }
     }
 
-    private void sendSlackMessage(HttpServletRequest request, Member member) {
-        if (member.isAdminRole()) {
-            request.setAttribute("member", member.getId() + " " + member.getName());
+    private void sendSlackBanNotification(HttpServletRequest request, String memberId) {
+        String memberName = memberLookupService.getMemberBasicInfoById(memberId).getMemberName();
+        slackService.sendSecurityAlertNotification(request, SecurityAlertType.MEMBER_BANNED, "ID: " + memberId + ", Name: " + memberName);
+    }
+
+    private void sendSlackUnbanNotification(HttpServletRequest request, String memberId) {
+        String memberName = memberLookupService.getMemberBasicInfoById(memberId).getMemberName();
+        slackService.sendSecurityAlertNotification(request, SecurityAlertType.MEMBER_UNBANNED, "ID: " + memberId + ", Name: " + memberName);
+    }
+
+    private void sendSlackLoginFailureNotification(HttpServletRequest request, String memberId) {
+        String memberName = memberLookupService.getMemberBasicInfoById(memberId).getMemberName();
+        if (memberLookupService.getMemberDetailedInfoById(memberId).isAdminRole()) {
+            request.setAttribute("member", memberId + " " + memberName);
             slackService.sendSecurityAlertNotification(request, SecurityAlertType.REPEATED_LOGIN_FAILURES, "로그인 실패 횟수 초과로 계정이 잠겼습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/login/dao/AccountLockInfoRepository.java b/src/main/java/page/clab/api/domain/login/dao/AccountLockInfoRepository.java
index 4a03c6786..a56947d4a 100644
--- a/src/main/java/page/clab/api/domain/login/dao/AccountLockInfoRepository.java
+++ b/src/main/java/page/clab/api/domain/login/dao/AccountLockInfoRepository.java
@@ -5,7 +5,6 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import page.clab.api.domain.login.domain.AccountLockInfo;
-import page.clab.api.domain.member.domain.Member;
 
 import java.time.LocalDateTime;
 import java.util.Optional;
@@ -13,7 +12,7 @@
 @Repository
 public interface AccountLockInfoRepository extends JpaRepository<AccountLockInfo, Long> {
 
-    Optional<AccountLockInfo> findByMember(Member member);
+    Optional<AccountLockInfo> findByMemberId(String memberId);
 
     Page<AccountLockInfo> findByLockUntil(LocalDateTime banDate, Pageable pageable);
 
diff --git a/src/main/java/page/clab/api/domain/login/domain/AccountLockInfo.java b/src/main/java/page/clab/api/domain/login/domain/AccountLockInfo.java
index 9ef54330a..aaf418aca 100644
--- a/src/main/java/page/clab/api/domain/login/domain/AccountLockInfo.java
+++ b/src/main/java/page/clab/api/domain/login/domain/AccountLockInfo.java
@@ -1,18 +1,16 @@
 package page.clab.api.domain.login.domain;
 
+import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.OneToOne;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
 
 import java.time.LocalDateTime;
@@ -29,9 +27,8 @@ public class AccountLockInfo extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @OneToOne
-    @JoinColumn(name = "member_id")
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     private Long loginFailCount;
 
@@ -39,9 +36,9 @@ public class AccountLockInfo extends BaseEntity {
 
     private LocalDateTime lockUntil;
 
-    public static AccountLockInfo create(Member member) {
+    public static AccountLockInfo create(String memberId) {
         return AccountLockInfo.builder()
-                .member(member)
+                .memberId(memberId)
                 .loginFailCount(0L)
                 .isLock(false)
                 .lockUntil(null)
diff --git a/src/main/java/page/clab/api/domain/login/dto/response/AccountLockInfoResponseDto.java b/src/main/java/page/clab/api/domain/login/dto/response/AccountLockInfoResponseDto.java
index e7fbf00fe..7b94a7d18 100644
--- a/src/main/java/page/clab/api/domain/login/dto/response/AccountLockInfoResponseDto.java
+++ b/src/main/java/page/clab/api/domain/login/dto/response/AccountLockInfoResponseDto.java
@@ -12,10 +12,10 @@ public class AccountLockInfoResponseDto {
 
     private String name;
 
-    public static AccountLockInfoResponseDto toDto(AccountLockInfo accountLockInfo) {
+    public static AccountLockInfoResponseDto toDto(AccountLockInfo accountLockInfo, String memberName) {
         return AccountLockInfoResponseDto.builder()
-                .id(accountLockInfo.getMember().getId())
-                .name(accountLockInfo.getMember().getName())
+                .id(accountLockInfo.getMemberId())
+                .name(memberName)
                 .build();
     }
 

From b35782abcbf9d03376f8fd7ce409221ec09bf206 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 14:26:12 +0900
Subject: [PATCH 40/47] =?UTF-8?q?refactor(Login):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../login/application/LoginService.java       | 28 +++++++-------
 .../application/MemberLookupService.java      |  5 +++
 .../application/MemberLookupServiceImpl.java  | 17 ++++++++-
 .../member/dto/shared/LoginMemberInfoDto.java | 37 +++++++++++++++++++
 .../slack/application/SlackService.java       |  4 +-
 .../slack/application/SlackServiceHelper.java | 10 ++---
 6 files changed, 78 insertions(+), 23 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/member/dto/shared/LoginMemberInfoDto.java

diff --git a/src/main/java/page/clab/api/domain/login/application/LoginService.java b/src/main/java/page/clab/api/domain/login/application/LoginService.java
index f9a243ef0..bc66213c7 100644
--- a/src/main/java/page/clab/api/domain/login/application/LoginService.java
+++ b/src/main/java/page/clab/api/domain/login/application/LoginService.java
@@ -21,7 +21,7 @@
 import page.clab.api.domain.login.exception.LoginFaliedException;
 import page.clab.api.domain.login.exception.MemberLockedException;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.global.auth.exception.TokenForgeryException;
 import page.clab.api.global.auth.exception.TokenMisuseException;
 import page.clab.api.global.auth.jwt.JwtTokenProvider;
@@ -57,15 +57,15 @@ public class LoginService {
     public LoginResult login(HttpServletRequest request, LoginRequestDto requestDto) throws LoginFaliedException, MemberLockedException {
         authenticateAndCheckStatus(request, requestDto);
         logLoginAttempt(request, requestDto.getId(), true);
-        Member loginMember = memberLookupService.getMemberByIdOrThrow(requestDto.getId());
-        loginMember.updateLastLoginTime();
+        LoginMemberInfoDto loginMember = memberLookupService.getLoginMemberInfoById(requestDto.getId());
+        memberLookupService.updateLastLoginTime(requestDto.getId());
         return generateLoginResult(loginMember);
     }
 
     @Transactional
     public LoginResult authenticator(HttpServletRequest request, TwoFactorAuthenticationRequestDto twoFactorAuthenticationRequestDto) throws LoginFaliedException, MemberLockedException {
         String memberId = twoFactorAuthenticationRequestDto.getMemberId();
-        Member loginMember = memberLookupService.getMemberById(memberId);
+        LoginMemberInfoDto loginMember = memberLookupService.getLoginMemberInfoById(memberId);
         String totp = twoFactorAuthenticationRequestDto.getTotp();
 
         accountLockInfoService.handleAccountLockInfo(memberId);
@@ -82,9 +82,8 @@ public String resetAuthenticator(String memberId) {
     }
 
     public String revoke(String memberId) {
-        Member member = memberLookupService.getMemberById(memberId);
         redisTokenService.deleteRedisTokenByMemberId(memberId);
-        return member.getId();
+        return memberId;
     }
 
     @Transactional
@@ -123,10 +122,10 @@ private void logLoginAttempt(HttpServletRequest request, String memberId, boolea
         loginAttemptLogService.createLoginAttemptLog(request, memberId, result);
     }
 
-    private LoginResult generateLoginResult(Member loginMember) {
-        String memberId = loginMember.getId();
+    private LoginResult generateLoginResult(LoginMemberInfoDto loginMember) {
+        String memberId = loginMember.getMemberId();
         String header;
-        boolean isOtpEnabled = Optional.ofNullable(loginMember.getIsOtpEnabled()).orElse(false);
+        boolean isOtpEnabled = Optional.of(loginMember.isOtpEnabled()).orElse(false);
         if (isOtpEnabled || loginMember.isAdminRole()) {
             if (!authenticatorService.isAuthenticatorExist(memberId)) {
                 String secretKey = authenticatorService.generateSecretKey(memberId);
@@ -150,14 +149,14 @@ private void verifyTwoFactorAuthentication(String memberId, String totp, HttpSer
         loginAttemptLogService.createLoginAttemptLog(request, memberId, LoginAttemptResult.TOTP);
     }
 
-    private TokenInfo generateAndSaveToken(Member member) {
-        TokenInfo tokenInfo = jwtTokenProvider.generateToken(member.getId(), member.getRole());
+    private TokenInfo generateAndSaveToken(LoginMemberInfoDto memberInfo) {
+        TokenInfo tokenInfo = jwtTokenProvider.generateToken(memberInfo.getMemberId(), memberInfo.getRole());
         String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
-        redisTokenService.saveRedisToken(member.getId(), member.getRole(), tokenInfo, clientIpAddress);
+        redisTokenService.saveRedisToken(memberInfo.getMemberId(), memberInfo.getRole(), tokenInfo, clientIpAddress);
         return tokenInfo;
     }
 
-    private void sendAdminLoginNotification(HttpServletRequest request, Member loginMember) {
+    private void sendAdminLoginNotification(HttpServletRequest request, LoginMemberInfoDto loginMember) {
         if (loginMember.isSuperAdminRole()) {
             slackService.sendAdminLoginNotification(request, loginMember);
         }
@@ -165,8 +164,7 @@ private void sendAdminLoginNotification(HttpServletRequest request, Member login
 
     private void validateMemberExistence(Authentication authentication) {
         String id = authentication.getName();
-        Member member = memberLookupService.getMemberById(id);
-        if (member == null) {
+        if (memberLookupService.getMemberById(id) == null) {
             throw new TokenForgeryException("존재하지 않는 회원에 대한 토큰입니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index b02a343cf..11b09e06a 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -3,6 +3,7 @@
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
+import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 
@@ -39,6 +40,10 @@ public interface MemberLookupService {
 
     BookBorrowerInfoDto getCurrentMemberBorrowerInfo();
 
+    LoginMemberInfoDto getLoginMemberInfoById(String memberId);
+
     void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate);
 
+    void updateLastLoginTime(String id);
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index d83eb004a..a8431c998 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -6,6 +6,7 @@
 import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.dto.response.MemberResponseDto;
 import page.clab.api.domain.member.dto.shared.BookBorrowerInfoDto;
+import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
@@ -117,10 +118,24 @@ public BookBorrowerInfoDto getCurrentMemberBorrowerInfo() {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public LoginMemberInfoDto getLoginMemberInfoById(String memberId) {
+        return memberRepository.findById(memberId)
+                .map(LoginMemberInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
     public void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate) {
-        Member member = getMemberById(memberId);
+        Member member = getMemberByIdOrThrow(memberId);
         member.updateLoanSuspensionDate(loanSuspensionDate);
         memberRepository.save(member);
     }
 
+    @Override
+    public void updateLastLoginTime(String id) {
+        Member member = getMemberByIdOrThrow(id);
+        member.updateLastLoginTime();
+        memberRepository.save(member);
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/LoginMemberInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/LoginMemberInfoDto.java
new file mode 100644
index 000000000..0ab964849
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/LoginMemberInfoDto.java
@@ -0,0 +1,37 @@
+package page.clab.api.domain.member.dto.shared;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.domain.Role;
+
+@Getter
+@Builder
+public class LoginMemberInfoDto {
+
+    private String memberId;
+
+    private String memberName;
+
+    private Role role;
+
+    private boolean isOtpEnabled;
+
+    public static LoginMemberInfoDto create(Member member) {
+        return LoginMemberInfoDto.builder()
+                .memberId(member.getId())
+                .memberName(member.getName())
+                .role(member.getRole())
+                .isOtpEnabled(member.getIsOtpEnabled())
+                .build();
+    }
+
+    public boolean isAdminRole() {
+        return role.toRoleLevel() >= 2;
+    }
+
+    public boolean isSuperAdminRole() {
+        return role.toRoleLevel() == 3;
+    }
+
+}
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
index 5f2c1a966..f792c595d 100644
--- a/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackService.java
@@ -9,7 +9,7 @@
 import org.springframework.stereotype.Service;
 import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
 import page.clab.api.domain.board.domain.SlackBoardInfo;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.global.common.slack.domain.GeneralAlertType;
 import page.clab.api.global.common.slack.domain.SecurityAlertType;
 import page.clab.api.global.common.slack.event.NotificationEvent;
@@ -29,7 +29,7 @@ public void sendSecurityAlertNotification(HttpServletRequest request, SecurityAl
         eventPublisher.publishEvent(new NotificationEvent(this, alertType, request, additionalMessage));
     }
 
-    public void sendAdminLoginNotification(HttpServletRequest request, Member loginMember) {
+    public void sendAdminLoginNotification(HttpServletRequest request, LoginMemberInfoDto loginMember) {
         eventPublisher.publishEvent(new NotificationEvent(this, GeneralAlertType.ADMIN_LOGIN, request, loginMember));
     }
 
diff --git a/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
index 67902c100..ebd6dc4cc 100644
--- a/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
+++ b/src/main/java/page/clab/api/global/common/slack/application/SlackServiceHelper.java
@@ -22,7 +22,7 @@
 import org.springframework.stereotype.Component;
 import page.clab.api.domain.application.dto.request.ApplicationRequestDto;
 import page.clab.api.domain.board.domain.SlackBoardInfo;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.global.common.slack.domain.AlertType;
 import page.clab.api.global.common.slack.domain.GeneralAlertType;
 import page.clab.api.global.common.slack.domain.SecurityAlertType;
@@ -103,8 +103,8 @@ public List<LayoutBlock> createBlocks(AlertType alertType, HttpServletRequest re
         } else if (alertType instanceof GeneralAlertType) {
             switch ((GeneralAlertType) alertType) {
                 case ADMIN_LOGIN:
-                    if (additionalData instanceof Member) {
-                        return createAdminLoginBlocks(request, (Member) additionalData);
+                    if (additionalData instanceof LoginMemberInfoDto) {
+                        return createAdminLoginBlocks(request, (LoginMemberInfoDto) additionalData);
                     }
                     break;
                 case APPLICATION_CREATED:
@@ -169,14 +169,14 @@ private List<LayoutBlock> createSecurityAlertBlocks(HttpServletRequest request,
         );
     }
 
-    private List<LayoutBlock> createAdminLoginBlocks(HttpServletRequest request, Member loginMember) {
+    private List<LayoutBlock> createAdminLoginBlocks(HttpServletRequest request, LoginMemberInfoDto loginMember) {
         String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
         String location = getLocation(request);
 
         return Arrays.asList(
                 section(section -> section.text(markdownText(String.format(":mechanic: *%s Login*", loginMember.getRole().getDescription())))),
                 section(section -> section.fields(Arrays.asList(
-                        markdownText("*User:*\n" + loginMember.getId() + " " + loginMember.getName()),
+                        markdownText("*User:*\n" + loginMember.getMemberId() + " " + loginMember.getMemberName()),
                         markdownText("*IP Address:*\n" + clientIpAddress),
                         markdownText("*Location:*\n" + location)
                 )))

From ce7fa67e4c956b3e7f89cb28162666dfe744dc76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 14:59:12 +0900
Subject: [PATCH 41/47] =?UTF-8?q?refactor(Event):=20=EB=B6=88=ED=95=84?=
 =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../blog/application/BlogEventProcessor.java  | 40 -------------------
 .../book/application/BookEventProcessor.java  | 34 ----------------
 .../application/CommentEventProcessor.java    | 34 ----------------
 .../application/DonationEventProcessor.java   | 34 ----------------
 4 files changed, 142 deletions(-)
 delete mode 100644 src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
 delete mode 100644 src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java
 delete mode 100644 src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java
 delete mode 100644 src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java b/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
deleted file mode 100644
index bd1a1d131..000000000
--- a/src/main/java/page/clab/api/domain/blog/application/BlogEventProcessor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package page.clab.api.domain.blog.application;
-
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.blog.dao.BlogRepository;
-import page.clab.api.domain.blog.domain.Blog;
-import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.event.MemberEventProcessor;
-import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
-
-import java.util.List;
-
-@Component
-@RequiredArgsConstructor
-public class BlogEventProcessor implements MemberEventProcessor {
-
-    private final BlogRepository blogRepository;
-
-    private final MemberEventProcessorRegistry processorRegistry;
-
-    @PostConstruct
-    public void init() {
-        processorRegistry.registerProcessor(this);
-    }
-
-    @Override
-    @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Blog> blogs = blogRepository.findByMemberId(member.getId());
-        blogs.forEach(Blog::delete);
-        blogRepository.saveAll(blogs);
-    }
-
-    @Override
-    public void processMemberUpdated(Member member) {
-        // do nothing
-    }
-}
diff --git a/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java b/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java
deleted file mode 100644
index 45af568a8..000000000
--- a/src/main/java/page/clab/api/domain/book/application/BookEventProcessor.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package page.clab.api.domain.book.application;
-
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.event.MemberEventProcessor;
-import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
-
-@Component
-@RequiredArgsConstructor
-public class BookEventProcessor implements MemberEventProcessor {
-
-    private final MemberEventProcessorRegistry processorRegistry;
-
-    @PostConstruct
-    public void init() {
-        processorRegistry.registerProcessor(this);
-    }
-
-    @Override
-    @Transactional
-    public void processMemberDeleted(Member member) {
-        // do nothing
-    }
-
-    @Override
-    @Transactional
-    public void processMemberUpdated(Member member) {
-        // do nothing
-    }
-
-}
diff --git a/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java b/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java
deleted file mode 100644
index 5f0bfcac7..000000000
--- a/src/main/java/page/clab/api/domain/comment/application/CommentEventProcessor.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package page.clab.api.domain.comment.application;
-
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.event.MemberEventProcessor;
-import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
-
-@Component
-@RequiredArgsConstructor
-public class CommentEventProcessor implements MemberEventProcessor {
-
-    private final MemberEventProcessorRegistry processorRegistry;
-
-    @PostConstruct
-    public void init() {
-        processorRegistry.registerProcessor(this);
-    }
-
-    @Override
-    @Transactional
-    public void processMemberDeleted(Member member) {
-        // do nothing
-    }
-
-    @Override
-    @Transactional
-    public void processMemberUpdated(Member member) {
-        // do nothing
-    }
-
-}
diff --git a/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java b/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java
deleted file mode 100644
index d2cff3110..000000000
--- a/src/main/java/page/clab/api/domain/donation/application/DonationEventProcessor.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package page.clab.api.domain.donation.application;
-
-import jakarta.annotation.PostConstruct;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
-import page.clab.api.domain.member.event.MemberEventProcessor;
-import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
-
-@Component
-@RequiredArgsConstructor
-public class DonationEventProcessor implements MemberEventProcessor {
-
-    private final MemberEventProcessorRegistry processorRegistry;
-
-    @PostConstruct
-    public void init() {
-        processorRegistry.registerProcessor(this);
-    }
-
-    @Override
-    @Transactional
-    public void processMemberDeleted(Member member) {
-        // do nothing
-    }
-
-    @Override
-    @Transactional
-    public void processMemberUpdated(Member member) {
-        // do nothing
-    }
-
-}

From 0fa465f8d8fb0411d156457b132c74ec827a6edd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 15:01:16 +0900
Subject: [PATCH 42/47] =?UTF-8?q?refactor(MembershipFee):=20Member=20?=
 =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?=
 =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20?=
 =?UTF-8?q?=EC=95=8A=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95?=
 =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/MembershipFeeService.java     | 33 +++++++++++--------
 .../dao/MembershipFeeRepositoryImpl.java      | 22 ++++++-------
 .../membershipFee/domain/MembershipFee.java   | 21 ++++++------
 .../dto/request/MembershipFeeRequestDto.java  |  5 ++-
 .../response/MembershipFeeResponseDto.java    |  8 ++---
 5 files changed, 47 insertions(+), 42 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java b/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
index 50466da4c..cb9afee66 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/application/MembershipFeeService.java
@@ -6,7 +6,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.membershipFee.dao.MembershipFeeRepository;
 import page.clab.api.domain.membershipFee.domain.MembershipFee;
 import page.clab.api.domain.membershipFee.domain.MembershipFeeStatus;
@@ -33,8 +33,8 @@ public class MembershipFeeService {
 
     @Transactional
     public Long createMembershipFee(MembershipFeeRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        MembershipFee membershipFee = MembershipFeeRequestDto.toEntity(requestDto, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        MembershipFee membershipFee = MembershipFeeRequestDto.toEntity(requestDto, currentMemberId);
         validationService.checkValid(membershipFee);
         notificationService.sendNotificationToAdmins("새로운 회비 내역이 등록되었습니다.");
         return membershipFeeRepository.save(membershipFee).getId();
@@ -42,35 +42,40 @@ public Long createMembershipFee(MembershipFeeRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<MembershipFeeResponseDto> getMembershipFeesByConditions(String memberId, String memberName, String category, MembershipFeeStatus status, Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        boolean isAdminOrSuper = currentMember.isAdminRole();
+        boolean isAdminRole = memberLookupService.getCurrentMemberDetailedInfo().isAdminRole();
         Page<MembershipFee> membershipFeesPage = membershipFeeRepository.findByConditions(memberId, memberName, category, status, pageable);
-        return new PagedResponseDto<>(membershipFeesPage.map(membershipFee -> MembershipFeeResponseDto.toDto(membershipFee, isAdminOrSuper)));
+        return new PagedResponseDto<>(membershipFeesPage.map(membershipFee -> {
+            String applicantName = memberLookupService.getMemberBasicInfoById(membershipFee.getMemberId()).getMemberName();
+            return MembershipFeeResponseDto.toDto(membershipFee, applicantName, isAdminRole);
+        }));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<MembershipFeeResponseDto> getDeletedMembershipFees(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        boolean isAdminOrSuper = currentMember.isAdminRole();
+        boolean isAdminRole = memberLookupService.getCurrentMemberDetailedInfo().isAdminRole();
         Page<MembershipFee> membershipFees = membershipFeeRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(membershipFees.map(membershipFee -> MembershipFeeResponseDto.toDto(membershipFee, isAdminOrSuper)));
+        return new PagedResponseDto<>(membershipFees.map(membershipFee -> {
+            String applicantName = memberLookupService.getMemberBasicInfoById(membershipFee.getMemberId()).getMemberName();
+            return MembershipFeeResponseDto.toDto(membershipFee, applicantName, isAdminRole);
+        }));
     }
 
     @Transactional
     public Long updateMembershipFee(Long membershipFeeId, MembershipFeeUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         MembershipFee membershipFee = getMembershipFeeByIdOrThrow(membershipFeeId);
-        membershipFee.validateAccessPermission(currentMember);
+        membershipFee.validateAccessPermission(currentMemberInfo);
         membershipFee.update(requestDto);
         validationService.checkValid(membershipFee);
         return membershipFeeRepository.save(membershipFee).getId();
     }
 
     public Long deleteMembershipFee(Long membershipFeeId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         MembershipFee membershipFee = getMembershipFeeByIdOrThrow(membershipFeeId);
-        membershipFee.validateAccessPermission(currentMember);
-        membershipFeeRepository.delete(membershipFee);
+        membershipFee.validateAccessPermission(currentMemberInfo);
+        membershipFee.delete();
+        membershipFeeRepository.save(membershipFee);
         return membershipFee.getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/membershipFee/dao/MembershipFeeRepositoryImpl.java b/src/main/java/page/clab/api/domain/membershipFee/dao/MembershipFeeRepositoryImpl.java
index df59dc1ce..430717414 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/dao/MembershipFeeRepositoryImpl.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/dao/MembershipFeeRepositoryImpl.java
@@ -23,25 +23,25 @@ public class MembershipFeeRepositoryImpl implements MembershipFeeRepositoryCusto
 
     @Override
     public Page<MembershipFee> findByConditions(String memberId, String memberName, String category, MembershipFeeStatus status, Pageable pageable) {
-        QMembershipFee qMembershipFee = QMembershipFee.membershipFee;
-        QMember qMember = QMember.member;
+        QMembershipFee membershipFee = QMembershipFee.membershipFee;
+        QMember member = QMember.member;
         BooleanBuilder builder = new BooleanBuilder();
 
-        if (memberId != null && !memberId.isEmpty()) builder.and(qMembershipFee.applicant.id.eq(memberId));
-        if (memberName != null && !memberName.isEmpty()) builder.and(qMember.name.eq(memberName));
-        if (category != null && !category.isEmpty()) builder.and(qMembershipFee.category.eq(category));
-        if (status != null) builder.and(qMembershipFee.status.eq(status));
+        if (memberId != null && !memberId.isEmpty()) builder.and(membershipFee.memberId.eq(memberId));
+        if (memberName != null && !memberName.isEmpty()) builder.and(member.name.eq(memberName));
+        if (category != null && !category.isEmpty()) builder.and(membershipFee.category.eq(category));
+        if (status != null) builder.and(membershipFee.status.eq(status));
 
-        List<MembershipFee> membershipFees = queryFactory.selectFrom(qMembershipFee)
-                .leftJoin(qMembershipFee.applicant, qMember)
+        List<MembershipFee> membershipFees = queryFactory.selectFrom(membershipFee)
+                .leftJoin(member).on(membershipFee.memberId.eq(member.id))
                 .where(builder)
-                .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, qMembershipFee))
+                .orderBy(OrderSpecifierUtil.getOrderSpecifiers(pageable, membershipFee))
                 .offset(pageable.getOffset())
                 .limit(pageable.getPageSize())
                 .fetch();
 
-        long count = queryFactory.selectFrom(qMembershipFee)
-                .leftJoin(qMembershipFee.applicant, qMember)
+        long count = queryFactory.selectFrom(membershipFee)
+                .leftJoin(member).on(membershipFee.memberId.eq(member.id))
                 .where(builder)
                 .fetchCount();
 
diff --git a/src/main/java/page/clab/api/domain/membershipFee/domain/MembershipFee.java b/src/main/java/page/clab/api/domain/membershipFee/domain/MembershipFee.java
index da52ea1d1..61a5f7e17 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/domain/MembershipFee.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/domain/MembershipFee.java
@@ -7,8 +7,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -18,7 +16,7 @@
 import lombok.Setter;
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.membershipFee.dto.request.MembershipFeeUpdateRequestDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -39,9 +37,8 @@ public class MembershipFee extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id")
-    private Member applicant;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @Column(nullable = false)
     @Size(min = 1, message = "{size.membershipFee.category}")
@@ -71,12 +68,16 @@ public void update(MembershipFeeUpdateRequestDto membershipFeeUpdateRequestDto)
         Optional.ofNullable(membershipFeeUpdateRequestDto.getStatus()).ifPresent(this::setStatus);
     }
 
-    public boolean isOwner(Member member) {
-        return this.applicant.isSameMember(member);
+    public void delete() {
+        this.isDeleted = true;
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isAdminRole()) {
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
+    }
+
+    public void validateAccessPermission(MemberDetailedInfoDto memberInfo) throws PermissionDeniedException {
+        if (!isOwner(memberInfo.getMemberId()) && !memberInfo.isAdminRole()) {
             throw new PermissionDeniedException("해당 회비를 수정/삭제할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/membershipFee/dto/request/MembershipFeeRequestDto.java b/src/main/java/page/clab/api/domain/membershipFee/dto/request/MembershipFeeRequestDto.java
index f073d7e10..cdceaa408 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/dto/request/MembershipFeeRequestDto.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/dto/request/MembershipFeeRequestDto.java
@@ -4,7 +4,6 @@
 import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.membershipFee.domain.MembershipFee;
 import page.clab.api.domain.membershipFee.domain.MembershipFeeStatus;
 
@@ -30,9 +29,9 @@ public class MembershipFeeRequestDto {
     @Schema(description = "증빙 사진", example = "https://images.chosun.com/resizer/mcbrEkwTr5YKQZ89QPO9hmdb0iE=/616x0/smart/cloudfront-ap-northeast-1.images.arcpublishing.com/chosun/LPCZYYKZ4FFIJPDD344FSGCLCY.jpg")
     private String imageUrl;
 
-    public static MembershipFee toEntity(MembershipFeeRequestDto requestDto, Member member) {
+    public static MembershipFee toEntity(MembershipFeeRequestDto requestDto, String memberId) {
         return MembershipFee.builder()
-                .applicant(member)
+                .memberId(memberId)
                 .category(requestDto.getCategory())
                 .account(requestDto.getAccount())
                 .amount(requestDto.getAmount())
diff --git a/src/main/java/page/clab/api/domain/membershipFee/dto/response/MembershipFeeResponseDto.java b/src/main/java/page/clab/api/domain/membershipFee/dto/response/MembershipFeeResponseDto.java
index 1ec870dd2..05b375bd8 100644
--- a/src/main/java/page/clab/api/domain/membershipFee/dto/response/MembershipFeeResponseDto.java
+++ b/src/main/java/page/clab/api/domain/membershipFee/dto/response/MembershipFeeResponseDto.java
@@ -31,13 +31,13 @@ public class MembershipFeeResponseDto {
 
     private LocalDateTime createdAt;
 
-    public static MembershipFeeResponseDto toDto(MembershipFee membershipFee, boolean isAdminOrSuper) {
+    public static MembershipFeeResponseDto toDto(MembershipFee membershipFee, String memberName, boolean isAdminRole) {
         return MembershipFeeResponseDto.builder()
                 .id(membershipFee.getId())
-                .memberId(membershipFee.getApplicant().getId())
-                .memberName(membershipFee.getApplicant().getName())
+                .memberId(membershipFee.getMemberId())
+                .memberName(memberName)
                 .category(membershipFee.getCategory())
-                .account(isAdminOrSuper ? membershipFee.getAccount() : null)
+                .account(isAdminRole ? membershipFee.getAccount() : null)
                 .amount(membershipFee.getAmount())
                 .content(membershipFee.getContent())
                 .imageUrl(membershipFee.getImageUrl())

From 63a0448cf4ece6d89a103cd36fb26f0860a54278 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 15:31:15 +0900
Subject: [PATCH 43/47] =?UTF-8?q?refactor(Notification):=20Member=20?=
 =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?=
 =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20?=
 =?UTF-8?q?=EC=95=8A=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95?=
 =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../ActivityGroupAdminService.java            |  8 ++--
 .../ActivityGroupBoardService.java            |  4 +-
 .../ActivityGroupMemberService.java           |  2 +-
 .../application/MemberLookupService.java      |  6 ++-
 .../application/MemberLookupServiceImpl.java  | 13 +++++-
 .../NotificationEventProcessor.java           | 41 ++++++++++++++++++
 .../application/NotificationService.java      | 42 ++++++++-----------
 .../dao/NotificationRepository.java           |  7 +++-
 .../notification/domain/Notification.java     | 26 ++++++------
 .../dto/request/NotificationRequestDto.java   |  5 +--
 10 files changed, 101 insertions(+), 53 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
index 6a9182b35..3b891a165 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupAdminService.java
@@ -62,7 +62,7 @@ public Long createActivityGroup(ActivityGroupRequestDto requestDto) {
         validationService.checkValid(groupLeader);
         activityGroupMemberService.save(groupLeader);
 
-        notificationService.sendNotificationToMember(currentMember, "활동 그룹 생성이 완료되었습니다. 활동 승인이 완료되면 활동 그룹을 이용할 수 있습니다.");
+        notificationService.sendNotificationToMember(currentMember.getId(), "활동 그룹 생성이 완료되었습니다. 활동 승인이 완료되면 활동 그룹을 이용할 수 있습니다.");
         return activityGroup.getId();
     }
 
@@ -87,7 +87,7 @@ public Long manageActivityGroup(Long activityGroupId, ActivityGroupStatus status
 
         GroupMember groupLeader = activityGroupMemberService.getGroupMemberByActivityGroupIdAndRole(activityGroupId, ActivityGroupRole.LEADER);
         if (groupLeader != null) {
-            notificationService.sendNotificationToMember(groupLeader.getMember(), "활동 그룹이 [" + status.getDescription() + "] 상태로 변경되었습니다.");
+            notificationService.sendNotificationToMember(groupLeader.getMember().getId(), "활동 그룹이 [" + status.getDescription() + "] 상태로 변경되었습니다.");
         }
         return activityGroup.getId();
     }
@@ -110,7 +110,7 @@ public Long deleteActivityGroup(Long activityGroupId) {
         activityGroupRepository.delete(activityGroup);
 
         if (groupLeader != null) {
-            notificationService.sendNotificationToMember(groupLeader.getMember(), "활동 그룹 [" + activityGroup.getName() + "]이 삭제되었습니다.");
+            notificationService.sendNotificationToMember(groupLeader.getMember().getId(), "활동 그룹 [" + activityGroup.getName() + "]이 삭제되었습니다.");
         }
         return activityGroup.getId();
     }
@@ -180,7 +180,7 @@ public String manageGroupMemberStatus(Long activityGroupId, String memberId, Gro
         groupMember.updateStatus(status);
         activityGroupMemberService.save(groupMember);
 
-        notificationService.sendNotificationToMember(member, "활동 그룹 신청이 [" + status.getDescription() + "] 상태로 변경되었습니다.");
+        notificationService.sendNotificationToMember(member.getId(), "활동 그룹 신청이 [" + status.getDescription() + "] 상태로 변경되었습니다.");
         return groupMember.getMember().getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
index e855dfcd9..433375682 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupBoardService.java
@@ -203,13 +203,13 @@ private void notifyMembersAboutNewBoard(Long activityGroupId, ActivityGroup acti
             groupMembers
                     .forEach(gMember -> {
                         if (!gMember.isOwner(member)) {
-                            notificationService.sendNotificationToMember(gMember.getMember(), "[" + activityGroup.getName() + "] " + member.getName() + "님이 새 게시글을 등록하였습니다.");
+                            notificationService.sendNotificationToMember(gMember.getMember().getId(), "[" + activityGroup.getName() + "] " + member.getName() + "님이 새 게시글을 등록하였습니다.");
                         }
                     });
         } else {
             GroupMember groupLeader = activityGroupMemberService.getGroupMemberByActivityGroupIdAndRole(activityGroupId, ActivityGroupRole.LEADER);
             if (groupLeader != null) {
-                notificationService.sendNotificationToMember(groupLeader.getMember(), "[" + activityGroup.getName() + "] " + member.getName() + "님이 새 게시글을 등록하였습니다.");
+                notificationService.sendNotificationToMember(groupLeader.getMember().getId(), "[" + activityGroup.getName() + "] " + member.getName() + "님이 새 게시글을 등록하였습니다.");
             }
         }
     }
diff --git a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
index 1a29b50c0..300b02596 100644
--- a/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
+++ b/src/main/java/page/clab/api/domain/activityGroup/application/ActivityGroupMemberService.java
@@ -149,7 +149,7 @@ public Long applyActivityGroup(Long activityGroupId, ApplyFormRequestDto formReq
 
         GroupMember groupLeader = getGroupMemberByActivityGroupIdAndRole(activityGroup.getId(), ActivityGroupRole.LEADER);
         if (groupLeader != null) {
-            notificationService.sendNotificationToMember(groupLeader.getMember(), "[" + activityGroup.getName() + "] " + currentMember.getName() + "님이 활동 참가 신청을 하였습니다.");
+            notificationService.sendNotificationToMember(groupLeader.getMember().getId(), "[" + activityGroup.getName() + "] " + currentMember.getName() + "님이 활동 참가 신청을 하였습니다.");
         }
         return activityGroup.getId();
     }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 11b09e06a..3aad095f3 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -12,6 +12,8 @@
 
 public interface MemberLookupService {
 
+    void ensureMemberExists(String memberId);
+
     Member getMemberById(String memberId);
 
     Member getMemberByIdOrThrow(String memberId);
@@ -26,9 +28,9 @@ public interface MemberLookupService {
 
     List<Member> findAllMembers();
 
-    List<Member> getAdmins();
+    List<String> getAdminIds();
 
-    List<Member> getSuperAdmins();
+    List<String> getSuperAdminIds();
 
     MemberBasicInfoDto getMemberBasicInfoById(String memberId);
 
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index a8431c998..3274b0864 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -21,6 +21,13 @@ public class MemberLookupServiceImpl implements MemberLookupService {
 
     private final MemberRepository memberRepository;
 
+    @Override
+    public void ensureMemberExists(String memberId) {
+        if (!memberRepository.existsById(memberId)) {
+            throw new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다.");
+        }
+    }
+
     @Override
     public Member getMemberById(String memberId) {
         return memberRepository.findById(memberId)
@@ -65,18 +72,20 @@ public List<Member> findAllMembers() {
     }
 
     @Override
-    public List<Member> getAdmins() {
+    public List<String> getAdminIds() {
         return memberRepository.findAll()
                 .stream()
                 .filter(Member::isAdminRole)
+                .map(Member::getId)
                 .toList();
     }
 
     @Override
-    public List<Member> getSuperAdmins() {
+    public List<String> getSuperAdminIds() {
         return memberRepository.findAll()
                 .stream()
                 .filter(Member::isSuperAdminRole)
+                .map(Member::getId)
                 .toList();
     }
 
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
new file mode 100644
index 000000000..c2e28269f
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
@@ -0,0 +1,41 @@
+package page.clab.api.domain.notification.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+import page.clab.api.domain.notification.dao.NotificationRepository;
+import page.clab.api.domain.notification.domain.Notification;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class NotificationEventProcessor implements MemberEventProcessor {
+
+    private final NotificationRepository notificationRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        List<Notification> notifications = notificationRepository.findByMemberId(member.getId());
+        notifications.forEach(Notification::delete);
+        notificationRepository.saveAll(notifications);
+    }
+
+    @Override
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
index ffbed68f6..abc8c9981 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationService.java
@@ -6,7 +6,6 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.dao.NotificationRepository;
 import page.clab.api.domain.notification.domain.Notification;
 import page.clab.api.domain.notification.dto.request.NotificationRequestDto;
@@ -31,16 +30,16 @@ public class NotificationService {
 
     @Transactional
     public Long createNotification(NotificationRequestDto requestDto) {
-        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getMemberId());
-        Notification notification = NotificationRequestDto.toEntity(requestDto, member);
+        memberLookupService.ensureMemberExists(requestDto.getMemberId());
+        Notification notification = NotificationRequestDto.toEntity(requestDto);
         validationService.checkValid(notification);
         return notificationRepository.save(notification).getId();
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<NotificationResponseDto> getNotifications(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<Notification> notifications = getNotificationByMember(currentMember, pageable);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<Notification> notifications = getNotificationByMemberId(currentMemberId, pageable);
         return new PagedResponseDto<>(notifications.map(NotificationResponseDto::toDto));
     }
 
@@ -50,51 +49,46 @@ public PagedResponseDto<NotificationResponseDto> getDeletedNotifications(Pageabl
         return new PagedResponseDto<>(notifications.map(NotificationResponseDto::toDto));
     }
 
+    @Transactional
     public Long deleteNotification(Long notificationId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        String currentMemberId = memberLookupService.getCurrentMemberId();
         Notification notification = getNotificationByIdOrThrow(notificationId);
-        notification.validateAccessPermission(currentMember);
+        notification.validateAccessPermission(currentMemberId);
         notificationRepository.delete(notification);
         return notification.getId();
     }
 
     public void sendNotificationToAllMembers(String content) {
         List<Notification> notifications = memberLookupService.findAllMembers().stream()
-                .map(member -> Notification.create(member, content))
+                .map(member -> Notification.create(member.getId(), content))
                 .toList();
         notificationRepository.saveAll(notifications);
     }
 
-    public void sendNotificationToMember(Member member, String content) {
-        Notification notification = Notification.create(member, content);
+    public void sendNotificationToMember(String memberId, String content) {
+        memberLookupService.ensureMemberExists(memberId);
+        Notification notification = Notification.create(memberId, content);
         notificationRepository.save(notification);
     }
 
     public void sendNotificationToMembers(List<String> memberIds, String content) {
         List<Notification> notifications = memberIds.stream()
-                .map(memberLookupService::getMemberByIdOrThrow)
-                .map(member -> Notification.create(member, content))
+                .map(memberId -> Notification.create(memberId, content))
                 .toList();
         notificationRepository.saveAll(notifications);
     }
 
-    public void sendNotificationToMember(String memberId, String content) {
-        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
-        Notification notification = Notification.create(member, content);
-        notificationRepository.save(notification);
-    }
-
     public void sendNotificationToAdmins(String content) {
-        sendNotificationToSpecificRole(memberLookupService::getAdmins, content);
+        sendNotificationToSpecificRole(memberLookupService::getAdminIds, content);
     }
 
     public void sendNotificationToSuperAdmins(String content) {
-        sendNotificationToSpecificRole(memberLookupService::getSuperAdmins, content);
+        sendNotificationToSpecificRole(memberLookupService::getSuperAdminIds, content);
     }
 
-    private void sendNotificationToSpecificRole(Supplier<List<Member>> memberSupplier, String content) {
+    private void sendNotificationToSpecificRole(Supplier<List<String>> memberSupplier, String content) {
         List<Notification> notifications = memberSupplier.get().stream()
-                .map(member -> Notification.create(member, content))
+                .map(memberId -> Notification.create(memberId, content))
                 .toList();
         notificationRepository.saveAll(notifications);
     }
@@ -104,8 +98,8 @@ private Notification getNotificationByIdOrThrow(Long notificationId) {
                 .orElseThrow(() -> new NotFoundException("존재하지 않는 알림입니다."));
     }
 
-    private Page<Notification> getNotificationByMember(Member member, Pageable pageable) {
-        return notificationRepository.findByMember(member, pageable);
+    private Page<Notification> getNotificationByMemberId(String memberId, Pageable pageable) {
+        return notificationRepository.findByMemberId(memberId, pageable);
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/notification/dao/NotificationRepository.java b/src/main/java/page/clab/api/domain/notification/dao/NotificationRepository.java
index b4769b491..d6e05e87d 100644
--- a/src/main/java/page/clab/api/domain/notification/dao/NotificationRepository.java
+++ b/src/main/java/page/clab/api/domain/notification/dao/NotificationRepository.java
@@ -4,12 +4,15 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.domain.Notification;
 
+import java.util.List;
+
 public interface NotificationRepository extends JpaRepository<Notification, Long> {
 
-    Page<Notification> findByMember(Member member, Pageable pageable);
+    Page<Notification> findByMemberId(String memberId, Pageable pageable);
+
+    List<Notification> findByMemberId(String memberId);
 
     @Query(value = "SELECT n.* FROM notification n WHERE n.is_deleted = true", nativeQuery = true)
     Page<Notification> findAllByIsDeletedTrue(Pageable pageable);
diff --git a/src/main/java/page/clab/api/domain/notification/domain/Notification.java b/src/main/java/page/clab/api/domain/notification/domain/Notification.java
index 9892018e3..058b4e865 100644
--- a/src/main/java/page/clab/api/domain/notification/domain/Notification.java
+++ b/src/main/java/page/clab/api/domain/notification/domain/Notification.java
@@ -5,8 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -16,7 +14,6 @@
 import lombok.Setter;
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
 
@@ -34,27 +31,30 @@ public class Notification extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
+
     @Column(nullable = false)
     @Size(min = 1, max = 1000, message = "{size.notification.content}")
     private String content;
 
-    @ManyToOne()
-    @JoinColumn(name = "member_id")
-    private Member member;
-
-    public static Notification create(Member member, String content) {
+    public static Notification create(String memberId, String content) {
         return Notification.builder()
+                .memberId(memberId)
                 .content(content)
-                .member(member)
                 .build();
     }
 
-    public boolean isOwner(Member member) {
-        return this.member.isSameMember(member);
+    public void delete() {
+        this.isDeleted = true;
+    }
+
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member)) {
+    public void validateAccessPermission(String memberId) throws PermissionDeniedException {
+        if (!isOwner(memberId)) {
             throw new PermissionDeniedException("해당 알림을 수정/삭제할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/notification/dto/request/NotificationRequestDto.java b/src/main/java/page/clab/api/domain/notification/dto/request/NotificationRequestDto.java
index 6c45b03f8..63746229d 100644
--- a/src/main/java/page/clab/api/domain/notification/dto/request/NotificationRequestDto.java
+++ b/src/main/java/page/clab/api/domain/notification/dto/request/NotificationRequestDto.java
@@ -4,7 +4,6 @@
 import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.notification.domain.Notification;
 
 @Getter
@@ -19,10 +18,10 @@ public class NotificationRequestDto {
     @Schema(description = "내용", example = "알림 내용", required = true)
     private String content;
 
-    public static Notification toEntity(NotificationRequestDto requestDto, Member member) {
+    public static Notification toEntity(NotificationRequestDto requestDto) {
         return Notification.builder()
                 .content(requestDto.getContent())
-                .member(member)
+                .memberId(requestDto.getMemberId())
                 .build();
     }
 

From 2b4dee1ae613ce89f37a4dcbe9b74082092d3b06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 15:54:07 +0900
Subject: [PATCH 44/47] =?UTF-8?q?refactor(Position):=20Member=20=EB=8F=84?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=A7=81?=
 =?UTF-8?q?=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20=EC=95=8A?=
 =?UTF-8?q?=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95=EB=B3=B4?=
 =?UTF-8?q?=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/MemberLookupService.java      |  3 ++
 .../application/MemberLookupServiceImpl.java  |  9 ++++
 .../member/application/MemberService.java     |  4 +-
 .../dto/shared/MemberPositionInfoDto.java     | 34 +++++++++++++++
 .../application/PositionEventProcessor.java   | 41 +++++++++++++++++++
 .../position/application/PositionService.java | 27 ++++++------
 .../position/dao/PositionRepository.java      |  7 ++--
 .../api/domain/position/domain/Position.java  | 16 ++++----
 .../dto/request/PositionRequestDto.java       |  3 +-
 .../dto/response/PositionMyResponseDto.java   | 15 ++++---
 .../dto/response/PositionResponseDto.java     | 15 ++++---
 11 files changed, 131 insertions(+), 43 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/member/dto/shared/MemberPositionInfoDto.java
 create mode 100644 src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
index 3aad095f3..85bf69667 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupService.java
@@ -6,6 +6,7 @@
 import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberPositionInfoDto;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -44,6 +45,8 @@ public interface MemberLookupService {
 
     LoginMemberInfoDto getLoginMemberInfoById(String memberId);
 
+    MemberPositionInfoDto getCurrentMemberPositionInfo();
+
     void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate);
 
     void updateLastLoginTime(String id);
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
index 3274b0864..cec61a678 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberLookupServiceImpl.java
@@ -9,6 +9,7 @@
 import page.clab.api.domain.member.dto.shared.LoginMemberInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
 import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
+import page.clab.api.domain.member.dto.shared.MemberPositionInfoDto;
 import page.clab.api.global.auth.util.AuthUtil;
 import page.clab.api.global.exception.NotFoundException;
 
@@ -134,6 +135,14 @@ public LoginMemberInfoDto getLoginMemberInfoById(String memberId) {
                 .orElseThrow(() -> new NotFoundException("[Member] id: " + memberId + "에 해당하는 멤버가 존재하지 않습니다."));
     }
 
+    @Override
+    public MemberPositionInfoDto getCurrentMemberPositionInfo() {
+        String currentMemberId = getCurrentMemberId();
+        return memberRepository.findById(currentMemberId)
+                .map(MemberPositionInfoDto::create)
+                .orElseThrow(() -> new NotFoundException("[Member] id: " + currentMemberId + "에 해당하는 멤버가 존재하지 않습니다."));
+    }
+
     public void updateLoanSuspensionDate(String memberId, LocalDateTime loanSuspensionDate) {
         Member member = getMemberByIdOrThrow(memberId);
         member.updateLoanSuspensionDate(loanSuspensionDate);
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberService.java b/src/main/java/page/clab/api/domain/member/application/MemberService.java
index 820fdb6b2..7b9e54c5c 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberService.java
@@ -203,10 +203,10 @@ private void checkMemberUniqueness(MemberRequestDto requestDto) {
     }
 
     public void createPositionByMember(Member member) {
-        if (positionRepository.findByMemberAndYearAndPositionType(member, String.valueOf(LocalDate.now().getYear()), PositionType.MEMBER).isPresent()) {
+        if (positionRepository.findByMemberIdAndYearAndPositionType(member.getId(), String.valueOf(LocalDate.now().getYear()), PositionType.MEMBER).isPresent()) {
             return;
         }
-        Position position = Position.create(member);
+        Position position = Position.create(member.getId());
         positionRepository.save(position);
     }
 
diff --git a/src/main/java/page/clab/api/domain/member/dto/shared/MemberPositionInfoDto.java b/src/main/java/page/clab/api/domain/member/dto/shared/MemberPositionInfoDto.java
new file mode 100644
index 000000000..90053d6f5
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/member/dto/shared/MemberPositionInfoDto.java
@@ -0,0 +1,34 @@
+package page.clab.api.domain.member.dto.shared;
+
+import lombok.Builder;
+import lombok.Getter;
+import page.clab.api.domain.member.domain.Member;
+
+@Getter
+@Builder
+public class MemberPositionInfoDto {
+
+    private String memberId;
+
+    private String memberName;
+
+    private String email;
+
+    private String imageUrl;
+
+    private String interests;
+
+    private String githubUrl;
+
+    public static MemberPositionInfoDto create(Member member) {
+        return MemberPositionInfoDto.builder()
+                .memberId(member.getId())
+                .memberName(member.getName())
+                .email(member.getEmail())
+                .imageUrl(member.getImageUrl())
+                .interests(member.getInterests())
+                .githubUrl(member.getGithubUrl())
+                .build();
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
new file mode 100644
index 000000000..fea0e96be
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
@@ -0,0 +1,41 @@
+package page.clab.api.domain.position.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+import page.clab.api.domain.position.dao.PositionRepository;
+import page.clab.api.domain.position.domain.Position;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class PositionEventProcessor implements MemberEventProcessor {
+
+    private final PositionRepository positionRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        List<Position> positions = positionRepository.findByMemberId(member.getId());
+        positions.forEach(Position::delete);
+        positionRepository.saveAll(positions);
+    }
+
+    @Override
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+
+}
diff --git a/src/main/java/page/clab/api/domain/position/application/PositionService.java b/src/main/java/page/clab/api/domain/position/application/PositionService.java
index 22eb51f6a..f2b4f512d 100644
--- a/src/main/java/page/clab/api/domain/position/application/PositionService.java
+++ b/src/main/java/page/clab/api/domain/position/application/PositionService.java
@@ -6,7 +6,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberPositionInfoDto;
 import page.clab.api.domain.position.dao.PositionRepository;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
@@ -28,8 +28,8 @@ public class PositionService {
 
     @Transactional
     public Long createPosition(PositionRequestDto requestDto) {
-        Member member = memberLookupService.getMemberByIdOrThrow(requestDto.getMemberId());
-        return positionRepository.findByMemberAndYearAndPositionType(member, requestDto.getYear(), requestDto.getPositionType())
+        memberLookupService.ensureMemberExists(requestDto.getMemberId());
+        return positionRepository.findByMemberIdAndYearAndPositionType(requestDto.getMemberId(), requestDto.getYear(), requestDto.getPositionType())
                 .map(Position::getId)
                 .orElseGet(() -> {
                     Position position = PositionRequestDto.toEntity(requestDto);
@@ -39,30 +39,33 @@ public Long createPosition(PositionRequestDto requestDto) {
 
     @Transactional(readOnly = true)
     public PagedResponseDto<PositionResponseDto> getPositionsByConditions(String year, PositionType positionType, Pageable pageable) {
+        MemberPositionInfoDto currentMemberInfo = memberLookupService.getCurrentMemberPositionInfo();
         Page<Position> positions = positionRepository.findByConditions(year, positionType, pageable);
-        return new PagedResponseDto<>(positions.map(PositionResponseDto::toDto));
+        return new PagedResponseDto<>(positions.map(position -> PositionResponseDto.toDto(position, currentMemberInfo)));
     }
 
     @Transactional(readOnly = true)
     public PositionMyResponseDto getMyPositionsByYear(String year) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        List<Position> positions = getPositionsByMemberAndYear(currentMember, year);
+        MemberPositionInfoDto currentMemberInfo = memberLookupService.getCurrentMemberPositionInfo();
+        List<Position> positions = getPositionsByMemberIdAndYear(currentMemberInfo.getMemberId(), year);
         if (positions.isEmpty()) {
             throw new NotFoundException("해당 멤버의 " + year + "년도 직책이 존재하지 않습니다.");
         }
-        return PositionMyResponseDto.toDto(positions);
+        return PositionMyResponseDto.toDto(positions, currentMemberInfo);
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<PositionResponseDto> getDeletedPositions(Pageable pageable) {
+        MemberPositionInfoDto currentMemberInfo = memberLookupService.getCurrentMemberPositionInfo();
         Page<Position> positions = positionRepository.findAllByIsDeletedTrue(pageable);
-        return new PagedResponseDto<>(positions.map(PositionResponseDto::toDto));
+        return new PagedResponseDto<>(positions.map(position -> PositionResponseDto.toDto(position, currentMemberInfo)));
     }
 
+    @Transactional
     public Long deletePosition(Long positionId) {
         Position position = getPositionsByIdOrThrow(positionId);
-        positionRepository.delete(position);
-        return position.getId();
+        position.delete();
+        return positionRepository.save(position).getId();
     }
 
     private Position getPositionsByIdOrThrow(Long positionId) {
@@ -70,8 +73,8 @@ private Position getPositionsByIdOrThrow(Long positionId) {
                 .orElseThrow(() -> new NotFoundException("해당 운영진이 존재하지 않습니다."));
     }
 
-    private List<Position> getPositionsByMemberAndYear(Member member, String year) {
-        return positionRepository.findAllByMemberAndYearOrderByPositionTypeAsc(member, year);
+    private List<Position> getPositionsByMemberIdAndYear(String memberId, String year) {
+        return positionRepository.findAllByMemberIdAndYearOrderByPositionTypeAsc(memberId, year);
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/position/dao/PositionRepository.java b/src/main/java/page/clab/api/domain/position/dao/PositionRepository.java
index a4dc9333a..a9c2dc836 100644
--- a/src/main/java/page/clab/api/domain/position/dao/PositionRepository.java
+++ b/src/main/java/page/clab/api/domain/position/dao/PositionRepository.java
@@ -5,7 +5,6 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
 
@@ -14,9 +13,11 @@
 
 public interface PositionRepository extends JpaRepository<Position, Long>, PositionRepositoryCustom, QuerydslPredicateExecutor<Position> {
 
-    Optional<Position> findByMemberAndYearAndPositionType(Member member, String year, PositionType positionType);
+    List<Position> findByMemberId(String id);
 
-    List<Position> findAllByMemberAndYearOrderByPositionTypeAsc(Member member, String year);
+    Optional<Position> findByMemberIdAndYearAndPositionType(String memberId, String year, PositionType positionType);
+
+    List<Position> findAllByMemberIdAndYearOrderByPositionTypeAsc(String memberId, String year);
 
     @Query(value = "SELECT p.* FROM \"position\" p WHERE p.is_deleted = true", nativeQuery = true)
     Page<Position> findAllByIsDeletedTrue(Pageable pageable);
diff --git a/src/main/java/page/clab/api/domain/position/domain/Position.java b/src/main/java/page/clab/api/domain/position/domain/Position.java
index 16cd0541e..4b7af2799 100644
--- a/src/main/java/page/clab/api/domain/position/domain/Position.java
+++ b/src/main/java/page/clab/api/domain/position/domain/Position.java
@@ -5,8 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
@@ -15,7 +13,6 @@
 import lombok.Setter;
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.global.common.domain.BaseEntity;
 
 import java.time.LocalDate;
@@ -34,9 +31,8 @@ public class Position extends BaseEntity {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
 
-    @ManyToOne
-    @JoinColumn(name = "member_id", nullable = false)
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     @Column(nullable = false)
     private PositionType positionType;
@@ -44,12 +40,16 @@ public class Position extends BaseEntity {
     @Column(nullable = false)
     private String year;
 
-    public static Position create(Member member) {
+    public static Position create(String memberId) {
         return Position.builder()
-                .member(member)
+                .memberId(memberId)
                 .positionType(PositionType.MEMBER)
                 .year(String.valueOf(LocalDate.now().getYear()))
                 .build();
     }
 
+    public void delete() {
+        this.isDeleted = true;
+    }
+
 }
diff --git a/src/main/java/page/clab/api/domain/position/dto/request/PositionRequestDto.java b/src/main/java/page/clab/api/domain/position/dto/request/PositionRequestDto.java
index 1b1d07f32..9c653eb93 100644
--- a/src/main/java/page/clab/api/domain/position/dto/request/PositionRequestDto.java
+++ b/src/main/java/page/clab/api/domain/position/dto/request/PositionRequestDto.java
@@ -4,7 +4,6 @@
 import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
 
@@ -26,7 +25,7 @@ public class PositionRequestDto {
 
     public static Position toEntity(PositionRequestDto positionRequestDto) {
         return Position.builder()
-                .member(Member.builder().id(positionRequestDto.getMemberId()).build())
+                .memberId(positionRequestDto.getMemberId())
                 .positionType(positionRequestDto.getPositionType())
                 .year(positionRequestDto.getYear())
                 .build();
diff --git a/src/main/java/page/clab/api/domain/position/dto/response/PositionMyResponseDto.java b/src/main/java/page/clab/api/domain/position/dto/response/PositionMyResponseDto.java
index cf33beadb..95ad6ad80 100644
--- a/src/main/java/page/clab/api/domain/position/dto/response/PositionMyResponseDto.java
+++ b/src/main/java/page/clab/api/domain/position/dto/response/PositionMyResponseDto.java
@@ -2,7 +2,7 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberPositionInfoDto;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
 
@@ -28,19 +28,18 @@ public class PositionMyResponseDto {
 
     private Map<String, List<PositionType>> positionTypes;
 
-    public static PositionMyResponseDto toDto(List<Position> positions) {
-        Member member = positions.getFirst().getMember();
+    public static PositionMyResponseDto toDto(List<Position> positions, MemberPositionInfoDto memberInfo) {
         Map<String, List<PositionType>> positionTypesByYear = positions.stream()
                 .collect(Collectors.groupingBy(
                         Position::getYear,
                         Collectors.mapping(Position::getPositionType, Collectors.toList())
                 ));
         return PositionMyResponseDto.builder()
-                .name(member.getName())
-                .email(member.getEmail())
-                .imageUrl(member.getImageUrl())
-                .interests(member.getInterests())
-                .githubUrl(member.getGithubUrl())
+                .name(memberInfo.getMemberName())
+                .email(memberInfo.getEmail())
+                .imageUrl(memberInfo.getImageUrl())
+                .interests(memberInfo.getInterests())
+                .githubUrl(memberInfo.getGithubUrl())
                 .positionTypes(positionTypesByYear)
                 .build();
     }
diff --git a/src/main/java/page/clab/api/domain/position/dto/response/PositionResponseDto.java b/src/main/java/page/clab/api/domain/position/dto/response/PositionResponseDto.java
index 30341d5b9..e09ab0715 100644
--- a/src/main/java/page/clab/api/domain/position/dto/response/PositionResponseDto.java
+++ b/src/main/java/page/clab/api/domain/position/dto/response/PositionResponseDto.java
@@ -2,7 +2,7 @@
 
 import lombok.Builder;
 import lombok.Getter;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberPositionInfoDto;
 import page.clab.api.domain.position.domain.Position;
 import page.clab.api.domain.position.domain.PositionType;
 
@@ -26,15 +26,14 @@ public class PositionResponseDto {
 
     private String year;
 
-    public static PositionResponseDto toDto(Position position) {
-        Member member = position.getMember();
+    public static PositionResponseDto toDto(Position position, MemberPositionInfoDto memberInfo) {
         return PositionResponseDto.builder()
                 .id(position.getId())
-                .name(member.getName())
-                .email(member.getEmail())
-                .imageUrl(member.getImageUrl())
-                .interests(member.getInterests())
-                .githubUrl(member.getGithubUrl())
+                .name(memberInfo.getMemberName())
+                .email(memberInfo.getEmail())
+                .imageUrl(memberInfo.getImageUrl())
+                .interests(memberInfo.getInterests())
+                .githubUrl(memberInfo.getGithubUrl())
                 .positionType(position.getPositionType())
                 .year(position.getYear())
                 .build();

From 534f0b7be607c72ee5c22bcfd7364af27ac05ec7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 16:14:43 +0900
Subject: [PATCH 45/47] =?UTF-8?q?refactor(WorkExperience):=20Member=20?=
 =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?=
 =?UTF-8?q?=EC=A7=81=EC=A0=91=20=EC=B0=B8=EC=A1=B0=ED=95=98=EC=A7=80=20?=
 =?UTF-8?q?=EC=95=8A=EA=B3=A0,=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=95?=
 =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20DTO=EB=A1=9C=20=EC=A3=BC=EA=B3=A0=EB=B0=9B?=
 =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../WorkExperienceEventProcessor.java         | 40 +++++++++++++++++++
 .../application/WorkExperienceService.java    | 26 ++++++------
 .../dao/WorkExperienceRepository.java         |  7 +++-
 .../workExperience/domain/WorkExperience.java | 19 +++++----
 .../dto/request/WorkExperienceRequestDto.java |  5 +--
 5 files changed, 71 insertions(+), 26 deletions(-)
 create mode 100644 src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java

diff --git a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
new file mode 100644
index 000000000..0a9a38e73
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
@@ -0,0 +1,40 @@
+package page.clab.api.domain.workExperience.application;
+
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.event.MemberEventProcessor;
+import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
+import page.clab.api.domain.workExperience.dao.WorkExperienceRepository;
+import page.clab.api.domain.workExperience.domain.WorkExperience;
+
+import java.util.List;
+
+@Component
+@RequiredArgsConstructor
+public class WorkExperienceEventProcessor implements MemberEventProcessor {
+
+    private final WorkExperienceRepository workExperienceRepository;
+
+    private final MemberEventProcessorRegistry processorRegistry;
+
+    @PostConstruct
+    public void init() {
+        processorRegistry.registerProcessor(this);
+    }
+
+    @Override
+    @Transactional
+    public void processMemberDeleted(Member member) {
+        List<WorkExperience> workExperiences = workExperienceRepository.findByMemberId(member.getId());
+        workExperiences.forEach(WorkExperience::delete);
+        workExperienceRepository.saveAll(workExperiences);
+    }
+
+    @Override
+    public void processMemberUpdated(Member member) {
+        // do nothing
+    }
+}
diff --git a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
index 7c094dd6a..ed5ec7997 100644
--- a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
+++ b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceService.java
@@ -6,7 +6,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.member.application.MemberLookupService;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.workExperience.dao.WorkExperienceRepository;
 import page.clab.api.domain.workExperience.domain.WorkExperience;
 import page.clab.api.domain.workExperience.dto.request.WorkExperienceRequestDto;
@@ -29,23 +29,22 @@ public class WorkExperienceService {
 
     @Transactional
     public Long createWorkExperience(WorkExperienceRequestDto requestDto) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        WorkExperience workExperience = WorkExperienceRequestDto.toEntity(requestDto, currentMember);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        WorkExperience workExperience = WorkExperienceRequestDto.toEntity(requestDto, currentMemberId);
         validationService.checkValid(workExperience);
         return workExperienceRepository.save(workExperience).getId();
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<WorkExperienceResponseDto> getMyWorkExperience(Pageable pageable) {
-        Member currentMember = memberLookupService.getCurrentMember();
-        Page<WorkExperience> workExperiences = workExperienceRepository.findAllByMember(currentMember, pageable);
+        String currentMemberId = memberLookupService.getCurrentMemberId();
+        Page<WorkExperience> workExperiences = workExperienceRepository.findByMemberId(currentMemberId, pageable);
         return new PagedResponseDto<>(workExperiences.map(WorkExperienceResponseDto::toDto));
     }
 
     @Transactional(readOnly = true)
     public PagedResponseDto<WorkExperienceResponseDto> getWorkExperiencesByConditions(String memberId, Pageable pageable) {
-        Member member = memberLookupService.getMemberByIdOrThrow(memberId);
-        Page<WorkExperience> workExperiences = workExperienceRepository.findAllByMember(member, pageable);
+        Page<WorkExperience> workExperiences = workExperienceRepository.findByMemberId(memberId, pageable);
         return new PagedResponseDto<>(workExperiences.map(WorkExperienceResponseDto::toDto));
     }
 
@@ -57,20 +56,21 @@ public PagedResponseDto<WorkExperienceResponseDto> getDeletedWorkExperiences(Pag
 
     @Transactional
     public Long updateWorkExperience(Long workExperienceId, WorkExperienceUpdateRequestDto requestDto) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         WorkExperience workExperience = getWorkExperienceByIdOrThrow(workExperienceId);
-        workExperience.validateAccessPermission(currentMember);
+        workExperience.validateAccessPermission(currentMemberInfo);
         workExperience.update(requestDto);
         validationService.checkValid(workExperience);
         return workExperienceRepository.save(workExperience).getId();
     }
 
+    @Transactional
     public Long deleteWorkExperience(Long workExperienceId) throws PermissionDeniedException {
-        Member currentMember = memberLookupService.getCurrentMember();
+        MemberDetailedInfoDto currentMemberInfo = memberLookupService.getCurrentMemberDetailedInfo();
         WorkExperience workExperience = getWorkExperienceByIdOrThrow(workExperienceId);
-        workExperience.validateAccessPermission(currentMember);
-        workExperienceRepository.deleteById(workExperienceId);
-        return workExperience.getId();
+        workExperience.validateAccessPermission(currentMemberInfo);
+        workExperience.delete();
+        return workExperienceRepository.save(workExperience).getId();
     }
 
     private WorkExperience getWorkExperienceByIdOrThrow(Long workExperienceId) {
diff --git a/src/main/java/page/clab/api/domain/workExperience/dao/WorkExperienceRepository.java b/src/main/java/page/clab/api/domain/workExperience/dao/WorkExperienceRepository.java
index f8106d8b0..d059f4368 100644
--- a/src/main/java/page/clab/api/domain/workExperience/dao/WorkExperienceRepository.java
+++ b/src/main/java/page/clab/api/domain/workExperience/dao/WorkExperienceRepository.java
@@ -5,13 +5,16 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.workExperience.domain.WorkExperience;
 
+import java.util.List;
+
 @Repository
 public interface WorkExperienceRepository extends JpaRepository<WorkExperience, Long> {
 
-    Page<WorkExperience> findAllByMember(Member member, Pageable pageable);
+    Page<WorkExperience> findByMemberId(String memberId, Pageable pageable);
+
+    List<WorkExperience> findByMemberId(String memberId);
 
     @Query(value = "SELECT w.* FROM work_experience w WHERE w.is_deleted = true", nativeQuery = true)
     Page<WorkExperience> findAllByIsDeletedTrue(Pageable pageable);
diff --git a/src/main/java/page/clab/api/domain/workExperience/domain/WorkExperience.java b/src/main/java/page/clab/api/domain/workExperience/domain/WorkExperience.java
index b74f05ecd..fe1e7f3f1 100644
--- a/src/main/java/page/clab/api/domain/workExperience/domain/WorkExperience.java
+++ b/src/main/java/page/clab/api/domain/workExperience/domain/WorkExperience.java
@@ -5,7 +5,6 @@
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.ManyToOne;
 import jakarta.validation.constraints.Size;
 import lombok.AccessLevel;
 import lombok.AllArgsConstructor;
@@ -15,7 +14,7 @@
 import lombok.Setter;
 import org.hibernate.annotations.SQLDelete;
 import org.hibernate.annotations.SQLRestriction;
-import page.clab.api.domain.member.domain.Member;
+import page.clab.api.domain.member.dto.shared.MemberDetailedInfoDto;
 import page.clab.api.domain.workExperience.dto.request.WorkExperienceUpdateRequestDto;
 import page.clab.api.global.common.domain.BaseEntity;
 import page.clab.api.global.exception.PermissionDeniedException;
@@ -51,8 +50,8 @@ public class WorkExperience extends BaseEntity {
     @Column(nullable = false)
     private LocalDate endDate;
 
-    @ManyToOne
-    private Member member;
+    @Column(name = "member_id", nullable = false)
+    private String memberId;
 
     public void update(WorkExperienceUpdateRequestDto workExperienceUpdateRequestDto) {
         Optional.ofNullable(workExperienceUpdateRequestDto.getCompanyName()).ifPresent(this::setCompanyName);
@@ -61,12 +60,16 @@ public void update(WorkExperienceUpdateRequestDto workExperienceUpdateRequestDto
         Optional.ofNullable(workExperienceUpdateRequestDto.getEndDate()).ifPresent(this::setEndDate);
     }
 
-    public boolean isOwner(Member member) {
-        return this.member.isSameMember(member);
+    public void delete() {
+        this.isDeleted = true;
     }
 
-    public void validateAccessPermission(Member member) throws PermissionDeniedException {
-        if (!isOwner(member) && !member.isSuperAdminRole()) {
+    public boolean isOwner(String memberId) {
+        return this.memberId.equals(memberId);
+    }
+
+    public void validateAccessPermission(MemberDetailedInfoDto memberInfo) throws PermissionDeniedException {
+        if (!isOwner(memberInfo.getMemberId()) && !memberInfo.isSuperAdminRole()) {
             throw new PermissionDeniedException("해당 경력사항을 수정/삭제할 권한이 없습니다.");
         }
     }
diff --git a/src/main/java/page/clab/api/domain/workExperience/dto/request/WorkExperienceRequestDto.java b/src/main/java/page/clab/api/domain/workExperience/dto/request/WorkExperienceRequestDto.java
index febf392b4..1ef63b519 100644
--- a/src/main/java/page/clab/api/domain/workExperience/dto/request/WorkExperienceRequestDto.java
+++ b/src/main/java/page/clab/api/domain/workExperience/dto/request/WorkExperienceRequestDto.java
@@ -4,7 +4,6 @@
 import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.Setter;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.workExperience.domain.WorkExperience;
 
 import java.time.LocalDate;
@@ -29,13 +28,13 @@ public class WorkExperienceRequestDto {
     @Schema(description = "종료일", example = "2023-12-31", required = true)
     private LocalDate endDate;
 
-    public static WorkExperience toEntity(WorkExperienceRequestDto requestDto, Member member) {
+    public static WorkExperience toEntity(WorkExperienceRequestDto requestDto, String memberId) {
         return WorkExperience.builder()
                 .companyName(requestDto.getCompanyName())
                 .position(requestDto.getPosition())
                 .startDate(requestDto.getStartDate())
                 .endDate(requestDto.getEndDate())
-                .member(member)
+                .memberId(memberId)
                 .build();
     }
 

From 6a719fe924f762ce63f65108698946a27b4b19fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 16:15:53 +0900
Subject: [PATCH 46/47] =?UTF-8?q?refactor(EventProcessor):=20=EB=A9=94?=
 =?UTF-8?q?=EC=86=8C=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../api/domain/accuse/application/AccuseEventProcessor.java     | 2 +-
 .../clab/api/domain/award/application/AwardEventProcessor.java  | 2 +-
 .../clab/api/domain/board/application/BoardEventProcessor.java  | 2 +-
 .../api/domain/member/event/MemberEventProcessorRegistry.java   | 2 +-
 .../notification/application/NotificationEventProcessor.java    | 2 +-
 .../api/domain/position/application/PositionEventProcessor.java | 2 +-
 .../application/WorkExperienceEventProcessor.java               | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
index 6dab8da72..54d599d70 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
@@ -22,7 +22,7 @@ public class AccuseEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
index 2372393aa..ef57a40fa 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
@@ -22,7 +22,7 @@ public class AwardEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
index e7ffa7a03..8fd70117f 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
@@ -22,7 +22,7 @@ public class BoardEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
index 90f9098a5..2a7c542f1 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessorRegistry.java
@@ -11,7 +11,7 @@ public class MemberEventProcessorRegistry {
 
     private final List<MemberEventProcessor> processors = new ArrayList<>();
 
-    public void registerProcessor(MemberEventProcessor processor) {
+    public void register(MemberEventProcessor processor) {
         processors.add(processor);
     }
 
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
index c2e28269f..48f5c5146 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
@@ -22,7 +22,7 @@ public class NotificationEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
index fea0e96be..0754debd9 100644
--- a/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
@@ -22,7 +22,7 @@ public class PositionEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override
diff --git a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
index 0a9a38e73..4741e575c 100644
--- a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
@@ -22,7 +22,7 @@ public class WorkExperienceEventProcessor implements MemberEventProcessor {
 
     @PostConstruct
     public void init() {
-        processorRegistry.registerProcessor(this);
+        processorRegistry.register(this);
     }
 
     @Override

From ac42e208261f8042f68a74142569ea5a3101ab2b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= <noop103@naver.com>
Date: Wed, 26 Jun 2024 16:21:33 +0900
Subject: [PATCH 47/47] =?UTF-8?q?refactor(EventProcessor):=20Member=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95/=EC=82=AD=EC=A0=9C=20=EC=9D=B4=EB=B2=A4?=
 =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EB=8C=80=ED=95=B4=20=EC=B5=9C=EC=86=8C?=
 =?UTF-8?q?=ED=95=9C=EC=9D=98=20=EC=A0=95=EB=B3=B4=EB=A7=8C=20=EC=A0=84?=
 =?UTF-8?q?=EB=8B=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/accuse/application/AccuseEventProcessor.java    | 7 +++----
 .../api/domain/award/application/AwardEventProcessor.java  | 7 +++----
 .../api/domain/board/application/BoardEventProcessor.java  | 7 +++----
 .../clab/api/domain/member/application/MemberService.java  | 4 ++--
 .../clab/api/domain/member/event/MemberDeletedEvent.java   | 7 +++----
 .../api/domain/member/event/MemberEventDispatcher.java     | 4 ++--
 .../clab/api/domain/member/event/MemberEventProcessor.java | 6 ++----
 .../clab/api/domain/member/event/MemberUpdatedEvent.java   | 7 +++----
 .../application/NotificationEventProcessor.java            | 7 +++----
 .../position/application/PositionEventProcessor.java       | 7 +++----
 .../application/WorkExperienceEventProcessor.java          | 7 +++----
 11 files changed, 30 insertions(+), 40 deletions(-)

diff --git a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
index 54d599d70..e80d22bc0 100644
--- a/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/accuse/application/AccuseEventProcessor.java
@@ -6,7 +6,6 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.accuse.dao.AccuseRepository;
 import page.clab.api.domain.accuse.domain.Accuse;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Accuse> accuses = accuseRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<Accuse> accuses = accuseRepository.findByMemberId(memberId);
         accuses.forEach(Accuse::delete);
         accuseRepository.saveAll(accuses);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 
diff --git a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
index ef57a40fa..c240e2647 100644
--- a/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/award/application/AwardEventProcessor.java
@@ -6,7 +6,6 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.award.dao.AwardRepository;
 import page.clab.api.domain.award.domain.Award;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Award> awards = awardRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<Award> awards = awardRepository.findByMemberId(memberId);
         awards.forEach(Award::delete);
         awardRepository.saveAll(awards);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 }
diff --git a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
index 8fd70117f..4a75bb9a8 100644
--- a/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/board/application/BoardEventProcessor.java
@@ -6,7 +6,6 @@
 import org.springframework.transaction.annotation.Transactional;
 import page.clab.api.domain.board.dao.BoardRepository;
 import page.clab.api.domain.board.domain.Board;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Board> boards = boardRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<Board> boards = boardRepository.findByMemberId(memberId);
         boards.forEach(Board::delete);
         boardRepository.saveAll(boards);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 }
diff --git a/src/main/java/page/clab/api/domain/member/application/MemberService.java b/src/main/java/page/clab/api/domain/member/application/MemberService.java
index 7b9e54c5c..7ef226d60 100644
--- a/src/main/java/page/clab/api/domain/member/application/MemberService.java
+++ b/src/main/java/page/clab/api/domain/member/application/MemberService.java
@@ -119,7 +119,7 @@ public String updateMemberInfo(String memberId, MemberUpdateRequestDto requestDt
         updateMember(requestDto, member);
         validationService.checkValid(member);
         memberRepository.save(member);
-        eventPublisher.publishEvent(new MemberUpdatedEvent(this, member));
+        eventPublisher.publishEvent(new MemberUpdatedEvent(this, member.getId()));
         return member.getId();
     }
 
@@ -143,7 +143,7 @@ public String verifyResetMemberPassword(VerificationRequestDto requestDto) {
     public String deleteMember(String memberId) {
         Member member = memberLookupService.getMemberByIdOrThrow(memberId);
         memberRepository.delete(member);
-        eventPublisher.publishEvent(new MemberDeletedEvent(this, member));
+        eventPublisher.publishEvent(new MemberDeletedEvent(this, member.getId()));
         return member.getId();
     }
 
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
index 692247e07..c9d40fa5e 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberDeletedEvent.java
@@ -2,16 +2,15 @@
 
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
-import page.clab.api.domain.member.domain.Member;
 
 @Getter
 public class MemberDeletedEvent extends ApplicationEvent {
 
-    private final Member member;
+    private final String memberId;
 
-    public MemberDeletedEvent(Object source, Member member) {
+    public MemberDeletedEvent(Object source, String memberId) {
         super(source);
-        this.member = member;
+        this.memberId = memberId;
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
index 4081107c0..76746915d 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventDispatcher.java
@@ -14,11 +14,11 @@ public class MemberEventDispatcher {
 
     @EventListener
     public void handleMemberDeletedEvent(MemberDeletedEvent event) {
-        processors.forEach(processor -> processor.processMemberDeleted(event.getMember()));
+        processors.forEach(processor -> processor.processMemberDeleted(event.getMemberId()));
     }
 
     @EventListener
     public void handleMemberUpdatedEvent(MemberUpdatedEvent event) {
-        processors.forEach(processor -> processor.processMemberUpdated(event.getMember()));
+        processors.forEach(processor -> processor.processMemberUpdated(event.getMemberId()));
     }
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
index 0bcb8fc4f..f3d34ca62 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberEventProcessor.java
@@ -1,11 +1,9 @@
 package page.clab.api.domain.member.event;
 
-import page.clab.api.domain.member.domain.Member;
-
 public interface MemberEventProcessor {
 
-    void processMemberDeleted(Member member);
+    void processMemberDeleted(String memberId);
 
-    void processMemberUpdated(Member member);
+    void processMemberUpdated(String memberId);
 
 }
diff --git a/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
index 37efe8d1b..b1bc7b142 100644
--- a/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
+++ b/src/main/java/page/clab/api/domain/member/event/MemberUpdatedEvent.java
@@ -2,16 +2,15 @@
 
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
-import page.clab.api.domain.member.domain.Member;
 
 @Getter
 public class MemberUpdatedEvent extends ApplicationEvent {
 
-    private final Member member;
+    private final String memberId;
 
-    public MemberUpdatedEvent(Object source, Member member) {
+    public MemberUpdatedEvent(Object source, String memberId) {
         super(source);
-        this.member = member;
+        this.memberId = memberId;
     }
 
 }
diff --git a/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
index 48f5c5146..71b686b20 100644
--- a/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/notification/application/NotificationEventProcessor.java
@@ -4,7 +4,6 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 import page.clab.api.domain.notification.dao.NotificationRepository;
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Notification> notifications = notificationRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<Notification> notifications = notificationRepository.findByMemberId(memberId);
         notifications.forEach(Notification::delete);
         notificationRepository.saveAll(notifications);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 
diff --git a/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
index 0754debd9..d4fc9c8ef 100644
--- a/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/position/application/PositionEventProcessor.java
@@ -4,7 +4,6 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 import page.clab.api.domain.position.dao.PositionRepository;
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<Position> positions = positionRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<Position> positions = positionRepository.findByMemberId(memberId);
         positions.forEach(Position::delete);
         positionRepository.saveAll(positions);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 
diff --git a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
index 4741e575c..08f2dc31c 100644
--- a/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
+++ b/src/main/java/page/clab/api/domain/workExperience/application/WorkExperienceEventProcessor.java
@@ -4,7 +4,6 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import page.clab.api.domain.member.domain.Member;
 import page.clab.api.domain.member.event.MemberEventProcessor;
 import page.clab.api.domain.member.event.MemberEventProcessorRegistry;
 import page.clab.api.domain.workExperience.dao.WorkExperienceRepository;
@@ -27,14 +26,14 @@ public void init() {
 
     @Override
     @Transactional
-    public void processMemberDeleted(Member member) {
-        List<WorkExperience> workExperiences = workExperienceRepository.findByMemberId(member.getId());
+    public void processMemberDeleted(String memberId) {
+        List<WorkExperience> workExperiences = workExperienceRepository.findByMemberId(memberId);
         workExperiences.forEach(WorkExperience::delete);
         workExperienceRepository.saveAll(workExperiences);
     }
 
     @Override
-    public void processMemberUpdated(Member member) {
+    public void processMemberUpdated(String memberId) {
         // do nothing
     }
 }