• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

13강 마지막 부분 질문 있습니다.

24.02.16 22:55 작성 24.02.16 23:38 수정 조회수 166

2

제가 람다식, 익명클래스, 제네릭에 대한 개념이 확실하게 안 잡혀서 코드이해가 너무 안 되길래 개념부터 다시 찾아보고 공부하고 있는데요 ㅠㅠㅠ 이렇게 공부하는 게 조금 과할 수 있지만 완벽하게 이해를 하고 넘어가고 싶습니다.

제가 궁금한 점은

  1. query함수를 실행하면 sql이 적용된 결과데이터베이스 전체가 mapRow함수의 파라미터 rs로 들어가게 되는 것 같은데 데이터베이스의 자료형이 ResultSet인가요?

  2. rowNum 변수의 필요성입니다. mapRow메서드를 오버라이딩을 해줄 때 rowNum사용을 전혀 안 하고 있는 것 같은데, 어떤 값이 rowNum으로 들어가고 어떻게 작동하는 건가요?

  3. mapRow메서드의 반환값은 UserResponse객체 형태인데 결과적인 getUsers메서드의 반환값이 어떻게 List<UserResponse> 형태로 변환될 수 있나요? query함수의 역할인가요?

감사합니다.

 

답변 1

답변을 작성해보세요.

1

안녕하세요! J_ 님! 질문 주셔서 감사합니다! 😊 이렇게 공부하시는 것은 전~~혀 과하지 않습니다. 오히려 익명클래스 -> 람다식 -> 함수형 인터페이스로 이어지는 흐름은 자바 8 의 등장으로 변경된 핵심 개념이기 때문에 잘 이해하고 넘어가시면 좋습니다! 👍 그럼 본격적으로 하나하나 답변 드리기 전에 보다 자세히 query 함수를 살펴보겠습니다.image

우선 query 함수를 보겠습니다. (https://github.com/spring-projects/spring-framework/blob/main/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java#L480 에서 확인하실 수도 있고, IDE에서 ctrl + 클릭 혹은 command + 클릭으로 들어가실 수도 있습니다)

 

우리가 사용한 query 함수는 매개변수로

  • 문자열

  • RowMapper<T>

를 받습니다. 이 때 RowMapper

image함수형 인터페이스로, mapRow 라는 메소드를 하나 갖고 있는데요, mapRow는 또 다시

  • ResultSet 이라는 쿼리 결과와

  • rowNum 이라는 현재 쿼리가 몇 번째 결과인지

확인하는 매개변수를 갖고 있습니다. 이런 정보는 위에 있는 주석을 통해 알 수 있죠! 👍

 

우리는 query에 RowMapper 를 넣어야 하니

new RowMapper<T> {
  @Override
  public T mapRow(ResultSet rs, int rowNum) throws SQLException {
    return T(); // <T> 타입이라고 명시하면 T 를 반환해야 하고, User 타입이라고 적으면 User를 반환해야 합니다.
  }
}

위와 같이 익명 클래스를 쓸 수도 있고, 아래와 같이 람다를 활용할 수도 있습니다.

(ResultSet rs, int rowNum) -> {
  return T();
}

이때 ResultSet int 라는 타입을 쓰기 귀찮다면, 타입도 생략할 수 있죠.

(rs, rowNum) -> {
  return T();
}

(익명 클래스 -> 함수형 인터페이스의 람다식 흐름을 통해 위의 세 코드가 동일하다는 것을 느껴야합니다 👍)

 

또한 query 함수의 매개변수 말고, 반환 타입을 잘 보면

imageList<T> 를 반환하고 있습니다!

 

아까 RowMapperT를 반환했는데, query 함수는 List<T>를 반환하고 있으니 RowMapper 가 만드는 데이터들을 List로 모아서 준다고 생각할 수 있죠.

 

 

이제 내부 구현도 한 번 봅시다. query 함수는

result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));

라는 코드를 갖고 있는데요, 여기서 다시 한 번 또다른 query 함수를 부르고 있습니다.

image그 query 함수를 찾아 넘어가보면, QueryStatementCallback 이라는 클래스를 만들어 아래에서

return execute(new QueryStatementCallback(), true);

QueryStatementCallback을 이용해 무언가 execute - 실행하고 있습니다.

 

그리고 QueryStatementCallback 코드 안을 보니 핵심적으로 이런 부분이 있죠!

ResultSet rs = null;
try {
  rs = stmt.executeQuery(sql);
  return res.extractData(rs);
}

이 코드 안에서 우리가 적어준 sql을 실행하고 (executeQuery) 그 결과에서 (rs) 데이터를 추출 (extractDat)하고 있던 것입니다!!

 

자 이제 우리는 모든 질문에 대답을 할 수 있습니다! 😊

 

[1. query함수를 실행하면 sql 을 적용한 결과 데이터베이스 전체가 어떤 과정,형태로 mapRow함수의 파라미터 rs로 들어가게 되나요?]

  • 위에서 보신 것처럼 sql을 적용한 결과를 Jdbc 내부에서 데이터베이스에 쿼리를 보낸후

  • QueryStatementCallback 을 이용해 그 결과를 반환하여 mapRow 함수의 파라미터 ResultSet rs 에 넣어주고 있습니다.

  • 여기서 더더 디테일하게 궁금하시면, execute() 함수까지 따라가보시면 됩니다.

 

[2.rowNum 변수의 필요성입니다. mapRow메서드를 오버라이딩을 해줄 때 rowNum사용을 전혀 안 하고 있는 것 같은데, 어떤 값이 rowNum으로 들어가고 어떻게 작동하는 건가요?]

  • rowNum으로 들어가는 값은 현재 실행되고 있는 상황이, 데이터베이스 조회 결과에서 몇 번째로 얻을 결과인지 입니다.

  • 예를 들어 데이터베이스에 5개의 데이터가 있다면, 우리가 오버라이드 한 메소드는 총 5번 불리고 rowNum은 0, 1, 2, 3, 4가 들어오게 됩니다.

    • (코드를 더더 따라가시다보면 RowMapperResultSetExtractor 에서 이런 코드를 발견하실 수 있습니다.)

    • extractData() -> 아까 보았던 extractData() 입니다!

image

  • 우리는 예제에서 rowNum 을 사용하지 않았지만 상황에 따라서는 조회 결과의 짝수번째만 사용한다거나, 조회 결과 번호에 따라 다른 로직을 처리해야 할 때 사용할 수도 있을 겁니다!

  • 라이브러리를 만드는 입장에서는 여러 사람이 사용할 수 있도록 해야 하니 이러한 기능도 넣은 것이라고 봐주시면 될 것 같습니다.

 

[3. mapRow메서드의 반환값은 UserResponse객체 형태인데 결과적인 getUsers메서드의 반환값이 어떻게 List<UserResponse> 형태로 변환될 수 있나요? query함수의 역할인가요?]

  • 우리가 최초로 호출했던 query 함수는 RowMapper<T> 가 T를 반환하면 그것을 모아 List<T>를 반환해 주었습니다.

  • 그리고 그것이 가능한 이유는 RowMapperResultSetExtractor 덕분입니다. 방금 위에서 보신 extractData() 함수를 갖고 있는 클래스 인데요, extractData() 가 ResultSet 을 받아 List<T>를 반환하고 있죠!

image

  • query 함수의 시그니처를 통해 List<T>가 나오는 것을 유추할 수 있고, 그 내부 원리에는 RowMapperResultSetExtractor가 있다고 생각해주시면 되겠습니다.

 

이렇게 우리가 사용하는 라이브러리나 프레임워크를 들어가보면, 우리가 궁금했던 내부 동작 원리를 조금 더 자세하게 확인하실 수 있습니다. 👍 열심히 공부해주셔서 감사합니다, 답변이 도움이 되었으면 좋겠습니다.

또 언제든 질문 편하게 남겨주세요!! 감사합니다! 🙇

J_님의 프로필

J_

질문자

2024.02.17

우와 정말 감사합니다!!!!