won1680
수강평 작성수
-
평균평점
-
블로그
전체 5![[워밍업 클럽 2기 BE 클린코드 & 테스트] 4주차 발자국](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 10. 25.
0
[워밍업 클럽 2기 BE 클린코드 & 테스트] 4주차 발자국
Practical Testing: 실용적인 테스트 가이드 수강과 미션을 진행하고 남긴 4주차 발자국입니다. 강의테스트 코드 작성 방법 뿐만 아니라, 테스트에 대한 마음가짐과 관점까지 배울 수 있었습니다. 강의에서 다루는 도구들 외에도 다양한 도구들을 소개해주었고, Hexagonal Architecture와 같이 추가로 학습하면 좋을 내용도 제시해주어 시야를 넓힐 수 있었습니다. 특히 Classicist, Mockist와 같이 테스트를 바라보는 다양한 관점을 접하면서 테스트에 대해 더 깊이 있게 생각해볼 수 있었습니다.'테스트는 문서다'라는 개념이 처음에는 막연하게 느껴졌습니다. 하지만 @DisplayName을 활용한 테스트 설명과 REST Docs를 통한 API 문서화를 실습하면서, 테스트 코드가 어떻게 문서의 역할을 할 수 있는지 구체적으로 이해할 수 있었습니다.Fixture 관리에 대한 부분은 특히 인상적이었습니다. 과거에는 단순히 코드 중복을 줄이는 데만 초점을 맞추었는데, 이번 강의를 통해 테스트 간의 독립성 보장과 가독성 측면에서 Fixture를 다루는 새로운 관점을 배울 수 있었습니다. 미션 (Day15, Day18)강의 내용을 정리하자라는 목표로 미션을 진행했습니다.시간이 부족해서 예시코드나 이전 프로젝트 경험을 녹여 구체적인 표현으로 정리하지 못한 것이 아쉽습니다. 회고워밍업 클럽에 참여하지 않았다면, 두 강의를 끝까지 듣지 못했을 것 같습니다. 4주간의 진도표 덕분에 학습 계획을 세우는 부담이 줄었고, 이러한 가이드라인이 없었다면 아마도 강의를 끝까지 완주하기 어려웠을 것 같습니다. 다만 중반에 다른 일정과 겹치게 되어 강의는 수강했지만, 충분히 소화하지 못한 부분이 있어 아쉽습니다. 짧은 기간에 많은 것을 채우다보니, 아직까진 작은 기능을 구현해도 배운 내용을 적절히 활용하기 보다는 고려할 것이 너무 많아 머리가 어지러운 경우가 많습니다. 그래도 이 과정이 강의에서 배운 내용을 소화하는 과정이라고 생각합니다. 이 과정이 끝나고 강의를 다시 들으면 이번에는 느껴보지 못했던 새로운 부분이 보일 것 같습니다.
![[워밍업 클럽 2기 BE 클린코드 & 테스트] Mission Day18](https://cdn.inflearn.com/public/main/blog/default_thumbnail.png?w=260)
2024. 10. 25.
0
[워밍업 클럽 2기 BE 클린코드 & 테스트] Mission Day18
Practical Testing: 실용적인 테스트 가이드를 수강하고 진행한 미션입니다. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.@MockMockito에서 제공하는 어노테이션스프링 컨텍스트 없이 단위 테스트 가능가짜 객체를 생성, 모든 메소드는 기본적으로 아무 동작도 하지 않음 -> Stubbing 필요@MockBeanSpring Boot Test에서 제공하는 어노테이션스프링이 mock 객체를 주입 -> 주로 통합테스트에 사용스프링 빈을 mock 객체로 대체할 때 사용@SpyMockito에서 제공하는 어노테이션스프링 컨텍스트 없이 단위 테스트 가능실제 객체를 사용실제 객체를 부분적으로 mock할 때 사용mock하지 않은 메소드는 실제 객체의 메소드가 동작함@SpyBeanSpring Boot Test에서 제공하는 어노테이션스프링이 spy 객체를 주입 -> 주로 통합테스트에 사용스프링 빈을 spy 객체로 대체할 때 사용 -> 실제 객체를 감싸서 일부 기능만 mock@InjectMocksMockito에서 제공하는 어노테이션@Mock이나 @Spy 등으로 생성된 객체를 주입시킴주로 테스트 대상 클래스의 의존성을 주입할 때 사용 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요? (@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)요구 사항게시판 게시물에 달리는 댓글을 담당하는 Service Test댓글을 달기 위해서는 게시물과 사용자가 필요하다.게시물을 올리기 위해서는 사용자가 필요하다.@BeforeEach void setUp() { ❓ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 1-6. 댓글 생성 // given ❓ // when ❓ // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 2-7. 댓글 수정 // given ❓ // when ❓ // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { 3-1. 사용자1 생성에 필요한 내용 준비 3-2. 사용자1 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 3-9. 사용자2가 사용자1의 댓글 수정 시도 // given ❓ // when ❓ // then 검증 } RefactoringFixture에 부분을 최대한 보수적인 관점으로 대했다. 꼭 필요한 부분만 BeforeEach 절에 담으려고 했고, 애매한 부분은 각 테스트 내에 담았다."자신이 작성한 댓글이 아니면 수정할 수 없다." 부분이 애매하다고 느꼈다. 검증 단계에서 서로 다른 사용자라는 부분을 추출해서 보여줄 여지가 있다고 생각해 해당 테스트 내에 다 담았다. 구체적인 로직이 있으면 검증에 영향이 가지 않고, 다른 테스트에서도 공통적으로 사용되는 부분이 있으면 setUp으로 이관해도 좋다고 생각한다.@BeforeEach void setUp() { 사용자 생성에 필요한 내용 준비 게시물 생성에 필요한 내용 준비 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 사용자 생성 게시물 생성 댓글 생성에 필요한 내용 준비 // when 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 사용자 생성 게시물 생성 댓글 생성에 필요한 내용 준비 댓글 생성 // when 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 사용자1 생성에 필요한 내용 준비 사용자1 생성 사용자2 생성에 필요한 내용 준비 사용자2 생성 사용자1의 게시물 생성에 필요한 내용 준비 사용자1의 게시물 생성 사용자1의 댓글 생성에 필요한 내용 준비 사용자1의 댓글 생성 // when 사용자2가 사용자1의 댓글 수정 시도 // then 검증 }

2024. 10. 21.
0
워밍업 클럽 2기 백엔드 클린코드&테스트 코드 3주차 발자국
Practical Testing 실용적인 테스트 가이드 섹션 1~6 중반까지 듣고 작성한 발자국 입니다. 강의테스트 코드의 필요성에 대한 설명을 다른 사람의 언어로 들을 수 있는게 좋았습니다.강의에서 다루는 내용 뿐만 아니라 추가적으로 학습할 방향성을 제시해주어서 좋았습니다.테스트 코드가 문서로서의 역할도 한다는 의견이 생각지도 못한 부분이었습니다. 과거 진행했던 미션에서 요구 사항에서 제대로 이해되지 않았던 부분이 테스트 코드를 보면서 구체적으로 이해되었던 경험이 떠올랐습니다.테스트를 작성하는 역량주니어 개발자에게 가장 기대하는 요소 중 하나채용 시 구현 과제 등에서 테스트 작성 여부, 테스트 코드 구현 방식을 확인소프트웨어의 품질을 보증하는 방법으로, 그 중요성을 알고 있는지를 확인테스트 코드를 통해 우리가 얻고자 하는 것빠른 피드백자동화안정감테스트 코드를 작성하지 않는다면변화가 생기는 매 순간마다 발생할 수 있는 모든 Case를 고려해야 한다변화가 생기는 매 순간마다 모든 팀원이 동일한 고민을 해야 한다빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다 미션 : Study Cafe 테스트 코드 작성요구 사항이 변해도 테스트 코드는 일관성을 가지고 유지되는 것도 테스트 코드 작성에서 중요한 부분이라고 생각했습니다. 그래서 요구 사항이 변해도 변하지 않을 것 같은 기능들 위주로 테스트 코드를 작성했습니다.https://github.com/Frod90/readable-code/tree/mission/day12 회고여러가지 일정이 겹쳐 정신없는 한 주였습니다. 이번 주를 보내며 느낀 것은 '욕심 부리지 말고 할 수 있는 것들부터 하자' 입니다. 다음주는 이번 주보다 일정이 많아서 하지 말 것들을 미리 정해두고 시작하는 한 주를 시작해야 할 것 같습니다.

2024. 10. 13.
0
워밍업 클럽 2기 백엔드 클린코드&테스트 코드 2주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법 섹션 6~7까지의 강의 수강과 미션을 진행하고 남긴 2주차 발자국입니다. 강의평소에 주석을 남기기 보단 따로 문서로 작성해서 관리하는 편이었는데, 주석에 대한 생각이 바뀌었고, 활용 방안에 또한 다시 생각해 볼 수 있는 기회였습니다.강의에서 제시한 변수와 메서드를 배치하는 하나의 방법을 배웠습니다. 메서드 순서 또한 팀원들과 협의 후 프로젝트 전반에서 일관성을 지켜야하는 컨벤션이라는 생각이 들었습니다. 미션스터디 카페 리펙토링코드를 분석하는 과정에서 메서드 분리가 필요한 부분들을 발견했습니다. 그러나 합성, VO(Value Object), 일급 컬렉션을 적용하면 해당 메서드들이 불필요해질 것으로 판단하여, 메서드 분리 전에 합성을 활용한 리팩토링을 진행했습니다.합성과 VO 사용에 익숙하지 않아 리팩토링을 완전히 마무리하지 못한 채 마감 기한에 도달했고, 계획했던 개선 사항들을 전부 적용하지 못한 상태로 미션을 제출하게 되었습니다.이 경험을 통해 점진적 리팩토링의 필요성을 느꼈습니다. 회고Keep 자소서 작성을 맞췄습니다.정해진 루틴에 맞춰 운동했습니다.Problem자소서 작성 때문에 강의와 미션에 몰입할 수 없었습니다.Try다음 주차 끝나기 전까지 밀린 강의 듣기점진적 리펙토링을 통해 스터디 카페 미션 다시 해보기

2024. 10. 06.
0
워밍업 클럽 2기 백엔드 클린코드&테스트 코드 1주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법 섹션 1~5까지의 강의 수강과 미션을 진행하고 남긴 1주차 발자국입니다. 강의Section 1: 소개환경 설정 및 용어 정리Section 2: 추상화추상과 구체'하나의 세계 안에서는 추상화 레벨이 동등해야 한다'는 메시지가 인상 깊었습니다. 이는 미래의 나와 동료 개발자들을 위한 배려라는 점이 특히 의미있었습니다. 개발 당시에는 해당 도메인과 비즈니스 로직이 머릿속에 선명하기 때문에 구체적 표현이 크게 문제되지 않습니다. 하지만 시간이 지나 다시 코드를 읽을 때 이해가 어려워 시간을 낭비한 경험이 많았습니다. 이러한 문제는 추상화를 통해 해결할 수 있으며, 이것이 바로 미래의 나를 배려하는 방법이라고 깨달았습니다.네이밍메서드명 작성 시 메서드 이름과 매개변수를 전치사로 연결하는 방법은 새로운 인사이트였습니다. 특히 오버로딩을 적용할 때 효과적일 것이라는 생각이 들었습니다.Section 3: 논리와 사고의 흐름부정어 처리부정어 처리를 위해 별도의 메서드를 만드는 방식은 깊이 고민해보지 않았던 부분입니다. 강의에서 제시된 코드를 보니 직관성이 크게 향상되어, 필요한 접근이라고 느꼈습니다.Optional그동안 Optional을 깊이 이해하지 못한 채 관성적으로 사용해왔습니다. 강의에서 Optional을 매개변수로 사용하지 말아야 하는 이유와 orElse()와 orElseGet()의 소스코드를 분석하면서, Optional의 잘못된 사용을 반성하게 되었습니다.Section 4: 객체 지향 패러다임SOLID이 부분은 미션과 밀접하게 연관되어 있었습니다. 개발 공부 과정에서 SOLID 원칙을 자주 접했지만, 실제 코드에 적용하기는 어려웠습니다. 따라서 SOLID에 대한 개인적 견해를 질문 형태로 정리하여, 작성한 코드가 이 원칙들을 잘 반영하고 있는지 확인하는 체크리스트로 활용하면 좋겠다고 생각했습니다.Section 5: 객체 지향 적용객체 지향 구현을 위한 다양한 도구VO, 일급 컬렉션, Enum, 다형성 등 객체 지향을 실현하기 위한 유용한 도구들이 소개되었습니다. VO와 일급 컬렉션은 간단한 미션에서만 적용해보았고, 실제 프로젝트에서는 활용하지 못했습니다. 이는 필요성을 제대로 인식하지 못했기 때문입니다. 강의를 통해 이러한 도구들의 용도와 가치를 깊이 이해하게 되었습니다. 시간 관계 상 프로젝트를 개선하지는 못했지만, 객체 지향을 효과적으로 적용할 수 있는 방향성을 발견할 수 있었습니다.1주차 강의 회고Keep수강과 미션을 일정에 맞춰서 진행함강의를 들으면서 이전에 진행한 프로젝트에 적용할 부분을 찾아본 것Problem시간 분배강의 수강에 예상보다 많은 시간이 소요됨 자소서, 새로 시작하는 프로젝트에 쓸 수 있는 시간이 부족했음Try시간 분배 다시 생각일주일 계획 짜기 DAY4 미션아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다. 요구사항 ✔ 사용자가 생성한 '주문'이 유효한지를 검증하는 메서드.✔ Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.)✔ 필요하다면 메서드를 추출할 수 있다.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; } 리펙토링 후 코드public class OrderService { private final LogPrinter logPrinter; public OrderService() { logPrinter = new LogPrinter(this.getClass()); } public boolean validateOrder(Order order) { if (order.hasNoItems()) { logPrinter.printInfo("주문 항목이 없습니다."); return false; } if (order.hasInvalidTotalPrice()) { logPrinter.printInfo("올바르지 않은 총 가격입니다."); return false; } if (order.hasNoCustomerInfo()) { logPrinter.printInfo("사용자 정보가 없습니다."); return false; } return true; } } public class Order { private final List items; private final Customer customer; public Order(List items, Customer customer) { this.items = items; this.customer = customer; } public boolean hasNoItems() { return items == null || items.isEmpty(); } public boolean hasInvalidTotalPrice() { return calculateTotalPrice() 구현 과정if문 depth 줄이기if문 조건절 메서드로 바꾸기(추상)로그 출력을 메서드로 빼서 일괄적으로 처리하게 하기구현 중 고민 사항Order 객체가 items의 총 가격을 계산하는 기능의 책임을 가지는 것이 맞는가?계산기 객체 추가 구현 고려validateOrder(Order order) 기능 자체가 Order 객체가 가져야 할 책임이지 않는가?validateOrder(Order order) 메서드를Order 객체로 옮기는 것 고려하기구현 후 아쉬웠던 점매직 넘버, 스트링 처리하지 않은 것로그 출력 기능에서 warning등 다른 메서드 활용을 고려하지 않은 것Order 객체에서 items나 customer 필드 관련 null 검증을 생성자에서 하지 않은 것 2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP한 클래스는 하나의 책임만 가져야 한다클래스를 변경하는 이유는 오직 하나여야 한다‘한 클래스, 기능이 가지는 책임의 범위가 어디까지 인가?’ 생각하기OCP확장에는 열려 있고, 변경에는 닫혀 있어야 한다기존 코드의 변경을 최소화 하면서 새로운 기능을 추가할 수 있어야 한다객체 지향에 집착하지 않기. 요구 사항에 따라 객체 지향을 고수하는 것보다 절차 지향이 확장에 더 열려있을 수 있다.LSP상위 타입의 객체를 하위 타입의 객체로 치환해도 프로그램은 정상적으로 동작해야 한다하위 클래스는 상위 클래스의 규약을 지켜야 한다'어떤 동작을 추상화하면 모든 하위 클래스가 올바르게 구현할 수 있을까?' 생각하기ISP클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다큰 인터페이스를 작은 단위로 분리해야 한다‘필요로 하는 메서드만 제공하는가?’ 생각하기DIP고수준 모듈은 저수준 모듈에 의존하지 않아야 한다. 둘 모두 추상화에 의존해야 한다추상화는 세부 사항에 의존하지 않야야 한다. 세부 사항이 추상화에 의존해야 한다SOLID원칙을 위한 원칙이 되지 않도록 하자팀의 상황과 프로젝트의 요구 사항에 맞게 사용하자미션 회고리펙토링 미션을 시간에 쫒겨 급하게 한 것이 아쉽다. 회고록을 작성하는 중에도 계속 아쉬운 점이 보임.일정 때문에 다른 사람의 미션을 읽어보지 못한 것이 아쉽다.같은 이유로 코드 리뷰를 받지 못한 것이 아쉽다.SOLID에 대한 내 생각을 질문 형태로 남기는 것은 좋은 생각이었던 것 같다. 이를 발전시켜 더 구체적인 질문으로 남기기




