블로그
전체 42025. 03. 28.
0
워밍업 클럽 3기 BE 클린코드&테스트 - 4주차 발자국
Day 16섹션6. Spring & JPA 기반 테스트Presentation Layer 테스트 (2)모킹: 가짜 객체로 대신하여 정상 동작할거야 라는 것을 가정하고프레젠테이션 레이어즉, 테스트하고자 하는 레이어에만 집중해서 테스트하겠다모킹이 들어가는순간 단위테스트인것인가?Day 17섹션7. Mock을 마주하는 자세Mock 객체는 Mock의 역할, Stub의 역할을 동시에 할 수 있다고 한다.나도 Classicist 쪽에 가까운 것 같다.모킹이 100% 재현할 것이라는 것에 항상 의심을 하기 때문이다.Day 18섹션8. 더 나은 테스트를 작성하기 위한 구체적 조언한 문단에 한 주제다, 하나의 테스트는 하나의 주제만을 가져야 된다테스트 환경의 독립성을 보장하고, 테스트 간 독립성을 보장하자테스트 수행도 비용이다. 테스트 마다 Spring Boot가 뜨는것을 줄이기 위해 환경을 통합하자.Day 19드디어 강의를 다 들었다.Spring REST Docs, Swagger 둘 다 일장일단이 있는 것 같아서 팀의 내부사정에 맞게 사용하면 될 듯 싶다.정말 의미있는 시간이었다.Day 20중간 점검미션Day 16Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다. 레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요?Persistence Layer특징: 데이터를 직접 접근하는 계층어떻게 테스트를 하면 좋을지: CRUD에만 집중하여 테스트하자. 비즈니스 로직이 들어가면 안된다.단위테스트같은 통합테스트로 진행하자. 수행 후 데이터를 잘 지워주자.Business Layer특징: 비즈니스 로직이 전개되는 계층어떻게 테스트를 하면 좋을지: 비즈니스 로직이 정상적으로 수행되는지 통합테스트로 진행하자. 수행 후 데이터를 잘 지워주자. 예외 케이스에 더 집중해야한다. 그것이 개발자의 역량이다.Presentation Layer특징: 외부에서 요청이 들어오는 계층어떻게 테스트를 하면 좋을지: 외부 파라미터에 대한 최소한의 유효성 검증을 진행하자. 하단 레이어는 모킹 처리하자. 진행하려는 유효성 검증이 어느 계층에서 검증해야 하는지 잘 생각해보자.Day 18@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.Mock: Mockito 단위 테스트에서 사용, 아무것도 지정하지 않으면 아무일도 일어나지않음. 행위를 검증MockBean: Spring Boot 테스트에서 사용, 스프링 컨테이너의 Bean을 목 객체로 교체Spy: 실제 객체를 기본으로 사용하고, 일부를 stubbing 가능SpyBean: Spring Boot 테스트에서 사용, 스프링 컨테이너의 Bean을 스파이 객체로 교체InjectMocks: 테스트 대상 객체에 @Mock, @Spy 객체들을 자동으로 주입아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요? (@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)개인적으로 테스트 환경의 독립성, 테스트 간 독립성을 보장하기 위해서 합치고 싶지 않습니다.모든 테스트에서 setUp 데이터를 사용할지라도, 나중에 추가될 테스트에서 해당 데이터를 사용할지 확신할 수 없고,또한 setUp에서 데이터를 만드는 것은 이 데이터가 어떤 테스트에서 필요한 것인지 명확하게 표현하지 못하는 것 같기 때문입니다.AfterEach로 데이터를 비워준다면 고려해볼 수는 있겠지만, 현재로서는 setUp에 합치는 방식이 적절하지 않다고 생각합니다. 끝났다.테스트 코드에 지레 겁을 먹지않게 해준 시간이었다.우리 모두 파이팅
백엔드
・
백엔드
・
워밍업클럽
・
테스트
・
박우빈
・
발자국
2025. 03. 21.
0
워밍업 클럽 3기 BE 클린코드&테스트 - 3주차 발자국
Day 12섹션6. Spring & JPA 기반 테스트Persistence Layer 테스트 (1)Persistence Layer 테스트 (2)DataJpaTest는 JPA 관련 빈들만 등록해주기 때문에 SpringBootTest보다 빠르다@ActiveProfiles 어노테이션을 사용해 활성화 할 프로파일을 설정할 수 있다.리스트를 검증할 때는 size를 먼저 검증하고extracting + contains 조합을 많이 사용한다extracting 내부에는 검증할 필드들을 넣어주면되고contains는 다양한 api들이 있다.여기서 사용한 containsExactlyInAnyOrder()는 순서 상관없이 인자로 들어온 튜플들이 정확히 존재하는지extracting에 적은 필드의 순서대로 적어주면 된다.Day 13Business Layer 테스트 (1)Persistence Layer는 비즈니스 가공 로직이 포함되어서는 안 된다.Business Layer는 Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개Business Layer 테스트 (2)SpringBootTest는 자동롤백이 안달려있고DataJpaTest는 자동롤백이 달려있음Day 14Business Layer 테스트 (3)Day 15Presentation Layer 테스트 (1)모킹: 가짜 객체로 대신하여 정상 동작할거야 라는 것을 가정하고프레젠테이션 레이어즉, 테스트하고자 하는 레이어에만 집중해서 테스트하겠다readOnly = true : 읽기전용 트랜잭션CRD 작업이 동작 X / only ReadJPA : CUD 스냅샷 저장, 변경감지 등을 안해도 되기 때문에 성능 향상CQRS - Command(CUD) / Query(R)미션Day 11사용자의 입력값은 무조건 불신을 깔고 들어가야한다는 강사님의 말씀이 떠올랐다.그래서 InputHandler를 테스트하고자 했다.StudyCafeIOHandler를 테스트해야하나 고민했지만, 통합해주는 역할일뿐입력에 대한 최종 책임은 InputHandler에 있다고 판단했다.기존 InputHandler에는private static final Scanner SCANNER = new Scanner(System.in);로 스캐너가 생성되어있어서 테스트할 때 nextLine() 예외가 발생했다.private final Scanner scanner; public InputHandler(Scanner scanner) { this.scanner = scanner; }사용자 입력같은 테스트하기 어려운 영역을 분리하자는 강사님의 말씀이 떠올라 외부에서 주입받도록InputHandler를 변경해주었다.또 해피 케이스말고 예외 케이스를 생각해 1~3 이외에 다른 입력시 들어왔을 때 예외가 잘 발생하는지 확인했다.또 사물함을 사용할 수 있는 패스권인지 확인하는 메서드를 검증하기 위해StudyCafePassType에 대한 테스트를 진행했다.확실히 enum 타입으로 객체로 만드니 관련 로직을 위한 공간이 생겨 테스트가 용이하구나를 느꼈다.그리고 가장 중요한 금액관련 테스트를 진행하고자했다.금액 관련 테스트를 StudyCafePassOrder에서 전부 다 진행할까 했지만,하나의 테스트는 하나의 책임만 가져야한다고 생각해서,StudyCafeSeatPass, StudyCafeLockerPass에서 각각 진행했다.테스트를 진행하면서 궁금한 점은StudyCafeLockerPass lockerPass = StudyCafeLockerPass.of(StudyCafePassType.FIXED, 4, 10000);처럼 of 메서드에서 csv 파일에 맞게 직접 입력해주는게 맞을지아니면 LockerPassFileReader에서 객체를 찾아내서 테스트 하는게 맞을지 궁금하다.그리고 DisplayName 짓는 것이 생각보다 되게 까다로웠다.테스트는 쉽지않다. 하지만 올바르지 못한 테스트는 오히려 혼란을 일으킬뿐이라는 것은 알게되었다.제대로 된 테스트를 작성하도록 노력하자.모킹 테스트를 왜 해야는지는 아직 명확하게 와닿진 않는다.알 때 까지 복습하자.
백엔드
・
워밍업클럽
・
테스트
・
발자국
・
박우빈
2025. 03. 15.
0
워밍업 클럽 3기 BE 클린코드&테스트 - 2주차 발자국
Day 6섹션6. 코드 다듬기주석의 양면성주석이 많다는 것은 우리의 비즈니스 요구사항을 잘 못 녹였다는 것후대에 전해야 할 “의사결정의 하스토리” 같은 도저히 코드로 표현할 수 없을 때 주석을 달자주석도 작성하는 순간 관리주체가 되고 버전이 생긴다.꼭 잊지말고 업데이트하자.변수와 메서드의 나열 순서나열 순서로도 의도와 정보를 전달할 수 있다는 것변수는 사용하는 순서대로메서드는 공개 메서드를 상단 비공개는 하단메서드도 기준을 가지고 배치하자.상태 변경 >> 판별 ≥ 조회리팩토링을 하면 두가지를 신경쓰자이 메서드 어디둘까?얘 공개인가, 비공개인가?패키지 나누기패키지는 문맥으로써의 정보를 제공할 수 있다.처음 만들 때부터 잘 고민해서 패키지를 나눠놓는 것이 제일 좋다.기능 유지보수하기 (1) - 버그 잡기추상화를 통해 책임을 단위로 잘 쪼개면변경해야하는 부분이 최소화된다.기능 유지보수하기 (2) - 알고리즘 교체하기dfs - 재귀, 스택재귀를 많이 호출하면 스택오버플로우가 발생한다.처음에는 row, col을 따로 관리했기때문에 재귀를 호출해야했지만CellPosition이라는 개념을 도입하면서 하나로 관리할 수 있게 되면서stack을 사용할 수도 있게 되었다.이렇게 클린코드 리팩토링을 통해서 생각의 전환도 해볼 수 있다IDE의 도움 받기결국 모든건 다 가독성을 위한 것코드 포맷 정렬 Option + Cmd + L포맷 규칙: .editorconfighttps://EditorConfig.orgDay 7미션진행Day 8섹션7. 리팩토링 연습연습 프로젝트 소개리팩토링 포인트추상화 레벨객체로 묶어볼만한 것은 없는지객체지향 패러다임에 맞게 객체들이 상호 협력하고 있는지SRPDIP일급 컬렉션리팩토링 한 단계마다, 그 이유를 설명할 수 있어야 한다.리팩토링 (1) - 추상화 레벨리팩토링 (2) - 객체의 책임과 응집도리팩토링(1)에서는 메서드 레벨에서의 추상화였다면리팩토링(2)에서는 객체 레벨에서의 추상화리팩토링 (3) - 관점의 차이로 달라지는 추상화외부에 있는 어떤 데이터를 필요로 해서 가져온다고 했을 때두 가지 관점으로 생각해볼 수 있다.어떤 데이터를 필요로 하는가?데이터를 어디로부터 어떻게 가져올 것인가?(방법)섹션8. 기억하면 좋은 조언들능동적 읽기내가 이해할 수 있도록 수단과 방법을 가리지 말자핵심목표는 우리의 도메인 지식을 늘리고 이전 작성자의 의도를 파악하는 것이다오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링은 좋지않다.은탄환은 없다지속 가능한 소프트웨어의 품질 vs 기술 부채를 안고 가는 빠른 결과물대부분의 회사는 돈을 벌고 성장해야 하고, 시장에서 빠르게 살아남는 것이 목표다.이런 경우에도, 클린 코드를 추구하지 말라는 것이 아니라,미래 시점에 잘 고치도록 할 수 있는 코드 센스가 필요하다.결국은, 클린 코드의 사고법을 기반으로 결정하는 것도구라는 것은, 일단 그것을 한계까지 사용할 줄 아는 사람이 그것을 사용하지 말아야 할 때도 아는 법이다.적정 수준을 알기 위해, 때로는 극단적으로 시도해보자.섹션9. Outro추상과 구체를 인식뿐 아니라 넘나들 수 있어야 한다Day 9Practical Testing: 실용적인 테스트 가이드섹션1. Intro강의소개메타인지가 중요하다.채용 시 구현 과제 등에서 테스트 작성여부, 테스트 코드 구현방식을 확인하는 만큼주니어 개발자에게 가장 기대하는 요소 중 하나이다.섹션2. 테스트는 왜 필요할까?테스트는 왜 필요할까?빠른 피드백, 자동화, 안정감올바른 테스트 코드로 이익을 만들어내자섹션3. 단위 테스트테스트 케이스 세분화하기이 요구사항이 과연 실제 내가 구현할 때 그 요구사항과 정확히 맞아떨어지는가암묵적이어서 얘기를 안한 것이 있거나,도출이 안된,드러나지 않은 요구사항이 있는지 항상 염두에 두고 고민을 해야한다.예를 들어, 한 종류의 음료 여러 잔을 한 번에 담는 기능이라고 했을 때0이나 1이 들어왔을 때 어떻게 할것인지테스트하기 어려운 영역을 분리하기현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등표준 출력, 메시지 발송, 데이터베이스에 기록하기 등 을 최대한 외부로 밀어내고 주입받자.섹션4. TDD: Test Driven DevelopmentTest Driven Development선 테스트 작성 후 기능 구현을 한다면,복잡도가 낮은(유연하며 유지보수가 쉬운), 테스트 가능한 코드로 구현할 수 있게 한다.쉽게 발견하기 어려운 엣지(Edge) 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.섹션5. 테스트는 [ ]다테스트 코드는 문서다프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다.DisplayName을 섬세하게~테스트 대신 ~ 할 수 있다명사의 나열보다 문장으로테스트 결과까지 기술하면 더 좋다BDD 스타일로 작성하기given, when, then에 맞추어 DisplayName에 명시하자미션Day 7[섹션 7. 리팩토링 연습]의 "연습 프로젝트 소개" 강의를 보고, '스터디 카페 이용권 선택 시스템' 프로젝트에서 지금까지 배운 내용을 기반으로 리팩토링을 진행해 봅시다.오늘 1차 리팩토링을 마치고, 다음날 자고 일어나서 다시 한번 내가 리팩토링한 코드를 살펴봅니다. 자고 일어나서 뇌가 맑아지면 새로운 시야가 열릴 때가 많거든요. 만약 추가로 수정하고 싶은 부분이 보인다면, 2차 리팩토링을 진행합니다.Day7 미션 - 1차 리팩토링일급 컬렉션을 사용해서 Passes, LockerPasses를 만들었다.필터 로직을 일급 컬렉션 내부에 등록했다.DIP를 적용해 StudyCafePassMachine이 인터페이스를 의존하게 만들었다.확장성을 위해 InputHandler, OutputHandler, ~ListHandler를 만들었다.StudyCafeFileHandler의 매직 스트링을 추출하였다.OutputHandler에 showExceptionMessage를 만들어 showSimpleMessage와 차별점을 두었다.ConsoleInputHandler의 사용자 입력에 대한 예외처리를 했다.StudyCafePass, StudyCafeLockerPass를 묶어서Order로 만들 수 있을 것 같아서 일단 만들어두었다.두 객체의 중복으로 있는 passType, duration, price, display()가 너무 거슬린다.사물함의 사용여부에 따라 분기가 되는데 StudyCafeLockerPassType을 만들어서 한번에 처리해보고 싶은데 방법이 떠오르지 않는다.머리를 비운 후 2차 리팩토링을 진행해봐야겠다.중간점검역시 스터디를 하길 잘했다는 생각이 든다.다른 사람들이 리팩토링 한 코드를 보면서 배울점이 많았고,나는 우물안 개구리임을 깨달았다.더 열심히 해야겠다 파이팅
백엔드
・
워밍업클럽
・
클린코드
・
테스트코드
・
박우빈
・
발자국
2025. 03. 07.
0
워밍업 클럽 3기 BE 클린코드&테스트 - 1주차 발자국
Day 2섹션1. Intro구름톤 딥다이브 스터디에서 진행하는 온라인 서점 프로젝트를 리팩토링 해보려고 계획중이다.클린 코드, 리팩토링 과정의 베스트 프랙티스는 위와 같으니 더할 나위 없이 좋은 타이밍같다.이전부터 도메인이라는 단어는 많이 들었지만, 도메인이 도대체 뭔지는 명확하게 알지 못했다.하지만 해결하고자 하는 문제 영역이 도메인이다 라는 것을 알게되었다.섹션2. 추상우리가 클린 코드를 추구하는 이유나도 가독성을 위해서 개행문자를 사용했다.하지만 단순히 이런 것을 얘기하기는게 아니었고,수많은 클린코드 원칙이 있지만 사실 가독성을 위함이었고이것을 왜 지켜야 하는가를 알아야 한다.수많은 조언들을 관통하는 아주 중요한 주제추상에 대해 알아보자. 프로그램의 정의대학 때 프로그램 = 데이터 + 코드라고 배웠는데, 똑같이 말씀해주셔서 놀랐다. 추상과 구체추상은 항상 구체적인 실재에서 시작해야한다.추상은 하늘에서 뚝 떨어지는게 아니라는 것이다.코드를 짤 때도 경험이 많다면 추상이 바로 이루어지겠지만,경험이 없는 주니어들은 구체적인 실재에서 시작을 하며 추상 경험을 키우는 것이 중요할 것 같다.리팩토링을 많이 해봐야겠다.우리가 일상생활에서 아무렇지 않게 하던 대화들 또한 추상이 이루어지고 있었다.정보 함축, 제거를 통해 추상화를 할 수 있고유추, 정보 재현, 이해를 통해 구체화를 할 수 있는데추상화 과정에서 중요한 정보를 부각시키지 못하면 추상화에 실패한다.또한 해석자가 동일하게 공유하는 문맥이 없어도 추상화에 실패한다.예를 들어, 뜬금없이 밤이라고 한다면먹는 밤인지 깜깜한 밤인지 모른다.잘못된 추상화가 야기하는 사이드 이펙트는 생각보다 정말로 크다.해당 도메인의 문맥안에서 정말 중요한 핵심 개념만 남겨서 표현하는 것바로 적절한 추상화가 중요하다. 이름 짓기이름을 짓는다는 행위는, 추상적 사고를 기반으로 한다.우리 도메인의 문맥 안에서 이해되는 용어를 사용하는 것이 중요하다.팁 4가지단수, 복수 구분하기이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기반복문을 처음배울 때 사용하는 i, j, k 같은 변수명에도 이름을 지어주어 추상화를 시켜주자.의미가 명확하게 전달되지 않는 변수명 또한 의미가 명확하게 전달될 수 있도록 변경하자. 메서드와 추상화한 문단의 주제는 반드시 하나다.한 메서드의 주제는 반드시 하나다. 메서드 선언부(중요)반환타입 메서드명 (파라미터)메서드명은 추상화된 구체를 유추할 수 있는, 적절한 의미가 담겨있어야한다.파라미터와 연결지어서 더 풍부한 의미를 전달할 수 있다.메서드 명명 규칙을 따지기보다, 질적으로 더 좋은 추상화를 해냈느냐가 중요한 포인트이다.파라미터의 타입, 개수, 순서를 통해서도 의미를 전달할 수 있다.파라미터는 외부 세계와 소통하는 창반환타입메서드 시그니처에 납득이 가는 적절한 타입의 반환값을 돌려주어야 한다.반환값이 있다면 테스트도 용이해지니 void 대신 반환할 만한 값이 있나 고민해보자.Q. 한 줄인데도 추상화를 해야하나?A. 그렇다. 코드의 양이 중요하다기 보다 추상화하는 의미가 중요한 것check라는 이름은 보통 void의 반환타입을 가진다.리팩토링 후엔 항상 테스트 코드로 검증하자. 추상화 레벨(중요)메서드를 추출한다는 것 자체가 외부 세계와 내부 세계를 나누고 추상화 레벨이 갈린다는 것추상화 레벨이 낮은 부분을 추출하고 경계를 만든다는 것하나의 세계 안에서는 추상화 레벨이 동등해야한다.추상화 레벨이 맞지 않는 순간 읽는 사람은 멈칫하게 된다.코드 흐름을 보고 추상화 레벨이 맞지 않는 부분이 있다면 추상화 레벨을 맞춰주자.메서드로 추출한다는 것의 의미는추상화 레벨을 동등하게 맞춰줌으로써 읽는 사람이 멈칫하지 않도록 만드는 기법 매직 넘버, 매직 스트링매직 넘버, 매직 스트링이란 추출되지 않은 날 것의 숫자나 문자열이름을 주고 의미를 부여하여 상수(변하지 않는 값)로 추상화시키자.가독성, 유지보수성이 올라간다.자바의 상수 네이밍 컨벤션은 대문자, 언더스코어Day 3섹션3. 논리, 사고의 흐름 뇌 메모리 적게 쓰기“정리 시스템에서 중요한 과제는최소의 인지적 노력으로 최대의 정보를 제공하는 것이다.”뇌 또한 추상화를 하고 있다는 것“뇌는 한 번에 한 가지 일 밖에 하지 못한다.”뇌도 CPU처럼 한 번에 한 가지 일 밖에 하지 못하고멀티태스킹을 한다는 것은 사실 CPU처럼 문맥교환이 일어나는 것이었다.인지적 경제성: 최소한의 인지만으로 최대의 효율을 내보자 early return이 함수를 메서드로 추출하고 early return 을 적용 시키면?인지적 경제성: 우리가 인지해야 할 정보를 최대한 줄이자 사고의 depth 줄이기중첩 반복문, 중첩 분기문중첩 반복문, 중첩 분기문의 depth를 줄이는 방법을 메서드로 추출하는 방법이 있었다.내부 세계와 외부 세계를 분리함으로써 사고를 쪼개는 것주의할 점보이는 depth가 아니라 사고의 depth를 줄이는 것무조건 2중 중첩을 줄이는 게 아니고 추상화의 의미가 있는지 꼭 살펴봐야한다.2중 중첩이 사고과정에 도움이 된다면 그냥 냅두는게 나을 수 있다.사용할 변수는 가깝게 선언하기사용할 변수를 가깝게 선언하자안그러면 인지해야 할 정보가 많아진다.리팩토링 꿀팁수정할 메서드를 바로 수정하면 컴파일 에러가 막 난다.컴파일 에러를 최소화하면서 리팩토링 하는게 에너지를 덜 쓰기때문에수정할 메서드를 복제해서 리팩토링을 진행한다.나는 강사님과 다르게 메서드 참조가 익숙치 않아서 람다로 냅두었다. 공백 라인을 대하는 자세공백 라인도 의미를 가진다.단락 또는 의미 구간마다 공백을 넣어주자부정어를 대하는 자세부정어구를 안써도 되는지 체크하기부정의 의미를 담은 다른 단어가 존재하는지 or 부정어구로 메서드명 구성하기ex) !isLeftDirection()을isRightDirection() 또는 isNotLeftDirection()으로 구성해서 인지적 경제성을 지키자부정연산자를 제거할 수 있다면 베스트 해피 케이스와 예외 처리사람은, 해피 케이스에 몰두하는 경향이 있다.예외처리를 꼼꼼하게 하는 것이 개발자의 역량사용자 입력은 무조건 불신을 깔고가자의도한 예외와 예상하지 못한 예외를 구분해서 잘 처리하자 Null을 대하는 자세항상 NPE를 방지하는 방향으로 경각심 갖자.return null을 자제하자.Optional은 비싼 객체이다. 꼭 필요한 상황에서 반환 타입에 사용하자.메서드 반환 타입으로만 사용되게 설계가 됐다.isPresent() - get()은 안티패턴이다.orElse, orElseThrow, ifPresent 등을 사용하자.orElse, orElseGet은 잘 알고 써야한다. 괄호안을 언제 실행하나?orElse(): 항상 괄호안 실행, 확정된 값일 때 사용orElseGet(): null인 경우 괄호안 실행, 값을 제공하는 동작(Supplier) 정의performanceHeavy()가 orElse일때는 항상 실행됨 orElseGet일때는 null일 때만 실행e.printStackTrace();는 실무에서는 안티패턴이다. 섹션4. 객체 지향 패러다임추상의 관점으로 바라보는 객체 지향객체: 추상화된 데이터 + 코드협력과 책임객체간의 협력과 객체가 담당하는 책임오브젝트에서 본 개념이다. 협력, 책임관심사의 분리높은 응집도, 낮은 결합도결합도: 하나가 바뀌었을 때 다른 하나가 영향을 받는 정도객체를 설계할 때 관심사의 분리, 추상화를 기반으로 어떻게 설계하면 좋을지 알아보자 객체 설계하기 (1)전체 로직에서 공통 관심사를 분리해서 객체를 만든다객체의 내부를 보면외부 세계에서 이 객체를 사용할 때 혹은 협력할 때 이 공개 메서드 선언부를 통해 책임을 드러낸다.이런 객체들이 모여서 객체간 협력이 발생객체는 관심사가 한 군데로 모여 유지보수성이 올라간다. 새로운 객체를 만들 때 주의할 점1개의 관심사로 명확하게 책임이 정의되었는지 확인하자.생성자, 정적 팩토리 메서드에서 유효성 검증이 가능하다.setter는 사용을 자제하자.getter도 처음에는 사용을 자제하자. 반드시 필요한 경우에 추가하자.객체에 메시지를 보내라. 객체는 책임을 가지고 협력하는 자율적인 존재이다.필드는 적을수록 좋다. 단, 미리 가공하는 것이 성능 상 이점이 있다면 필드로 갖고있는 것이 좋을 수 있다. 객체 설계하기 (2)캡슐화 되어있는 데이터를 바깥에서 알고 있다고 생각하지 말자모르기 때문에 짐작해서 물어보는 것이 최선인 것 → 메시지를 보내는 것리팩토링 중에 도메인 지식을 얻었다.(열렸다/닫혔다 | 사용자가 체크했다 는 다른 개념)도메인 지식은 만들어가는게 아니라 발견하는 것이다.기존에 원래 있던 도메인 지식인데 우리가 아직 발견하지 못한 것뿐이다 라고 얘기를 한다.새로운 도메인 지식을 발견했을 때 과감하게 우리의 사고를 전환할 수 있어야 한다. Day 4SOLID객체지향 설계를 우리가 더 이해하기 쉽고 유연한 형태로 유지보수하기 쉽게 만드는데 도움을 주는 원칙들하나씩 알아보자 SRP: Single Responsibility Principle하나의 클래스는 단 한 가지의 변경 이유만을 가져야 한다.변경이유 = 책임책임이라는 것을 발견해내는게 굉장히 어렵다.OCP: Open-Closed Principle확장에는 열려있고, 수정에는 닫혀있어야 한다.추상화와 다형성을 활용해서 OCP를 지킬 수 있다.LSP: Liskov Substitution Principle상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.ISP: Interface Segregation Principle클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.인터페이스를 잘게 쪼개라ISP를 위반하면 불필요한 의존성으로 인해 결합도가 높아진다.DIP: Dependency Inversion Principle상위 수준의 모듈은 하위 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.의존성: 하나의 모듈이 다른 모듈을 참조하는 것의존성의 순방향: 고수준 모듈이 저수준 모듈을 참조하는 것DIP, DI, IoC의 차이점에 대해서 인지하자. Day 5섹션5. 객체 지향 적용하기상속과 조합상속보다 조합을 사용하자상속은 부모와 자식의 결합도가 높아서 수정이 어렵다. Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해서, 불변성, 동등성, 유효성 검증 등을 보장해야한다.불변성: final, setter 금지동등성: 동일성이 달라도 내부 값으로 같은 값 객체 취급, equals() & hashCode() 재정의 필요유효성 검증: 객체가 생성되는 시점에 값에 대한 유효성을 보장하기이 강의를 듣고나서,왜 equals() & hashCode()를 재정의 해야하는지,만약 둘 중 하나만 재정의 하는 경우엔 어떻게 되는지,어떻게 hash 자료형을 구현하는지에 대해 스스로 공부할 수 있었다.VO vs. EntityEntity는 식별자가 존재, VO는 식별자 없음 일급 컬렉션처음 강의를 들었을 때는 왜 오류가 나는지 일급 컬렉션쪽을 의심하고있었다.하지만 아예 다른 메서드에 인자로 넘겨줄 때 같은 객체를 넘기고 있었기 때문에 발생하는 문제였다.그리고 new ArrayList()로 새로운 리스트 객체를 넘겨주는데어떻게 CellPosition 들은 똑같지? 궁금해져서 찾아보았더니new ArrayList(positions)는 리스트만 새로 만들고, 내부 원소들은 기존 객체를 참조한다고 한다.만약 복사본을 만들지 않고 원본 객체를 계속 가져다 쓰면 어떻게 될까 직접 해보았는데,예상했을땐 지뢰가 일렬로 붙어있는걸 생각했는데 별로 문제는 없어보였다.아마 CellPosition을 이미 만든 뒤에 섞었기 때문에 상관없을듯 하다.꼭 새로운 복사 리스트를 만들어서 반환해야 하는 경우는 무엇이 있을까 궁금하다. Enum의 특성과 활용상수의 집합이며, 상수와 관련된 로직을 담을 수 있는 공간상태와 행위를 한 곳에서 관리할 수 있는 추상화된 객체변경이 정말 잦은 개념은 Enum보다 DB로 관리하는 것이 나을 수 있다. 다형성 활용하기반복적인 if문을 단순하게 만들어볼 수 없을까?→ 어떤 조건을 만족하면, 조건에 해당하는 행위 수행변하는 것은 조건과 행위처음 들었을 때는 enum value마다 인터페이스 구현을 할 수 있는 것을 몰랐다.두번째 들었을 때는 까먹었다.역시 복습이 중요하다. 숨겨져 있는 도메인 개념 도출하기도메인 지식은 숨겨져있는 것을 발견하는 것이다.완벽한 설계는 없다. 그 당시의 최선이 있을 뿐 미션Day 2https://github.com/p-seonggeun/readable-code추상과 구체의 예시추상과 구체의 예시 문제를 해결하면서 내가 오늘 한 일에 대해서 생각해보았다.오늘 나는 하얀풍차에 가서 빵을 사왔는데 이것에 대해서 추상화와 구체화를 해봐야겠다고 생각했다.추상화 레벨에 따라 적어보았다.Day 4아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.public boolean validateOrder(Order order) { if (order.doesNotHaveAnyItem()) { log.info("주문 항목이 없습니다."); return false; } if (order.doesNotHaveCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } if (order.hasNegativeTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } return true; }처음에 논리, 사고의 흐름에서 배운 인지적 경제성을 기반으로 early return을 적용시켜보았다. 그리고 사고의 Depth를 줄이기 위해서 총 가격이 0보다 큰지 확인하고 사용자정보가 있는지 확인하는 로직을 분리해냈다. 그리고 부정어를 대하는 자세를 떠올려 여러가지 메서드명들을 생각해보았다. isInvalid, isEmpty(), totalPriceIsGreaterThanZero() 등 여러개를 생각하고 고민해보았다. 너무 포괄적이거나 길어서 최종적으로 doesNotHave, has로 선택했다. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP: 하나의 클래스는 하나의 책임만 가져야 한다. 변경이 있을 때 파급효과가 적으면 SRP를 잘 지킨것이다.OCP: 확장에는 열려있고, 변경에는 닫혀있다. 추상화와 다형성을 활용하면 지킬 수 있다.LSP: 컴파일 성공을 넘어서 인터페이스 기능을 보장해야한다. 다형성에서 구현 클래스들은 인터페이스 규약을 지켜야 한다는 것ISP: 특정 클라이언트에 맞는 여러개의 인터페이스가 범용 인터페이스 하나보다 낫다. 인터페이스가 명확해지고 대체 가능성이 높아진다.DIP: 추상화에 의존해야하고 구체화에 의존하지 말자 구체화에 의존하면 변경이 어려워진다.미루지 않고 잘 참여했다.다음주도 이렇게만 하면 될 것 같다.두번째 듣는것인데도 까먹은 부분들이 많이 보였다.역시 복습이 중요하다.모두 파이팅
백엔드
・
워밍업클럽
・
백엔드
・
클린코드
・
3기
・
박우빈
・
1주차발자국