블로그

dohi

워밍업 클럽 4기 - 백엔드 1주차 회고

수정내역썸네일 추가1주차 회고강의 수강‘추상과 구체’ 과제를 통해 추상 개념에 대해 다시 한번 생각해볼 수 있었다.매직 넘버를 접하며, 추상적인 값은 별도 변수로 분리해 명확하게 표현하는 것이 중요하다는 점을 알게 되었다.추상화 레벨 개념을 통해 코드의 표현 단계와 책임 분리에 대해 고민해보는 계기가 되었다.내 코드를 분석하면서, 읽는 사람이 뇌의 메모리를 얼마나 사용할지 생각해보았고, 얼리 리턴을 적용해 리팩토링을 시도했다.특히 ‘부정어를 대하는 자세’에서는 코드에 부정 표현이 들어갈 때의 가독성과 인지 부담에 대해 다시 생각해보게 되었다.이점은 왜 이렇게 사용해야할 까 라는 생각을 했었지만 SOLID에서 이렇게 많이 사용하는 구나를 하면서 배우게 되었다.과제를 직접 수행하면서, 배운 내용을 실전에서 적용해보며 리팩토링 감각을 조금씩 익혀갔다.SOLID 원칙 각 요소를 복습할 수 있었고, 이를 실제 코드에 어떻게 적용해볼 수 있을지 고민해보는 기회가 되었다.특히 getter setter에 사용에 대해서 고려를 많이 하게 되었다.  미션Day 2추상을 고려할때 일단 사람들 간에 약속적인 의미로 생각하였다구체에는 자세하게 적었지만 불필요한 내용을 간추리기 라고 생각하였다. Day 4AS-IS에 있는 코드를 다 분석하고 TO-BE에는 배운 코드를 다 이용하려고 하였다해당 과제에 자세하게 적긴했지만일단 미리 리턴을 하도록 정리하였고부정어 처리그후 예외처리로 마무리하였다. SRP :한 클래스는 딱 하나의 역할만 하자.OCP :기존 코드를 고치지 않고, 기능을 확장할 수 있어야 한다.LSP:부모 타입을 사용하는 곳에 자식 타입을 써도 제대로 작동해야 한다.ISP :쓸데없는 인터페이스는 나눠서 큰 종합세트를 주지말고 작은 인터페이스 여러개가 좋다DIP:고수준 모듈은 저수준 모듈에 의존하지 말고, 추상(인터페이스)에 의존하자.

백엔드워밍업클럽워밍업클럽4기백엔드워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - 1주 차 발자국

👣1주 차 발자국 ✅ 강의 요약- 추상 우리가 클린 코드를 추구하는 이유 - 가독성이해가 잘 된다.유지보수 하기 쉬워진다. 시간과 자원이 절약된다.클린코드를 관통하는 주제 - '추상' 추상이란?중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버리는 것.적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽도록 돕는다. 적절한 추상화는 무엇인가?이름 짓기단수,복수 구분이름 줄이지 않기은어/방언 사용 금지비슷한 상황에서 자주 사용하는 단어, 개념 습득하기  메서드로 추상화한 메서드의 주제는 하나추상화된 구체를 유추할 수 있는 적절한 메서드 선언부 구성추상화 레벨 동등하게 구성하나의 세계 안에서는 추상화 레벨이 동등해야 한다. 매직 넘버, 매직 스트링의미 있는 숫자,문자열은 상수로 추출해 가독성과 유지보수성을 높인다.- 논리, 사고의 흐름아래의 과정들을 통해 읽기 좋은 코드를 만들어 뇌 메모리를 적게 쓰게 한다. early returnelse 사용을 지양해 아래쪽 코드를 읽을 필요를 줄인다.사고의 depth 줄이기추상화를 통해 사고의 depth를 줄이고, 사용할 변수는 가깝게 설정한다.공백 라인의미 단위를 공백라인으로 나누어 가독성을 높인다.부정어 부정어구를 사용 하지 않아도 된다면, 사용 안하게끔 리팩토링 한다.부정의 의미를 담는 다른 언어나 부정어구로 메서드명을 만들어 부정 연산자 사용을 지양한다.예외처리예외 발생할 가능성을 낮춘다.의도한 예외와 예상하지 못한 예외를 구분해 작성한다.항상 NPE를 생각하자.- 객체 지향 패러다임객체란 결국 추상화된 데이터 + 코드객체가 여러개 생기면서 객체간 협력과 책임을 구분하는게 중요해짐. 객체간 협력, 책임의 관점에서 추상화를 생각해야 한다.관심사에 따라 객체를 나눈다.높은 응집도, 낮은 결합도가 생긴다.객체에 메세지를 던져야 한다.getter/setter를 지양한다. - SOLIDSRP: 하나의 클래스는 하나의 변경 이유만을 가져야 한다.OCP: 기존의 코드 수정없이 시스템의 기능을 확장할 수 있어야 한다.LSP: 상속 구조에서, 자식클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.ISP: 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다. (그럴 경우, 인터페이스를 분리해야 한다.)DIP: 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. - 객체 지향 적용하기상속과 조합상속은 결합도가 높다.중복이 생기더라도 조합과 인터페이스를 활용하자.Value Object불변성, 동등성, 유효성을 보장해야하며 도메인의 개념을 추상화한 객체Entity식별자가 존재. 식별자가 같으면 동등한 객체로 취급한다.일급 컬렉션컬렉션을 Wrapping한 객체컬렉션을 추상화시켜 가공된 로직을 넣을 수 있다.getter로 반환 해야 할 경우, 새로운 컬렉션을 만들어서 반환Enum상수의 집합상태와 행위를 한 곳에서 관리할 수 있는 추상화된 객체변경이 잦으면 DB로 관리하는 걸 고려다형성 활용 변하는 것과 변하지 않은 것을 구분해서 추상화숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것변경이 많이 일어날 것 같은 미래를 예측해서 최선의 설계를 하고, 다시 돌아올 수 있도록 코드를 만들자.✅ 강의 회고코드를 따라 치면서 많이 학습했다고 느꼈지만, 어느 학습이던 복습이 중요하겠지만 개념적인 부분은 특히 복습이 중요한 것 같다. 강의 예제(코드)가 좋아서 다시 돌려서 리팩토링 해보면서 따라가니까 좀더 확실하게 얻을 수 있었다.학습 시간적인 측면에서 관리를 잘 못해서 처음에 몰아 듣고, 마지막에 몰아 듣게 되었는데 강의가 주제별로 잘 나뉘어져 있기 때문에 시간 관리를 위해서라도 잘 쪼개서 시간관리를 잘 해서 완강과 학습에 도움이 되도록 해야할 것 같다. ✅ 미션 과정특별히 어렵게 고민한 것은 없었다.추상과 구체, SOLID는 일상생활에서 자주 볼 수 있는 것으로 나타내면 좋을 것 같아 각각 자동차, 스마트폰에 빗대어 표현했다. ✅ 미션 회고'자기만의 언어로 키워드 정리하기' 가 정말 중요한 것 같다.어떤 분야던 학습하면서 머리에 이미지화 해서 기억한다던가 하면 오래 기억한다고 하는데, 이번 과정에서 그것을 느꼈다.SOLID 원칙을 달달 외우기는 했지만 설명을 한다면 어떻게 ...?이번 미션을 통해 자신만의 언어로 이미지화 했기 때문에 누구에게 설명할 때는 딱딱한 텍스트 보다는 부드럽게 설명할 수 있을 것 같다.강의 - Readable Code: 읽기 좋은 코드를 작성하는 사고법

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - 4주차 발자국

👣4주 차 발자국 ✅ 강의 요약섹션 7. Mock을 마주하는 자세 Test double 테스팅이 어려운 경우, 실제 객체를 대신해 테스트를 진행할 수 있도록 만들어주는 객체DummyFakeStubSpyMock Dummy아무 것도 하지 않는 깡통 객체객체는 전달되지만 사용되지 않는 객체이다.Fake단순한 형태로 동일한 기능은 수행하지만, 프로덕션에 쓰기에는 부족한 객체Stub테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체고정값을 반환하는 객체로, 정해진 응답이 필요시 사용SpyStub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체일부는 실제 객체처럼 동작시키고, 일부만 스터빙 할 수 있다.Mock행위에 대한 기대를 명세하고 그에 따라 동작하도록 만들어진 객체Stubbing ?Mock 객체의 행동을 조작하는 것.Stub은 메서드가 특정 값을 반환하도록 설정하기 때문에, 반환값에 대한 검증을 한다.Mock은 특정 메서드가 올바르게 호출 되었는지 검증한다.Stub 은 상태 검증, Mock 은 행위 검증Mokito 주요 애너테이션@Mock, @Spy, @InjectMocks, @MockBean, @SpyBean각 애너테이션 요약 - 링크BDDMockitoBDD 스타일로 Mockito를 사용할 수 있도록 지원해주는 라이브러리Mockito를 상속해서 구현한 객체로써 모든 기능은 Mockito와 동일 섹션 8. 더 나은 테스트를 작성하기 위한 구체적 조언하나의 테스트에 하나의 검증만 수행분기문, 반복문 등의 논리 구조를 지양테스트 케이스 별로 각각의 테스트 코드를 작성제어 가능한 테스트가 되도록 작성LocalDate.now(), LocalDateTime.now() 와 같이 외부요인으로 제어가 불가능한 경우는 사용하지 않는 게 좋다.제어할 수 없는 값을 제어 가능하게 변경하자.테스트 간 독립성 보장외부 조건, 순서, 공유 상태 등에 영향을 받지 않고 항상 동일한 조건에서 수행되어야 한다.Test Fixturegiven절에 테스트의 픽스처를 구성한다. (가독성과 유지보수 측면에서 유리) 별도의 data.sql 데이터를 추출하지 않는다.단위테스트 내에서 모두 표현한다. data.sql과 같은 공통 로직으로 테스트 픽스처 구성하지 않기픽스처 생성 로직은 같은 테스트 클래스에서 관리하기테스트코드 안에 메서드를 추출할 때, 정말 필요한 값만 파라미터로 받기 deleteAll(), deleteAllInBatch()엔티티 하나씩 remove() VS 한 번에 삭제 (bulk delete)deleteAll()은 다수의 쿼리 발생으로 테스트 비용이 증가할 수 있다. deleteAll()은 연관된 엔티티가 삭제되고, deleteAllInBatch()는 삭제가 안된다.용도에 맞게 각 메서드를 적절히 사용해야 한다.  @ParameterizedTest, @DynamicTest@ParameterizedTest동일한 테스트를 다른 입력값으로 테스트 할 때 사용@ValueSource, @CsvSource, @MethodSource 등 다양한 소스로부터 테스트가 가능하다.@DynamicTest테스트를 실행할 때 동적으로 생성하는 방식@TestFactory 를 사용한다.섹션 9. Appendix지만 중요한 것들학습 테스트잘 모르는 기능, 라이브러리, 프레임워크를 학습하기 위한 테스트 코드여러 테스트 케이스를 스스로 정의하고 검증하는 과정을 통해 구체적인 동작과 기능을 학습Spring Rest Docs테스트 코드를 통한 API 문서 자동화 도구API 명세를 문서로 만들고 제공함으로써 협업을 원활하게 한다.✅강의 회고 Layer 별로 어느 도구들을 사용하며 테스트를 해야 하는지 알 수 있었다. 특히 Presentation layer에서 Mock 사용법이 가장 기억에 남을 것 같다. 아무래도 테스트 코드를 작성 안하고 수동으로 테스트를 해왔을 때, Presentation layer의 결과를 직접 보면서 했기 때문에 이 부분을 어떻게 자동화를 할 수 있을지에 대한 궁금증이 해결되는 순간이였다. Mock 애너테이션별 사용법을 배운 것도 앞으로 테스트 코드를 작성하는데 큰 도움이 될 것 같다고 느꼈고, 강의 내내 TDD로 추가 요구사항을 해결해 나가는 점이 TDD 방식을 자연스럽게 익히게 된 것 같아 학습에 뭔가 여운이 남는다.그 외에도 BaseEntity 사용과 같은 클린 코드를 위한 설계와 리팩터링도 강의 내내 이루어져 복습까지 함께 하는 느낌이였다.아마 강의를 다시 들어야 온전히 습득할 수 있겠지만, 이번 첫번째에도 많은걸 얻을 수 있었던 것 같다.✅ 미션 과정 & 회고강의내용을 떠올리면서 계층별로 어느 부분에 중심적으로 테스트 해야하는지를 떠올리며 BDD 패턴을 적용해 해결 했다.예제게 우리가 친숙한 댓글 로직 이여서 효율적인 테스트에만 집중해서 해결 할 수 있었던 것 같다.초기화 부분에 대한 부분도 고려하면서 미션을 했다.

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - 3주 차 발자국

👣3주 차 발자국 Practical Testing: 실용적인 테스트 가이드 ✅ 강의 요약섹션 6. Spring & JPA 기반 테스트 Layered Architecture단점 : 기술에 대한 강결합 통합테스트단위테스트 만으로는 기능 전체의 신뢰성을 보장할 수 없다. (여러 모듈 및 여러 객체가 협력하기에) 여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트Spring / JPA 훑어보기 & 기본 엔티티 설계 IoC (Inversion of Control)객체의 생성과 소멸, 생명주기 관리를 제 3자가 하는 원리DI (Dependency Injection)필요한 객체를 직접 생성하는 것이 아닌 외부에서 생성해 주입하는 것IoC이 DI로 실현된다.AOP (Aspect Oriented Programming)비즈니스 흐름과 관계없는 공통 기능 (트랜잭션, 로깅 등) 을 분리해 모듈화 시키는 것을 의미스프링에서는 Proxy형태로 제공ORM (Object-Relational Mapping)객체 지향 패러다임과 관계형 DB 패러다임의 불일치를 해결 JPA (Java persistence API)Java 진영의 ORM 기술 표준JPA - 인터페이스, 구현체 - Hibernate (대표적으로 사용됨)Spring에서는 JPA를 한번 더 추상화한 Spring Data JPA 형태로 제공주로 QueryDSL과 같이 사용됨  Persistence Layer 테스트@DataJpaTestJPA 관련 컴포넌트만 테스트할 때 사용하는 전용 어노테이션@SpringBootTest보다 가볍다.@Transactional이 있어 롤백이 된다.그치만 선호하지 않는다.Persistence LayerData access의 역할만 해야한다.Data의 CRUD에만 집중한 레이어.Business Layer 테스트비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용을 통해 개선 (통합적 테스트)트랜잭션을 보장해야 한다.@WebMvcTestPresentation Layer에 대한 단독 테스트시 사용하는 어노테이션@Transactional(readOnly = true)읽기 전용JPA에서는 스냅샷 저장, 변경감지가 일어나지 않아 성능 향상 read only값을 보고 DB의 endpoint를 분리할 수 있다. (true일 경우, read 전용 DB로 보낸다.)구분을 잘 하는 것이 중요하다.readOnly = true 구분이 누락될 수 있기에 아래 방법을 사용.service class 상단에 readOnly = true를 걸고, CUD 작업이 있다면 메서드 단위에 @Transactional을 건다.CQRS읽기(조회)와 쓰기(명령)의 책임을 분리하는 소프트웨어 아키텍처 패턴Command(CUD) 와 Query(Read) 의 책임을 분리하자. (read가 훨씬 많이 일어난다.) 성능 최적화, 비지니스 로직의 분리, 장애 격리를 할 수 있다. ✅ 강의 회고WebMvcTest의 사용과 Transactional(readOnly = true)의 중요성을 알게 되었다.그리고 @SpringBootTest는 데이터 클랜징이 필요 하기 때문에 클랜징 때문에 일어나는 문제를 유의해서 봐야하는걸 배웠다. 데이터 세팅에 손이 많이 가기 때문에 테스트가 귀찮다고 느껴지는 것이 아닌가 싶었다. ✅ 미션 과정 & 회고미션 PR - link우선 스터디 카페 이용권 선택 시스템을 기반으로 미션을 진행했다.강의 내용처럼 @DisplayName을 최대한 자세히 작성하려고 했고, 미션 조건처럼 3개 이상의 서로 다른 클래스를 만족하기 위해 기능이 다른 클래스를 선택해 작성했다.그리고, BDD (given/when/then) 스타일을 따라 작성했다.강의 내용처럼 테스트가 '성공한다' , '실패한다' 의 의미를 포함하지 않게(테스트의 현상을 포함하지 않게) DispayName을 작성하려 했고, 최대한 도메인에 관련된 이름을 넣어서 표현했다. TDD 방식으로 작성한 테스트가 아니기 때문에 해피케이스만 작성된 것 같다. 아무래도 이에대한 보완은 중간점검때 확인해 봐야 할 것 같다. 강의 - Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - 2주 차 발자국

👣2주 차 발자국 ✅ 강의 요약섹션 6. 코드 다듬기 좋은 주석 - 주석의 양면성주석이 많으면 ?추상화가 덜 되고 가독성이 좋지 않은 코드 일 가능성이 높다.그렇다면, 주석을 써야하는 경우는?히스토리를 알 수 없을 경우, 주석으로 상세히 설명 변수와 메서드 나열 순서변수사용하는 순서대로 나열객체의 public/private 메서드public 은 상단, private 은 하단 순으로 나열. public은 중요도에 따라 다시 나열.public - 상태 변경 > 판별 > 조회private - 출현한 순서대로 패키지 나누기적당한 수준으로 잘 나누어야 한다. (모호한 표현이긴 한데, 추상화 하는 개념 단위로 나누어야 되는 느낌)협업의 경우, 대규모 패키지 변경은 협의 후 진행패키지 이동으로 충돌 가능성이 존재 기능 유지보수하기정렬 단축키, linting, style - sonarlint, editorconfig 섹션 7. 리팩토링 연습 메서드 추출로 추상화 레벨 맞추기 OptionalNPE 방지메서드 파라미터에 Optional 사용은 안티패턴orElse()orElseGet()orElseThrow() 각각의 차이 객체에 메시지 보내기객체의 값을 꺼내서 사용하는 것이 아니라 존중하고 메시지를 보내야 한다. 객체의 책임과 응집도구현에 초점을 맞춘 추상화 VS 도메인 개념에 초점을 맞춘 추상화 구현에만 초점을 맞춘 경우, 해당 구현체가 거대해 질 수 있다.( ex. file에서 읽는 것에 초점을 맞추는 것이 아니라, 어떤 데이터를 읽는 것인가 에 초점을 맞춰야 한다. )헥사고날 아키텍처 (포트, 어댑터) 의 기본 개념 섹션 8. 기억하면 좋은 조언들 능동적 읽기리팩토링하면서 읽기 도메인 지식을 늘려야 함 오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링구현체가 하나인 인터페이스너무 이른 추상화 은탄환은 없다만능인 것은 없다. 소프트웨어 품질 VS 빠른 결과물추후의 리팩터링을 고려한 코드 작성 능력이 필요적정 기술의 범위 내에서 사용되어야 한다. 결국 연습을 통한 적정 수준, 적정 시점을 깨달아야 함. 강의 - Readable Code: 읽기 좋은 코드를 작성하는 사고법  섹션 3. 단위 테스트 단위 테스트작은 코드(클래스 또는 메서드) 단위를 독립적으로 검증하는 테스트검증 속도가 빠르고, 안정적 수동 테스트, 자동화 테스트사람이 검증하는 수동 테스트System.out.println 으로 직접 확인하는 방법기계가 검증하는 자동화 테스트 Junit5, AssertJJunit5 : 단위 테스트를 위한 테스트 프레임워크AssertJ : 테스트 코드 작성을 원할하게 돕는 테스트 라이브러리 - 풍부한 API 메서드 체이닝 지원 해피 케이스, 예외 케이스예외 케이스 : 암묵적 혹은 드러나지 않은 요구사항에서 발견 테스트하기 어려운 영역 분리테스트하기 어려운 영역관측할 때마다 다른 값에 의존 - ex)현재 날짜/시간, random, 전역 변수/함수, 사용자 입력외부 세계에 영향을 주는 경우 - ex) 표준 출력, 메시지 발송, DB 테스트하기 쉬운 영역 (순수 함수)같은 입력에는 항상 같은 결과외부 세상과 단절된 형태 lombok 사용 가이드@Data, @Setter, @AllArgsConstructor 지양양방향 연관 관계의 @ToString 순환 참조 문제 섹션 4. TDD: Test Driven Development TDD ?프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론 일반적인 개발 방법의 경우 (기능 -> 테스트 순서의 경우)테스트 누락 가능성해피 케이스만 검증할 가능성잘못된 구현을 늦게 발견할 가능성  TDD의 경우 (테스트 -> 기능 순서의 경우)테스트 가능한 코드로 구현할 수 있음엣지 케이스구현에 대한 빠른 피드백 테스트 코드 보장 -> 과감한 리팩터링이 가능 레드 - 그린 - 리팩토링Red : 실패하는 테스트 작성Green : 테스트 통과 하는 최소한의 코딩Refactor : 구현 코드 개선 테스트 통과 유지 애자일 방법론반복적 개발(Iterative Development): 짧은 개발 주기(스프린트)를 반복하며 지속적으로 개선.유연성: 요구사항 변경을 수용할 수 있도록 유동적으로 진행.고객 참여: 개발 과정에서 지속적인 피드백을 반영하여 사용자 중심 개발 가능.자율적인 팀 구성: 개발팀이 자체적으로 의사 결정을 하며, 빠르게 문제를 해결함. 익스트림 프로그래밍XP(Extreme Programming, 익스트림 프로그래밍)애자일 방법론 중 하나고객의 요구사항 변화에 빠르게 대응할 수 있도록 짧은 개발 반복 주기(Iteration)와 강한 협업실천 방법론으로 TDD를 제시 (켄트백)스크럼, 칸반스크럼애자일 프레임워크로, 일정한 스프린트 동안 작업을 계획하고 진행하는 반복적이고 점진적인 개발 방식칸반Workflow와 가시성을 중심으로 한 애자일 프레임워크 섹션 5. 테스트는 []다. 테스트 코드는 문서다.프로덕션 기능을 설명해주는 것이 테스트 코드 문서다.다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완할 수 있다.고민했던 내용(테스트 코드)을 팀 자산(소스 코드)으로 공유할 수 있다. @DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명 만으로는 테스트 의도 파악이 어렵기 때문에 나옴 (Junit5) 섬세한 DisplayName 예시특정 시간 이전에 주문을 생성하면 실패한다. ( X )영업 시작 시간 이전에는 주문을 생성할 수 없다. ( O ) 도메인 용어를 사용하여 추상화된 내용을 담기ex. 영업 시작 시간테스트의 현상을 중점으로 기술하지 말 것'실패한다' 와 같은 것은 검증과 무관 Given / When / Then - 주어진 환경, 행동, 상태 변화Given : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증 TDD vs BDDBDDTDD에서 파생된 개발 방법 (TDD와 비교해보자)시나리오 기반한 테스트 케이스 자체에 집중하여 테스트한다. Junit vs SpockSpock은 Groovy언어로 BDD 패턴을 적용함 언어가 사고를 제한한다.명확하지 못한 테스트 코드는 사고를 제한할 수 있다.  강의 - Practical Testing: 실용적인 테스트 가이드 ✅ 강의 회고완강 후에 느낀 것은 '결국엔 많이 해봐야 익숙해지겠다.' 였다. 텍스트로 정리한 내용들을 기억 하고 있지만 정작 미션을 했을 때를 돌이켜 보면 손이 움직이지 않는다. 영어를 배웠지만 원어민 앞에서 얼어붙는 느낌이랄까?그래도 강의를 통해 추상화 하는 과정을 따라해보고 지향해하 하는 점과 지양해야 하는 점의 방향성을 알게 되었기 때문에 좋았다. 강의의 실습내용을 반복하다보면, 언젠간 나도 내 코드에 만족할 수 있지 않을까? TDD는 이전에 다른 강의를 통해 접한 내용을 복습하는 과정과 같아서 좋았다. TDD를 접했지만 아직까지도 TDD 방법론으로 프로젝트를 시작해 본 적이 없고, 테스트 라이브러리의 메서드도 쓰는 것만 쓰고... 반성을 한 시간이였다.'왜? 배워야 하는가' 를 집중해서 배운 것 같은데 , 잘 알고 써야 의미가 있다는 느낌을 받았다. 알고 써야 의미있는 검증이 되는 것이지, 적당히 알고 쓰면 해피케이스만 작성하게 되고 의미없는 행위로 끝날 가능성이 높을 것이다. ✅ 미션 과정강의를 통해 배운 내용들을 최대한 적용해 보려 했었다.model패키지를 보면서 이전 강의의 지뢰찾기 리팩터링 보다는 좀더 추상화 되어 있는 상태라고 느꼈다.컬렉션 추상화를 위해 일급컬렉션을 적용했고, StudyCafePassMachine에서 반복적으로 나오는 로직을 추상화 시켜 내용을 진행했다. 그리고 model 내부에 display 하는 내용들이 있어, io 패키지의 구현체들로 로직을 옮겼다. ✅ 미션 회고접근 자체는 좋았다고 생각했는데, 구현이 막히고 시간에 쫓겨 강의를 보고 다시 리팩터링 했다. 어떻게 보면 답지를 배낀것이긴 하다. 기존의 model패키지에 있는 displa부분이 가장 고민이였는데, 출력과 금액 계산을 어떻게 나누는 가를 order라는 추상화를 통해 풀어가는 것을 보고 새로운 추상화를 만들어 가는 것을 과감히 시도해 봐야 겠다라고 느꼈다.그리고 마지막에 file을 읽는 행위의 구현에 집중하는 것이 아니라, 어떤 데이터를 읽는 것인가에 집중해서 리팩터링을 하는 것이 기억에 남는다. 헥사고날 아키텍처의 기본 개념인데, 도서를 통해 이름을 본 적이 있는데 기억해 두었다가 필요할 때 학습을 통해 적용해 보면 좋을 것 같다. 

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - [Day 18 미션]

미션 내용1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.아래 3개의 테스트가 있습니다. ✔ 게시판 게시물에 달리는 댓글을 담당하는 Service Test ✔ 댓글을 달기 위해서는 게시물과 사용자가 필요하다. ✔ 게시물을 올리기 위해서는 사용자가 필요하다.내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@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 검증 } 미션 수행1)@Mock순수한 Mockito 객체에 사용한다. (순수 자바 환경의 단위테스트에서 사용 가능) @InjectMocks나 수동으로 주입 받는다.Spring Context와 무관하다.@MockBean스프링 빈을 Mocking할 때 사용 (스프링 부트를 띄우는 통합테스트에서 사용 가능)@Autowired를 통해 주입 받는다.Spring Context에서 관리한다.@Spy@Spy는 객체의 일부 동작만 Mocking 한다.(실제 객체를 감싸서 일부는 실제 객체처럼 동작시키고, 일부만 Stub 가능)@Mock과 마찬가지로 순수 Mockito 객체를 사용하며, Spring Context와 무관하다.@SpyBean@Spy를 Spring Context에서 관리하는 어노테이션.@InjectMocks순수한 Mockito 객체(@Mock, @Spy)를 자동으로 주입할 때 사용** Spring Context에서는 @Autowired를 통해 @MockBean, @SpyBean 객체 주입, 순수한 Mock을 사용할 때는 @InjectMocks으로 주입2)@BeforeEach void setUp() { 1-1, 2-1, 3-1 사용자1 생성에 필요한 내용 준비 1-2, 2-2, 3-2 사용자1 생성 1-3, 2-3, 3-5 사용자1의 게시물 생성에 필요한 내용 준비 1-4, 2-4, 3-6 사용자1의 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-5. 댓글 생성에 필요한 내용 준비 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 //then 검증 } 강의 - Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - [Day 16 미션]

워밍업 클럽 4기 백엔드 - [Day 16 미션] 미션 내용Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다. 레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요?Layered Architecture관심사의 분리로 각 레이어를 독립적으로 테스트할 수 있다. Persistence Layer데이터베이스 및 외부 저장소와의 상호작용하는 레이어1) 특징Data access의 역할만 해야한다. (데이터 가공 로직 X)Data의 CRUD에만 집중한 레이어.2) 테스트 방법데이터 Access에 대한 테스트를 진행한다. (데이터 저장 및 조회 기능, 데이터 무결성) layer를 떼어서 테스트 하기 때문에 단위테스트 느낌이기 때문에 @DataJpaTest(@Transactional 이미 포함)를 사용해 좀더 쉽게 수행할 수 있지만, Spring을 통해 서버를 띄워서 하는 테스트 이기 때문에 통합 테스트 성격의 @SpringBootTest와 @Transactional를 조합해서 작성한다. Business Layer비지니스 로직 및 도메인 로직에 대한 책임이 있는 레이어1) 특징도메인 모델을 조작하고 검증하는 역할.Persistence Layer와의 상호작용을 통해 전개 (통합적 테스트)트랜잭션을 보장해야 한다. 2) 테스트 방법비지니스 로직 검증을 위해, 보이지 않는 예외 케이스를 고려한 테스트를 해야함.Persistence Layer 를 포함해 통합 테스트 한다. 데이터 클렌징 시, 객체간의 협력이 많은 Business Layer에서는 @AfterEach에서 deleteAllInBatch() 사용.deleteAll은 조회 후 하나씩 삭제하므로, 쿼리가 다수 발생함. Presentation LayerUI 및 컨트롤러의 책임1) 특징외부 세계의 요청을 가장 먼저 받는 계층이면서 응답을 반환하는 곳.비지니스 로직을 포함하지 않고, Business Layer에 위임함.요청 데이터에 대한 검증을 수행하는 곳으로 Controller와 DTO를 포함.2) 테스트 방법요청 파라미터에 대한 최소한의 검증을 수행한다. (ex 파라미터의 필수값 여부) Controller는 하위레이어 (Business Layer, Persistence Layer)를 Mocking해 독립적으로 테스트 한다.@WebMvcTest를 이용해 테스트 하고자 하는 Controller를 등록한다. Business Layer의 Service 클래스를 @MockBean을 통해 Mocking한다.*Mocking은 스프링에서 제공하는 아래 프레임워크를 사용한다.MockMvcMock(가짜) 객체를 사용해 스프링 MVC 동작을 재현할 수 있는 테스트 프레임워크강의 - Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽4기워밍업클럽백엔드

이강호

워밍업 클럽 4기 백엔드 - [Day 4 미션]

  미션 내용1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.리팩토링 전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 boolean validateOrder(Order order) { if (order.isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.isTotalPriceInvalid()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.isCustomerInfoMissing()) { log.info("사용자 정보가 없습니다."); return false; } return true; }  2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.스마트폰에 비유해서 설명하겠습니다.S – 단일 책임 원칙 (Single Responsibility Principle)"카메라는 사진만 찍자. 스피커와 마이크기능까지 있으면 그만큼 고장날 위험이 많아지고 고치기 힘들다."하나의 부품에 여러 기능을 가지면 하나의 기능이 고장날 때, 모든 기능을 사용 못하고 유지보수가 어려워진다. O – 개방-폐쇄 원칙 (Open/Closed Principle)"앱을 추가해도 운영체제를 고치지 않아도 돼야 한다."새로운 기능(앱, 확장 기능)을 추가하더라도 기존 OS나 시스템 코드는 건드리지 않아야 한다.기존 구조는 닫혀 있고, 확장은 열려 있어야 함.L – 리스코프 치환 원칙 (Liskov Substitution Principle)"새 카메라 모듈을 껴도, '사진 찍기 버튼'은 그대로 작동해야 한다."카메라 모듈을 새 버전으로 바꾸더라도 기존 방식대로 사진이 찍히고 동작이 유지돼야 한다.대체 부품(서브클래스)은 원래 부품(슈퍼클래스)을 완벽히 대체할 수 있어야 함. I – 인터페이스 분리 원칙 (Interface Segregation Principle)"문자만 보내는 앱에 카메라, GPS, 블루투스를 요구하지 말자."앱이 자기에게 필요 없는 기능(인터페이스)까지 강제로 구현하게 하면 불필요한 권한 요청과 복잡도가 생김.기능을 작게 나눠서 필요한 기능만 연결할 수 있어야 한다. D – 의존 역전 원칙 (Dependency Inversion Principle)"핸드폰 운영체제는 '충전기'에 의존해야지, 삼성 65W 모델 같은 구체적인 충전기에 의존하면 안 된다."OS나 하드웨어는 '충전 어댑터' 라는 기능(인터페이스)만 알면 된다.충전기 종류가 바뀌더라도 시스템이 동작되어야 확장성이 생긴다.강의 - Readable Code: 읽기 좋은 코드를 작성하는 사고법 미션 내용 - Readable Code: 읽기 좋은 코드를 작성하는 사고법 中 4. 객체 지향 패러다임

백엔드워밍업클럽4기워밍업클럽백엔드

채널톡 아이콘