문제 해결


1. Entity 에서 Setter Method가 열려있음 → Entity @Data 제거, 필요한 메서드 명시적으로 생성

<aside> 🔧 기존 코드

@Data
@Entity
@Table(name = "tester")
@NoArgsConstructor
public class Tester extends BaseTime {
	...
}

변경된 코드

@Entity
@Getter
@NoArgsConstructor(access= AccessLevel.PROTECTED)
public class Tester extends BaseEntity{
		...
		public void verifyPassword(String password) {
		        isValidPassword(password);
		    }
	
		public long pointToCash(long point) {
	      isAccountEmpty();
	      IsPointNaturalNumber(point);
	      long cash = point * 19 / 20;
	      isEnoughPoint(point);
	      this.point -= point;
	      return cash;
	  }
		...
}

해당 내용과 같은 방식으로, 프로젝트의 모든 Entity를 변경했습니다.

2. Service에서 Entity의 값을 직접 변경 하는 등 너무 많은 책임을 가짐 → Service의 역할을 축소, Service가 가졌던 책임 일부를 Entity로 이전

<aside> 💡 기존 코드

@Service
@RequiredArgsConstructor
public class MakerAuthServiceImpl implements MakerAuthService {

	@Override
    @Transactional
    public PatchTestResponse patchTest(
            UUID makerId,
            UUID testId,
            @RequestBody PatchTestRequest request
    ) {
        Maker maker = makerRepository.findById(makerId)
                .orElseThrow();
        Test test = testRepository.findById(testId)
                .orElseThrow();
        long beforePoint = 
                ((long) test.getReward() * test.getParticipantCapacity());

        String symbolImageRoot =
                FileHandler.saveProfileFileData(request.getSymbolImage());
        test = request.toEntity(test, symbolImageRoot);

        long needPoint =
                (long) test.getReward() * test.getParticipantCapacity();
        if (maker.checkAvailableCreateTest(needPoint - beforePoint)) {
            maker.setPoint(maker.getPoint() - (needPoint - beforePoint));
            return new PatchTestResponse(test.getId());
        } else {
            throw new MakerRevertTestFailException("수정하기 위한 잔여 Point가 부족합니다.\\n 필요한 Point: " + (needPoint - beforePoint) + ", 잔여 Point: " + maker.getPoint());
        }
    }
	
	...

}

변경된 코드

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MissionService {
		...

    @Transactional
    public UpdateMissionIncludeImageResponse updateIncludeImageMission(UUID missionId, UpdateMissionIncludeImageRequest request) {
        Mission mission = missionRepository.findById(missionId).orElseThrow(
                () -> new MissionNotFoundException("id: " + missionId + " not found")
        );
        FileHandler.removeTestImage(mission.getImageURL());
        String imageFileURL = FileHandler.saveTestImageFileData(request.getImage());

        mission.updateIncludeImageMission(
                request.getTitle(),
                request.getContent(),
                imageFileURL,
                request.getReward(),
                request.getLimitPerformer(),
                LocalDate.parse(request.getRecruitmentTimeStart(), DateTimeFormatter.ISO_LOCAL_DATE),
                LocalDate.parse(request.getRecruitmentTimeLimit(), DateTimeFormatter.ISO_LOCAL_DATE),
                LocalDate.parse(request.getDurationTimeStart(), DateTimeFormatter.ISO_LOCAL_DATE),
                LocalDate.parse(request.getDurationTimeLimit(), DateTimeFormatter.ISO_LOCAL_DATE)
        );

        UpdateMissionIncludeImageResponse response = UpdateMissionIncludeImageResponse.fromMission(mission);
        return response;
    }
		...
}

Service의 책임을 줄여주었습니다. Service에서는 Conroller에서 값을 받아와 DB에서 값을 조회하고, DTO의 값을 이용해 비지니스 로직을 처리하는 책임만을 남겨주었습니다.

해당 방식으로 모든 Service를 변경했습니다. 그리고 이 전에는 Service를 역할에 따라 나누었는데, 이번에는 Entity를 기반으로 나누었습니다.

3. JPA 사용하지만, 사용방법을 정확하게 알지 못함. → JPA 사용 방법 숙지 후 양방향 맵핑 구현, 그 외 JPA에서 지원하는 다양한 타입 사용, QueryDSL 사용