블로그
전체 6#카테고리
- 백엔드

2024. 10. 27.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 4주차 발자국
Practical Testing: 실용적인 테스트 가이드 수강 후 작성한 4주차 발자국입니다. 4주차 강의 정리 Test DoubleDummy : 아무 것도 하지 않는 깡통 객체Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체Stub : 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체 그 외에는 응답하지 않는다. - 상태 검증 (State Verification)Spy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체처럼 동작시키고 일부만 Stubing 할 수 있다.Mock : 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체 - 행위 검증 (Behavior Verification)BDDMockitoMockito에서 BDD 스타일에 맞춰서 작성할 수 있게 끔 도와주는 라이브러리Classicist vs MockistClassicistMocking 처리를 다 하지 말고 진짜 객체로 최대한의 테스트를 해야 한다는 주의Mockist모든 것을 Mocking 위주로 테스트하자. 기능이 보장된 것들은 mocking 처리하고 테스트를 빠르고 간단하게 하자는 주의더 나은 테스트를 작성하기 위한 구체적 조언한 문단에 한 주제테스트는 문서로서의 기능을 한다.하나의 테스트가 한 문단이라고 생각한다면 하나의 테스트는 하나의 주제만을 가져야 한다.완벽하게 제어하기어떤 테스트 환경을 조성하기 위해 완벽하게 제어할 수 있어야 한다.현재 시간, 랜덤 값, 외부 시스템과 연동하는 경우테스트 환경의 독립성을 보장하자테스트 간 결합도가 생기는 경우 독립성을 보장해야 한다.테스트 간 독립성을 보장하자두 개 이상의 테스트에 대한 독립성을 보장해야 한다.한 눈에 들어오는 Test Fixture 구성하기테스트를 위해 원하는 상태로 고정시킨 일련의 객체given절에서 생성했던 모든 객체들을 의미한다Test Fixture 클렌징deleteAll vs deleteAllInBatch@ParameterizedTest하나의 테스트 케이스인데 값을 여러 개로 바꿔보면서 테스트를 하고 싶을 때 사용하면 좋은 테스트@DynamicTest어떠 환경을 설정해놓고 이 환경에 변화를 주면서 테스트하고싶을때 사용하면 좋은 테스트private 메서드는 테스트할 필요가 없다.테스트에서만 필요한 메서드 중 생성자 등은 프로덕션 코드에 생성해도 좋다. 4주차 회고이번 워밍업 클럽에 참여하면서 느낀점 한 가지는 '조금이더라도 계속해서 공부하자'이다. 워밍업 클럽에 참여하면서 지식을 많이 얻어갔다고는 못하겠지만 확실한건 공부에 대한 동기부여가 됐다. 이번 테스트 코드 강의를 들으면서 테스트 코드 자체를 처음 작성해보고 JPA도 처음이어서 강의를 보긴 했어도 100퍼센트 이해하진 못했다. 그래서 이번 워밍업 클럽이 끝난 후엔 JPA 스프링 자바에 대한 공부를 더 해야겠다고 생각했다. 부족한 점이 너무너무 많다고 느껴서 퇴근 후에 조금이라도 공부해서 실력을 꾸준히 쌓을 것이다.

2024. 10. 25.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 DAY 15 미션
Practical Testing: 실용적인 테스트 가이드 수강 후 작성한 DAY 15 미션입니다. Persistence Layer특징 Persistence Layer는 데이터 엑세스에 집중한 레이어이며 비즈니스 로직이 포함되서는 안된다. 오직 데이터에 접근하는 CRUD 역할만 수행해야 한다.테스트 방법 JPA 레포지토리를 상속 받은 레포지토리를 테스트한다. 데이터베이스에 엑세스하는 로직으로만 테스트해야한다. Business Layer특징 Business Layer는 비즈니스 로직을 구현하는 역할을 한다. Persistence Layer와의 상호작용을 통해 비즈니스 로직을 전개시킨다. 가장 중요한 역할은 트랜잭션을 보장해야 한다. 로직을 전개하다가 문제가 발생하면 롤백되어야 한다. 작업 단위에 대한 원자성 보장 책임을 가진다. 테스트 방법 Business Layer테스트는 Persistence Layer를 통합해서 두개의 레이어를 한번에 테스트한다. Presentation Layer특징 Presentation Layer은 외부 세계의 요청을 가장 먼저 받는 계층이고 파라미터에 대한 최소한의 검증을 수행한다. 비즈니스 로직을 전개시키기 전에 유효성 검증을 하는것을 중점으로 한다.테스트 방법 하위의 Business Layer, Persistence Layer를 Mocking 처리하여 테스트한다. 가짜 객체로 대선해서 정상 동작할 것을 가정하고 테스트한다. MockMvc라는 프레임워크를 사용한다.

2024. 10. 20.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 3주차 발자국
Practical Testing: 실용적인 테스트 가이드 수강 후 작성한 3주차 발자국입니다. 3주차 강의 정리 테스트를 작성하는 역량주니어 개발자에게 가장 기대하는 요소 중 하나채용 시 구현 과제 등에서 테스트 작성 여부, 테스트 코드 구현 방식을 확인소프트웨어 품질을 보증하는 방법으로, 그 중요성을 알고있는지를 확인무엇을 학습?테스트 코드가 필요한 이유좋은 테스트 코드란 무엇일까?실제 실무에서 진행하는 방식 그대로 테스트를 작성해가면서 API를 설계하고 개발하는 방법정답은 없지만 오답은 있다! 구체적인 이유에 근거한 상세한 테스트 작성 팁 테스트를 왜 해야하는가?내가 개발한 기능이 내가 의도한대로 정상 동작하는지 확인되어야한다.이걸 사람이 수동으로 검증하는게 아니라 자동화시키자는것개발한 소프트웨어의 신뢰성을 높일수있다테스트 코드가 엉망, 복잡함→ 부작용→ 잘짜야한다…테스트 코드를 작성하지 않는다면변화가 생기는 매순간마다 발생할 수 있는 모든 case를 고려해야한다.변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트 코드가 병목이 된다면프로덕션 코드의 안정성을 제공하기 힘들어진다.테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다.올바른 테스트 코드는자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.소프트 웨어의 빠른 변화를 지원한다.팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.가까이 보면 느리지만, 멀리보면 가장 빠르다 단위테스트 (Unit test)작은 코드단위를 독립적으로 검증하는 테스트 (클래스, 메서드)검증 속도가 빠르고, 안정적이다.JUnit 5단위 테스트를 위한 테스트 프레임워크XUnit - Kent BeckAssertJ테스트 코드 작성을 원활하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝 지원테스트 케이스 세분화하기질문하기 : 암묵적이거나 아직 드러나지 않은 요구사항이 있는가?해피케이스예외케이스 - 음료 수량은 음수로 입력한다면, 0을 입력한다면 경계값 테스트 (범위, 구간, 날짜 등)경계값이 존재한다면 테스트는 항상 경계값에서 테스트 하는게 좋다.예를들어 3이상일때 A라는 조건을 만족해야한다. 는 요구사항이 있다면 해피케이스 테스트는 3으로 하는것이 좋다. 4나 5보다.. 예외케이스는 2로 하는게 좋다. 테스트하기 어려운 영역을 구분하고 분리하기관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, 데이터 베이스에 기록하기 등@Data, @Setter, @AllArgsConstructor 지양 TDD : Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론red - green - refactor실패하는 테스트 작성해서 빨간불보기테스트 통과하는 최소한의 코딩해서 초록불보기초록불을 유지하면서 리팩토링 구현코드 개선TDD의 핵심가치 → 피드백내가 작성하는 코드에 대해서 빠르게 피드백을 받을 수 있다는 장점기존의 선 기능 구현 후 테스트 작성하는 방식테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성 (해피케이스만..)잘못된 구현을 다소 늦게 발견한 가능성선 테스트 작성 후 기능 구현복잡도가 낮은 테스트 가능한 코드로 구현할 수 있게 한다.쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다. 테스트는 문서다프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있다.DisplayName을 섬세하게테스트 메서드가 어떤 역할을 하는지 이름 지을 수 있다.ex)음료 1개 추가 테스트→ 음료를 1개 추가할 수 있다명사의 나열보다 문장으로 작성하기~테스트 지양하기음료를 1개 추가할 수 있다→ 음료를 1개 추가하면 주문 목록에 담긴다.테스트 행위에 대한 결과까지 기술하기특정 시간 이전에 주문을 생성하면 실패한다→ 영업 시작 시간 이전에는 주문을 생성할 수 없다.도메인 용어를 사용하여 한층 추상화된 내용을 담기메서드 자체의 관점보다 도메인 정책 관점으로테스트의 현상을 중점으로 기술하지 말 것성공한다 실패한다는 피하기 BDD: Behavior Driven DevelopmentTDD에서 파생된 개발 방법함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트 케이스 자체에 집중하여 테스트한다.개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준을 권장Given / When / ThenGiven : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증어떤 환경에서(given)어떤 행동을 진행했을 때(when)어떤 상태 변화가 일어난다.(then)→ DisplayName에 명확하게 작성할 수 있다. Spring & JPA 기반 테스트Layered Architecture 레이어드 아키텍쳐스프링 MVC 기반에서 가장 많이 사용되는 아키텍쳐 왜 레이어를 구분하는가? → 관심사의 분리사용자의 요청이 왔을때 각 레이어별로 역할을 할당테스트 하기 복잡해보인다는 생각이 들 수 있다.여러 객체가 협력해서 하나의 기능을 동작하게 하는 경우는 어떻게 테스트 할 것인가, 단위 테스트만으로는 커버 못함→ 통합 테스트가 필요하다 (Integration test)여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트일반적으로 작은 범위의 단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다.풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Spring / JPASpringLibrary vs Framework라이브러리는 내 코드가 주체가 돼서 필요한 기능이 있다면 외부에서 끌어와서 사용. 외부에서 끌어오는걸 라이브러리라고 표현한다.프레임워크는 이미 프레임이 있는것. 동작할 수있는 환경이 있음, 내 코드가 수동적으로 환경에 들어가서 동작스프링은 프레임워크로써 제공하는 환경이 있고 그거에 맞춰서 코드를 작성하면 동작하는 구조 스프링 3대 개념IoC (Inversion of Control)객체에 대한 생명주기 관리는 제 3자가 하는것,, 제어의 역전이 일어남.DI (Dependency Injection)컨테이너라는 제 3자가 주입해준 객체를 사용한다.AOP (Aspect Oriented Programming)비즈니스 흐름과 관련없는 부분을 관점이라는 용어로 부르고JPA (Java Persistence API)ORM (Object-Relational Mapping)orm 의 한 종류로 jpa가 존재함ORM객체 지향 패러다임과 관계형 DB 패러다임의 불일치이전에는 개발자가 객체의 데이터를 한땀한땀 매칭하여 DB에 저장 및 조회ORM을 사용함으로써 개발자는 단순 작업을 줄이고, 비즈니스 로직에 집중할 수 있다.JPAJava 진영의 ORM 기술 표준인터페이스이고 여러 구현체가 있지만 보통 Hibernate를 많이 사용한다.반복적인 CRUD SQL을 생성 및 실행해주고, 여러 부가 기능들을 제공한다.편리하지만 쿼리를 직접 작성하지 않기 때문에 어떤 식으로 쿼리가 만들어지고 실행되는지 명확하게 이해하고 있어야 한다.Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공QueryDSL과 조합하여 많이 사용한다. (타입체크, 동적쿼리)JPA에서 주고 사용되는 어노테이션들@Entity, @Id, @Column@ManyToOne, @OneToMany, @OneToOne, @ManyToMany 3주차 회고이번주엔 테스트 코드에 대해 학습했는데 개발자로 취업하고 테스트 코드를 제대로 작성해본 것은 처음이었다. 지금 회사에서는 테스트코드를 작성하지 않는다. IT관련 미디어에서 테스트 코드 작성해야한다. TDD 해야한다. 이런 말을 많이 하는데 회사에선 안하니까 계속 묵혀두기만 했었다. 입사 처음엔 테스트 코드에 대한 필요성을 잘 못느끼다가 요즘 들어서는 점점 느끼고 있고 공부의 필요성을 느꼈다. 기존에 있었던 기능에 대해서 코드를 수정하면 기존에 잘 돌아가던 기능도 에러가 발생할까 걱정되었고, 코드를 다 작성하고도 혹시나 내가 못찾은 버그는 없을까 불안한 마음이 생겼었다. 또 테스트 시 입력해야 할 항목이 많은 경우는 하나하나 선택하고 테스트 해봐야 하는게 정말 불편했다. 지금은 수동으로 하나하나 테스트 하고 있긴 한데 적합한 방법은 아닌 것 같다는 생각이 계속 들고 있다. 이번 테스트코드 과정을 통해 지금까지 회피하고 묵혀뒀던것들을 어느 정도라도 해소할 수 있었으면 좋겠다.

2024. 10. 13.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 2주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법 수강 후 작성한 2주차 발자국입니다. 2주차 강의 정리좋은 주석우리가 리팩토링할 때 정말 큰 난관 중 하나는 히스토리를 전혀 알 수 없는 코드다.후대에 전해야할 의사결정의 히스토리를 도저히 코드로 표현할 수 없을때 주석으로 상세하게 설명한다.자주 변하는 정보는 최대한 지양해서 작성한다.관련 정책이 변하거나 코드가 변경되었다면 주석도 함께 업데이트한다.우리가 가진 모든 표현 방법을 총동원해 코드에 의도를 녹여내고, 그럼에도 불구하고 전달해야 할 정보가 남았을때 사용하는 주석변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열한다.인지적 경제성메서드의 순서객체는 협력을 위한 존재이다공개 메서드를 상단에 배치하는것을 선호공개 메서드끼리도 기준을 가지고 배치하는 것이 좋다상태변경 >> 판별 ≥ 조회비공개 메서드는 공개 메서드에서 언급된 순서대로 배치한다중요한것은 나열 순서로도 의도와 정보를 전달할 수 있다는 것패키지 나누기패키지는 문맥으로써의 정보를 제공할 수 있다.대규모 패키지 변경은 팀원과의 합의를 이룬 시점에 하자알고리즘 교체하기재귀를 사용한 DFS도 결국 Stack이다.→ 스레드마다 생기는 스택 영역에는, 함수를 호출할 때마다 frame이 쌓인다.→ frame은 지역 변수, 연산을 위한 정보 등을 담고있다.→ 스택 영역은 결국 크기가 제한되어 있다.IDE의 도움 받기코드 포맷 정렬ctrl + alt + l코드 품질Sonarlint포맷 규칙.editorconfig2주차 강의, 미션 회고강의를 듣고 미션을 수행는 내내 강의를 처음부터 다시 돌려봐야겠다..는 생각이 계속 들었다. 강의는 들었어도 이걸 실제로 적용하려니까 머리가 백지장이 되어버렸다. 지금까지 지뢰찾기 게임을 리팩토링하며 여러가지 스킬들을 배웠는데 내가 놓쳤던 점은 그냥 이런 개념이 있구나,, 받아들이기만 했다는 점이다. 이제는 그냥 받아들이지만 말고 왜 이 개념을 여기에 적용할 수 밖에 없는지, 적용 전과 후가 어떻게 달라졌는지,,까지 사고의 흐름도 완벽하게 파악해야겠다고 생각했다. 이제 다음주부턴 테스트코드 강의 시작이다. 1주차와는 다른 마음가짐으로! 강의 듣고 더 열심히 해야겠다..

2024. 10. 06.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 1주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법 수강 후 작성한 1주차 발자국입니다. 1주차 강의 정리섹션1인텔리제이, jdk 설치 및 readable code fork 를 진행하였다.기존에 jdk8버전을 사용했기에 추가로 jdk17버전을 설치하여 실행했다.섹션2추상화가장 중요한 정보만 남기고 덜 중요한 정보는 덜어내는 과정을 추상화라고 한다.도메인 영역 별로 추상화 기준이 다를 수 있다.이름짓기는 추상화의 가장 대표적인 행위이다. 이름 짓기단수와 복수 구분이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기 메서드 선언부메서드명은 추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름으로 지어야 한다.파라미터와 연결지어 더 풍부한 의미를 전달할 수도 있다.파라미터의 타입, 개수, 순서를 통해 의미를 전달할 수 있다.파라미터는 외부 세계와 소통하는 창메서드 시그니터에 납득이 가는, 적절한 타입의 반환값 돌려주기void 대신 충분히 반환할 만한 값이 있는지 고민해보기 추상화 레벨하나의 세계 안에서는 추상화 레벨이 동등해야한다.추상화 레벨을 동등하게 맞춰줌으로써 읽는 사람이 자연스럽게 사고가 흘러갈수있게 하는 기법매직넘버, 매직 스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 가독성, 유지보수성 올라감자바에서 상수는 대문자, 언더스코어로 작성한다섹션3뇌 메모리 적게 쓰기코드를 작성할때는 읽는 사람의 뇌 메모리를 적게쓰도록 가독성있게 작성하는게 중요하다.인지적 경제성을 추구하자.Early returnEarly return 으로 else 사용을 지양하자.사고의 depth 줄이기중첩 분기문, 중첩 반복문사고 과정의 depth를 줄이는게 중요하다사용할 변수는 가깝게 선언하기 공백 라인을 대하는 자세공백 라인도 의미를 가진다.→ 복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.부정어를 대하는 자세부정어구를 쓰지 않아도 되는 상황인지 체크하기부정의 의미를 담은 다른 단어가 존재하는지 고민하기 or 부정어구로 메서드명 구성 → isNot doesNot never 등 → 부정 연산자 (!) 의 가독성 떨어지므로해피케이스와 예외처리예외가 발생할 가능성 낮추기어떤 값의 검증이 필요한 부분은 주로 외부 세계와의 접점→ 사용자 입력, 객체 생성자, 외부 서버의 요청 등의도한 예외와 예상하지 못한 예외를 구분하기→ 사용자에게 보여줄 예외와, 개발자가 보고 처리해야 할 예외 구분Null을 대하는 자세항상 NullPointerException을 방지하는 방향으로 경각심 가지기메서드 설계 시 return null을 자제한다.→ 만약 어렵다면, Optional 사용을 고민해본다.Optional에 관하여→ Optional은 비싼 객체다. 꼭 필요한 상황에서 반환 타입에 사용한다. → Optional을 파라미터로 받지 않도록 한다. 분기 케이스가 3개나 된다. → Optional을 반환받았다면 최대한 빠르게 해소한다.섹션4객체 설계하기비공개 필드(데이터), 비공개 로직(코드)공개 메서드 선언부를 통해 외부 세계와 소통 → 각 메서드의 기능은 객체의 책임을 드러내는 창구객체의 책임이 나뉨에 따라 객체 간 협력이 발생객체가 제공하는 것절차 지향에서 잘 보이지 않았던 개념을 가시화관심사가 한 군데로 모이기 때문에, 유지보수성 높아짐 → 객체 내부에서 객체가 가진 데이터의 유효성 검증 책임을 가질 수 있다.여러 객체를 사용하는 입장에서는, 구체적인 구현에 신경 쓰지 않고 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다.새로운 객체를 만들 때 주의할 점1개의 관심사로 명확하게 책임이 정의되었는지 확인하기 → 메서드를 추상화할 때와 비슷하다. → 객체를 만듦으로써 외부 세계와 어떤 소통을 하려고 하는지 생각해보자.생성자, 정적 팩토리 메서드에서 유효성 검증이 가능하다. → 도메인에 특화된 검증 로직이 들어갈 수 있다.setter 사용 자제 → 데이터는 불변이 최고다. 변하는 데이터더라도 객체가 핸들링할 수 있어야한다. → 객체 내부에서 외부 세계의 개입 없이 자체적인 변경, 가공으로 처리할 수 있는지를 확인 → 만약 외부에서 가지고 있는 데이터로 데이터 변경 요청을 해야하는 경우, ‘set~’ 이라는 단순한 이름 보다는 ‘update~’ 같이 의도를 드러내는 네이밍을 고려하자.getter도 처음에는 사용 자제. 반드시 필요한 경우에 추가하기, 객체에 메시지를 보내라 → 필드의 수는 적을수록 좋다.필드의 수는 적을수록 좋다. → 불필요한 데이터가 많을수록 복잡도가 높아지고 대응할 변화가 많아진다. → 필드 A를 가지고 계산할 수 있는 A’ 필드가 있다면 메서드 기능으로 제공SOLIDSRP: Single Responsibility Principle단일 책임 원칙, 하나의 클래스가 하나의 책임만 갖도록 설계하는 원칙하나의 클래스, 객체는 단 한가지의 변경 이유만을 가져야 한다. = 단 하나의 책임, 관심사를 가져야 한다.객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경되는가?동일한 상수를 A클래스, B클래스 둘다 쓰는 상황관심사의 분리SRP를 지키게 되면 높은 응집도, 낮은 결합도를 가지게 된다.응집도란 클래스나 모듈 내에 있는 요소들이 긴밀하게 연관되어있는 정도를 의미한다. 단일 책임을 가지면 응집도는 높아진다.결합도는 두 개 이상의 객체가 협력한다고 했을 때 하나의 객체가 변경되었을때 다른 객체가 영향받는 정도이다. 서로 다른 두 객체간의 의존성을 최소화시키는게 결합도를 낮춘다는 의미이다.OCP: Open-Closed Principle개방-폐쇄 원칙확장에는 열려 있고, 수정에는 닫혀 있어야 한다.→ 기존 코드의 변경 없이, 시스템의 기능을 확장할 수 있어야 한다.추상화와 다형성을 활용해서 OCP를 지킬 수 있다.새로운 요구사항이 생겼을때 기존 코드의 변경 없이 시스템의 기능을 확장할 수 있어야 한다.인터페이스를 활용하자LSP: Liskov Substitution Principle리스코프 치환 원칙상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다. → 자식 클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다. → 부모 클래스를 자식클래스로 변경해도 동일한 결과를 내야한다.LSP를 위반하면, 상속 클래스를 사용할 때 오동작, 예상 밖의 예외가 발생하거나, 이를 방지하기 위한 불필요한 타입 체크가 동반될 수 있다.상속 구조를 사용한다면 LSP를 잘 지키도록 구조 설계를 해야한다.부모와 자식 클래스가 있을때 자식 클래스의 기능이 더 많게 된다.ISP : Interface Segregation Principle인터페이스 분리 원칙클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.→ 인터페이스를 잘게 쪼개라ISP를 위반하면, 불필요한 의존성으로 인해 결합도가 높아지고, 특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.기능 단위로 인터페이스를 잘게 나눠서 사용해라하나의 인터페이스에 여러 기능이 몰려있으면 불필요한 의존성으로 결합도가 높아진다.DIP: Dependency Inversion Principle의존성 역전 원칙상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존하는것 → 저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다. 섹션5상속과 조합상속보다 조합을 사용하자상속은 수정이 어렵다.부모와 자식의 결합도가 높다결합도가 높은 설계조합과 인터페이스를 활용하는 것이 유연한 구조상속을 통한 코드의 중복 제거가 주는 이점보다 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다그냥 부모의 기능을 별도의 객체로 두고 이걸 가져다가 쓰면 상속은 사용하지 않아도 된다유지보수하기 쉬운 조합을 사용하는것이 좋다Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해서 불변성 동등성 유효성 검증 등을 보장해야 한다.불변성 : final 필드, setter 금지동등성 : 서로 다른 인스턴스여도 내부의 값이 같으면 같은 값 객체로 취급한다.유효성 검증 vo vs entity Entity는 식별자가 존재한다. 식별자가 아닌 필드의 값이 달라도, 식별자가 같으면 동등한 객체로 취급한다.VO는 식별자 없이, 내부의 모든 값이 다 같아야 동등한 객체로 취급한다.개념적으로 전체 필드가 다같이 식별자 역할을 한다고 생각해도 된다.일급 컬렉션일급 시민다른 요소에게 사용 가능한 모든 연산을 지원하는 요소변수로 할당될 수 있다.파라미터로 전달될 수 있다.함수의 결과로 반환될 수 있다.ex) 일급 함수함수형 프로그래밍 언어에서, 함수는 일급 시민이다.함수는 변수에 할당될 수 있고, 인자로 전달될 수 있고, 함수의 결과로 함수가 반환될 수도 있다.일급 컬렉션컬렉션을 포장하면서, 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위함단 하나의 컬렉션 필드만을 가진다.컬렉션을 추상화하며 의미를 담을 수 있고, 가공 로직의 보금자리가 생긴다.가공 로직에 대한 테스트도 작성할 수 있다.만약 getter로 컬렉션을 반환할 일이 생긴다면, 외부 조작을 피하기 위해 꼭 새로운 컬렉션으로 만들어서 반환해주자.EnumEnum은 상수의 집합이며, 상수와 관련된 로직을 담을 수 있는 공간이다.상태와 행위를 한 곳에서 관리할 수 있는 추상화된 객체특정 도메인 개념에 대해 그 종류과 기능을 명시적으로 표현해줄 수 있다.만약 변경이 정말 잦은 개념은, Enum보다 DB로 관리하는 것이 나을 수 있다.다형성 활용하기추상화와 다형성을 활용하여 반복되는 if문 제거, OCP 지키기숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것객체 지향은 현실을 100% 반영하는 도구가 아니라, 흉내내는 것이다.현실 세계에서 쉽게 인지하지 못하는 개념도 도출해서 사용해야 할 때가 있다.설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측히고, 시간이 지나 만약 틀렸다는 것을 인지하면 언제든 돌아올 수 있도록 코드를 만들어야 한다.완벽한 설계는 없다. 그 당시의 최선이 있을 뿐1주차 강의 수강 회고이번 1주차에서는 지금까지의 잘못된 코드 습관들을 하나씩 알게 되었고 강의에서 배운대로 고쳐 나가야겠다고 다짐하게되었다. 이번에 알게된 나의 잘못된 코드 습관은 다음과 같다.복수형 이름 짓기 시 ~(e)s 사용하지 않고 ~List 사용했던 것줄임말 사용했던 것 ex) cnt, idx 등추상화 레벨을 생각하지 않았던 것부정연산자 많이 사용했던 것getter, setter 남발했던 것 강의를 보면서 뜨끔했던 순간이 정말 많았다. 그중 특히나 추상화 레벨 강의가 기억에 남는다. 강사님께서 안 좋은 예시를 처음에 보여주셨는데 나였으면 그냥 작성하고 넘어갔을 것 같은 코드였어서 정말 뜨끔했다. 이번 강의를 통해 추상과 구체에 대해 알게 됐고 이제 코드에 배운대로 적용해봐야겠다고 생각했다. 아직 많이 부족하지만 코드를 작성할때 나름 가독성있게 코드를 작성하려고 노력했었는데 클린코드를 어떻게 작성하는지 제대로 강의를 들어보니 지금껏 내가 작성했던 코드는 뭐였을까... 다시 한 번 되돌아 볼 수 있었던 1주였던 것 같다.1주차 미션DAY2 미션추상과 구체 예시 작성'라면끓이기'를 구체 레벨에서 표현한다면?라면 1개를 준비한다.냄비를 준비한다.물 500ml을 준비한다.가열장치를 준비한다.가열장치에 냄비를 올린다.냄비에 물 500ml을 넣는다.가열장치를 켜서 냄비를 가열한다.냄비의 물이 끓을때까지 기다린다.면을 넣는다.건더기 스프를 넣는다.분말 스프를 넣는다.3분동안 면을 저으면서 끓인다.가열장치를 끈다.라면을 그릇에 담아 완성한다. '라면 끓이기' 라는 말이 물넣고 스프넣고 면넣고,, 등등 모든 과정이 함축되어 있는 말 같아서 위와 같이 작성했다. DAY4 미션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; }사용자가 생성한 '주문'이 유효한지를 검증하는 메서드.Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.)필요하다면 메서드를 추출할 수 있다. 풀이과정validateOrder 메서드에서 수행하는 기능을 간단하게 정리하면,주문 항목이 없는지 체크총 가격이 0보다 작은 수인지 체크사용자 정보가 없는지 체크총 3단계로 정리하였다. 그리고 각 단계에서 Early Return을 적용하여 불필요한 else를 없애고 depth를 줄였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } 위 코드에서 부정 연산자(!)를 긍정어로 수정하고 get~ 같은 직접적인 메소드명 대신 Order 객체에게 의도된 메세지를 전달하여 가독성 좋게 if문을 수정하였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.hasNoneItem()) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (order.isTotalPriceLessThenZero()) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (order.hasNoneCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP: Single Responsibility PrincipleSRP는 하나의 클래스, 하나의 객체는 하나의 책임만을 가져야 한다는 원칙이다. 우리는 코드를 작성할 때 응집도가 높게, 결합도가 낮게 동작하도록 구현하여야 하는데 SRP를 지키게 되면 높은 응집도, 낮은 결합도를 가지게 된다. 응집도란 클래스나 모듈 내에 있는 요소들이 연관되어 있는 정도를 말하며 SRP를 지키면 이 연관되어 있는 정도가 낮아지므로 응집도는 높아진다. 결합도는 두 개 이상의 객체가 협력한다고 했을 때 하나의 객체가 변경될 경우 다른 객체가 영향 받는 정도를 의미한다. SRP를 지키면 두 객체간 의존성을 최소화시킬 수 있으므로 결합도를 낮출 수 있다. OCP: Open-Closed PrincipleOCP는 확장에는 열려있고, 수정에는 닫혀있어야 한다는 원칙이다. 우리는 코드를 한 번 작성하게 되면 끝이 아니라 지속적으로 코드를 수정하게되는 상황이 발생한다. 만약 코드 작성 후 새로운 요구사항이 발생하면 기존 코드를 수정해야하는데 기존 코드의 변경 없이 새로운 요구사항을 적용시킬 수 있어야 한다. 그렇게 하기 위해서는 인터페이스와 같은 추상을 통해 개방-폐쇄 원칙을 지킬 수 있으며 항상 기능의 확장을 고려하여 코드를 작성해야 한다. LSP: Liskov Substitution PrincipleLSP는 상속 구조를 설계할 때 부모 클래스를 자식 클래스로 변경해도 동일 결과를 낼 수 있도록 설계해야한다는 원칙이다. 자식 클래스는 부모 클래스의 기능 + @로 구현되어야 하고 부모 클래스의 기능이 변경되면 안 된다. 만약 LSP를 위반한다면 상속 구조에서 오동작이 발생할 수 있으므로 LSP를 지켜 상속 구조를 설계해야 한다. ISP : Interface Segregation PrincipleISP는 기능 단위로 인터페이스를 분리하라는 원칙이다. 하나의 인터페이스 안에 기능이 여러가지 들어있다면 인터페이스 사용 시 사용하지 않는 기능이 생길 것이다. 이렇게 되면 불필요한 의존성이 발생하므로 결합도가 높아지게 된다. 또한 인터페이스에 기능 변경이 생겼을 때 여러 클래스에 영향을 끼칠 수 있다. 이를 방지하게 위해서 인터페이스는 기능 단위로 잘게 쪼개서 사용해야한다. DIP: Dependency Inversion PrincipleDIP는 상위 수준의 모듈은 하위 수준의 모듈에 의존하면 안되며 모두 추상화에 의존해야한다는 원칙이다. 상위 수준의 모듈은 추상화 레벨이 높은 모듈을 의미하고, 하위 수준의 모듈은 추상화 레벨이 낮은 모듈을 의미한다. 만약 상위 수준의 모듈이 하위 수준의 모듈을 참조하게되면 문제가 발생할 수 있다. 하위 수준 모듈은 구체에 가깝기 때문에 변경될 가능성이 높다. 변경 가능성이 높다면 상위 수준 모듈에도 영향을 줄 수 밖에 없다. 그래서 인터페이스와 같은 추상을 통해 상위수준, 하위수준 모듈 모두 직접 서로 의존하는게 아니라, 추상화에 의존하도록 하는 의존성 역전이 필요하다.1주차 미션 회고DAY4 미션 코드 리팩토링에서 강의를 듣기 전이었다면 Early return만 적용해서 else, else if만 없애고 끝냈을 것이었다. 이번에 강의를 듣고 '부정어 사용 지양', '객체에게 메세지 전달' 을 배워 부정 연산자(!)를 긍정어로 수정하고 get~ 같은 직접적인 메소드명 대신 Order 객체에게 의도된 메세지를 전달하도록 리팩토링 하였다. 앞으로 코드를 작성할 때도 강의에서 배운 내용 대로 적용해봐야겠다고 생각했다.

2024. 10. 03.
0
워밍업 클럽 2기 BE 클린코드&테스트코드 DAY 4 미션
Readable Code: 읽기 좋은 코드를 작성하는 사고법 수강 후 작성한 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; }사용자가 생성한 '주문'이 유효한지를 검증하는 메서드.Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.)필요하다면 메서드를 추출할 수 있다. 풀이과정validateOrder 메서드에서 수행하는 기능을 간단하게 정리하면,주문 항목이 없는지 체크총 가격이 0보다 작은 수인지 체크사용자 정보가 없는지 체크총 3단계로 정리하였다. 그리고 각 단계에서 Early Return을 적용하여 불필요한 else를 없애고 depth를 줄였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } 위 코드에서 부정 연산자(!)를 긍정어로 수정하고 get~ 같은 직접적인 메소드명 대신 Order 객체에게 의도된 메세지를 전달하여 가독성 좋게 if문을 수정하였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.hasNoneItem()) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (order.isTotalPriceLessThenZero()) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (order.hasNoneCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP: Single Responsibility PrincipleSRP는 하나의 클래스, 하나의 객체는 하나의 책임만을 가져야 한다는 원칙이다. 우리는 코드를 작성할 때 응집도가 높게, 결합도가 낮게 동작하도록 구현하여야 하는데 SRP를 지키게 되면 높은 응집도, 낮은 결합도를 가지게 된다. 응집도란 클래스나 모듈 내에 있는 요소들이 연관되어 있는 정도를 말하며 SRP를 지키면 이 연관되어 있는 정도가 낮아지므로 응집도는 높아진다. 결합도는 두 개 이상의 객체가 협력한다고 했을 때 하나의 객체가 변경될 경우 다른 객체가 영향 받는 정도를 의미한다. SRP를 지키면 두 객체간 의존성을 최소화시킬 수 있으므로 결합도를 낮출 수 있다. OCP: Open-Closed PrincipleOCP는 확장에는 열려있고, 수정에는 닫혀있어야 한다는 원칙이다. 우리는 코드를 한 번 작성하게 되면 끝이 아니라 지속적으로 코드를 수정하게되는 상황이 발생한다. 만약 코드 작성 후 새로운 요구사항이 발생하면 기존 코드를 수정해야하는데 기존 코드의 변경 없이 새로운 요구사항을 적용시킬 수 있어야 한다. 그렇게 하기 위해서는 인터페이스와 같은 추상을 통해 개방-폐쇄 원칙을 지킬 수 있으며 항상 기능의 확장을 고려하여 코드를 작성해야 한다. LSP: Liskov Substitution PrincipleLSP는 상속 구조를 설계할 때 부모 클래스를 자식 클래스로 변경해도 동일 결과를 낼 수 있도록 설계해야한다는 원칙이다. 자식 클래스는 부모 클래스의 기능 + @로 구현되어야 하고 부모 클래스의 기능이 변경되면 안 된다. 만약 LSP를 위반한다면 상속 구조에서 오동작이 발생할 수 있으므로 LSP를 지켜 상속 구조를 설계해야 한다. ISP : Interface Segregation PrincipleISP는 기능 단위로 인터페이스를 분리하라는 원칙이다. 하나의 인터페이스 안에 기능이 여러가지 들어있다면 인터페이스 사용 시 사용하지 않는 기능이 생길 것이다. 이렇게 되면 불필요한 의존성이 발생하므로 결합도가 높아지게 된다. 또한 인터페이스에 기능 변경이 생겼을 때 여러 클래스에 영향을 끼칠 수 있다. 이를 방지하게 위해서 인터페이스는 기능 단위로 잘게 쪼개서 사용해야한다. DIP: Dependency Inversion PrincipleDIP는 상위 수준의 모듈은 하위 수준의 모듈에 의존하면 안되며 모두 추상화에 의존해야한다는 원칙이다. 상위 수준의 모듈은 추상화 레벨이 높은 모듈을 의미하고, 하위 수준의 모듈은 추상화 레벨이 낮은 모듈을 의미한다. 만약 상위 수준의 모듈이 하위 수준의 모듈을 참조하게되면 문제가 발생할 수 있다. 하위 수준 모듈은 구체에 가깝기 때문에 변경될 가능성이 높다. 변경 가능성이 높다면 상위 수준 모듈에도 영향을 줄 수 밖에 없다. 그래서 인터페이스와 같은 추상을 통해 상위수준, 하위수준 모듈 모두 직접 서로 의존하는게 아니라, 추상화에 의존하도록 하는 의존성 역전이 필요하다.
백엔드




