@@ -251,69 +251,69 @@ private User getUser(UserPrincipal userPrincipal) {
251251 public Mono <ResponseEntity <ApiResponse <ProfessorPracticeListRes >>> generateAndSavePractice (
252252 UserPrincipal userPrincipal , Long noteId , CreatePracticeReq createPracticeReq ) {
253253
254- // 유저와 교수 인증 (예외 발생 시 중단)
255- validateUser (userPrincipal );
256- validateProfessor (userPrincipal );
254+ // 유저와 교수 인증 (세션 내에서 실행 → Lazy 문제 없음)
255+ User user = validateUser (userPrincipal );
256+ Professor professor = validateProfessor (userPrincipal );
257+ Note note = validateNote (noteId );
257258
258- // 비동기로 노트 조회 (블로킹 방지)
259- return getNoteAsync (noteId )
260- .flatMap (note -> {
261- String summary = note .getSummary ().getContent ();
262- int practiceSize = determinePracticeSize (createPracticeReq .getPracticeSize ());
263-
264- Mono <List <CreatePracticeRes >> practiceMono ;
265- if ("BOTH" .equalsIgnoreCase (createPracticeReq .getType ())) {
266- int oxCount = calculateOxCount (practiceSize );
267- int shortCount = practiceSize - oxCount ;
268-
269- Mono <List <CreatePracticeRes >> oxMono = quizGeneratorService .generateQuestions (summary , oxCount , "OX" , 1 );
270- Mono <List <CreatePracticeRes >> shortMono = quizGeneratorService .generateQuestions (summary , shortCount , "SHORT" , oxCount + 1 );
271- practiceMono = Mono .zip (oxMono , shortMono )
272- .map (tuple -> combineQuestions (tuple .getT1 (), tuple .getT2 ()));
273- } else {
274- practiceMono = quizGeneratorService .generateQuestions (summary , practiceSize , createPracticeReq .getType (), 1 );
275- }
276-
277- // 기존 sequence 최대값 조회 → Mono로 감싸서 flatMap 안에서 사용
278- return Mono .fromCallable (() -> practiceRepository .findMaxSequenceByNoteId (noteId ))
279- .subscribeOn (Schedulers .boundedElastic ())
280- .flatMap (lastSequence -> practiceMono .flatMap (practices -> {
281-
282- int startSequence = lastSequence + 1 ; // 기존 마지막 번호 다음부터 시작
283- List <Practice > entities = new ArrayList <>();
284-
285- for (int i = 0 ; i < practices .size (); i ++) {
286- CreatePracticeRes p = practices .get (i );
287- PracticeType type = PracticeType .valueOf (p .getPracticeType ());
288-
289- entities .add (Practice .builder ()
290- .note (note )
259+ // Lazy 객체 → 여기서 모두 안전하게 초기화
260+ Folder folder = note .getFolder ();
261+ Long professorId = folder .getProfessor ().getProfessorId ();
262+ String professorName = folder .getProfessor ().getProfessorName ();
263+ String noteTitle = note .getTitle ();
264+ Long realNoteId = note .getNoteId ();
265+
266+ String summary = note .getSummary ().getContent ();
267+ int practiceSize = determinePracticeSize (createPracticeReq .getPracticeSize ());
268+
269+ Mono <List <CreatePracticeRes >> practiceMono ;
270+ if ("BOTH" .equalsIgnoreCase (createPracticeReq .getType ())) {
271+ int oxCount = calculateOxCount (practiceSize );
272+ int shortCount = practiceSize - oxCount ;
273+
274+ Mono <List <CreatePracticeRes >> oxMono = quizGeneratorService .generateQuestions (summary , oxCount , "OX" , 1 );
275+ Mono <List <CreatePracticeRes >> shortMono = quizGeneratorService .generateQuestions (summary , shortCount , "SHORT" , oxCount + 1 );
276+ practiceMono = Mono .zip (oxMono , shortMono )
277+ .map (tuple -> combineQuestions (tuple .getT1 (), tuple .getT2 ()));
278+ } else {
279+ practiceMono = quizGeneratorService .generateQuestions (summary , practiceSize , createPracticeReq .getType (), 1 );
280+ }
281+
282+ // 비동기로 넘어가는 부분 → 엔티티 사용 x, 값만 사용 o
283+
284+ return Mono .fromCallable (() -> practiceRepository .findMaxSequenceByNoteId (noteId ))
285+ .subscribeOn (Schedulers .boundedElastic ())
286+ .flatMap (lastSequence -> practiceMono .flatMap (practices -> {
287+
288+ int startSequence = lastSequence + 1 ;
289+ List <Practice > entities = new ArrayList <>();
290+
291+ for (int i = 0 ; i < practices .size (); i ++) {
292+ CreatePracticeRes p = practices .get (i );
293+ PracticeType type = PracticeType .valueOf (p .getPracticeType ());
294+
295+ entities .add (Practice .builder ()
296+ .note (note ) // 여기까진 안전함
291297 .sequence (startSequence + i )
292298 .content (p .getContent ())
293299 .result (p .getResult ())
294300 .solution (p .getSolution ())
295301 .additionalResults (type == PracticeType .OX ? null : p .getAdditionalResults ())
296302 .practiceType (type )
297303 .build ());
298- }
304+ }
299305
300- return Mono .fromCallable (() -> {
301- practiceRepository .saveAll (entities );
302- return entities ;
306+ return Mono .fromCallable (() -> {
307+ practiceRepository .saveAll (entities );
308+ return entities . stream (). map ( this :: toResponse ). toList () ;
303309 }).subscribeOn (Schedulers .boundedElastic ())
304- .map (saved -> {
305- List <ProfessorPracticeRes > responses = saved .stream ()
306- .map (this ::toResponse )
307- .toList ();
308-
309- var folder = note .getFolder ();
310- var professor = folder .getProfessor ();
310+ .map (responses -> {
311311
312312 ProfessorPracticeListRes response = ProfessorPracticeListRes .builder ()
313- .noteId (note . getNoteId () )
314- .noteTitle (note . getTitle () )
315- .professorId (professor . getProfessorId () )
316- .professorName (professor . getProfessorName () )
313+ .noteId (realNoteId )
314+ .noteTitle (noteTitle )
315+ .professorId (professorId )
316+ .professorName (professorName )
317317 .reqList (responses )
318318 .build ();
319319
@@ -324,12 +324,9 @@ public Mono<ResponseEntity<ApiResponse<ProfessorPracticeListRes>>> generateAndSa
324324
325325 return ResponseEntity .ok (api );
326326 });
327- }));
328- });
327+ }));
329328 }
330329
331-
332-
333330 /** Practice → ProfessorPracticeRes 매핑용 */
334331 private ProfessorPracticeRes toResponse (Practice p ) {
335332 return ProfessorPracticeRes .builder ()
0 commit comments