diff --git a/src/main/java/com/example/enjoy/controller/HomeController.java b/src/main/java/com/example/enjoy/controller/HomeController.java index 25e966a..0b9d4fb 100644 --- a/src/main/java/com/example/enjoy/controller/HomeController.java +++ b/src/main/java/com/example/enjoy/controller/HomeController.java @@ -1,11 +1,8 @@ package com.example.enjoy.controller; - -import ch.qos.logback.core.model.Model; import com.example.enjoy.dto.TrackProgressDto; import com.example.enjoy.service.TrackService; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -18,17 +15,10 @@ public class HomeController { private final TrackService trackService; @GetMapping("/home") - public String showMyProgress(Model model) { // 1. Model 객체를 파라미터로 추가 - // TODO: 추후 Spring Security 등과 연동하여 실제 로그인한 사용자 ID를 가져와야 함 - Long currentStudentId = 1L; // 2. 테스트용 임시 학생 ID 사용 - - // 3. 학생의 이수 현황을 계산하는 새로운 서비스 메서드 호출 - List progressData = trackService.calculateTrackProgress(currentStudentId); - - // 4. 조회된 데이터를 "progressData"라는 이름으로 모델에 추가 - //model.addAttribute("progressData", progressData); - - // 5. 데이터를 표시할 뷰(html)의 이름을 반환 - return "home"; + public List getProgress() { + // 1. 반환 타입을 List로 변경 + Long currentStudentId = 1L; + // 2. 서비스 호출 후 데이터를 바로 반환 + return trackService.calculateTrackProgress(currentStudentId); } } diff --git a/src/main/java/com/example/enjoy/controller/TrackController.java b/src/main/java/com/example/enjoy/controller/TrackController.java index 9460444..e77fa79 100644 --- a/src/main/java/com/example/enjoy/controller/TrackController.java +++ b/src/main/java/com/example/enjoy/controller/TrackController.java @@ -1,4 +1,32 @@ package com.example.enjoy.controller; +import com.example.enjoy.dto.TrackDetailDto; +import com.example.enjoy.service.TrackService; +import lombok.RequiredArgsConstructor; +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.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/tracks") public class TrackController { + + private final TrackService trackService; + + /** + * 특정 트랙의 상세 정보를 조회하는 API + * @param trackId 조회할 트랙의 ID + * @return TrackDetailDto - 트랙의 상세 정보 + */ + @GetMapping("/{trackId}") + public TrackDetailDto getTrackDetailsById(@PathVariable Long trackId) { + + // TODO: 추후 Spring Security 연동 후 실제 로그인한 학생 ID를 가져와야 함 + Long currentStudentId = 1L; + + // 5. 서비스의 메서드를 호출하여 결과를 받아온 후, 그대로 반환 + return trackService.getTrackDetails(currentStudentId, trackId); + } } diff --git a/src/main/java/com/example/enjoy/dto/CourseStatusDto.java b/src/main/java/com/example/enjoy/dto/CourseStatusDto.java index dcbe8f9..5c8ca1a 100644 --- a/src/main/java/com/example/enjoy/dto/CourseStatusDto.java +++ b/src/main/java/com/example/enjoy/dto/CourseStatusDto.java @@ -1,5 +1,6 @@ package com.example.enjoy.dto; +import lombok.Data; import lombok.Getter; import lombok.Setter; @@ -7,8 +8,8 @@ * 과목 정보 DTO * (title, year, semester, code, status) */ -@Getter -@Setter + +@Data public class CourseStatusDto { private String title; // 과목명 (기존 courseName) diff --git a/src/main/java/com/example/enjoy/dto/TrackDetailDto.java b/src/main/java/com/example/enjoy/dto/TrackDetailDto.java index 9b1a5bb..f69fee5 100644 --- a/src/main/java/com/example/enjoy/dto/TrackDetailDto.java +++ b/src/main/java/com/example/enjoy/dto/TrackDetailDto.java @@ -1,21 +1,19 @@ package com.example.enjoy.dto; +import lombok.Data; import lombok.Getter; import lombok.Setter; import java.util.List; /** * 상세 UI 화면의 트랙 탭 하나의 전체 정보를 담는 DTO - * 새로 정의된 CourseStatusDto를 사용 */ -@Getter -@Setter -public class TrackDetailDto { - - private String trackName; // 트랙 이름 - private int completedCount; // 이수한 과목 수 - private int requiredCount = 6; // 이수 필요 과목 수 - // 리스트의 타입이 새로운 CourseStatusDto로 변경 - private List courses; +@Data // 또는 @Getter, @Setter 등 +public class TrackDetailDto { + private Long trackId; + private String trackName; + private String department; + private String description; // 트랙에 대한 설명 추가 + private List courses; // 트랙에 포함된 과목 목록 } diff --git a/src/main/java/com/example/enjoy/entity/StudentCourse.java b/src/main/java/com/example/enjoy/entity/StudentCourse.java index 674d50c..af517cb 100644 --- a/src/main/java/com/example/enjoy/entity/StudentCourse.java +++ b/src/main/java/com/example/enjoy/entity/StudentCourse.java @@ -8,7 +8,7 @@ @Entity @Getter -public class StudentCourse { +public class StudentCourse { // 학생이 실제로 이수한 과목 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/com/example/enjoy/entity/TrackCourse.java b/src/main/java/com/example/enjoy/entity/TrackCourse.java index c9f0156..c50ee73 100644 --- a/src/main/java/com/example/enjoy/entity/TrackCourse.java +++ b/src/main/java/com/example/enjoy/entity/TrackCourse.java @@ -1,10 +1,11 @@ package com.example.enjoy.entity; import jakarta.persistence.*; +import lombok.Data; import lombok.Getter; @Entity -@Getter +@Data public class TrackCourse { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/example/enjoy/repository/ScheduledCourseRepository.java b/src/main/java/com/example/enjoy/repository/ScheduledCourseRepository.java deleted file mode 100644 index 2a82b5e..0000000 --- a/src/main/java/com/example/enjoy/repository/ScheduledCourseRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.enjoy.repository; - -public interface ScheduledCourseRepository { -} diff --git a/src/main/java/com/example/enjoy/repository/TrackCourseRepository.java b/src/main/java/com/example/enjoy/repository/TrackCourseRepository.java deleted file mode 100644 index d263ef0..0000000 --- a/src/main/java/com/example/enjoy/repository/TrackCourseRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.example.enjoy.repository; - -import com.example.enjoy.entity.TrackCourse; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TrackCourseRepository extends JpaRepository { -} \ No newline at end of file diff --git a/src/main/java/com/example/enjoy/repository/TrackRepository.java b/src/main/java/com/example/enjoy/repository/TrackRepository.java index 99c2f63..c35d37c 100644 --- a/src/main/java/com/example/enjoy/repository/TrackRepository.java +++ b/src/main/java/com/example/enjoy/repository/TrackRepository.java @@ -3,10 +3,21 @@ import com.example.enjoy.entity.Track; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + import java.util.List; +import java.util.Optional; public interface TrackRepository extends JpaRepository { @Query("SELECT DISTINCT t FROM Track t LEFT JOIN FETCH t.courses") List findAllWithCourses(); + + /** + * 특정 ID의 트랙을 과목 정보와 함께 조회합니다 (N+1 문제 해결). + * @param trackId 트랙 ID + * @return 트랙 정보 (Optional) + */ + @Query("SELECT t FROM Track t JOIN FETCH t.courses WHERE t.id = :trackId") + Optional findByIdWithCourses(@Param("trackId") Long trackId); } \ No newline at end of file diff --git a/src/main/java/com/example/enjoy/repository/UserRepository.java b/src/main/java/com/example/enjoy/repository/UserRepository.java deleted file mode 100644 index 01913a7..0000000 --- a/src/main/java/com/example/enjoy/repository/UserRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.enjoy.repository; - -public interface UserRepository { -} \ No newline at end of file diff --git a/src/main/java/com/example/enjoy/service/TrackService.java b/src/main/java/com/example/enjoy/service/TrackService.java index 7afb620..dd1d328 100644 --- a/src/main/java/com/example/enjoy/service/TrackService.java +++ b/src/main/java/com/example/enjoy/service/TrackService.java @@ -1,6 +1,8 @@ package com.example.enjoy.service; import com.example.enjoy.dto.CourseDto; +import com.example.enjoy.dto.CourseStatusDto; +import com.example.enjoy.dto.TrackDetailDto; import com.example.enjoy.dto.TrackProgressDto; import com.example.enjoy.entity.StudentCourse; import com.example.enjoy.entity.Track; @@ -9,6 +11,7 @@ import com.example.enjoy.repository.TrackRepository; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @@ -23,8 +26,6 @@ public class TrackService { private final TrackRepository trackRepository; private final StudentCourseRepository studentCourseRepository; // 기존 기능 - // ... (기존의 saveStudentCoursesFromExcel, calculateTrackProgress 메서드) ... - /** * 모든 트랙 정보를 학과별로 그룹화하여 반환하는 메서드 */ @@ -37,6 +38,9 @@ public Map> getAllTracksGroupedByDepartment() { .collect(Collectors.groupingBy(Track::getDepartment)); } + /** + * 학생이 이수한 과목 이름을 Set으로 반환하는 private 메서드 + */ public List calculateTrackProgress(Long studentId) { // 1. 학생의 이수 과목 목록 조회 Set completedCourseNames = studentCourseRepository.findByStudentId(studentId) @@ -89,4 +93,67 @@ public List calculateTrackProgress(Long studentId) { return progressList; } + + /** + * 학생이 이수한 과목 이름을 Set으로 반환하는 메서드 + */ + @Transactional(readOnly = true) + public TrackDetailDto getTrackDetails(Long studentId, Long trackId) { + + // 1. [리팩토링] 학생 이수 과목 조회 로직을 private 메서드로 호출 + Set completedCourseNames = getCompletedCourseNames(studentId); + + // 2. ID로 트랙 정보와 소속 과목들을 한번에 조회 + Track track = trackRepository.findByIdWithCourses(trackId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 트랙입니다.")); + + // 3. 트랙의 과목 목록을 CourseStatusDto 리스트로 변환 + List courseStatusList = track.getCourses().stream() + .map(trackCourse -> { + // 4. [수정] DTO 객체 생성 및 실제 필드에 맞게 데이터 세팅 + CourseStatusDto dto = new CourseStatusDto(); + dto.setTitle(trackCourse.getCourseName()); + // (TrackCourse 엔티티에 getCourseCode, getYear, getSemester가 있다고 가정합니다) + dto.setCode(trackCourse.getCourseCode()); + dto.setYear(trackCourse.getAcademicYear()); + dto.setSemester(trackCourse.getAcademicSemester()); + + // 5. [수정 & 리팩토링] 이수 여부를 판단하고, 그 결과에 따라 status(String) 값을 세팅 + if (isCourseCompleted(trackCourse, completedCourseNames)) { + dto.setStatus("COMPLETED"); + } else { + dto.setStatus("NONE"); + } + + return dto; + }) + .collect(Collectors.toList()); + + // 6. 최종적으로 TrackDetailDto를 조립하여 반환 + TrackDetailDto trackDetailDto = new TrackDetailDto(); + trackDetailDto.setTrackId(track.getId()); + trackDetailDto.setTrackName(track.getName()); + trackDetailDto.setDepartment(track.getDepartment()); + trackDetailDto.setCourses(courseStatusList); + + return trackDetailDto; + } + + /** + * 학생 ID로 해당 학생이 이수한 모든 과목명을 조회합니다. + */ + private Set getCompletedCourseNames(Long studentId) { + return studentCourseRepository.findByStudentId(studentId) + .stream() + .map(StudentCourse::getCourseName) + .collect(Collectors.toSet()); + } + + /** + * 특정 과목이 이수 완료되었는지 확인합니다. (과목 별칭 포함) + */ + private boolean isCourseCompleted(TrackCourse course, Set completedCourseNames) { + return completedCourseNames.contains(course.getCourseName()) || + (course.getCourseAlias() != null && completedCourseNames.contains(course.getCourseAlias())); + } }