🚩 ! 요구사항 확인: 필수 구현 기능
- 회원 가입 기능
이메일,비밀번호,성별,전화번호,주소,권한을 저장할 수 있습니다.
- ADMIN, USER
권한이 있습니다.이메일은올바른 이메일 형식을 지켜야 합니다.비밀번호는최소 8자 이상, 15자 이하이며 알파벳 대소문자(a~z, A~Z), 숫자(0~9), 특수문자로 구성되어야 합니다.- 회원가입 성공을 확인할 수 있는 값을 반환합니다.
- ex) HTTP Status Code, Error Message …
- 로그인 기능
- 회원은
이메일,비밀번호를 입력하여 서버에 로그인을 요청할 수 있습니다.- 로그인 성공 시,
회원의 정보와JWT를 활용하여 토큰을 발급하고,
발급한 토큰을 Header에 추가한 후 로그인 성공을 확인할 수 있는 값과 함께 반환합니다.
- ex) HTTP Status Code, Error Message …
- 강사 등록 기능
이름,경력(년차),회사,전화번호,소개를 저장할 수 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- ADMIN 권한을 가진 회원만 강사 등록이 가능합니다.
- 등록된 강사의 정보를 반환 받아 확인할 수 있습니다.
- 강의 등록 기능
강의명,가격,소개,카테고리,강사,등록일을 저장할 수 있습니다.
- Spring, React, Node
카테고리가 있습니다.- 강사 한 명이 여러 개의 강의를 촬영할 수도 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- ADMIN 권한을 가진 회원만 강의 등록이 가능합니다.
- 등록된 강의의 정보를 반환 받아 확인할 수 있습니다.
- 선택한 강의 조회 기능
- 선택한 강의의 정보를 조회할 수 있습니다.
- 모든 사용자가 강의를 조회할 수 있습니다.
- 강의를 촬영한 강사의 정보를 확인할 수 있습니다.
- 강사의 정보에
전화번호는 제외 되어있습니다.
- 카테고리별 강의 목록 조회 기능
- 선택한
카테고리에 포함된 강의를 조회할 수 있습니다.
- 모든 사용자가 강의를 조회할 수 있습니다.
- 강사의 정보는 제외됩니다.
- 조회된 강의 목록은 선택한 기준에 의해 정렬됩니다.
강의명,가격,등록일중 기준을 선택할 수 있습니다.- 내림차순, 오름차순을 선택할 수 있습니다.
- 선택한 강의 댓글 기능
- 선택한 강의에 댓글을 등록할 수 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- 회원만 댓글 등록이 가능합니다.
- 댓글 등록 성공을 확인할 수 있는 값을 반환합니다.
- ex) HTTP Status Code, Error Message …
- 선택한 강의를 조회할 때 해당 강의에 등록된 댓글들도 함께 조회할 수 있습니다.
- 선택한 강의의 선택한 댓글 수정 기능
- 선택한 강의의 선택한 댓글을 수정할 수 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- 해당 댓글을 등록한 회원만 댓글 수정이 가능합니다.
- 댓글 수정 성공을 확인할 수 있는 값을 반환합니다.
- ex) HTTP Status Code, Error Message …
- 선택한 강의의 선택한 댓글 삭제 기능
- 선택한 강의의 선택한 댓글을 삭제할 수 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- 해당 댓글을 등록한 회원만 댓글 삭제가 가능합니다.
- 댓글 삭제 성공을 확인할 수 있는 값을 반환합니다.
- ex) HTTP Status Code, Error Message …
- 선택한 강의 좋아요 기능
- 선택한 강의에 좋아요를 등록할 수 있습니다.
- 로그인을 통해 발급받은 JWT가 함께 요청됩니다.
- 회원만 좋아요 등록이 가능합니다.
- 이미 해당 강의에 좋아요를 한 상태라면 좋아요가 취소됩니다.
- 좋아요 등록/취소 성공을 확인할 수 있는 값을 반환합니다.
- ex) HTTP Status Code, Error Message …
- 선택한 강의를 조회할 때 해당 강의의 좋아요 수를 함께 조회할 수 있습니다.



.
-> 이번 과제에서는 저번 3과제에서 이어서 강의와 강사간의 연관관계에 대해 한번 더 도전해보았다. 이번에는 조건에 써져있는 것처럼 선택한 강의를 조회할시 그 강의를 찍은 강사에 대한 정보가 나오도록 코드를 보완해 보았다.
먼저 강의 Entity에 강사를 단방향으로 연결해주고
@ManyToOne
@JoinColumn(name = "tutor_id") // 데이터베이스의 외래키 컬럼 지정
private Tutor tutor;
public Lecture(LectureRequestDto requestDto) {
this.title = requestDto.getTitle();
this.price = requestDto.getPrice();
this.description = requestDto.getDescription();
this.category = requestDto.getCategory();
this.tutor = tutor; // Tutor 객체 설정
강의의 RequestDto에도 tutorId를 추가 해줬다.
그런 후에 LectureService 클래스 내의 registerLecture 메서드에서 Lecture 엔티티를 생성하고 저장하는 로직을 수정했다. 현재 Lecture 엔티티를 생성할 때 Tutor 객체가 연결되지 않은 상태로 저장되고 있기 때문에, Tutor 객체를 찾아 Lecture 객체에 연결한 후 저장하는 과정을 추가해야 했다.
그런데 강의 등록때는 말고 조회시에만 보일수는 없을까?

조회시에는 전화번호는 빼야하니까 LectureResponseDto 클래스는 강의 생성때를 위해 놔두고 강의 조회 기능에서 Map을 사용하여 응답을 생성하도록 만들었다.
@Service
@RequiredArgsConstructor
@Slf4j
public class LectureService {
private final LectureRepository lectureRepository;
private final TutorRepository tutorRepository;
private final LectureLikeRepository lectureLikeRepository; // 좋아요 기능 때문에 추가
// 강의 등록
public LectureResponseDto registerLecture(LectureRequestDto requestDto) {
// 로그 남기기
log.info("Registering a new lecture with title: {}", requestDto.getTitle());
// 중복 강의 체크
if (lectureRepository.existsByTitle(requestDto.getTitle())) {
// 중복 강의가 있는 경우 로그를 남기고 예외를 발생.
log.warn("Lecture registration failed: Duplicate lecture title {}", requestDto.getTitle());
throw new IllegalStateException("Lecture with title " + requestDto.getTitle() + " already exists");
}
// category 유효성 검사
// Enum 값 검증
if (!EnumSet.allOf(LectureEnum.class).contains(requestDto.getCategory())) {
throw new IllegalArgumentException("Invalid category: " + requestDto.getCategory());
}
// Tutor 조회
Tutor tutor = tutorRepository.findById(requestDto.getTutorId())
.orElseThrow(() -> new EntityNotFoundException("Tutor not found"));
// Lecture 엔티티 생성
Lecture lecture = new Lecture();
lecture.setTitle(requestDto.getTitle());
lecture.setPrice(requestDto.getPrice());
lecture.setDescription(requestDto.getDescription());
lecture.setCategory(requestDto.getCategory());
lecture.setTutor(tutor); // 여기에서 Tutor 설정
// 강의 저장
Lecture savedLecture;
// 저장 시도
try {
savedLecture = lectureRepository.save(lecture);
} catch (DataAccessException e) {
// 데이터베이스 액세스 중 예외 발생 시 로그를 발생
log.error("Lecture registration failed: Database access error", e);
throw e; // 예외를 상위 계층으로 전파
}
// 저장 성공 로그
log.info("Lecture registered successfully with id: {}", savedLecture.getLectureId());
// LectureResponseDto 생성 및 반환
return new LectureResponseDto(savedLecture);
}
// 강의 조회
public Map<String, Object> getLectureDetails(Long lectureId) {
Lecture lecture = lectureRepository.findById(lectureId)
.orElseThrow(() -> new EntityNotFoundException("Lecture not found"));
// 좋아요 수를 가져옴
Long likeCount = lectureLikeRepository.countByLecture(lecture);
Map<String, Object> response = new HashMap<>();
response.put("lectureId", lecture.getLectureId());
response.put("title", lecture.getTitle());
response.put("price", lecture.getPrice());
response.put("description", lecture.getDescription());
response.put("category", lecture.getCategory());
Tutor tutor = lecture.getTutor();
if (tutor != null) {
Map<String, Object> tutorDetails = new HashMap<>();
tutorDetails.put("tutorName", tutor.getTutorName());
tutorDetails.put("experienceYears", tutor.getExperienceYears());
tutorDetails.put("company", tutor.getCompany());
// 전화번호는 제외
response.put("tutor", tutorDetails);
response.put("likeCount", likeCount); // 좋아요 수 추가
}
return response;
}
// 강의 조회
@GetMapping("/user/lecture/{lectureId}")
public ResponseEntity<Map<String, Object>> getLecture(@PathVariable Long lectureId) {
Map<String, Object> lectureDetails = lectureService.getLectureDetails(lectureId);
return ResponseEntity.ok(lectureDetails);
}
그에맞게 Controller 부분도 수정 해주었다.
이렇게 하니 등록시에는 강의 등록 정보만나오고 조회시에 강사 정보도 포함해서 나오게 되었다.
이걸 바탕으로 강의에 대한 댓글 기능과 좋아요 기능도 완성했다.
각 Entity에 강의과 유저를 Id로 찾아와 연결하고 수정과 삭제시에는 findById로 작성자가 맞는지 찾고 맞으면 실행시킨다.