블로그
전체 42024. 10. 27.
0
[워밍업 클럽 스터디 2기 - BE] (클린코드, 테스트코드) 4주차 발자국
해당 포스팅은 Practical Testing: 실용적인 테스트 가이드를 학습하면서 작성한 회고입니다.학습 회고Mock 사용 방법에 대해 다루었고, 구체적으로 Mocking 시점과 방법, Mocking을 적용할 적절한 시점에 대해 명확하게 이해할 수 있었고, Mock을 활용하여 더욱 효율적인 테스트 작성에 대한 설명을 통해 여러 고민을 해볼수 있는 좋은 시간이었습니다. Mock 활용과 Testing 전략Mock을 사용하는 시점에 대해 Classicist와 Mockist 관점을 접하며, 각 접근 방식의 장단점을 비교해 볼 수 있었습니다. Classicist는 실제 객체를 활용한 통합 테스트에 집중하는 반면, Mockist는 Mocking을 통해 테스트 효율을 높이고 빠르게 검증하는 데에 초점을 맞추는 방식입니다. 개인적으로는 Mockist 방식이 코드의 효율적인 테스트를 위해 더 적합하게 느껴졌습니다. Mock과 Stub, Spy를 포함한 다양한 테스트 Double의 기능을 익히면서 단위 테스트와 통합 테스트에 맞는 도구를 선택하는 방법을 명확히 알게 되었습니다. 개선된 테스트 작성에 대한 구체적인 팁더 나은 테스트를 위해 필요한 몇 가지 구체적인 조언을 배웠습니다. 먼저, 각 테스트는 하나의 주제에 집중하도록 구성하여 가독성을 높이는 것이 중요하다는 점을 강조하고 있습니다. 또한, LocalDateTime.now()와 같은 변동 가능한 값들은 제어 가능한 상태로 고정하고, 테스트 간 독립성을 보장하는 것이 좋다는 것을 확인할 수 있었습니다. 그리고 @ParameterizedTest와 @DynamicTest를 통해 반복적이거나 변동하는 시나리오를 효율적으로 테스트하는 방법도 배우게 되었습니다. Spring REST Docs와 API 문서화테스트를 기반으로 API 문서를 자동으로 생성하는 Spring REST Docs도 다루었습니다. 이는 개발 문서의 신뢰도를 높여주기 때문에 협업 시에도 큰 장점이 될 수 있지만, 설정이 다소 복잡하고 문서 작성 시 코드 양이 많아지는 단점도 존재합니다. 이번에 배운 내용을 바탕으로 Spring REST Docs 설정과 활용 방법을 추가로 학습해보고 싶습니다. 전체 회고4주간 진행된 스터디를 통해 클린 코드와 테스트 코드의 중요성을 재차 느끼게 되었으며, 이번 학습이 실제 업무와 개인 프로젝트에서 코드 품질을 높이는 데 큰 밑거름이 될 것이라고 생각합니다. 평소 테스트 코드와 관련해 갈망이 있었으나 실제로 깊이 있게 배워본 적은 없었는데, 이번 기회를 통해 실질적인 테스트 작성 방법과 Mocking 전략을 체계적으로 학습할 수 있어 좋았습니다.스터디가 끝난 후에도 배운 내용을 바탕으로 테스트 코드 학습을 꾸준히 이어나갈 계획입니다.
2024. 10. 20.
0
[인프런 워밍업 스터디 클럽 2기 백엔드(클린코드, 테스트코드)] 3주차 발자국
TDD에 대해가까이보면 느려보이지만 멀리보면 가장 빠른 개발 방법 테스트를 하는 이유빠른 피드백을 위해 레이어별로 코드 작성하고… 빌드하고… 포스트맨 돌리고… 하는 시간을 절약할 수 있습니다. 자동화된 피드백 사람이 직접 수동으로 확인하는게 아니라 테스트 환경에서 피드백을 받을수 있습니다.심리적 안정감 내가 작성한 코드에 대한 안정감, 자신감귀찮게 느껴지지만 직접 작성해보니 생각보다 많은 시간을 아낄수 있게 도와준다는 인상을 받았습니다. 단위테스트(Unit test)작은 코드 단위를 독립적으로 검증하는 테스트클래스, 메소드등 가장 작은 단위를 테스트 하는 것외부에 의존하지 않음 TDD(Test Driven Development)사이클의 실패하는 테스트, 최소한의 코딩이 이해되지 않았었는데극단적인 예시지만 바로 이해를 시켜주셨습니다. 테스트를 먼저 작성하는 이유기능 먼저 작성하면기능 먼저 구현시 테스트 자체가 누락될 가능성이 높음 → 귀찮으니까해피케이스만 검증할 가능성이 높음 → 사고가 갇히게 됨 예외 상황을 예측하기 어려움잘못된 구현을 늦게 알아차림 → 유지보수가 힘들어 진다.결국 테스트가 불가능한 코드가 탄생하게 될수도테스트를 먼저 작성하면복잡도가 낮은, 테스트가 가능한 코드로 구현이 시작됨예외 케이스에 대해 생각하므로 문제를 조기에 발견 가능함과감하게 리팩토링, 구현이 가능하다. TDD에 대한 오해테스트의 진짜 목적은 검증이 아닌 상호 작용을 통한 프로덕션 코드의 발전입니다테스트 코드는 프로덕션 코드에 대한 여러 케이스를 보여주기 때문에하나의 명세가 될 수 있고 무엇이 필요한지 어떤걸 주의해야할 지 코드로 설명이 가능합니다.덕분에 프로덕션, 도메인에 대해 여러 시각과 관점을 가질수 있게 되고이런 장점은 결과적으로 팀의 자산이 되어 팀원 모두에게 도움을 줄 수 있습니다. 하지만 테스트 코드가 병목이 될수도 있다.그래서 우리가 고민 해야할 것드러나지 않은, 숨겨진 혹은 암묵적인 요구사항이 없는지 기획 의도는 대부분 해피케이스를 말합니다 때문에 예외 케이스를 스스로 고민해봐야 합니다 특히 엣지 케이스 경계값 테스. 트가 필요하지 않우리가 무얼 검증하고 있는것인지 생각해봐야함. 테스트 코드는 반드시 프로덕션 코드와 같지 않아도 됨 하지만 내가 만드는 기능이 무엇인지 어떤 검증이 필요한지를 명확하게 해야함값에 의존하는 코드인지 외부 세계에 영향을 주는 코드인지 판단하기 = 테스트하기 어려운 기능 값에 의존적이라면 테스트 내부에 있던 값을 파라미터, 상수로 값을 메서드 밖으로 꺼내는걸 고려해야하고 외부 세계에 영향. 을 준다면 신중하게, 예외처리 등을 섬세하게 처리해주어야 합니다. 회고테스트 코드를 안개속에 있는것처럼 느끼고 있었는데너무 쉽고 명확하게 설명을 해주셔서 좋았습니다.저에게 큰 도움이 된 주차였습니다.
2024. 10. 13.
0
[인프런 워밍업 스터디 클럽 2기 백엔드(클린코드, 테스트코드)] 2주차 발자국
학습 내용 강의에서 배운 리팩토링 기법, 원칙등을 이용하여 직접 리팩토링을 진행하는 주차였다. 학습 내용 회고리팩토링의 관점에 대해 다시 생각해볼 수 있었다.완전한 추상화가 아닌 생산성에 도움이 될 정도의 추상화 과도하지 않은 추상화를 추구해야하며도메인 지식을 발견하면 그때 구조를 수정해도 된다는 너무 현재의 완벽함에 집착하지 않아도된다는걸 느꼈던 것 같다.추상과 구체에 대해 생각하며 리팩토링을 진행해야겠다 미션코드를 리팩토링하는 미션이었다.시간이 없어 읽어보기만 하고 직접 리팩토링 하지는 못했지만 다른분들의 코드를 보며 많은걸 배웠다.특히 오버엔지니어링을 경계 해야겠다는 생각이 생긴 미션이었다. 마치며2주간 어떻게 해야 좋은 코드를 만들까에 대한 관점을 배우는 유익한 시간이었다.코드를 바라보는 관점과 구체적으로 어떻게 수정을 하는지까지 알려주는정말 큰 도움이 되는 시간이었다.다음 테스트 코드 강의도 매우 기대가 된다.
2024. 10. 06.
0
[워밍업 클럽 스터디 2기::백엔드] 1주차 발자국
일주일간의 학습 내용추상 (抽象)코드 단위에서의 추상 레벨을 지키는 법메서드를 추출하는 기준과 이름 짓기등도메인의 책임 분리에 대해서만 추상화를 생각하고 있었는데코드안에서 추상 레벨을 일관되게 유지하고 제가 제일 어려워하던 이름짓는법을매우 쉽게 설명해주셔서 큰 도움이 되었습니다. 논리, 사고의 흐름읽기 쉬운 코드를 작성하고 싶었으나 그동안은 메서드의 구조, 이름만 신경을 쓰고 있었습니다.잊고 있던 early return을 상기했고 부정문을 메소드로 대체했을때 인지부하를 줄여읽는이로 하여금 더 이해하기 쉬운 코드를 작성할 수 있다는게 신선했습니다. 객체 지향 패러다임SOLID 원칙을 코드에 적용하시는 것을 보고 많이 배웠습니다.늘 SOLID한 코드인가를 고민했는데 강사님의 강의를 듣고 좀 더 시야가 트인 느낌입니다. 학습 회고칭찬하고 싶은 점프로젝트 진행중에도 시간을 쪼개어 강의를 듣고 과제를 수행했다진행중인 프로젝트에도 적용을 시키려 노력했다. 아쉬웠던 점부트캠프에 참여중이라 시간 제약이 있어 강의를 완전 깊게 이해하지 못한 느낌이라 아쉽다.여러번 강의를 듣고 싶은데 시간이 없었다... 보완하고 싶은 점강의 내용을 글로 정리하려했으나 글쓰기가 아직은 많이 어렵게 느껴진다.글을 정리하는법을 공부해야할 것 같다. 미션public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true;}내가 생각한 문제점들1. 추상화 부족 이 메서드는 주문 항목을 확인하고, 사용자 정보를 확인하고, 총 가격을 확인하고 있다. 주문 검증이라는 책임을 지키는 듯 보이지만 내부를 살펴보면 3가지 일을 직접하고 있기에 확장이 어렵고 단일 책임을 지키지 못한다는 판단을 하였다 이런 문제들은 추상화를 제대로 하지 않았기에 생긴 문제라 판단하여 이 문제를 추상화 부족이라는 문제로 추상화하였다.2. 중첩된 조건문 else문을 보면 else문에서 끝나는 것이 아니라 다시 조건문을 이용하고 그 안에서 다시 조건문을 이용하는 복잡한 형태인데 이런 형태가 가독성을 해치고 로직을 복잡하게 만들고 있다는 생각이 들었다.3. 중복된 조건 order.getTotalPrice() > 0 를 else if에서도 사용중인데 0보다 크거나, 아니거나의 조건 판별을 위해서라면 두번씩이나 조건을 붙이는건 효율적이지 못하다는 결론을 내렸다4. 부정 연산자에 의한 사고의 흐름 방해 order.getTotalPrice() > 0 와 order.hasCustomerInfo에 ! 부정 연산자를 붙인 형태로 보는이로 하여금 한번더 어떤 코드인가를 생각하게 만드는 좋지 않은 조건문이라는 생각이 들었다.내가 생각한 해결방법들1. 추상화 부족 먼저 주문 검증이라는 책임을 어떻게 분산을 시킬것인가를 정했는데 각각의 조건문에서 원하는 검증들을 각각의 메서드들로 분할을 시켜주기로 했다. 리팩토링후에는 validateOrder 내부를 주문 항목 확인, 사용자 정보 확인, 총 가격 확인이라는 각각의 책임을 가진 메서드들로 나누고 validateOrder는 주문 검증이라는 명확한 책임을 가지게 만들어야 겠다고 생각했다.2. 중첩된 조건문 1번에서의 메서드 분할에 따라 각각의 메서드를 조건문에 넣어주기로 했고 6개의 if 또는 else를 3개의 if문으로 줄여보기로 했다3. 중복된 조건 조건문의 순서와 early return을 이용해 order.getTotalPrice()의 조건을 한번만 이용하기로 했다 총가격이 0보다 작거나 같다면 사용자 정보 확인전에 return을 해주어 불필요한 중복 조건을 제거했다.4. 부정 연산자에 의한 사고의 흐름 방해 더 직관적인 코드를 만들기 위해 부정 연산자를 메서드 내부로 숨기고 메서드의 이름을 isCustomerInfoNotFound라는 문장으로 만들어 validateOrder 메서드만 확인해도 해당 조건문에서 무엇을 확인하는지 알 수 있게끔 수정했다. 결과 코드public boolean validateOrder(Order order) { if (isOrderItemsEmpty(order)) { log.info("주문 항목이 없습니다."); return false; } if (isTotalPriceInvalid(order)) { log.info("올바르지 않은 총 가격입니다."); return false; } if (isCustomerInfoNotFound(order)) { log.info("사용자 정보가 없습니다."); return false; } return true;}private boolean isOrderItemsEmpty(Order order) { return order.getItems().isEmpty();}private boolean isTotalPriceInvalid(Order order) { return order.getTotalPrice() }private boolean isCustomerInfoNotFound(Order order) { return !order.hasCustomerInfo(); }회고재미있는 미션이었다.실제 프로젝트에서는 리팩토링이 부담스럽게 느껴졌는데부담없이 내 맘대로 리팩토링을 진행했기에 마음편히 진행하고 다른 분들의 코드와 비교도 해볼수 있는게 아주 좋다.