CODING/스파르타 내일배움캠프 TIL

40_삼총사_JPA, 연관관계 실시간 구현 세션_일정관리 Develop 리팩토링_25.2.11(화)

codingTrip 2025. 2. 11. 22:52

코트카타

42) 삼총사

class Solution {
    public int solution(int[] number) {
        int answer = 0;
        for(int i = 0; i < number.length; i++) {
                for(int j = i+1; j< number.length; j++) {
                    for(int k = j+1; k < number.length; k++) {
                        if(number[i]+number[j]+number[k] == 0)
                            answer++;
                    }
                }
            }
        return answer;
    }
}

중첩 for문을 사용하지 않고 다른 방법을 찾기에는 너무 복잡했다...

점점 나에게는 어려워지는 것 같다.


JPA, 연관관계 실시간 구현 세션

N:M은 처음 해보는 것이라서 쉽지 않았다.

사실 지금도 다 이해하지는 못했다.

튜터님 github : https://github.com/Nhahan/spring-docs/tree/main/library

 

@Getter
@Entity
@NoArgsConstructor
public class BookAuthor {

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id")
    private Book book;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    private Author author;

    public BookAuthor(Book book, Author author) {
        this.book = book;
        this.author = author;
    }
}

위와 같이 @ManyToOne만 사용하면 된다.

    @Transactional(readOnly = true)
    public List<BookResponseDto> findByName(String name) {
        List<BookAuthor> bookAuthors = bookAuthorRepository.findByBook_Name((name));
        List<BookResponseDto> dtos = new ArrayList<>();
        for (BookAuthor bookAuthor : bookAuthors) {
            Book book = bookAuthor.getBook();
            Author author = bookAuthor.getAuthor();
            dtos.add(new BookResponseDto(
                    book.getId(),
                    book.getName(),
                    author.getName()
            ));
        }
        return dtos;
    }

나의 경우에는 책 조회 시,

책 id는 고유 식별자이므로

책 이름을 파라미터로 받아서 조회했다.

매핑 테이블인 bookAuthor 리스트를 만들어서 입력한 책 이름에 해당하는 데이터를 불러왔다.

그리고 이를 dto에 매핑해서 저장했다.

나는 이 정도로 만족하고 싶다.


2025.02.11(화)

일정관리 Develop 리팩토링

 

리팩토링 내용

- 패키지 엔티티별로 분리

- 일정, 댓글 responseDto에 수정일, 생성일 필드 추가

- 메서드명 수정

- Controller에 있는 비즈니스 로직 -> Service로 옮기기

 /* 수정 전 */
 @PatchMapping("/{scheduleId}")
    public ResponseEntity<Void> updateTitleAndContents(
            @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
            @PathVariable("scheduleId") Long id,
            @Valid @RequestBody UpdateTitleAndContentsRequestDto requestDto
    ) {

        Member findMember = scheduleService.findMember(id);
        Long findMemberId = findMember.getId();

        // 작성자만 수정 가능
        if (!loginMember.getId().equals(findMemberId)){
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        // 수정 로직
        scheduleService.updateTitleContents(id, requestDto.getTitle(), requestDto.getContents());

        return new ResponseEntity<>(HttpStatus.OK);
    }
    
 /* 수정 후 */
    @PatchMapping("/schedules/{scheduleId}")
    public ResponseEntity<ScheduleResponseDto> updateTitleAndContents(
            @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
            @PathVariable("scheduleId") Long id,
            @Valid @RequestBody UpdateTitleAndContentsRequestDto requestDto
    ) {
        ScheduleResponseDto scheduleResponseDto
                = scheduleService.updateTitleContents(id, requestDto.getTitle(), requestDto.getContents(), loginMember);

        return ResponseEntity.ok(scheduleResponseDto);
    }

작성자만 해당 일정을 수정할 수 있도록 만드는 로직을 Service로 옮겨서

Controller는 간결하게 보이도록 했다.

@Transactional
    public ScheduleResponseDto updateTitleContents(Long id, String title, String contents, Member loginMember) {
        Schedule schedule = scheduleRepository.findById(id).orElseThrow(
                () -> new ApplicationException(ErrorMessageCode.NOT_FOUND,
                        List.of(new ApiError(CustomErrorMessageCode.ID_NOT_FOUND.getCode(),
                                             CustomErrorMessageCode.ID_NOT_FOUND.getMessage())))
        );
        Long findMemberId = schedule.getMember().getId();
        log.info("fineMemberId={}",findMemberId);
        log.info("loginMember.getId()={}",loginMember.getId());

        // 작성자만 수정 가능
        if (loginMember.getId() != findMemberId){
            throw new ApplicationException(ErrorMessageCode.FORBIDDEN,
                    List.of(new ApiError(CustomErrorMessageCode.NOT_OWNER.getCode(),
                                         CustomErrorMessageCode.NOT_OWNER.getMessage())));
        }

        schedule.updateTitleContents(title, contents);

        return new ScheduleResponseDto(
                schedule.getId(),
                schedule.getTitle(),
                schedule.getContents(),
                schedule.getMember().getName(),
                schedule.getMember().getEmail(),
                schedule.getCreatedAt(),
                schedule.getModifiedAt()
        );
    }

 

- 예외처리 커스텀하기(Enum 사용해서 코드 구성하기)

@Getter
@RequiredArgsConstructor
public enum CustomErrorMessageCode {

    ID_NOT_FOUND("ID_NOT_FOUND", "해당 id를 찾을 수 없습니다."),
    NOT_OWNER("NOT_OWNER", "본인(작성자)이 아닌 경우 권한이 없습니다."),
    INVALID_PASSWORD("INVALID_PASSWORD", "비밀번호가 일치하지 않습니다.");

    private final String code;
    private final String message;
}

예외처리 커스텀 code를 위와 같이 enum 타입으로 만들어서

아래와 같은 메시지가 보이도록 만들었다.