블로그

sdsd988

Layered Architecture 구조의 테스트 작성 방법

인프런 워밍업 클럽 백엔드 2기 클린코드, 테스트 코드 참여 미션 수행 중 하나입니다!관련 강의 :Practical Testing: 실용적인 테스트 가이드( https://www.inflearn.com/course/practical-testing-%EC%8B%A4%EC%9A%A9%EC%A0%81%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C/dashboard)1. Layered Architecture ? 소프트웨어 개발 방법 중 하나로, 계층(Layer) 의 수에 따라 N-tier Architecture 부르기도 한다.계층은 어떻게 나누는 것인가? - 책임에 따라서 나눈다.강의에서는 3-tier Architecture 기준으로 나누었다. (Persistence, Business, Prsentation) 2. Layer(Persistence, Business, Prsentation) 1. Persistence Layer  책임 : 도메인(객체)의 생성과 검증 도메인의 생성과 관련된 책임을 가진 계층이다.애플리케이션에서 생성한 객체(Domain)이 데이터베이스에 올바르게 저장되는지 검증하는 과정이 필요하다. Persistent Layer 는 객체가 어떻게 저장되고, 어떤 파라미터를 필요로하는 지 이해할 수 있는 계층이다.테스트 코드 작성을 통해, Business 계층에서 사용될 객체들에 대해 이해를 가질 수 있는 계층이라 생각한다.뒤에 올 Business, Presentation 계층의 작성을 위한 기본적인 토대를 제공하는 계층이다.따라서, 이어질 계층에서 도메인에 대한 고민을 할 필요 없도록 테스트 코드를 작성해주는 것이 필요하다고 생각한다. 2. Business Layer 책임 : 도메인의 비지니스 로직 수행 객체의 생성과 검증이 완료된 토대에서, 서비스의 로직 을 수행하는 책임을 가진 계층이다.Persistent Layer에서 검증되고, 생성된 객체가 수행하는 책임을 검증하는 계층이라고 볼 수 있다.그렇기에, 코드의 양이 많고 테스트 코드 작성도 복잡해진다.Service(Business)은 Persistent(Repository) 의존한다. 즉 2개의 계층이 테스트에 필요해진다.중요하다고 생각하는 지점은, 이 계층은 비지니스 로직 에 집중해야 하는 계층이라는 점이다.객체의 생성과 관련된 코드는 감추고 , 로직과 관련된 코드는 표현되어야 한다.강의를 들으면서, 책임을 어떻게 분리할 것인가? 라는 의문이 생길 수 있다고 생각했다.비지니스 계층을 작성하면서, 도메인과 비지니스 그리고 로직과 로직 사이의 리팩토링이 많이 이루어 질 수 있다고 생각!  3. Presentation Layer책임 : 생성과 로직이 처리된 데이터의 출력 2 계층에서 생성되고, 처리된 데이터가 올바르게 생성되거나 프런트엔드에 올바르게 반환되는지 확인하는 책임을 가진 계층Business 계층에 의존한다. Business Layer 테스트에서 주의해야 할 점과 같이, Presentation Layer 검증에 집중해야 한다.하지만, Business Layer의 특징은 복잡하다는 것이 있었다. 대체(Mock)개념의 필요성Presentation Layer 계층의 테스트에 집중하기 위해 Mockito 를 활용하여, 가짜 객체를 생성하고 테스트에 도입한다.결국 데이터의 바인딩(Binding), 맵핑(Mapping) 에 집중해야 한다.API를 호출하면, 의도된 매개 변수를 받고 올바른 응답을 생성하는 과정을 검증할 수 있어야 한다.  

백엔드백엔드인프런워밍업클럽테스트스프링레이어드아키텍쳐

김민성

[워밍업 클럽 스터디 2기 - BE] (클린코드, 테스트코드) day 15 미션

 출처 : 인프런 워밍업 클럽 스터디 2기 - 백엔드 클린코드, 테스트 코드(Java, Spring Boot)Practical Testing: 실용적인 테스트 가이드 Layered Architecture 핵심은 관심사의 분리. 역할을 나누어 유지보수성을 늘린다. Persistence Layer데이터와 DB가 만나는 계층JPA, MyBatis 등등 결국 최종 쿼리를 DB로 전달해야 함.이 계층은 DB에 쿼리를 전달하는 역할만 해야지 여기서 뭔가 데이터를 가공한 후에 쿼리를 만들면 안됨.데이터 가공은 비즈니스 계층에서 하는 것이 바람직하다고 생각.테스트 시 스프링 컨테이너를 사용하지만 쿼리 자체를 테스트하는 것이기 때문에 단위 테스트 성격을 띈다.테스트 방법@SpringBootTest를 사용 시 @Transaction이 없기 때문에 tearDown()을 구현@DataJpaTest 사용 시 @Transaction이 있기 때문에 이에 따른 트랜잭션 전파를 잘 고려해서 판단해야 함.이걸 고려하지 않고 테스트 작성 시 테스트는 잘 통과하는데 실제로는 안될 수도 있음.테스트 시 샘플 객체를 만들어서 저장 후 (`given`) 테스트할 메서드를 호출 (`when`)검증(`then`)시 단일 객체라면 그냥 비교하면 되는데, List같은 자료구조에 담긴 여러 객체라면 1. 먼저 사이즈를 체크2. extracting()으로 검사할 항목 체크3. contains*() + tuple(데이터)로 비교@Test @DisplayName("원하는 판매상태를 가진 상품들을 조회한다.") void findAllBySellingStatusIn() { // given Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000); Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500); Product product3 = createProduct("003", HANDMADE, STOP_SELLING, "팥빙수", 7000); productRepository.saveAll(List.of(product1, product2, product3)); // when List<Product> products = productRepository.findAllBySellingStatusIn(List.of(SELLING, HOLD)); // then assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD) ); } Business Layer핵심적인 비즈니스 로직을 처리하는 계층Persistence Layer와 상호작용한다.트랜잭션이 보장되어야 한다.테스트 방법Persistence Layer를 묶어서 통합 테스트Persistence Layer를 Mocking 해서 단위 테스트Business Layer는 트랜잭션이 중요하기 때문에 테스트시에도 이에 따른 트랜잭션 전파를 잘 고려해서 판단 해야 함.  Presentation Layer클라이언트로부터 데이터를 받아 Business Layer로 요청을 넘기는 계층데이터에 대한 기본적인 검증을 수행 (필수 입력 값, 데이터 타입, null 등)Business Layer로부터 받은 데이터를 사용자에게 반환Business Layer가 Presentation Layer를 모르도록 설계하면 좋다.Business Layer에 클라이언트에게 받은 데이터를 바로 넘기는 것 보다 서비스 전용 DTO로 래핑해서 넘기기.클라이언트에게 값을 넘겨줄 때는 정해진 포맷(errorCode, message, data 등)으로 넘겨주는 것이 좋다.테스트 방법데이터에 대한 기본적인 검증을 수행Business Layer는 Mocking해서 테스트 진행mockMvc를 활용하여 Http 응답에 대한 검증을 진행한다.@Test @DisplayName("신규 주문을 등록한다.") void createOrder() throws Exception { // given OrderCreateRequest request = OrderCreateRequest.builder() .productNumbers(List.of("001")) .build(); // when // then mockMvc.perform(post("/api/v1/orders/new") .content(objectMapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON) ) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value("200")) .andExpect(jsonPath("$.status").value("OK")) .andExpect(jsonPath("$.message").value("OK")); }  

백엔드테스트코드워밍업클럽미션

zooxop

인프런 워밍업 스터디 클럽 2기 백엔드(클린코드&테스트코드) Day 15 미션

인프런 워밍업 클럽 2기, 백엔드(클린코드&테스트코드) 과정에 참여하고 있습니다.이번 글은 Day 15 미션 제출을 위해 작성하였습니다.[미션 내용]Layered Architecture 에서 각 레이어별 어떤 특징이 있고, 어떻게 테스트를 하면 좋을지에 대해 나만의 언어로 표현해보자강의 링크: Readable Code: 읽기 좋은 코드를 작성하는 사고법 Layered ArchitectureLayered Architecture는 관심사와 책임의 분리를 위해 만들어진 계층 구조 아키텍쳐이다. 관심사를 기준으로 책임을 나누면, 각 계층의 응집도를 높이고 결합도를 낮춘, 유지보수에 용이한(확장이 쉬운) 코드를 작성할 수 있게 된다는 이점을 얻을 수 있다.Spring 진영에서 일반적으로 얘기하는 Layered Architecture는 보통 아래와 같이 3-tier(3 계층)로 구성되어 있는 아키텍쳐이다. Persistence Layer (영속성, 데이터베이스 레이어)Data Access의 역할을 한다. ~~Repository 비즈니스 가공 로직이 포함되어서는 안된다.Data에 대한 CRUD에만 집중한 레이어4-tier 구조의 Database Layer 가 3-tier 구조에서는 이 레이어에 포함된다.Business Layer (비즈니스 레이어)비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개시킨다.트랜잭션을 보장해야 한다.작업 단위에 대한 원자성 Presentation Layer (사용자 요청&응답 레이어)외부 세계의 요청을 가장 먼저 받는 계층 파라미터에 대한 최소한의 검증을 수행한다. 각 Layer 별 테스트 방법 공통적으로 테스트 코드를 작성할때는 BDD(Behavior-Driven Development) 스타일로 작성하는 것을 권장한다. 이는 테스트 코드의 가독성을 높여준다는 장점이 있다. BDD는 given/when/then 구조로 작성하는 것을 기본으로 한다.테스트 시나리오는 가급적 작은 코드 단위를 독립적으로 검증하는 것을 목표로 하는것이 좋다. 검증 속도가 빠르고, 외부에 의존을 하지 않기 때문에 안정적으로 테스트를 수행할 수 있게 한다. 그리고 해피 케이스와 예외 케이스를 도출 해낼수 있어야 한다. 이를 위해선 테스트를 작성할 때 항상 "암묵적이거나, 아직 드러나지 않은 요구사항이 있는가?" 라는 질문을 끊임없이 스스로에게 던지는 습관을 기르는 것이 도움이 된다.또, given 코드를 작성할 때는 가급적 경계값이 되는 데이터를 사용하여 긍정 케이스와 부정 케이스를 각각 검증하도록 시나리오를 작성하는 것이 좋다. 여기서 말하는 경계값이란, 성공과 실패 조건의 경계에 걸쳐 있는 값을 의미한다. 예를 들어, 주어진 값이 10 이상일 때 부터 성공하는 케이스에 대해 실패 테스트에는 9를, 성공 테스트에는 10을 사용하는 것을 말한다. Persistence LayerJPA Repository에 대한 단위 테스트를 수행하기 위해서 @DataJpaTest 애노테이션을 사용한다. @DataJpaTest 는 JPA와 관련된 설정만 로드하기 때문에 최소 비용으로 JPA 테스트를 수행할 수 있게 해주고, @Transactional 을 기본적으로 내장하고 있으므로 매 테스트 코드가 종료되면 자동으로 DB가 롤백되도록 해준다.다만, 의존을 주입받기 위해서는 @AutoWired를 사용해야 한다는 것을 주의해야 한다.@ActiveProfiles("test") @DataJpaTest class UserRepositoryTest { @Autowired private UserRepository userRepository; @DisplayName("User 이름으로 User 를 찾을 수 있다.") @Test public void findUserByName() { // given User givenUser = new User("alex", "alex@example.com"); userRepository.save(givenUser); // when User foundUser = userRepository.findByName(givenUser.getName()); // then assertThat(foundUser.getName()).isEqualTo(givenUser.getName()); } }위 예시는 아주 단순한 JPA Repository의 쿼리 메서드를 테스트하는 예제이다. 예시를 위해 최대한 간단하게 작성한 것이므로, 위와 같은 Hibernate 라는 거대한, 검증된 라이브러리가 제공해주는 쿼리 메서드에 대한 테스트는 굳이 매번 작성할 필요는 없다고 생각한다. 그러니 스스로가 직접 작성한 쿼리 메서드에 대한 테스트를 꼼꼼하게 하는것에 집중하는 것을 권장한다.참고로, @ActiveProfiles("test") 는 테스트 코드 실행 영역에서는 실제 DB가 아닌 테스트용 DB로 연결되도록 하기 위해 추가해준 애노테이션임을 알아두자. Business LayerService 코드에 대한 테스트를 수행하기 위해서는 JPA Repository에 대한 의존성이 추가로 필요하게 된다. 이를 위해 @SpringBootTest 애노테이션을 사용해서 자동으로 의존성 주입을 받고 코드를 간단하게 작성할 수 있지만, @SpringBootTest는 간편한 만큼 많은 비용이 들어간다는 점을 항상 염두에 두어야 한다.다음은 @SpringBootTest 를 사용한 Business Layer 테스트 코드 예제이다.@ActiveProfiles("test") @SpringBootTest class UserServiceTest { @Autowired private UserService userService; @Autowired private UserRepository userRepository; @AfterEach void tearDown() { userRepository.deleteAllInBatch(); } @DisplayName("유효 상태인 유저를 모두 조회할 수 있다.") @Test void findAllUserByValid() { // given User user1 = new User("testUser", "test@example.com", true); // 이름, 이메일, 유효상태 User user2 = new User("testUser", "test@example.com", false); userRepository.saveAll(List.of(user1, user2)); // when List<User> foundUsers = userService.findAllUserByValid(); // then assertThat(foundUsers).hasSize(1) .extracting("name", "email", "isValid") .contains( tuple("testUser", "test@example.com", true) ); } }위 예제에서 @Transactional 을 사용하지 않고 @AfterEach 를 통해 매 테스트마다 Repository의 데이터 클렌징을 수행하도록 코드를 작성하였는데, 이는 휴먼 에러를 줄여주기 위한 코딩 습관과 연관이 있다.만약, 실수로 실제 Service 코드에서 데이터를 저장, 수정, 삭제하는 메서드(또는 클래스 레벨)에 @Transactional 을 달아주지 않았다면 트랜잭션 관리의 부재로 인해 개발자가 의도하지 않은 에러가 발생할 수 있는 가능성이 생긴다. 만약 이 때 테스트 코드에 @Transactional 을 달아서 테스트를 수행했다면 테스트 시점에는 그 에러를 발견할 가능성이 희박해진다. 따라서, 개발자가 Service 레이어 코드에 @Transacional 애노테이션을 빼먹는 실수를 테스트 코드에서 잡아낼 수 있는 가능성을 열어주기 위해, 테스트 코드에서 @Transactional 대신 tearDown 클렌징 기법을 사용한 경우인 것이다. 그리고 @SpringBootTest 를 사용하지 않고 테스트를 작성하기 위해서는 Repository 코드를 @Mock 으로 작성하면 된다. @InjectMocks 애노테이션이 붙어있는 UserService 객체가 생성될 때, UserRepository 에는 @Mock 이 붙어있으므로 대안(가짜) 객체가 생성되어 주입되게 된다. 따라서 테스트 메서드 내부에서 해당 쿼리 메서드의 행동을 함께 정의해주는 방법으로 테스트를 작성할 수 있다.@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @DisplayName("유효 상태인 유저를 모두 조회할 수 있다.") @Test void findAllUserByValid() { // given User user1 = new User("testUser1", "test1@example.com", true); User user2 = new User("testUser2", "test2@example.com", false); List<User> mockUsers = List.of(user1, user2); when(userRepository.findAll()).thenReturn(mockUsers); // when List<User> foundUsers = userService.findAllUserByValid(); // then assertThat(foundUsers).hasSize(1) .extracting("name", "email", "isValid") .contains( tuple("testUser1", "test1@example.com", true) ); } }  Presentation LayerController 코드에 대한 테스트는 @WebMvcTest 애노테이션과 MockMvc 객체를 활용해서 를 수행할 수 있다. 이를 통해 API 엔드포인트의 동작, HTTP 상태 코드, 헤더, 요청과 응답에 대한 검증을 할 수 있다.@WebMvcTest(UserController.class) public class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test public void testGetUser() throws Exception { User user = new User(1L, "testUser", "test@example.com"); when(userService.getUserById(1L)).thenReturn(user); mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("testUser")); } }Presentation Layer는 Service 코드에 대한 의존성이 필요하게 된다. 마찬가지로, Mock 객체로 대체해서 테스트를 수행해서 Controller 로직만을 독립적으로 검증해야 한다. 이 때 Business Layer와의 차이점은, @Mock 이 아닌 @MockBean 을 사용해서 의존성을 주입받을 수 있다는 것인데, 이는 @WebMvcTest 는 Spring Boot 테스트 프레임워크의 전반적인 설정과 부트스트래핑 과정을 포함하고 있기 때문에 가능한 것이다.

백엔드워밍업클럽백엔드미션클린코드테스트코드

[Practical Testing: 실용적인 테스트 가이드] 회고 3주차

출처 : Practical Testing: 실용적인 테스트 가이드 학습 내용 요약lombok 주의사항@Data,@Setter,@AllArgsConstructor 지양양방향 연관관계 시 @ToString 순환 참조 문제섹션3 단위 테스트테스트케이스 세분화경계값 테스트예외 테스트  테스트 어려운 영역 분리하기관측할 때마다 다른 값에 의존현재 시간랜덤값전역 변수/함수사용자 입력외부 세계에 영향을 주는 코드표준 출력메시지 발송데이터베이스 기록 등테스트 하기 좋은 코드순수함수같은 입력에는 항상 같은 결과외부와 단절  섹션4 TDDRGBRED : 실패하는 테스트 작성GREEN : 테스트만 통과하는 최소한의 코드Blue : 코드 개선 및 테스트 통과 유지 선 기능 구현 후 테스트 작성테스트 누락 가능성특정 케이스(해피 케이스)만 검증할 가능성잘못된 구현을 늦게 발견할 가능성  선 테스트 후 기능 구현복잡도가 낮은(유연, 유지보수 쉬운) 테스트 가능한 코드로 구현하게 된다쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다구현에 따른 빠른 피드백(테스트 결과 확인)과감한 리팩토링 가능섹션5 테스트는 []다DisplayName을 섬세하게명사 나열보다는 문장으로[X] 음료 1개 추가 테스트 (~테스트 지양)[O] 음료 1개를 추가할 수 있다[OO] 음료 1개를 추가하면 주문 목록에 담긴다 (테스트 행위 결과까지 기술)도메인 용어 사용[X] 특정 시간 이전에 주문을 생성하면 실패한다[O] 영업 시작 시간 이전에는 주문을 생성할 수 없다. (도메인 용어 사용하여 추상화된 내용 담기)테스트 현상 중점으로 기술 XBDD 스타일로 작성하기 TDD에서 파생된 개발 방법함수 단위 테스트 보다는 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨) 권장  Given / When / ThenGiven시나리오 진행에 필요한 모든 준비(데이터 생성)When시나리오 행동 진행Then시나리오 진행에 대한 결과 명시, 검증 섹션6 Spring & JPA 기반 테스트엔티티 생성 테스느는 id NotNull 체크assertThat(orderResponse.getId()).isNotNull();목록 조회 테스트는 개수, 데이터 값 일치 확인assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD) );@SpringBootTest vs @DataJpaTest@Transactional 유무 차이@Transactional이 없는 class 테스트에 @Transactional을 사용하는 경우 테스트 신뢰성이 깨질 수 있음 

성규

[워밍업 클럽 스터디 2기 :: BE 클린코드 & 테스트] 4주차 발자국

MockMvcMock(가짜) 객체를 사용해 스프링 MVC 동작을 재현할 수 있는 테스트 프레임워크  Test DoubleDummy아무 것도 하지 않는 깡통 객체Fake단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체Stub (상태검증)테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체 그 외에는 응답하지 않는다.SpyStub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체처럼 동작 시키고 일부만 Stubbing 할 수 있다.Mock (행위검증)행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체BDDMockitoBDD : 행동 주도 개발Mockito 를 한번 감싸서 호출하는 것 뿐한 문단에 한 주제!완벽하게 제어하기DateTime.Now 처럼 제어하지 못하는 것 보다는 제어 할 수 있는 부분으로 변경외부 시스템을 이용해야하는 경우는 Mock 을 활용하여 처리테스트 환경의 독립성을 보장하자테스트는 given 에서 문제가 발생하지 않도록, 문제는 when, then 에서 나게 유도 생성자를 활용하여 처리하는 걸 추천테스트 간 독립성을 보장하자공유자원을 활용하여 처리를 하는 경우 서로에게 영향이 갈 수가 있음한 눈에 들어오는 Test Fixture 구성하기Fixture : 고정물, 고정되어 있는 물체테스트를 위해 원하는 상태로 고정시킨 일련의 객체@BeforeAll, @BeforeEach 는 아래 질문에 부합할 때 사용!! 테스트 내용을 히애하는데 문제가 없는가? 수정해도 모든 테스트에 영향을 주지 않는가??테스트에 필요한 것만 생성을 할 수 있도록 간편하게 적용테스트에서 중복으로 사용하게 되는 함수나 클래스등을 모아서 처리 하는 방식은 모아둔 곳이 거대해져서 더 안 좋아 질 수도 있음deleteAllInBatch연관관계와 다르게 순서를 안 지키면 삭제 안됨DELETE 만 발생deleteAll전체 조회 후 외래키와 관계된 데이터를 건마다 삭제ALL select → EACH delete다수의 쿼리로 인해 많은 비용이 발생@Parameterized Test단 하나의 테스트 인데 값을 바꿔가면서 테스트 하고 싶을 떄@DynamicTest시나리오 작성하듯이 순차적으로 테스트 발생 시킬 수 있음테스트 수행도 비용이다!통합테스트를 하게 되면 스프링부트가 중복으로 많이 호출 되게 됨같은 @SpringBootTest 라도 조금이라도 다르면 다시 호출 됨새롭게 스캔해야하는 경우에도 다시 호출 ex) MockBeanMockBean 을 사용하는 것과 안하는 것 두개를 나눠서 처리 해도 됨서버를 뜨는 시간을 줄이는 것이 테스트 양이 많아 질수록 이득Private 메서드의 테스트는 어떻게 하나요??테스트를 할 필요가 없다(?)객체를 분리할 시점인가?? 확인Public Method 를 테스트 하다보면 Private Method 는 자연스럽게 검증이 됨테스트에서만 필요한 메서드가 생겼는데 프로덕션 코드에서는 필요 없다면??만들어도 된다. 하지만 보수적으로 접근하기

백엔드

[워밍업 클럽 2기] Day 15 - 레이어 별 특징과 테스트 방법

워밍업 클럽 2기: Clean Code & Test Code의 Day 15 미션입니다. 🗄 Persistence(Data Access) 계층특징데이터 소스와의 상호작용을 담당한다데이터를 저장하고, 읽고, 업데이트하고 삭제하는 CRUD 작업을 수행한다데이터 JPA, JPQL, JDBC, 등의 여러 기술이 사용될 수 있다비즈니스 로직이 포함되면 안된다테스트 방법어느 기술을 사용하든, 신뢰성있는 테스트를 위해서 레포지토리의 테스트를 하는 것을 권장한다@SpringBootTest 또는 @DataJpaTest를 사용해서 데이터베이스에 CRUD 작업이 제대로 수행 되는지 테스트 한다@SpringBootTest와 @DataJpaTest의 차이를 알자 🚀 Business 계층특징핵심 비즈니스 로직을 처리하는 계층Persistence(Data Access)와의 상호작용을 통해 비즈니스 로직을 수행한다트랜잭션이 보장되어야 한다읽기 전용 트랜잭션과 CUD 트랜잭션(Command)을 적재적소에 적용하면 좋다트랜잭션의 종류에 따라 추후에 서비스를 나누는 것도 생각할 수 있다테스트 방법Persistence 계층까지 묶어서 통합 테스트를 하는 방식으로 테스트하거나, Persistence 계층을 모킹해서 단위 테스트 방식으로 테스트 한다.테스트 간 격리를 위해서 레포지토리를 초기화하는 로직이 필요하다@BeforeEach, @AfterEach, 등을 사용한 setUp이나 tearDown 메서드를 만들어서 레포지토리를 비운다@Transactional을 사용한다(서비스에 @Transactional이 붙어있지 않아도 동작한다. @Transactional의 효과에 대해 잘 파악하고 있어야 한다.) 💻 Presentation 계층특징클라이언트로 부터 입력을 받아서 비즈니스 계층으로 해당 요청을 보내는 계층요청을 제일 먼저 받는 계층입력 데이터에 대한 기본적인 검증을 수행한다Presentation 계층에서의 검증과 Business 계층에서의 검증을 분리해서 생각해야 한다Presentation 계층에서는 보통 형식적인 검증을 한다(예시: 필수 입력 값 검사, 데이터 타입 검사, null 검사, 빈 문자열 검사, 등...)Business 계층에서는 비즈니스 로직에 따른 도메인 유효성 검사가 이루어진다Business 계층으로 부터 결과를 받아서 클라이언트로 반환한다컨트롤러에서 사용하는 요청 DTO가 서비스 계층으로 침투하지 못하도록 컨트롤러 계층에서 서비스 전용 DTO로 변환하는 것을 권장한다(상황에 따라 다를 수 있을것 같다. 만약 받는 포맷이 변할 가능성이 거의 없다면, 그냥 컨트롤러의 DTO를 쭉 사용해도 괜찮지 않을까 생각이 된다.)테스트 방법Business, Persistence 계층을 모킹해서 테스트한다MockMvc 같은 도구를 사용해서 HTTP요청과 응답을 시뮬레이션 한다모킹을 위해서 Mockito 같은 프레임워크를 사용할 수 있다 🔍 참고Readable Code: 읽기 좋은 코드를 작성하는 사고법

백엔드워밍업클럽2기테스트코드Day15미션

예진안

인프런 워밍업 클럽2 cs <day11>

운영체제가상메모리 | 세그먼트, 페이징RAM | 가변분할, 고정분할(세그먼트,페이징이랑 가변분할이랑 고정분할이랑 갑자기 뭔차이지? 라는 생각이 들어서 이전 수업 듣고 까먹지 않게 적어놨다)(가상메모리 ram 차이가아니고 그냥 가변분할은 어떤 종류다~ 말하는거같기도하고)가상메모리 개요컴퓨터마다 메모리의 크기가 달라, 작업할 프로세스크기 > 메모리 일 경우 -> 실행불가문제 해결 방법 : 가상메모리를 사용. 메모리 관리자가 프로세스크기와 할당할 번지를 신경쓰지 않도록 0x0 번지부터 할당한다고여기게 한다.메모리 관리자 덕분에 프로세스와 메모리는 직접적으로 부딪힐 일이 없다.? 가상메모리 안에 메모리 관리자가 들어있나여? 사진이 안에 들어가있네 가상메모리 관리자라고 해야되나가상메모리 크기는 이론적으로는 무한, cpu 비트 수 + 메모리 크기에 정해진다.프로세스 처리 양 > 메모리 크기 일때처리하지 못한 프로세스를 하드디스크에 있는 스왕엽역으로 이동처리가 필요할 때 물리 메모리로 가져와서 실행 시킨다.- 32bit = 4GB => 메모리 크기도 4GB- 메모리 크기가 4GB 일 때, 프로세스 4GB *5 개 작업해야한다. => 디스크의 스왑영역으로 이동동적 주소변환 (DAT) : 메모리 관리자는 물리메모리와 스왑여역까지 합쳐 프로세스가 사용하는 가상주소를 물리주소로 변환실제 0x0 번지 물리주소에는 운영체제 영역 -> 프로세스들은 사용 x가상메모리 시스템에서는 나머지영역을 가변분할 방식, 고정분할 방식으로 나뉜다.-가변분할 (세그먼테이션) / 고정분할방식 (페이징) / 페이지드 세그먼트 (혼용)메모리 관리자는 가상주소 - 물리주소 => 1대1 매핑 테이블로 관리세그멘테이션 (배치 정책)세그멘테이션 -> 프로세스를 함수, 모듈 등으로 세그먼트를 구성한다.사용자 입장 : 세그먼트들(함수 + 모듈 ..)은 서로 인접하지 않는다.프로세스 입장 : 영역들이 서로 인접해 있다.논리주소논리주소는 사용자, 프로세스 CPU 사용논리주소 -> 물리주소 변환은 메모리관리자가 담당메모리 관리자는 세그멘테이션 테이블을 가지고 물리 메모리 주소를 계산한다.논리주소 -> 물리주소 변환 과정cpu에서 논리주소 전달 -> 메모리 관리자가 몇번 세그먼트인지 알아낸다-> 메모리 관리자 내에 있는 Segment Table Base Register 를 이용해서 물리 메모리 내에 있는 세그멘테이션을 찾는다.(여기서 STBR 와 세그멘테이션 테이블은 다르다. STBR에서 세그멘테이션을 찾고 찾은 걸로 세그먼트번호 참조)-> 세그먼트 번호를 인덱스로 Base Address, BoundAddress를 찾는다.(운영체제는 컨텍스트 스위칭이 일어날 때마다 STBR의 내용을 해당 프로세스의 값으로 바꿔줘야한다.)-> Bound Address는 메모리의 크기를 나타낸다.메모리 관리자는 cpu에서 받은 논리주소와 Bound Address의 크기를 비교한다.-> 논리주소 < Bound Address 일 경우, 논리주소 + Base Address = 물리주소 구하기.논리주소 > Bound Address 일 경우, 메모리를 침범했다고 생각하고 에러를 내보낸다.세그먼트 3번이 0x750번지로 접근cpu의 요청을 받은 메모리 관리자, STBR 을 이용해서 물리 메모리 내에 있는 세그멘테이션을 찾는다.-> 세그먼트 번호를 인덱스로 세그멘테이션 테이블을 참조한다. 3번 세그먼트, BA 500-> 논리주소 > Bound Address 이므로 에러를 발생시킨다. 세그멘테이션 장점 :메모리를 가변적으로 분할할 수 있다.코드영역,데이터영역,힙,스택 모두 모듈로 처리가능 => 각 영역의 공유와 접근보호가 쉽다.세그멘테이션 단점 :외부단편화 발생페이징(배치정책)고정분할 방식 사용메모리 할당 시 정해진 크기로 페이지를 나눈다. =>관리하기 쉬움, 외부단편화 발생하지 않음논리주소공간에서는 페이지, 물리주소 공간에서는 프레임이라고 부른다.페이징 주소변환페이지 넘버 (인덱스 ) = 논리주소 / 페이지크기오프셋 = 논리주소 % 페이지크기메모리관리자는 페이지테이블을 가진다.CPU 32bit , 논리주소공간 4GB 물리주소 공간 2GB (논리 > 물리 크기를 설정함으로써 스왑영역을 사용하는 걸로 가정)논리주소공간을 16MB 256페이지로 나눈다.물리주소 공간(2GB) -> 16MB * 128 로 나눈다CPU가 논리주소를 메모리 관리자에게 넘긴다.-> 메모리관리자 안에 있는 PageTableBaseRegister를 이용해서 페이지 물리 메모리에 있는 페이지 테이블을 찾는다.-> 페이지 번호는 인덱스로, 프레임 번호를 알아낸다.-> 오프셋을 이용해 물리주소로 변환한다.ex 1번 인덱스의 프레임 번호는 1번. 프레임번호 + 오프셋 = 물리주소 => 1477217(페이지 테이블에 프레임 = Invalid 일경우 스왑영역에 저장된 부분임)(PTBR은 운영체제가 컨텍스트 스위칭할 때 마다 해당 프로세스의 것으로 업데이트해준다)세그멘테이션 vs 페이징세그멘테이션 : 프소세스마다 크기가 달라서 bound Address가 필요하다.페이징 : 모든 페이지가 크기가 동일해서 Bound Address가 필요하지 않다.페이징의 내부단편화세그멘테이션 : 논리적인 영역별로 세그먼트를 나눈다.페이징 : 논리적인 영역별로 나누기보다 정해진 크기로 나눈다. 특정 영역만 공유하거나 권한 부여가 어렵다.페이지 테이블의 크기를 얼만큼씩 나눌지가 제일 중요하다,프로세스 많아질수록, 페이지테이블 늘어난다. => 프로세스가 실제로 사용할 수있는 메모리영역🔽페이지 테이블도 물리 메모리의 운영체제 영역에 저장되어 있다.-> 페이지 테이블이 크면 사용자 영역이 부족해진다STBR로 세그멘테이션을 찾는다고 했는데 그럼 세그멘테이션이 겁나 많아서 세그멘티에션 테이블도 많다는 건가? 위와 같은 맥락인 것 같다.페이지드 세그멘테이션(배치정책)세그멘테이션 + 페이징 장점 취합세그먼트로 나눠 관리해서 다른 프로세스와 공유, 권한 부여가 쉽다.오버헤드가 적고 페이지 크기를 정해 나눠서 할당 시킨다.메모리 접근 권한 : 메모리 특정번지에 부여된 권한읽기(READ), 쓰기(Write), 실행(EXECUTE)프로세스는 영역마다 접근권한이 있다.CODE영역 : 프로그램 그자체. 수정되면 안됨. 읽기 실행권한DATA 영역 : 일반변수, 전역변수, 상수로 선언한 변수가 저장. 읽기권한 쓰기권한은 없거나 있다.HEAP,STACK영역 : 읽기 쓰기 권한메모리 접근 권한 검사 : 논리주소에서 물리주소로 변환할 때마다 실시된다.권한비트를 추가해서 검사를 한다.권한이 없는 메모리가 접근 시 에러를 발생시킨다.페이지드 세그멘테이션 : 세그멘테이션 테이블 + 페이지 테이블 사용권한비트 , Bass Addres(페이지넘버), Bound Address(페이지개수) 로 변환 시킨다.cpu에서 논리주소를 메모리 관리자에게 넘긴다.-> 세그먼트를 찾는 방식으로 세그먼트를 찾는다.-> 권한비트로 세그먼트 권한을 위반하는지 검사한다.(권한 위반 시 프로세스 종료)-> 페이지 넘버 = 인덱스. 페이지 넘버에서 인덱스를 참조해서 프레임을 알아낸다.페이지드 세그먼트 단점 : 세그멘테이션테이블과 페이지 테이블을 참조할 때 총 메모리 접근이 두 번이다.=> 현재 운영체제는 페이지드 세그먼트와 페이징 기능을 적절히 섞어 작업한다. 알고리즘삽입 정렬정렬된 영역 맨 끝 원소와 정렬되지 않은 영역 첫 원소와 크기를 비교하여 정렬하고정렬되지 않은 영역에서 비교하는 원소A > 정렬된 영역 원소B 경우 => 정렬된 영역 원소 다음 원소와 비교해당원소 자리에 정렬된 영역 원소B 복사정렬되지 않은 영역에서 비교하는 원소A < 정렬된 영역 원소B 경우 => 정렬된 영역 원소 이전 원소에 A 삽입function InsertionSort (arr) { for(let i =1; i< arr.length; ++i){ //정렬되지 않은 배열. 첫번째원소는 정렬되었다고 가정 let insertingData = arr[i]; for(let j =i-1; j>=0;--j){ //정렬된 배열 if(arr[j] > insertingData ){ arr[j+1] =arr[j]; }else{ break;// for문탈출 } } arr[j+1] = insertingData; } }오늘의 질문메모리 관리자는 어디에 위치해있나? cpu안? 혹은 메모리마다 가지고 있나?cpu 와 메모리 관리자는 따로 존재한다.CPU가 메모리에 접근하는 것을 관리하는 컴퓨터 하드웨어 부품 이라고 한다.테이블은 메모리에 할당되어있다고 하지만 그럼 STBR이랑 PTBR은 메모리 관리자 안에 있는데 어캐그럼? 험~ 

알고리즘 · 자료구조운영체제알고리즘인프런워밍업클럽2기

예진안

인프런 워밍업 클럽2 cs <day13>

알고리즘퀵정렬퀵정렬은 병합정렬과 같이 분할 정복 알고리즘으로~재귀를 사용한다.피벗 : 첫 피벗은 첫번째 인덱스left, right : 각자 왼쪽 오른쪽 끝에 있는 것rightStartIndex : 왼쪽으로 이동, 피벗보다 작은 값을 만나면 멈춘다.leftStartIndex : 오른쪽으로 이동, 피벗보다 큰 값을 만나면 멈춘다.rightStartIndex와 leftStartIndex가 조건에 의해 멈췄을 때 두값을 스왑하여 바꾼다.rightStartIndex와 leftStartIndex가 지나칠 때 멈춘다. rightStartIdnex값과 피벗 위치를 바꿔 피벗은 5, rightSI는 4가된다.-> 피벗을 기준으로 왼쪽은 피벗보다 작은 값, 오른쪽은 피벗보다 큰 값으로 나뉘게되고나눠진 그 배열을 또 위에서 정렬한 것처럼 반복피벗을 기준으로 배열을 반으로 나눈다 -> O(log n) 이작업을 데이터 n개만큼 반복해야하므로 n을 곱한다. -> O(nlog n)최악의 경우 : 피벗이 가운데가 아니고 한쪽으로쏠림 -> O(n^2)근데 평균성능보는거라 Θ(nlogn) Θ는 세타병합이랑 퀵이랑 비교했을 때 병합이 더 좋아보이지만 퀵은 적은비교 적은 메모리사용으로 퀵이짱인것이다~ 운영체제주변장치내부구조를 보자면...레지스터들 : 입출력 작업 시에 데이터를 저장하는 역할 이 값들은 메모리에 이동되기도 한다.세가지 버스를 따로 받을 수있다. Address, Data, Control캐릭터 디바이스와 블록 디바이스 데이터 전송단위가 캐릭터(글자)이거나 블록 단위캐릭터 디바이스 특 : 데이터 전송단위가 캐릭터로 상대적으로 작다. 마우스,키보드블록 디바이스 특 : 데이터 전송단위가 블록단위로 캐릭터보다는 크다. 하드디스크 ,ssd 주변 장치들은 메인보드 내의 버스로 연결된다. 하나의 버스였지만 I/O작업을 수행하기위해서 입출력 제어기와 여러 개의 버스가 추가됐다.(CPU가 I/O 작업때문에 다른작업을 못했기때문)CPU는 I/O작업을 만나면 입출력 제어기에게 입출력 작업을 맡기고 다른 작업을 실행하러간다.입출력제어기 - 시스템버스와 입출력 버스로 구분 시스템버스는 CPU, 메모리가 사용하고 입출력 버스는 주변장치들이 사용입출력버스 - 고속입출력, 저속입출력->속도차이로인한 병목현상을 해결그래픽카드는 주변 장치이지만 대용량의 작업을 실행하기때문에 시스템 버스를 사용한다.입출력 제어기는 여러 주변장치를 처리하는데 입출력 버스에서 온 데이터를 메모리로 옮겨야한다. -> 입출력 제어기가 메모리 접근을 위해 CPU를 거쳐야되는데 CPU 도움없이 DMA제어기를 추가시켰다.Direact Memory Access : cpu없이 입출력 제어기가 메모리에 접근할 수있도록 한다.Memory Mapped I/O : cpu와 DMA 제어기가 사용하는 메모리 영역을 나눠 혼돈을 막는다.마우스광학마우스 , 키보드아래 카메라를 통해 초당 1500회를 찍어 디바이스 컨트롤러 안 DSP로 보낸다.x,y 축 좌표 움직임을 캐치한다.DSP가 움직임과 클릭을 감지했을 때 cpu에게 인터럽트를 발생시켜 마우스 드라이버가 동작해 데이터를 읽어간다.마우스 드라이버는 운영체제에게 이벤트 신호를 주고 운영체제는 이 이벤트를 foreground 애플리케이션으로 전달해준다.해당 애플리케이션은 받은마우스 이벤트를 처리한다. 하드디스크하드시스크 구조플레터 : 플레터는 여러개의 트랙으로 구성되었고 표면을 자성을 띈다. n극일경우 0, s일경우 1스핀들 : 플레터 자기화된 원판으로 이뤄졌다. 디스크암이 읽기/쓰기 헤드로 플레터의 표현을 read헤드 : 헤드는 여러 개 있고 항상 같이 움직인다. 이 헤드들은 여러 개의 플래터를 가리키게 된다.실린더 : 여러개의 같은 집합의 플래터에 있는 트랙의 집합을 실린더라고한다.트랙 : 여러 개의 섹터로 나뉜다.섹터 : 하드디스크의 가장 작은 단위유저프로세스가 하드 디스크의 특정 섹터에 접근하고 싶어서 이러한 요청을 보낸다.실린더 c로 가서 트랙 b에 있는 섹터 d를 읽어라seek : 디스크암은 헤드를 실린더 c로 이동seek time : 헤드를 실린더로 이동시키는데 시용하는 시간 트랙b의 섹터가 d 가 헤드에 닿을 때가지 스핀들ㅇ르 회전시키고 헤드에 섹터d가 읽히면 작업이 끝난다.다른 전자 기기 작업시간은 ns 단위지만 헤드 이동시간은 ms라 하드디스크가 느리다. 플레시 메모리(ssd)와 하드디스크자성을 띄는 하드디스크는 자석을 가져다대면 망가지지만 플레시 메모리는 그러지 않고 전기적으로 데이터를 읽어 조용하다.스핀들과 같은 회전축때문에 충격에 약한 하드디스크ssd 단점 : 특정 지점에 데이터를 썼다면 덮어쓰기가 불가능하다. 똑같은 지점에 데이터를 쓰고 싶으면 지워야하지만 메모리 지우기 기능 횟수가 정해져있다. ->똑같은 구간에 지우기 쓰기를 여러 번할 경우 망가질 수있다.    

알고리즘 · 자료구조알고리즘운영체제인프런워밍업클럽2cs

whffkaos007

워밍업 클럽 2기 BE 클린코드&테스트 : 미션 - Day 15

 이 글은 박우빈님의 강의를 참조하여 작성한 글입니다. 미션 - Day 15 미션 내용Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다. 레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요? 1. 계층별 특칭 Layered Architecture : Presentation Layer <-> Business Layer <-> Persistence Layer Layered Architecture 는 3가지 계층으로 구성된다.웹 클라이언트와 연결된 Controller 부분에 해당하는 Presentation Layer, 서비스의 비즈니스 로직을 처리하는 Business Layer, DAO에 해당하는 Persistence Layer가 있다. 이렇게 계층을 나누는 이유는 관심사의 분리이다. 관심사, 책임을 나누기에 테스트의 작성을 편리하게 하고 신뢰성을 높이고 유지보수 또한 수월하다.  Presentation Layer외부 세계의 요청을 가장 먼저 받는 계층파라미터에 대한 최소한의 검증을 수행한다. Business Layer비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용(Data를 읽고 쓰는 행위)을 통해 비즈니스 로직을 전개시킨다.트랜잭션을 보장해야 한다. Persistence LayerData Access의 역할비즈니스 가공 로직이 포함되어서는 안 된다. Data에 대한 CRUD에만 집중한 레이어   2. 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요? 하나의 모듈을 기준으로 독립적으로 진행되는 단위 테스트와 둘 이상의 여려 모듈이 협력하여 기능을 통합적으로 검증하는 통합 테스트가 있다.각 계층에 대한 테스트는 다음과 같이 진행하면 좋을 거 같다. Persistance Layer -> Spring과 통합한 단독 계층 테스트(Spring, Jpa 등 활용한 DB 접근), 단위 테스트 성격 Business Layer -> Persistence를 포함한 통합 테스트 Presentation Layer -> 이외 계층을 Mocking한 단독 계층 테스트, 단위 테스트 성격 강사님의 의견과 유사한 생각을 가지고 있다. 위와 같은 생각은 다음과 같은 이유 때문이다. 이유Persistence Layer는 DAO 관련 계층이다. 컨트롤러나 서비스 등 이외 계층과 협력할 필요가 없다. 그렇기에 스프링, JPA를 활용한 테스트를 진행할 수 밖에 없다. A와 B 기능이 각각 있을 때, 정상적으로 동작할 수 있다. 하지만 A + B 와 같이 사용된다면 실제 결과는 어떻게 나올지 모른다. 이러한 여러 모듈이 복잡하게 상호작용할 수록 이를 예측하는 건 더 어렵다. 이처럼 Business Layer 와 Persistence Layer를 통합한 테스트를 진행하면 더 높은 신뢰성을 보장할 수 있다.또한 Persistence Layer에 대한 테스트를 이미 작성했다면 굳이 Persistence Layer를 Mocking하지 않는 것이 더 비용적으로 합리적일 수 있다. Presentation Layer는 외부 세계와 연결된 계층이다. 외부 세계로 받은 정보에 대한 검증이 필요하다.도메인의 성격, 서비스에 대한 내용과는 무관하다. 따라서 굳이 도메인 관련 로직을 검증할 필요는 없다고 생각한다.물론 각 모듈이 상호작용할 때와 각각 작용할 때는 다른 결과값이 나올 수 있기에 3가지 계층에 대한 검증을 한 번에 하는 것이 더 신뢰성을 보장할 것이다.그렇지만 비용 또한 고려해야 하므로 수동 테스트로 마무리하거나 비용이 나오더라도 꼭 검증해야할 정도로 중요하다면 그때 모든 계층에 대한 통합 테스트를 작성하여 신뢰성을 보장하면 된다고 생각한다.    

백엔드테스트Layered-Architecture

대협

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

이 블로그는 정보근님의 입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 강의 기반으로 코드작성과 코드설명을 적었습니다 이번 2주차 발자국 내용은 spring 테스트 코드에 대한 내용이 대부분이다.아무래도 Spring에서 가장 중요한 요소는 테스트 코드라고 생각되어 강의를 들으면서 썼던 TEST 코드에 대해 하나하나씩 파헤쳐 볼 예정이다 !package com.hyup.portfolio.domain.repository import com.hyup.portfolio.domain.entity.Project import org.springframework.data.jpa.repository.JpaRepository import java.util.Optional interface ProjectRepository : JpaRepository<Project, Long> { fun findAllByIsActive(isActive: Boolean): List<Project> override fun findById(id: Long): Optional<Project> } 위 코드에서 궁금한 내용은 ?[ QUESTION ] findById를 왜 override를 해서 사용하는 이유 -> JpaRepository가 이미 제공하는 메서드를 다시 선언하기 때문에 오버라이드를 해야함-> override를 활용해서 재정의 하면 기능적으로 안좋다.[ WHY ?] override를 활용해서 재정의 하면 기능적으로 안좋은 이유1. 기본 동작의 일관성 문제JpaRepository에서 제공하는 findById는 기본적인 CRUD 동작을 안정적이고 효율적으로 처리하는데, 이를 재정의하면 예상치 못한 문제를 발생시킬 수 있음.다른 개발자들이 코드를 이해할 때, 어려움을 안겨줄 수 있음2. 유지보수성 저하JPA의 업데이트나 변경 사항이 발생했을 때 호환성 문제가 발생할 가능성이 높음기능적 안정성 감소 Spring Data Jpa는 트랜잭션 관리, 데이터베이스 연결 관리, 성능 최적화 등을 내부적으로 처리. 이를 재정의 하면 내부적으로 처리되던 여러 최적화나 기능이 무시될 수 있음 [ SOLVE ] 그럼 override를 활용하지 않고 어떤 방법을 사용할 수 있나?CUSTOM Repository 사용interface ProjectRepository : JpaRepository<Project, Long> { fun findAllByIsActive(isActive: Boolean): List<Project> override fun findById(id: Long): Optional<Project> }interface CustomProjectRepository { fun findByIdCustom(id: Long): Optional<Project> } 쿼리 메서드 활용interface ProjectRepository : JpaRepository<Project, Long> { fun findAllByIsActive(isActive: Boolean): List<Project> fun findByIdAndIsActive(id: Long, isActive: Boolean): Optional<Project> }@DataJpaTest 메서드@DataJpaTest 메서드를 타고 들어가면 @Transactional 이라는 어노테이션 존재@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @BootstrapWith(DataJpaTestContextBootstrapper.class) @ExtendWith(SpringExtension.class) @OverrideAutoConfiguration(enabled = false) @TypeExcludeFilters(DataJpaTypeExcludeFilter.class) @Transactional @AutoConfigureCache @AutoConfigureDataJpa @AutoConfigureTestDatabase @AutoConfigureTestEntityManager @ImportAutoConfiguration public @interface # DataJpaTest 어노테이션이 포함하고 있는 어노테이션 이중 @Transactional 이라는 어노테이션은 테스트 메소드 하나를 하나의 트랜잭션으로 보고 메소드가 종료될 때 그 트랜잭션에서 발생한 작업들을 모두 롤백해줌 [ QUESTION ] 트랜잭션에서 발생한 작업들을 모두롤백해야 하는 이유[ ANSWER ]테스트 간 독립성 보장테스트는 독립적으로 수행되어야 하고, 하나의 테스트가 다른 테스트에 영향을 주지 않아야 함. 트랜잭션을 롤백하면, 테스트 내에서 발생한 모든 데이터 변경이 원래 상태로 돌아가기 때문에 데이터베이스 상태가 항상 초기 상태로 유지데이터 일관성 유지테스트가 끝난 후에도 트랜잭션을 롤백하지 않으면, 테스트 중에 삽입되거나 수정된 데이터가 데이터베이스에 남아 있게 된다. 이렇게 되면 이후의 테스트나 실제 어플리케이션 실행에 영향을 미쳐, 데이터의 일관성이 깨질 수 있음테스트 성능 향상테스트 중 데이터베이스에 많은 데이터를 삽입하거나 수정하는 경우, 이를 실제로 영구 저장하는 것보다 트랜잭션을 롤백하는 것이 성능 면에서 유리.인스턴스 생명주기PER_METHOD (기본값)각 테스트 메서드마다 새로운 인스턴스가 생성각 테스트 메서드는 독립적인 상태 유지테스트마다 새로운 인스턴스가 생성되므로, 테스트 클래스의 상태를 공유할 수 없고 테스트 간의 의존성도 없어야 함.@TestInstance(TestInstance.Lifecycle.PER_METHOD) // 생략 가능 (기본 값) class ExampleTest { @BeforeEach void setUp() { // 테스트 메서드 실행 전 호출됨 } @Test void testA() { // testA 실행 시 새로운 인스턴스가 생성됨 } @Test void testB() { // testB 실행 시 또 다른 새로운 인스턴스가 생성됨 } }PER_METHOD는 각 테스트마다 독립적으로 실행되어서 서로의 상태에 영향을 주지 않지만 상태를 공유하는 것이 불가능하다. 2. PER_CLASS하나의 인스턴스만 생성되고, 모든 테스트 메서드에서 이 인스턴스가 재사용된다.@BeforeAll을 사용하면 클래스 전체에서 한 번만 실행되므로 성능을 향상시킬 수 있음@TestInstance(TestInstance.Lifecycle.PER_CLASS) class ExampleTest { @BeforeAll void initAll() { // 모든 테스트 메서드 실행 전 한 번만 호출됨 } @Test void testA() { // testA 실행 시 동일한 인스턴스가 사용됨 } @Test void testB() { // testB 실행 시에도 동일한 인스턴스가 사용됨 } }PER_CLASS는 상태를 공유할 수 있어 성능이 향상되지만 테스트 간 인스턴스 상태가 공유되므로, 테스트 간 의존성이 발생함.@BeforeAll, @AfterAll@BeforeAll : PER_CLASS에서만 사용되며, 모든 테스트 전에 한 번만 실행@AfterAll : PER_METHOD와 PER_CLASS 모두에 사용되고 모든 테스트 전에 한 번만 실행 됨[QUESTION] @BeforeAll 이 PER_CLASS에서만 사용되는 이유, @AfterAll은 PER_METHOD, PER_CLASS 모두 사용가능한 이유[ANSWER]@BeforeAll은 클래스 수준에서 한 번만 실행되는 메서드. 즉 테스트 클래스의 모든 테스트 메서드가 실행되기 전에 딱 한 번 실행됨PER_CLASS에서는 테스트 클래스에 대해 하나의 인스턴스만 생성되므로, @BeforeAll 메서드가 클래스 인스턴스에서 한 번 실행되고, 이후의 모든 테스트가 동일한 인스턴스를 사용@AfterAll 은 모든 테스트 메서드가 실행된 후에 한 번만 실행되는 메서드이다. 여기서 PER_CLASS에서는 클래스 인스턴스가 하나만 생성되므로, 모든 테스트가 끝나고 한번만 호출되면 된다.PER_METHOD에서는 각 테스트마다 새로운 인스턴스가 생성되지만, @AfterAll이 테스트 메서드가 모두 끝난 후에 한 번 호출되도록 관리할 수 있다.미션 - REST API 설계 이번 미션에서는 REST API 설계하는 미션이 주어졌다. HTTP 주요 메서드 정리 (GET / POST / PUT / PATCH / DELETE)GET : 리소스 조회POST : 요청 데이터 처리, 주로 등록에 사용PUT : 리소스를 대체, 즉 덮어쓰기 수행PATCH : 리소스 부분 변경DELETE : 리소스 삭제이번 미션과 발자국을 하면서 백엔드 개발자가 HTTP 주요 메서드에 대해 집중해야 된다는 것을 알 수 있었다. api를 설계할려면 어쩔수 없이 각 HTTP 메서드에 대해 알아야 URL을 설정할 때 어떻게 만들어야 되는지를 알 수 있기 때문이다. 이번주 는 개인 일정이 너무 많아서 아쉽게도 진도를 다 따라가지 못했다. 발자국은 만들어야 되기 때문에 여기서 내가 가장 공부해볼만한 것이 TEST 코드에 대해서 조사를 하는 것이기 때문에 열심히 하였다.GET, POST, PUT, PATCH, DELETE 메서드에 대해서 조금이라도 공부할 수 있어서 좋았고, 따로 공부해서 블로그에 올려야 겠다고 생각하였다!

백엔드백엔드인프런javakotlin발자국

워밍업 클럽 2기 백엔드 클린코드&테스트 코드 3주차 발자국

Practical Testing 실용적인 테스트 가이드 섹션 1~6 중반까지 듣고 작성한 발자국 입니다. 강의테스트 코드의 필요성에 대한 설명을 다른 사람의 언어로 들을 수 있는게 좋았습니다.강의에서 다루는 내용 뿐만 아니라 추가적으로 학습할 방향성을 제시해주어서 좋았습니다.테스트 코드가 문서로서의 역할도 한다는 의견이 생각지도 못한 부분이었습니다. 과거 진행했던 미션에서 요구 사항에서 제대로 이해되지 않았던 부분이 테스트 코드를 보면서 구체적으로 이해되었던 경험이 떠올랐습니다.테스트를 작성하는 역량주니어 개발자에게 가장 기대하는 요소 중 하나채용 시 구현 과제 등에서 테스트 작성 여부, 테스트 코드 구현 방식을 확인소프트웨어의 품질을 보증하는 방법으로, 그 중요성을 알고 있는지를 확인테스트 코드를 통해 우리가 얻고자 하는 것빠른 피드백자동화안정감테스트 코드를 작성하지 않는다면변화가 생기는 매 순간마다 발생할 수 있는 모든 Case를 고려해야 한다변화가 생기는 매 순간마다 모든 팀원이 동일한 고민을 해야 한다빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다 미션 : Study Cafe 테스트 코드 작성요구 사항이 변해도 테스트 코드는 일관성을 가지고 유지되는 것도 테스트 코드 작성에서 중요한 부분이라고 생각했습니다. 그래서 요구 사항이 변해도 변하지 않을 것 같은 기능들 위주로 테스트 코드를 작성했습니다.https://github.com/Frod90/readable-code/tree/mission/day12 회고여러가지 일정이 겹쳐 정신없는 한 주였습니다. 이번 주를 보내며 느낀 것은 '욕심 부리지 말고 할 수 있는 것들부터 하자' 입니다. 다음주는 이번 주보다 일정이 많아서 하지 말 것들을 미리 정해두고 시작하는 한 주를 시작해야 할 것 같습니다.

유진

3주차 - thymeleaf 정리 | 인프런 워밍업클럽 2기 - 백엔드

인프런 워밍업 클럽 2기입문자를 위한 Spring Boot with Kotlin(https://inf.run/bXQQQ) 강의를 듣고 작성하였습니다. thymeleafThymeleaf is a modern server-side Java template engine for both web and standalone environments.https://www.thymeleaf.org/타임리프는 스프링 프레임워크에서 사용하는 서버사이드 클라이언트 개발용 탬플릿 엔진입니다. 탬플릿 엔진은 html을 이용해 틀을 짜두고 서버에서 DB등으로 조회한 정보를 동적으로 끼워넣을 수 있게 작업합니다. Node.js에서는 pug나 EJS를 사용했던 기억이 있습니다. 친구집 강쥐 폴리 참고로 아래 코드를 가장 먼저 수정하면, IDE에서 타임리프 자동완성을 지원해주어서 좋았습니다.<html lang="ko" xmlns:th="http://www.thymeleaf.org"> 영역 반복th:each와 th:block 문법을 통해 프래그먼트 반복을 할 수 있습니다.  fragment중복되는 부분을 프레그먼트로 분리하여 삽입해서 사용할 수 있는 부분이 인상깊었습니다. React 기준으로는 컴포넌트랑 유사하다고 느꼈습니다.  내용대체th:text 문법을 사용합니다. | 문법을 사용하면 패턴? 등으로 이용이 가능한거같습니다. <div th:text="|${project.name}|"> 이 부분 텍스트가 project.name으로 교체됩니다 </div>이런 식으로 태그 내부에서 컨텐츠를 교체합니다. # (참고용) EJS 문법 <div> <%=project.name%> </div> if문th:if="${#strings.isEmpty(detail.url)}" 

Practical Testing 3주차 발자국

서문테스트에 대해서는 늘 많은 고민이 있었다.테스트를 작성하다보니 많은 선택지들이 있었는데 이번 3주차 간의 강의를 통해서 이전에 수강했던 강의를 복습하는 차원에서 다시 한 번 바라보았다.특히나 처음에 강의를 들었을 경우에 가장 와닿고 도움이 많이 되었던 말은 테스트 코드는 좋은 구조를 설계해나가는 과정이라는 것이다.테스트를 작성해나가다 보면 내가 짜놓은 코드의 설계가 확장성 있게 작성이 되지는 않았는지, BDD 기반으로 테스트 코드를 작성해 나가면서 내가 짠 메서드의 행위가 테스트 하기 쉽도록 작성되어 있는지에 대해서 쉽게 파악 할 수 있었다.취업 전에 이 강의를 보고 난 이후의 경험과 현재 취업을 하고 나서 여러가지 테스트 서적을 보고 난 이후의 경험이 합쳐져서 더 많은 도움이 되고는 한다.단위 테스트기존에는 단위 테스트의 기준을 항상 비즈니스 레이어 기준으로 잡았었다. 단위 테스트는 책 한권으로 다룰 만큼 되게 폭 넓은 분야인데 그렇게 넓게 다룰 필요가 있는가? 라고 생각을 하기도 하였다.하지만, Effective Software Testing 책에서 읽어 본 바로는 하나의 계층만 테스트 할 수 있는 구조라면 단위 테스트이다.라는 말이 나의 모호함을 해결해주었다. 다른 말로, 통합 테스트와 단위 테스트의 기준은 무엇을 바라보아야 하지? 라고 했을 때, 다른 계층과의 결합이 생기는 순간 이는 통합 테스트를 바라보면 된다는 것이다.이 관점에서 통합 테스트를 작성 할 때 Mocking 하는 것을 더 생각해 볼 필요가 있다. Mock은 주어진 행위에 대한 결과가 내가 예측한 값을 사용하게 된다. 이는, 실제로 그 값이 내려오지 않을 수 있음을 시사하는데 이로써 해당 계층에 대한 테스트가 신뢰도 있는 테스트가 작성되지 않는다는 것이다.따라서, Mock을 사용한 테스트를 하게 되면 이는 단위 테스트를 작성하는 형태가 된다. 내 계층에서 어떠한 메서드가 이 값을 받았을 때 해당 계층의 결과는 이 결과가 나와야 해. 라는 의미를 가지게 되는 것이다.프레젠테이션 레이어이 부분은 강의를 보면서도 아직 고민이 많다.우리는 프레젠테이션 레이어 테스트를 작성하면서 강의에서도 컨트롤러 영역에서의 서비스 계층 영역을 mocking 하여 결과 값을 가지고 오는 것을 볼 수 있다. 물론 전제는 서비스가 테스트되었기 때문에 서비스 계층에서의 결과 값은 예측 값이 맞겠지만, 이는 서비스 영역에서 결과 값이 필드가 바뀌는 경우에 놓칠 수 있는 경우를 시사하게 된다.물론 이 부분은 개발자가 신경써야 하는 부분이기도 하지만 휴먼 에러를 방치하는 것은 또 좋지 않다고 생각을 하긴 한다. 이 방법을 해결하기 위해서는 결국 E2E (End-To-End) 테스트를 진행하기 위해서 assured 테스트를 사용해보기도 하였는데, 이럴 경우에 Persistent 레이어까지 전부 test container를 띄우면서 실 상황과 유사한 상황으로 테스트를 진행하다보니 테스트에 많은 시간이 소요되게 된다.물론, 우빈님이 소개해주신 스프링 부트 컨테이너의 빈 목록을 한데 모아서 관리한다면 속도 개선은 많은 부분 이루어지지만, 그 이전에 테스트를 실행 할 때마다 해당 테스트에 필요한 데이터들을 새로이 만들어주는 과정이 많은 시간을 잡게 되고, 이는 결국 비즈니스 개발에 많은 시간을 잡아먹게 된다.추가적으로, E2E로 작성하게 되는 경우에 나타나는 문제점은 어찌됐든 서비스 영역의 테스트 결과 값이 서비스 계층의 비즈니스가 완성되기전까지 문서화를 위하여 Dummy 형태의 응답 값을 가지게 된다는 것이다.이러한 여러가지 상황 속에서 아직까지 나만의 답을 찾지 못하였으니 계속 경험을 가지면서 적절한 나만의 기준을 찾는 것이 좋을 것 같다. 해당 내용에 대해서는 우빈님께 질문을 드릴 예정이다.영속성 레이어이 부분도 많은 고민을 가지고 있었다. 취업 준비를 할 때에는 H2 인메모리 DB를 사용하여 많은 부분 영속성 레이어의 테스트를 구성 할 수 있었으나, 실제로 현업에 왔을 때에는 H2에서는 정상 동작하나 실제 DB에서는 작성하지 않는 상황들이 적지 않게 발생하는 것을 볼 수 있었다. 이를 대체 할 방법이 Test Container를 띄워서 테스트를 작성하는 것인데, 시간이 생각보다 오래 걸린다. 아직 이 부분도 해결이 되지 않은 것 같다. 우빈님 강의 뿐만이 아니라 더 많은 서적들을 보면서 영속성 계층을 어떻게 처리하는게 좋을지 이 부분도 경험치를 쌓아나가야 하는 것 같다.모든 상황에서는 은탄환은 존재하지 않는다. 실제로 어떻게 하는지 지금 내 레벨에서는 최대한 카피하여 많은 경험치를 쌓아가는 과정이 중요하다고 생각한다. 또 좋은 기술이 나오지 않을까? 라는 생각이 들기도 한다.마무리옛날에는 테스트를 짜는데 시간이 오래걸린다는 말이 어느정도 맞는 말이라고 생각한다. 하지만, 현재에는 기술이 너무 좋아졌다. copilot을 사용하면서 테스트를 짜는 시간을 거의 300%는 더 빠르게 작성 할 수 있게 해주는 것 같다.기술을 적절히 활용하면서 회사 내에 테스트를 짜는 문화를 좀 더 거부감 없게 만들어나가고 싶다.

소프트웨어 테스트테스트단위테스트회고

[워밍업 클럽 스터디 2기 :: BE 클린코드 & 테스트] 3주차 발자국

단위 테스트JUNIT5 : 단위 테스트를 위한 테스트 프레임워크AssertJ : 테스트 코드 작성을 원할하게 돕는 테스트 라이브러리테스트 팁테스트하기 어려운 영역을 구분하고 분리하기테스트 하고자 하는 영역에서 테스트 하기 어려운 영역을 외부로 분리할수록 테스트 가능한 코드는 많아짐테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드현재 날짜/ 시간, 랜덤 값, 전역 변수/함수, 사용자 입력함수의 input에 해당외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, 데이터 베이스에 기록하기함수의 output에 해당TDDTDD : 프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도록하는 방법론TDD 순환 사이클RED : 실패하는 테스트 작성Green : 테스트 통과 최소한의 코딩REFACTOR : 구현 코드 개선 테스트 통과 유지TDD 핵심 가치피드백이라는 것은 내가 작성하는 구현 코드 프로덕션 코드에 대해서 자주 그리고 빠르게 피드백을 받을 수 있다는 의미결론 선 테스트 작성, 후 기능 구현을 해야함..테스트는 []이다.기술 자체의 숙련도도 중요하지만 소프트 스킬도 중요소프트 스킬커뮤니케이션 능력태도 일에 임하는 몰입도 자세글 글쓰기 능력문서 작성 능력테스트는 왜 문서일까?TDD는 클라이언트가 되서 프로덕션의 기능들을 테스트@DisplayName("음료를 1개 추가할 수 있다.") 명사의 나열보다 문장으로테스트 행위에 대한 결과까지 기술하기도메인 용어를 사용하여 추상화된 내용 담기테스트의 현상을 중점으로 기술하지 말기BDDTDD에서 파생된 개발 방법Given : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시 , 검증어떤 환경에서/ 어떤 행동을 진행했을 떄 / 어떤 상태가 일어난다.→ DisplayName에 명확하게 작성할 수 있음

박민지

(인프런 워밍업 클럽-스터디 2기) 프로덕트 디자인 3주차 발자국

이번 주에도 열심히 컴포넌트를 만들고 다크모드를 비롯한 여러 모드를 만들어 보았다. 다크모드 적용은 컬러 배리어블 등록이 다였지만 생각보다 그럴듯한 화면을 만드는게 힘들었다.다크모드 색상을 조정하는데 얼마나 뭘 어떻게 해야하는지 감이 잘 잡히지 않아 여러번 조정했다.그리고 시각적으로는 대비가 큰데 플러그인을 돌려보면 계속 녹색이 나오지 않아서 당최 어찌 해야할지 모르겠다. 더 연구를 해볼 예정이다.  데스크탑, 타블렛, 모바일 반응형 만드는게 좀 복잡하지만 재밌었는데 각각 화면의 특성을 이해할 수 있었고 불린을 어떤 방식으로 적용해야하는지 배울수 있어서 좋았다. 무엇보다 만들고나니 클릭 몇 번에 휙휙 바뀌는게 재밌고 신기했다 ㅎㅎ한번은 다크모드 만드는데 한 부분만 계속 적용이 안되어서 버그인가 싶었는데 그럴리가.. 역시 휴먼에러이고 내가 배리어블 등록을 primary가 아니라 다른 배리어블로 등록해 놓아서 그렇게 되는 거였다. 이거 가지고 30분을 씨름했는데 역시 더 꼼꼼하게 챙겨야 한다. 체크 또 체크!다중 언어 지원은 아직 피그마에서도 잘 안되는 것 같아 (여러 번역 AI를 써봤지만 영어>한국어는 번역투가 너무 뚜렷해서 어색하게 느껴진다.) 어떻게 업데이트 될지 궁금하다.  그리고 어드민 페이지를 만들면서 내가 그동안 어떻게 컴포넌트를 잘못만들고 있었는지 알게되었다. 예시가 있고 따라하면서도 중간 중간 이해를 못하거나 타이밍을 놓치면 원하는 대로 안나올 때가 있는데 이럴때 뜯어보고 수정하는 건 잊지 않게 된다. 확실히 컴포넌트만 따라 만들다가 페이지를 구성해보니 다르게 느껴졌다. 아직 완벽하게 익히려면 더 만들고 더 고민해 보아야겠지만 겉핥기로만 알고 있던 피그마라는 프로그램을 더 잘 알게 되어서 너무 좋다. 이제 정말 얼마 남지 않았다. 끝까지 완주하고 싶다. 잘한 일 - 어쨌거나 과제 전부 제출반성 - 헤메이다가 과제 하나를 늦게 제출했다.  

UX/UI워밍업디자인시스템배리어블다크모드

인프런 워밍업 클럽 백엔드 3주차 후기

3주차 후기 Readable Code 강의를 재밌게 수강하고, 드디어 고대하던 강의 중 하나인 테스트 코드 강의를 들었다.나는 새로 기능을 만들어 볼 때마다 JUnit으로 테스트 코드를 돌려보려고 한다. 근데 아직 경험이 많이 부족하고 시간이 부족하다는 핑계로 미루게 된다. 그러다 보니 자연스레 서버를 켜서 직접 API를 호출해서 테스트를 하는 비효율적인 방법을 선택하고 있다.이전에 인프런 CTO의 이동욱 님의 어느 글에서 안 좋은 방식이라 했던걸 내가 하고 있던 것이다..! ㅎㅎ이를 지양하기 위해 이번 테스트 강의를 열심히 들었고, 들을 수록 이래서 테스트 코드를 작성해야 함을 배우게 되었다.강의를 마저 다 들어서 얼른 회사에서도 다 적용해볼 수 있도록 해야겠다. 섹션2: 테스트는 왜 필요할까테스트는 왜 필요할까?테스트는 사실.. 귀찮은 작업실무에서는 짧은 시간에 기능을 구현해서 QA(Quality Assuarance)해야는데 시간이 부족함근데 왜 테스트 코드를 짜야할까?기존 프로덕션 코드가 있었는데, 기능을 계속 추가하면서 테스트를 하게 됨근데 아래와 같이 기존 코드를 일부 참고하는데가 생기면서 새로 검증을 해야됨프로덕션 코드가 계속 커지면 테스트하는데만 시간이 다 소진되어버림또한 사람이 하게 되면 실수&누락이 발생하게 될 수 있음 테스트 코드를 작성하지 않으면변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 함변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 함빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다올바른 테스트 코드는자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있음소프트웨어의 빠른 변화를 지원한다.팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.가까이 보면 느리지만, 멀리 보면 가장 빠르다우리가 테스트 코드로 얻을 수 있는 점빠른 피드백: 코드가 실패하면 바로 알 수 있음자동화: 기계가 자동으로 해줘서 사람이 탈 일이 없어짐안정감: 그로 인해 안정감이 늚💡 물론 테스트 코드를 어렵게 짜면 더 힘들어 질 수도 있다. 잘 짜는 것이 중요! 테스트는 귀찮지만 해야한다! 섹션3: 단위 테스트 수동테스트 VS 자동화된 테스트아래와 같이 print를 활용해서 수동으로 확인하는게 과연 맞을까?class CafeKioskTest { @Test void add() { CafeKiosk cafeKiosk = new CafeKiosk(); cafeKiosk.add(new Americano()); System.out.println(">>> 담긴 음료 수 : " + cafeKiosk.getBeverages().size()); System.out.println(">>> 담긴 음료 : " + cafeKiosk.getBeverages().get(0).getName()); } } JUnit5로 테스트하기단위 테스트(Unit test)작은 코드 단위(클래스 or 메서드)를 독립적으로 검증하는 테스트독립적: 외부 네트워크 같은 환경에 의존하는게 아닌 딱 코드 단위를 테스트검증 속도가 빠르고, 안정적임JUnit5단위 테스트를 위한 테스트 프레임워크XUnit - Kent Beck 창시SUnit(Smalltalk), JUnit(Java), NUnit(.NET) 등AssertJ테스트 코드 작성을 원할하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝 지원테스트 케이스 세분화하기해피 케이스: 요구 사항이 그대로 만족하는 케이스 (해피해피)예외 케이스: 예외가 발생하는 경우도 생각해야함두 가지를 고려하기 위해 경계값 테스트를 잘 만들어야 함범위(이상, 이하, 초과, 미만), 구간, 날짜 등테스트하기 어려운 영역을 분리하기추가 요구사항가게 운영 시간(10:00~22:00) 외에는 주문을 생성할 수 없다.public Order createOrder() { LocalDateTime currentDateTime = LocalDateTime.now(); final LocalTime currentTime = currentDateTime.toLocalTime(); if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) { throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요."); } return new Order(LocalDateTime.now(), beverages); } 현재 코드와 같이 시간에 대한 값을 프로덕션 코드에 넣어두면 테스트할 때마다 환경이 달라져 일관성 있는 테스트를 하기 어려워진다이처럼 테스트하기 어려운 영역을 구분하고 분리해야함public Order createOrder(LocalDateTime currentDateTime) { final LocalTime currentTime = currentDateTime.toLocalTime(); if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) { throw new IllegalArgumentException("주문 시간이 아닙니다. 관리자에게 문의하세요."); } return new Order(LocalDateTime.now(), beverages); } 다음과 같이 파라미터로 외부에서 주입받게 해두면 테스트하기 좋아짐 테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을 주는 코드표준 출력(로그), 메시지 발송, 데이터베이스에 기록하기 등 순수 함수(pure functions)같은 입력에는 항상 같은 결과외부 세상과 단절된 형태테스트하기 쉬운 코드 섹션4: TDD Test Drive DevelopmentTDD: Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론테스트 작성 → 기능 구현레드 그린 리팩토링레드: 실프하는 테스트 작성 (실패를 봐라)그린: 테스트 통과, 최소한의 코딩 (일단은 통과 시켜~)리팩토링: 구현 코드 개선, 테스트 통과 유지핵심 가치: 피드백내가 작성하는 코드에 대해서 자주, 빠르게 피드백을 받을 수 있음선 기능 구현, 후 테스트 작성의 문제점 (우리가 일반적으로 했던)테스트 자체의 누락 가능성특정 테스트(해피 케이스) 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 후 기능 구현 (TDD 방식)복잡도가 낮은(유연하며 유지보수가 쉬운) 테스트 가능한 코드로 구현할 수 있게 한다.쉽게 발견하기 어려운 엣지(Edge) 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.클라이언트 관점에서의 피드백을 주는 Test Driven 섹션5: 테스트는 []다테스트는 []다.테스트는 문서다문서?프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜서, 모두의 자산으로 공유할 수 있음DisplayName을 섬세하게명사의 나열보다 문장으로 작성A이면 B이다A이면 B가 아니고 C다테스트 행위에 대한 결과까지 기술하면 더 좋음<aside> 💡음료 1개 추가 테스트 → ~테스트 지양하기음료를 1개 추가할 수 있다.음료를 1개 추가하면 주문 목록에 담긴다 (결과 기술)</aside>도메인 용어를 사용하여 한층 추상화된 내용을 담자메서드 자체의 관점보다 도메인 정책 관점으로 작성테스트의 현상을 중점으로 기술하지 말자<aside> 💡특정 시간 이전에 주문을 생성하면 실패한다. (AS-IS)영업 시작 시간 이전에는 주문을 생성할 수 없다. (TO-BE)</aside>BDD(Behavior Driver Development) 스타일로 작성하기TDD에서 파생된 개발 방법함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트한다.개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준(레벨)을 권장Given / When / ThenGiven: 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등) → 어떤 환경에서When: 시나리오 행동 진행 → 어떤 행동을 진행했을 때Then: 시나리오 진행에 대한 결과 명시, 검증 → 어떤 상태 변화가 일어난다→ DisplayName에 명확하게 작성할 수 있음intelliJ → Live Templates → Java 단축키 등록패키지 풀 네임까지 적어야 자동 import 됨@org.junit.jupiter.api.DisplayName("") @org.junit.jupiter.api.Test void $METHOD_NAME$() { // given $END$ // when // then } 키워드 정리Spock: groovy 기반 BDD 프레임워크언어가 사고를 제한한다한 번 언어로 규정해 놓으면 우리의 사고도 그에 맞춰 제한이 됨명확하지 표현 못한 테스트 자체가 허들이 되고 사고를 제한할 수 있음문서로써 테스트도 중요 섹션6: Spring & JPA 기반 테스트Layered Architecture보통 아래와 같이 계층을 나눔 → 관심사의 분리를 위해 (책임을 나누고 유지보수성 높이기)테스트하기 어려운 부분을 분리해서 해당 부분만 집중하는 것은 동일 통합 테스트(Integration test)여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트일반적으로 작은 범위의 단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없음풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Spring/JPA 훑어보기 & 기본 엔티티 설계Library VS Famework라이브러리는 내 코드가 주체가 됨필요한 기능이 있으면 외부에서 라이브러리를 끌어와서 도구로써 사용프레임워크는 이미 동작하게끔 환경이 구성이 되어있음내 코드는 수동적으로 프레임워크에 꽂혀서 이용이 됨Spring FameworkIoC(Inversion Of Control)제어의 역전객체의 생명 주기 제어를 프레임워크가 맡아줌DI(Dependency Injection)의존성 주입특정 객체를 바로 주입해주는게 아니라 인터페이스를 주입하여 약한 결합을 해주도록 활용A는 B객체를 주입 받지만, 어떻게 받는지 관심없고 단지 주입만 받아 사용AOP(Aspect Oriented Programming)관점 지향 프로그래밍비즈니스 흐름과 관계없는 부분을 관점을 한 데로 모아 활용프록시 활용트랜잭션, 로깅 등에 활용ORM: Object-Relational-Mapping기존 RDB 패러다임 불일치를 가운데에서 맞춰주게끔 해줌객체 지향 팰러다임과 관계형 DB 패러다임의 불일치이전에는 개발자가 객체의 데이터를 한땀한땀 매핑하여 DB에 저장 및 조회(CRUD)ORM을 사용함으로써 개발자는 단순 작업을 줄이고, 비즈니스 로직에 집중할 수 있음JPA(Java Persistence API)자바 진영의 ORM 기술 표준인터페이스이고, 여러 구현체가 있지만 보통 Hibernate를 많이 사용함반복적인 CRUD SQL을 생성 및 실행해주고, 여러 부가 기능들을 제공함편리하지만 쿼리를 직접 작성하지 않기 때문에, 어떤 식으로 쿼리가 만들어지고 실행되는지 명확하게 이해하고 있어야 함!Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공QueryDSL과 조합하여 많이 사용함 (타입체크, 동적쿼리)@Entity, @ID, @Column@ManyToOne, @OneToMany, @OneToOne, @ManyToManyManyToMany는 일대다-다대일 관계로 풀어서 사용하길 권장!

백엔드

[워밍업 클럽] BE 클린코드&테스트 3주차 발자국

강의 요약테스트란?테스트를 하는 이유, 필요한 이유빠른 피드백자동화안정감테스트 코드가 없다면?변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야한다.빠르게 변화하는 소프트웨어의 안정성을 보장할 수 없다.테스트 코드가 병목이 된다면프로덕션 코드의 안정성을 제공하기 힘들다.테스트 코드 자체가 유지보수하기 어려운, 새로운 짐이 된다.잘못된 검증이 이루어질 가능성이 생긴다.올바른 테스트 코드란?자동화 테스트빠른 시간 안에 버그를 발견비용을 절약소프트웨어의 빠른 변화를 지원팀 차원의 이익단기적으론 느리지만, 장기적으로는 빠르다.단위 테스트작은 코드 단위를 독립적으로 검증하는 테스트빠름안정적JUnit 5AssertJ / Fluent assertions for javaAssertJ테스트 코드 작성을 원활하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝 지원테스트 케이스 세분화하기테스트 코드 작성 전에 암묵적이거나 아직 드러나지 않은 요구사항이 있는가? 생각해보자금액이 0 이하인가?수량이 0 이하인가?해피 케이스와 예외 케이스경계값 테스트가 중요하다.테스트하기 어려운 영역을 분리하기테스트 할 때마다 변경되는 값이 있다면, 테스트를 정상적으로 할 수 없다.테스트 하고자 하는 영역을 잘 구분하자외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역이란?관측할 때마다 다른 값에 의존하는 코드현재 날짜/시간, 랜덤 값, 전역 변수/함수, 사용자 입력 등외부 세계에 영향을 주는 코드표준 출력, 메시지 발송, 데이터베이스에 기록하기 등// BAD public Order createOrder() { LocalDateTime currentDateTime = LocalDateTime.now(); LocalTime currentTime = currentDateTime.toLocalTime(); if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) { throw new IllegalArgumentException("주문 시간이 아닙니다."); } return new Order(currentDateTime, beverages); } // GOOD public Order createOrder(LocalDateTime currentDateTime) { LocalTime currentTime = currentDateTime.toLocalTime(); if (currentTime.isBefore(SHOP_OPEN_TIME) || currentTime.isAfter(SHOP_CLOSE_TIME)) { throw new IllegalArgumentException("주문 시간이 아닙니다."); } return new Order(currentDateTime, beverages); } TDD테스트 주도 개발 Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 개발 방법론RED → GREEN → REFACTOR실패하는 테스트 작성테스트가 통과하도록 최소한의 코딩구현 코드 개선, 테스트 통과 유지하면서 리팩토링선 기능 구현, 후 테스트 작성 시테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성해피 케이스잘못된 구현을 다소 늦게 발견할 가능성선 테스트 작성, 후 기능 구현 시복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다.유연하면서 유지보수가 쉬움쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백을 받을 수 있다.과감한 리팩토링이 가능해진다.테스트 코드는 문서다프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점을 보완어느 한 사람이 과거에 경험했던 고민의 결과물을 팀 차원으로 승격시킴→ 모두의 자산으로 공유할 수 있다.문서의 기능을 하기 위해서…@DisplayName 을 섬세하게테스트 행위에 대한 결과까지 기술하기BAD : 음료 1개 추가 테스트GOOD : 음료를 1개 추가할 수 있다.BEST : 음료를 1개 추가하면 주문 목록에 담긴다!도메인 용어를 사용하여 한층 추상화된 내용을 담기BAD : 특정 시간 이전에 주문을 생성하면 실패한다.GOOD : 영업 시작 시간 이전에는 주문을 생성할 수 없다.테스트의 현상을 중점으로 기술하지 말 것BDDBehavior Driven DevelopmentTDD에서 파생된 개발 방법함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 수준을 권장 Given / When / ThenGiven : 시나리오 진행에 필요한 모든 준비 과정 (객체, 값, 상태 등)When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증어떤 환경에서 (Given)어떤 행동을 진행했을 때 (When)어떤 상태 변화가 일어난다 (Then)→ DisplayName에 명확하게 작성할 수 있다. 미션 회고읽기 쉬운 코드에서 작성하였던 지뢰찾기를 기반으로 테스트 코드를 작성하였다.어떤 코드를 테스트 해야할 지, 어떻게 테스트 코드를 짜야할 지 깊이 고민하게 되었다. 느낀점테스트코드에 대해 어느 정도 감이 생기고 있는 것 같다. 하지만 여전히 실무에 적용하기엔 너무 어렵다.비지니스 로직이 변경되더라도 테스트 코드가 어느정도 내구성을 가졌으면 하는데, 이렇게 작성하는게 매우 어렵다.또한 Given 절에서 객체를 사전준비하는 과정이 너무 번거롭다.이를 해결할 수 있는 좋은 방법이 있을 것 같은데..여전히 테스트 코드는 귀찮고 어렵다.

백엔드

sdsd988

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

3주차 강의 : 단위 테스트, TDD | 테스트는 [ ] 다, Spring & JPA 기반 테스트 요약1. 테스트 코드가 무엇이고, 왜 해야하는지2. Spring MVC, JPA 기반 테스트는 어떻게 작성하는지3. 테스트 코드 작성하는 것이 얼마나 중요한(귀찮은) 일인지강의 내용 단위 테스트테스트 코드를 작성하지 않는 환경의 테스트 (수동 테스트)테스트 코드를 통한 테스트테스트 코드 작성 시 고민해야 할 부분테스트 케이스를 어떻게 나눌 것인가? (해피, 예외)테스트 하기 어려운 영역은 어떻게 분리할 것인가?  2 .TDD | 테스트는 [] 다.TDD(Test-Driven-Development)란?테스트 코드를 작성 방법의 조언(DisplayName 작성, BDD 스타일)테스트는 경계값 테스트를 하는 것이 좋다. Spring & JPA 기반 테스트레이어드 아키텍쳐, JPA에 대해Persistent-Layer TestRepository 계층을 테스트한다.쿼리 실행을 통해 기대한 결과를 반환하는 지 테스트Business-Layer TestService 계층 테스트서비스 계층을 위해서는, 통합 테스트가 필요할 수 있다.SpringbootTest, DataJpa 테스트의 차이테스트 계층에서의 @Transactional 사용 시 주의해야 할 사항Stream()의 활용Presentation-Layer Test Mockito를 활용한 컨트롤러 서비스 코드 작성프런트엔드의 응답, 예외 처리Controller, Service 계층의 의존성 해결미션 - Readable Code 에서 작성한 코드의 테스트 코드 작성해보기이전 강의에서 리팩토링한 코드의 테스트 코드를 작성했다.3가지 클래스의 7가지 테스트 케이스를 작성하는 것이 과제! 테스트 코드를 작성하는 것이 어려웠는데, 돌아보면 기존 코드(지뢰찾기, 스터디카페)에 대한 이해 부족에서 기인그렇기에 이번 미션(테스트 코드를 작성)을 통해 좋았던 점은, 기존 코드의 이해 3주차 회고 - 테스트 코드에 대한 생각이 바뀌었다. Java, Spring을 인프런을 통해 공부했기에, 테스트 코드가 중요하고, 좋다는 것은 알고 있었다.강의 초반에 우빈님이 테스트 코드는 귀찮은 것 이라고 정의한 적이 있었던 것 같은데, 공감되었다.하나의 객체에서 추가 요건으로 메서드 추가 된다면, 즉시 테스트 코드를 추가해야 한다. (귀찮다.)또한, 테스트 코드가 개발자에게 도움이 되려면, 꼼꼼하게, 잘 작성해야 한다. (@Transactional, 예외 케이스)    

백엔드인프런워밍업클럽테스트코드JPASpring백엔드

채널톡 아이콘