블로그
전체 52025. 04. 09.
0
워밍업 클럽 3기 백엔트 테스트 참여 후기
워밍업 클럽 3기 백엔드 테스트에 참여하면서, 단순히 기능 구현을 넘어서 ‘읽기 좋은 코드’의 중요성과 테스트 코드의 역할에 대해 다시 한 번 생각하게 되는 계기가 되었습니다. 읽기 좋은 코드란?이번 테스트는 단순히 작동하는 코드를 제출하는 것이 아닌, “누가 봐도 이해하기 쉬운 코드”를 얼마나 잘 작성하느냐가 핵심이었습니다. 테스트 코드의 중요성테스트에서는 단위 테스트와 통합 테스트를 얼마나 잘 설계하느냐도 주요 평가 요소였습니다.테스트를 작성하면서 자연스럽게 코드의 의도를 더 명확하게 표현할 수 있었고, 리팩토링에도 자신감을 얻을 수 있었습니다. 마무리이번 워밍업 클럽 참여를 통해, 코드를 어떻게 설계하고, 어떻게 표현해야 좋은 개발자인지를 되짚어보는 시간이었습니다. 특히 협업을 염두에 둔 코딩 스타일과 테스트 코드의 중요성에 대해 실감할 수 있었습니다. 실력을 점검하는 데 그치지 않고, 더 나은 개발자로 성장할 수 있는 좋은 기회였습니다.
백엔드
・
인프런
・
인프런워밍업클럽
・
스터디3기
2025. 03. 26.
0
실용적 테스트 가이드 발자국 4주차
Test Double 이란? 테스트에서 실제 객체를 대체하여 사용하는 가짜 객체(test substitute)의 총칭Test Double은 단위 테스트 시 실제 객체 대신 사용하는 “대역 객체”입니다.마틴 파울러(Martin Fowler)는 이를 5가지 유형으로 정리했습니다.Dummy아무 것도 하지 않는 깡통 객체Fake단순한 형태로 동일한 기능을 수행하나, 프로덕션에서는 쓰기는 부족한 객체Stub테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체SpyStub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체 처럼 동작시키고 일분만 Stubbing 할 수 있다.Mock행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체 Mock과 Stub의 혼란 테스트에 대한 개인적인 생각입니다. 테스트를 작성하는 방법의 시작이 누군가에게는 마틴 파울러의 Test Double과 같은 개념에서 출발했을 수도 있지만, 대부분은 TDD(Test-Driven Development)를 가장 먼저 접하지 않을까 생각이 든다.아래는 TDD 스타일의 테스트 코드를 chatGPT 에게 요청하여 작성된 코드이다.// OrderTest.java import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class OrderTest { @Test void 주문_총_금액을_계산한다() { Product apple = new Product("사과", 1000); Product banana = new Product("바나나", 2000); Order order = new Order(); order.addProduct(apple); order.addProduct(banana); assertEquals(3000, order.totalPrice()); } }Product 를 Mock 를 사용하여, 테스트 코드 작성// OrderTest.java import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; public class OrderTest { @Test void 주문_총_금액을_계산한다_Product_Mock_사용() { // Arrange Product apple = mock(Product.class); Product banana = mock(Product.class); when(apple.getPrice()).thenReturn(1000); when(banana.getPrice()).thenReturn(2000); Order order = new Order(); order.addProduct(apple); order.addProduct(banana); // Act & Assert assertEquals(3000, order.totalPrice()); } }Mockito의 mock을 통한 테스트에 익숙해질때쯤 마틴 파울러의 test double의 stub과 mock의 혼란 자연스럽게 Mockito의 mock을 이용하여 mock 테스트 해왔다 생각했지만, test double의 stub과 mock의 혼합된 테스트를 해온것이 아닌가 생각이든다. 그러기에 stub과 mock 테스트가 명확하게 구별 된다기 보다는 같은 것이 아닌가? 라는 혼란이 오는게 아닌가 싶다. 두 테스트의 then에 해당 하는 부분에서는 상태(값)을 assertEquals(3000, order.totalPrice()); 확인하고 있다.그렇다면 해당 두 테스트는 test double에서 이야기하는 Mock 테스트 인가? Stub 테스트인가? 주문_총_금액을_계산한다() Product 실제 객체 사용 최종적으로 상태(값)에 대한 확인 -> Stub 주문_총_금액을_계산한다_Product_Mock_사용()Product 부분을 Mock 하였으며, Order 객체의 totalPrice의 상태(값)을 확인 -> Mock & Stub해당 테스트에서 상태(값) assertEquals(3000, order.totalPrice()); 을 확인 하는 것이 아닌 verify(apple).getPrice(); 이 였다면 Mock 테스트이지 않을까? 조금 더 확인이 필요할 것으로 생각이 된다.
2025. 03. 23.
0
실용적 테스트 가이드 발자국 3주차
### ✨ 요약 *단위 테스트(Unit Test)**는 클래스나 메서드 단위로 동작을 검증하여 빠르고 안정적인 피드백을 제공한다. *통합 테스트(Integration Test)**는 실제 Bean 간의 상호작용을 검증하며, 환경 설정이나 데이터베이스 연결 등 외부 의존성까지 포함된다. *Spring Test 어노테이션** (@DataJpaTest, @WebMvcTest, @SpringBootTest)을 적절히 활용하여 테스트의 범위와 목적에 맞는 환경을 설정한다. *Mock 객체**를 활용하여 의존성을 제거하고 테스트 대상을 고립시킨다.### 📝 회고 *테스트 전략은 계층 구조와 목적에 맞게 선택해야 한다** * 단위 테스트는 빠르고 명확한 검증에 좋지만, 시스템 전체의 유기적인 동작은 통합 테스트로 확인해야 한다. *테스트 어노테이션의 정확한 의미를 이해하는 것이 중요하다** * @WebMvcTest는 Controller만 로드하고, @MockBean으로 의존성을 주입해야 한다. * @SpringBootTest는 모든 Bean을 로드하므로 무겁고 느릴 수 있으나, 실제 실행 환경과 유사하다. *테스트 코드는 리팩터링과 유지보수 관점에서도 중요한 자산이다** 테스트가 어렵다는 이유로 생략하지 말고, *필수적인 도메인 로직에는 반드시 단위 테스트를 작성**하는 습관을 들여야 한다. *Mock을 언제 쓸지, 실제 구현체를 언제 쓸지 판단하는 안목이 필요하다** * 외부 API나 복잡한 비즈니스 로직은 Mock으로 격리하고, 실제 DB와의 연동은 따로 분리하여 테스트하는 것이 이상적이다.
2025. 03. 16.
0
클린코드 발자국 2주차
## 5. 코드 다듬기### ✨ 요약- 주석이 많다는 것은 코드가 비즈니스 로직을 명확히 반영하지 못했다는 신호일 수 있다. - 코드만으로도 의미를 전달할 수 있도록 가독성을 높이는 것이 중요하다.- 메서드와 변수 정렬 방식에도 신경 쓰자. - public → private 순서, 상태 변경 > 판별 > 조회 순서를 유지하면 가독성이 올라간다.- 패키지 구조를 의미 있게 나누는 것이 중요하다. - 너무 세밀하게 나누면 관리가 어렵고, 너무 큰 단위로 묶으면 유지보수가 어렵다. - 프로젝트의 규모와 요구사항에 맞는 적절한 구조를 고민해야 한다.---## 6. 리팩토링 연습### ✨ 요약- 리팩토링은 단순한 코드 변경이 아니라, 코드의 응집도를 높이고 결합도를 낮추는 과정이다. - 리팩토링을 할 때는 단순한 코드 변경이 아니라, 객체 간의 관계와 책임을 다시 한번 점검하는 것이 중요하다.- DIP(의존 관계 역전 원칙)를 고려하면서 설계해야 한다. - 의존성을 인터페이스에 두고, 구체적인 구현체를 변경할 수 있도록 설계하면 유연성이 증가한다.- 일급 컬렉션의 개념을 적극적으로 활용해야겠다. - 컬렉션을 직접 노출하는 것은 유지보수 측면에서 위험하므로, 이를 감싸는 클래스를 활용하면 더 안전하다.---## 7. 기억하면 좋은 조언들### ✨ 요약- 리팩토링과 코드 리뷰는 꾸준히 해야 한다. - 직접 코드를 개선해보면서 배우는 것이 가장 좋은 학습법이다.- 오버 엔지니어링을 피하기 위해 현실적인 선택을 하자. - 너무 많은 추상화나 인터페이스는 오히려 유지보수를 어렵게 만들 수 있다.- "한계까지 사용해본 개발자가 적정 수준을 더 잘 안다." - 새로운 기술을 도입할 때는 충분히 사용해보고, 어느 수준에서 활용해야 할지 감을 잡아야 한다.---## 8. Outro### ✨ 요약- 처음부터 완벽한 추상화를 만들려고 하기보다는, 개선해 나가는 과정이 중요하다. - 개발을 하면서 추상화의 필요성이 점점 더 명확해지는 순간이 있다. - 이를 경험하면서 더 좋은 설계를 할 수 있게 된다.- 변경이 용이한 코드를 작성하는 것이 가장 중요하다. - 코드의 가독성을 유지하면서도, 변경이 필요할 때 쉽게 수정할 수 있도록 구조를 만들어야 한다.---## 최종 결론- 객체 지향 설계의 핵심은 변경에 유연한 구조를 만드는 것 - 좋은 코드는 단순히 동작하는 코드가 아니라, 이해하기 쉽고 유지보수하기 쉬운 코드 - 완벽한 설계는 존재하지 않으며, 지속적인 개선을 통해 더 나은 구조를 찾아가는 과정이 중요하다.
백엔드
・
클린코드
2025. 03. 09.
0
클린코드 발자국 1주차
# 📚 학습 요약 & 회고 ## 1. 추상의 중요성### ✨ 요약- 추상화는 불필요한 정보를 제거하고 핵심 개념만 남기는 과정.- 추상은 항상 구체적인 실제에서부터 시작해야 한다. ### 📝 회고- 적절한 추상화의 중요성 정말 중요한 핵심만을 남겨서 표현하였는지 확인 할 것- 추상화의 가장 대표적인 행위 이름 짓기의 중요성 ## 2. 논리적 사고의 흐름### ✨ 요약- Early Return을 사용하여 중첩된 조건문을 줄이고 가독성을 향상시킨다.- ! 지양하고, 긍정 조건을 활용하여 isValid(), isEmpty() 같은 메서드를 사용한다. ### 📝 회고- ! 연산자를 제거한 코드가 훨씬 읽기 쉽다.- Early Return을 적극 활용해 중첩을 줄이는 습관을 길러야겠다. ## 3. 객체 지향 패러다임### ✨ 요약- 절차 지향 vs 객체 지향 vs 함수형 프로그래밍의 차이- 객체는 데이터와 기능을 함께 가지며, 객체 간 협력을 통해 동작한다.- 관심사의 분리(Separation of Concern)를 통해 높은 응집도, 낮은 결합도를 유지해야 한다. ### 📝 회고- 객체 간 협력을 고려하며 설계하는 것이 중요함 ## 4. 객체 설계 원칙 (SOLID)### ✨ 요약- S: 하나의 클래스는 하나의 책임을 가져야 한다.- O: 확장은 열려있고, 수정은 닫혀 있어야 한다.- L: 부모 클래스를 자식 클래스로 바꿔도 문제가 없어야 한다.- I: 필요한 기능만 인터페이스로 제공한다.- D: 구현 클래스가 아닌 인터페이스에 의존해야 한다. ### 📝 회고- SOLID 원칙을 활용하면 유지보수하기 쉬운 확장 가능한 코드를 만들 수 있음을 배웠다.- OCP를 지키기 위해 다형성을 적극 활용해야 한다는 점이 중요함.## 🎯 최종 회고- "코드를 읽기 쉽게 만드는 것이 가장 중요한 원칙이다!"
백엔드
・
클린코드