해결된 질문
작성
·
223
·
수정됨
2
제가 람다식, 익명클래스, 제네릭에 대한 개념이 확실하게 안 잡혀서 코드이해가 너무 안 되길래 개념부터 다시 찾아보고 공부하고 있는데요 ㅠㅠㅠ 이렇게 공부하는 게 조금 과할 수 있지만 완벽하게 이해를 하고 넘어가고 싶습니다.
제가 궁금한 점은
query함수를 실행하면 sql이 적용된 결과데이터베이스 전체가 mapRow함수의 파라미터 rs로 들어가게 되는 것 같은데 데이터베이스의 자료형이 ResultSet인가요?
rowNum 변수의 필요성입니다. mapRow메서드를 오버라이딩을 해줄 때 rowNum사용을 전혀 안 하고 있는 것 같은데, 어떤 값이 rowNum으로 들어가고 어떻게 작동하는 건가요?
mapRow메서드의 반환값은 UserResponse객체 형태인데 결과적인 getUsers메서드의 반환값이 어떻게 List<UserResponse> 형태로 변환될 수 있나요? query함수의 역할인가요?
감사합니다.
답변 1
1
안녕하세요! J_ 님! 질문 주셔서 감사합니다! 😊 이렇게 공부하시는 것은 전~~혀 과하지 않습니다. 오히려 익명클래스 -> 람다식 -> 함수형 인터페이스로 이어지는 흐름은 자바 8 의 등장으로 변경된 핵심 개념이기 때문에 잘 이해하고 넘어가시면 좋습니다! 👍 그럼 본격적으로 하나하나 답변 드리기 전에 보다 자세히 query
함수를 살펴보겠습니다.
우선 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
는
함수형 인터페이스로, 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 함수의 매개변수 말고, 반환 타입을 잘 보면
List<T>
를 반환하고 있습니다!
아까 RowMapper
는 T
를 반환했는데, query 함수는 List<T>
를 반환하고 있으니 RowMapper
가 만드는 데이터들을 List
로 모아서 준다고 생각할 수 있죠.
이제 내부 구현도 한 번 봅시다. query 함수는
result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
라는 코드를 갖고 있는데요, 여기서 다시 한 번 또다른 query
함수를 부르고 있습니다.
그 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() 입니다!
우리는 예제에서 rowNum
을 사용하지 않았지만 상황에 따라서는 조회 결과의 짝수번째만 사용한다거나, 조회 결과 번호에 따라 다른 로직을 처리해야 할 때 사용할 수도 있을 겁니다!
라이브러리를 만드는 입장에서는 여러 사람이 사용할 수 있도록 해야 하니 이러한 기능도 넣은 것이라고 봐주시면 될 것 같습니다.
[3. mapRow메서드의 반환값은 UserResponse객체 형태인데 결과적인 getUsers메서드의 반환값이 어떻게 List<UserResponse> 형태로 변환될 수 있나요? query함수의 역할인가요?]
우리가 최초로 호출했던 query
함수는 RowMapper<T>
가 T를 반환하면 그것을 모아 List<T>
를 반환해 주었습니다.
그리고 그것이 가능한 이유는 RowMapperResultSetExtractor
덕분입니다. 방금 위에서 보신 extractData()
함수를 갖고 있는 클래스 인데요, extractData() 가 ResultSet
을 받아 List<T>
를 반환하고 있죠!
query
함수의 시그니처를 통해 List<T>
가 나오는 것을 유추할 수 있고, 그 내부 원리에는 RowMapperResultSetExtractor
가 있다고 생각해주시면 되겠습니다.
이렇게 우리가 사용하는 라이브러리나 프레임워크를 들어가보면, 우리가 궁금했던 내부 동작 원리를 조금 더 자세하게 확인하실 수 있습니다. 👍 열심히 공부해주셔서 감사합니다, 답변이 도움이 되었으면 좋겠습니다.
또 언제든 질문 편하게 남겨주세요!! 감사합니다! 🙇
우와 정말 감사합니다!!!!