프리코스를 시작한지도 벌써 3주가 훌쩍 넘었습니다. 주차별로 회고를 쓰는 것이 목적이었지만 어째서인지(?) 잘 지켜지지 않았지만 3주차 미션을 하고 나서는 '무조건 써야겠다!' 라는 생각이 들어 회고를 작성해보네요.
이제껏 했던 코딩에서는 원하는 기능을 구현하는 것이 우선이었습니다. 하지만 이번 미션에서는 처음으로 개발의 전체적인 과정에 집중을 해보았습니다.
개발의 전체적인 과정?
아직 더욱 큰 범위는 알지 못하지만, 이번에 신경을 썼던 부분은
입니다. 처음에는 정말 막막했지만 공부하고 다른 분들의 2주차 코드리뷰를 해보며 점차 감을 잡았고, 설계를 하고 하나하나 구현을 해보면서 결국 완성을 할 수 있었습니다. 이 과정의 기억을 진심으로 잊고 싶지 않았기 때문에 이 회고를 꼭 작성해보고 싶었습니다.
이번 미션을 위해 설계를 하는 과정에서 꽤 많은 시간이 걸렸습니다. MVC 패턴에 5 Layer Architecture의 Service를 추가시켜서 구성했는데, 각 구성요소들의 역할에 대한 정의가 제대로 확립되지 않았던 것이 가장 큰 이유였습니다. 그래서 먼저 설계를 하며 가졌던 의문에 대해 하나하나 정리해보겠습니다.
MVC 패턴을 보며 자주 등장하는 단어였습니다. 하지만 몇 개의 글을 보면서도 잘 이해가 가지 않았고 이해하는데 꽤 시간이 걸렸습니다. 지금 이에 대해 이해한 뜻은 다음과 같습니다.
A가 B에 의존한다.
= A가 B를 알고 있다.
= A가 B의 코드를 가지고 있다.
= B의 코드를 수정했을 때 A가 영향을 받을 가능성이 있다.
package View;
public static void printLotto (Lotto lotto) {
System.out.println(lotto.getNumbers());
}
위의 코드는 View에서 출력하기 위해 Lotto 클래스의 인스턴스를 직접 인수로 받고 있습니다. Lotto는 domain요소로 Model에 속하기 때문에 위와 같이 코드를 짤 경우 'View가 Model에 의존한다' 'View가 Model의 존재를 알고 있다' 라고 할 수 있는 것입니다.
Controller와 Service처음에 MVC를 접하며 Controller에서 프로그램의 로직을 담당한다 라고 배웠었습니다. 그 후 Service가 있는 5 Layer Architecture에 대해 보았고, 이 아키텍쳐에서는 Service에서 로직을 담당한다라고 말했습니다. 이 개념을 본 후 든 의문은
그럼 Controller 는 필요없는 것인가?
였습니다. 저는 Controller에 들어있는 모든 것이 로직이라고 생각했었기 때문입니다.
하지만 공부를 하다보니 Controller에는 로직 뿐만 아니라 View를 호출하고, Model에서 데이터를 가져오는 프로그램 흐름 또한 담당하고 있다는 사실이 느껴졌습니다. 이 사실을 깨닫고 '비지니스 로직' 이라는 개념 또한 한번 더 확실하게 이해할 수 있었습니다.
즉, Service 는 Controller가 맡고 있던 역할 중 비지니스 로직을 분리한 것이였습니다. MVC 패턴으로만 프로그램을 구현할 경우 Controller가 너무 거대해진다는 말이 있었는데 그 이유는 프로그램 로직과 비지니스 로직을 같이 가지고 있기 때문이었습니다. 그러면 이번 로또 미션에서 구현했던 하나의 Controller를 예로 들어보겠습니다.
public class LotteryController {
private static LotteryDrawService lotteryDrawService = new LotteryDrawService();
public static void draw() {
List<Integer> winningNumbers = InputView.takeWinningNumberInput();
OutputView.insertLineBreak();
int bonusNumber = InputView.takeBonusNumberInput();
OutputView.insertLineBreak();
WinningNumber winningNumber = lotteryDrawService.createWinningNumber(winningNumbers, bonusNumber);
lotteryDrawService.updateResult(winningNumber);
}
}
이 부분은 로또의 추첨기능에 대한 Controller 입니다. 보시면 Controller는 프로그램 순서에 맞게 View와 Service를 호출할 뿐 어떠한 비지니스 로직도 갖고 있지 않습니다. 즉, 추첨을 할 때 로또 시스템안에서 어떤 일이 벌어지는지는 Controller는 알지 못한다는 말이죠. 따라서 로또 시스템안에서 벌어지는 일을 비지니스 로직이라고 할 수 있고 이는 LotteryDrawService 에서 처리를 하게 됩니다.
이번 미션 전까지 제가 알고 있던 단위테스트의 필요성은 모두 글로 배운 지식이었습니다. 하지만 이번 미션을 구현하면서 온 몸으로 체감할 수 있었습니다. 어떤 점에서 이렇게 느끼게 됐는지 정리해보겠습니다.
사실 단위테스트에 관련된 글을 보면서 들었던 생각 중 하나가 '저렇게 간단한 기능인데 오류가 날 일이 있을까?' 였습니다. 하지만... 그 일은 바로 제 코드에서 일어났습니다. 초반에 의무적으로 작성했던 테스트 코드에서 테스트 통과가 되지 않은 것입니다. 만약에 테스트를 안하고 완성을 했을 때 에러가 났다면... 어디서부터 봐야될지 정말 막막했을 것 같습니다. 연습이 아니라 실무였다면 간단한 실수를 커버하지 못한 것은 곧바로 큰 손실로 이어질 것입니다.
한 질문에 대해서 생각해보겠습니다.
Q. 간단한 기능이었는데도 왜 실수가 나는 것일까요?
이 질문에 대해 '테스트' 라는 친구는 과연 어떻게 대답할까요? 제 생각엔 이렇게 대답할 것 같습니다.
테스트 : 질문 자체가 잘못됐어! 실수는 어디서나 특별한 이유 없이 날 수 있다는 것을 인정해야 돼. 너흰 로봇이 아니잖아?
(물론 그렇다고 실수는 남발하면 안되겠죠...?)
어떤가요? 보는 분마다 다르게 느끼실 수도 있겠지만 이번 미션을 통해 느낀바를 '테스트' 친구의 입을 빌려 얘기해보았습니다. (다른 의견이 있으시다면 댓글 남겨주시면 감사드리겠습니다!)
글재주가 없어 내용이 이리저리 하긴 했지만, 한줄로 정리하면서 이 부분을 마치겠습니다.
코드를 짤 때는 항상 실수할 확률이 존재하고 실수를 했을 때 그것을 가장 적은 비용으로 막을 수 있는 것이 단위테스트입니다.
이번 미션을 하면서 기능 하나를 구현할 때마다 바로바로 테스트 코드를 만들면서 진행하였습니다. 그러다보니 여러기능들이 모인 Service 를 구현할 때 '제대로 동작할까?' 에 대한 불안감이 거의 없었던 것 같습니다. 사실 당연한 말일 수도 있어요. 하지만 그건 어디까지나 단위테스트를 했을 때의 얘기라고 생각합니다. 테스트를 하지 않고 Service를 구현했다면 굉장히 불안했을 것 같습니다. 그리고 그 것은 곧 생산성에 영향을 미치게 될 것입니다. 왜냐하면 에러의 가능성을 확인하기 위해 이미 작성되었던 코드를 살펴봐야 하기 때문입니다.
심리적 안정감이 중요한 이유는 하나 더 있습니다. 그건 바로 '즐기기 위함'입니다. 코딩하는 내내 불안해 하며 작업을 한다면... 오래 일을 할 수 있을까요? 그에 대한 걱정을 최소화해서 과정 자체를 즐기면서 하는 것이 일을 위해, 또 저희 자신을 위해 좋을 것 입니다.
의도가 분명히 있는 코드를 작성하자.
의도가 정확하든 틀리든 상관없습니다. 변수명, 메서드명, 코드 한줄 등 작성한 모든 코드에는 왜 그렇게 작성하였는지 이유를 밝힐 수 있어야한다고 생각합니다. 이번 미션을 통해 느낀 이유는 2가지 입니다.
1번은 어느정도 공감을 하실 것 같습니다. 의도가 분명히 드러나는 코드는 읽을 때 이해하기 쉬우니까요. 그렇다면 배우는 것과는 어떤 관계를 가질까요?
남에게 설명을 하려면 저부터 정확하게 알고 있어야합니다. 실제로 설계를 시작할 때 많은 자료를 보면서 제가 구조로 택한 MVC에 대해 설명할 수 있게끔 공부를 하였고, 각종 네이밍을 할 때도 컨벤션이 있는지 없는지 항상 찾아보게 되었습니다. 이에 대한 효과는 단순히 가독성 좋은 코드를 짜는 것에 그치지 않고 성장으로 이어지게 된다는 것을 느꼈습니다.
저는 이 것을 다른 분들과 코드 리뷰를 하면서 느꼈습니다. 코드 리뷰를 받으면서 다른 분들이 남긴 왜 라는 질문에 대해 제 의도를 가능한 분명하게 설명을 해보았습니다. 그 결과 제가 잘한 점과 잘못한 점에 대해서 더욱더 명확하게 피드백을 받을 수 있었습니다. 또한 의견이 갈려서 토론을 해야할 때도 정확한 주제로 토론을 할 수 있어 굉장히 건설적인 이야기가 오고 갔습니다. 이를 통해 저는 코드리뷰를 주고 받으며 구현하는 것이 아님에도 불구하고 정말 많이 배울 수 있었습니다.
많은 분들이 겪어 보셨을 것이라 생각합니다.
나는 이렇게 했고 다른 분은 저렇게 했는데 뭐가 맞는거지?
이에 대한 답을 찾기 위해 많이 찾아보고 서로 많은 이야기가 오고 갑니다. 이 과정을 통해 어느 쪽이 맞다라고 결론이 나왔던 주제가 있었지만, 다른 한편으로 아무리 이야기를 해보아도 어느 쪽이 맞는지 정할 수 없는 주제도 있었습니다. 그리고 맞다고 생각한 쪽이 오히려 틀렸던 적도 있었습니다.
그렇다면 맞는 결론으로 마침표를 찍은 토론만이 의미있는 토론일까요? 이 질문에 대한 답을 이번 미션을 통해 어느정도 답을 내리게 되었습니다. 제가 생각한 답은 '아니요' 입니다. 피어 리뷰 스터디를 진행하며 위에서 언급한 3가지 종류의 토른이 다 나왔었지만 어떤 토론이든 하나 빠질 것없이 중요한 토론이었습니다. 답을 찾는 것도 중요하지만 더 중요하다고 느꼈던 것은 '사고의 확장' 입니다. 다른 사람들의 의견을 통해 제가 생각할 수 있는 선택지가 늘어나게 되면 그만큼 정확한 판단을 할 확률이 높아지는 것입니다. 궁극적으로 커뮤니케이션을 통해 이 확률을 높이는 과정이 제가 이번에 느끼게 된 답을 찾아나가는 과정입니다.
개발 트렌드는 끊임없이 변화합니다. 어제는 A가 맞았었는데 오늘은 B가 맞을 수도 있죠. 그러면 이를 정하는 것은 누구일까요? 누구 한명이 나서서 정하는 것이 아닌 개발자들의 목소리가 합쳐져 만들어낸 결과입니다. 만약 제 목소리가 포함이 되어있었다면 저는 의도하지 않아도 트렌드를 따라가게 되는 것입니다. 그래서 저는 이번 미션을 통해서 지속가능한 개발자가 되기 위해 '커뮤니티'와 '소통'이 얼마나 중요한지 한번 더 깨닫게 되었습니다.
조금 빠진 부분이 있을 수도 있겠지만 3주차 미션을 하며 느낀 모든 중요한 점들을 기록으로 남겨 놓을 수 있어 너무 뿌듯합니다. 쓰면서 다시 생각해보아도 하나하나 느낀점들이 소중하고 계속 발전시켜나가야 하는 점들이었습니다.
이제 현재 진행 중인 마지막 주차의 미션만 남았네요. 이 미션도 끝까지 최선을 다해서 3주차 만큼, 또는 이상으로 많이 배우고 성장했으면 좋겠습니다. 🔥
마지막으로 이러한 기회를 많은 사람들에게 제공한 우테코에게 정말 감사드립니다 😊
잘보고갑니다!