블로그
전체 42024. 10. 27.
0
인프런 워밍업 스터디 클럽 2기 - 백엔드(클린코드, 테스트코드) 4주차 발자국
4주라는 시간이 엄청 길줄 알았는데 순식간에 지나갔다. 여태 학습한 내용을 정리하면서 마무리해보자.Mock 다루기3주차에 흐름이 끊기는 것 같아 Presentation Layer 테스트진도를 나갔기 때문에 Mock부터 시작했다.MockMvc부터 시작해서 테스트 더블까지 다뤘는데,자세한 내용은 블로그에 따로 작성해봤다. Mockito 파헤치기 with Mock, SpyMock을 사용한 테스트는 테스트가 성공했다고해서 실제 운영 환경에서도 정상적으로 기능이 동작하리라 확신할 수는 없다는 생각이 있어 Mock 사용을 일부러 피했는데, 글을 작성하면서 효율적인 테스트는 어떻게 짤 수 있는가, 테스트 더블을 어떻게 사용하면 테스트 격리가 가능할지 고민할 수 있는 좋은 시간이 되었다.미션과 피드백Day18 미션은 테스트 더블 애노테이션의 각 용법과 차이점, BDD 스타일로 코드 배치하기였다.피드백에서 머리를 한대 맞은 느낌이었는데, 핵심은 중복 제거가 아니고 도메인에 집중해야한다는 것이다.사용자, 게시물은 간접적이고, 댓글 작성은 직접적이기 때문에 given절에 배치해야 한다는 것이다.중복 제거에 초점을 너무 둔 나머지 미처 생각하지 못한 부분이었다. 항상 나무가 아닌 숲을 볼 것!맺음한 달동안 초 집중할 수 있을 줄 알았는데, 막상 진행하니까 많이 신경쓰지 못한 것 같아 아쉬움이 남는다.그래도 깔끔한 코드를 다루는 법, 테스트 코드를 어떻게 작성해야 하는지 얻어가는 것이 훨씬 많았다. 이제 프로젝트에 적용해서 내 것으로 만드는 시간을 가질 예정이다.다른 러너들도 고생많았습니다!
2024. 10. 20.
0
인프런 워밍업 스터디 클럽 2기 - 백엔드(클린코드, 테스트코드) 3주차 발자국
개요테스트는 소프트웨어의 품질을 보증하는 작업 중 하나이다.테스트 코드를 작성하는 것을 통해 버그를 조기에 발견하고 견고한 소프트웨어를 만들 수 있으며 반대로 테스트 코드를 작성하지 않으면, 변화가 발생할 때마다 모든 케이스를 고려하는 번거로움이 생기며 유지보수가 어려워진다.Practical Testing 강의에서 테스트 코드를 작성하는 방법과 접근법을 알아보는 시간을 가진다.테스트하기 어려운 코드3주차에는 테스트 프레임워크인 JUnit5을 이용해 자동화된 테스트, 테스트 케이스 세분화(해피케이스와 예외 케이스)하는 작업부터 시작한다.우빈님이 강의에서 가장 중요하다고 언급한 '테스트하기 어려운 영역 분리'가 가장 기억에 남는데, 현재 날짜와 시간, 랜덤한 값, 전역변수, 사용자 입력과 같이 관측할 때마다 '다른 값에 의존하는 코드'와 표준출력, DB 기록, 메시지 발송과 같이 '외부에 영향을 주는 코드'와 같이 어려운 영역을 분리하는 것이다.코드에서는 메서드 내에 선언한 LocalDateTime 때문에 특정 시간에만 성공하고 나머지는 실패하게 되는데, LocalDateTime을 파라미터로 받도록 외부로 분리하는 과정을 통해 이를 알아봤다.핵심은 테스트 하고자하는 영역을 확실히 구분하고, 이를 구분하는 시야를 길러야 한다는 것!계층형 아키텍처에서 테스트계층형 아키텍처는 역할과 관심사에 따라 분리한 아키텍처로 각 계층은 애플리케이션에서 화면 표시나 비즈니스 로직, DB 작업 등 관심사에 따라 분리된다.3주차 권장 진도가 Business Layer에서 멈춰 흐름이 끊기는 느낌이어서 Presentation Layer까지 학습했다.흔히 사용되는 아키텍처로 각 계층별 역할과 어떤 테스트를 해야하는지 알아봤는데클라이언트 요청을 받는 순서대로 정리해봤다.'Presentation 계층'은 사용자의 요청을 받아 처리하는 레이어로 주로 HTTP 요청/응답과 관련된 로직을 포함한다. 때문에 클라이언트가 보내는 HTTP 요청을 실제로 테스트하고 엔드포인트가 의도대로 동작하는지 확인'Business 계층'은 도메인 객체를 활용한 다양한 처리 과정을 포함하며 데이터 접근 및 처리 로직을 포함하지 않으며, 트랜잭션을 보장해야 한다. 비즈니스 로직을 독립적으로 테스트하며 데이터 접근 레이어인 Repository나 외부 API는 Mocking하여 순수한 비즈니스 로직이 예상대로 작동하는지 검증'Persistence 계층'은 Database나 외부 API와의 통신 등을 처리한다. 테스트에서는 쿼리 메서드가 올바르게 동작하는지, 데이터 저장 및 조회가 정상적으로 수행되는지 확인미션 Day12미션에서는 Readable code에서 다뤘던 코드 중 하나인 스터디 카페 이용권 시스템으로 테스트 코드를 작성하게 되었다.코드를 작성하는데 큰 어려움은 없어서 미션을 제출하고 다른 지원자들이 작성한 코드를 살펴봤는데 Mock을 사용한 코드도 있었다. 개인적으로 classicist 쪽에 가깝기 때문에 실제 객체 사용이 어렵지 않다면 테스트 더블을 사용하지 않는 쪽인데 다양한 관점을 볼 수 있어 많이 배울 수 있었다.맺음테스트 코드의 중요성은 말해 입아프다. 이제 한 주 남았는데 완주 이상으로 얻을 수 있도록 노력해야겠다.러너들 화이팅~
2024. 10. 13.
0
인프런 워밍업 스터디 클럽 2기 - 백엔드(클린코드, 테스트코드) 2주차 발자국
워밍업 클럽을 엊그저께 시작한 것 같은데 벌써 절반에 다다랐다.2주차의 다룬 내용은 주로 코드 리팩토링과 유지보수에 관련된 주제들이었다.코드 다듬기 파트먼저 주석의 존재 의의부터 시작하여 변수와 메서드 나열 순서로 이어졌다.주석 같은 경우, 필요한 경우에만 쓰는 것이 좋다. 모든 방법을 동원해 코드에 의도를 녹여냈지만 그럼에도 전달해야 할 정보가 남았을때 사용하는 것이 주석이다. 클린 코드에서도 주석에 대해서 '나쁜 코드를 보완하지 못한다'라고 설명하는데 이보다 적합한 표현은 없다고 생각한다.이어서 '논리, 사고의 흐름'의 연장선이라고 볼 수 있는 변수와 메서드의 나열 순서도 다룬다.변수는 사용하는 순서대로 나열하며, 항상 객체 입장에서 생각하자는 것이 핵심이다. 상태가 변경되는 변수 및 메서드를 우선으로 배치하고 판별, 조회 로직이 있는 메서드들 순으로 하는 기준을 배우고 코드에 원칙을 적용하는 리팩토링 하는 과정을 거쳤다. 컨벤션에 대한 정답은 없는 법이지만 협업과 가독성 관점에서 전적으로 동의하는 내용이 주를 이루었다.리팩토링 연습 파트객체 지향 원칙을 적용하고 여태 배웠던 것을 전체적으로 리팩토링 하는 파트이다.먼저 추상화 레벨에 관한 부분이다. 이번 파트는 이미 예제로 작성되어있는 코드를 리팩토링 하는 과정을 거치는 것이어서 살짝 어려웠다.StudyCafePassMachine 클래스에서 코드 중복부분을 제거하고 일급 컬렉션을 적용할 수 있는 부분을 찾아내 변경한 것 까지는 좋았지만 객체에 메시지를 던진다는 것이 익숙하지 않아 많이 해맸다. 강의에서는 Order 객체를 만들어 책임을 분리하는 접근 방법같이 내가 생각하지 못했던 여러 관점을 엿볼 수 있었는데, 확실히 코드는 돌려보면서 평가받아야 문제점과 개선점을 찾을 수 있다는 점을 다시 한번 크게 느끼게 되었다.강의를 쫓아가느라 바빴지만 개인적으로 가장 유익한 파트였으며 미션으로 여태 학습한 것을 동원하여 코드에 대한 고민을 할 수 있는 시간이었다.다른 시험이 겹쳐서 코드 리팩토링에 시간을 많이 투자하지 못해서 아쉬웠지만, 시간을 내서 내 방식으로 다시 리팩토링하여 개선해 볼 예정이다.맺음강의에서 다루는 내용이 상당히 많았지만 결국 '추상'이라는 키워드에 입각하여 이에 벗어나지 않은채 클린 코드의 정수를 학습할 수 있는 시간이었다.무작정 답을 알려준다기 보다는 답을 찾아가는 방법을 유도해주는 우빈님의 방식덕에 워밍업 클럽에 몰입할 수 있었다.3주차부터는 테스트 코드에 관한 내용이다. 이미 한 번 완강했지만 스터디 클럽의 커리큘럼을 통해 다시 돌아보는 좋은 시간이 될 것 같다.이번엔 시험이 있어서 시간 분배에 실패했지만 남은 2주 잘 마무리하도록 시간을 적절히 투자하여 우수러너 욕심을 한 번 내볼 생각이다.러너들 화이팅~출처 우빈님의 강의
2024. 10. 06.
0
인프런 워밍업 스터디 클럽 2기 - 백엔드(클린코드, 테스트코드) 1주차 발자국
클린 코드를 추구하는 이유는?개발을 시작한지 좀 된 사람이라면 로버트 마틴의 ‘클린 코드’라는 책을 읽진 않아도 들어 봤을 것이다.클린 코드의 부제를 보면 '애자일 소프트웨어 장인 정신'인데 부제 그대로 장인 정신에 관한 내용이다. 여기서 거추장스럽게 애자일이 뭔지 이해할 필요 없이 클린 코드의 목적을 말하자면 바로 '가독성'을 추구하는 것이다.코드는 사람이 작성하고, 읽기 때문에 클린 코드를 추구해야하는 근원적 이유는 눈에 잘 들어오고 쉽게 이해가능 해야하는 것이다.하지만 이를 왜 지켜야할까? 그 이유를 코드를 통해 체득해 나가는 것이 이 강의의 핵심 주제이다. 추상이를 체득하기 위해 가장 중요한 키워드는 바로 '추상'인데 강의 내내 보게 될 키워드이며 그만큼 중요하다.추상의 사전적 의미 다음과 같다.여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용.사물이나 개념에서 특성이나 속성을 추출한다고 나와있다. 개발이 아니더라도 일상 생활에서 '추상적 사고'는 이미 뿌리깊이 자리 잡고 있다.예를 들어 '날씨'는 대기 중에서 일어나는 모든 기상 현상을 포괄하는 추상적 개념으로 온도, 습도, 강수량, 기압 등의 요소가 결합하여 특정한 기후 조건을 형성하는 것을 의미한다.이를 구체적으로 바라본다면 “오늘 아침 기온은 20°C이고, 바람은 시속 10km로 불고 있으며, 30%의 습도를 동반한 맑은 하늘”이라는 식으로 각 요소를 수치로 구체화할 수 있다. (미션-Day2)정리하면 '구체'라는 것은 여러가지 개별 사건에 하나하나 대응하는 것으로, '추상'은 공통적인 특징을 하나로 정리해 일반화한 것이다.이처럼 적절한 추상화는 일상 생활 뿐만 아니라 컴퓨터 과학에서 다루는 복잡한 데이터를 읽기 쉽게 도와준다.클린 코드는 이를 다음과 같이 설명한다. 우리가 함수를 만드는 이유는 큰 개념을(다시 말해, 함수 이름을) 다음 추상화 수준에서 여러 단계로 수행하기 위해서가 아니던가. - 클린코드, 45page사실 이미 클린 코드에 나온 내용을 강박처럼 지키고 있었기 때문에, 섹션 2인 추상과 섹션 3 논리, 사고의 흐름은 어려움없이 넘길 수 있었다.때문에 Day4 미션인 읽기 좋은 코드로 리팩토링하기도 어려움없이 코드를 작성할 수 있었다. // 리팩토링 전 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; } // 리팩토링 후 public boolean validateOrder(Order order) { try { validateItems(order); validateTotalPrice(order); validateCustomerInfo(order); } catch (InvalidOrderException e) { log.info(e.getMessage()); return false; } return true; } private void validateItems(Order order) throws InvalidOrderException { if (order.hasNoItems()) { throw new InvalidOrderException("주문 항목이 없습니다."); } } private void validateTotalPrice(Order order) throws InvalidOrderException { if (order.isPricePositive()) { throw new InvalidOrderException("올바르지 않은 총 가격입니다."); } } private void validateCustomerInfo(Order order) throws InvalidOrderException { if (order.hasNoCustomerInfo()) { throw new InvalidOrderException("사용자 정보가 없습니다."); } } // 커스텀 예외 InvalidOrderException class InvalidOrderException extends Exception { public InvalidOrderException(String message) { super(message); } } 조기 Return, 부정어로 인한 읽는데 중간에 한번 막히게 되는 코드 개선, 예외 처리 등 한 눈에 코드를 알아볼 수 있게 끔 리팩토링 해봤다. 이 부분은 코드가 더 복잡해진다면 어떻게 하면 좋을지 숙련이 필요하다고 생각하게 된 부분이었다. 전체적으로 규칙뿐만 아니라 추상화라는 거대한 개념을 이해하기에 아주 좋은 파트였다.객체 지향 적용하기객체 지향 패러다임, 특히나 객체 지향 5원칙인 SOLID는 기계처럼 법칙을 외운다고 해서 절대 이해할 수 없기 때문에 이해한 것을 바탕으로 코드를 재구성해봤다. - 미션 Day4코드를 쓰면서 계속 염두에 뒀던 것은 비즈니스 요구 사항이 항상 변경될 것을 인식하고 있었다는 것이다. 나중에 요구 사항이 바뀌더라도 작성했던 코드의 틀을 유지하면서 변경하려면 어떻게 해야할까?라는 고민을 자연스럽게 하게되었다.그에 대한 키워드는? 역시나 '추상화'였다.미션에서 작성했던 코드와 글의 마무리가 만족스럽지 않아, 다시 SOLID 원칙을 다시 짧게 정리해본다면> SRP: 모듈(클래스 or 클래스의 집합)이 변경되는 이유는 한 가지여야 함 -> 책임에 따라 코드를 분리하게되면 새로운 비즈니스 요구 사항이 생겼을 때 변화가 발생하더라도 수정할 대상이 명확해진다.> OCP: 확장에 대해 열려있고 수정에 대해 닫혀있다는 것 -> 인터페이스에 의존하도록 추상화함으로써 기존 코드를 수정하지 않은 채 수정 및 확장이 가능> LSP: 하위 타입은 상위 타입을 대체할 수 있다는 것 -> upcasting된 상태에서 부모의 메서드를 사용해도 동작이 의도대로 흘러가야 함> ISP: 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 함 -> 목적과 용도에 적합한 인터페이스 만을 제공하는 것이 목표> DIP: 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 되며, 저수준 모듈이 고수준 모듈에 의존해야 함 -> 추상 클래스나 인터페이스를 참조하여 거의 변화가 없는 것에 의존시켜 각 클래스간의 결합도를 낮추는 것이 목적이렇게 정의할 수 있지 않을까 싶다.이제 컴포지션 사용, VO 객체, 일급 컬렉션과 같은 개념들은 이어지는 미션에서 직접 적용하고 글을 다시 정리해 볼 생각이다.맺음강의를 보며 단순히 ‘깨끗한 코드’라는 차원을 넘어서, 내가 작성한 코드를 나중에 동료들이 쉽게 이해할 수 있을까에 대한 고민을 계속 하게되는 시간을 보냈다.그러던 중, 인프콘에서 발표를 마치고 토비님이 블로그에 남기신 말이 있는데클린 코드는 그저 이름 잘 짓고, 함수 작게 만들고, 주석 달지말고, 스타일 통일하고, 디미터 법칙 지키자라는 수준의 피상적이고 기계적인 주장을 하는 캐치프레이즈가 아니다. 대부분 앞부분만 보고 6장 넘어서 나오는 내용에 대해서는 얘기도 하지 않는, 책에 나오는 저자가 꽤나 고민하면서 수집하고 애써 만진 코드는 술렁술렁 넘기고 말 책이 아니란 말이다.읽으면서 굉장히 따끔한 문장이었다. 클린 코드는 단순히 형식적인 규칙을 따르는 것이 아니라, 코드를 읽는 사람, 즉 나와 동료 개발자들 간의 소통을 원활하게 하고, 더 나아가 유지보수성과 확장성을 높이는 데 중점을 둬야한다는 것이다.더 이상 코드를 ‘깨끗하게’만 작성하는 것이 목표가 아니라, 의도를 명확히 전달하고, 미래의 변화에 유연하게 대응할 수 있는 코드를 작성하는 것이 중요하다는 점을 깨달았다. 앞으로도 클린 코드 원칙을 단순한 규칙 그 이상으로 생각하며, 더 나은 개발자, 더 나은 동료가 되기 위해 지속적으로 고민하고 실천하도록 노력해야겠다.출처- Clean Code- 우빈님의 강의- 애자일 소프트웨어 개발 선언- 토비님의 블로그