블로그

10

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

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🙊 시작이 반이다... 벌써 중간점검 ?인프런 워밍업 클럽이 시작한지 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 6개월 전
sein

[워밍업 클럽 4기 - 백엔드] 마지막 발자국

마지막 회고[Day 16] Presentation Layer(2)여섯 번째 섹션 중에서 제일 재밌게 학습한 날이 아닌가 싶다. 백엔드 개발자가 아니다보니 혼자 간단한 프로젝트를 만들어보고자.. 챗GPT를 스승삼아 개발했었는데 챗GPT가 항상 그렇듯 한 번에 완벽한 대답을 내주진 않는다. 챗 GPT가 HTTP 응답 메세지를 구조화하라고 알려줬지만, 정확한 답변은 주지 못한 상태에서 혼자 이것저것 찾아보다가 흉내만 내도록 구현했던 것 같은데 이번 챕터에서 ApiResponse를 마주하다니! 뜻밖의 이득이었다. 또한 Exception 처리도 @ControllerAdvice도 나름 혼자 사용해봤는데 만족할만한 결과물이 나오지 않았다. 사실 어떻게 하는지 개념이 잡히지가 않고, 눈으로 관련 코드를 본 적도 없어서 제대로 잘 적용할 수가 없었는데 이번 강의가 마치 나에게는 완벽한 정답처럼 다가왔다. 그래서 이번 강의는 엄청나게 눈을 반짝이면서 봤다.그 외에 제일 기억에 남았던 건 spring-validation이다. Reqeust Dto에서는 해당 필드의 타입이 유효한 값인가를 검증하게하고, 그 외에 도메인과 관련된 유효성 검사들은 좀 더 안쪽 내부에서 진행해야 된다, 즉 책임을 분리해야 한다는 지식이었다. [Day 17] Mock을 마주하는 자세Mock을 처음 접해봤는데 제대로 된 개념이 잡혀있지 않아 Mock과 Stub을 정확히 구분하고, 언제 써야하고, 어떻게 써야하는지 감이 잡히지 않았다. 조금 더 공부해봐야할 것 같은 주제다.그리고 Classicist와 Mockist 얘기를 듣고 나도 내 기준을 생각해 봤는데 Classicist가 맞는 것 같다고 결론을 내렸다. 완벽한 프로그램은 없는 것처럼 완벽한 Mocking도 없을 것이다. 그래서 Mocking 위주로 대부분의 테스트로 짠 프로그램이 진짜 객체를 마주했을때 과연 완벽하게 동작할 수 있을 것인가?를 생각해보면 아닐 확률이 더 크다고 생각한다.아무튼 Mock에 대한 건 좀 더 공부해보자. [Day 18] 더 나은 테스트를 작성하기 위한 구체적 조언되게 많은 주제들을 짤막하게 다루었다. 집중력이 그리 좋지 않은 나로서는 좋은 챕터였다. 그리고 꿀팁과 실무적인 관점을 배울 수 있었다. 이번 강의 전에 테스트 코드를 직접 짜보는 미션을 진행했었는데 직접 코드를 짰었을때 어려운 부분이나 '이게 맞나' 싶은 부분을 긁어주는 강의었다. 여러 개의 타입을 가진 Enum에 관련된 테스트를 진행했는데 한 가지 타입만 테스트하면 안 될 것 같아서.. 타입 검사하는 한 테스트에 모든 타입에 대한 given, when, then을 적었었는데 @ParameterizedTest라는 좋은,, 방법이 있어서 다음에는 고민만 하지말고 더 나은 대안을 찾을 수 있도록 구글에 한 번 검색해보자고 생각했다. 약간 부끄 [Day 19] 학습 테스트 | REST Docs테스트를 학습용으로 쓰다니! 내 입장에서는 놀라운 발상이었다. 앞으로 궁금한 라이브러리들은 마음놓고 테스트하면서 공부할 수 있게 됐다!REST Docs는 처음해봤다. 예전에 한 번 써본건 Swagger였는데 Swagger 쓰다가 REST Docs를 쓰니까 약간 어색하면서, UI가 뭔가 부족한거같으면서, 어렵다는 느낌을 받았다. 그래도 Swagger는 프로덕션 코드에 직접적으로 추가해줘야하는 코드들이 많았던 걸로 기억하는데 REST Docs는 테스트 코드 기반으로 만들어진다니 더 깔끔한 관리가 될 것 같다고 생각했따. [Day 20] 중간 점검마지막 중간 점검이다! 너무 아쉬워. 테스트 코드를 본격적으로 공부해본게 처음이라서 혹시나 내 질문이 이상한 질문일까봐, 너무 쉬울까봐 하지 말까했는데 내가 올리기 전까지 아무도 질문을 안올려서 나라도 일단 냅다 올리자 하고 올렸다. 용기내서 올린 질문은 나에게 100배의 좋은 지식으로 돌아왔다. 다음에는 더 용기가 생긴다면 코드 리뷰도 한 번 받아보고 싶다고 생각했다.다른 분들의 코드리뷰를 보면서 도대체 어떻게 저렇게 생각하고 저렇게 구현하지?라는 생각을 많이했다. 나도 우리 회사에서 나름 잘하는 사람 중에 하난데 여기 오니까 우물 안 개구리라 생각됐다. 더 많이 공부하고 나보다 더 잘난 사람들을 많이 접해야 내가 성장하는 것 같다.  마지막 회고워밍업 클럽은 되게 충동적으로 신청했다. 0기를 수료했었는데도 신청한 이유는 0기와는 전혀 다른 주제여서 뭔가싶어 쓰윽 봤는데 여태 한번도 배우지 않았던 주제지만 궁금은 했었던 주제여서 할인 코드도 주는 김에 신청했던 것 같다. 되게 가벼운 마음으로 신청했지만 무거운 뇌를 갖고 돌아가는 강의다. 추상화도 이렇게 본격적으로 코드에 녹여 본 것도 처음이고 테스트 코드도 작성은 해봤지만 그저 강의의 일부정도라 깊게 배우진 않았다. 게다가 내가 직접 내 머리로 생각해서 작성했던 건 아예 처음이었다. 처음인만큼 난이도가 초급이지만 나에게는 중급 정도의 난이도였으며, 솔직히 워밍업 클럽이 아니었다면 도중에 듣고 탈주했을 것만 같았다. 매일매일 힘내라고 말씀해주시면서 공지사항에 메세지를 보내주신 박우빈님 덕에 완주할 수 있었던 것 같다. 언젠가 백엔드로 직무 전환에 성공한다면 이번에 획득한 지식들을 가지고 실무에 적용해볼 수 있는 날이 왔으면 좋겠다. 그럼 우빈님한테 배웠다고 자랑해야지 ㅎㅎ다들 고생많으셨고 감사합니다!

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

sein

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

3주차 회고[Day 11] 미션Day 11일에는 테스트 코드를 직접 작성해보는 미션을 진행했다. Readable 강의에서 지뢰찾기와 스터디 카페에 대한 테스트 코드를 3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트를 작성하는 미션이었다. 객체지향으로 리팩토링 했었던 지난 미션보다는 시간이 적게 걸렸지만, 이번 미션도 여전히 생각할 거리가 너무 많았다.고민을 많이 했던 부분은 전체적으로 테스트를 작성할 수 있는 시간이 물리적으로 부족했기 때문에 선택과 집중을 해서 정말 테스트가 필요할만한 부분에 적용해보고 싶어 어떤 클래스를 선정할지 생각을 많이했다.또, 단위 테스트 위주로 할지 7개만 작성하니까 통합 테스트 격으로 할지도 많이 고민했는데 테스트를 작성하는 실력이 그리 뛰어나진 않아 처음부터 천천히 하기로해서 단위 테스트 위주로 짰던거 같다.이번 미션도 역시 재밌었다! [Day 12] Persistence Layer이번 챕터에서는 전체적인 레이어드 아키텍처 Presentation, Business, Persistence Layer들에 대한 간략한 설명과 통합 테스트의 정의, 그리고 앞으로 개발할 프로젝트의 설계에 대해 배웠다. 강의를 들으면서 느낀 건 강의가 정말 초보자 위주로 짜여져 있다는 것이다(positive). Library와 Framework의 차이점, spring의 대표적인 기술, ORM 등에 대한 설명들을 테스트 강의에서 들을 줄은 몰랐는데 초보자는 아무래도 반복학습이 중요하다보니 이런 기초적인 것들을 하나씩 짚어주셔서 개념을 다시 바로 한 번 더 잡기에 좋았다. 제일 기억에 남는 건 테스트를 작성할 때 클래스 레벨에 @SpringBootTest와 @DataJpaTest 어노테이션이다.SpringBootTest스프링 부트 컨텍스트를 로드한다.웹 환경, 서비스, 레포지토리, 컴포넌트 등 모든 Bean을 실제처럼 사용할 수 있다.DataJpaTestSpringBootTest에서 JPA 관련 컴포넌트만 로드 되게끔 경량화슬라이스 테스트로 빠르게 JPA 레이어만 검증할 때 사용된다.@Service나 @Controller등의 Bean이 로드되지 않는다. [Day 13-14] Business Layer이번 섹션에서는 비즈니스 로직이 메인인 Business Layer에 대해 알아보고, 코드를 구현했다.비즈니스 레이어는 비즈니스 로직을 구현하는 레이어고, Persistence와의 상호작용을 통해 비즈니스 로직을 전개시키며, 트랜잭션을 보장(원자성)해야 한다. 이번 코드 구현에서는 고객이 주문을 할 수 있는 Order, Stock의 전반적인 부분을 개발했다. 코드를 구현하면서 드는 의문점은 여태 다른 강의를 들을 때 상품의 재고 관련한 데이터는 Product에 필드로 관리했는데 이 강의에서는 별도의 엔티티로 따로 빼준게 의아했다. 혼자 생각해봤는데 Stock의 데이터는 최대 품절, 아니면 재고 현황(숫자)만 보여주면 될 것 같았기 때문이다. 그래서 질문을 냅다 올리려다 다른 사람이 비슷한 질문을 한게 있나 찾아봤는데 있어서 읽어봤더니 아하!하게 되었다.관련 질문 : stock을 entity로 분류하는 이유 [Day 15] Presentation Layer(1)이번 섹션에서는 마지막 레이어인 Presentation layer를 구현하고 테스트했다. Presentation Layer는 외부 세계의 요청을 가장 먼저 받아들이는 계층이며, 받은 파라미터에 대해 최소한의 검증을 수행해야한다. 추가적인 요구사항에 대한 테스트를 작성하고 개발을 하면서 @Transaction에 readOnly란 속성에 대해 배웠다.대부분의 서비스는 CRUD 중에 읽기인 'R' 비중이 매우 높다. 그래서 Read와 CUD(command) DB를 슬레이브(R) / 마스터(CUD)로 분리해서 장애를 격리하는 방식을 추천한다고 하셨다.코드 상에서는 일단 클래스 레벨에 무조건 @Transactional을 주는게 아니라 전체적으로 readOnly = true 속성을 먹게끔 작성하고, CUD 작업이 있는 메서드에만 따로 @Transactional을 붙여주는게 성능 상으로도 더 좋다고 말씀해주셨다.@Transactional(readOnly = true) public class service { @Transaction public void save {} } 3주차 회고초보자 입장에서는 TDD로 테스트부터 개발까지 모든 과정을 함께 참여할 수 있어서 나에게 더 좋은 경험으로 다가온 강의였다. 시간은 좀 길었지만 그만큼 얻는 것도 많아 아주 유익했다. 다음에는 강의 없이 혼자서 간단한 프로젝트를 TDD 기반으로 만들어보고싶다! 이번주도 열공😄 출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드

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

sein

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

2주차 회고[Day 6] 리팩토링: 코드 다듬기이번 챕터에서 제일 기억에 남는 건 '주석'이었다. 주석 사용 법은 배웠지만 좋은 주석은 처음 알게된 개념이라 도움이 많이 됐다. 실무에서도 개발하기 급급해서 기존에 있던 주석까지 신경쓰지 않았었는데 주석과 코드는 한 몸이라 생각하고 앞으로 꼼꼼히 봐야겠다고 생각했다.그리고 키워드 정리 시간에 지뢰찾기에 앞으로 추가해볼 수 있는 기능들을 몇몇개 말해주셨는데 내 힘으로 기능들을 추가해보는 시간을 가져도 좋을 것 같다. [Day 7] 클린코드 리팩토링 실습(미션)이번 챕터는 온전히 미션을 위한 날이었다. studycafe 패키지에 작성된 코드를 읽어보고, 추상화 해보는 미션을 진행했다.고작 조각 코드인 몇 줄 짜리가 아니라 더 어렵긴 했지만 더 생각할 수 있었던 것들이 많아서 좋았다.코드를 다 읽어보고 난 후에 여태 배운 추상화 기법들이 머릿속을 스쳐지나갔다. 이 부분에서 뭘 하면 좋을 것 같은데?! 정도의 생각이 들었다. 다만, 구현으로 실천하기가 매우 어려웠다. 생각과 구현 능력이 절실히 필요하다고 생각했다. 시간 상의 문제로 완벽히 추상화를 하진 못했지만 그래도 백지에서 추상화를 진행해본적이 처음이라 어렵지만 재밌었던 시간이었다. 추후 중간 점검 때 코드 리뷰에서 정말 많은 것을 얻었다. 각자의 실력도 다르지만 각자의 생각도 다르고, 관점도 달랐다. 그래서 배운 게 두 배, 아니 세 배 더 많아진 것 같다! [Day 8] 리팩토링 연습 | 기억하면 좋은 조언들studyCafe를 처음부터 끝까지 우빈님과 함께 진행했다. 순전히 내 뇌로는 닿을 수 없는 부분까지 닿을 수 있었던 거 같았다. 이래서 '나를 이끌어 줄 상사가 중요하구나'를 느꼈다.추상화도 어렵지만 오버 리팩토링이 너무 어려운 것 같다. 오버 리팩토링이 되는 시점을 정확히 캐치할 수 없다보니 그냥 둥둥 뇌 속에 떠도는 구름 같은 개념을 보는 것 같다. 우빈님이 말씀하신대로 오버 리팩토링은 경험에 의해서만 얻을 수 있다고하니 추상화를 많이 도전해보는 것이 중요하다고 느꼈다.실무에서도 추상화를 많이 접할 수 있으면 더 좋았을 것 같은데 그러지 못한 환경에 조금 갈증을 느끼며 오늘도 이직 단단히 다짐한다. Readable Code : 읽기 좋은 코드를 작성하는 사고법 강의가 막을 내렸다. 혼자서 도전했으면 어려움에 어지럼증을 느껴 끝까지 완주하지 못했을 것 같은 강의었다. 워밍업 클럽 덕분에 포기하지 않고 한 강의를 완강할 수 있어서 참으로 감사함을 느낀다.  [Day 9] 단위 테스트 | TDD | 테스트는 []다새로운 Practical Testing 테스트 강의를 시작했다!이번 챕터에서는 기본적인 테스트에 대해서 알게 되었고, TDD, BDD에 대해 얘기 나누었다. TDD는 말로만 들어봤었는데 강의에서 TDD 방식으로 코드를 짜나간다고 하시니 약간 생각지도 못한 이득 본 것 같아 엄지를 들었다.  강의를 계속 들으면서 느끼는 점은 코딩 실력도 중요하지만 언어 능력이 중요하다는 걸 엄청나게 많이 깨닫는다. 코딩은 사람이 컴퓨터에게 하는 언어라 컴퓨터가 못 알아 들으면 에러도 내주고 테스트 통과도 시켜주는데 사람이 하는 언어는 내가 뱉은 말을 정말 다양하게 해석할 수 있는 여지가 있어 정확하고 확실한 언어로 내 생각과 코드를 전달해야 정말 실력 있는 개발자인 것 같다.앞으로 그런 개발자가 되도록 노력하자!  출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드

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

sein

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

1주차 회고[Day 2] 추상과 구체이번 챕터에서 가장 와 닿았던 키워드는 '읽기'다. 사실 코딩을 하면서 '읽기 좋은 코드'를 작성하기 보다는 별 생각 없이 '원하는 요구사항에 맞춰 개발하면 됐지'라는 생각이 커서 내 코드를 읽을 후손들은 생각지도 못한 채 개발을 했었다. 그렇다보니 강의를 듣는 내내 처음 해보는 생각들이라 시간이 많이 더뎌졌다.추상화 레벨 파트를 들으면서 메서드를 이렇게 잘게 쪼개본 적이 없어서 많이 당황스러웠다. 여태 라인 수에 따라 돈을 내는 것도 아닌데 전체적인 라인 수를 줄이려고 노력했지 메서드를 잘게 쪼개는 형식으로 개발한 건 처음이었다.이론적인 공부도 중요하지만, 코드를 가독성 좋게 잘 짜는 사람의 코드를 경험하고, 이해하고 작성해보는 시간이 개발자에게 엄청난 경험을 안겨주는 것을 실감했다. [Day 2] 미션미션 링크처음 만난 미션은 어려웠다(1번 제외). 추상과 구체를 스스로 생각해본 적이 없었기 때문이다. 흔히 아는 붕어빵, 자동차, 동물 이런 예시들을 듣고, 적용하기만 해봤지 추상과 구체를 자세하게, 특히 레벨 별로 나눠서 생각해 본 적은 정말 없어서 꽤 오랫동안 생각에 잠겼다.내용을 적어 내려가면서 '이게 맞나?'싶었다. 왜냐면 내가 나름의 추상 레벨로 구분 해 놓은 것들이 사실 파고들면 파고들 수록 더 깊게 구체화 시킬 수 있었기 때문이다. 어떻게 나눠야 적정 레벨로 나눌 수 있는지가 참 애매했던 것 같다. 정답은 없는 것 같다고 결론 내렸다. 다만, 이 능력을 키워갈 수는 있을 것 같다. 처음 시작이 어렵지 앞으로 추상과 구체를 레벨 별로 잘 나누어보는 연습을 해야겠다. [Day 3] 논리 사고의 흐름 | 객체 지향 패러다임나에게는 이 챕터가 제일 곤욕이었다. 재직자라 수업을 들을 수 있는 시간은 한정적인데다 다음 날에 미션까지 있어 마음이 너무 조급했다. 퇴근하고 듣는 수업은 눈이 감기기 일쑤였다. 그래서 복습이 매우매우 필요한 챕터라고 생각한다.그래도 기억 나는 것들을 적어보자면 '평소에 내가 생각하지 못했던 것들'이었다. 나는 대단한 개발자가 아니다. 그래서 스스로 'Early return'이나 'depth 줄이기' 등과 같은 내용을 생각하지 못한다. 이번 챕터에서 가장 많이 배웠다고 해도 무리가 없다.객체 지향 패러다임도 많이 배웠다. 단순히 이론만이 아닌 리팩토링하는 실습까지 같이해서 많이 배운 것 같다. 물론 혼자 스스로 적용해보기엔 아직 무리지만. 객체 지향 패러다임에서는 'getter'와 'setter'를 자제하란 내용이 신기했다. Setter의 지양은 너무 많이 알려져있는 내용이라 그러려니 했지만 Getter의 경우는 처음 듣는 얘기었다. Entity를 생성할 때 Lombok으로 @Getter를 만들고 보는 내 입장에선 배울 게 정말 많았다.이번 챕터는 배워서 바로 쓰기에는 조금 어려운 내용이지 않았나 싶다. 앞으로 배운 내용들을 복습하고, 새로운 곳에 적용해보는 연습이 필요할 것 같다. [Day 4] SOLID그 유명한 SOLID에 대해 공부했다. 내용들은 워낙 유명하니 굳이 적진 않겠다. SOLID는 유명하지만 코드에 적용하기는 매우 어려운 내용이다. 각종 블로그에서 보이는 짤막한 코드 조각을 보면 이걸 어떻게 실무에 적용할지 약간 까마득해진다. 우빈님 강의에서는 짧지 않는 코드를 SOLID의 각 내용에 맞춰 점진적으로 리팩토링하니까 더 이해가 잘 됐던 것 같다. 물론 초보가 따라가기엔 어려웠지만 내가 그리 갈망하던 초급 -> 중급으로 넘어갈 수 있는 실력을 좀 갈고 닦은 느낌이 들었다. [Day 4] 미션미션 링크이번 미션도 시간이 오래 걸렸다. 실제로 리팩토링하는 부분이 있어서 많은 고민을 했던 것 같다. 솔직히 다른 분들의 코드를 참고하기도 ㅎ 했다. 뭐.. 그러면서 실력이 느는 것 아닐까?!(뻔뻔) 그래도 양심적으로 조그만 부분만 참고했다. 제시된 메서드만 간단하게 리팩토링해보기엔 내 실력을 믿고 검증할 수가 없어서 코드가 돌아가게끔 최소한으로 class들을 만들고 실행시켜봤더니 마음이 한결 편해졌다.어려웠긴 했지만 코딩을 직접적으로 하는 미션이 너무 재밌어서 시간 가는 줄 모르고 했다.이런 미션이 또 나왔으면 좋겠다. [Day 5] 객체 지향 적용하기나는 '상속' 밖에 몰랐고, '상속'이 객체 지향의 꽃인 줄 알았다. 근데 실무에서는 잘 쓰지 않는다니! 또 충격을 받았다. 충격을 5일째 받으니 머리가 어질어질.. 요즘엔 '상속'보다는 '조합'을 더 많이 쓴다고 한다. 어쩐지 내가 '상속'으로 개발했을 때 조금이라도 변경이 생기면 머리가 아프더라..! 이런 가려운 부분을 잘 긁어주셔서 참으로 감사했다. 종합적인 회고그저 배운게 많았던 한 주였다. 복습이 필요함을 느꼈고, 내가 많이 부족하다는 것도 느꼈다. 이 강의를 통해 좀 더 나은 개발자가 되길 소망한다. 화이팅! 출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법

백엔드워밍업클럽backend박우빈

sein

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

미션@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 정리@Mock 순수 자바 환경의 테스트 단위에서도 사용할 수 있다.가짜 객체를 생성한다. 실제 로직은 없다.행위에 대한 기대를 명세하고 그에 따라 동작하도록 한다.보통 테스트 대상 클래스의 의존 객체를 대체할 때 사용한다. @MockBean스프링 부트 환경 같이 Bean을 사용하는 통합 테스트에서 사용할 수 있다.스프링의 올라가있는 실제 Bean을 Mock 객체로 교환한다. @Spy순수 자바 환경의 테스트 단위에서도 사용할 수 있다.stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체다.일부는 실제 객체처럼 동작시키고, 다른 일부만 stubbing할 수 있다. 즉, 선택적 구현과 mocking을 섞어 사용할 수 있다. @SpyBean스프링 부트 환경 같이 Bean을 사용하는 통합 테스트에서 사용할 수 있다.기존 Bean을 Spy 객체로 감싸 실제 로직을 유지하면서 특정 메서드만 moking 한다. @InjectionMock순수 자바 환경의 테스트 단위에서도 사용할 수 있다.mock 객체를 자동으로 주입해준다.테스트 대상 객체에 mock된 의존성 객체들을 자동으로 주입한다. 즉, 스프링의 도움을 받지 않고 의존성을 주입할 때 사용한다.  테스트 코드 리팩토링@BeforeEach void setUp() { 1-1. 댓글 레포지토리.deleteAllInBatch(); 1-2. 게시글 레포지토리.deleteAllInBatch(); 1-3. 사용자 레포지토리.delteAllInBatch(); } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-1. 사용자 생성 - createUser() 호출 1-2. 게시물 생성 - createPost() 호출 // when 1-3. 댓글 생성 - createReply() 호출 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-1. 사용자 생성 - createUser() 호출 2-2. 게시물 생성 - createPost() 호출 2-3. 댓글 생성 - createReply() 호출 // when 2-4. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-1. 사용자1 생성 - createUser() 호출 3-2. 게시물1 생성 - createPost() 호출 3-3. 댓글1 생성 - createReply() 호출 3-4. 사용자2 생성 - createUser() 호출 // when 3-5. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 } private User createUser() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 } private Post createPost() { 1-1. 게시물 생성에 필요한 내용 준비 1-2. 게시물 생성 } private Reply createReply() { 1-1. 댓글 생성에 필요한 내용 준비 1-2. 댓글 생성 } 한마디강의에서 배웠던 내용을 잘 적용해볼 수 있었던 미션같아서 한 번 더 정리하는 느낌이라 좋았다. 마지막 미션까지 달릴 수 있어서 뿌듯함 100배 출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽박우빈백엔드테스트코드

sein

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

미션MVC 기반에서 가장 많이 사용되는 3티어 레이어별로 특징과 테스트 방법을 정리해보자! Layered Architecture 일반적으로 controller(presentation) -> service(business) -> repository(persistence) 구조로 이루어져있다.레이어를 단계적으로 구분하는 이유는 관심사 분리가 목적이다. persistence LayerDB와 직접적인 소통을 하는 계층이며 CRUD 쿼리 작업을 처리한다.Business 계층에서 넘어온 데이터를 처리한다.주로 JPA와 QueryDSL을 사용한다.  테스트 방법클래스 레벨에서 @SpringBootTest, @DataJpaTest 어노테이션을 사용할 수 있다.@SpringBootTest를 사용하는 것을 더 추천한다(다른 테스트와의 환경 통합을 위해)작성한 쿼리가 의도대로 동작하는지 테스트한다. business Layer비즈니스 로직이 메인이다.presentation에서 데이터를 받으며 중요 비스니스 로직을 수행하고 persistence 계층으로 전달하는 중간다리 역할을 한다.  테스트 방법클래스 레벨에 @SpringBootTest를 사용하거나 Mock을 사용하는 경우 @ExtendWith(MockitoExtension.class)를 사용한다. 이 역시 환경의 통합을 위해 되도록이면 SpringBootTest를 사용하는 것이 좋다. 스트 객체를 생성할 때 관련 테스트 메서드에는 꼭 필요한 데이터만 파라미터로 넘겨받아 빌더 패턴을 활용한다.단위 테스트를 작성할 수도, persistence 계층와 통합해서 테스트할 수도 있다. presentation Layer외부 세계를 제일 처음 맞이하는 계층이다. 또한 외부 세계에 응답 데이터를 전달해주는 계층이다.  테스트 방법사용자 요청의 데이터에 최소한의 검증을 해야한다. (validation) 다만, 타입의 유효성 자체에 대한 검증이 아닌 도메인과 관련된 검증들은 presentation 계층이 아닌 더 내부에서 진행한다.클래스 레벨에 @WebMvcTest(className.class)를 사용한다.mock을 사용한다.직렬화, 역직렬화를 위해 ObjectMapper 객체가 필요하다.   출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽박우빈테스트코드MVC

sein

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

미션아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.[as-is]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; } [to-be]public class Day4 { public boolean validateOrder(Order order) { try { isOrderItemsEmpty(order); isCustomerInfoEmpty(order); isTotalPricePositive(order); System.out.println("유효성 검사를 통과했습니다."); return true; } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); return false; } } private static void isCustomerInfoEmpty(Order order) { if(!order.hasCustomerInfo()) { throw new IllegalArgumentException("사용자 정보가 없습니다."); } } private static void isTotalPricePositive(Order order) { if(order.getTotalPrice() < 0) { throw new IllegalArgumentException("올바르지 않은 총 가격입니다."); } } private static void isOrderItemsEmpty(Order order) { if(order.getItems().isEmpty()) { throw new IllegalArgumentException("주문 항목이 없습니다."); } } } class Order { private final List<Item> orderItem = new ArrayList<>(); private Customer customer; public void setItem(Item item) { orderItem.add(item); } public void setCustomer(Customer customer) { this.customer = customer; } public List<Item> getItems() { return orderItem; } public int getTotalPrice() { int totalPrice = 0; for(Item item : orderItem) { totalPrice += item.getPrice(); } return totalPrice; } public boolean hasCustomerInfo() { return customer != null; } } class Customer { private String id; public Customer(String id) { this.id = id; } } class Item { private final int price; public Item(int price) { this.price = price; } public int getPrice() { return price; } }수업에서 배운 내용들을 차분히 적용하도록 노력했다. 다음과 같은 순서로 리팩토링을 진행했다.메인인 validateOrder() 메서드 외엔 추상화를 적용하지 못했다. (시간이 없..어서) Early return복잡하게 얽혀있던 if-else문을 뜯어내 if문을 최소화했다. 메서드 추상화메서드의 경우엔 이름이 정말 고민 많았는데, 일단 세 메서드 다 isXXX로 통일시켰다.예외처리예외 처리를 통해 유효성 검사가 통과되지 않았을 때의 로직을 통일했다. return false를 하나하나 써주지 않아도 됐다. 테스트 결과public void run() { System.out.println("======= 시작 ======="); Order order1 = new Order(); validateOrder(order1); // "주문 항목이 없습니다." Order order2 = new Order(); Item item = new Item(1000); order2.setItem(item); validateOrder(order2); // "사용자 정보가 없습니다." Order order3 = new Order(); Item item2 = new Item(-10); order3.setCustomer(new Customer("id3")); order3.setItem(item2); validateOrder(order3);// "올바르지 않은 총 가격입니다." Order order4 = new Order(); Item item4 = new Item(1000); order4.setCustomer(new Customer("id4")); order4.setItem(item4); validateOrder(order4); // "유효성 검사를 통과했습니다." } SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP : Single Responsibility Principle / 단일 책임 원칙이상적인 사회 모습. 회사에서 한 사람 당 하나의 책임만 가졌으면 좋겠는 것 처럼, 객체는 단 하나의 책임만 가져야 한다. 하나의 객체가 여러 책임을 가질 경우 변경 시 파급력이 크다. OCP : Open-Closed Principle / 개방-폐쇄 원칙열린교회 닫힘. 새 신도는 환영하면서 문을 열어주지만 나갈때는 아니란다. 소프트웨어 요소는 확장에는 열려있어야 하고 변경에는 닫혀있어야 한다. 추가적인 요구사항에 유연하게 확장할 수 있어야한다. 기능 추가 시 최소한의 코드를 수정해야 함(물론 로직에 직접적인 영향을 주는 코드는 수정 X)  LSP : Liskov Substitution Principle / 리스코프 치환 원칙자식은 부모의 거울이다! 자식 클래스는 부모 클래스의 기능을 정확하게 확장해야한다. ISP : Interface Segregation Principle / 인터페이스 분리 원칙객체와 똑같다. 하나의 인터페이스에 너무 많은 기능을 넣으면 원치않는 의존성이 생길 수 있다. DIP : Dependency Inversion Principle / 의존 역전 원칙구현체가 아닌 추상에 의존해야 한다. 구현체에 의존하게 되면 어떠한 인터페이스에 대한 구현체가 바뀔 때마다 다른 코드에 영향을 줄 수 있다 !! 결합도를 낮추고 유연한 구조를 만든다.  출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법

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

sein

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

미션강의에서 안내하는 것처럼, 프로젝트를 개인 계정으로 fork하고 강의를 수강해 주세요.github 주소 : https://github.com/techhan/readable-code.git 추상과 구체" 강의를 듣고, 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어봅시다.추상 : 택배를 받았다.구체 과정고객이 주문한다.판매자가 확인 후 택배 송장을 뽑는다.계약된 회사가 택배를 수거하러 방문한다.택배를 수거해서 한 번 이상의 상하차를 거친다.택배 배달원이 고객에게 택배를 전달한다. 짧은 회고두 번째 문제를 처음 맞닥뜨렸을 때는 좀 당황스러웠다. 추상과 구체. 프로그래밍을 배우면서 추상과 구체에 대해 들어본 적은 있었다. 단순히 '동물-강아지' 이런 수준. 들어만 봤지 내가 직접 생각해 본 적은 별로 없었고, 더군다나 3~5문장으로 적어 본 적은 더 없어서 꽤나 생각하는데 애를 먹었다. 그래도 한 번 생각의 물꼬가 터지니, 위에서 도출해 낸 구체 과정에서도 완벽한 구체가 아닌, 그 안에 또 구체, 또 구체, 또 구체...가 있는 걸 알게 됐다. 예를 들면 고객이 주문하는 과정에서도 '핸드폰 켜기 > 사이트 들어가기 > 장바구니에 담기 > 결제하기 > 완료하기' 등등의 구체들이 숨어 있다는 게 별 거 아닌 것 같지만 흥미로웠다. 추상과 구체는 기억에 매우 오래 남을 것 같다.  출처 인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법 

백엔드인프런워밍업클럽4기백엔드박우빈ReadableCodeCleanCodeBackend

livoi

[워밍업 클럽 4기 - 백엔드] 2주차 회고

인프런 워밍업클럽 백엔드 스터디를 진행하며 2주차 회고를 정리해봅니다. KEEP스터디를 하면서 가장 좋은 점은 함께 공부하는 커뮤니티가 있다는 점 인것 같습니다. 특히 진도표를 같이 따라갈 동료를 모집해 함께 진행중인데, 비슷한 고민들을 하고 있다는 점들이 큰 위로가 되면서 동기부여가 됩니다.PROBLEM읽기좋은 코드 강의를 진행하면서 가장 어려웠던 부분은, 리팩토링이라는게 커다란 규칙은 있지만 문법같은 규칙은 아니어서 강사님께서 강의에서 경험의 영역이라고 했던 부분이 어떤 말인지 직접 과제를 해보면서 체감이 되었습니다. 코드를 읽어가면서 추상화 레벨이 맞지 않는 부분들을 찾아가는 연습이 계속해서 필요하다는 생각이 들었습니다.TRY강의 진도를 따라가고 학습하는데 가장 큰 목표가 있지만 , 읽기좋은 코드와 테스트코드 작성을 모두 체화하고 싶은 목표를 달성하기 위해서는 더 적극적으로 학습해야 할 것 같습니다. 이번주에 학습하면서 테스트가 가능한 영역과 불가능한 영역들을 분류해보는 눈을 더 키우고 작은 영역부터 테스트코드를 작성해 코드의 안정성을 확보해보는 연습을 해보고 싶습니다.

백엔드인프런워밍업클럽테스트코드박우빈

10

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

👣 발자국 책갈피인프런 워밍업 클럽 3기 백엔드 코드 발자국 1주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 2주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 3주차인프런 워밍업 클럽 3기 백엔드 코드 발자국 4주차✅미션PR 책갈피[미션 Day 2] 추상과 구체 예시 작성[미션 Day 4] 리팩토링 & SOLID 원칙[미션 Day 7] 리팩토링 연습[미션 Day 11] 단위테스트 작성[미션 Day 16] 레이어드 아키텍처 특징 및 테스트 작성법[미션 Day 18] Mock 어노테이션 종류 및 차이점 & BDD 패턴 매치🎼 알레그레토 : 조금 빠르게 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://inf.run/Y4cf2강의 : 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 6개월 전
정예은

[워밍업클럽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기 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박우빈

채널톡 아이콘