1. 오늘 학습 키워드
- N:M(다대다) 관계와 연결 테이블: Product와 Folder 사이의 다대다(N:M) 관계를 해결하기 위한 ProductFolder 매핑 엔티티 활용
- 데이터 무결성 검증: 비즈니스 로직(Service) 단에서 데이터 존재 여부, 소유권 권한, 중복 등록을 차단하는 다중 예외 처리(throw new)
- Spring Data JPA 쿼리 메서드: 언더바(_)를 활용하여 참조 대상의 내부 필드까지 조건으로 검색하는 findAllByUserAndProductFolderList_FolderId 메서드 구현
- Java 객체 비교: 기본 타입(==)과 참조 타입(.equals())의 차이 및 Long 타입 ID 비교 시 주의점
2. 오늘 학습 한 내용을 나만의 언어로 정리하기
- 관심 상품에 폴더를 입히다: 기존에는 상품을 등록하고 조회하는 기능만 있었다면, 오늘은 사용자가 원하는 폴더(예: '살 것', '위시리스트')를 만들고 특정 상품을 그 폴더에 집어넣는 기능을 구현했다.
- 징검다리 테이블(ProductFolder)의 중요성: 하나의 상품은 여러 폴더에 들어갈 수 있고, 하나의 폴더도 여러 상품을 가질 수 있다. 이 다대다관계를 맺어주기 위해 중간에 외래키(FK)들을 모아둔 연결 테이블을 관리하는 레포지토리를 사용했다.
- 코드는 무조건 깐깐하게 검증부터: addFolder 로직을 짜며 배운 핵심은 '저장(save)은 가장 마지막에 하는 것'이다. 상품과 폴더가 진짜 존재하는지 확인하고, 로그인한 유저가 주인이 맞는지 체크하고, 이미 등록된 중복 데이터가 아닌지 샅샅이 검사한 뒤에야 비로소 저장 도장을 찍어준다.
- JPA 쿼리 메서드의 마법: findAllByUserAndProductFolderList_FolderId 처럼 JPA는 메서드 이름만 잘 지어도 엔티티 내부 리스트를 타고 들어가 안쪽 엔티티의 ID 조건까지 걸어 복잡한 JOIN 쿼리를 자동으로 만들어 준다는 것을 체감했다.
3. 학습하며 겪었던 문제점 & 에러
🛑 문제 & 에러 정의
- 상황: 폴더에 상품 추가 기능(addFolder)을 테스트하려는데, 특정 계정으로 요청을 보내면 성공하지 못하고 로그인 창으로 화면이 튕겨버리는 현상이 발생함.
- 원인 분석: 서비스 로직의 조회 권한 범위 차이 때문이었음. ADMIN(관리자) 계정은 전체 유저의 상품을 다 볼 수 있도록 설계되어 있어서 화면에 '남의 상품'이 노출되었고, 이를 내 폴더에 담으려고 하니 서비스단의 소유권 검증 로직에서 예외가 발생해 시큐리티에 의해 로그인 창으로 튕긴 것이었음.
UserRoleEnum userRoleEnum = user.getRole();
Page<Product> productList;
if(userRoleEnum == userRoleEnum.USER){
productList = productRepository.findAllByUser(user, pageable);
} else {
productList = productRepository.findAll(pageable);
}
※ 내가 한 시도
- 자바 코드 내에 예외를 던지는 부분(IllegalArgumentException 등)이 먼저 터진 건가 싶어 서비스 로직을 점검함.
- 자바에서 ID(숫자)를 비교할 때 대문자 Long 객체 타입을 ==로 비교하면 주소값 비교가 되어 버그가 날 수 있음을 인지하고, .equals()를 사용해 내용물(값)을 올바르게 비교하고 있는지 대조군을 체크함.
- 계정을 일반 사용자 계정으로 변경하여 테스트를 다시 진행함.
if(!product.getUser().getId().equals(user.getId())
|| !folder.getUser().getId().equals(user.getId())){
throw new IllegalArgumentException("회원님의 관심상품이 아니거나 회원님의 폴더가 아닙니다.");
}
※ 해결 방법
- USER(일반 사용자) 계정으로 로그인하니 본인이 등록한 상품만 목록에 나오기 때문에, 폴더에 추가할 때도 소유권 검증(product.getUser().getId().equals(user.getId()))을 무사히 통과하여 정상적으로 저장됨을 확인하고 해결함!
★ 새롭게 알게 된 점
- 관리자 권한과 일반 사용자 권한의 데이터 조회 범위가 다를 때, 기능 검증 단계에서 사이드 이펙트(부작용)가 발생할 수 있음을 배웠다.
- 자바에서 소문자 int/long은 ==로 비교하지만, JPA 엔티티 ID로 자주 쓰이는 대문자 Long은 객체이므로 안전하게 값을 비교하려면 반드시 .equals()를 써야 데이터 정합성이 깨지지 않는다는 점을 명확히 정리했다.
★ 이 문제&에러를 다시 만나게 되었다면?
- 앞으로 테스트를 진행할 때는 항상 테스트용 시나리오 계정(일반 유저A, 일반 유저B, 관리자)을 명확히 분리하고, 에러가 나면 해당 데이터의 실제 '소유자 ID'와 현재 로그인한 '유저 ID'의 권한 관계를 먼저 비교해 보겠다!
4. 내일 학습 할 것은 무엇인지
- Spring 심화주차 강의 듣기
- 알고리즘 코드카타 풀기
- TIL 작성하기!
화이팅!!!