Showing Posts From

작성

테스트 코드 작성: 해야 하는데 시간이 없어

테스트 코드 작성: 해야 하는데 시간이 없어

테스트 코드, 또 미뤘다 오늘도 PR 올렸다. 테스트 코드 없이. 리뷰어가 물어볼 거다. "테스트는요?" 그럼 나는 답한다. "다음 스프린트에 추가하겠습니다." 이게 벌써 세 번째다. 다음 스프린트는 안 온다. 우리 모두 알고 있다. 아침 스탠드업에서 PM이 말했다. "이번 주 배포 일정 타이트합니다." 타이트하다는 건 야근한다는 뜻이다. 테스트 코드 쓸 시간은 당연히 없다는 뜻이다. 모니터를 보며 펜을 돌린다. 막 짠 코드가 화면에 있다. 200줄짜리 서비스 로직. 분기 처리가 5개. 예외 케이스가 3개. 테스트 짜려면 최소 10개는 필요하다. 시계를 본다. 오후 4시. 퇴근까지 2시간. 코드 리뷰 반영하고, 문서 업데이트하고, 내일 회의 자료 준비해야 한다. 테스트는 무리다.악순환의 시작 테스트 없는 코드는 빠르다. 짜는 동안만. 이번 주에 3개 기능 만들었다. 테스트 코드 없이. 다음 주에 버그 리포트 5개 받았다. 엣지 케이스였다. 내가 놓친 분기들. 버그 수정하는 데 하루 반 걸렸다. 테스트 짰으면 2시간이면 찾았을 버그다. 근데 수정할 때도 테스트 안 짰다. 시간이 없어서. 후배가 물어봤다. "형, 이 함수 어떻게 동작하는 거예요?" 내가 3달 전에 짠 코드다. 주석도 없다. 테스트도 없다. 나도 기억 안 난다. 코드를 다시 읽었다. 10분 걸렸다. 테스트 코드가 있었으면 3분이면 이해했을 것이다. 테스트는 문서다. 살아있는 문서. 레거시 코드가 쌓인다. 테스트 없는 코드가 레거시가 되는 건 빠르다. 6개월이면 충분하다. 리팩토링 회의에서 팀장이 말했다. "이 부분 개선이 필요한데요." 나는 고개를 저었다. "건드리면 어디가 터질지 모릅니다." 테스트가 없어서. 결국 안 건드린다. 기술 부채가 쌓인다. 이자는 복리다.시간이라는 핑계 "시간 없어서 테스트 못 짰다"는 거짓말이다. 나한테 하는. 진짜는 이거다. 테스트 짜는 게 귀찮다. 당장 눈에 보이는 결과가 없다. 기획자는 테스트 코드 커버리지에 관심 없다. PM은 기능이 돌아가는지만 본다. 테스트는 미래를 위한 투자다. 근데 나는 오늘이 바쁘다. 내일도 바쁠 거다. 투자할 여유가 없다. 이게 7년째 반복이다. 신입 때는 달랐다. TDD 책도 샀다. JUnit 강의도 들었다. 첫 회사에서 테스트 커버리지 80% 채웠다. 그땐 시간이 있어서가 아니다. 배우고 싶어서였다. 지금은 다르다. 연차가 쌓이면서 핑계만 늘었다. "레거시라 테스트 짜기 힘들어요." "의존성이 너무 많아요." "모킹하기 복잡해요." 다 맞는 말이다. 근데 변명이다. 진짜 이유는 이거다. 테스트 없이도 어떻게든 돌아간다. 버그 나면 그때 고친다. 장애 나면 야근한다. 이게 익숙하다. 편한 길이다. 당장은.돌고 도는 이야기 목요일 저녁. 슬랙에 메시지가 떴다. "결제 API 오류 났어요." 심장이 뛴다. 내가 화요일에 수정한 부분이다. 테스트 없이 배포했다. 프로덕션 로그를 연다. NullPointerException. 예상 못 한 케이스였다. 테스트 짰으면 잡았을 거다. 핫픽스 시작. 코드 고치는 건 10분. 검증하는 데 1시간. 배포하고 모니터링하느라 2시간. 퇴근은 10시. 집에 가는 지하철에서 생각했다. "다음엔 테스트 꼭 짜야지." 이게 몇 번째 다짐인지 모르겠다. 금요일 아침. 새 기능 티켓이 떨어졌다. 월요일까지 완료. 타이트하다. 테스트 짤 시간은 또 없다. 악순환이다. 회사는 테스트를 강제하지 않는다. 커버리지 목표도 없다. PR에 테스트 없어도 머지된다. "바쁘니까 다음에"가 통한다. 처음엔 편했다. 이제는 무섭다. 내가 짠 코드가 시한폭탄처럼 느껴진다. 언제 터질지 모른다. 터지면 내가 고쳐야 한다. 주말에도 전화 온다. 변명들의 목록 "레거시라 테스트 짜기 힘들어요." 맞다. 의존성 주입 안 된 코드. 싱글톤 지옥. static 메서드 천지. 테스트하기 어렵게 설계됐다. 근데 새 코드는? 내가 어제 짠 코드도 테스트하기 어렵다. 내가 그렇게 짰다. 습관이다. "모킹하기 너무 복잡해요." 외부 API 6개 호출하는 함수. 데이터베이스 4개 테이블 조인. 레디스 캐시 체크. 모킹할 게 너무 많다. 근데 이건 테스트의 문제가 아니다. 설계의 문제다. 함수가 너무 많은 일을 한다. 책임이 분리 안 됐다. 테스트가 어려운 코드는 나쁜 코드다. 이건 학교에서 배웠다. 실무에선 잊었다. "시간 대비 효율이 안 나와요." 코드 짜는 데 1시간. 테스트 짜는 데 30분. 50% 오버헤드다. 비효율적으로 보인다. 근데 나중에 버그 고치는 시간은? 어제 핫픽스에 3시간 썼다. 테스트 있었으면 버그 자체가 없었다. 장기적으로 보면 테스트가 더 빠르다. 근데 나는 단기밖에 못 본다. 다음 주 배포만 보인다. "우리 팀은 빠른 개발이 중요해요." 스타트업 마인드. 일단 만들고 본다. 시장 검증이 먼저다. 테스트는 나중에. 근데 나중은 안 온다. 기능이 쌓인다. 유저가 늘어난다. 버그가 폭발한다. 그때는 더 못 짠다. 더 바쁘니까. 빠르게 가려면 제대로 가야 한다. 이것도 학교에서 배웠다. 실무에선 반대로 한다. 테스트 짜는 사람들 팀에 한 명 있다. 테스트 빡빡하게 짜는 후배. PR 올리면 테스트가 같이 온다. 유닛 테스트. 통합 테스트. 엣지 케이스까지. 코드보다 테스트가 더 길다. 처음엔 답답했다. "왜 이렇게 오래 걸려?" 3일이면 끝날 걸 5일 걸린다. 근데 그 코드는 안 터진다. 버그 리포트에 그 후배 이름이 없다. 리팩토링도 빠르다. 테스트가 보호하니까. 나는 빨리 짠다. 3일이면 끝낸다. 그리고 다음 주에 2일 동안 버그 고친다. 결국 5일이다. 야근 포함하면 더 길다. 누가 더 빠른 걸까. 오픈소스 코드를 볼 때가 있다. 유명한 라이브러리들. 테스트 커버리지 90% 넘는다. 그래서 믿고 쓴다. 내 코드는 어떨까. 커버리지 15%. 나도 내 코드 못 믿는다. 수정할 때마다 떨린다. 어디가 터질지 몰라서. 테스트는 자신감이다. 없으면 항상 불안하다. 시작하지 못하는 이유 테스트를 시작하기 어려운 건 맞다. 지금 프로젝트는 테스트가 없다. 시작하려면 환경부터 세팅해야 한다. JUnit 5? Mockito? AssertJ? 뭘 쓸지부터 정해야 한다. 기존 코드는 테스트하기 어렵게 짜여 있다. 리팩토링부터 해야 한다. 그러면 기능 개발은 언제 하나. 완벽하게 하려면 못 한다. 이게 함정이다. 테스트는 0 아니면 100이 아니다. 50도 괜찮다. 30도 없는 것보단 낫다. 어제 짠 핵심 로직 하나만 테스트 짜도 된다. 버그 나기 쉬운 부분만. 전체 커버리지 90% 목표 안 세워도 된다. 근데 나는 완벽주의자다. 시작하면 제대로 하고 싶다. 그래서 시작을 안 한다. 완벽한 테스트를 기다리다가 아무 테스트도 못 짠다. 이게 7년째다. 시간의 역설 테스트 짤 시간이 없어서 테스트를 안 짠다. 그래서 버그가 난다. 버그 고치느라 시간이 더 없어진다. 그래서 테스트를 더 못 짠다. 역설이다. 시간을 아끼려고 테스트를 건너뛴다. 결국 더 많은 시간을 쓴다. 이걸 알면서도 반복한다. 이번 달 야근 시간을 계산했다. 40시간. 대부분 버그 수정과 핫픽스였다. 테스트가 있었다면? 10시간이면 충분했을 것이다. 30시간을 아낄 수 있었다. 그 시간이면 테스트 100개는 짰다. 근데 다음 달도 똑같을 것이다. 테스트 짤 시간이 없어서. "바빠서 테스트 못 짠다"는 말은 틀렸다. "테스트 안 짜서 바쁜" 게 맞다. 원인과 결과가 뒤집혔다. 이걸 깨닫는 데 7년 걸렸다. 바꾸는 데는 얼마나 걸릴까. 작은 시작 오늘 결심했다. 하나만 짜보기로. 새 기능 티켓을 받았다. 쿠폰 발급 API. 간단해 보인다. 근데 분기가 많다. 중복 발급 체크. 기간 검증. 재고 확인. 유저 등급별 차등 지급. 예전 같으면 그냥 짰다. 포스트맨으로 몇 번 찔러보고 PR 올렸을 것이다. 오늘은 다르게 해봤다. 함수 하나 짜고, 테스트 하나 짰다. 정상 케이스. 통과했다. 다음 분기 추가하고, 테스트 추가했다. 중복 발급 시도. 예외 발생 확인. 통과했다. 이렇게 하나씩. 30분 더 걸렸다. 근데 확신이 생겼다. 이 코드는 안 터진다. 리팩토링도 해봤다. 함수 이름 바꾸고, 파라미터 순서 바꿨다. 테스트가 깨졌다. 고쳤다. 다시 통과했다. 테스트가 없었으면 못 했을 리팩토링이다. 어디가 망가질지 몰라서. PR 올렸다. 테스트 코드가 같이 올라갔다. 처음이다. 리뷰어가 댓글 달았다. "오 테스트 있네요. 좋습니다." 기분이 묘했다. 이게 당연한 건데. 변화는 느리다 일주일에 하나씩 짜기로 했다. 새 기능 하나. 리팩토링 하나. 버그 수정 하나. 뭐든 하나만. 완벽하지 않다. 커버리지는 여전히 낮다. 레거시는 그대로다. 근데 조금씩 늘고 있다. 한 달 뒤. 테스트 30개가 생겼다. 작은 숫자다. 근데 없는 것보다 훨씬 낫다. 버그가 줄었다. 체감된다. 이번 달 핫픽스가 2개였다. 저번 달은 5개였다. 리팩토링도 조금씩 한다. 테스트가 있는 부분만. 조심스럽지만 할 수 있다. 후배가 물어봤다. "형, 갑자기 왜 테스트 짜요?" 뭐라 답해야 할까. "이제야 정신 차렸어" 이렇게 말했다. 변화는 극적이지 않다. 어느 날 갑자기 좋아지지 않는다. 조금씩, 천천히. 근데 방향은 맞다. 처음으로 맞는 방향으로 가는 기분이다. 여전히 바쁘다 테스트 짜기 시작했지만 여전히 시간은 없다. 어제도 야근했다. 기획 변경으로 코드 다 갈아엎었다. 테스트도 같이 갈아엎었다. 시간 두 배 걸렸다. 짜증 났다. "테스트 없었으면 진작 끝났을 텐데." 이런 생각도 들었다. 근데 배포는 안심하고 했다. 터지진 않을 거다. 테스트가 통과했으니까. 다음 날 아침. 슬랙 알림 없었다. 버그 리포트 없었다. 조용한 아침이다. 이게 얼마 만인지. 시간은 여전히 부족하다. 근데 쓰는 방식이 달라졌다. 예전엔 빨리 짜고, 나중에 고치느라 시간 썼다. 지금은 천천히 짜고, 나중이 편하다. 같은 시간인데 결과가 다르다. 불안한 빠름보다 확실한 느림이 낫다. 후회와 다짐 7년을 돌아본다. 처음부터 테스트 짰으면 어땠을까. 지금쯤 시니어답게 코드 짰을 것이다. 레거시 만들지 않았을 것이다. 근데 후회해도 소용없다. 과거는 못 바꾼다. 바꿀 수 있는 건 오늘뿐이다. 오늘 하나 짰다. 내일도 하나 짤 거다. 이게 계획의 전부다. 완벽한 계획은 없다. 거창한 목표도 없다. 그냥 하나씩. 테스트는 시간이 없어서 못 짜는 게 아니다. 안 짜는 거다. 선택의 문제다. 나는 이제 다르게 선택한다. 느리지만 확실하게. 불안하지 않게.테스트는 사치가 아니라 기본이다. 이제야 안다.