오늘도 정말 비가 많이 왔다. 아침에 자다가 비소리에 놀라서 깨고.. 공부하면서 습함에 놀라고.. 운동 다녀오면서 더 습한 날씨에 놀랐다. 정신이 많이 늘어지긴 했지만... 그래도 공부는 시간가는지 모르게 재밌게 했다. 아침에 기본 문제들을 푸는데 생각보다 내가 많이 모르고 있는 부분들이 많아서 반성을 많이했다. 원래는 어려운 것으로 공부하고 학습하려했지만, 기초부터 탄탄하게 쌓고서 각종 알고리즘 문제를 풀이를 하는 것으로 계획을 변경했다.
프로젝트 문제 해결
나는 오늘 이 문제를 해결하기 위해 정말 여러 자료를 검색하고, 생각했다.
나는 무엇을 하고 싶은 것일까?
나는 productQuerydslRepository 에서 findByProductCategory() 메서드를 호출하여, JPAEntity(ProductEntity) 를 가져오고, 이를 도메인 Entity(Product)로 변환하여 반환하고 싶었다. 이 과정에서 Product 가 없을 경우, Null의 가능성을 고려하여, Optional을 붙였다. 그리고 이 생각을 구현하기 위해 문제의 코드를 작성했다.
이 과정에서 제일 문제였던 부분은 반환 타입이다.(빨간 List 부분) List 를 어떻게 Optional 으로 바꾸지..? 하는 고민을 정말 많이 했다. 진짜 이걸 어떻게 할 수 있을까? 하는 책상 앞에 벙쪄 있기도 했다.
검색을 해도, 내 검색 능력이 부족해서인지 문제 해결을 위한 방법은 찾지 못했다. 더 정확히는 검색으로 나온 자료들을 내가 다 이해하지 못했다. 그러다 문득 과거 강의를 통해 공부했던 Java 프로젝트가 생각이 났다.
나는 바로 이전 프로젝트에서 내가 원하는 부분을 검색해보았다. 그리고 다행히도(?) 원하는 부분이 있었다. 그리고 해당 부분을 참고하여, 현 문제가 되는 메서드를 개선하고, 에러를 해결할 수 있었다.
빨간 에러표시가 사라지고 난뒤, 나는 궁금해졌다. 이게 어떻게 해결이 된거지? stream 을 사용해야만 map 을 사용할 수 있던 것이 아닌가? 하는 생각이 들었다.
Optional 내의 map()
map 메서드는 Optional 클래스에도 존재했다.(그리고 Stream 인터페이스에도 존재했다.) Stream 과, Optional 모두 map 메서드가 존재한다는 것을 알지 못하여, 내가 크게 혼란스러워 했던 것이다.
/**
* If a value is present, returns an {@code Optional} describing (as if by
* {@link #ofNullable}) the result of applying the given mapping function to
* the value, otherwise returns an empty {@code Optional}.
*
* <p>If the mapping function returns a {@code null} result then this method
* returns an empty {@code Optional}.
*
* @apiNote
* This method supports post-processing on {@code Optional} values, without
* the need to explicitly check for a return status. For example, the
* following code traverses a stream of URIs, selects one that has not
* yet been processed, and creates a path from that URI, returning
* an {@code Optional<Path>}:
*
* <pre>{@code
* Optional<Path> p =
* uris.stream().filter(uri -> !isProcessedYet(uri))
* .findFirst()
* .map(Paths::get);
* }</pre>
*
* Here, {@code findFirst} returns an {@code Optional<URI>}, and then
* {@code map} returns an {@code Optional<Path>} for the desired
* URI if one exists.
*
* @param mapper the mapping function to apply to a value, if present
* @param <U> The type of the value returned from the mapping function
* @return an {@code Optional} describing the result of applying a mapping
* function to the value of this {@code Optional}, if a value is
* present, otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is {@code null}
*/
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
해당 부분을 이해해 본다면(GPT의 힘을 빌렸다..) 다음과 같다.
- map 메서드는 매핑 함수를 인수로 받는다. 이 매핑 함수는 옵션의 현재 값을 받아들이고 잠재적으로 다른 유형의 새 값을 반환하는 역할이다.
- 옵션이 비어 있는 경우(isPresent()가 false를 반환함) map 메서드는 빈 옵션을 반환한다.
- 옵션이 비어 있지 않으면 mapper.apply(value)를 사용하여 매핑 함수가 값에 적용된다. 매핑 함수의 결과는Optional.ofNullable()을 사용하여 새 Optional로 래핑된다.
- 매핑 함수가 null을 반환하면 Optional.ofNullable()은 빈 Optional을 반환한다.
- 매핑 함수가 널이 아닌 값을 반환하는 경우, Optional.ofNullable()은 이를 새 Optional로 래핑하여 반환한다.
요약하면, map 메서드를 사용하면 매핑 함수를 사용하여 옵션의 값을 변환할 수 있다. 옵션이 비어 있으면 빈 옵션을 반환하며, 옵션이 비어 있지 않으면 매핑 함수가 값에 적용되고 결과는 새 옵션으로 래핑된다. 이 메서드는 옵션의 존재 여부를 명시적으로 확인하지 않고도 옵션 값에 대한 후처리를 편리하게 수행할 수 있는 방법을 제공하는 것이다.
동적쿼리 작성: 조건 검색
이 문제를 해결하는 것으로 지금 고민이 끝이 난것은 아니다. 나는 querydsl을 통해 동적 쿼리를 작성하는 메서드를 추가하기로 결정했다.
어떤 기능을 추가해야할까 고민을 하였고, 조건 검색 기능을 추가하면 좋을 것 같다는 생각이 들었다.
생각해보면, 상품을 검색할 때, 전체 상품을 조회하거나, 카테고리별로 상품을 조회하거나, 또는 특정 상품 자체를 조회하기도 한다. 근데 나는 지금 현제 전체 상품을 조회하는 기능이 없다. 그리고 특정 상품을 조회하는 방법으로는 ProductId 를 알아야만 가능하다. name, price 와 같은 상품의 특성을 활용한 쿼리가 누락된 것이다. 그래서 나는 개발되지 않은 이 기능들을 동적 쿼리를 통해 한번에 개발할 수 있을 것이라 판단하여, 해당 기능을 동적쿼리로 작성하기로 하였다.
이를 개발하기 위해서는 DTO가 필요하다. 그리고 여렇 이름 후보들 중, 내가 선택한 DTO 네이밍은 DynamicSearchProductQuery 이다. 조금 이름이 길지만, 조금 길더라도 명확하게 표현하고 싶었다. 이름이 길어 불편하다 할 수 있지만, IDE가 자동완성해주니 크게 불편함이 없을것이다.
그런데 이를 계획하니, 다른 고민이 생겼다. 오늘 해결한 쿼리가 새로 개발할 쿼리로 대체가 가능할 것으로 보이기 때문이다. 그래서 고민이다.. JPA Repository 에 적합한 쿼리와 Querydsl에 적합한 쿼리는 무엇일까.. 동적 쿼리의 문제점은 또 무엇일까? 생각이 많아진다.
이에 대한 지금 생각은, Query(조회)는 Querydsl에서, Command(상태변경)은 JPA Repository 에서 하는 것이 좋지 않을까?
알고리즘 문제를 풀면 느끼는 부분을 프로젝트를 개발하면서도 느낀다. 역시, 아직 기초가 부족하다.(공부하자 공부!) 그리고 이 유용한 것들을 완벽한 내 도구로 사용하고 싶다. 그리고 더 나아가 이런 멋진 도구를 만들고 싶다.
아 그리고 오랜만에 책을 새로 하나 샀다.
여기 있는 책들 중 가장 보고싶은 책은 하나 샀다. 원래는 지금 있는 책들을 읽고서 사려고했는데, 어제 승원이와 DDD 에 대한 이야기를 하면서, 실제 프로젝트 예시를 좀 더 보고싶어서 샀다. 너무설렌다. 재밌겠다...
'회고 > TIL' 카테고리의 다른 글
Product Adapter: DataAccess PR 작성, 알고리즘 문제풀이, 독서 (0) | 2023.07.17 |
---|---|
주간 한주 요약: 프로젝트 개발, 독서, 알고리즘 문제풀이, 행사 (1) | 2023.07.16 |
만들면서 배우는 클린아키텍처 독서: 테스트, Product Adapter(out.dataaccess) 개발 및 테스트, 알고리즘 문제풀이 (1) | 2023.07.12 |
원인 파악 및 삽질, 알고리즘 문제풀이 (0) | 2023.07.11 |
Product Adapter out 개발, 고민 (1) | 2023.07.10 |