댓글 생성 시, 현재 로그인한 member의 세션키를 가져오는 과정에서 ClassCastException
오류가 발생했다.
HttpSession session = request.getSession(false);
Long memberId = (Long) session.getAttribute("sessionKey"); // 오류 발생
CommentResponseDto commentResponseDto = commnetService.save(id, memberId, dto);
세션키로 Email을 저장했는데, String
을 Long
으로 변환하려고 하니 오류가 난 것이다. 따라서 다음과 같이 수정하여 문제를 해결했다.
HttpSession session = request.getSession(false);
String email = (String) session.getAttribute("sessionKey");
CommentResponseDto commentResponseDto = commnetService.save(id, email, dto);
Service에서도 id로 찾던 member를 email로 찾아 반환했다.
// Member member = memberRepository.findByIdOrElseThrow(id); // 기존 코드
Member member = memberRepository.findByEmailOrElseThrow(email);
댓글 작성 시, 아래와 같이 INSERT문은 제대로 실행된 것 같은데 DB에서는 comment 테이블이 보이지 않는 문제가 있었다.
@Transactional
을 추가하지 않은 것이 원인이다. 트랜잭션이 자동으로 커밋되지 않아서 데이터가 DB에 반영되지 않았던 것이다. Service 계층에서 DB 작업을 수행할 때는 반드시 @Transactional
을 사용해 트랜잭션을 관리해야한다.
다음과 같이 @Transactional
을 붙인다.
@Transactional
public CommentResponseDto save(Long scheduleId, String email, CommentRequestDto dto) {
Schedule schedule = scheduleRepository.findByIdOrElseThrow(scheduleId);
Member member = memberRepository.findByEmailOrElseThrow(email);
Comment comment = new Comment(dto.getContents(), member, schedule);
Comment savedComment = commentRepository.save(comment);
return new CommentResponseDto(
savedComment.getId(),
savedComment.getContents(),
member.getUsername(),
savedComment.getCreatedAt(),
savedComment.getModifiedAt()
);
}
이렇게 해도 되지만, 어제 <연관 관계 매핑 세션>에서 튜터님께서 클래스 레벨에 우선@Transactional(readOnly = true)
를 사용한다는 것이 기억나 다음과 같이 수정했다. 그리고 지난번 피드백을 다시 보니 다른 튜터님께서도 "트랜잭션 범위를 확실히 하기 위해 DB 업데이트와 관련된 모든 작업을 트랜잭션 내에서 처리하는 것이 좋다."라고 피드백을 남겨주셨다.
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CommentService {
private final CommentRepository commentRepository;
private final ScheduleRepository scheduleRepository;
private final MemberRepository memberRepository;
@Transactional
public CommentResponseDto save(Long scheduleId, String email, CommentRequestDto dto) {
Schedule schedule = scheduleRepository.findByIdOrElseThrow(scheduleId);
Member member = memberRepository.findByEmailOrElseThrow(email);
Comment comment = new Comment(dto.getContents(), member, schedule);
Comment savedComment = commentRepository.save(comment);
return new CommentResponseDto(
savedComment.getId(),
savedComment.getContents(),
member.getUsername(),
savedComment.getCreatedAt(),
savedComment.getModifiedAt()
);
}
}
이 문제를 해결하면서 잘 신경쓰지 않았던 부분의 개념을 확실히 익히게 되었다.
@Transactional(readOnly = true)
적용@Transactional
이 없어도 동작하지만, @Transactional(readOnly = true)
를 사용하면 성능이 최적화 되며 Lazy Loading(지연 로딩) 사용 시 트랜잭션 유지가 필요할 수 있음@Transactional
을 개별적으로 추가해야 함.@Transactional
을 명시하면, 클래스 레벨의 readOnly = true
를 무시하고 데이터 변경이 가능한 트랜잭션으로 동작어제 연관 관계 매핑 못하는 못나가는 방에서 못나와서(...) 과제를 하나도 못하고 굉장히 피곤한 상태로 잤다.(TIL도 못쓴채..) 그래서 오늘 일찍 일어나서 오전 중에 댓글 CRUD 끝내고 오후에는 페이지네이션, 예외 처리 분리, 기타 리팩토링까지 했다. README까지 끝내려고 했는데 머리가 멍해져서 그냥 내일 하기로..🫠