테스트하기 쉽도록 객체 쪼개기 / ProxySQL의 캐치프라이즈(문제 해결 전에 문제 인식부터. 문제 인식 전에 도메인부터) / 설계를 뒤엎는 것은 도메인을 조금 더 이해한 것이다
-
한 일
뭔가 요즘 하루는 꽉꽉 채워서 사는 것 같은데 자바 공부는 따로 못하고 있다.
시간을 누수하는 포인트가 있나? 요즘 운동을 자주 가긴 하는데 운동에 다녀오면 공부에 집중하기 힘들다. 매일 가지는 말고 날짜를 좀 조정해 봐야겠다.
배운 것
테스트하기 쉽도록 객체 쪼개기
이건 어디서 권장하는 스타일은 아니고, 내가 개발한 야매 비법이다.
메서드를 단위 테스트할 때 가끔 주입받는 객체에 따라서 테스트하기 어려워질 때가 있다.
예를 들어, 다음과 같은 코드가 있다고 하자
class A {
public Something doSomething(Location location) {
int x = location.getX();
int y = location.getY();
return makeSomething(x, y);
}
}
그런데 문제는 Location 객체를 생성할 때, 제어할 수 없을 때가 있다. 혹은 Location 생성에 필요한 포함된 객체에 필요한 내용이 너무 많은데, doSomething에 필요한 x, y 속성 외에 테스트와는 관련 없는 내용들이 추가되어 테스트 비용이 너무 비싸진다는 문제가 발생한다.
이런 경우에 이렇게 테스트할 수 있다.
class A {
public void doSomething(Location location) {
doSomething(location.getX(), location.getY());
}
public Something doSomething(int x, int y) {
return makeSomething(x, y);
}
}
메서드 오버라이딩을 통해 객체는 그대로 받을 수 있도록 해 프로덕션에서는 그대로 사용하고, 필요한 부분만 꺼낸 부분을 다시 정의해 이 부분을 테스트하는 것이다.
이러면 테스트 코드도 훨씬 간단해지고, 테스트 작성 이전에 객체 생성에 대한 피곤함도 줄어든다.
그런데 막상 이런 경우는 자주 겪는 건 아니고, location의 getter()를 사용해야 하는 특이한 경우에만 사용된다. 보통 같으면 location에다가 메서드 호출 책임을 양도할 것 같긴 하다.
문제점은 public 네임스페이스가 너무 더러워지는 것이 있다고 볼 수 있다.
문제를 해결하려면 인식부터
https://proxysql.com/
모 개발 커뮤니티에서 위와 같은 사진에서 왜 ProxySQL에서 "Scale 100K+ connections across thousands of servers"를 캐치프라이즈로 내세우는지에 대한 퀴즈가 나왔다.
이를 풀기 위해 여러 답을 제시했는데 결국 풀지는 못하고 출제자 분께서 답을 알려주셨다.
내가 어떻게 풀어나갔는지 회고해보자.
처음에는 ProxySQL이 가진 특징으로 답을 유추해 보았다. 왜냐하면 ProxySQL이 가진 특징과, ProxySQL에서 내세우는 본인 제품의 특징 간 유사점이 존재한다고 생각했기 때문이다.
이번에는 Proxy라는 특징이 가지는 특성과 + 캐치프라이즈의 문구와 어떤 내용이 겹치는지에 대해 생각하고 이에 대해서 유추하기 시작했다.
이때까지도 감을 잘 잡지 못하고 있었다. 그냥 특징들을 짚고 있었다. 여기서 진짜 중요했던 건, ProxySQL이 해결하려고 했던 문제는 어떤 문제였고, 그 문제를 어떻게 해결해서 이러한 캐치프레이즈를 내세웠던 걸까?를 궁금해 했어야 했던 것 같다.
결국 답을 못풀어서 출제자 분께서 답을 알려주셨다. 여기서 생각했던 건, 아 MySQL은 다른 DB들과 비교해서 어떤 문제점이 있으니까 ProxySQL이 그 문제를 풀려고 시도를 했구나. 즉, 문제 인식부터 똑바로 해야 하는데 그 전에 있어 도메인 지식(여기서는 MySQL 아키텍처)이 있어야 해결하려는 문제에 대해 공감하고 제대로 인식할 수 있구나. 하는 생각이 들었다.
카뱅에 있을 때 항상 들었던 말이 "해결하려는 문제에 대해 먼저 생각하라"였다. 근데 나 같은 경우 도메인 지식이 부족하다 보니 문제에 공감할 수 없었다. 따라서 주위 사람들에게 그 도메인에 대해 끊임 없이 물어봤어야 했던 것이다.
오늘 티키타카를 하면서 그제서야 그 말뜻을 어느정도 이해한 듯 싶었다.
또 한가지, 아는만큼 보이기 때문에 어디서든 겸손해야 한다는 태도다.
MySQL에서는 스레드 단위로 작업을 할당하는데, 이때 캐시의 경우 글로벌 메모리와 로컬 메모리로 나뉘어져 로컬 메모리는 스레드 끼리 공유하지 못한다는 단점이 있어 성능상 이슈가 있다고 한다.
Real MySQL 4.1.3.2를 읽어보면 로컬 메모리 영역에 대해 말하는데, sort buffer / join buffer / bin log cache / network buffer와 같은 영역들이 로컬 메모리 영역이다.
영역들 하나하나가 캐시하지 못하면 성능이 뚝뚝 떨어질 듯한 이름을 가졌는데, 나는 Real MySQL을 2번 읽었음에도 불구하고 여태 메모리가 글로벌 영역과 로컬 영역으로 나뉘어지는지도 몰랐다. 캐시라고 하면 InnoDB 버퍼풀 하나만 존재해서 거기서 모든 캐시 내용을 공유할 것이라 생각했다.
정말 아는 만큼 보인다는 말이 딱 맞는 것 같다...
느낀 것
설계하다 날리는 것은 시간 낭비한게 아니다
요즘 들어 느끼는 거지만, "TDD라는 것은 결국 코드를 짜기 전에 도메인에 대해서 완벽히 분석했니?"라고 수시로 되묻는 과정이라는 생각이 든다.
TDD를 기반으로 out -> in 방식으로 코드를 짜다보면 설계가 잘못되어서 수시로 갈아 엎고는 한다.
그런데 오늘 포비가 올린 강의 영상을 다시 보면서, "설계하다가 날리는 것을 시간 낭비한 것이 아니라 도메인에 대한 이해를 높인 것이라는 생각을 하라"고 말하는 부분에서 큰 감명을 얻었다.
이때 얻은 도메인에 대한 이해를 얻을 수 있고, 객체 설계를 더 잘해질 수 있다. 꼭 한 번에 완벽하게 설계하려고 시도하지 않았으면 좋겠다는 말을 덧붙였는데, 평소 테스트와 코드를 많이 뒤엎는 나에게서는 많은 공감이 되었다.
특히, 코드를 뒤엎을까봐 '아 설계를 완벽하게 하지 않으면 테스트는 잘 짜지 못하겠어' 하는 생각이 종종 들 때가 있는데, 이런 마인드에도 도움이 된 것 같아.