블로그

sdsd988

[워밍업 클럽 스터디 2기 백엔드(클린코드, 테스트코드)] 1주차 발자국

1주차 강의섹션 1. 추상과 구체섹션 2. 논리, 사고의 흐름 | 객체 지향 패러다임섹션 3. 객체 지향 패러다임 (SOLID)요약 : 좋은 코드(클린코드)를 작성하기 위한 도구와 사용 방법을 학습 강의 내용추상과 구체 (클린코드의 목적과 도구)논리, 사고의 흐름  우리는 코드를 읽는 시간이 더 많다.따라서, 코드는 쉽게 읽히고 이해되어야 한다.이를 위해, 우리는 추상, 구체 를 이해하고 분리해야 한다.메서드 작성, 파라미터 선언, 반환값 고민, 매직 스트링 등 좋은 코드 작성을 위한 고민과 도구를 학습조기 리턴, 공백 라인, 부정어 등 이해하기 쉬운 코드 작성을 위해 조심해야 하는 부분 학습 한번쯤 들어보거나, 읽어본 이야기다. 그렇지만, 현업에서 개발하면서 시간을 핑계로, 선배들이 좋게 보지 않는다는 이유로 실무 개발과 공부한 내용을 분리해서 개발해왔다. 그렇기에, 실제 코드를 바탕으로 고민하고 코드를 작성해보는 경험이 좋았다. 개발자는 고민하고, 코드를 직접 작성해봐야 뭐가 된다는 것을 새삼 느낄 수 있었다. 객체 지향 패러다임단일 책임 원칙객체의 변경 기준은 책임이다.책임이 단일하지 않다면, 변경의 기준도 단일하지 않다.이로 인해 객체의 코드 변경의 원칙이 무너진다. 코드를 읽고, 객체를 사용해야 하는 입장에서 어려움을 겪게 된다.개방 / 폐쇄 원칙책임이 과중히 부과되어서는 안된다.즉 객체는 자신의 책임에 대해서는 기능을 확장할 수 있지만, 자신이 아닌 책임에 대해서는 영향을 받으면 안된다.현재의 책임이 구체인지 추상인지 고민하고, 구체라면 추상화와 다형성을 활용해서 원칙을 지킬 수 있다.리스코프 치환 원칙 사용하는 입장에서 부모 클래스와 자식 클래스의 차이를 알지 못해야 한다.즉 슈퍼 클래스는 불필요한 기능을 상속해서는 안된다. 객체의 상속 사용시 주의해야 할 것 같다.인터페이스 분리 원칙책임을 명확하게 갖는 것은, 인터페이스도 예외가 아니다.인터페이스는 추상화, 다형성의 중요한 도구인 동시에 책임을 갖는다.의존성 역전 원칙기능은 변하고, 요구사항은 추가된다.그렇기에 고수준 모듈은 구체(저수준 모둘)를 참조하는 것이 아니라 추상에 의존해야 한다.새로운 요구 사항이 추가되고, 기능이 변경된다고 하더라도 추상에 의존한다면 코드의 변경 범위를 최소화 할 수 있다  미션 : 배운 내용을 바탕으로 고민해보기미션1. 추상과 구체 사례 예시 제출미션2. 과제 코드 리팩토링 제출미션은 강의를 학습한 내용을 적용해보는 방향으로 접근했다.특히, 고민 해보는 것을 중요하게 생각했다.과거에도 강의를 보고 학습하면서, 직접 고민해보는 시간이 부족했다고 생각했다. public class Delivery { private Order order; public boolean validateOrder(Order order) { if (validateItemSize(order)) { log.info("주문 항목이 없습니다."); return false; } if (validateTotalPrice(order)) { log.info("올바르지 않은 총 가격입니다."); return false; } if (DoesNotHaveCustomerInfo(order)) { log.info("사용자 정보가 없습니다."); return false; } return true; } private boolean validateTotalPrice(Order order) { return order.getTotalPrice() <= 0; } private boolean validateItemSize(Order order) { return order.doesNotHaveItems(); } private boolean DoesNotHaveCustomerInfo(Order order) { return order.doesNotHaveCustomInfo(); } } 1주차 회고 계획한 것 보다 강의와 미션에 시간을 할애하지 못했다.과제를 제출하고, 다른 참여생분들이 작성한 미션을 보면서 반성하고 자극되었다.특히 아쉬운 점은, 과제를 pdf, github 등을 통해 더 깔끔하게 제출할 수 있었는데 하지 못한점이 아쉽다.좋았던 점은, 워밍업 클럽이라는 곳에 소속감이 생겨서 주어진 기간내에 강의를 완강했다는 것. 혼자 수강했다면 이 정도 진로를 나갈 수 없었을 것이라고 생각한다.가장 좋았던 것은 미션이었다. 강의에서도 가장 좋은 점이 직접 리팩토링을 하는 시간이 많다는 것.워밍업 클럽에서도 미션이 가장 좋았다. 직접 무언가를 하는 경험이 지금의 내게 가장 맞다고 생각되었다.

백엔드백엔드클린코드ReadableCode읽기좋은코드를작성하는사고법

[워밍업 클럽 2기] Day4 - 논리, 사고의 흐름 & SOLID

워밍업 클럽 2기 [Clean Code & Test Code](https://www.inflearn.com/roadmaps/5699) 로드맵의 Day4 미션입니다. 1. 코드 리팩토링As-Ispublic 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; } To-Bepublic boolean validateOrder(Order order) { if (order.itemsIsEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.customerInfoIsEmpty()) { log.info("사용자 정보가 없습니다."); return false; } if (order.totalPriceIsNegative()) { log.info("올바르지 않은 총 가격입니다."); return false; } return true; }고려한 내용!를 쓰지 않고 부정어구를 사용해서 처리일찍 return할 수 있는 부분은 return을 해서 이전 내용을 신경 쓰지 않아도 된다else 지양분기문 중첩 depth 줄이기 2. SOLID(객체 지향의 5대 원칙)SRP(단일 책임)SRP는 하나의 클래스 또는 모듈은 하나의 책임을 가져야 한다는 원칙이다. Q. 그럼 책임이라는 것은 무엇일까? 여기서 책임은 "변경의 이유"라고 표현할 수 있다. 만약 하나의 클래스가 여러 가지 이유로 변경되어야 한다면, 그 클래스는 여러 가지 책임을 가지고 있다는 뜻이다. 이렇게 여러가지 책임이 하나의 클래스에 존재한다면, 추후에 유지보수하기 어려워질 가능성이 높다. Q. 그러면 책임의 범위를 어떻게 정하는 것이 좋을까?책임의 범위라는 것은 문맥과 상황에 따라 다를 수 있다. 이런 책임의 범위를 잘 정하는 것은 경험적인 영역이 많이 포함된다. (한마디로 책임을 보는 눈을 기르기 위해서는 경험을 많이 쌓자!)확실한 것은 어떤 변경이 있을 때 파급 효과가 적다면 SRP를 잘 따른 것으로 볼 수 있다. 정리하자면 SRP는 변경의 이유를 하나로 만들어서 코드의 응집성을 높이고, 결합도를 낮추면서 유지보수성을 개선하는 것 이다. 이때 책임(변경의 이유)의 범위를 잘 정할수 있는 능력을 기르는 것이 중요하다. OCP(개방 폐쇄)OCP는 소프트웨어가 확장에는 열려있고, 변경에는 닫혀 있어야 한다는 원칙이다. 쉽게 말해서, 새로운 기능이나 요소를 추가할 때 기존의 코드 변경 없이 추가가 가능해야 한다. Q. OCP는 어떻게 구현할까?인터페이스를 구현한 새로운 클래스를 만들어서 새로운 기능을 구현하거나 추가하는 다형성을 활용해서 구현한다. 조금더 자세히 설명하자면, 구현체가 아닌 인터페이스를 의존하도록 하고, 단순히 구현체를 변경하는 방식으로 기능의 변경이 가능하도록 설계하는 것이다. LSP(리스코프 치환)LSP는 프로그램의 객체는 프로그램을 깨트리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 원칙이다. 쉽게 말해서 상속 관계에서 자식 클래스의 객체가 부모 클래스의 객체를 완전히 대체해도 정상적으로 동작해야 한다는 의미다. ISP(인터페이스 분리)ISP는 클라이언트는 자신이 사용하지 않는 인터페이스(메서드)에 의존하면 안된다는 원칙이다. 쉽게 말해서, 인터페이스를 명확한 기준을 가지고 더 작은 인터페이스로 분리하자는 원칙이다. Q. 사용하지 않는 인터페이스에 의존하지 말자는 것이 무슨 의미인가?인터페이스가 너무 광범위하면 인터페이스를 구현하는 클래스들이 사용하지도 않을 메서드를 오버라이딩 해야하는 상황이 발생한다. 이를 방지하기 위해서, 명확한 기준을 가지고 인터페이스를 더 작게 분리하자는 것이 ISP 원칙이다. 불필요한 메서드 오버라이딩 외에도, 변경으로 인한 파급 효과를 줄이기 위해서 ISP를 적용할 수 있다.쉽게 말해서, 특정 클라이언트를 위한 인터페이스(좁은 범위의 책임을 가지는 인터페이스) 여러개가 범용적인 인터페이스 하나보다 낫다는 것이다. DIP(의존 관계 역전)DIP는 구체화에 의존하면 안되고, 추상화에 의존해야 한다는 원칙이다. 더 자세히 말하자면 고수준 모듈과 저수준 모듈 모두 추상화에 의존해야 한다는 뜻이다. 쉽게 말해서, 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻이다. Q. 구체적인 요소의 의존을 피하는 이유는?구체적이라는 것은 변동 가능성이 높다는 것이다. 저수준 모듈의 변경 사항이 있을 때 마다 해당 모듈을 사용하는 모듈도 변경이 필요할 가능성이 높아진다. 반면에, 추상화에 의존하게 되면 저수준 모듈이 변경되어도, 고수준의 모듈에는 영향이 가지 않는다(의존 관계의 역전). DIP는 DI(의존성 주입)과 IoC(제어의 역전)이라는 개념과 자주 다루어진다. IoC(제어의 역전)IoC는 프로그래머가 작성한 코드가 프레임워크의 제어를 받게 되는 패턴을 이야기 한다. 전통적인 프로그램에서의 흐름은 프로그래머가 작성한 프로그램이 라이브러리의 코드를 호출해 이용한다. 하지만 제어의 역전이 적용된 구조에서는 프레임워크의 코드가 프로그래머가 작성한 코드를 호출한다.DI(의존성 주입)DI는 클라이언트의 생성에 대한 의존성을 클라이언트의 행위로부터 분리하는 것이다. 쉽게 말해서, 필요한 의존성을 외부로 주입을 받는 것이라고 생각하면 된다. 의존성이라는 것은 동작하기 위해 필요한 클래스 또는 객체이다. Q. 그러면 주입(의존 관계 연결)은 누가 해주는 것인가?주입 받는 쪽이나 주입하는 의존성이 아닌 제 3자가 해준다. 이를 보통 IoC 컨테이너, DI 컨테이너, 등의 표현을 사용한다.스프링을 예시로 들자면, ApplicationContext가 이를 해준다.  

백엔드ReadableCode워밍업클럽2기Day4

Seul Ki Lee

워밍업 클럽 2기 BE 클린코드&테스트 day4 과제

Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)아래 내용은 위의 인프런 강의를 들으면서 과제 및 내용을 정리해봤습니다. 아래 코드 리팩토링 하기public boolean validateOrder(Order order){ if(order.getItems().size() == 0){ log.info("주문 항목이 없습니다"); }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; }본 코드의 문제는 아래와 같다if, else if, else 그리고 중접 if 등으로 인하여 주문의 유효성 조건이 한 눈에 들어오지 않는다=> early return을 사용해보자 무분별한 getter의 사용(if(order.getItems().size() == 0))=> 주문에 담긴 상품이 있는지 확인하는 내용인데 굳이 getter를 써야할까?=> 오히려 order에 item 항목이 있다고 세부 내용을 노출해버리고 있다=> 의미있는 이름의 메소드를 order에서 그 역할을 책임하도록 해보자 부정어의 사용 if(order.hasCustomerInfo()) / !(order.getTotalPrice() > 0)=> 부정어를 사용하면 코드의 가독성을 해친다 => 1순위 긍정어를 사용하여 메소드 만들 수 있는지 확인=> 2순위 긍정어를 사용할 수 없다면 메소드명으로 부정을 나타내서 만들어보자 위의 내용을 고려한 최종 코드는 아래와 같다public boolean validateOrder(Order order){ if(order.isEmptyOrderItem()){ log.info("주문 항목이 없습니다"); return false; } if(order.isLessThanOrEqualToZero()){ log.info("올바르지 않은 총 가격입니다"); return false; } if(order.isEmptyCustomerInfo()){ log.info("사용자 정보가 없습니다"); return false; } return true; }public class Order{ private List<Item> items; public boolean isEmptyOrderItem(){ return order.getItems().size() == 0; } public boolean isLessThanOrEqualToZero(){ return order.getTotalPrice() <= 0 }; public boolean isEmptyCustomerInfo(){ return !hasCustomerInfo(); } public double getTotalPrice(){...} public boolean hasCustomerInfo(){...} }SOLID에 대하여 자기만의 언어로 정리SRP클래스가 변경될 이유는 단 하나다!!클래스 설계 시, 하나의 관심사로 응집하여 구현하도록 하자OCP기존 코드의 변경 없이, 기능 추가하도록 설계가 되어야 한다 다형성을 이용하여 클라이언트 코드의 변경 없이 구현체를 쉽게 변경이 가능하다LSP부모 클래스에서 정의한 스펙을 자식 클래스에서도 지켜야 한다instance of로 타입 체크하여 특정 타입에 대해서만 처리가 필요하다는 건 LSP를 위반한 사례이다!!ISPSRP와 비슷하게 인터페이스 또한 한 가지 책임을 가지도록 분리를 잘해야 한다DIP클래스 참조 시, 추상화 레벨이 높은 인터페이스, 추상클래스, 상위 클래스를 참조하도록 하자코드에 구현체가 드러나게 되면 기능 변경시, 클라이언트 코드의 변경이 생기므로추상화 레벨이 높은 인터페이스 등을 메소드 파라메터, 필드, 리턴타입으로 활용하도록 하자

백엔드ReadableCode워밍업클럽2기

채널톡 아이콘