블로그
전체 62025. 03. 30.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 4주차 발자국
이번 주차에서는테스트 코드 리뷰더 나은 테스트를 작성하기 위한 구체적 조언 1. 테스트 코드 리뷰 정리 테스트 커버리지코드 커버리지(테스트 커버리지)는 괜찮은 부정 지표지만 동시에 좋지 않은 긍정 지표다. 테스트 커버리지가 너무 낮을 경우 테스트가 충분하지 않다는 좋은 증거가 되지만, 테스트 커버리지가 100%라고 해서 반드시 양질의 테스트 스위트가 보장되지는 않는다.학습 시에는 높은 커버리지를 목표로 하는 경험이 도움이 될 수 있다 테스트 시 사용하는 자원private 메소드라도 상단에 위치시켜 보기 편하도록 한다네이밍에 신경쓰자 (ex : target~ all~) 검증하는 데이터가 변경될 시 테스트가 깨지는 현상은 자연스러운 현상이다데이터 정책 ex passtype이 바뀐다면 이를 사용한 테스트 코드들이 전부 깨질 것으로 예상된다프로덕션 코드가 변경되면 테스트 코드 또한 영향을 받는 것은 당연하다테스트 코드가 실패 하는 것을 보고 영향 범위를 인지 할 수 있다. (프로덕션 코드 변경에 대한 영향을 인지 못하는 것이 더욱 큰 문제)수정해야 하는 비용은 들겠지만, 이는 자연스러운 현상이며, 테스트 코드에 대한 존재 이유이기도 하다 검증해야 하는 테스트 케이스가 너무 많아질 경우 코드 자체의 가독성을 위해 반복문을 선택할 수 있지만 이 또한 코드 이해에 허들이 될 수 있음을 인지해야 한다ex 4개 정도면 그냥 나열하자 or 10개 이상이면 반복문을 돌리자 검증부는 상수로 쓰인 데이터를 하드코딩 해서 검증하자ex EMPTY_SIGN 이 EMPTY_SIGN인지 검증 하는 형태는 항상 통과하게 된다 → 테스트 하는 의미가 없음EMPTY_SIGN 이 ㅁ 문자열 인지 검증 (하드코딩) 간단한 로직의 경우 테스트 해야할까?getter 정도는 테스트 하지 않아도 무방. getter와 거의 동일한 작업을 하는 is~메서드 등한줄이라도 가공 비교 판별 등 비지니스에 직결된다면 테스트 해야 한다 테스트 시 새로운 제약사항이 필요하다고 판단되면 클라이언트에게 역 제안도 가능하다 displayname 에 변경이 일어나기 쉬운 내용 넣지 말자 (ex 파일 경로)f/u 하기 힘듦 2. 더 나은 테스트를 작성하기 위한 구체적 조언 한 문단에 한 주제여러가지 논리 구조(분기문, 반복문)가 들어 가는 것을 피한다 완벽하게 제어하기현재 시간 같은 제어할 수 없는 변수는 쓰는 것을 지양하자. 수행되는 환경 (로컬/배포)에 따라 달라질 가능성이 있다. 테스트 환경의 독립성을 보장하자팩토리 메서드는 프로덕션 코드에서 의도를 가지고 만드는 편 → 테스트 환경에서 사용은 지양하는 것이 좋다대신 순수한 생성자를 가지고 테스트 환경을 위한 given절에서 객체 생성 테스트 간 독립성 보장공유 자원 사용 금지 TestFixture테스트를 위해 원하는 상태로 고정시킨 일련의 객체각 테스트 입장에서 어떻게 구성 되는지 몰라도 내용을 이해하는데 문제가 없을 때수정해도 모든 테스트에 영향을 미치지 않을 때예) 댓글 생성 테스트의 경우 → 댓글 생성에 집중 → 테스트를 위한 게시글 생성 로직, 사용자 생성 로직 등이 @BeforeEach에 위치시켜 TestFixture을 구성할 수 있다. TestFixture클렌징deleteAll의 경우 셀렉트 쿼리, 각 레코드 마다 딜리트 쿼리가 건 단위로 나가게 된다. → 성능 이슈가 생길 수 있음deletAllInBatch가 더욱 효과적으로 생각된다트랜잭션 롤백을 사용하는 전략의 경우 SpringBatch를 사용한 배치 통합 테스트의 경우 사용하기 어렵다. 테스트 환경 통합하기테스트 수행에 드는 시간 또한 잘 관리하여야 한다스프링을 띄우는 환경이 조금이라도 달라지면 테스트 시 새로운 컨택스트를 띄우게 된다동일한 환경에서 띄운다면 시간을 단축 가능하다datajpatest → 데이터 jpa 관련 빈들만 올려서 빠르게 테스트 할 수 있지만, 서비스 에서 @Transactional로 테스트 후 사용하게 된다면 스프링을 새로 띄우게 된다는 걸 알아야함서비스 테스트를 하면서 같이 레포지토리 테스트를 하는 것이 좋은 전략일 수 있다. private 메서드의 테스트테스트 하지 않는다테스트 하고 싶어진다면 객체 분리의 신호일 수 있다. 테스트에서만 필요한 메서드보수적으로 생성한다매우 간단한 메서드 or 추후에 프로덕션 코드에서 사용할 가능성이 있는 경우예시 size(), isEmpty()
2025. 03. 27.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 Day 18 미션
Q1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.Mock 관련@Mock패키지: org.mockito대상: 일반 객체 동작: 실제 객체 대신 가짜(Mock) 객체를 생성하여 테스트에 사용.사용 예: 단위 테스트 환경에서 의존성 주입 없이 독립적으로 사용. @MockBean패키지: org.springframework.boot대상: Spring 컨텍스트에 등록된 Bean동작: 스프링 컨텍스트에 등록된 Bean을 Mock 객체로 대체하여 테스트.사용 예: 스프링 환경에서 테스트를 수행할 때 사용.Spy 관련@Spy패키지: org.mockito대상: 일반 객체동작: 실제 객체를 생성하되, 특정 메서드에 대해 행위와 결과를 지정할 수 있음 (부분 모킹)사용 예: 단위 테스트 환경에서 실제 객체의 일부 메서드만 모킹할 때 사용.@SpyBean패키지: org.springframework.boot대상: Spring 컨텍스트에 등록된 Bean동작: 스프링 컨텍스트에 등록된 Bean을 Spy 객체로 대체하여 부분 모킹사용 예: 스프링 환경에서 실제 빈의 일부 메서드를 모킹할 때 사용.InjectMocks패키지: org.mockito대상: @Mock 또는 @Spy로 생성된 객체동작:테스트 대상 클래스의 인스턴스를 생성.해당 클래스가 의존하는 객체들을 자동으로 주입 (@Mock 또는 @Spy로 선언된 객체가 주입)사용 예:@Mock UserRepository userRepository; @InjectMocks UserService userService; // userRepository가 자동으로 주입 @InjectMocks vs @Autowired@InjectMocks:Mock 또는 Spy를 통해 의존성을 주입.단위 테스트 환경에서 의존성을 자동으로 설정.@Autowired:스프링 컨테이너에서 관리되는 빈을 가져와 의존성을 설정.예시 비교:// 단위 테스트 @Mock UserRepository userRepository; @InjectMocks UserService userService; // userRepository가 주입 // 스프링 컨테이너 @MockBean UserRepository userRepository; @Autowired UserService userService; // userRepository가 주입 Q2. 각 항목 배치하기 public class CommentServiceTest { @BeforeEach void setUp() { 1-1, 2-1, 3-1 사용자1 생성에 필요한 내용 준비 1-2, 2-2, 3-2 사용자1 생성 1-3, 2-3, 3-5 사용자1의 게시물1 생성에 필요한 내용 준비 1-4, 2-4, 3-6 게시물1 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-5. 댓글 생성에 필요한 내용 준비 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 } } 출처 : https://inf.run/EBCNE
2025. 03. 25.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 Day 16 미션
레이어 별 특징 및 테스트 방법1. Presentation Layer (프리젠테이션 계층)특징사용자 요청을 받아 비즈니스 계층으로 전달하고, 결과를 클라이언트에게 반환하는 역할.입력 데이터에 대한 기본적인 검증을 수행. Presentation 계층에서의 검증과 Business 계층에서의 검증을 분리해서 생각해야 한다Presentation 계층에서는 보통 형식적인 검증을 한다(예시: 필수 입력 값 검사, 데이터 타입 검사, null 검사, 빈 문자열 검사, 등...)컨트롤러에서 사용하는 요청 DTO가 서비스 계층으로 침투하지 못하도록 컨트롤러 계층에서 서비스 전용 DTO로 변환하는 것을 권장(상황에 따라 다를 수 있다. 만약 받는 포맷이 변할 가능성이 거의 없다면, 그냥 컨트롤러의 DTO를 쭉 사용해도 괜찮을 수 있다.)프론트와의 소통을 위해 RestDocs라이브러리를 사용하기도 한다테스트 방법MockMvc와 같은 도구를 사용하여 HTTP 요청과 응답을 시뮬레이션 (SliceTest)Mockito 같은 프레임워크를 사용하여 Business Layer와 Persistence Layer를 Mocking요청 객체와 응답 객체를 만들고, 관련 동작에 필요한 Service를 모킹해와서 컨트롤러의 동작을 검증@WebMvcTest 어노테이션을 사용하여 Presentation Layer만 로드하여 테스트스프링을 띄우는 비용이 비싸기 때문에 테스트를 위한 스프링 서버를 최소한으로 띄우기도 한다 2. Business Layer (비즈니스 계층)특징핵심 비즈니스 로직을 처리하며, Persistence Layer와의 상호작용을 통해 로직을 수행비즈니스 로직에 따른 도메인 유효성 검사가 이루어진다트랜잭션 관리에 주의해야 한다테스트 방법Persistence Layer를 Mocking하여 단위 테스트를 수행하거나, Persistence Layer와 함께 통합 테스트를 진행테스트와 트랜잭션 관리법1. @BeforeEach와 @AfterEach를 사용하여 테스트 간 격리를 위한 setUp 및 tearDown 메서드를 사용2. @Transactional을 사용하여 트랜잭션을 관리테스트 코드에서 @Transactional을 사용하면 비즈니스 로직이 트랜잭션을 포함하고 있는 것처럼 착각할 수 있으니 주의 3. Persistence Layer (데이터 접근 계층)특징데이터 소스와의 상호작용을 담당, CRUD 작업 수행비즈니스 로직을 포함하지 않아야 한다다양한 데이터베이스 기술들이 사용될 수 있다는 것을 염두 해 두어야 한다(JPA, JDBC, ...)테스트 방법@DataJpaTest를 사용하여 JPA 관련 빈들만 주입하여 가벼운 테스트를 수행 하거나@SpringBootTest를 사용하여 전체 애플리케이션을 로드하여 통합 테스트를 수행할 수 있다.@SpringBootTest는 @Transactional을 포함하지 않으므로 주의 출처 : https://inf.run/EBCNE
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 3주차 발자국
학습 내용 정리이번 주차에서는Layer에 대한 테스트 코드 학습테스트 코드 적용 실습1. Layer에 대한 테스트 코드통합 테스트통합 테스트의 목적 여러 모듈이 협력하는 기능을 통합적으로 검증. 서로 다른 두 객체가 협력해서 동작할 때 단위테스트로만으로 예측하기 매우 어렵다 풍부한 단위 테스트 & 통합 테스트 두 가지 모두 필요. 단위 테스트는 각 모듈의 정상 작동을 보장, 통합 테스트는 전체 시스템의 흐름을 검증.레이어를 구분하는 이유 관심사의 분리! 각 레이어는 특정한 역할을 수행하도록 한다. 이를 통해 유지보수성 극대화Presentation Layer외부 요청을 받고, 최소한의 검증을 수행.도메인과 관련된 검증은 비지니스 레이어에서 수행문자열이 문자열 다운지 정도만 검Business Layer, Persistence Layer를 Mocking해서 테스트 Business Layer비즈니스 로직을 구현하며, Persistence Layer와 함께 통합 테스트 진행Persistence Layer데이터 접근 역할을 수행하며, 비즈니스 로직은 포함X거의 단위 테스트와 비슷한 성격을 가진다간단한 경우라도 작성한 메서드 쿼리 보장, 추후에 jpa가 아닌 다른 환경으로 변할 수 도 있다. 이를 보장하기 위해 작성 테스트시 사용 어노테이션@SpringBootTest전체 애플리케이션을 로드하여 통합 테스트를 수행 @DataJpaTestJPA 관련 빈들만 주입하여 가벼운 테스트를 수행.내부에 @Transactional이 존재하여 롤백이 된다. @WebMvcTestPresentation Layer에 대한 단독 테스트를 위해 사용다른 레이어들은 mocking을 통해 제어JPA관련 빈들을 로드하지 않기 때문에 주의해야 할 점JPA 관련 빈을 로드하지 않는다. 따라서, @EnableJpaAuditing과 같은 JPA 관련 설정이 어플리케이션 클래스에 선언되어 있으면 테스트 시 JPA 관련 빈을 찾지 못해 오류가 발생한다@EnableJpaAuditing을 별도의 @Configuration 클래스로 분리하여 어플리케이션 클래스에서 제거테스트 클래스에 JpaMetamodelMappingContext를 @MockBean으로 추가하는 방법도 있다@Transactional서비스 테스트 시 @SpringBootTest + @Transactional 주의점테스트 코드에서 @Transactional을 사용하면 비즈니스 로직이 트랜잭션을 포함하고 있는 것처럼 착각할 수 있다.@Transactional(readOnly = true)CQRS : command(CUD)와 query(R)를 분리쿼리용 서비스와 커맨드용 서비스를 분리할 수 도 있다. -> DB에 대한 엔드포인트 구분Master/Slave DB를 나눔으로써 부하를 줄임 동시성 이슈낙관적 락 (Optimistic Lock)데이터를 읽을 때는 락을 걸지 않고, 데이터를 업데이트할 때 충돌이 발생하지 않았는지 확인하는 방식주로 읽기 작업이 많은 환경에서 사용, 데이터의 버전을 관리하여 충돌을 감지장점: 데드락 가능성이 적고 성능이 비교적 좋다단점: 충돌이 발생하면 롤백이나 재시도가 필요할 수 있다.비관적 락 (Pessimistic Lock)데이터를 읽을 때부터 락을 걸고, 업데이트가 완료될 때까지 락을 유지하는 방식입니다주로 수정 작업이 많은 환경에서 사용, 데이터의 일관성을 유지하기 위해 미리 락을 걸어 다른 트랜잭션이 접근하지 못하게 한다.장점: 데이터 무결성을 잘 유지할 수 있다.단점: 락 경합으로 인해 성능 저하가 발생할 수 있다. 2. 테스트 코드 적용 실습 실습 진행 시 가장 중요하게 생각했던 부분왜 이 부분에 테스트 코드가 필요할까?주로 비지니스 로직이 있는 부분오류가 나타나기 가장 쉬운 부분이라고 생각되어서뭘 어떻게 검증해야 할까?예시 데이터가 로직(테스트 하고 싶은 메스드)을 거친 후 예상되는 결과가 정확하게 일치할 경우 테스트 통과하도록 작성어떤 테스트를 진행하는지 쉽게 알아보려면 (네이밍, bdd스타일 신경쓰기)도메인 용어를 사용실습 진행 프로젝트 위치https://github.com/aammddkkzxc/readable-code/tree/main/src/test/java/cleancode/studycafe/tobe어려웠던 부분 / 고민사항https://github.com/aammddkkzxc/readable-code/blob/main/src/test/java/cleancode/studycafe/tobe/needfeedback.md
2025. 03. 16.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 2주차 발자국
학습 내용 정리 이번 주차에서는코드 외적인 부분에서 추구할 수 있는 클린 코드 규칙새로운 프로젝트에서 실습, 피드백테스트 코드 작성 입문 이론 1. 코드 외적인 부분으로도 맥락이 형성 된다.변수와 메서드를 어떻게 위치 시킬 것인가고도화 될 수록 변수, 메서드가 많아질 수 있다.이에 따라 필요한 변수 메서드를 찾는 비용이 점점 증가할 수 있다.이 때 특정 규칙/맥락에 따라 잘 정리한다면 비용을 상당히 많이 감소 시킬 수 있겠다.여러 사람이 같이 일할 때 특히 더 강력해지는 도구가 될 수 있다.주석에 대한 것주석의 필요성에 대해 모호하던 상황이었다.정책 의사 결정의 세부 사항, 히스토리 같은 것들은 코드로 표현하기 어렵고, 따로 문서로 정리하게 될 것이라고 생각된다.이 때 주석이 정말 강력한 도구가 될 수 있겠는 것을 알게 되었다. 문서를 따로 만들 필요 없이 단일화 할 수 있겠구나.다만 이 때 주석을 사용하게 된다면 버전 이라는 새로운 맥락을 형성하므로, 코드와 주석을 같이 업데이트 시켜주는 것에 매우 신경 써야 한다. 2. 새로운 프로젝트에서 실습, 피드백실습 진행 프로젝트 위치https://github.com/aammddkkzxc/readable-code/tree/main/src/main/java/cleancode/studycafe/practice피드백 요청 사항 https://github.com/aammddkkzxc/readable-code/blob/main/src/main/java/cleancode/studycafe/practice/needfeedback.md 피드백 적용 https://github.com/aammddkkzxc/readable-code/blob/main/src/main/java/cleancode/studycafe/practice/feedback.md내용 요약검증 메소드를 검증하고자 하는 해당 클래스 내부에 위치 시키자생성자에서 의미 부여를 피하자정적 팩토리 메서드의 강력한 점은 네이밍을 부여하여 역할을 명확히 할 수 있다는 것따라서 의미가 부여되는 초기화 작업이 있다면, 생성자 보다는 정적 팩토리 메서드 내부에서 하는 것이 좋을 수 있다정답을 찾아내려고 하지 말고 논리와 의도에 집중하자코드에 대한 선택은 모든 맥락에 의해서 정해진다. 심지어 팀 내부 상황 같은 외부적 요인까지도 영향을 미친다정답이 있다고 가정하고 접근하게 된다면, 각종 원칙이나 용어에 매몰될 위험이 있다. 언제까지나 원칙과 용어들은 더 나은 선택을 위한 도구일 뿐이라는 것을 잊지 말자.명확한 의도를 가지고 구현/리팩토링 하되, 요구 사항이나 합의가 변하면 언제든 변경할 수 있는 자세를 지니자클린 코드를 왜 추구하는가팀의 생산성을 위해서. 주종 관계의 역전에 주의하자느낀 점저번 주에 배운 원칙들을 적용 시키는 것에 대해 어려울 것으로 예상했는데, 역시 쉽지 않았다.보는 것 만으로는 한계가 있다는 것을 다시 한번 체감했다.이번에 실제 연습을 해봄으로써 좀 더 성장할 수 있었다최근 개발 공부를 하면서 침체기와 막막함을 느끼고 있었는데, "그래 나 이런 것 재밌어 했지"라고 리프래쉬 할 수 있었다. 3. 테스트 코드테스트 코드/TDD의 중요성이전엔 그냥 막연하게 확실히 하는 것이 좋으니까 중요하겠지라고 생각했었고, 크게 동기 부여가 되지 않았었다.강의를 통해 배운 중요성피드백이 빠르다클라이언트 관점으로 개발하여 해피케이스에 매몰되는 것을 피할 수 있다.모호한 기획이나 암묵적인 약속들을 찾아낼 수 있다과감한 리팩토링이 가능해진다테스트 하기 어려운 케이스란?코드를 실행 할 때마다 바뀌는 부분을 함수 내부에 가지고 있으면서, 제어하기 힘든 경우날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등함수 내부에 갖고 있지 않도록 분리한다. (매개변수로 받아오는 형태로 만들면 제어 가능)어느 레이어 까지 분리 시킬 지 결정이 필요해진다외부에 영향을 주는 경우외부 세계라는 것 자체가 테스트 시 검증이 어렵다표준 출력(로그), 메세지 발송, 데이터베이스 에 기록 등코드외에 다른 테스트 도구 필요DisplayName을 섬세하게, BDD스타일단순 명사의 나열 x 행위부터 결과 까지도메인 관점으로 상세하게 작성메서드 자체의 관점 x테스트 현상 중점 x개발자가 아닌 사람이 봐도 이해할 수 있을 정도로출처 강의 : 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
2025. 03. 09.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국
학습 내용 요약 읽기 좋은 코드가 되기 위해서 고려하여야 할 많은 사항들을 알 수 있었다.공부한 내용들 중 가장 인상 깊었던 내용들을 나만의 방식으로 정리해보려고 하니 두 가지의 카테고리를 만들어 정리할 수 있었다. 1. 맥락(도메인 지식, 클래스)에 맞도록 하고, 필요하다면 의미를 부여한다클래스에 속하는 것 자체로 의미를 내포하게 된다.메서드의 모든 구성요소 (이름 뿐만 아니라 파라미터, 리턴타입도)를 고려해야 한다. 사용되는 관점을 특히 주의매직 넘버, 매직 스트링 사용value object, 일급 컬렉션, enum의 공통점 → 의미 부여를 하고, 해당 의미를 잘 수행하도록(검증 등) 하는 공간(클래스)를 만드는 것조건/행위의 분기가 반복 될 때는 이 또한 다형성을 활용할 수 있다. 2. 읽는 이로 하여금 여러 번 생각하지 않아도 이해하기 쉽도록 한다.누구나 바로 이해할 수 있는 네이밍하기, 팀의 약속 준수추상화 레벨 맞추기부정어 쓰지 않기조건문은 early return을 통해 중첩된 사고를 하지 않도록 유도중첩 그 자체로 표현이 되는 경우에는 그대로 두는 것이 유리할 때도 있다. 다음주엔..솔직히 배웠던 내용들을 개념적으로는 이해를 한 것 같다. 특정 개념들은 감탄하며 재미있게 공부도 했었다. 이걸 코드 레벨에서 강의를 듣지 않고 다시 적용한다 생각하면 할 수 있을까 자신이 없다. 공부한 여러 고려 사항들을 적용해야 하는 부분을 발견하는 과정부터 연습을 다시 해봐야 실력이 늘 것으로 생각된다. 미션 수행 시 고려했던 사항early return메서드 파라미터로 표현되는 내용 네이밍에 포함 시키지 않기order클래스가 내부적으로 로직을 수행하고 메세지를 전달한다고 가정부정연산자 쓰지 않도록 고려한 네이밍 주어진 리턴 타입 준수하기 학습을 더 진행한 후다시 보니 조건/행위의 분기가 반복되는 형태인 것 같다. 어쩌면 다형성을 활용할 수 있겠지만, 리턴 타입이 true/false단순 두개 여서 과할 수도 있겠다는 생각이 들었다.리팩토링 하다보니 나도 모르게 검증 순서가 바뀌었는데, 이걸 함부로 고치면 기존 로직을 수정하지 못한다 가정 시 문제가 생길 수도 있다고 생각된다기존 : 주문 항목 검증 -> 사용자 정보 검증 -> 가격 검증수정 : 주문 항목 검증 -> 가격 검증 -> 사용자 정보 검증로그를 하는 것을 추출하고 싶다는 생각이 들었음출처강의 : Readable 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