블로그
전체 8#카테고리
- 백엔드
#태그
- 백엔드
- 워밍업클럽4기
- 워밍업클럽
- 워밍업클럽백엔드
- day2미션
2025. 06. 22.
1
워밍업 클럽 4기 - 백엔드 4주차 회고
4주차 회고강의수강Mock을 마주하는 자세Mockito로 Stubbing하기Test Double@Mock, @Spy, InjectMocksBDDMockitoClassicist VS Mockist키워드 정리더 나은 테스트를 작성하기 위한 구체적 조언한 문단에 한 주제완벽하게 제어하기테스트 환경의 독립성 보장하자테스트 간 독립성을 보장하자한 눈에 들어오는 Test Fixture 구성Text Fixture 클렌징@ParameterizedTest@DynamicTest 테스트 수행도 비용이다. 환경 통합하기Q&A키워드정리 Appendix학습 테스트Spring REST DocsOutro테스트를 작성하는 마음가짐미션Day 16https://www.inflearn.com/blogs/11176Day 18https://inf.run/S7UnS 회고이번 너무 많고 좋은정보가 있어 직접적인 강의에 대한 모든 흐름이 느껴져서회고에 자세하게 적어보려합니다.저에게 테스트는 귀찮지만 한번 만들어놓으면 계속쓰는 도구 그이상그이하가 아니였습니다.테스트 코드를 배우고 아 테스트과정에서 계속반복되면 이게 문제겠구나(메일보내기 및 알람보내기)아! 이건 테스트는 한번만해도되는데그런관점을 배우게 되었고테스트 코드를 오히려 설계할때 어떤 관점과 어떤 중요한점 마음가짐을 더알려주는 강의였습니다.사실 사수에게 배울 수 없는 관점을 더 알려주셨고실제 라이브에서도 저런생각을 하면서 코드를 짜는구나제 코드를 바라보면서 문제점과 부족한 점이 점점 보이기 시작했습니다.사실 코드에는 정답이 없다고는 하지만맨날 제가 보기 좋은 코드만 짠 것같았고테스트를 작성하면서 실제 배운관점을 프로젝트에 적용하면서 도움이되었습니다.이번에 한이유는 실제로 사용하고 바로바로 배우기위해서 시작한거였는데많은 관점을 배웠고 실제 프로젝트에서도 적용하면서 도움이 많이 되었습니다.그동안에 매번 다보기전에 정리만 하고 마무리 안보고 다음강의로 넘어가느라 빠르게 넘어간것들 다시 정리해야겠어요지하철에서 보고 영감받으면 정리하고 넘어가고 하다보니 넘어간게 엄청많았더라고요 핫그만큼 강의 사이사이에 엄청 중요한게 많고 영감이 많아서 늘 도움이 된게 많았어요 감사합니다 일단 다봤지만 체크가안된건 오늘 쓰윽 마지막까지 봐야겠어요
백엔드
・
백엔드
・
워밍업클럽4기
・
워밍업클럽
2025. 06. 19.
1
워밍업 클럽 4기 - 백엔드 Day 18
과제 Day181. Mockito 주요 어노테이션 정리 (@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks)@Mock순수 Mockito 기반 Mock 객체 생성. 실제 동작 없이 필요한 부분만 Stubbing 가능. Spring Context와 무관@MockBeanSpring Context에 등록되는 Mock 객체.기존 Bean을 대체함. @SpringBootTest, @WebMvcTest 등에서 사용.@Spy실제 객체를 감싸서 일부만 Stubbing 가능. 상태 확인이나 부분 동작 제어 시 유용. Mockito 단독 사용 가능.@SpyBeanSpring Context에 등록된 Bean을 Spy로 교체.실제 동작은 유지하면서 일부만 Stubbing.@InjectMocks@Mock 또는 @Spy 객체를 테스트 대상 클래스에 주입. 직접 객체 생성 없이 의존성 자동 주입을 지원.요약 Spring 없이 단순 로직 테스트: @Mock, @InjectMocks, @SpySpring 환경 테스트: @MockBean, @SpyBean실제 동작을 살리되 일부만 조작하고 싶을 때: @Spy, @SpyBean전체를 가짜 객체로 대체할 때: @Mock, @MockBean2. 테스트의 각 항목을 @BeforeEach, given절, when절에 배치AS-IS@BeforeEach void setUp() { ❓ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 1-6. 댓글 생성 // given ❓ // when ❓ // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 2-7. 댓글 수정 // given ❓ // when ❓ // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { 3-1. 사용자1 생성에 필요한 내용 준비 3-2. 사용자1 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 3-9. 사용자2가 사용자1의 댓글 수정 시도 // given ❓ // when ❓ // then 검증 } TO-BE@BeforeEach void setUp() { 0-1. 사용자 생성에 필요한 내용 준비 (1-1, 2-1, 3-1) 0-2. 사용자 생성 (1-2, 2-2, 3-2) 0-3. 게시물 생성에 필요한 내용 준비 (1-3, 2-3, 3-5) 0-4. 게시물 생성 (1-4, 2-4, 3-6) } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-5. 댓글 생성에 필요한 내용 준비 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-1. 댓글 생성에 필요한 내용 준비 (2-5) 2-2. 댓글 생성 (2-6) // when 2-3. 댓글 수정 (2-7) // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-1. 사용자2 생성에 필요한 내용 준비(3-3) 3-2. 사용자2 생성(3-4) 3-3. 사용자1의 댓글 생성에 필요한 내용 준비(3-7) 3-4. 사용자1의 댓글 생성(3-8) // when 3-5. 사용자2가 사용자1의 댓글 수정 시도 (3-9) // then 검증 }작업.공통으로 사용하는 게시판과 사용자(1)은 Setup댓글도 공통으로 사용한다라고 볼 수 있지만 writeComment의 테스트에서 When에 해당하여 제외그외 when에 기준으로 필요한 데이터 생성은 given에 배치
백엔드
・
워밍업클럽4기
・
워밍업클럽
・
백엔드
2025. 06. 17.
1
워밍업 클럽 4기 - 백엔드 Day 16
수정내역썸네일 추가과제1. Persistence Layer데이터를 읽고 쓰는 기능, 즉 DB와 직접 연결되는 창구 역할을 합니다.데이터 저장, 조회, 수정, 삭제 같은 순수한 CRUD 작업만 담당해야 하며, 복잡한 계산이나 로직은 포함되면 안 됩니다.어떻게 테스트?JPA 기반의 Repository를 중심으로 테스트합니다.@ActiveProfiles를 사용해 테스트 전용 DB를 따로 두어 테스트실제 데이터를 저장하고 불러오면서 동작을 검증합니다.예: 특정 조건으로 조회 시 원하는 값이 정확히 나오는지 확인 @DataJpaTest 사용시 자동으로 롤백 (@Transactional)포인트빠르게 테스트하고 싶으면 @DataJpaTest순수하게 CRUD에 집중해야 하므로, 로직이 들어있다면 오히려 잘못된 구조일 수 있음2. Business Layer비즈니스 로직을 담는 곳입니다.예를 들어 “결제를 하면 재고가 줄어야 한다” 같은 업무 흐름을 구현하는 레이어며트랜잭션 보장과 동시성을 고민해야한다.어떻게 테스트?@SpringBootTest 또는 @ServiceTest 등으로 전체 흐름을 확인할 수 있는 환경 구성필요한 데이터는 테스트 전 직접 저장하거나 Mock 처리경계값 테스트given-when-then 구조로 시나리오 기반 테스트 작성포인트이 레이어는 트랜잭션이 중요한 레이어이므로, 롤백 여부도 테스트 대상핵심은 “이 비즈니스 흐름이 내가 기대한 대로 잘 작동하느냐”임3. Presentation Layer웹 요청(HTTP 등)을 처음으로 받아들이는 입구입니다.컨트롤러에서 파라미터를 검증하고, 서비스에 요청을 위임하며, 응답을 구성합니다.즉, 입력과 출력의 인터페이스 역할을 합니다.어떻게 테스트?단위 테스트처럼 접근컨트롤러만 띄우고, 내부 서비스나 레포지토리는 Mock 처리MockMvc, @WebMvcTest 등을 이용해 REST API 요청/응답을 검증validation, 상태 코드, JSON 응답 구조를 확인포인트복잡한 로직보다 “입력값에 따라 어떤 응답이 나오는가”에 집중실제 호출 흐름이 아니라 API 인터페이스가 잘 동작하는지만 보자과제 회고각 레이어마다 역할이 다르기 때문에,그에 맞는 테스트 전략과 도구도 달라져야 한다는 걸 이번에 확실히 느꼈습니다.Persistence는 DB를 잘 다루는지Business는 로직 흐름이 맞는지Presentation은 입력과 출력이 잘 연결되는지
워밍업클럽
・
워밍업클럽4기
・
백엔드
2025. 06. 15.
2
워밍업 클럽 4기 - 백엔드 3주차 회고
수정내역썸네일 추가3주차 회고 강의 수강레이어드 아키텍처와 계층별 테스트 전략이번 주에는 Spring 기반의 레이어드 아키텍처 구조를 중심으로,Persistence / Business / Presentation Layer 각각의 책임과 역할을 이해하고이에 적절한 단위 및 통합 테스트를 직접 구성해보는 학습을 진행했습니다.1. Persistence Layer 테스트Persistence Layer는 애플리케이션의 가장 하단에서 데이터 접근(CRUD)만을 책임지는 레이어(data access의 역할)비즈니스 로직은 이 레이어에 포함되지 않아야 하며, 주로 JPA 기반 Repository를 테스트 대상으로 삼습니다.사용 기술: Spring Data JPA, @DataJpaTest테스트 대상: Repository + JPA 동작 확인특징실제 DB와 유사하게 동작하는 경량 통합 테스트 수행DB 변경 없이 테스트 가능 (자동 롤백) 2. Business Layer 테스트Business Layer는 도메인의 핵심 로직을 처리하는 계층으로, 서비스 클래스가 주를 이룹니다.Persistence Layer와 상호작용하며 트랜잭션을 보장하는 것이 중요합니다.테스트 대상: Service 클래스, 로직 흐름사용한 테스트 기법given-when-then 패턴 검증 내용:입력에 따라 기대하는 응답이 나오는지트랜잭션 롤백이 제대로 작동(@Transactional)@SpringBootTest, @DataJapTest차이 DataJpaTest 는 트랜잭션 어노테이션이 있어 자동 롤백 3. Presentation Layer 테스트Presentation Layer는 외부 요청(HTTP 등)을 가장 먼저 받는 계층으로,파라미터 검증이나 라우팅 처리 등의 역할을 담당합니다.테스트 방식: 컨트롤러 단위 테스트 + 나머지 계층은 Mocking학습한 포인트:@Transactional(readOnly = true)를 클래스 단에 선언하여 조회 메서드는 최적화CUD 작업은 개별 메서드에 @Transactional을 덮어써 트랜잭션 활성화동시성 이슈를 고려한 설계 및 분리 4. 테스트 환경 구성application.yml을 통해 프로파일(local/test) 분리테스트 환경에 따라 다른 DB, 포트, 설정이 적용되도록 구성테스트 도중 DB 변경 방지를 위해 자동 롤백 + @AfterEach 정리 수행어노테이션역할@SpringBootTest전체 애플리케이션 컨텍스트 로딩@DataJpaTestJPA 관련 빈만 주입, 빠른 테스트 가능미션Day 11https://github.com/Goddohi/warming-up-readable-code/tree/study/day11 회고이번 주는 단순히 테스트 코드를 작성하는 것이 아니라각 계층의 역할에 맞춘 책임 분리와 테스트 전략 수립의 중요성을 몸소 느낄 수 있던 시간이었다.특히 레이어드 구조를 이해하고,Persistence는 데이터 검증에만 집중하고Business는 로직 흐름과 트랜잭션을,Presentation은 외부 입력에 대한 응답 및 검증에만 집중하는테스트의 방향성과 목적을 처음 명확히 잡아볼 수 있었다.이번에는 회사 출근과 프로젝트가 야간근무도 몰려서 많은 정리를 못해서 중구난방이라아직 잘 이해를 못한 점이 많다.더 한번더 복습이 필요하다.
백엔드
・
백엔드
・
워밍업클럽4기
・
워밍업클럽
2025. 06. 08.
2
워밍업 클럽 4기 - 백엔드 2주차 회고
수정내역썸네일 추가2주차 회고강의 수강리팩토링에 대해서 배운점을 회사 본업을 하면서 실전 적용도 해봤다회사에서는 리팩토링에 대해서 왜 그렇게 수정하는지 잘 적어만 준다면 다 허락을 해줬다.실제로 중복 코드가 많은 부분은 리팩토링으로 정리 시켰고강의를 보면서 중요시 생각한 점객체에게 바로 예의없이 꺼내지않기객체에 대한 메서드로 해결 주석에 대한 양면성회사에서 관련된 주석이 많았는데몇몇개는 친절한 주석도 있었으나 너무 주석이 많았다.좋은 주석을 달아보려고 해야한다.변수와 메서드의 나열 순서테스트만들고 테스트하는 것은 통과하기 위한 테스트를 제작할 가능성이 있고테스트를 하고 만들면 통과 하기 위한 기능을 만들 수가 있다라는 생각.미션Day 7https://github.com/Goddohi/warming-up-readable-code/tree/study/studycafe회고회고에 적으려고 미션을 아주 짧게 적었었는데코드 리뷰를 멘토님께서 디테일 하게 해주셨는데코드 리뷰를 해주시면서 선배개발자로써의 개념으로 이야기를 해주셧고 다른 사람의 코드를 볼 수있는 경험이 좋았다.이번주는 회사일이 많이 바빠서 이동하면서 듣느라 작성할 만한 문건을 만들지 못했지만들을때마다 도움되는 개념이였던 만큼 한번더 다시 한번더 듣고 정리 해야겠다
워밍업클럽
・
백엔드
・
워밍업클럽4기
2025. 06. 01.
2
워밍업 클럽 4기 - 백엔드 1주차 회고
수정내역썸네일 추가1주차 회고강의 수강‘추상과 구체’ 과제를 통해 추상 개념에 대해 다시 한번 생각해볼 수 있었다.매직 넘버를 접하며, 추상적인 값은 별도 변수로 분리해 명확하게 표현하는 것이 중요하다는 점을 알게 되었다.추상화 레벨 개념을 통해 코드의 표현 단계와 책임 분리에 대해 고민해보는 계기가 되었다.내 코드를 분석하면서, 읽는 사람이 뇌의 메모리를 얼마나 사용할지 생각해보았고, 얼리 리턴을 적용해 리팩토링을 시도했다.특히 ‘부정어를 대하는 자세’에서는 코드에 부정 표현이 들어갈 때의 가독성과 인지 부담에 대해 다시 생각해보게 되었다.이점은 왜 이렇게 사용해야할 까 라는 생각을 했었지만 SOLID에서 이렇게 많이 사용하는 구나를 하면서 배우게 되었다.과제를 직접 수행하면서, 배운 내용을 실전에서 적용해보며 리팩토링 감각을 조금씩 익혀갔다.SOLID 원칙 각 요소를 복습할 수 있었고, 이를 실제 코드에 어떻게 적용해볼 수 있을지 고민해보는 기회가 되었다.특히 getter setter에 사용에 대해서 고려를 많이 하게 되었다. 미션Day 2추상을 고려할때 일단 사람들 간에 약속적인 의미로 생각하였다구체에는 자세하게 적었지만 불필요한 내용을 간추리기 라고 생각하였다. Day 4AS-IS에 있는 코드를 다 분석하고 TO-BE에는 배운 코드를 다 이용하려고 하였다해당 과제에 자세하게 적긴했지만일단 미리 리턴을 하도록 정리하였고부정어 처리그후 예외처리로 마무리하였다. SRP :한 클래스는 딱 하나의 역할만 하자.OCP :기존 코드를 고치지 않고, 기능을 확장할 수 있어야 한다.LSP:부모 타입을 사용하는 곳에 자식 타입을 써도 제대로 작동해야 한다.ISP :쓸데없는 인터페이스는 나눠서 큰 종합세트를 주지말고 작은 인터페이스 여러개가 좋다DIP:고수준 모듈은 저수준 모듈에 의존하지 말고, 추상(인터페이스)에 의존하자.
백엔드
・
워밍업클럽
・
워밍업클럽4기
・
백엔드
・
워밍업클럽백엔드
2025. 05. 30.
1
워밍업 클럽 4기 - 백엔드 Day 4
수정내역썸네일 추가과제1. 읽기 좋은 코드로 리팩토링해 보기AS-ISpublic 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-BEEarly Return 적용if else 문에 대한 조건에 대하여 if문 별 return이 존재하다는걸 파악하였고 조건에 맞게 Early Return적용 order가 null일 경우 추가 적용공백 라인 적용if문에 대한 공백 라인을 적용함으로서 코드의 분기점을 명확하게 구분부정연산자를 메서드안에 포함하여 메서드명으로 확인할 수 있도록 변경!(order.getTotalPrice() > 0), !order.hasCustomerInfo() 의 가독성 향상을 위한 메서드화 예외 처리return false; 에 대해서 각 메서드에서 IllegalArgumentException 예외를 던지도록 수정하였고예외처리를 통해 log를 try-catch문으로 한번에 처리하도록 수정(1번버전) 1. 예외처리 버전 ( 다소 엄격 )public boolean validateOrder(Order order) { if (order == null) { log.info("Order 객체가 null입니다."); return false; } try{ validateOrderItemsExist(order); validateTotalPrice(order); validateCustomerInfoPresent(order); return true; }catch(IllegalArgumentException e) { log.info(e.getMessage()); return false; } } public void validateOrderItemsExist(Order order) { if (order.getItems().size() == 0) { //order.getItems().isEmpty()도 고려 throw new IllegalArgumentException("주문 항목이 없습니다."); } } public void validateTotalPrice(Order order){ if((order.getTotalPrice() 2. 예외처리 미적용public boolean validateOrder(Order order) { if (isOrderNull(order)) return false; if (isOrderItemsEmpty(order)) return false; if (isTotalPriceInvalid(order)) return false; if (isCustomerInfoMissing(order)) return false; return true; } public boolean isOrderNull(Order order) { if (order == null) { log.info("Order 객체가 null입니다."); return true; } return false; } public boolean isOrderItemsEmpty(Order order) { if (order.getItems().size() == 0) { //order.getItems().isEmpty()도 고려 log.info("주문 항목이 없습니다."); return true; } return false; } public boolean isTotalPriceInvalid(Order order) { if((order.getTotalPrice() 2. SOLID에 대하여 자기만의 언어로 정리해 봅시다. 1. SRP - Single Responsibility Principle(단일 책임 원칙)한 클래스는 딱 하나의 역할만 하자.하나의 클래스가 너무 많은 일을 하려고 하지 말자. 2. OCP- Open/Closed Principle(개방/폐쇄 원칙)기존 코드를 고치지 않고, 기능을 확장할 수 있어야 한다.수정에는 닫혀 있고, 확장에는 열려 있어야 한다.조건문을 늘리기보다는 새로운 클래스를 추가해 기능을 확장하자. 3. LSP - Liskov Substitution Principle(리스코프 치환 원칙)부모 타입을 사용하는 곳에 자식 타입을 써도 제대로 작동해야 한다.부모 클래스의 역할을 자식이 완전히 대체할 수 있어야 한다.자식 클래스가 부모의 약속을 깨면 안 된다. 4. ISP - Interface Segregation Principle(인터페이스 분리 원칙)쓸데없는 인터페이스는 나눠서 큰 종합세트를 주지말고 작은 인터페이스 여러개가 좋다클라이언트가 사용하지 않는 메서드에 의존하게 하지 말자.하나의 거대한 인터페이스보다는, 작은 인터페이스 여러 개가 좋다. 5. DIP - Dependency Inversion Principle(의존 역전 원칙)고수준 모듈은 저수준 모듈에 의존하지 말고, 추상(인터페이스)에 의존하자.핵심 로직(고수준)이 세부 구현(저수준)에 끌려다니면 유연성이 떨어진다.인터페이스를 사이에 두고 서로 느슨하게 연결하자. 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
백엔드
・
워밍업클럽4기
・
워밍업클럽
2025. 05. 27.
1
워밍업 클럽 4기 - 백엔드 Day 2 추상과 구체
수정내역썸네일 추가과제"추상과 구체" 강의를 듣고, 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어보기 예시 1)추상 : 오늘 저녁으로 고기를 구워 먹었다구체오늘 저녁으로 삼겹살을 높은 온도로 구워서 익은 상태로 먹었다.오늘 19시10분에 삼겹살을 프라이팬에 100도 이상 달궈진 상태에 올려 구워서 익은 상태로 먹었다.나는 2025년 5월 27일 19시 10분에 삼겹살을 프라이팬에 100도 이상 달궈진 상태에 올려 양옆을 2분씩 번갈아가면서 뒤집기를 6회를 하여 익은 상태로 먹었다예시 2)추상 : 나는 어제 스터디OT를 참여했어구체나는 어제 20시에 인프런 스터디 OT를 참여했어.나는 어제 20시에 인프런에서 진행하는 워밍업 클럽 4기에 스터디 OT를 참여했어나는 어제 20시에 인프런에서 진행하는 워밍업 클럽 4기에 스터디 OT를 온라인 화면공유프로그램으로 참여했어 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
워밍업클럽
・
백엔드
・
워밍업클럽4기
・
day2미션