블로그
전체 4#카테고리
- 백엔드
2024. 10. 27.
0
[인프런 워밍업클럽 백엔드 스터디 2기] 4주차 발자국
4주차 학습내용Practical Testing : 실용적인 테스트 가이드강의를 학습하며 작성한 내용입니다.MOCK을 마주하는 자세Mockito로 Stubbing하기테스트에 불필요한 과정을 줄이기 위해 mock을 사용해서 테스트를 한다.메일전송같이 긴 작업 (트랜잭션에는 참여하지 않아도 되는)에는 @Transactional을 걸지 않는 게 좋다.stubbing : mock 객체에 원하는 행위를 정의하는 것Test DoubleStub - 상태 검증 (State Verification)Mock - 행위 검증 (Behavior Verification)순수 Mockito로 검증해보기@Mock : @ExtendWith(MockitoExtension.class) 를 함께 사용해야 함.verify() : mock객체에 대해 원하는 메서드가 특정 조건으로 실행되었는지 검증@Spy : 한 객체에서 일부는 실제 객체를 쓰고 싶고 나머지 일부만 stubbing을 하고 싶을 때 사용Mockito의 @Spy는 실제 객체를 기반으로 만들어지기 때문에 when절을 쓸 수 없다. 더 나은 테스트를 작성하기 위한 구체적 조언테스트 환경의 독립성을 보장하자@DisplayName("재고와 관련된 상품이 포함되어 있는 주문번호 리스트를 받아 주문을 생성한다.") @Test void createOrderWithStock() { // given Stock stock1 = Stock.create("001", 2); Stock stock2 = Stock.create("002", 2); stock1.deductQuantity(3); ... //when //then assertThatThrownBy(() -> orderService.createOrder(request, registeredDateTime)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("재고가 부족한 상품이 있습니다."); } 재고가 2개인데 3개를 차감하려고 하면 차감할 수량이 없다고 테스트에 오류가 발생한다. 테스트하고자 하는 행위는 createOrder인데 재고 차감이라는 다른 행위를 끌어다쓰면서 두 가지 케이스가 혼합이 되어있기 때문에 주문 생성 자체에 대한 테스트 실패가 아니라 given절을 생성하다가 테스트를 실패하게 된다. 이런 경우 테스트가 더 복잡해지면 실패 원인을 유추하는 데 어려움이 생긴다.테스트에 독립성이 보장되지 않으면 복잡해졌을 때 실패 원인을 유추하는 데 어려움이 생긴다.테스트에서는 팩토리 메서드를 지양하는 게 좋다.팩토리 메서드도 어떤 의도를 갖기 때문에 순수한 생성자나 빌드로 given절을 구성하는 게 더 좋다.최대한 독립성을 보장해서 테스트 환경을 구성하는 것이 좋다.테스트 독립성을 보장하자기본적으로 테스트는 순서에 무관해야 한다. a테스트가 수행된 이후에 b테스트가 수행되어야 성공한다는 개념은 없어야 한다. 독립적으로 언제 수행되던지 같은 결과를 내야 한다.Test Fixture테스트를 위해 원하는 상태로 고정시킨 일련의 객체공통의 fixture는 테스트의 결합도를 높여 모든 테스트에 영향을 주기 때문에 지양하는 것이 좋다.builder의 parameter에는 클래스 내에서 필요한 것들만 남겨놓는 것이 좋다. 미션레이어 아키텍처에 대해 나만의 언어로 풀어 쓰는 미션이었는데, 강의를 듣고 공부도 했지만 막상 설명한다고 생각하고 쓰려니 헷갈리는 부분들이 생겨서 다시 공부해보면서 작성했다.Mockito 관련 애노테이션의 차이점을 정리하고, 테스트코드를 적절히 배치하는 미션이었는데 배운 걸 적용한다는 느낌이 잘 드는 미션이어서 수월하게 수행한 것 같다. 💬 회고하루에 들어야 하는 강의 양이 생각보다 많아서 매일매일 진도표대로 듣지는 못했지만 워밍업클럽을 통해 약간의 강제성이 생겨 들어보고 싶었던 로드맵을 빠르게 훑어볼 수 있었다. 리팩토링이나 제대로 된 테스트코드 작성은 멀게만 느껴지고 막막했는데 강의를 듣고, 미션을 수행하다보니 어떤 방향으로 공부하고 적용시켜 나가야할지 어느 정도 감이 잡힌(?) 느낌이다. 계속 공부하면서 내것으로 만들기 위해 적용하고 발저시키는 연습을 해야겠다.
백엔드
2024. 10. 20.
0
[인프런 워밍업클럽 백엔드 스터디 2기] 3주차 발자국
3주차 학습내용Practical Testing : 실용적인 테스트 가이드 강의를 학습하며 작성한 내용입니다. 단위 테스트테스트케이스 나누기 해피 케이스 (모든 게 올바르게 동작) 예외 케이스 테스트 하기 어려운 영역 구분하고 분리하기 (외부로 분리할수록 테스트 가능한 코드가 많아짐)외부에서 들어오는 값에 의존하는 코드 (현재 날짜/시간, 랜덤 값) 외부에 영향을 주는 코드 (표준 출력, 메시지 발송) TDD: Test Driven Development: 프로덕션 코드보다 테스트 코드를 먼저 작성해서 테스트가 구현 과정을 주도하도록 하는 개발 방법- 복잡도가 낮은(유지보수가 쉬운), 테스트 가능한 코드로 구현할 수 있다.- 쉽게 발견하기 어려운 예외상황을 놓치지 않게 해준다.- 과감한 리팩토링이 가능하다. 테스트는 [ ]다DisplayName을 섬세하게@DisplayName("음료 1개를 추가") 보다는 @Display("음료를 1개 추가하면 주문 목록에 담긴다.") 처럼 테스트의 행위에 대한 결과까지 기술하기"~테스트"라는 표현 지양하기, 문장으로 작성 도메인 용어를 사용해서 좀 더 추상화된 내용 담기테스트의 현상 중점으로 기술하지 말 것주문을 생성하면 실패한다(x)주문을 생성할 수 없다. BDD (Behavior Dirven Development) 스타일로 작성하기 : TDD에서 파생된 개발 방법 필수 단위 테스트에 집중하기 보다 시나리오에 기반한 "테스트 케이스" 자체에 집중한다. given / when / then 💻미션 - 테스트코드 작성 minsweeper에 대한 테스트 코드를 작성했봤는데 사실 어떤 부분을 어떻게 테스트해야할지 감이 오지 않았다. 간단한 게임이라도 내부 동작에 대해 작성할 테스트 코드가 많다는 건 알겠는데 @DisplayName과 bdd스타일 따르는 것만 신경 쓰고 정작 단위 테스트의 본질적인 목적을 잊고 작성한 것 같아서 아쉽다. 강의를 다 들은 후 다시 강의 내용을 가이드삼아 찬찬희 케이스를 나눠 작성해보고 싶다. 그리고 studycafe에 대해서도 단위 테스트 작성을 연습해봐야겠다. Spring&JPA 기반 테스트 Persistence Layer repository layer에 대한 테스트 @DataJpaTest 사용 가능 -> rollback 관련 작업 설정 필요 없음 Business Layer 재고라는 개념 도입 재고 차감 예외 테스트 assertThatThrownBy(() -> stock.deductQuantity(quantity)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("차감할 재고 수량이 없습니다.");Stock에서 체크 후 예외를 던지는 것과 OrderService에서 체크 후 예외를 던지는 것은 다르다. (같은 상황이지만 발생할 수 있는 상황이 다르기 때문에 메시지도 다르다.) Service에서는 메시지가 사용자단까지 갈 수 있기 때문에 사용자 친화적인 메시지로 작성한다.Presentation Layer 외부 세계의 요청을 가장 먼저 받는 계층 파라미터에 대한 최소한의 검증을 수행한다.MockMvc테스트 -> @WebMvcTest 사용 전체 빈 컨텍스를 다 띄우는 게 아니라 컨트롤러 레이어만 떼서 사용직렬화하기 위해 ObjectMapper 주입 💬3주차 회고 테스트 코드의 중요성은 알지만 제대로 작성해 본 적은 없어서 하나씩 따라하면서 적용시켜봐야 겠다는 생각으로 강의를 들었다. 듣다보니 테스트 코드 작성에 대한 내용 말고도 아직 스프링과 자바에서도 익숙하지 않은 개념, 헷갈리는 내용들이 많다는 걸 깨달았다. 특히 Layer별 테스트 부분은 강의가 길고 내용이 많아 힘들었지만, 테스트 작성에 대한 부분 말고도 스프링에서의 Validation 적용에 대해 다시 한 번 공부할 수 있어서 좋았다. 리팩토링 강의에서 계속 들었던 책임 분리가 테스트 코드 작성에서도 중요하다는 걸 느꼈다.그리고 앞으로 작은 프로젝트를 하더라도 기능과 서비스의 확장을 염두한 개발을 해야겠다고 생각했다.
백엔드
2024. 10. 13.
0
[인프런 워밍업클럽 백엔드 스터디 2기] 2주차 발자국
2주차 학습내용Readable Code : 읽기 좋은 코드를 작성하는 사고법 강의를 학습하며 작성한 내용입니다.1주차에 이어 강의를 듣는데 이미 진도가 약간 밀려있어서 진도표대로 수강하는 데에는 약간 어려움이 있어서 아쉽다. 코드 다듬기주석의 양면성 주석에 너무 의존하면, 추상화 레벨이 적절하지 않아 저품질 코드가 된다. 전해야 할 히스토리를 도저히 코드로 표현할 수 없을 때 주석으로 설명한다.변수와 메서드의 나열 순서 패키지 나누기 패키지를 쪼개지 않으면 관리가 더 어려워진다. (너무 쪼개도 어렵다) 대규모 패키지 변경은 conflict가 발생 가능하므로 팀원과 합의 후에! 리팩토링 연습중복 제거와 메서드 추출 객체에 메시지 보내기I/O 통합일급 컬렉션 적용 메서드 책임의 위치 - Day7 미션 사실 이번에는 시간적 여유가 부족해 미션 제출일까지 미션을 다 수행하지 못했다.기본적인 래픽토링인 중복을 제거 이후에는 '추상화 레벨 맞추기'에 집중하려고 했다. 강의를 보면서 실습할 때와 달리 코드에 대한 이해가 부족해서 어려움을 느꼈다. 강의에서 설명하시는 객체의 책임 분리와 일급 컬렉션 반환 부분은 혼자서는 생각하기가 어려웠다. 앞으로도 반복해서 학습해야 할 것 같다. 기억하면 좋은 조언들능동적 읽기 능동적 읽기 -> 눈으로만 읽지 말고, 리팩토링을 해보면서 읽자. (공백으로 단락 구분, 메서드 추출 등) git reset --hard로 언제든지 돌아갈 수 있다.도메인 지식을 늘리는 것이 능동적 읽기의 목표 오버엔지니어링 필요한 수준 이상의 엔지니어링 -> ex) 너무 이른 추상화 중간점검(라이브) 중간점검 라이브에서 사전에 받은 질문들에 대한 답변과 미션 피드백을 들을 수 있었다. 다른 분들은 개발과 리팩토링에 대해 어떤 고민을 갖고 있는지와 그에 대한 강사님의 생각을 들을 수 있어서 유익한 시간이었다. (신입 기준) 주니어 개발자를 뽑을 때 빠르게 적응할 수 있는지와 기존 학습의 주도성을 주로 보며 기술 자체는 크게 기대하지 않는다.알고 있다고 한 기술에 대해서는 어느 정도까지 학습과 고민을 했는지 보여줄 수 있어야 한다.커밋은 의도적으로 나누는 것이 좋다.당장은 완벽한 클린코드를 만드는 게 불가능하더라도 최소한의 리팩토링을 하고 주석이라도 달아 다음에 어떤 방향으로 갈지 기록해둬야 한다. 사실 강의를 들으면서도 '내가 리팩토링 강의를 들을만큼 충분한 개발 지식과 경험을 갖고있는 게 맞나?'하는 생각이 들 때가 있지만 내가 하고 있는 개발에도 충분히 적용할만한, 적용해야 하는 부분들이 많은 것 같다.
2024. 10. 06.
0
[인프런 워밍업클럽 백엔드 스터디 2기] 1주차 발자국
1주차 학습내용 추상잘 쓰여진 코드라면, 한 메서드의 주제는 반드시 하나다.생략할 정보와 의미를 부여하고 드러낼 정보를 구분하는 것이 중요하다. 추상화는 코드의 양보다 의미가 중요하다. 추상화 레벨이 갑자기 달라지면 코드를 읽는 입장에서 이해하는 데 어려움이 있기 때문에 어떤 의미를 담을 때 주변에 비해 너무 구체적인 의미를 담지 않는지에 대해 생각해야 한다. 논리,사고의 흐름 Early return 사고의 depth 줄이기 부정의 의미를 담는 다른 단어가 존재하는지 확인, 부정어구로 메서드명 구성 ('!'는 가독성이 떨어짐) 예외처리 -> 항상 NullpointException을 방지하는 방향으로 경각심을 갖고 코드 작성 메서드 설계시 return null을 자제하기! 객체 지향 패러다임 새로운 객체를 만들 때 1개의 관심사로 명확하게 책임이 정의되었는지 확인하기 setter 사용 자제하기, getter도 처음에는 사용 자제하고 반드시 필요한 경우에 추가 필드의 수는 적을수록 좋다. 객체 지향 적용하기 상속은 결합도가 높아 수정이 어려우니 조합과 인터페이스라는 유연한 구조를 사용하자. Value Object (VO) : 도메인의 어떤 개념을 추상화하여 표현한 값 객체 Entity와 VO의 차이는 식별자의 유무 (VO는 식별자가 없음)getter로 컬렉션을 반환해야 한다면 새로운 컬렉션으로 만들어 반환(외부의 조작 피하기 위해) 1주차 미션 [DAY2 미션]추상과 구체의 예시로 "집에서 아이스 라떼 만들기"의 과정을 설명했다. 일상 속의 예시를 생각하는 과정에서 추상과 구체라는 개념을 더 이해할 수 있었다.[DAY4 미션]예시 코드를 리팩토링 하는 과정에서 시간적 여유가 부족해 꼼꼼히 보지 못해 아쉬운 부분이 있다. (다시 리팩토링 해 볼 예정) SOLID에 대해 알고는 있지만 막상 설명하려고 하면 잘 나오지 않았는데 이번 기회에 나만의 언어로 적어보면서 더 정리할 수 있었다.회고가볍게 생각했는데 생각보다 매일 일정한 분량의 강의를 듣는다는 것이 쉽지 않았다. 그리고 막연히 '클린코드'는 좀 더 기술적인 역량이 갖춰진 상태에서 접해야 하는 부분이라고 생각했는데 강의를 듣다보니 내가 짜고 있는 코드에도 충분히 적용할만한 내용들이 많았다. 그리고 강의를 들으면서 객체지향에 대한 개념과 추상과 구체에 대한 내용을 코드 리팩토링에 어떤 식으로 적용할 수 있는지에 대해 생각해 볼 수 있었다.