특정 카테고리를 불러오는 API 를 구현할 때 카테고리 안에 카테고리에 따른 반찬리스트가 담겨져있기 때문에,
Category Entity 내에 List<Banchan> items
형태로 필드를 추가하고 그것을 response하는 형식으로 할려고 했다.
Entity 내에 그렇게 추가하면 @MappedCollection
을 써야하는것 같았는데, 어떻게 사용하는지도 모르겠고 다른 코드들을 봐도 이해가 안갔다.
결론을 내린게 테이블들이 부모,자식 상속관계에 있지않아도 다시말해서 어떤 구조적인걸 따지지않고 어쨋든 Category 를 Response할때 일단 데이터 먼저 잘 전달할수 있도록 생각했다.
잠깐 카테고리 테이블도 반찬에다가 그냥 붙여버리면 되지않나? 즉 테이블하나로 해결볼수없을까 생각하기도 했는데,
그렇게 하면 좀 곤란한 이유가 카테고리 내에 name
이라는 컬럼이 카테고리 번호에 종속적이라는 느낌을 받았기때문이다.
예를들어서, 이전에 반찬디테일을 반찬에다가 한테이블로 묶어줘도 괜찮다고 생각한 이유는 어차피 반찬이 가져야할 정보라고 생각했기 때문이지만,
카테고리 이름은 반찬 자체에 대한 정보와는 상관없는 정보이기 때문이다.
그래서category_id
와 name
컬럼을 가진 테이블을 하나 더 만들었다.
그리고 반찬 테이블에도 카테고리 테이블과 연결시켜주기 위해 category_id를 추가해주었다.
이렇게 생각하고 코드를 짜기로 했다.
막상 코드로 짜보려고 하니까 부딪혔던 문제가 Entity 내에 items 를 추가하고 Entity를 가지고 바로 클라이언트와 통신하려하니까 디비에도 items를 가지는 컬럼이 있어야해서 문제였다. 근데 이전 TIL에서도 언급했듯이 그렇게 할 수 없기때문에 방법을 찾아봤는데 @MappedCollection
이 그 정답이었던거 같다.
근데 사용법도 모르겠고 이해도 되지않아서 이걸 어떻게 해야하나 고민했다.
디비에 저장하지않은 items를 빼내기위해서 그리고 Entity와 클라이언트가 통신하지 않기위해 카테고리도 DTO를 만들기로 했다.
카테고리 DTO로 클라이언트에 response하도록 만들었더니 자연스럽게 모든문제가 풀렸던거 같다.
DTO에 추가적으로 response할 데이터를 만들어주면 되었기 때문이다.
예를들어서 List<BanchanDto> items
를 그냥 추가해주고 반환해주면 됐다.
items
에 반찬을 어떻게 담을지 그 고민을 좀 많이했다.
첨에 시도한건 그냥 CategoryDto
클래스 내에 BanchanRepository
를 만들고 @Autowired
를 달아주고 그걸 사용해서 바로 필요한 반찬리스트를 뽑아내려고했다.
public class CategoryDto {
@Autowired
private BanchanRepository banchanrepository;
private String category_id;
private String name;
// ...생략
}
하면서도 이렇게 하면 안될것같은 예감이 직감했는데,,(왜냐면 레포를 컨트롤러나 서비스단에서 사용하니까..) 역시나 안됐다.
레포에서 반찬을 꺼내는 부분에서 NullPointerException이 났다.
Null은 일단 아닐거라고 생각해서 junit으로 테스트 해봤다.
@Test
@DisplayName("반찬레포에서 카테고리에 따라서 잘 가져오는지 확인")
void getBanchanList() {
List<Banchan> banchanList = banchanRepository.findBanchansByCategory_id("17011200");
assertThat(banchanList).isNotNull();
}
결과는 Pass✅
결과가 패스이기때문에 Null이 아니라 데이터가 있는데도 집어넣지 못하고 있는 상황이라고 결론 내릴수있었다.
public class CategoryDto {
@Autowired
private BanchanRepository banchanRepository;
// ...생략
}
그러면 역시 직감한대로 이게 문제였다고 생각해서 Controller나 Service가 아니라 그냥 DTO 모델에서 Repository를 사용할수있는 방법이 없을까 찾아봤는데,
그런 방법은 없는거 같고 (구조상으로도 문제인듯)
지나가다가 Having a repository dependent on another repository 라는게 눈에 끌려서 봤고, 같은 클래스내에서 여러 레포를 사용하는 예제코드를 보게됐다.
같은 클래스내에서 여러 레포를 사용한다?
아~Dto내에 레포를 만들어서 어찌할게 아니라 걍 서비스단에서 레포를 두개가져와서 사용하면 되겠구나 하고 깨달았다 ㅋㅋ
@Service
public class CategoryService {
private CategoryRepository categoryRepository;
private BanchanRepository banchanRepository;
// ...생략
// ...생략
// ...생략
public CategoryDto findBestBanchansByCategory(String category_id) {
Category category = categoryRepository.findCategoryByCategory_id(category_id);
CategoryDto categoryDto = CategoryDto.of(category, findBanchanListByCategoryId(category_id));
return categoryDto;
}
private List<BanchanDto> findBanchanListByCategoryId(String catrgory_id) {
List<BanchanDto> banchanDtoList = new ArrayList<>();
List<Banchan> banchanList = new ArrayList<>();
banchanList = banchanRepository.findBanchansByCategory_id(catrgory_id);
for (int i = 0; i < banchanList.size(); i++) {
banchanDtoList.add(BanchanDto.of(banchanList.get(i)));
}
return banchanDtoList;
}
}
CategoryDto를 리턴해줄때 카테고리 Entity를 Dto로 바꿔주는 메서드 파라미터에다가 위에처럼 반찬리스트를 카테고리서비스에서 반찬만들어서 넣어준다.