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

김한솔님의 프로필 이미지

작성한 질문수

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

15강. 유저 업데이트 API, 삭제 API 예외 처리 하기

15강 질문 드립니다!

23.04.01 14:48 작성

·

460

2

15강 람다식 질문 드립니다!

선생님 안녕하세요!

15강에서 작성해주신 람다식이 있는데

람다식을 사용하지 않은 코드도 댓글로 알려주실 수 있으실까요~!

자바 공부를 시작한 지 얼마 되지 않아서 혼자 해보려 해도 잘 안되네요..! 부탁 드리겠습니다!! 감사합니다!

답변 1

3

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

2023. 04. 01. 18:12

안녕하세요 한솔님! 질문 남겨주셔서 감사드립니다!! 😊

람다식 부분을 조금 더 알고 싶으셨군요~!! 👍 제가 Step 별로 조금 더 설명드려 보겠습니다.

 

[1. query 시그니처 살펴보기]

먼저 우리는 우리가 사용하고 싶은 라이브러리의 기능을 확인해보아야 해요!! 즉, JdbcTemplate.query 라는 메소드가 어떻게 생겼는지 알아야~ 해댱 기능을 사용할 수 있죠!

우리가 사용한 JdbcTemplate.query 를 눌러 보면 다음과 같은 메소드 시그니처를 갖고 있습니다. 메소드 시그니처란, 메소드의 이름, 메소드가 받는 매개변수, 반환 결과 등을 의미합니다!!

@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException {
  // 생략
}

보시면 JdbcTemplate 클래스의 query 메소드는 String 타입의 문자열과 RowMapper 타입의 객체, 그리고 Object...인 가변인자를 받고 있습니다! 여기서 가변인자란, 같은 타입의 값을 여러개 받을 수 있다는 의미입니다.

예를 들어,

public void doSomething(String... strs) {

}

라는 함수가 있으면 우리는 doSomething("A", "B", "C") 와 같이 문자열 여러개를 마음껏 넣을 수 있습니다.

자~ 다음 포인트는 RowMapper 인데요! RowMapper 타입의 객체를 넣어줘야 하죠~ RowMapper도 어떻게 생겼는지 코드를 눌러 확인해보면, 다음과 같이 생긴걸 알 수 있습니다.

@FunctionalInterface
public interface RowMapper<T> {

	@Nullable
	T mapRow(ResultSet rs, int rowNum) throws SQLException;

}

제네릭과 같이 어려운 부분도 있지만, 가장 직관적으로 알 수 있는 부분은 RowMapper 는 인터페이스이고! mapRow 라는 하나의 메소드를 가지고 있다는 겁니다!! 이제 우리가 사용할 query가 어떻게 생겼는지 알았으니, 이 기능을 한 번 사용해볼게요!

 

[2. 가장 원시적인 형태로 query를 호출해보기 작성해보기]

@PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user WHERE id = ?";
    jdbcTemplate.query(readSql, [ 여기가 바로 RowMapper가 들어갈 자리 ]], request.getId()).isEmpty();
}

우선 질문주신 RowMapper 부분을 빼놓고 코드를 작성해보았습니다! 여기까지는 이해하시는데 크게 어렵지 않으실거에요~

그럼 이제 RowMapper 를 넣어주어야죠!! 우리가 어떤 타입의 객체를 집어 넣을 때 해당타입의 인스턴스를 집어넣을 수도 있고 그 타입의 하위 클래스의 인스턴스를 집어 넣을 수도 있습니다. 객체의 다형성이죠~ 여기서 RowMapper 는 인터페이스이기 때문에 인터페이스의 인스턴스를 바로 집어 넣을 수 없고, 하위 클래스를 만들어준다음 그 클래스의 인스턴스를 넣어야 합니다.

자 그러면 이렇게 코드를 작성해볼 수 있겠네요

// UserController와는 아예 다른 파일에 위치한 클래스입니다!
// 여기서 <Integer>의 의미는 mapRow의 결과로 Integer를 반환하겠다는 의미입니다.
public class UserFindRowMapper implements RowMapper<Integer> {
  @Override
  public Integer mapRow(ResultSet rs, int rowNum) throws SQLException {
    return 0; // readSQL의 결과가 있으면 0으로 바꿔치기한다.
  }
}
@PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user WHERE id = ?";
    RowMapper<Integer> mapper = new UserFindRowMapper();
    jdbcTemplate.query(readSql, mapper], request.getId()).isEmpty();
}

우리는 RowMapper를 넣어주어야 하는데, RowMapper를 바로 넣을 수 없으니 (인터페이스라서요!) RowMapper를 implements 하는 클래스를 만들고 그 클래스의 인스턴스를 넣어주었습니다!

 

[3. 익명 클래스를 사용해 query 호출해보기]

자 그런데~ 당연히 이런 방법은 번거롭습니다! 새로운 클래스 파일을 만들기 얼마나 귀찮습니까~~ 그래서 바로 "익명 클래스"라는게 등장합니다! 익명 클래스란, 새로운 클래스를 만들지 않고도 어떤 인터페이스를 implements하는 클래스의 인스턴스를 만들어주는 문법입니다. 이걸 적용해보면 코드는 다음과 같이 바뀌게 됩니다.

  @PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user WHERE id = ?";
    // 여기가 바꼈습니다!!!!!
    RowMapper<Integer> mapper = new RowMapper<Integer>() {
      @Override
      public Integer mapRow(ResultSet rs, int rowNum) throws SQLException {
        return 0;
      }
    };

    jdbcTemplate.query(readSql, mapper, request.getId()).isEmpty();
  }

new 인터페이스 이름으로 익명 클래스를 사용해주었고요~ 아까 새로운 클래스 파일을 하나 만드는 것보다는 훨~~씬 낫습니다!!

 

[4. 람다식을 사용해 query 호출해보기]

자 하지만 개발자들은 여기서 만족하지 않았습니다. 이 조차도 너무너무 귀찮은거죠! 😅 그래서 어떤 인터페이스의 함수가 하나만 있는 경우는, "람다식"이란걸 쓸 수 있게 합니다.

예를 들어 RowMapperT mapRow(Result rs, int rowNum) 함수를 하나만 가지고 있죠!! 그러면 그냥~ 이렇게 @Override 쓰고~ return 0; 쓰고~ 할 필요 없이

RowMapper<Integer> mapper = new RowMapper<Integer>() {
  @Override
  public Integer mapRow(ResultSet rs, int rowNum) throws SQLException {
    return 0;
  }
};

이 코드를

RowMapper<Integer> mapper = (rs, rowNum) -> 0;

이렇게!

바꿔버렸습니다. 어차피 함수도 하나니까~ rs만 보더라도 매개변수 ResultSet rs 이란걸 알 수 있고 rowNum 만 보더라도 int rowNum 이란걸 알 수 있죠. 또한 { return 0; } 도 너무 귀찮아서 -> 0 으로 바꿔버렸습니다.

그래서 코드를 다시 한 번 작성해보면,

  @PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user WHERE id = ?";
    // 여기가 바꼈습니다!!!!!
    RowMapper<Integer> mapper = (rs, rowNum) -> 0;
    jdbcTemplate.query(readSql, mapper, request.getId()).isEmpty();
  }

이렇게 쓸 수 있고요!! 여기서 변수로 굳이 만들지 않고 바로 값을 넣어주면~

  @PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user WHERE id = ?";
    // 여기가 바꼈습니다!!!!!
    jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty();
  }

이렇게 바뀝니다 ㅎㅎㅎ

 

또 궁금한 점 있으시면 편하게 질문 남겨주세요!! 👍👍👍

감사합니다~!! 🙇🙇

김한솔님의 프로필 이미지
김한솔
질문자

2023. 04. 03. 12:55

자세히 설명해주셔서 감사합니다~!!