인프런 커뮤니티 질문&답변

Gahee Kim님의 프로필 이미지
Gahee Kim

작성한 질문수

자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

16강. Section2 정리. 다음으로!

동적쿼리

작성

·

673

·

수정됨

1

 안녕하세요! 아직 강의를 다 듣지는 않았지만, 궁금한게 있어 질문드립니다! 제가 현재 진행해야 하는 프로젝트는 동적으로 쿼리를 생성하는 게 중요합니다. 테이블에 10개의 컬럼이 있다면, 2개를 선택해서 보여줄 수도, 3개를 선택해서 보여줄 수도 다양한 조합으로 보여줘야 합니다. 저는 node.js와 flask로 웹을 개발한 경험이 있는데요, 컬럼을 리스트(또는 배열)로 받고 for문을 돌려가면서 컬럼 and 컬럼 and ... 이런 식으로 문자열을 만든 다음 select문 문자열에 for문 돌리면서 만든 문자열을 넣어서 예를 들면 select {컬럼조합} from 테이블 이런 식으로 만든 뒤 실행시켰거든요. 문자열만 잘 작성하면 그 문자열대로 바로 실행이 되니 오히려 쉽다고 느꼈습니다. 근데 스프링부트는 쿼리 수행이 굉장히 정적이라는 느낌인데요....제가 아직 공부 중이라 잘은 모르겠으나 어디서는 조회하고자 하는 컬럼에 대한 인터페이스를 만들라고 하는데, 그 조합이 한두개가 아닌지라.....그걸 하나하나 만들 수도 없고요...이건 where조건도 마찬가지고 join도 마찬가지고요...다른 프레임워크에서는 쉽게 가능하던걸 springboot가 바보 프레임워크가 아닌 이상 지원 안해주는 건 말이 안될 거 같고 이런 걸 어떻게 해야 하는 걸까요?

답변 2

1

Gahee Kim님의 프로필 이미지
Gahee Kim
질문자

빠르게 답변 주셔서 감사합니다. 일단 해당 강의 후속 강의로 QueryDSL을 공부해봐야겠네요! 그전에 하나 더 궁금한게 있습니다. 제가 하는 프로젝트가 UI에서 원하는 쿼리 조건을 조합하여 정보를 입력하면, API 서버에서 DB에 해당 조건대로 데이터를 가져와야 합니다. 그러다보니 A라는 테이블에 10개 컬럼이 있을 때 어떨 때는 a, b만 어떨 때는 c,d,e 또 어떨 떄는 a에 조건을 넣어서 이런 식으로 굉장히 동적으로 데이터를 가져와야 합니다. 근데 제가 막혔던 부분 중 하나는 선생님께서 response 객체를 만들어서 넘기는데 이렇게 하면 저렇게 다양한 조합대로 response 객체를 만들 수가 없거든요.

return jdbcTemplate.query(sql, (rs, rowNum) -> {
    long id = rs.getLong("id");
    String name = rs.getString("name");
    int age = rs.getInt("age");
    return new UserResponse(id, name, age);
});

예를 들어 코드보면 id랑 name이랑 age를 return하는데 어떨 때는 id만 어떨 때는 id, name만 또 어떨 때는 id랑 age만 어떨 때는 id, name, age만 이런 식으로 가져와야하는데 저렇게 만들면 안되서요. response 객체를 안만들고 hashmap으로 만들까 했는데 이것도 먼가 잘 안되네요...

return userRepository.findAll().stream()
        .map(UserResponse::new)
        .collect(Collectors.toList());

이건 JPA 예제도 마찬가지고요.

public Interface UserInfoMapping {
	String getName();
    int getAge();
}

public interface UserRepository extends JpaRepository<User, Long> {
	List<UserInfoMapping> findAllById(Long id);		// 이름, 나이만!
}

제가 인터넷에서 본 에제인데 결과적으로 워낙 다양한 조합이 나올 수 있어서 먼가 저 위의 것이 맞지 않아보이고요.

 

분명 가능한 방법이 있을 거 같은데 이제 막 시작한 저로서는 좀 쉽지가 않네요.....querydsl로 하면 제가 원하는 부분이 해결 되는 걸까요?

 

최태현님의 프로필 이미지
최태현
지식공유자

위 내용의 강의는 Kotlin 기반으로 이루어져 있어서, Kotlin + Spring 조합을 사용하실게 아니라면, Querydsl 부분만 블로그로 찾아보셔도 충분할 거에요 ㅎㅎㅎ

 

A라는 테이블에 10개 컬럼이 있을 때 어떨 때는 a, b만 어떨 때는 c,d,e 또 어떨 떄는 a에 조건을 넣어서 이런 식으로 굉장히 동적으로 데이터를 가져와야 합니다.

에 대해서 조금 더 추가적인 의견을 말씀드려 보자면요!

이 요구사항은 사실 2가지로 나누어 생각해 봐야 합니다.

  1. 쿼리의 결과물을 필드 A / 필드 B / 필드 C의 다양한 조합으로 가져오고 싶다.

  2. 쿼리에 사용할 조건 (where condition 또는 join)을 다양한 조합으로 쓰고 싶다.

    1. where 필드 A = ? and 필드 B = ? 일 수도 있고, where 필드 A = ? 일 수도 있고 등등..

 

첫 번째 요구사항은 보통 서버에서 fit하게 해결하지 않습니다.

예를 들어, 어떤 상황에서는 유저의 이름만 필요하고 어떤 상황에서 이름과 나이가, 또 다른 상황에서는 나이가 필요하다고 생각해보겠습니다.

일반적으로 이런 경우에, 서버는 3개의 API를 만들지 않습니다! 단지 서버는 이름과 나이를 모두 전달해줄 뿐이고, 클라이언트 side에서 필요한 필드만 골라 사용하면 됩니다!

즉, 서버에서 필드를 세세하게 내려주는 경우는 거의 없으며, 만약 필드까지 완전히 fit하게 내려주고 싶으시다면 REST API 대신 GraphQL이란 방법론을 고민해보아야 합니다!

 

쿼리의 조건을 동적으로 처리하는 두 번째 요구사항은 Querydsl을 사용하시면 아주 유연하게 해결하실 수 있습니다. 👍

감사합니다!!

1

최태현님의 프로필 이미지
최태현
지식공유자

안녕하세요, Gahee님!! 좋은 질문 주셔서 감사드립니다!! 🙏

질문 주신내용을 정리해보면,

  • node.js 또는 flask에서는 문자열 쿼리를 문자열 연산 (ex. "A" + "A" = "AA") 으로 다양한 조건의 쿼리를 편하게 만들었으나

  • Spring Boot에서는 어떻게 동적 쿼리를 만들 수 있을지 모르겠다

인 것 같습니다! 😊

 

본격적인 답변을 드리기 위해 서버 애플리케이션에서 DB에 쿼리를 어떻게 날릴 수 있는지 그 방법론을 간단히 정리 드려 보겠습니다!!!

  1. 첫 번째 방법은 문자열 SQL을 사용하는 방법입니다.

    • 아마 node.js나 flask에서 사용하셨다는 방법이 이 방법이 아닐까 싶습니다.

    • 아무래도 가장 큰 장점이라면, 코드가 직관적이고 사용하기 쉽다고 생각이 되네요! Section2 까지 들으셨다면 사용해 보셨겠지만, Spring에서는 JdbcTemplate 을 이용해 처리할 수 있습니다.

    • 다만, 문자열 SQL은 몇 가지 단점이 있습니다. (23강에서 그 단점들에 대해 이야기 하고 있습니다!!)

    • 또한 SQL Injection과 같은 공격 방법도 문자열 SQL을 바로 사용할 때의 단점이라 할 수 있습니다.
      예시 : https://noirstar.tistory.com/264

  2. 두 번째 방법은 QueryMapper를 사용하는 방법입니다.

    • 문자열 SQL의 아쉬운 점을 일부 해결하기 위해 나온 방법론으로, 서버에 존재하는 객체와 해당 객체와 관련된 문자열 SQL을 적절히 이어준다고 생각하시면 됩니다!

    • Spring에서는 MyBatis 를 이용해 처리할 수 있습니다.

  3. 세 번째 방법은 ORM을 사용하는 방법입니다.

    • 첫 번째 방법과 두 번째 방법의 아쉬움 (23강에서 설명되고 있습니다!! 👍) 을 해결하기 위해 객체와 테이블을 매핑하는 방법입니다.

    • ORM을 적절히 사용하면 SQL을 직접 작성할 필요 없이 다양한 쿼리를 사용할 수 있게 되죠.

    • 아마 인터페이스를 만들라고 하는데, 그 조합이 한두개가 아닌지라.....그걸 하나하나 만들 수도 없고요...이건 where조건도 마찬가지고 join도 마찬가지고요... 라고 언급해주신 부분이 바로 이 기능으로 생각됩니다!

    • Spring에서는 JPASpring Data JPA 를 이용해 처리할 수 있습니다.

    • 하지만 말씀해주신 것처럼 동적쿼리를 비롯한 몇 가지 아쉬운 점이 존재합니다! 😭 그렇기 때문에 아래 4번째 방법이 존재하는데요!

  4. 마지막 방법은 정적 쿼리 생성 도구를 사용하는 방법입니다.

    1. Spring 진영에서는 대표적으로 QueryDSL이 있습니다. Querydsl 을 사용하시면 동적 쿼리를 type-safe하고 반복 가능하게 설계할 수 있습니다.
      예시 : https://jojoldu.tistory.com/394

 

Spring Data JPA 의 아쉬운 점과 QueryDSL에 대한 소개는 https://inf.run/wDut 강의 37강에서 나와 있습니다! 결제 없이 보실 수 있도록 해당 회차를 잠시 무료로 열어 두었습니다! 🙇

 

결론적으로 말씀드려 보자면, 스프링 애플리케이션으로 서버를 개발할 때에는 JPA (Spring Data JPA)QueryDSL 을 사용해서

  • 간단한 쿼리 작성 : Spring Data JPA

  • 복잡한 동적 쿼리 작성 : QueryDSL

을 사용한다고 생각해주시면 될 것 같습니다!

 

혹시나 또 궁금한 점 있으시면 편하게 질문 남겨주세요!! 감사합니다!! 🙏

Gahee Kim님의 프로필 이미지
Gahee Kim

작성한 질문수

질문하기