블로그

10

우빈님의 세심한 코드 리뷰 - 인프런 워밍업 클럽 3기 백엔드 코드 ✨

인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차 🙊 시작이 반이다... 벌써 중간점검 ?인프런 워밍업 클럽이 시작한지 2주가 지났다.. 앞으로 남은 발자국이 2개 뿐이다.. 👣이번주에는 중간점검으로 온라인 라이브가 진행 되었다. 온라인 라이브에 대한 내용은 다음과 같다.미션 Day 4 에 대한 공통 피드백Q&A에 대한 답변미션 Day 7 코드리뷰 진행각 세션은 놀라울 정도의 세심한 우빈님의 피드백 덕분에 많은 인사이트를 얻게 되어 좋은 시간이었다. 나도 첫번째로 코드리뷰를 신청하고 라이브 마지막에 코드리뷰를 받았는데 너무 좋은 경험이었다. 💪(커피를 좋아하시기로 유명한 우빈님께 Q&A 세션 도중 저가 커피 브랜드 중 어디 브랜드가 제일 맛있냐는 질문이 나왔는데.. 드셔 보신 적이 없다고 답변해 주신 부분이 인상적이었다.. ㅋㅋ 😁)✨ 우빈님의 세심한 코드 리뷰위에서 이야기했듯이 코드리뷰를 첫번째로 신청해서 우빈님께 온라인 라이브 시간에 코드리뷰를 받았다.해당 미션은 "스터디 카페" 프로그램을 리팩토링하는 미션이였는데.. 작년 4분기에 강의를 수강했을 당시에 3번이나 진행하였다.첫 번째 리팩토링, 강의를 듣기 전에 리팩토링두 번째 리팩토링, 강의를 수강하며 리팩토링세 번째 리팩토링, 강의를 수강 후 정리하며 다시 리팩토링이번이 네 번째 리팩토링이였는데 할 때마다 왜 새로운 것인지.. 🥲그래도 손이 기억이라도 한 듯 나름 순조롭게(?) 미션을 진행하게 되었고, 추가적으로 리팩토링을 진행하였고 해당 부분을 리뷰를 받고 싶어 신청하게 되었다.(우빈님께서 4번이라는 부분에 놀라셨는지(?).. 디스코드 스레드에 댓글을 남겨주셨다! 🤣)다시 돌아와서.. 코드리뷰 받은 내용은 아래와 같다.1⃣ 중요 도메인 StudyCafePassType의 구조화 🔗 Github PR 링크StudyCafePassType 구조화 리팩토링♻ 리팩토링 코드public enum StudyCafePassType implements PassTypeSelectable, PassTypeFormatter { // 📝 인터페이스 구조화 HOURLY("시간 단위 이용권") { // 📝 사용자 입력에 대한 구조화 @Override public boolean selected(String userInput) { return "1".equals(userInput); } // 📝 사용자 출력 포맷에 대한 구조화 @Override public String format(StudyCafePass pass) { return String.format("%s시간권 - %d원", pass.getDuration(), pass.getPrice()); } } }✏ 우빈님 리뷰Q. 클래스 내부에서 사용자 입력값 및 출력값에 사용하는 인터페이스를 구현함으로써 오버 엔지니어링이 된 것 같은 느낌이 드네요.. 🤦‍♂️ A. 저도 그렇게 생각해요.. ㅋㅋㅋㅋ 오버 엔지니어링이기보다 PassType은 중요한 도메인 모델인데, Input에서만 의미를 가지는 사용자 선택지가 침투하고 있다. 사용자 선택 방법이 "a", "b", "c"로 바뀐다면? 단순히 입력 방식을 바꿨을 뿐인데 무료 도메인 모델이 수정되어야 하는 엄청난 사태가 발생한다. 항상 구조화를 하는 것이 정답은 아니다. Output format도 마찬가지이다. 책임이 우선이다. 적절한 책임의 분배가 객체의 결합도를 낮추고 응집도를 높이는 것이다. 🤔 돌아보기단순히, OCP를 적용하기 위해 접근해서 리팩토링 했었는데..적절한 객체 책임 분리를 하지 못했으며, 중요한 도메인 모델을 수정하는 엄청 큰 사이드 이펙트가 일어날 수 있다는 점을 간과 했다는 것이다.다음부터는 구조화를 남발하지 않고 책임에 집중해서 리팩토링 해야겠다..2⃣ 이용권을 읽는 부분과 읽은 부분의 개념을 추출하여 객체 분리 🔗 Github PR 링크ReadLockerPasses 객체 분리♻ 리팩토링 코드public class ReadLockerPasses { // 📝 LockerPasses를 해석하는 객체 분리 private final List<StudyCafeLockerPass> passes; private ReadLockerPasses(List<StudyCafeLockerPass> passes) { this.passes = passes; } // 📝 lines을 해석하여 List<StudyCafeLockerPass> 객체를 만들어준다. public static ReadLockerPasses ofLines(List<String> lines) { List<StudyCafeLockerPass> passes = lines.stream() .map(ReadLockerPasses::ofLine) .toList(); return new ReadLockerPasses(passes); } private static StudyCafeLockerPass ofLine(String line) { String[] values = line.split(CSV_SPLITTER); // ⭐️ CSV라는 방식에 종속적 StudyCafePassType studyCafePassType = StudyCafePassType.valueOf(values[0]); int duration = Integer.parseInt(values[1]); int price = Integer.parseInt(values[2]); return StudyCafeLockerPass.of(studyCafePassType, duration, price); } // 📝 StudyCafeLockerPasses를 생성해준다. public StudyCafeLockerPasses toPasses() { return StudyCafeLockerPasses.of(passes); } }✏ 우빈님 리뷰Q. 일급 컬렉션을 적용하기 위해 toPasses() 메서드를 생성했는데 Read~라는 네이밍을 가진 클래스에 많은 책임이 부여된 것 같아 네이밍이 모호한 것 같습니다. 좋은 방법이 있을까요..? 🧐 A. 많은 책임이라고 생각하신 이유가 있을까요? "ReadLockerPasses는 어디선가 읽은 lines를 가지고 StudyCafeLockerPasses를 만들어준다"의 책임으로 보여서, 어색하지 않으며 테스트 코드 작성도 가능하다. 그와 별개로 CSV라는 방식에 종속되어있다. CSV형식이 다른 방식으로 바뀌었을 때 같이 바뀌어야 하는 부분이 CSV_SPLITTER 부분이다. 의도한 것 이라면 상관없다. 🤔 돌아보기Read라는 클래스명을 가지고 있어 StudyCafeLockerPasses를 생성해주는 메서드가 존재해 많은 책임이 있다고 생각했는데..우빈님 리뷰 이후에 다시 보니.. 그렇게 어색한가 싶기도 하다.. ㅎㅎ해당 클래스의 작성 당시 CSV 방식을 의존하려는 의도는 없었다. 단순히 읽은 부분의 개념을 추출한 것인데.. 위의 클래스는 CSV_SPLITTER상수가 사용되어 의도하지 않게 CSV라는 방식에 종속적이게 된 것이다.CSV라는 방식이 변경되면 객체 로직이 바뀌어야 한다.객체 구현 시, 종속성에 대해서 방어적으로 접근할 필요가 있어보인다.3⃣ ProvideException 커스텀 예외🔗 Github PR 링크ProvideException 커스텀 예외♻ 리팩토링 코드// 📝 이용권을 가져오는 과정에서 생긴 에러의 커스텀 예외 클래스 생성 public class ProvideException extends RuntimeException { public ProvideException(String message) { super(message); } } public class StudyCafePassMachine { public void run() { try { outputHandler.showPassOrderSummary(order); } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); } catch (ProvideException e) { // 📝 커스텀 예외 catch outputHandler.showSimpleMessage("이용권을 제공받을 수 없습니다."); } catch (Exception e) { outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); } } }✏ 우빈님 리뷰Q. AppException 성격이랑 다른 것 같다고 생각되어 Provider 인터페이스에서 발생하는 예외 클래스 ProvideException를 별도로 생성하였습니다. A. 혹시 어떻게 다르다고 생각셨나요?? AppException의 의도는, 프로그램에서 발생할 수 있는 대부분의 애플리케이션 상황을 정의하는 최상위 예외 클래스이다. 만약 ProvideException을 별도로 표기하여 더 구체적인 상황을 나타내고 싶으면, AppException을 상속받아서 구성해야 한다. 그렇지 않으면 커스텀 예외 클래스가 늘어남에 따라 catch절도 같이 늘어날 것이다. 추가적으로, "이용권을 제공받을 수 없습니다."라는 메시지가 사용자 친화적이지 않다. 🤔 돌아보기리팩토링 당시, 초기 이용권을 가져와야만 프로그램이 실행된다는 관점에서 ProvideException의 커스텀 예외 클래스를 작성하였다.하지만 이용권을 가져오는 부분은 프로그램 내부에서 필요한 시점마다 호출하고 있어우빈님 리뷰대로 AppException 클래스를 상속받아서 작성하는 것이 더 나은 설계 같다.예외 메세지도 사용자 관점에서는 친화적이지 않은 것이 분명하다.내가 키오스크 시스템을 사용하다가 저런 메세지를 마주한다면... 화가 날 것 이다... 😡프로그램의 의도를 정확히 파악할 필요가 있어보인다. 또한 예외 메세지도 누가 보는지에 따라 고민해보는 습관을 길러야겠다.이렇게, 요청한 3개의 리뷰와 2개의 추가 리뷰를 받아 보았다..고작 3일 만에 7명이나 리뷰를 해주셨는데 세심하고 또 세심했다... 퀄리티가 상당했다.. ✨이번 온라인 라이브를 통해 우빈님에 대한 팬심과 존경심이 더욱 커졌다....! 📈리뷰해주신 내용으로 다시 리팩토링을 함으로써 한층 더 Readable Code에 대한 성장을 경험할 수 있었다. 🚀💡 자기만의 언어로 강의 키워드 정리하기 섹션 6. 코드 다듬기좋은 주석 - 주석의 양면성주석이 많다는 것 : 추상화가 덜 되고 가독성이 좋지 않은 코드 (코드 품질 저하 📉)주석이 필요한 경우 : 히스토리를 알 수 없을 경우, 주석으로 상세히 설명변수와 메서드 나열 순서변수 : 사용하는 순서대로 위치한다. (인지적 경제성 / 뇌 메모리 줄이기)객체의 공개/비공개 메서드 : 공개 메서드를 상단에 위치하고, 비공개 메서드 하단에 위치한다. 공개 메서드 중에서도 중요도의 순서에 따라 배치한다.공개 메서드 : 객체의 상태를 변경 하는 부분이 가장 상단에 위치하도록 - 상태 변경 >>> 판별 >= 조회비공개 메서드 : 출현한 순서대로패키지 나누기여러 파일들의 네임 스페이스를 관리하기 때문에 적당한 수준으로 잘 나누어야 한다.대규모 패키지 변경은 팀원과의 합의 필요 -> 추후 conflict가 생길 수 있다.기능 유지보수하기정렬 단축키, linting, style - sonarlint, editorconfig섹션 7. 리팩토링 연습메서드 추출로 추상화 레벨 맞추기Optionalreturn null / Optional 파라미터 사용은 안티패턴이다.객체에 메시지 보내기객체를 존중하고 메시지를 보내자.객체의 책임과 응집도⭐️ 추상화 관점의 차이 - FileHandler구현에 초점을 맞춘 추상화 VS 도메인 개념에 초점을 맞춘 추상화File을 read하는 부분의 로직들은 전부 FileHandler에 들어갈 것이다. 잘못된 객체 응집일 수도 있다..방법에 초점을 맞춘 설계 방식이 아닌 어떤 데이터를 가져오는 가에 대한 초점을 맞추는 것이 좋다.섹션 8. 기억하면 좋은 조언들능동적 읽기가지고 있는 리팩토링 기법들을 총동원해서 읽자. -> 리팩토링하면서 읽기눈으로만 보는 수동적 읽기는 권장하지 않는다.도메인 지식을 늘리기 위해서 능동적 읽기가 필요하다. (작성자의 의도 파악)오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링예시 1. 구현체가 하나인 인터페이스구현체가 수정할 때마다 인터페이스도 수정해야 함코드 탐색의 어려움예시 2. 너무 이른 추상화정보가 숨겨지기 때문에 복잡도가 높아진다.후대 개발자들이 선대의 의도를 파악하기가 어렵다.은탄환은 없다클린 코드도 은탄환이 아니다.실무에서의 줄다리기지속 가능한 소프트웨어 품질 VS 기술 부채를 안고 가는 빠른 결과물 -> 클린 코드를 대비한 코드 센스가 필요하다.모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다.항상 정답인 기술은 없다.한계까지 연습해보고, 적정 수준, 적정 시점을 깨닫는 것이 필요하다.섹션 3. 단위 테스트단위 테스트작은 코드(클래스 또는 메서드) 단위를 독립적으로 검증하는 테스트 -> 가장 기본이 되는 테스트검증 속도가 빠르고, 안정적수동 테스트, 자동화 테스트 -> 인지 필요사람이 검증하는 수동 테스트 -> sout으로 출력하고 눈으로 직접 확인기계가 검증하는 자동화 테스트Junit5, AssertJJunit5 : 단위 테스트를 위한 테스트 프레임워크AssertJ : 테스트 코드 작성을 원할하게 돕는 테스트 라이브러리 - 풍부한 API 메서드 체이닝 지원해피 케이스, 예외 케이스 -> 테스트 케이스 세분화예외 케이스 : 암묵적 혹은 드러나지 않은 요구사항에서 발견경계값 테스트범위, 구간, 날짜 경계값들로 테스트를 해야한다.테스트하기 쉬운/어려운 영역 (순수함수)테스트 하기 어려운 영역을 구분하고 분리하기외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드 : 현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력외부 셰계에 영향을 주는 코드 : 표준 출력, 메시지 발송, 데이터베이스에 기록하기순수 함수 - 테스트하기 쉬운 영역같은 입력에는 항상 같은 결과외부 세상과 단절된 형태lombok@Data, @Setter, @AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션 4. TDD: Test Driven DevelopmentTDD 테스트 주도 개발 (Test Driven Development)로, 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론선 기능 구현, 테스트 작성의 문제점 (일반적인 개발) - 구현순서 : 기능 -> 테스트테스트 자체의 누락 가능성해피 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 기능 구현 (TDD) - 구현순서 : 테스트 -> 기능복잡도(유연하며 유지보수가 쉬운)가 낮은 테스트 가능한 코드로 구현할 수 있게 한다.테스트가 힘든 코드를 위한 코드 작성이 가능예를 들면 LocalDateTime.now()의 경우 외부세계로 분리해서 테스트를 하기 편한 코드를 작성할 수 있다.프로덕션 코드를 작성한 후 테스트 코드를 작성하기 귀찮을수도..쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.테스트 코드가 보장TDD의 관점이전의 관점 : 테스트는 구현부 검증을 위한 보조 수단변화된 관점 : 테스트와 상호 작용하며 발전하는 구현부레드 - 그린 - 리팩토링Red : 실패하는 테스트 작성Green : 테스트 통과 하는 최소한의 코딩Refactor : 구현 코드 개선 테스트 통과 유지애자일 방법론 vs 폭포수 방법론애자일 방법론 https://agilemanifesto.org/iso/ko/manifesto.html반복적 개발(Iterative Development): 짧은 개발 주기(스프린트)를 반복하며 지속적으로 개선.유연성: 요구사항 변경을 수용할 수 있도록 유동적으로 진행.고객 참여: 개발 과정에서 지속적인 피드백을 반영하여 사용자 중심 개발 가능.자율적인 팀 구성: 개발팀이 자체적으로 의사 결정을 하며, 빠르게 문제를 해결함.폭포수 방법론단계적 개발: 요구 분석 → 설계 → 구현 → 테스트 → 배포 → 유지보수 순서로 진행됨.문서 중심: 각 단계마다 문서화가 철저하게 이루어짐.선형 구조: 이전 단계가 완료되어야 다음 단계로 넘어갈 수 있음.변경이 어려움: 초기에 요구사항을 확정하면 이후 변경이 어렵고 비용이 많이 듦.익스트림 프로그래밍XP(Extreme Programming, 익스트림 프로그래밍)는 애자일 방법론 중 하나로, 빠른 개발 주기와 지속적인 피드백을 중심으로 하는 소프트웨어 개발 방법론이다. 고객의 요구사항 변화에 빠르게 대응할 수 있도록 짧은 개발 반복 주기(Iteration)와 강한 협업 문화를 강조한다.스크럼, 칸반스크럼애자일 프레임워크로, 일정한 스프린트 동안 작업을 계획하고 진행하는 반복적이고 점진적인 개발 방식이다. 짧은 개발 스프린트를 통해 빠르게 결과물을 만들고 지속적으로 개선하는 것이 핵심이다.1⃣ 백로그 작성 – 제품 백로그에 모든 요구사항을 정리2⃣ 스프린트 계획 – 스프린트 기간 동안 수행할 작업 선정3⃣ 스프린트 진행 – 개발 진행 및 매일 스탠드업 미팅4⃣ 스프린트 리뷰 – 개발 완료된 기능을 검토5⃣ 회고(Retrospective) – 개선점을 찾고 다음 스프린트에 반영칸반Workflow와 가시성을 중심으로 한 애자일 프레임워크로, 지속적인 개선과 작업량 관리를 중점적으로 다룬다. 작업을 시각적으로 표현하여 현재 진행 상황을 쉽게 파악할 수 있도록 합니다.1⃣ Backlog: 해야 할 작업을 모아둠2⃣ To Do: 현재 진행할 작업3⃣ In Progress: 진행 중인 작업4⃣ Review/Test: 리뷰나 테스트가 필요한 작업5⃣ Done: 완료된 작업섹션 5. 테스트는 []다.테스트 코드는 문서다.프로덕션 기능을 설명해주는 것이 테스트 코드 문서다.다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완할 수 있다.고민했던 내용(테스트 코드)을 팀 자산(소스 코드)으로 공유할 수 있다.@DisplayName - 도메인 정책, 용어를 사용한 명확한 문장메서드명만으로 어떤 것을 검증하고자 하는 의도 파악이 어려움Junit5에 추가한 어노테이션이다.문장 형태로 섬세하게 테스트 검증에 대한 내용을 어노테이션안에 작성한다.섬세한 DisplayName특정 시간 이전에 주문을 생성하면 실패한다. ❌영업 시작 시간 이전에는 주문을 생성할 수 없다. ✅도메인 용어를 사용하여 추상화된 내용을 담기 -> 메서드 자체의 관점 보다 도메인 정책 관점 (특정 시간 -> 영업 시작 시간 ✅)테스트의 현상을 중점으로 기술하지 말 것 (~실패한다 ❌)Given / When / Then - 주어진 환경, 행동, 상태 변화Given : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증TDD vs BDDBDDTDD에서 파생된 개발 방법시나리오 기반한 테스트 케이스 자체에 집중하여 테스트한다.Junit vs SpockSpock은 Groovy언어로 BDD 패턴을 적용해서 테스트 코드를 작성할 수 있다.언어가 사고를 제한한다.명확하지 못한 테스트 코드는 사고를 제한할 수 있다.문서로서의 테스트를 신경 쓸 필요가 있다. 🏃 돌아보며..미션과 발자국을 정신없이 진행하다 보니 벌써 남은 인프런 워밍업 클럽도 2주밖에 남지 않았다.처음에 OT 라이브 당시 러너가 120명 정도였는데, 이번 중간 점검 라이브 때는 60명 정도로 줄어들었다.강의 내용 자체는 어렵지 않지만.. 2개의 강의(14시간 + 12시간)를 한 달만에 들으면서 미션과 발자국을 진행하는 건 쉽지 않아 보인다..나는 미리 강의를 수강해서 다행이다라는 생각이 든다.. 😅하지만, 쉽지 않은 만큼 성실히 참여한다면 단기간 내 성장하는 데 큰 도움이 될 것이다.그리고 이번 주에 코드리뷰를 신청하기 잘했다는 생각이 들었다. 🍀위에서도 여러 번 언급했지만 우빈님의 세심한 리뷰 탓(?)에내가 미션을 수행하는 데 있어 우빈님보다 세심하게 집착 했었나..? 반성하게 된다... 😭마지막 주차 온라인 라이브에서도 테스트 코드에 대한 코드리뷰가 진행된다고 한다.기회가 된다면 또 한 번 코드리뷰를 받아 단골 손님이 되고 싶다. 😂2주를 걸쳐, 읽기 좋은 코드의 스터디 과정은 이번주로 막을 내렸다.다음 주차부터는 테스트 코드에 대해 본격적으로 스터디하는 과정이 진행된다.남은 2주도 화이팅하며 좋은 성장을 이루길 기대해 본다. 🔥끝으로, 3월 중순이 되니 이제 슬슬 봄 내음이 나는 것 같다.. 🌸얼어붙은 개발 시장에도 봄이 찾아왔으면 좋겠다.. 🧊발자국 2주차 끄읕 ![출처]인프런 워밍업 클럽 : https://www.inflearn.com/course/offline/warmup-club-3-be-code강의 : https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

백엔드인프런워밍업클럽백엔드3기발자국박우빈클린코드읽기좋은코드

10 14일 전
10

살짝 서두른 Spring 기반 테스트 코드 - 인프런 워밍업 클럽 3기 백엔드 코드✨

인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차🎼 알레그레토 : 조금 빠르게 3주 차부터는 미션과 발자국을 조금 서둘러 진행할 예정이다.앞서 언급했듯이, 이번 주부터 항해 플러스가 시작되어 최대한 진도를 빠르게 빼보려고 한다.현재 날짜(3월 17일) 기준으로 Day 16 미션과 3주 차 발자국을 작성 중이며,이번 주 안에 Day 18 미션과 4주 차 발자국도 작성을 완료하는 것이 목표이다.(가능할지는 모르겠지만 ㅎㅎ)조금 급하게 진행하는 감이 있어 개인적으로 많이 아쉽다.워밍업 클럽에만 온전히 집중할 수 있었다면 더 많은 성장을 할 수 있었을 텐데 말이다...하지만, 후회는 하지 않는다. 다음 기수의 존재는 우빈님만 아시겠지만, 다음에 또 없을 수도 있는 기회를 놓치고 싶지 않았기 때문이다. 👍나는 위의 이미지처럼 Trello를 활용해 인프런 워밍업 클럽에 참여하고 있다.미션 제출 날짜가 일정하지 않다 보니, 제출 하루 전에 노티를 받도록 설정해 두고 유용하게 사용 중이다. 🙃💡 자기만의 언어로 키워드 정리하기 섹션 6. Spring & JPA 기반 테스트Layered Architecture레이어드 아키텍처의 단점 : 기술에 대한 강결합이 심하다는 단점이 존재Hexagonal Architecture도메인 모델은 외부의 것들을 아예 모른다.도메인 모델 중심 (멀티 모듈 및 시스템이 커진다면..)단위테스트 vs. 통합테스트단위테스트 만으로는 커버하기 어려운 영역이 존재 (여러 모듈 및 여러 객체가 협력하기 때문에)통합테스트란?여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다.IoC, DI, AOPORM, 패러다임의 불일치, HibernateSpring Data JPAQueryDSL@SpringBootTest vs @DataJpaTest@DataJpaTest는 @SpringBootTest보다 가볍다.@DataJpaTest보다는 @SpringBootTest를 더 선호@DataJpaTest는 @Transactional이 있어 롤백이 된다.@SpringBootTest는 클렌징을 해주어야 한다.@SpringBootTest vs @WebMvcTest@SpringBootTest는 E2E 테스트, 즉 통합테스트 시 사용하는 어노테이션이다.@WebMvcTest는 Presentation Layer에 대한 단독 테스트시 사용하는 어노테이션이다.다른 레이어들은 mocking을 통해 동작을 제어한다.@Transactional(readOnly = true)테스트에서 사용 시, 롤백 되는 것에 유의 해야 한다.트랜잭션 경계 설정을 해야한다.엔드포인트를 잘 설계해야 한다.Optimistic Lock, Pessimistic Lock낙관적 락 : 데이터 충돌이 자주 발생하지 않을 것이라 낙관적으로 가정하고, 트랜잭션을 진행하는 방식데이터를 읽을 때는 락을 걸지 않고, 데이터를 업데이트 시 버전 비교하여 충돌 여부 판단성능 저하를 최소화, 동시성을 높이는 데 유리비관적 락 : 데이터 충돌이 자주 발생할 것이라 비관적으로 가정하고, 트랜잭션이 데이터를 사용할 때 미리 잠금을 거는 방식데이터 일관성을 유지하는 데 초점트랜잭션이 완료될 때까지 다른 트랜잭션이 데이터를 수정할 수 없음데드락 발생 가능CQRS명령 조회 책임 분리 : Command Query Responsibility Segregation읽기(조회)와 쓰기(명령)의 책임을 분리하는 소프트웨어 아키텍처 패턴장점성능 최적화확장성 증가데이터 모델 최적화비지니스 로직의 명확한 분리단점복잡성 증가데이터 동기화 문제트랜잭션 관리 어려움@RestControllerAdvice, @ExceptionHandler@RestControllerAdvice : ControllerAdvice의 기능을 하는데 JSON으로 응답을 해주는 Advice커스텀 예외를 던지고 @RestControllerAdvice의 @ExceptionHandler에서 예외를 처리할 수 있다.Spring bean validation@NotNull, @NotEmpty, @NotBlank도메인 요구사항에서 나오는 validation과 책임 분리해야한다.Controller 단에서는 최소한의 validation을 통한 검증이 이루어져야 한다.@WebMvcTestObjectMapperJackson 라이브러리에서 제공하는 클래스로, Java 객체와 JSON 간의 변환을 담당하는 역할직렬화, 역직렬화를 수행Mock, Mockito, @MockBeanMock : 실제 객체 없이 동작을 모방하여 단위 테스트를 수행하는 가짜 객체Mockito : Java에서 Mock 객체를 쉽게 생성하고 관리할 수 있는 라이브러리@MockBean : Spring 컨텍스트에 Mock 객체를 등록하여 실제 빈을 대체@Mock : 순수한 자바에서 Spring 컨텍스트가 필요하지 않을 때 사용@MockBean : Spring 컨텍스트에서 특정 빈을 Mocking 하고 싶을 때 사용👨🏻‍💻 미션 회고[미션 Day 11][미션 PR]#6스터디 카페 이용권 선택 시스템 단위 테스트 작성미션 조건은 다음과 같다.✔각 프로젝트 모두 강의 중에 작성한 tobe 패키지 코드를 기준으로 함 (lesson 6-4 가 가장 마지막 버전)✔3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트 작성 ➡ 단, 같은 인터페이스를 구현하고 있는 구현체들은 1개 클래스로 간주한다.✔무엇을 테스트하고자 했는지를 잘 나타낸 @DisplayName 작성하기✔BDD(given/when/then) 스타일 따르기 (주석으로 표기)가장 작은 단위의 메서드 부터 단위 테스트를 작성하면서,테스트 커버리지를 높이기 위해, 모든 주요 로직에 대한 단위테스트를 작성하고자 했다.단위테스트를 작성하면서 강의에서도 강조한 내용 중에 하나인 @DisplayName을 잘 작성하기 위해 많이 고민했다.'권'이라는 Pass의 의미를 '패스'로 통일하여 일관성있게 작성하였다.읽는 사람으로 하여금 뇌 메모리를 적게 쓰게 하기 위해 @DisplayName도 최대한 추상화해서 작성하려고 노력했다.테스트의 현상을 중점적으로 작성하지 않으려고 하였다.하지만, 사용자 입력을 받는 클래스인 InputHandler의 단위테스트를 작성하는 과정에서 어려움이 있었다.InputHandler 내부에서 static으로 선언되어 있어 mocking도 하기 어려웠다.public class InputHandler { private static final Scanner SCANNER = new Scanner(System.in); }프로덕션 코드를 수정하지 않는다는 요구사항을 지키면서는 테스트가 어려웠다. 만약 프로덕션 코드를 수정한다면 테스트가 가능해질 것이다.👆 Scanner를 외부세계로 분리하면 테스트가 가능할 것 같다.✌ InputHandler 상위의 인터페이스를 생성 후, 테스트 전용 support 성격의 구현체를 만들어서 테스트가 가능할 것 같다. 미션 제출 후, 차후에 프로덕션 코드를 수정해서 테스트를 작성 해볼 예정이다.🏃 돌아보며..Day 11 미션은 제출이 끝이 아니라, 중간점검 라이브에 이어 마지막 라이브에서 코드 리뷰 단계가 남아있다.중간 점검 때 세심한 코드 리뷰를 받고 수정하면서 많은 성장을 했기에 이번에도 신청하지 않을 이유가 없었다.다른 러너 분들도 꼭 받아봤으면 한다. 😊아직까지 혼자 신청해서 뻘줌해서가 아니라... 진짜 좋은 기회이자 경험이다... 🤣코드 리뷰 대상자로 뽑히길 기대하며.. 마지막 4주차도 화이팅 💪[출처]인프런 워밍업 클럽 : https://www.inflearn.com/course/offline/warmup-club-3-be-code강의 : https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95/dashboard

백엔드인프런워밍업클럽백엔드3기발자국박우빈테스트코드실용적인테스트가이드

10 8일 전
정예은

[워밍업클럽3기] 백엔드 코드 - 박우빈 발자국 2주차

강의 수강 노션 링크https://www.notion.so/DAY06-1b2010f075ca80d09e08d4dd35376dd5?pvs=4https://www.notion.so/DAY07-1b3010f075ca80b4945cd230929481ff?pvs=4https://www.notion.so/DAY09-1b4010f075ca805f8e1ec01a7c00b16c?pvs=4https://www.notion.so/DAY10-1b4010f075ca80a7a731da0aa16b8e43?pvs=4출처[워밍업클럽 리더블코드 ][워밍업클럽 테스트코드] 👣발자국2주차👣🏫배운 내용 🏫주석의 양면성자주 변하는 정보는 최대한 주석 사용 지양하기우리가 가진 모든 표현방법을 총 동원해서 → 코드에 녹여 → 주석 사용 지지뢰찾기 리팩토링게임의 상태를 주석으로 설정하는 대신 → ENUM으로 관리그래서 외부에서 호출해서 사용하기무한루프 반복 구조는 위험특정 상황에서만 반복문 돌도록 바꿔줘야한다.지뢰찾기 같은 케이스에서는 게임이 “진행중” 일때만 반복문 돌 수 있도록 처리하기변수와 메서드의 나열 순서💡상태변경 메서드 >> 판별 메서드 >> 조회 메서드 순으로 나열 하자이때, 메서드 우선순위는 공개 메서드에서 private메서드 순으로 내려와야한다.  자동테스트?그동안 내가 학원에서 배워온 건, 수동테스트 였나보다스프링부트에서 애너테이션을 활용하여 수동사냥만 해왔던 것..인가?그리고 단위로 /unit으로 쪼개서 (메소드,클래스별로) 테스트를 진행 ⇒ 단위테스트그러다보니 검증속도도 빠르고 안정적임  JUnit이란?단위 테스트를 위한 프레임워크 → 퀜트백 프레임워크풍부한API제공해주는 프레임워크로 테스트 코드 작성해보자   JUnit vs assertJ 두개의 차이점AssertJ 의 장점자연어 가까워 가독성이 좋다체이닝 방식이 가능함JUni5의 단점assertEquals는 단순히 "Expected: A, Actual: B" 결과물만 추출함기능이 단순하고 제한적  assertJ 다양한 메서드기본적인 검증 isEqualTo() : 두 값이 같은지 비교 isNull() : 값이 null인지 비교 isTrue() : 값이 true인지 비교컬렉션 검증 hasSize() : 컬렉션의 크기 비교 —> 리스트의 사이즈 찾기 contains() : 컬렉션에 특정 요소가 포함되어 있는지 확인 isEmpty() : 컬렉션이 비어 있는지 확인 🎶경계값 조건 * 정수가 3이상일때, A라는 조건 만족해야함.해피케이스경계값 활용하기 !즉, 3에대한 테스트를 짜보자.5에 대해는 만족하지만, 3이 만족이 안될 수 있잖아 !예외 케이스2로 조건값보다 더 아래쪽 범위로 테스트 하기❗인사이트❗칭찬깃 사용법에 대해 좀더 연구하고, 프로젝트에 적용하며 강의를 따라가려고 노력하였다.깃에 대해 전혀 몰랐던 사람으로,,, 개발 공부하기 위해선 깃 활용이 무척이나 중요하다는 걸 깨달았다.코드리팩토링시 강의를 보며 , 강사님이 로직 처리를 하는 한단계 한단계씩 끊어서 정리하였다.테스트코드 진행시, 테스트하기 어려운 부분을(요구사항에 맞게 테스트 로직을 짰는데, 그 요구사항이 개발하는 시점의 요구사항이랑 충돌이 될때 )잘 이해하고 숙지하며 이 로직에 대해선 따로 분리하여 테스트 코드 관리하는 시야가 필요하다. 아쉬움그러나, 동영상 일시정지를 하고 노션에 정리한다고 한들, 온전히 내것이 되는가? 아쉬움이 남아있다.이상태로 다시 한번 해보세요~ 주어지면 , 아무것도 못한다.내 스스로 코드를 짜보는 학습이 필요할 것 같다.이번 미션11 코드 제출도 어디서 어떻게 시작 해야 할지 막막하다 앞으로 어떻게?지금도 지뢰찾기 코드 마스터 하지도 않고, 미션 제출도 선생님 코드 따라치기만 했었다. 지뢰찾기 로직을 파악하기엔 내 머리가 아직 준비가 안되었고, 내 마음의 여유가 준비되지 않은 상태였다. 현재 국비학원 졸업작품으로 팀프로젝트를 지난주에 시작 하다보니, 우선순위는 팀포폴이다. 그래서 시간을 내어 지뢰찾기 코드를 마스터 하기에는 조금은 어려울 듯 하여 ,,,, 팀포폴이 어느정도 마무리가 되어가면 그때 지뢰찾기 자바 코드 눈에 익히고 리팩토링 수업을 다시 들으며 공부를 해야 할 것 같다는 생각이 든다.   🧑🏻‍💻두번째 중간점검 나의 코드를 다른분 코드와 비교해보자 !미션4 미션 공통 피드백static정적 메소드는 빼자 ( 인텔리제이 단축키 사용한 사람 적.발) 풀스택 취업 준비백엔드의 매력은 ?눈에 예쁘게 보이는거 좋아하는데 → 프론트 개발자도 고민 → 프론트 앤드를 어느정도 잘 할줄 아는 백앤드 개발자가 되기로 함성향상 잘 맞을 것 같았다. 복잡한 방식을 여러 방법으로 접근 할 수 있는게 성향이 잘 맞았다. 따라치기만 하는 지금 상황어려움 보다는 익숙함의 문제이다 . 어려움건 10%일뿐 .익숙하지 않아서 거부감이 드는 것 일뿐,진짜 어려운건 아님 . 석박사 해야지 알수있는 정도는 아님메타인지 및 의도적으로 수련하는 것이 가장 빠르고 명확하다 💡될때까지 반복해라.💡계층구조 패키지 나누기 기준이란?도메인중심 ( 유저, 히스토리, 오더 )도메인별로 관심사가 명확해짐프로젝트가 커서, 도메인별로 떨어져야한다 → 아주 좋아유저가 회원이라는 도메인이 정말 중요해서 떼어야함 → 아주 유리 하다컨트롤러,서비스 계층들이 각각 저 도메인별로 나누어져 있다보니, 패턴이 달라질 수 있음공통기능이 멀리 떨어져 있으니, 공통기능이 중복으로 생성 될 우려가 있음 레이어 중심 ( 컨트롤러,서비스,모델)한눈에 레이어러 보기 좋아.도메인간 결합도가 증가해서 MSA전환이 불리하다 💡작은 프로젝트이면 레이어중심이 좋다💡큰 프로젝트는 도메인 중심으로 잡자. 개발 면접a 먼저 개발 지식 질문 CS기초, 스프링등b 이력서 기반 질문인성 질문 (개발에 대한 태도 )질문의 빈도는 A>B>C그러나 C가 별로이면 무조건 탈락취업준비에 대해회사가 원하는 기술들 JD가 무엇인지 공통적으로 찾고 있는 기술스택이 무엇인지 찾아보기그리고, 그 회사만이 찾고있는 기술,팀 도메인이 무엇인지 챙겨보기예상질문리스트 검색해서 → Interview Question Driven 취준 하기

백엔드박우빈워밍업클럽클린코드백엔드발자국

정예은

[워밍업클럽3기] 클린코드-박우빈 발자국 1주차

학습내용섹션1~4📝미래의 나를 위해, 미래의 자손을 위해이름 짓기는 깔쌈하게 ! 중요키워드만 뽑아서 !중요한 정보만 남기는 추상화 잘 하기 !메서드 생성클린코드를 위해 각 로직별로 추상화를 하여 메서드로 만들어주자!✅“한가지 역할” 을 하는 코드 블럭을 찾고, 메서드로 분리✅그에 맞는 메서드 “이름 “ 지어주기✅<aside> 💡⭐메서드 생성 단축키 =ctrl+alt+m</aside>   학습정리 링크https://www.notion.so/DAY02-1ab010f075ca81ed8b20fd23dead0c76?pvs=4https://www.notion.so/DAY-04-SOLID-1-1ab010f075ca81abbcd8c909d84e74ce?pvs=4👣회고👣이번 주는 SOLID 원칙을 중심으로 코드 리팩토링을 진행하며, 보다 견고하고 유지보수하기 쉬운 구조를 고민하는 시간을 보냈다. 💡 잘한 점✅ 메서드 추출을 통해 가독성을 높이고 코드의 역할을 명확히 함✅ 기존 코드를 무조건 변경하기보다는, 확장 가능성을 고려하면서 구조를 잡아나감✅ 인터페이스와 추상 클래스의 활용을 고민하며 유연한 설계를 연습함 ⚠ 아쉬운 점아직은 강사님이 따라하는 대로 코드를 있는 그대로 따라치기만 하는 과정으로 수업을 들었음하나하나씩 로직과 메서드들을 분석해가며 수업을 들으려니, 30분 수업은 나에게 60분이되어 돌아왔음그만큼 시간을 오래 잡아먹기 때문에 진도 맞추기가 너무 어려웠다..내가 이 로드맵을 참여한게 올바른 선택이긴 할까? 라는 고민도 많이 들었지만, 일단 코드 100번정도 따라쳐보면 대충 흐름이 파악되지 않을까? 생각하며 수업을 듣고 노션에 정리하던 한주였다....  🎯 다음 주 목표단순히 원칙을 따르는 것이 아니라, 상황에 맞는 적용법을 체득하기미션을 해결할 때, "왜 이렇게 설계했는가"를 먼저 고민하고 코드를 작성하는 습관 들이기   📢미션📢Day02추상 : 눈사람을 만든다 구체 :대기중에 떠다니는 먼지가 핵이 되어, 이 핵을 중심으로 수증기가 응결해가며 형성되는 결정체의 집합체를 손으로 뭉친다2덩이로 둥글게 뭉쳐서 몸통과 머리로 붙여준다주변에 굴러다니는 , 자연에서 산출되는, 생물이 아닌 단단한 고체 물질을 눈과 코에 붙여준다  Day04SOLID원칙단일책임원칙클래스는 하나의 책임만 가져야 한다.책임을 인지하고 분리하고 다른 클래스 만들기.메인 도입부에 게임 실행부 넣지 않고 → 지뢰찾는 로직을 담은 클래스를 하나 생성해서 하나의 책임만 갖도록 Minesweeper 개방 폐쇄 원칙기존 코드를 많이 변경하지 않고 확장할 수 있도록 설계하기 추후 유지보수나 조건들이 추가로 생겨날때 당황하지 않도록 너무 상수로만 값이나 데이터 정의 내리지 않기리스코브 치환 원칙자식은 부모를 대체해서 일할 수 있고, 부모는 자식을 대체할 수 없다. 부모 클래스를 사용하는 곳에 자식 클래스를 넣어도 문제가 없어야 함인터페이스 분리 원칙하나의 커다란 인터페이스 사용하는게 아니라, 여러개의 인터페이스로 분리하기 하나의 인터페이스에는 하나의 메서드만 , 관련된 메서드만 넣어야함의존성 역전 원칙구체적인 구현 클래스가 아니라, 인터페이스나 추상 클래스에 의존 하도록 설계  public boolean validateOrder(Order order) { if (isInvalidOrder(order)) { return false; } return true; } private boolean isInvalidOrder(Order order) { if (order.doesNotHaveAnyItem()) { log.info("주문 항목이 없습니다."); return true; } if (order.doesNotHaveCustomerInfo()) { log.info("사용자 정보가 없습니다."); return true; } if (order.hasNegativeTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return true; } return false; } 👣회고👣미션을 해결하면서 "추상화"의 중요성을 몸소 체감한 한 주였음특히, 눈사람 만들기 예제를 통해 구체적인 행동을 추상화하는 연습을 했고, 이를 코드에도 적용하려 노력했다. 

백엔드워밍업클럽워밍업클럽3기박우빈백엔드백엔드스터디지뢰찾기클린코드리팩토링

워밍업 클럽 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에 합치는 방식이 적절하지 않다고 생각합니다. 끝났다.테스트 코드에 지레 겁을 먹지않게 해준 시간이었다.우리 모두 파이팅

백엔드백엔드워밍업클럽테스트박우빈발자국

워밍업 클럽 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 짓는 것이 생각보다 되게 까다로웠다.테스트는 쉽지않다. 하지만 올바르지 못한 테스트는 오히려 혼란을 일으킬뿐이라는 것은 알게되었다.제대로 된 테스트를 작성하도록 노력하자.모킹 테스트를 왜 해야는지는 아직 명확하게 와닿진 않는다.알 때 까지 복습하자.

백엔드워밍업클럽테스트발자국박우빈

워밍업 클럽 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차 리팩토링을 진행해봐야겠다.중간점검역시 스터디를 하길 잘했다는 생각이 든다.다른 사람들이 리팩토링 한 코드를 보면서 배울점이 많았고,나는 우물안 개구리임을 깨달았다.더 열심히 해야겠다 파이팅

백엔드워밍업클럽클린코드테스트코드박우빈발자국

하얀종이개발자

워밍업 클럽 3기 클린코드 스터디 - 1주차 발자국

클린코드 스터디 - 발자국을 통해서 1주동안의 강의와 미션을 통해 무엇을 배웠고, 어떻게 느꼈는지 작성합니다. 섹션1~5 배운 내용섹션 2. 데이터를 추상화추상과 구체이름짓기, 메서드와 추상화, 메서드 선언부, 추상화 레벨, 매직넘버와 스트링 섹션 3. 코드를 추상화인지적 경제성메서드를 리팩토링 하는 방법Early return공백 라인, 부정어예외처리stream API와 Optional() 섹션 4. 객체지향 페러다임객체지향의 특성SOLID 원칙 섹션5. 객체 지향 적용하기상속과 조합Value Object 일급 컬렉션Enum의 특성과 활용 다형성 활용숨겨져 있는 도메인 개념 도출하기  발자국 1주차 회고 💡 배운 점기존에 어설프게 객체지향 코드를 작성했던 부분을 왜 이렇게 작성해야 하는지어떻게하면 더 잘 읽힐 수 있는지를 많이 배운 것 같아요.특히, 인지적 경제성: 최소한의 인지만 가져가서 최대한의 효율을 내보자!! 라는 말을 많이 깨닫게 된 것 같습니다. 🤷‍♂ 잘한 점읽기 좋은 코드를 이해한 상태로 내가 바꿔보고 강의를 보면서 나와 다른점이 뭔지 보면서 많이 느꼈어요.굉장히 재밌어서 그런지, 매일 꾸준히 공부한 부분도 좋았습니다.이제는 코드를 칠 때 한번 더 생각하게 되는거 같아요. 어떻게 작성하면 다른사람이 편하게 읽을 수 있을까를요.₩⚠ 아쉬운 점영어가 약해서 메서드명 짓기가 너무 서투르다는 것을 느꼈어요.다른 사람들이 메서드명을 어떻게 짓는지 많이 보면서 더 읽기 좋은 메서드명을 찾아봐야 할 것 같아요. 🎯 다음 주 목표따로 정리한 내용 한번씩 읽어보기코드를 따라 치는게 아니라 내가 바꿔보고, 강의를 통해서 바뀐 부분 비교하기단순히 원칙만 지키는게 아니라, 더 읽기 좋은 코드를 작성하기 위해 깊게 고민하기   한마디아직 많이 부족하구나 라는 것도 느꼈지만, 더 좋은 코드를 작성할 수 있게 될 거라는 생각에 너무 즐거워요. 🙃다들 다음주도 화이팅입니다.  

백엔드워밍업클럽클린코드박우빈ReadableCode

워밍업 클럽 3기 BE 클린 코드 - 1주차 발자국

⭐️ 박우빈님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 수강하며 작성한 내용입니다.  강의 수강명확한 이름 짓기추상화 레벨 맞추기빨리 리턴하기추상화를 통해 중첩문 줄이기공백의 의미부정어보다 긍정어/바로 읽히도록예외 가능성 낮추기Setter 금지, Getter는 생각해보고 쓰기필드 수는 적게 사용하기도메인 지식은 만드는 것이 아니라 발견하는 것 미션Day2카페에서 커피를 마시는 것을 구체 레벨로 표현하기 카페에 도착한다.원하는 음료를 키오스크 혹은 직원에게 주문한다.‘나’는 자리에 앉는다.직원이 음료를 제조한다.제조한 음료를 ‘나’에게 전달한다.전달받은 음료를 가지고 자리에 돌아온다.음료를 마신다.Day4https://night-geography-507.notion.site/3-BE-Day-4-1af205648a1680e78f9ade8e629bdc6a  회고미리 듣는 건 좋았지만 하루 이틀 미뤄지다보니 온전히 집중해서 수강하지 못한 점이 아쉽다.미션을 통해 추상화를 직접 적용해보고 SOLID에 대해서도 내 생각을 정리해 볼 수 있어서 흥미로웠다.목표다음주는 꼭 주어진 챕터를 그날그날 듣고 끝내는게 목표!미리 하겠다는 욕심에 더 미루지 말자무조건 따라하기 보다 왜 이렇게 했는지 생각해보기좋은 건 실무에 바로 적용해서 내 걸로 만들기

백엔드워밍업클럽워밍업클럽3기박우빈백엔드백엔드스터디

whdudwo1143

인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국

섹션2. 추상추상화란 구체적인 요소들을 감추고 중요한 특징만을 표현하는 것이다. 특정한 측면 외 나머지는 버린다는 점이 인상적이었습니다.또한 추상화에도 각각의 레벨이 있다는 생각을 해본적이 없었는데, 동일한 내부 세계에서는 동일한 추상화 레벨을 가진다는 내용이 인상적이었습니다.섹션3. 논리, 사고의 흐름하나의 문단에는 하나의 의미를 가지고있고, 하나의 메서드에는 하나의 기능만 가져야한다는 내용이 인상적이었습니다.Early Return불필요한 else를 제거하고, 조건에 맞으면 즉시 return하여 흐름을 명확하게 유지하는 방법을 배웠습니다.사고의 깊이(depth) 줄이기중첩된 분기문과 반복문의 깊이를 줄이고, stream을 활용하여 선형적으로 코드를 읽을 수 있도록 개선하는 방법을 익혔습다.부정어 최소화부정 연산자(!) 사용을 줄이고, 긍정적이고 직관적인 표현으로 메서드 이름을 짓는 것이 가독성 향상에 도움이 됨을 깨달았습니다.미션 해결 과정Day 2 미션"추상과 구체 사이의 적절한 균형을 찾는 것이 중요하다."코드가 지나치게 구체적이면 가독성이 떨어지고, 반대로 너무 추상적이면 실제 동작이 모호해진다는 점을 고민할 수 있어서 유익했습니다.Day 4 미션리팩토링 미션을 진행하며, 중첩된 for문과 if문을 Early Return 방식으로 변환하여 코드를 더 읽기 쉽고 직관적으로 만들었습니다. 또한, 가독성을 고려한 메서드 명명법을 고민하면서 개선해 나갔습니다.또한, SOLID 원칙을 나만의 방식으로 정리하는 미션을 수행하면서 단순 암기가 아니라 스스로 이해하며 정리하는 과정을 거쳤다. 여러 자료를 찾아보면서 개념을 내 언어로 표현한 것이 큰 도움이 되었다.회고추상과 구체 사이의 적절한 지점을 찾는 것이 중요느것을 깨닫게 되었습니다. 제가 가지고 있는 개발하며 안좋은 습관중 하나는 한 번에 모든 것을 처리하려다 보니, 하나의 메서드에 여러 기능을 넣는다는점이었습니다. 이번 수업을 통해 이를 반성하고 채움보다 비움을 통해 더 명료하게 개발하는 것의 중요성을 실감할 수 있었다.실습과 미션을 통해 이를 직접 경험하며, Early Return과 중첩 최소화 등 작은 코드 개선만으로도 가독성이 크게 향상될 수 있음을 체감할 수 있었다.스스로 칭찬하고 싶은 점미션을 통해 배운 내용을 즉시 적용하여 리팩토링하면서 개념을 내 것으로 만들려고 노력한 점추상화 레벨을 생각하며 개발하려 노력한 점하나의 메서드는 하나의 기능만 하도록 개발하려 노력한 점아쉬웠던 점 & 보완할 점변수명과 메서드명을 더 명확하고 직관적으로 짓는 능력이 부족하다고 느낌추상화 레벨을 동일하게 맞추는 방법을 더 깊이 있게 연습할 필요성을 느낌다음 주 학습 목표개인 프로젝트를하며 배운내용을 실전에 사용하는것을 목표로 삼고있다.

백엔드워밍업클럽백엔드클린코드3기박우빈1주차발자국발자국

워밍업 클럽 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주차발자국

도호

[인프런워밍업클럽2기] 1주차 발자국

본 회고는 인프런 박우빈 지식공유자님의 Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 듣고 작성된 내용입니다.총평 예상했던 것 보다 하루에 들어야하는 강의 분량이 많아서 힘들었다. 하지만 실무를 하면서 고민했던 부분들이 조금이나마 해결이 된 것에 보람을 느꼈다.그리고 영어 공부를 할 필요성을 느꼈다. Readable하기 위해서 중요한 것은 네이밍이라고 생각한다. 아무리 열심히 추상화를 하고 결합도를 낮춰도 변수와 메서드 이름이 의도를 담지 못한다면 의미가 없는 것 이라고 생각한다.이 생각을 한 이유는 강의를 보면서 지식 공유자님의 네이밍에 무릎을 탁 쳤기 때문이다. 나였다면 어떻게 했을까를 생각해보니 너무 초라했다. KPT 회고 Keep강의를 들으면서 나에게 필요했던, 내가 고민 했던 부분들을 위주로 정리해 나갔다.코드를 따라가면서 나라면 어떻게 수정 했을 지 고민하고 비교해보았다.Problem미션을 위한 정도까지만 강의를 듣고 DAY 5의 분량을 수강하지 못하였다. 한번 미루니 분량이 걷잡을 수 없이 늘어나버렸다. Try강의를 미리 들어놔야겠다.학습 내용 요약 읽기 좋은, 가독성이 좋은 코드를 짜야하는 이유는 무엇일까? 나는 코드를 읽을 때 생각없이 읽혀야한다고 생각한다. 문제 해결을 위해서 혹은 기능 수정을 위해서 기존 코드를 읽을 때 코드를 잘못 이해하게 된다면 잘 동작하던 부분에도 문제가 생길 수 있다. 그리고 우리의 개발 기간이 배로 늘어날 것이다. 어찌되었던 개발자는 회사의 제품을 잘 만들어서 그로 인해 회사에 수익을 가져다줘야하는 역할을 맡고 있기 때문이다. 개발 기간이 줄어들면 그만큼 투입되는 인건비가 줄어드는 것 일 테니까 말이다. 읽기 좋은 코드를 짜기 위해서는 아래 항목들에 신경을 써야한다.네이밍추상화Depth 그리고 공백 즉 비즈니스가 담고있는 내용을 쉽게 읽히게 해야한다.객체 지향 코드가 무조건 가독성이 좋고 어느 상황에서든 적용되어야하는 패러다임은 아니다. 상황에 따라서 절차 지향 코드가 더 적절할 때도 있을테니까.그렇지만 알고서 안쓰는거랑 모르고 안쓰는 것, 잘못알고 잘못 쓰는 것은 다르다. 강의에서는 객체 지향 패러다임에 대해서 여러 세션을 할당해서 설명하고 있다.흔히들 객체지향에서 이야기하는 SOLID, 캡추상다 같은 특징들은 구글 검색만 해도 나오니까 넘어가도록 하겠다.미션 회고이번 주차의 미션은 실생활의 행동을 추상화, 코드를 리팩토링하고 SOLID를 정의하기 였다.정의와 추상화는 어렵지 않았지만 코드 리팩토링은 약간의 고민이 필요했다.이유는 유효성 검증을 ealry return할 때, Order의 사용자가 유효한지를 Order가 검사하는 것이 맞을까? 라는 고민을 했었다.나의 결론은 Order도 User의 정보를 갖고있기 때문에 Order 레벨에서 User에 대한 검사정도는 필요하다고 판단했다. User가 null일 수도 있으니까 말이다.다음 미션은 리팩토링해보기인데 정말 기대된다. 아자아자

인프런워밍업클럽2기readablecode박우빈

채널톡 아이콘