테스트 코드 작성하면 좋은 점

코드를 수정하고나서 풀리퀘를 올렸는데 테스트가 실패한 것을 보고 내가 놓친 부분을 찾아서 수정하는 경험을 했었고 테스트의 가치를 피부로 느낄 수 있었다. 우리 팀은 커버리지를 KPI로 잡는다. 업무 시간의 20%는 테스트 작성, 빌드 타임 개선, 모듈화 등의 기술 과제에 할당한다. 테스트 코드를 작성하고 유지하는 경험을 통해 느낀 점이 있다.

더 나은 아키텍처를 좇게 한다

테스트가 어려운 코드는 좋지 않은 구조일 가능성이 매우 높다. 테스트 가능한 코드는 의존성에 대한 고민이 녹아있다. 특히 의존성 역전 원칙을 철저히 지키면서 소스 코드의 의존성을 정리하고 관리해야 하는데 이에 대해서는 엉클밥이 의존성 규칙dependency rule을 통해 잘 설명하고 있다.

내 코드 개밥먹기dogfooding

테스트를 작성하면 본인이 짠 코드를 사용자로써 직접 체험해볼 수 있다. 마치 집을 짓고 내부를 꾸미다가 밖으로 나와서 외부에서는 어떻게 보이는지 확인하는 것이다. 창문은 어떻게 생겼으며 문은 어떻게 열고, 입구는 어디에 있는지 등등. 창문과 입구는 public API와 같다. 집 내부는 private 요소들이다. 테스트에서 객체의 동작을 검사할 때는 public API를 쓴다. 객체를 직접 생성도 해보고 설정을 하고 public 메서드를 호출 하거나 프로퍼티에 접근해본다. 그러면서 내가 만든 코드의 사용성을 확인해볼 수 있다. API가 엉성하면 테스트를 작성하기도 까다롭고 고통스럽다. ‘이런 것까지 테스트해야돼?’ 또는 ‘이 케이스는 왜 이렇게 테스트하기가 어렵지?’ 등등 여러 상황을 맞닥뜨린다. 따라서 API의 사용성에 신경쓸 수 밖에 없고, 신경 쓰다보면 더 심플하고 직관적인 API를 만들 수 있게 된다. 더 심플하다는건 여러가지 의미가 있는데 내 생각엔 public 메서드나 프로퍼티의 갯수가 적은게 특히 중요한 것 같다.

심플하고 직관적인 API 설계

기능 개발 → 테스트 작성 → 기능 수정 → 테스트 수정의 과정을 반복하다보면 API를 만들때 설계 단계에서 미리 큰 그림을 그려볼 수 있는 근육이 생기는 것 같다. 가령 기능 수정을 조금 하고나니 기존 테스트를 전부 폐기하고 새로 짜야할때가 발생한다. 이러면 설계가 처음부터 좋지 않았을 가능성이 높다. 쉽게 바뀌지 않는 정책적인 것을 public으로 공개하고, 상세 구현부는 숨기거나 외부로 옮겨야 한다. 설계가 잘못되면 테스트를 작성하는게 불가능하거나, 어찌저찌 되더라도 유지하는게 괴롭다. 이런 일이 반복되면 객체의 구조나 역할, 테스트 가능성testability을 고려할 수 밖에 없다. 무엇을 이 객체 안에 넣어야하며 무엇을 빼야하고, 어떻게 의존성을 둬야할지 고민하게 한다. 어떻게 코드를 설계해야할지 막막하거나 어떤 패턴이 더 나은지 판단하기 어렵다면 테스트 코드를 기준으로 삼는 것도 좋은 선택인 것 같다.

의미있는 테스트를 만들고 유지할 수 있게 팀의 역량 증가

안타깝지만 그냥 어느날 갑자기 테스트 코드를 짜고 싶다고 짤 수 있는건 아니다. 테스트를 짤 수 있는 코드 구조와 환경이 갖춰져 있어야 한다. 따라서 테스트를 짤 수 있는 아키텍처를 도입하고 유지해야 한다. 애플의 MVC는 테스트 짜기가 너무 힘들다. UI를 만들지 않고는 유저 액션 이벤트를 발생시킬수 없다는 문제점이 있다. 또한 뷰컨트롤러는 라이프사이클 관련 메서드가 복잡하고 갯수가 많아서 테스트가 까다롭다. 그리고 의존성을 주입하는 것 마저도 간단하지가 않다. 그래서 MVVM, VIPER, RIBs 등의 아키텍처가 생겨났다. 그러나 아키텍처를 도입한다고 끝이 아니다. 아키텍처는 은탄환이 아니기 때문에 도입하더라도 팀이 처한 상황과 해결해야 하는 문제에 따라 끊임없이 변형된다. 그래서 시간이 지나도 테스트가 가능한, 그리고 테스트를 짜는게 힘들지 않고 재밌는 구조를 유지하는 기술 리더십이 중요하다. 또한 테스트를 짜는 일이 힘들거나 생산성이 떨어지지 않는 환경을 구축하고 적절한 툴을 도입하거나 만드는 팀워크도 중요하다.

Tags: tests  

The Advantages of Writing a Test Code

I uploaded a full request after modifying the code, but when I saw that the test failed, I searched for the parts that I missed and corrected them. That was when I really felt the value of testing. My team measures coverage as KPI. 20% of working hours allotted for technical assignments such as making tests, build time improvements, and modularizations. There is something I felt through the experience of writing and maintaining the test codes.

It makes you follow a better architecture

A code that is difficult to test is highly likely to have a poor structure. A testable code incorporates concerns about dependency. You need to strictly follow the dependency inversion principle as you manage and organize the dependency of the source code. Uncle Bob explains it well through the dependency rule.

Dogfooding My Codes

Once you make the test, you can experience the codes you’ve written as a user. It’s like building a house and decorating the interior and then coming out to check how it looks from the outside. Checking what the window looks like, how to open the door, and where the entrance is. The window and the entrance are the same as the public API. The interior of the house is the private elements. We use the public API to check object actions in a test. We try generating an object ourselves, configuring them, calling a public method, or approaching the properties. By doing that, you can check the usability of the codes that you’ve written. If the API is sloppy, it’s difficult and painful to make a test. You may face situations where you may get thoughts like, ‘do I have to test this?’ or ‘why is this case so difficult to test?’. Therefore, you have no choice but to pay attention to the usability of the API, and if you pay attention, you can create a simpler and more intuitive API. Being simpler has several meanings, but I think the fact that there are a few public methods or properties is especially important.

A Simple and Intuitive API Design

Developing a feature → Making a test → Modifying the feature → Modifying the test. If you keep repeating the above process, it’s like growing muscles that can draw a big picture in advance within the designing stage of creating an API. For example, there are cases when the existing tests all need to be discarded and written all over again after some feature modifications. If that’s the case, it’s highly likely that the design was bad from the beginning. Publicly disclose the things that are related to policy, which do not change easily, and the detailed implementations need to be hidden or moved to the outside. If the design goes wrong, it would be impossible to make a test, or if it’s made, it’s going to be a pain to maintain it. If this happens repeatedly, we have no choice but to consider the structure, role, and testability of the object. It makes you think about what to put in this object, what to take out, and how to add dependencies. If you’re having trouble thinking about how to design a code or judging which pattern is better, it seems like a good choice to use the test codes as a standard.

Increase the team’s ability to create and maintain meaningful tests

Unfortunately, it’s not like you can just make a test code one day because you want to. There needs to be a code structure and an environment where you can make a test. Therefore, you need to introduce and maintain an architecture that can create a test. It’s too difficult to create a test on Apple’s MVC. There is a problem where a user action event cannot occur without creating a UI. Also, testing is difficult in ViewContoller because the lifecycle-related method is complex and there are too many of them. It’s also not simple to inject dependency. So architectures like MVVM, VIPER, RIBS, etc. were created. However, introducing architecture is not the end. Architectures are not silver bullets, so they constantly change depending on the problems that need to be solved and the situations the team is placed in. Therefore, technical leadership that maintains a structure where testing is possible over time, and testing is not difficult but interesting is important. Also, teamwork which builds an environment that doesn’t make testing feel difficult and maintains the level of productivity, and introduces or makes appropriate tools is important.

Tags: tests  

다른 표준시간대 다국적 개발팀에서 원격으로 일하는 법

우리팀은 모바일 개발자 9명으로 이뤄져있고 출신국이 정말 다양하다. 대한민국, 중국, 인도, 벨라루스, 이탈리아, 방글라데시 6개 국가에서 모였는데 비슷한 규모의 팀 중 사내에서도 손에 꼽히는 다양성인 것 같다. 원래는 모두 싱가폴이 근무지인데, 코로나로 인해 국가간 여행이 제한되기 직전 모국으로 휴가를 갔던 동료들이 여태 돌아오지 못했다. 그래서 팀원 두 명은 인도와 벨라루스에서 재택 근무를 하고 있다. 애초에 싱가폴 직원은 전부 재택 근무 중이기 때문에 크게 다를바가 없어 보였지만 시간대가 다르기 때문에 업무 방식에 변화가 생길 수 밖에 없었다. 생전 처음 원격으로 근무하랴, 시간대가 다른 팀원들과 협업하랴 정신없이 보내다가 이제 어느정도 안정적인 업무 방식을 찾은거 같다.

1. 시차 고려해서 메시지 보내기

미팅 시간을 잡을 때는 당연히 시차를 고려하게 된다. 두 시간 반 느린 인도 지사에 개발자가 많이 있기 때문에 평소에도 미팅은 오후에만 잡는 것이 생활화 돼있었다. 하지만 몇 달 업무를 해보니 채팅도 시차에 맞춰서 보내는 편이 좋다는걸 알게됐다. 처음에는 ‘메시지 보내놓으면 출근해서 보고 답장을 주겠지’ 라고 생각하고 내 편의에 맞춰 메시지를 보냈다. 그러다보니 상대방이 답장을 안주면 나도 잊어버리고 나중에 메시지를 다시 보내야하는 일이 잦았다.

생각해보니 나같은 사람이 많다면 메시지를 받는 사람 입장에서는 출근하자마자 메시지가 많이 와있을테고, 묻히는 메시지가 생겼을 것이다. 이제는 상대방 업무 시간에 맞춰서 메시지를 보낸다. 그랬더니 답장을 더 빨리, 확실하게 받을 수 있게 됐다. 이걸 잘하기 위해 쓰고 있는 도구가 두 개 있는데 하나는 맥 상태바에 여러 시간을 볼 수 있는 간단한 앱이고 다른 하나는 슬랙 리마인드 기능이다. 슬랙에서 특정 메시지에 알람을 걸어놓을 수 있어서 상대방 출근시간 즈음 맞춰놓으면 까먹지 않고 관련 내용을 참고해서 메시지를 보낼 수 있다.

👇 상태바에 여러 시간 표시해주는 맥 앱. 인도와 두시간 반, 벨라루스와 다섯 시간 시차가 있다.



👇 슬랙에 있는 메시지 리마인더 기능

2. 팀원 출근시간 전에 코드리뷰 남기기

서로의 업무에 가장 직접적으로 영향을 주는게 코드리뷰다. 일정 인원의 승인을 받아야만 마스터 브랜치에 병합할 수 있고 CI 작업이 두시간이나 걸리기 때문에 MR을 올리고 코드 리뷰를 받고 수정하고 승인을 받고 병합하는데에만 최소 반나절, 길게는 2~3일이 걸리기도 한다. 시차가 5시간인 벨라루스에서 일하는 팀원과 리뷰를 주고 받을때 특히 신경써야 했다. 리뷰가 늦어지면 코드 병합이 하루 이틀 더 늦어지기 십상이었다. 예를 들어 내가 오후 느지막이 리뷰를 남기고 퇴근을 하면 벨라루스 팀원은 댓글을 보고 코드를 수정하거나 아니면 의견을 댓글로 남긴다. 내 시각으론 밤 늦게거나 새벽이라 다음날이 돼야 확인할 수 있다. 그런데 내가 다음날에도 퇴근 직전에 MR을 검토하면 팀원의 두번째 응답은 또 그 다음날 확인할 수 밖에 없다. 이렇게 댓글이 한 두번만 오고가도 이틀이 지나가버린다.

그래서 이제는 출근하면 가장 먼저 MR부터 확인한다. 내가 아침에 의견을 남겨 놓고 다른 일을 하다보면 오후에 팀원이 출근해서 확인하고, 당일에 한번 더 대화가 오고갈 수 있다. 합의가 빠르면 승인까지 할 수 있고 그날 병합을 할 수도 있다. 단지 서너 시간의 차이로 코드 병합이 하루 단위로 늦춰질 수 있다는걸 생각하면 업무 순서를 조정하는건 쉬운 일이다. 의식적으로 하다보니 습관이 들었다.

3. 1대1 또는 소규모 통화 하기

원격 근무를 하면 의사 소통이 예전만큼 활발하지 않다. 옆자리에 있었더라면 쉽게 대화를 시작했을 텐데 이제는 대답을 기다려야 해서 간단한 대화나 잡담이 힘들다. 그런데 소소한 대화의 중요성을 요즘 부쩍 느끼고 있다. 동료와 친밀감을 쌓을수도 있고 개인 업무에 직접적인 도움을 받기도 하고, 외국 생활에 필요한 정보를 주고 받기도 한다. 원격 근무 때문에 대화도 의도적으로 하지 않으면 교류가 너무 없다. 보통 줌 미팅에 들어가면 사람들이 다 모이기 전까지 시간이 약간 빈다. 자투리 시간에라도 인사를 나누고 근황을 묻는다.

별도로 시간을 마련하면 더 좋다. 최근에 큰 프로젝트를 끝낸 후 그 동안 배운 것들을 정리하고 팀의 지식으로 남길겸 문서를 만들었고, 팀원들에게 앞으로의 방향을 제안하고 아이디어를 공유하는 시간을 두어 번 가졌다. 긴 시간은 아니었지만 다른 팀원들의 의견을 다양하게 들어볼 수 있었고, 무엇보다 미팅 이후에도 관련 주제로 기술적인 논의가 계속 이어지는 것을 봤다. 그리고 업무 미팅과 달리 정보 공유차 부담없이 모이면 다양한 얘기가 오고간다. 그러는 와중에 새로운 주제가 떠오르기도 했고 서로의 문제를 해결해주기도 했다.

원격 근무하는 현 상황은 주니어 개발자에게는 특히 더 어려운 시기같다. 쉽게 도움을 요청할 수 있는 사람이 옆에 없어서 힘들 것 같다. 기회가 있을때 1대1 통화로 도움을 주고는 있지만, 예전보다는 많이 부족하지 않나 싶다. 주니어 개발자라면 더 적극적으로 도움을 요청하고, 시간을 잡아서 통화를 하는 등의 노력을 해서 다른 개발자들과 접촉을 더 늘려야 할 것 같다.

앞으로

우리팀 사무실의 임대차 계약이 만료됐는데 연장을 안했다고 한다. 아무래도 내년 중순 쯤 완공될 사옥에 입주하기 전까지 이런 근무 형태가 계속될 예정이다. 코로나로 인한 여행 제한, 이동 제한, 만남 제한 때문에 여러모로 힘들고 업무 방식도 급변해서 쉽지 않지만 이런 상황 속에서 더 행복하고 지속 가능하게 일할 수 있는 방법이 뭘까 계속 고민하게 된다.

Tags: WFH, multinational, multi-timezone, collaboration