Repository: saero-jinro/saerojinro-server
Pull request #125: feature/#123 강연자 조회시 현재 강의 리스트 반환
Author: login: fbehddn
Labels: name: feature
description: New feature
Created: 2025-03-17T04:53:50Z | Merged: 2025-03-18T06:30:18Z

## ⚡️ 관련 이슈
> 관련 있는 이슈를 태그해주세요.
- #123 

## 📍주요 변경 사항
> 주요 변경 사항에 대해 작성해주세요.

- 강의 조회 응답 필드 변경
- 강연자 id로 강의 조회 api 구현

## 🗣To Reviewer
> 의논이나 고려해야하는 내용을 작성해 주세요.



Diff:
diff --git a/saerojinro-admin/src/main/java/goorm/saerojinro/admin/api/lecture/presentation/request/LectureCreateRequest.java b/saerojinro-admin/src/main/java/goorm/saerojinro/admin/api/lecture/presentation/request/LectureCreateRequest.java
index 875999cb..7a93c72e 100644
--- a/saerojinro-admin/src/main/java/goorm/saerojinro/admin/api/lecture/presentation/request/LectureCreateRequest.java
+++ b/saerojinro-admin/src/main/java/goorm/saerojinro/admin/api/lecture/presentation/request/LectureCreateRequest.java
@@ -12,18 +12,12 @@
 
 @Builder
 public record LectureCreateRequest(
-	@Schema(description = "강의명", example = "공간지능 혁신을 통한 온오프라인 통합 경험의 미래", requiredMode = REQUIRED)
+	@Schema(description = "강의명", example = "AI와 미래 업무: 생성형 AI가 바꾸는 기업 혁신", requiredMode = REQUIRED)
 	@NotNull String title,
 
 	@Schema(description = "강의 내용", example = "Part 1: 온오프라인 경험을 연결하는 네이버 지도가 공간지능과 만나 제공하게 될 미래 모습을 소개합니다.", requiredMode = REQUIRED)
 	@NotNull String contents,
 
-	@Schema(description = "강의 썸네일 파일 ID", example = "1", requiredMode = REQUIRED)
-	@NotNull Long thumbnailId,
-
-	@Schema(description = "강의 자료 파일 ID", example = "2", requiredMode = REQUIRED)
-	@NotNull Long materialId,
-
 	@Schema(description = "강의 수용 인원", example = "100", requiredMode = REQUIRED)
 	@NotNull @Positive Long maxCapacity,
 
@@ -33,19 +27,19 @@ public record LectureCreateRequest(
 	@Schema(description = "강의 종료 시간", example = "2025-03-01T12:00:00", requiredMode = REQUIRED)
 	@NotNull LocalDateTime endTime,
 
-	@Schema(description = "강의 장소", example = "온라인", requiredMode = REQUIRED)
+	@Schema(description = "강의 장소", example = "Room A-1", requiredMode = REQUIRED)
 	@NotNull String location,
 
 	@Schema(description = "강의 카테고리", example = "BACKEND", requiredMode = REQUIRED)
 	@NotNull Category category,
 
-	@Schema(description = "강연자 이름", example = "Cole Palmer", requiredMode = REQUIRED)
+	@Schema(description = "강연자 이름", example = "박민수", requiredMode = REQUIRED)
 	@NotNull String speakerName,
 
-	@Schema(description = "강연자 이메일", example = "google@gmail.com", requiredMode = REQUIRED)
+	@Schema(description = "강연자 이메일", example = "mspark@gmail.com", requiredMode = REQUIRED)
 	@NotNull String speakerEmail,
 
-	@Schema(description = "강연자 회사, 직급", example = "블라블라 스타트업 CEO", requiredMode = REQUIRED)
+	@Schema(description = "강연자 회사, 직급", example = "OO 스타트업 CEO", requiredMode = REQUIRED)
 	@NotNull String speakerPosition,
 
 	@Schema(description = "한 줄 소개", example = "안녕하세요 OO 기업에서 OO를 담당하는 OOO 입니다.", requiredMode = REQUIRED)
@@ -54,6 +48,12 @@ public record LectureCreateRequest(
 	@Schema(description = "상세 약력", example = "OO 회사 / 프로젝트 OO 담당", requiredMode = REQUIRED)
 	@NotNull String speakerFilmography,
 
+	@Schema(description = "강의 자료 파일 ID", example = "1", requiredMode = REQUIRED)
+	@NotNull Long materialId,
+
+	@Schema(description = "강의 썸네일 파일 ID", example = "2", requiredMode = REQUIRED)
+	@NotNull Long thumbnailId,
+
 	@Schema(description = "강연자 사진 파일 ID", example = "3", requiredMode = REQUIRED)
 	@NotNull Long speakerPhotoId
 ) {
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/application/FileFacade.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/application/FileFacade.java
index ce82b6f1..3ab04a0e 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/application/FileFacade.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/application/FileFacade.java
@@ -12,9 +12,8 @@
 public class FileFacade {
 	private final FileQueryService fileQueryService;
 
-	public FileResource getFileResource(String uri) throws IOException {
-		File file = fileQueryService.getFileByUri(uri);
-
+	public FileResource getFileResource(Long id) throws IOException {
+		File file = fileQueryService.getFileById(id);
 		return FileResource.from(file);
 	}
 }
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileController.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileController.java
index 1e3d027b..7c167864 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileController.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileController.java
@@ -6,7 +6,7 @@
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.core.io.Resource;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.PathVariable;
 
 @Tag(name = "File", description = "파일 API")
 public interface FileController {
@@ -21,5 +21,5 @@ public interface FileController {
 			)
 		}
 	)
-	ResponseEntity<Resource> downloadLectureFile(@RequestParam(value = "materialUri") String uri);
+	ResponseEntity<Resource> downloadLectureFile(@PathVariable Long id);
 }
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileControllerImpl.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileControllerImpl.java
index 6550d9ea..cf016e26 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileControllerImpl.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/file/presentation/FileControllerImpl.java
@@ -10,8 +10,8 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
 
 import java.io.IOException;
 
@@ -22,10 +22,10 @@ public class FileControllerImpl implements FileController {
 	private final FileFacade fileFacade;
 
 	@Override
-	@GetMapping("/download")
-	public ResponseEntity<Resource> downloadLectureFile(@RequestParam(value = "materialUri") String uri) {
+	@GetMapping("/download/{id}")
+	public ResponseEntity<Resource> downloadLectureFile(@PathVariable Long id) {
 		try {
-			FileResource fileResource = fileFacade.getFileResource(uri);
+			FileResource fileResource = fileFacade.getFileResource(id);
 			File file = fileResource.file();
 			Resource resource = fileResource.resource();
 
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureDetailResponse.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureDetailResponse.java
index 8e45cba2..d288016b 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureDetailResponse.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureDetailResponse.java
@@ -11,40 +11,54 @@
 
 @Builder
 public record LectureDetailResponse(
+	@Schema(description = "강의 시작 시간", example = "2025-03-01T10:00:00", requiredMode = REQUIRED)
+	LocalDateTime startTime,
+
+	@Schema(description = "강의 종료 시간", example = "2025-03-01T12:00:00", requiredMode = REQUIRED)
+	LocalDateTime endTime,
+
+	@Schema(description = "강의 장소", example = "온라인", requiredMode = REQUIRED)
+	String location,
+
 	@Schema(description = "강의명", example = "공간지능 혁신을 통한 온오프라인 통합 경험의 미래", requiredMode = REQUIRED)
 	String title,
 
-	@Schema(description = "강의 내용", example = "Part 1: 온오프라인 경험을 연결하는 네이버 지도가 공간지능과 만나 제공하게 될 미래 모습을 소개합니다.", requiredMode = REQUIRED)
+	@Schema(description = "강의 카테고리", example = "BACKEND", requiredMode = REQUIRED)
+	Category category,
+
+	@Schema(description = "강의 내용", example = "이 강의는 ~ 만들 수 있습니다", requiredMode = REQUIRED)
 	String contents,
 
-	@Schema(description = "강의 자료 파일 ID", example = "3")
+	@Schema(description = "강의 자료 파일 ID", example = "3", requiredMode = REQUIRED)
 	Long materialsId,
 
-	@Schema(description = "강의 카테고리", example = "BACKEND", requiredMode = REQUIRED)
-	Category category,
+	@Schema(description = "강연자 이름", example = "박민수", requiredMode = REQUIRED)
+	String speakerName,
 
-	@Schema(description = "강의 시작 시간", example = "2025-03-01T10:00:00", requiredMode = REQUIRED)
-	LocalDateTime startTime,
+	@Schema(description = "강연자 이메일", example = "google@google.com", requiredMode = REQUIRED)
+	String speakerEmail,
 
-	@Schema(description = "강의 종료 시간", example = "2025-03-01T12:00:00", requiredMode = REQUIRED)
-	LocalDateTime endTime,
+	@Schema(description = "강연자 소개", example = "안녕하세요 박민수입니다", requiredMode = REQUIRED)
+	String introduction,
+
+	@Schema(description = "강연자 사진 url", example = "bucketUrl/uploads/{lectureId}/speaker", requiredMode = REQUIRED)
+	String speakerPhotoUrl
 
-	@Schema(description = "강의 장소", example = "온라인", requiredMode = REQUIRED)
-	String location,
 
-	@Schema(description = "강연자 ID", example = "1L", requiredMode = REQUIRED)
-	Long speakerId
 ) {
 	public static LectureDetailResponse from(Lecture lecture) {
 		return LectureDetailResponse.builder()
-			.title(lecture.getTitle())
-			.contents(lecture.getContents())
-			.materialsId(lecture.getMaterialFile().getId())
-			.category(lecture.getCategory())
 			.startTime(lecture.getStartTime())
 			.endTime(lecture.getEndTime())
 			.location(lecture.getLocation())
-			.speakerId(lecture.getSpeaker().getId())
+			.title(lecture.getTitle())
+			.category(lecture.getCategory())
+			.contents(lecture.getContents())
+			.materialsId(lecture.getMaterialFile().getId())
+			.speakerName(lecture.getSpeaker().getName())
+			.speakerEmail(lecture.getSpeaker().getEmail())
+			.introduction(lecture.getSpeaker().getIntroduction())
+			.speakerPhotoUrl(lecture.getSpeaker().getImageFile().getPhysicalPath())
 			.build();
 	}
 }
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureResponse.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureResponse.java
index 8560924a..540776ee 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureResponse.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureResponse.java
@@ -11,14 +11,11 @@
 
 @Builder
 public record LectureResponse(
-	@Schema(description = "강의 고유 ID", example = "1", requiredMode = REQUIRED)
+	@Schema(description = "강의 ID", example = "1", requiredMode = REQUIRED)
 	Long id,
 
-	@Schema(description = "강의명", example = "공간지능 혁신을 통한 온오프라인 통합 경험의 미래", requiredMode = REQUIRED)
-	String title,
-
-	@Schema(description = "강의 카테고리", example = "BACKEND", requiredMode = REQUIRED)
-	Category category,
+	@Schema(description = "강의 썸네일 Uri", example = "bucketUrl/uploads/{lectureId}/thumbnail/123456789.jpg", requiredMode = REQUIRED)
+	String thumbnailUri,
 
 	@Schema(description = "강의 시작 시간", example = "2025-03-01T10:00:00", requiredMode = REQUIRED)
 	LocalDateTime startTime,
@@ -26,21 +23,28 @@ public record LectureResponse(
 	@Schema(description = "강의 종료 시간", example = "2025-03-01T12:00:00", requiredMode = REQUIRED)
 	LocalDateTime endTime,
 
-	@Schema(description = "강연자 이름", example = "Cole Palmer", requiredMode = REQUIRED)
+	@Schema(description = "강의명", example = "AI와 미래 업무: 생성형 AI가 바꾸는 기업 혁신", requiredMode = REQUIRED)
+	String title,
+
+	@Schema(description = "강의실 위치", example = "ROOM A-1", requiredMode = REQUIRED)
+	String location,
+
+	@Schema(description = "강연자 이름", example = "박민수", requiredMode = REQUIRED)
 	String speakerName,
 
-	@Schema(description = "강연자 사진", example = "local/file_0000000.jpg", requiredMode = REQUIRED)
-	String speakerImageUri
+	@Schema(description = "카테고리", example = "BACKEND", requiredMode = REQUIRED)
+	Category category
 ) {
 	public static LectureResponse from(Lecture lecture) {
 		return LectureResponse.builder()
 			.id(lecture.getId())
-			.title(lecture.getTitle())
-			.category(lecture.getCategory())
+			.thumbnailUri(lecture.getThumbnailFile().getPhysicalPath())
 			.startTime(lecture.getStartTime())
 			.endTime(lecture.getEndTime())
+			.title(lecture.getTitle())
+			.location(lecture.getLocation())
 			.speakerName(lecture.getSpeaker().getName())
-			.speakerImageUri(lecture.getSpeaker().getImageFile().getPhysicalPath())
+			.category(lecture.getCategory())
 			.build();
 	}
 }
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureSummaryResponse.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureSummaryResponse.java
index 93be3ac0..fbfde029 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureSummaryResponse.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/lecture/presentation/response/LectureSummaryResponse.java
@@ -14,7 +14,7 @@ public record LectureSummaryResponse(
 	@Schema(description = "카테고리", example = "BACKEND", requiredMode = REQUIRED)
 	Category category,
 
-	@Schema(description = "강의 썸네일 Uri", example = "uploads/lecture/thumbnail/123456789.jpg", requiredMode = REQUIRED)
+	@Schema(description = "강의 썸네일 Uri", example = "bucketUrl/uploads/{lectureId}/thumbnail/123456789.jpg", requiredMode = REQUIRED)
 	String thumbnailUri,
 
 	@Schema(description = "강의명", example = "공간지능 혁신을 통한 온오프라인 통합 경험의 미래", requiredMode = REQUIRED)
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerController.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerController.java
deleted file mode 100644
index 30ecd29d..00000000
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package goorm.saerojinro.api.speaker.presentation;
-
-import goorm.saerojinro.api.speaker.presentation.response.SpeakerDetailResponse;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.Schema;
-import io.swagger.v3.oas.annotations.responses.ApiResponse;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PathVariable;
-
-@Tag(name = "Speaker", description = "강연자 API")
-public interface SpeakerController {
-	@Operation(
-		summary = "강연자 세부조회",
-		description = "강연자의 세부 정보를 조회합니다",
-		responses = {
-			@ApiResponse(
-				responseCode = "200",
-				content = @Content(schema = @Schema(implementation = SpeakerDetailResponse.class))
-			)
-		}
-	)
-	ResponseEntity<SpeakerDetailResponse> findById(@PathVariable Long speakerId);
-}
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerControllerImpl.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerControllerImpl.java
index 85415f0a..e69de29b 100644
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerControllerImpl.java
+++ b/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/SpeakerControllerImpl.java
@@ -1,22 +0,0 @@
-package goorm.saerojinro.api.speaker.presentation;
-
-import goorm.saerojinro.api.speaker.presentation.response.SpeakerDetailResponse;
-import goorm.saerojinro.domain.speaker.application.SpeakerQueryService;
-import goorm.saerojinro.domain.speaker.domain.Speaker;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-@RestController
-@RequestMapping("/api/speakers")
-@RequiredArgsConstructor
-public class SpeakerControllerImpl implements SpeakerController {
-	private final SpeakerQueryService speakerQueryService;
-
-	@GetMapping("/{id}")
-	public ResponseEntity<SpeakerDetailResponse> findById(@PathVariable(value = "id") Long speakerId) {
-		Speaker speaker = speakerQueryService.findById(speakerId);
-
-		return ResponseEntity.ok(SpeakerDetailResponse.from(speaker));
-	}
-}
diff --git a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/response/SpeakerDetailResponse.java b/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/response/SpeakerDetailResponse.java
deleted file mode 100644
index 25e22250..00000000
--- a/saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/presentation/response/SpeakerDetailResponse.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package goorm.saerojinro.api.speaker.presentation.response;
-
-import goorm.saerojinro.domain.speaker.domain.Speaker;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Builder;
-
-import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
-
-@Builder
-public record SpeakerDetailResponse(
-	@Schema(description = "강연자 사진", example = "1", requiredMode = REQUIRED)
-	@NotNull Long speakerPhotoId,
-
-	@Schema(description = "강연자 이름", example = "Cole Palmer", requiredMode = REQUIRED)
-	@NotNull String name,
-
-	@Schema(description = "강연자 이메일", example = "google@gmail.com", requiredMode = REQUIRED)
-	@NotNull String email,
-
-	@Schema(description = "한 줄 소개", example = "안녕하세요 OO 기업에서 OO를 담당하는 OOO 입니다.", requiredMode = REQUIRED)
-	@NotNull String introduction,
-
-	@Schema(description = "강연자 회사, 직급", example = "블라블라 스타트업 CEO", requiredMode = REQUIRED)
-	@NotNull String position,
-
-	@Schema(description = "주요 경력", example = "OO 회사 / 프로젝트 OO 담당", requiredMode = REQUIRED)
-	@NotNull String filmography
-
-) {
-	public static SpeakerDetailResponse from(Speaker speaker) {
-		return SpeakerDetailResponse.builder()
-			.speakerPhotoId(speaker.getImageFile().getId())
-			.name(speaker.getName())
-			.email(speaker.getEmail())
-			.introduction(speaker.getIntroduction())
-			.position(speaker.getPosition())
-			.filmography(speaker.getFilmography())
-			.build();
-	}
-}
diff --git a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/lecture/application/LectureQueryService.java b/saerojinro-domain/src/main/java/goorm/saerojinro/domain/lecture/application/LectureQueryService.java
index 427a4a9d..9cc69b69 100644
--- a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/lecture/application/LectureQueryService.java
+++ b/saerojinro-domain/src/main/java/goorm/saerojinro/domain/lecture/application/LectureQueryService.java
@@ -56,5 +56,4 @@ public List<Lecture> getRecommendedLectureByDate(Map<Category, Integer> category
 			.sorted(Comparator.comparing(lecture -> categoryPriorityMap.getOrDefault(lecture.getCategory(), Integer.MAX_VALUE)))
 			.toList();
 	}
-
 }
diff --git a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/application/SpeakerQueryService.java b/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/application/SpeakerQueryService.java
deleted file mode 100644
index 3107c208..00000000
--- a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/application/SpeakerQueryService.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package goorm.saerojinro.domain.speaker.application;
-
-import goorm.saerojinro.domain.speaker.domain.Speaker;
-import goorm.saerojinro.domain.speaker.domain.SpeakerRepository;
-import goorm.saerojinro.domain.speaker.exception.SpeakerNotFoundException;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-@Service
-@RequiredArgsConstructor
-public class SpeakerQueryService {
-	private final SpeakerRepository speakerRepository;
-
-	@Transactional(readOnly = true)
-	public Speaker findById(Long speakerId) {
-		return speakerRepository.findById(speakerId).orElseThrow(SpeakerNotFoundException::new);
-	}
-}
diff --git a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java b/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java
index 07e7ad07..8bdff808 100644
--- a/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java
+++ b/saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java
@@ -2,6 +2,7 @@
 
 import goorm.saerojinro.common.domain.BaseTimeEntity;
 import goorm.saerojinro.domain.file.domain.File;
+import goorm.saerojinro.domain.lecture.domain.Lecture;
 import jakarta.persistence.*;
 import lombok.*;
 
@@ -35,6 +36,9 @@ public class Speaker extends BaseTimeEntity {
 	@JoinColumn(name = "image_file_id", nullable = false)
 	private File imageFile;
 
+	@OneToOne(mappedBy = "speaker")
+	private Lecture lecture;
+
 	public static Speaker create(String name,String email, String position, String introduction, String filmography, File image) {
 		return Speaker.builder()
 			.name(name)
diff --git a/saerojinro-domain/src/testFixtures/java/lecture/application/LectureQueryServiceTest.java b/saerojinro-domain/src/testFixtures/java/lecture/application/LectureQueryServiceTest.java
index e57afac2..8ed7f31c 100644
--- a/saerojinro-domain/src/testFixtures/java/lecture/application/LectureQueryServiceTest.java
+++ b/saerojinro-domain/src/testFixtures/java/lecture/application/LectureQueryServiceTest.java
@@ -66,23 +66,23 @@ void setUp() {
 		FakeLectureRepository fakeLectureRepository = new FakeLectureRepository();
 		lectureQueryService = new LectureQueryService(fakeLectureRepository);
 
-		Speaker SPEAKER = Speaker.create(
-			NAME, EMAIL, POSITION, INTRODUCTION, FILMOGRAPHY, SPEAKER_IMAGE_FILE
-		);
+		Speaker speaker = Speaker.builder()
+			.id(1L)
+			.name(NAME)
+			.email(EMAIL)
+			.build();
 
 		lecture1 = fakeLectureRepository.save(Lecture.create(
-				SPEAKER, TITLE, CONTENTS, THUMBNAIL_FILE, MATERIAL_FILE, MAX_CAPACITY,
+			speaker, TITLE, CONTENTS, THUMBNAIL_FILE, MATERIAL_FILE, MAX_CAPACITY,
 				START_TIME, END_TIME, LOCATION, CATEGORY
 			)
 		);
 
 		lecture2 = fakeLectureRepository.save(Lecture.create(
-				SPEAKER, TITLE + 2, CONTENTS, THUMBNAIL_FILE, MATERIAL_FILE, MAX_CAPACITY,
+			speaker, TITLE + 2, CONTENTS, THUMBNAIL_FILE, MATERIAL_FILE, MAX_CAPACITY,
 				START_TIME, END_TIME, LOCATION, CATEGORY
 			)
 		);
-
-		fakeLectureRepository.save(lecture1);
 	}
 
 	@Test
@@ -92,7 +92,7 @@ void getAllLecture_success() {
 		List<Lecture> lectures = lectureQueryService.getAll();
 
 		// then
-		assertEquals(3, lectures.size());
+		assertEquals(2, lectures.size());
 		assertEquals(TITLE, lectures.get(0).getTitle());
 		assertEquals(TITLE + 2, lectures.get(1).getTitle());
 	}
@@ -157,7 +157,7 @@ void getByStartTime_Success() {
 
 		// then
 		assertNotNull(lectureList);
-		assertEquals(3, lectureList.size());
+		assertEquals(2, lectureList.size());
 		assertEquals(START_TIME, lectureList.get(0).getStartTime());
 	}
 
@@ -171,7 +171,7 @@ void getAllLectureBetween_Success() {
 
 		// then
 		assertNotNull(lectureList);
-		assertEquals(3, lectureList.size());
+		assertEquals(2, lectureList.size());
 		assertEquals(START_TIME, lectureList.get(0).getStartTime());
 	}
 
diff --git a/saerojinro-domain/src/testFixtures/java/mock/repository/FakeLectureRepository.java b/saerojinro-domain/src/testFixtures/java/mock/repository/FakeLectureRepository.java
index a778aa39..15caadf0 100644
--- a/saerojinro-domain/src/testFixtures/java/mock/repository/FakeLectureRepository.java
+++ b/saerojinro-domain/src/testFixtures/java/mock/repository/FakeLectureRepository.java
@@ -44,8 +44,8 @@ public Optional<Lecture> findById(Long id) {
 	@Override
 	public Optional<Lecture> findByIdWithLock(Long id) {
 		return data.stream()
-				.filter(lecture -> lecture.getId().equals(id))
-				.findFirst();
+			.filter(lecture -> lecture.getId().equals(id))
+			.findFirst();
 	}
 
 	@Override
@@ -59,7 +59,7 @@ public List<Lecture> findByStartTimeBetween(LocalDateTime start, LocalDateTime e
 			.filter(lecture -> {
 				LocalDateTime lectureTime = lecture.getStartTime();
 				boolean isAfterOrEqualStart = !lectureTime.isBefore(start);
-				boolean isBeforeEnd         = lectureTime.isBefore(end);
+				boolean isBeforeEnd = lectureTime.isBefore(end);
 				return isAfterOrEqualStart && isBeforeEnd;
 			})
 			.toList();
diff --git a/saerojinro-domain/src/testFixtures/java/mock/repository/FakeSpeakerRepository.java b/saerojinro-domain/src/testFixtures/java/mock/repository/FakeSpeakerRepository.java
index c74bf6f5..c296735e 100644
--- a/saerojinro-domain/src/testFixtures/java/mock/repository/FakeSpeakerRepository.java
+++ b/saerojinro-domain/src/testFixtures/java/mock/repository/FakeSpeakerRepository.java
@@ -11,7 +11,7 @@
 
 public class FakeSpeakerRepository implements SpeakerRepository {
 	private final List<Speaker> data = Collections.synchronizedList(new ArrayList<>());
-	private final AtomicLong sequence = new AtomicLong(0);
+	private final AtomicLong sequence = new AtomicLong(1);
 
 	@Override
 	public Speaker save(Speaker speaker) {
diff --git a/saerojinro-domain/src/testFixtures/java/speaker/application/SpeakerQueryServiceTest.java b/saerojinro-domain/src/testFixtures/java/speaker/application/SpeakerQueryServiceTest.java
deleted file mode 100644
index fadfeb05..00000000
--- a/saerojinro-domain/src/testFixtures/java/speaker/application/SpeakerQueryServiceTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package speaker.application;
-
-import goorm.saerojinro.domain.file.domain.File;
-import goorm.saerojinro.domain.speaker.application.SpeakerQueryService;
-import goorm.saerojinro.domain.speaker.domain.Speaker;
-import goorm.saerojinro.domain.speaker.domain.SpeakerRepository;
-import mock.repository.FakeSpeakerRepository;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-public class SpeakerQueryServiceTest {
-	private SpeakerQueryService speakerQueryService;
-	private SpeakerRepository speakerRepository;
-
-	private static final Long ID = 1L;
-	private static final String NAME = "Cole palmer";
-	private static final String EMAIL = "google@mail.com";
-	private static final String POSITION = "00 기업 / CEO";
-	private static final String INTRODUCTION = "안녕하세요 OO 기업 CEO OOO 입니다";
-	private static final String FILMOGRAPHY = "AA 기업 - 백엔드 개발 담당";
-
-	private static final String LOGICAL_NAME = "Speaker_Image";
-	private static final String PHYSICAL_PATH = "uploads/speaker/123456.jpg";
-	private static final Long FILE_SIZE = 3000L;
-	private static final String EXTENSION = "jpg";
-
-	private static final File SPEAKER_IMAGE_FILE = File.create(
-		LOGICAL_NAME,
-		PHYSICAL_PATH,
-		FILE_SIZE,
-		EXTENSION
-	);
-
-	@BeforeEach
-	void setUp() {
-		speakerRepository = new FakeSpeakerRepository();
-		speakerQueryService = new SpeakerQueryService(speakerRepository);
-	}
-
-	@Test
-	@DisplayName("정상적으로 강연자를 조회한다")
-	void findSpeaker_success() {
-		// given
-		Speaker speaker = Speaker.create(NAME, EMAIL, POSITION, INTRODUCTION, FILMOGRAPHY, SPEAKER_IMAGE_FILE);
-		speakerRepository.save(speaker);
-
-		// when
-		Speaker createdSpeaker = speakerQueryService.findById(0L);
-
-		// then
-		assertNotNull(createdSpeaker);
-		assertEquals(EMAIL, createdSpeaker.getEmail());
-		assertEquals(POSITION, createdSpeaker.getPosition());
-		assertEquals(INTRODUCTION, createdSpeaker.getIntroduction());
-		assertEquals(FILMOGRAPHY, createdSpeaker.getFilmography());
-
-		assertNotNull(createdSpeaker.getImageFile());
-		assertEquals(PHYSICAL_PATH, createdSpeaker.getImageFile().getPhysicalPath());
-	}
-}
diff --git a/saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/jpa/LectureJpaRepository.java b/saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/jpa/LectureJpaRepository.java
index c0fe841d..1705955a 100644
--- a/saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/jpa/LectureJpaRepository.java
+++ b/saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/jpa/LectureJpaRepository.java
@@ -30,4 +30,7 @@ public interface LectureJpaRepository extends JpaRepository<Lecture, Long> {
 	List<Lecture> findByStartTimeGreaterThanEqualAndEndTimeLessThanEqual(LocalDateTime startTime, LocalDateTime endTime);
 
 	List<Lecture> findByCategoryInAndStartTime(List<Category> categories, LocalDateTime lectureTime);
+
+	@Query("SELECT l FROM Lecture l JOIN FETCH l.speaker s WHERE s.id = :speakerId")
+	Optional<Lecture> findBySpeakerId(@Param("speakerId") Long speakerId);
 }


Reviews:
user: login: LeeShinHaeng
body: 수고 많으셨어요!
코멘트 하나 남겼으니 확인 부탁드려요
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T05:15:00Z
user: login: fbehddn
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T05:15:42Z
user: login: minjo-on
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T05:17:55Z
user: login: fbehddn
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T06:04:33Z
user: login: minjo-on
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T06:25:42Z
user: login: LeeShinHaeng
body: LGTM! 
자주 바뀌는 요구사항에 고생이 많으십니다 👍
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T00:11:59Z
user: login: LeeShinHaeng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T00:20:00Z
user: login: fbehddn
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-18T06:32:02Z

Review comments:
diff_hunk: @@ -0,0 +1,27 @@
+package goorm.saerojinro.api.speaker.application;
+
+import goorm.saerojinro.api.speaker.presentation.response.SpeakerDetailResponse;
+import goorm.saerojinro.domain.lecture.application.LectureQueryService;
+import goorm.saerojinro.domain.lecture.domain.Lecture;
+import goorm.saerojinro.domain.speaker.application.SpeakerQueryService;
+import goorm.saerojinro.domain.speaker.domain.Speaker;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@RequiredArgsConstructor
+public class SpeakerFacade {
+	private final SpeakerQueryService speakerQueryService;
+	private final LectureQueryService lectureQueryService;
+
+	@Transactional(readOnly = true)
+	public SpeakerDetailResponse findById(Long speakerId) {
+		Speaker speaker = speakerQueryService.findById(speakerId);
+		Lecture lecture = lectureQueryService.getBySpeakerId(speaker.getId());
path: saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/application/SpeakerFacade.java
user: login: LeeShinHaeng
body: ```Lecture lecture = lectureQueryService.getBySpeakerId(speakerId);```
위 처럼 바로 speakerId를 사용하고, findById 과정은 생략되어도 될것 같아요!
created_at: 2025-03-17T05:13:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+package goorm.saerojinro.api.speaker.application;
+
+import goorm.saerojinro.api.speaker.presentation.response.SpeakerDetailResponse;
+import goorm.saerojinro.domain.lecture.application.LectureQueryService;
+import goorm.saerojinro.domain.lecture.domain.Lecture;
+import goorm.saerojinro.domain.speaker.application.SpeakerQueryService;
+import goorm.saerojinro.domain.speaker.domain.Speaker;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@RequiredArgsConstructor
+public class SpeakerFacade {
+	private final SpeakerQueryService speakerQueryService;
+	private final LectureQueryService lectureQueryService;
+
+	@Transactional(readOnly = true)
+	public SpeakerDetailResponse findById(Long speakerId) {
+		Speaker speaker = speakerQueryService.findById(speakerId);
+		Lecture lecture = lectureQueryService.getBySpeakerId(speaker.getId());
path: saerojinro-api/src/main/java/goorm/saerojinro/api/speaker/application/SpeakerFacade.java
user: login: fbehddn
body: 엥 그러네요 
created_at: 2025-03-17T05:15:42Z
author_association: COLLABORATOR
diff_hunk: @@ -60,4 +60,9 @@ public List<Lecture> findByCategoryInAndStartTime(List<Category> categories, Loc
 	public List<Lecture> findAll() {
 		return lectureJpaRepository.findAll();
 	}
+
+	@Override
+	public Optional<Lecture> findBySpeakerId(Long speakerId) {
+		return lectureJpaRepository.findBySpeakerId(speakerId);
+	}
path: saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/impl/LectureRepositoryImpl.java
user: login: minjo-on
body: 이부분도 지워도 될 것 같아요~
created_at: 2025-03-17T05:17:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,4 +60,9 @@ public List<Lecture> findByCategoryInAndStartTime(List<Category> categories, Loc
 	public List<Lecture> findAll() {
 		return lectureJpaRepository.findAll();
 	}
+
+	@Override
+	public Optional<Lecture> findBySpeakerId(Long speakerId) {
+		return lectureJpaRepository.findBySpeakerId(speakerId);
+	}
path: saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/impl/LectureRepositoryImpl.java
user: login: fbehddn
body: `Lecture lecture = lectureQueryService.getBySpeakerId(speakerId);`
현재 speakerFacade 에서 speakerId로 강의 조회시 사용중인데, 다른 방법이 있는지 궁금해요 !
created_at: 2025-03-17T06:04:33Z
author_association: COLLABORATOR
diff_hunk: @@ -60,4 +60,9 @@ public List<Lecture> findByCategoryInAndStartTime(List<Category> categories, Loc
 	public List<Lecture> findAll() {
 		return lectureJpaRepository.findAll();
 	}
+
+	@Override
+	public Optional<Lecture> findBySpeakerId(Long speakerId) {
+		return lectureJpaRepository.findBySpeakerId(speakerId);
+	}
path: saerojinro-infra/src/main/java/goorm/saerojinro/infra/repository/impl/LectureRepositoryImpl.java
user: login: minjo-on
body: 이 부분은 양방향 매핑을 설정하면 될 것 같아요 !
created_at: 2025-03-17T06:25:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,6 +36,9 @@ public class Speaker extends BaseTimeEntity {
 	@JoinColumn(name = "image_file_id", nullable = false)
 	private File imageFile;
 
+	@OneToOne(mappedBy = "speaker")
path: saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java
user: login: LeeShinHaeng
body: ```
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "lecture_id", nullable = false)
```
타 1대1 컬럼처럼 위와 같이 설정하지 않고 mappedBy를 사용하신 이유가 있을까요?
created_at: 2025-03-18T00:19:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,6 +36,9 @@ public class Speaker extends BaseTimeEntity {
 	@JoinColumn(name = "image_file_id", nullable = false)
 	private File imageFile;
 
+	@OneToOne(mappedBy = "speaker")
path: saerojinro-domain/src/main/java/goorm/saerojinro/domain/speaker/domain/Speaker.java
user: login: fbehddn
body: lecture를 연관관계의 주인으로 설정하기 위해서 `mappedBy`를 사용했습니다 ! 
created_at: 2025-03-18T06:32:01Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-17T04:55:33Z
body: ### Test Coverage Report
|Overall Project|96.36%|:green_apple:|
|:-|:-|:-:|

|Module|Coverage||
|:-|:-|:-:|
|saerojinro-domain|99.77%|:green_apple:|

<details>
<summary>Files</summary>

|Module|File|Coverage||
|:-|:-|:-|:-:|
|saerojinro-domain|[Speaker.java](https://github.com/saero-jinro/saerojinro-server/blob/def2a1d9d6018e8cf2f00cbace9e401a029fe345/saerojinro-domain%2Fsrc%2Fmain%2Fjava%2Fgoorm%2Fsaerojinro%2Fdomain%2Fspeaker%2Fdomain%2FSpeaker.java)|100%|:green_apple:|
||[LectureQueryService.java](https://github.com/saero-jinro/saerojinro-server/blob/def2a1d9d6018e8cf2f00cbace9e401a029fe345/saerojinro-domain%2Fsrc%2Fmain%2Fjava%2Fgoorm%2Fsaerojinro%2Fdomain%2Flecture%2Fapplication%2FLectureQueryService.java)|100%|:green_apple:|

</details>
author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-17T04:56:02Z
body: ## [Codecov](https://app.codecov.io/gh/saero-jinro/saerojinro-server/pull/125?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=saero-jinro) Report
All modified and coverable lines are covered by tests :white_check_mark:

:loudspeaker: Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/255)
author_association: NONE