오늘 CodeKata는 3문제를 풀었다. 이제 1문제만 더 풀면 LV5는 다 푼다. 오늘 푼 문제는 전부 Euclidean 관련 문제였기에 최대공약수를 구하는 함수만 여러번 우려먹었다. 그것과 별개로 마지막에 Corner Case를 생각 못해서 코드를 우려먹었음에도 3문제밖에 못 풀었다.
이어서 과제를 수정을 하였다. 수정 대상은 이하 코드이다.
fun findByWriterUsernameOrderByCreatedDateAsc(pageable: Pageable, username: String): List<Todo>
fun findByWriterUsernameOrderByCreatedDateDesc(pageable: Pageable, username: String):List<Todo>
fun findByOrderByCreatedDateAsc(pageable: Pageable): List<Todo>
fun findByOrderByCreatedDateDesc(pageable: Pageable): List<Todo>
이것을 이번에 배운 QueryDSL로 바꿔서 동적으로 바꾸었고, 그 과정은 다소 험난했디.
일단 시작부터 종속성이 꼬였다. 이전에 QueryDSL을 해보려다가 그냥 내버려둔 적이 있어서 gradle에 이전에 추가한 QueryDSL 종속성이 있었는데, 이것 때문에 이번에 추가한 종속성과 충돌이 나서 작동이 되어야하는 코드가 작동이 안되었다. 강의 코드를 복사 + 붙여넣기를 해도 되지 않아서 Java 코드까지 본 결과, 종속성이 꼬인 것을 확인 할 수 있었다.
그래서 이전에 추가한 종속성을 지우고 나서야, 제대로 작동을 했다.
그러고나서, 구현 방식에 대해 고민을 했다. 이전 코드에선 writer만을 filtering을 하였는데, 이번 코드는 동적으로 Query를 바꾸는 김에, 여러 인자를 활용해 filtering을 하고 싶다는 욕심이 났다. 즉, 저 위의 4개의 메소드를 하나의 메소드로 더 유연하게 통합을 하고 싶었다.
우선 나는 String을 활용해 해당 값의 이름을 가진 특정 객체의 메소드를 부를 수 있는지 찾아보았다. 안타깝게도, 찾기가 어려워서 차선책을 택했다. 비교 대상을 Map으로 저장한다. 즉, post에 title을 Pair("title", post.title) 이렇게 저장을 한다. 어차피 문자열만을 비교할 것이 때문에 Map의 자료형은 Map<string, StringPath>가 된다.
이렇게 저장을 하고 BooleanBuilder에서 사용할 수 있고, contains(특정 문자열 존재 여부 확인) 메소드를 가진 자료형이 무엇인지 알아보기 위해서, 이전에 contains를 사용한 코드를 찾아서 Ctrl로 코드를 올라가 보았다. 그렇게 StringExpression이란 자료형이란 것을 알게 되었고, StringPath를 StringExpression으로 자료형을 변환할 수 있는지 as를 사용하였다. 일단 IDE 상으로 문제가 없었기에 다음 단계를 건넌다.
이제 검색 키워드를 Controller로 부터 받아와야한다. 나는 단순하게 Map을 사용해서 검색 키워드를 받아오겠다고 생각을 했다. 처음엔 직접 Controller에서 Map을 받아오려다가 다소 애먹다가, 이전에 List를 DTO로 감싸서 수월하게 구현을 한 것을 떠올려서 DTO로도 감싸보기도 했다. 그런데 DTO을 사용하다보니 Map을 사용하기 보단 그냥 DTO의 필드 멤버를 사용하는 게 익숙하고 훨씬 낫지 않은가? 란 머리 속에서 번뜩였다. 어찌보면 내 머리는 꽉 막힌 모양이였다. 이 과정에서 또 GET에 RequestParam을 사용해서 문제가 발생했던거는 잊도록하자.
하지만, 이렇게 DTO를 사용하면 문제가 있었다. 내가 기존에 구현을 한 것은 Map을 가져오는 것을 상정해서 구현했는데, DTO를 사용하면, 이것을 어떻게 수정을 어떻게 할 지가 고민이 되었다. 즉, DTO를 Map으로 변환해야한다. 그냥 DTO를 사용하면 기존의 유연한 코드의 의미가 퇴색된다가 생각을 해서 결국 DTO를 Map으로 변환하는 법을 구글링을 해보았지만, 편한 답은 보이지 않았다. 대신에 DTO Class의 멤버변수들 이름을 얻고, 해당 변수의 특정 인스턴스의 값을 얻는 법을 알게 되었고, 즉각 사용하였다.
그렇게 한 결과 무사히 QueryDSL을 사용한 Repository를 구현할 수 있었다. 처음 생각한 코드보다 확장성이 떨어지지만, 몇 시간에 걸쳐서 만든 코드 몇 줄은 보람찬 일이 되었다.
private val keywordMap = mapOf(Pair("title", todo.title),
Pair("writer", todo.writer.username), Pair("content", todo.content))
override fun getTodos(pageable: Pageable, orderByAsc: Boolean, searchKeywordDTO: SearchKeywordDTO?): Page<Todo>
{
var whereClause = BooleanBuilder(todo.id.isNotNull)
searchKeywordDTO?.also {
whereClause = BooleanBuilder()
SearchKeywordDTO::class.memberProperties.forEach(){
search -> whereClause.or((keywordMap[search.name] as StringExpression)
.contains(search.get(it).toString()))}
}
val content = queryFactory.selectFrom(todo)
.where(whereClause)
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.orderBy(*getOrder(pageable, todo, orderByAsc))
.fetch()
return PageImpl(content, pageable, queryFactory.select(todo.count()).from(todo).fetchOne() ?: 0L)
}
그 밖에 AOP를 적용하려다 실패하기도 했다.
다음주부터 다시 팀 프로젝트다 이번엔 열심히 한 명도 빠짐없이 열심히 참여했으면 좋겠다. 이왕이면, 비슷한거나 더 높은 실력을 가지신 분과 팀을 맺어서 실무에 가까운 프로젝트를 진행하고 싶다.
'부트캠프 일지' 카테고리의 다른 글
부트캠프 40일차 후기 (1) | 2024.01.23 |
---|---|
부트캠프 39일차 후기 (1) | 2024.01.22 |
부트캠프 36일차 후기 (1) | 2024.01.17 |
부트캠프 34일차 후기 (1) | 2024.01.15 |
뉴스피드 프로젝트 회고 (0) | 2024.01.15 |