우아한테크코스 레벨2에서는 스프링 프레임워크에 대한 학습을 진행하게 된다. 하지만 첫날 OT를 듣고 나니 개인적으로 스프링 자체보다는 처음 접하는 기술에 대해 학습하는 방법을 찾으려고 노력해야 한다는 것이 핵심이라고 느껴졌다. 생각해보면 아무리 지금 스프링을 열심히 공부해둔다고 해도 현업에 가서는 전혀 다른 기술을 사용하게 될 가능성이 더 높았다. 그럴 때 결국 도움이 되는 본질적인 스킬은 신기술을 올바르게 사용하는 방법을 빠르게 학습하는 것이라는 생각이 들었다. 그래서 개인적인 레벨2 목표를 신기술을 접했을 때 학습하는 방법을 찾고 연습하는 것으로 결정했다. (물론 스프링 자체에 대한 학습도 빼먹지 말자...!)
첫 번째 미션을 진행하는 중 Spring JDBC에 대한 학습 테스트를 진행하게 되었다. 학습을 진행하던 도중 다음과 같은 코드를 보게 되었다.
@Repository
public class UpdatingDAO {
private JdbcTemplate jdbcTemplate;
public UpdatingDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private final RowMapper<Customer> actorRowMapper = (resultSet, rowNum) -> {
Customer customer = new Customer(
resultSet.getLong("id"),
resultSet.getString("first_name"),
resultSet.getString("last_name")
);
return customer;
};
/**
* public int update(String sql, @Nullable Object... args)
*/
public void insert(Customer customer) {
String sql = "insert into customers (first_name, last_name) values (?, ?)";
}
/**
* public int update(String sql, @Nullable Object... args)
*/
public int delete(Long id) {
String sql = "delete from customers where id = ?";
return 0;
}
/**
* public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)
*/
public Long insertWithKeyHolder(Customer customer) {
final String sql = "insert into customers (first_name, last_name) values (?, ?)";
return null;
}
}
@Test
void key() {
Customer customer = new Customer("Leonor", "Watling");
Long id = updatingDAO.insertWithKeyHolder(customer);
assertThat(id).isNotNull();
}
아래에 있는 key 테스트를 통과시킬 수 있도록 위의 insertWithKeyHolder를 구현해야 했다. 하지만 여기서 학습에 대한 근본적인 의문이 들었다. 지금은 누군가가(이번의 경우는 우테코 코치님 브리!!) 정말 친절하게 학습해야할 기능과 그 기능을 수행할 수 있는 프레임워크의 키워드, 그리고 해당 키워드를 사용한 코드를 테스트할 수 있는 테스트 코드까지 제시해준 상태이다. 하지만 만약 현업에서 모종의 이유로 나온지 얼마 되지 않은 신기술을 사용해야 한다거나 기존 기술의 최신버전을 사용해야 하는 경우가 생긴다면 어떻게 될까? 참고할 수 있는 레퍼런스를 구글링으로 찾기 힘들것이며 오로지 의지할 곳이라고는 공식문서 밖에 없을 것이다. 그런 상황에서 어떻게 신기술에 대한 이해와 학습을 진행할 것인지에 대한 연습이 의식적으로 필요하다는 생각이 들었다.
그래서 Spring JDBC의 keyholder라는 키워드를 아예 모른 상태로 어떻게 공식문서를 통해 학습해서 내 코드로 적용할 수 있을까에 대해 고민하며 진행해보기로 결정했다.
먼저 JdbcTemplate에 대한 기본 지식이 있어서 이를 통해 update 기능을 수행하는 도중 update된 데이터들의 key를 반환해야하는 필요성이 생겼다고 가정했다. 일단 별다른 레퍼런스가 없기에 스프링 공식문서에서 JdbcTemplate의 update 기능을 설명하는 부분을 찾아본다.
어? 공식문서에는 update에 대한 기본적인 방법들만 명시되어 있다. 스프링에 구현되어 있지 않은 메서드는 결국 내가 구현해서 사용해야한다는 뜻인데... 직접 구현해야할 내용이 늘어난다는 사실에 머리가 살짝 어지러워진다. 과연 스프링이 이런 기능을 정말 구현해두지 않았을까...? 마지막 믿음을 가지고 공식문서를 다시 한 번 자세히 읽어본다.
역시 똑똑한 사람들이 만든 프레임워크에 이 정도만 구현되어 있을리는 없다. 공식문서에는 JdbcTemplate의 update 기능을 비롯해서 일부 몇가지 용법만을 다루고 전체 구현된 기능들은 javadoc을 참고하라며 링크를 걸어주고 있다. 해당 링크를 들어가서 명시된 update 내용들을 보니 더 많은 기능들이 구현되어 있는 것을 확인할 수 있다.
여기에서 공식문서를 통해 학습하는 팁을 하나 얻을 수 있었다. 공식문서에서 반드시 100% 모든 기능을 다루는 것은 아니라는 것이다. 만약 공식문서에 나와있지 않은 기능을 찾고자 한다면 먼저 공식문서에 명시된 기능이 전부인지 확인해보자. 만약 위의 사례처럼 일부 용법의 예시만 나와있는 거라면 javadoc 등 전체 API 구현 리스트(문서)를 찾아서 볼 수 있도록 하자.
다시 본론으로 돌아와서 update 구현 목록들을 살펴보다 보니 keyHolder라는 흥미로운 키워드가 보인다. keyHolder라는 객체 타입을 매개변수로 가지는 update 메서드가 있는데 뭔가 우리가 원하는 기능을 제공해줄 것 같다는 느낌이 든다.
해당 update 메서드의 Parameter 설명 부분을 보니 KeyHolder라는 객체는 생성된 키들을 저장하고 있는 객체라고 명시되어 있다. 드디어 우리가 원하는 update 기능을 찾았다. 그러면 이제 문제는 이 update 메서드를 어떻게 사용해야 하는지로 바뀌었다. 일단 KeyHolder라는 객체 타입을 전혀 모르니 걸려있는 링크를 타고 들어가 본다.
KeyHolder 클래스는 인터페이스라는 것을 알게 되었다. 그러면 우리는 update 메서드를 사용하기 위해 어떻게 동작하는지 아직 모르는 KeyHolder라는 인터페이스의 구현체를 직접 만들어줘야 하는 것일까? 우울해지려던 찰나 바로 아래 쪽에 All Known Implementing Classes라는 항목을 발견할 수 있다. 그렇다. 똑똑한 사람들은 사용자를 위해서 이미 구현체를 만들어 둔 것이다!
추가내용
필자는 본문에 서술한 방식으로 GeneratedKeyHolder라는 구현체를 알게 되었으나 다음과 같이 update 메서드 명세 부분에 맨 아래 부분을 보면 GeneratedKeyHolder에 대한 링크가 걸려있음을 확인할 수 있다. 어떻게든 사용자에게 구현체가 이미 있다는 사실을 노출시키려는 스프링 제작자들의 마음을 알아주도록 하자...!
이제 GeneratedKeyHolder라는 구현체의 존재와 어떻게 생성해서 사용할지도 알게 되었으니 update 메서드를 사용해보려던 찰나...
PreparedStatementCreator는 또 무엇인가...? 기존 update 메서드들이 String 타입으로 sql 문을 매개변수로 받던 것과 다른 메서드 시그니처를 가지고 있다. 일단 위에서 했던 것처럼 똑같이 링크를 타고 들어가본다.
당황하지 말고 아래를 다시 보면 함수형 인터페이스라는 설명이 붙어있다. 그렇다면 의도적으로 개발자에게 구현해서 사용하라는 의미를 담고 있다고 판단할 수 있다. 그러면 어떤 내용을 구현해줘야 하는 걸까?
해당 인터페이스는 JdbcTemplate 클래스에 의해 제공되는 connction을 이용해 PreparedStatement 객체를 만들어준다고 한다. 그러면 우리는 update 기능을 사용하기 위해 해당 기능을 구현해줘야만 한다는 결론에 이르렀다. 그런데 필자는 사실 PreparedStatement 객체와는 아직 친숙한 사이가 아니기 때문에 어떻게 해야할 지 조금 막막한 감정이 들었다. 과연 어떻게든 사용자들에게 사용법을 전달하려고 하는 스프링 제작자들이 이에 대한 예시 코드 하나 남겨놓지 않았을까...? 하는 의문이 들었다.
그래서 울며 겨자먹는 마음으로 스프링 공식문서에서 검색을 시도해봤다.
놀랍게도 아까는 발견하지 못했던 update 메서드 사용 예시를 발견할 수 있었다. 하마터면 JdbcTemplate 문서를 타고 들어가서 PreparedStatement 객체에 대해 열심히 탐구할 뻔 했다. 물론 이것이 나쁜 행위는 절대 아니지만 우리의 목적은 키를 반환하는 update 메서드를 어떻게 사용할 수 있는지가 목적이었기에 더 빠른 경로로 우리의 목표를 달성했다.
해당 과정을 통해 필자는 다음과 같은 학습 테스트 통과 코드를 작성할 수 있었다.
/**
* public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)
*/
public Long insertWithKeyHolder(Customer customer) {
final String sql = "insert into customers (first_name, last_name) values (?, ?)";
KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
this.jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, new String[] { "id" });
ps.setString(1, customer.getFirstName());
ps.setString(2, customer.getLastName());
return ps;
}, generatedKeyHolder);
return Objects.requireNonNull(generatedKeyHolder.getKey()).longValue();
}
필자는 이런 과정을 통해서 공식문서를 통한 학습에 대해 다음과 같은 팁을 얻을 수 있었다.
- 스프링 제작자들은 웬만큼 복잡하다고 느껴지는 기능에 대한 사용 예시 코드들은 공식문서에 써놨다.
- 똑똑한 스프링 제작자들을 믿어보자!
- 만약 공식문서에 나와있지 않은 내용이라고 판단되어 직접 API문서를 찾아보더라도 새롭게 얻게 되는 키워드들에 대해서는 최우선적으로 공식문서에서 검색을 해보는 습관을 들여보자.
사실 공식문서를 빠르게 훑어서 볼 수 있는 능력이나 목차만 보고도 어떤 내용이 있을지 감을 잡는 능력이 있다면 이런 삽질은 하지 않고도 원하는 기능을 찾아서 사용할 수 있었을 것이다. 하지만 필자는 스프링을 처음 접하는 주니어 개발자 지망생이기에 이러한 과정을 반복하며 그런 능력을 길러야만 하는 것이 아닐까하는 생각이 들었다.
처음으로 공식문서를 통해 어떤 기능이 구현되어 있는지 찾아보고 사용해보려고 시도해봤다. 생각보다 너무 많은 시간을 쏟았지만 그만큼 공식문서에 대한 두려움과 거부감은 조금 떨칠 수 있었던 기회가 되지 않았나 싶다. 앞으로 우테코에서 진행하는 모든 부분을 이렇게 진행할 수는 없겠지만 의식적으로 조금씩 연습해서 나중에는 공식문서를 끼고 살 수 있는 개발자가 되도록 노력해야겠다.
'우아한테크코스 > 학습 정리' 카테고리의 다른 글
[Level 2] Layered Architecture에 대한 개인적인 고찰 (0) | 2023.04.27 |
---|---|
[Level 2] Repository와 Dao를 분리하는 기준 (5) | 2023.04.21 |
[Level 1] 인터페이스와 추상클래스에 대한 개인적인 고찰 (0) | 2023.04.08 |
[Level 1] Dto 사용에 관한 개인적인 고찰 (3) | 2023.03.19 |
[Level 1] 좋은 객체의 7가지 덕목 (4) | 2023.03.11 |