블로그

박준형

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

4주차에 배운 것들📌Test Double"Test Double"은 테스트 중에 실제 객체를 대체하기 위해 사용하는 객체를 통칭하는 개념으로 다섯 가지 유형이 존재Dummy : 아무 것도 하지 않는 깡통 객체Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에서 쓰기에는 부족한 객체ex. FakeRepository Stub : 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체 그 외에는 응답하지 않는다.상태 검증(State Verification) : 메서드 호출 후 Stub이 상태가 어떻게 바뀌었는지 검증Mock : 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체행위 검증(Behavior Verification) : 메서드에 파라미터를 넘겨줬을 때, 어떤 값을 반환할지 행위를 검증Spy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체 일부는 실제 객체처럼 동작시키고 일부만 Stubbing할 수 있다. 📌 나는 Classicist 일까 Mockist 일까"Classicist VS. Mockist" 강의를 듣기 전까지 Mockist였다. "Mocking을 통해서 단위 테스트로 기능을 테스트했으면 통합 테스트에서는 안해도 될 것 같은데?"처럼 단순하게 생각했다. 그런데 코치님께서 Classicist를 지향하시는 이유를 듣고난 후 다시 생각해보았다. 어쩔 수 없이 Mocking을 해야하는 상황을 제외하고, 각 기능들이 의존 관계를 가질 때는 어떤 예외가 발생할지 모르기 때문에 최대한 프로덕션 환경과 비슷하게 테스트를 하는 것이 좋다고 생각되었다. 그래서 앞으로는 Classicist 방식으로 테스트 하는 것을 지향해야겠다. 📌 그렇다면 Mocking은 언제?💡 Mocking이 유용한 상황은 대표적으로 2가지가 있다.외부 시스템에 의존하는 기능외부 시스템 같이 오류가 발생해도 우리가 통제할 수 없다.테스트할 때마다 비용이 발생하는 기능 ex) 메일 전송 ❓ 만약 한 객체에 특정 기능은 Mocking을 하고싶고, 실제 동작도 테스트하고 싶다면?이럴 땐 Spy 객체를 활용할 수 있다! Mocking을 원하는 특정 기능만 Stubbing을 하고, 나머지는 원본 객체처럼 실제 동작을 테스트할 수 있다.🔗 Mock 관련 어노테이션 정리 📌 Mockito도 BDD 스타일로Mockito.when(mailSendClient.sendEmail(anyString(), anyString())) .thenReturn(true); BDDMockito.given(mailSendClient.sendEmail(anyString(), anyString())) .willReturn(true);기존 Mockito에서는 stubbing을 위한 기능의 메서드명이 when 인데, stubbing 과정은 given 절에서 해야하는 작업이라 메서드명이 부자연스러웠다. 그럴땐 Mockito를 한 번 감싼 BDDMockito를 사용하면 BDD 스타일에 더 자연스럽게 코드를 작성할 수 있다. BDDMockito에서 given 은 Mockito의 when 과 동일한 기능을 한다. 📌한 문단에 한 주제@Test @DisplayName("상품 타입이 재고 관련 타입인지를 체크한다.") void containsStockTypeEx() { // given ProductType[] productTypes = ProductType.values(); for (ProductType productType : productTypes) { if(productType == ProductType.HANDMADE) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isFalse(); } if(productType == ProductType.BAKERY || productType == ProductType.BOTTLE) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isTrue(); } } }각 타입들을 모두 검증할 수 있도록 하나의 테스트로 구성하는 것은 단점이 있다.한 문단에 두 가지 이상의 내용이 포함된다.반복문 자체가 여러 논리 구조가 들어가게 하고, 보는 사람의 사고를 복잡하게 한다.💡이럴 경우에는 케이스를 나눠서 테스트를 작성하는 것이 좋고, 이 방식은 지양하자! 📌테스트 환경의 독립성을 보장하자다른 API를 끌고와서 테스트 간 결합도가 생길 수 있다.// given Stock stock1 = Stock.create("001", 2); Stock stock2 = Stock.create("002", 2); stock1.deductQuantity(3); // 예외 : 차감할 재고 상품이 없습니다.테스트 주제와 맞지 않은 given 절에서 예외가 발생하고 있음왜 실패했는지 유추하기 어려운 상황이 발생💡 독립성을 보장하는 방법given 절에서 순수 builder 또는 생성자 기반으로 데이터를 생성팩토리 메서드나 메서드 기반은 지양어떠한 의도를 가지고 필요한 인자만 받거나 내부에 검증 로직이 포함되어 있을 수 있기 때문 📌Test Fixture 구성하기✅Test Fixture : given 절에서 생성한 객체들Fixture : 고정물, 고정되어 있는 물체테스트를 위해 원하는 상태로 고정시킨 일련의 객체💡 공통된 Fixture들을 만들어야 할 때문서로서의 역할을 위해서 given 절에서 생성하는 것이 좋다!@BeforeEach void setUp() { // before method }@BeforeEach 를 사용공유 변수 같이 테스트 간 독립성을 훼손(테스트 간 결합도가 생김)상위 setUp 메서드에서 어떤 데이터를 생성하는지 확인해야 하며 파편화를 유발문서로서의 역할을 할 수 없음 만약 각 테스트 입장에서 봤을 때 아예 몰라도 테스트 내용을 이해하는 데에 문제가 없는 경우나 해당 공유 변수를 수정해도 모든 테스트에 영향을 주지 않는 경우는 사용해도 괜찮다.data.sql을 사용하여 테스트 데이터 생성파편화가 발생 (given 절에 데이터가 없으므로 어떤 데이터로 테스트를 하는지 확인하기 불편함)테스트가 커질 수록 또 다른 관리 포인트가 됨builder를 private 메서드로 구성파라미터에 해당 테스트에서 필요한 데이터만 받도록 구성테스트 데이터용 builder를 한 곳에 모아서 사용자바 특성 상 필요한 파라미터가 달라 새로운 builder가 계속 생겨 오히려 복잡도가 상승🚨Test Fixture 클렌징 시 유의 사항deleteAllInBatch()테이블 전체를 벌크성으로 빠르게 삭제할 수 있음외래키 제약 조건로 인해 순서를 잘 고려해야함여러 트랜잭션 경계가 참여하는 @Transactional 의 롤백 기능을 사용하기 어려워 해당 방법을 사용해야 함 deleteAll()해당 테이블을 조회하여 엔티티를 통해 delete(엔티티) 방식으로 데이터를 삭제데이터 개수만큼 쿼리가 발생 → 병목지점 유발자신과 외래키 제약 조건으로 연관된 데이터도 조회하여 삭제 📌@ParameterizedTest여러가지 값에 대한 테스트를 하나의 테스트 코드에 녹이기 위한 방법// Case 1 @ParameterizedTest @CsvSource({"HANDMADE, false", "BOTTLE, true", "BAKERY, true"}) @DisplayName("상품 타입이 재고 관련 타입인지를 체크한다.") void containsStockType4(ProductType productType, boolean expected) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); } // Case 2 private static Stream<Arguments> provideProductTypesForCheckingStockType() { return Stream.of( Arguments.of(ProductType.HANDMADE, false), Arguments.of(ProductType.BOTTLE, true), Arguments.of(ProductType.BAKERY, true) ); } @ParameterizedTest @MethodSource("provideProductTypesForCheckingStockType") @DisplayName("상품 타입이 재고 관련 타입인지를 체크한다.") void containsStockType5(ProductType productType, boolean expected) { // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); } 📌@DynamicTest환경을 설정하고, 환경에 변화를 중간 중간 검증을 하는 시나리오를 테스트하고싶을 때 사용@TestFactory @DisplayName("재고 차감 시나리오") Collection<DynamicTest> stockDeductionDynamicTest() { // given Stock stock = Stock.create("001", 1); return List.of( DynamicTest.dynamicTest("재고를 주어진 개수만큼 차감할 수 있다.", () -> { // given int quantity = 1; // when stock.deductQuantity(quantity); // then assertThat(stock.getQuantity()).isZero(); }), DynamicTest.dynamicTest("재고보다 많은 수의 수량으로 차감 시도하는 경우 예외가 발생한다.", () -> { // given int quantity = 1; // when // then assertThatThrownBy(() -> stock.deductQuantity(quantity)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("차감할 재고 상품이 없습니다."); }) ); }💡 List.of() 에 정의된 순서대로 테스트가 동작하며 이전 테스트의 형태를 그대로 유지하기 때문에 테스트 간 결합이 생겨 시나리오를 구성하여 테스트가 가능하다. 📌 테스트 환경 통합하기테스트 환경 통합전에는 전체 테스트(gradle → Tasks → verification → test) 수행 시 총 6번의 스프링 컨텍스트가 로딩되었다. 테스트가 많아질 수록 이는 더 많은 시간을 소모하게 되고, 테스트를 하지 않게 할 수 있다.@ActiveProfiles("test") @SpringBootTest public abstract class IntegrationTestSupport { @MockitoBean protected MailSendClient mailSendClient; } // 통합이 필요한 클래스에서 상속하여 환경 통합 class OrderServiceTest extends IntegrationTestSupportMocking을 하고 있는 클래스를 통합하려면 상위 클래스에 해당 객체를 옮겨주어야 한다. Mock객체가 없는 클래스와 있는 클래스는 다른 환경이라고 인식하여 제대로 통합이 이루어지지 않기 때문이다.이럴 경우 Mock이 필요없는 클래스에도 객체가 들어가는데 이게 싫다면, 테스트를 두 가지 환경으로 분리하여 Mock이 필요한 클래스, 필요하지 않은 클래스로 나누고 필요한 클래스의 상위 클래스에 Mock 객체를 몰아 넣는 형식으로 구성할 수 있다.@WebMvcTest(controllers = { OrderController.class, ProductController.class }) public abstract class ControllerTestSupport { @Autowired protected MockMvc mockMvc; @Autowired protected ObjectMapper objectMapper; @MockitoBean protected OrderService orderService; @MockitoBean protected ProductService productService; } class ProductControllerTest extends ControllerTestSupportController 테스트 환경 통합도 동일하게 진행하면 된다.✅환경을 통합한 후에는 총 2번의 스프링 컨텍스트가 로딩되어 테스트 수행 시간을 단축할 수 있었다. 📌Q. private 메서드의 테스트는 어떻게 하나요?private 메서드는 테스트할 필요가 없다!만약 private 메서드를 테스트 하고 싶은 때에는 “객체를 분리할 시점인가?”에 대해서 생각해봐야 한다.public 메서드(공개 API)를 가지고 있다는 것은 이를 의존하는 테스트코드, 컨트롤러 즉 클라이언트 입장에서는 공개 메서드만 알면 된다. 외부로 노출되지 않는 private 메서드에 대해서는 알 필요가 없다는 것이다. public 메서드의 테스트를 수행하면서 private 메서드의 테스트도 저절로 수행되기 때문이다.해당 private 메서드의 테스트는 꼭 필요하다고 생각되면 객체에 역할에 맞게 분리가 되지 않았을 수 있다. 그러므로 객체를 분리하여 다른 클래스로 만든 뒤 테스트를 구성하는 것이 바람직하다. 📌Q. 테스트에서만 필요한 메서드가 생겼는데 프로덕션 코드에서는 필요 없다면?만들어도 된다! 하지만 보수적으로 접근해야 한다.@Test @DisplayName("신규 상품을 등록한다.") void createProduct() throws Exception { // given ProductCreateRequest request = ProductCreateRequest.builder() .type(ProductType.HANDMADE) .sellingStatus(ProductSellingStatus.SELLING) .name("아메리카노") .price(4000) .build(); }해당 request 객체는 프로덕션 코드에서는 필요가 없다.(@RequestBody 로 외부로부터 값을 받기 때문) 하지만 테스트를 하기 위해서는 꼭 필요하다.객체로서 가져도 되는 기능(빌더, 생성자, 사이즈)이면서 미래에도 충분히 사용이 될 수 있는 기능이라면 만들어도 된다. 📌 학습 테스트?잘 모르는 기능, 라이브러리, 프레임워크를 학습하기 위해 작성하는 테스트여러 테스트 케이스를 스스로 정의하고 검증하는 과정을 통해 보다 구체적인 동작과 기능을 학습할 수 있다.관련 문서만 읽는 것보다 훨씬 재미있게 학습할 수 있다.💡사용하려는 라이브러리를 테스트 코드를 작성하면서 학습하면 팀 내에서도 공유하는 자료로도 활용할 수 있다. 📌 Spring REST Docs테스트 코드를 통한 API 문서 자동화 도구API 명세를 문서로 만들고 외부에 제공함으로써 협업을 원활하게 한다.기본적으로 AsciiDoc을 사용하여 문서를 작성한다.👍장점테스트를 통과해야 문서가 만들어진다. (신뢰도가 높다.)프로덕션 코드에 비침투적이다.👎단점코드 양이 많다.설정이 어렵다.지금까지 API 문서를 위해 Swagger를 사용했는데, Swagger는 어노테이션으로 선언만 해주면 해당 API가 검증되지 않아도, 해당 API의 명세가 생성된다. 그에 반해 Spring REST Docs는 테스트 코드가 통과해야 명세가 생성되므로 안정성을 보장하는 면에서 더 이점이 있다고 생각한다. 그렇지만 명세를 위한 코드의 양이 너무 많고, 작성하기 번거로웠다. 이 문제를 해결하기 위해 자동화할 수 있는 방법을 찾아봐야겠다.미션을 통해 배운 것들📌 Day 16각 레이어가 어떤 특징들을 가지는지 명확하게 알 수 있었고, 적합한 테스트 방법과 Classicist인지 Mockist인지 생각할 수 있는 기회를 준 좋은 미션이였습니다!해당 미션을 수행할 때까지는 Business Layer에서 통합 테스트를 수행 시 Persistence Layer의 기능은 단위 테스트로 검증했으니 Mocking하여 비즈니스 로직만 테스트하는 것이 좋다고 생각했는데, 이후 "Classicist VS. Mockist" 강의를 듣고 해당 부분에 대한 제 생각을 다시 정리할 수 있었습니다. 📌 Day 18다른 강의에서 Mock 관련 기능들을 간단히 배웠는데, 그 때까지는 Mocking을 한다는 것이 정확히 뭔지 잘 이해하지 못했습니다. 그리고 비슷한 어노테이션과 서로 다른 주입법들로 인해서 복잡하고 이해하기 힘들었습니다.이번 강의를 통해 "Mocking을 왜 해야하는지"와 각 어노테이션들을 사용하는 방법과 상황에 대해서 완벽하게 이해할 수 있었습니다. 미션을 통해 같은 Mock 객체를 생성할 때도 @Mock 을 사용해야할 때, @MockitoBean 을 사용해야할 때를 명확하게 알 수 있었고, setUp절에 배치하면 좋은 Fixture 들을 구분하는 방법도 이해할 수 있었습니다.

백엔드워밍업클럽4기백엔드4주차발자국

터프남

인프런워밍업클럽4기 devops - 4주차 발자국

[강의]#1.Helm과 Kustomize 비교하며 사용-2#2.ArgoCD 빠르게 레벨업-1#3.ArgoCD 빠르게 레벨업-2#4.ArgoCD 빠르게 레벨업-3 [미션 진행 소감]마지막 주차까지 끝났다. 처음 목표를 했던 다짐과 의지보다는 성실히 잘 수행하지는 못했던 것 같고 개념과 지식을 모두 학습하겠다는 목표도 지키지는 못했던 것 같다. 물론 첫술에 배부를순 없지만 그래도 하나 의미있게 생각할 수 있는건 수료를 했다는 것이다. 작은 성공 경험이 쌓여서 큰 성공 경험을 만들어낸다고 하는데 나는 이런 작은 성공 경험이 올해에 몇개씩은 쌓이고 있는 것 같아서 유의미하게 생각한다. 이런 워밍업클럽을 만들어준 인프런에게 감사하고 데브옵스4기를 진행해준 일프로 강사님에게 정말 고맙다는 이야기를 하고싶다. 강사님의 수업자료나 준비 자료들을 보면 매번 감탄이 나온다. 이제 내가 해야할건 복습과 조금 더 쿠버네티스에 친숙해지기이다. 난 SI 개발자인데 사실 인프라를 다루는게 크게 많지는 않다. 가끔 운영업무를 할 때가 있는데 그때 k8s를 처음 경험해봤다. 강의 마지막까지 들었을때 그때 운영사에서 쓴 ArgoCD 라던지 Jenkins에 대해서 조금은 이해할 수 있게되어서 좋았다. 이제는 그런 지식을 내 머릿속에 확실하게 그림과 개념으로 정리할때라고 본다. 비록 6월달에 이제 워밍업클럽 4기가 끝나지만... 강의는 항상 무제한으로 수강할 수 있으니 계속계속 듣고 정리하고 내 지식으로 남겨야겠다. 감사합니다.

데브옵스 · 인프라

최준

인프런 워밍업 클럽 백엔드- devops 4주차 발자국

미션 https://dev-gongmyoung.tistory.com/62컨테이너 관련 기술은 도커만 알고 있었는데 이번 강의들을 들으며 containerd에 대해서도 알게 되었다. 이번 미션을 진행하며 미션에서 설명한 상황이 일어날 수 있음을 알게 되었다. 또 containerd에도 도커와 같이 같은 일들을 하는 명령어들이 있음을 알게 되었다. 이렇듯 다양한 기술들이 있는데 본질을 정확히 알고 있다면 명령어를 찾아서 사용하는 것을 어렵지 않을 것이라고 생각했다.  강의 수강이제 쿠버네티스를 활용해 배포에 대해 듣기 시작하였다. DevOps에 다양한 직군들이 있고 다양한 오픈소스들이 사용된다는 것을 알게 되었다. 또한 Jenkins를 처음 사용해보면서 그렇게 어렵지만은 않겠다는 생각을 했다. 쿠버네티스라는 기술에 관심을 가지게 된 이유가 바로 이 배포였다. 배포에 대해 잘 학습하고 이전 프로젝트를 쿠버네티스를 활용해 배포하는 것에 도전해보고 싶다.  회고벌써 마지막이라는 것이 실감이 나지 않는다. 강의를 마지막까지 다 들으며 잘 마무리하려고 노력해야겠다. 복습이 이렇게 오래 걸릴지 몰라 처음에 미뤄둔 것이 너무 아쉽다. 워밍업 클럽이 다 끝난 이후에라도 복습을 다 해 유종의 미를 거둬야겠다.

Hino

워밍업 클럽 4기 DevOps - 4주차 발자국

# 발자국인프런 워밍업 클럽 4기 DevOps의 4주차를 완료하고 작성하는 발자국입니다!강의명 : 쿠버네티스 어나더 클래스 (지상편) - Sprint 1, 2# 4주차 회고어느새 워밍업 클럽이 종료되었습니다..!이번 주차에서는 Kustomize와 ArgoCD와 중점적으로 배웠는데 솔직히 강의를 들어도 아직... 어디 던져놔도 제가 쓸수있는 수준이 아닌 거 같아서 기존 계획처럼 장기로 보는 게 맞는거 같네요..ㅎ아직 대학생 4학년이라는 점에서 시간은 없지만(?) 시간이 많다는(?) 부분덕에 DevOps의 길을 목표로 계속 잡을 수 있을 거 같습니다. 다만, 프론트라던지 백엔드라던지 Next.js나 Spring Boot를 중점으로 하고 있는데 공백이 길면 이쪽은 망하니 우선 개발자의 길을 한번 밟긴할 거 같아서 3~5년 정도 뒤의 목표로 잡아두고 우선은 개발자의 취업을 목표로 할 거 같아요저는 4주라는 기간동안 2주 동안은 정말 열심히 했고, 솔직히 후반 2주 동안은 졸작, 시험 공부, 팀플 등 이리 치이고 저리 치이고보니 뭐,, 온전히 집중하진 못했던 거 같습니다. 그래도 완주하려고 노력했고 분명 흘려들었던 부분이 있지만 다음에 다시 들었을 때는 좀 더 수월해질 거 같아요. 사실 DevOps라는 직종이 현직자들 사이에서 기업의 수준으로 나뉘고 있다고 전에 실무자가 말씀주시더라고요. 정확히는 좋은 개발 문화를 가진 회사의 5가지 조건 중 하나가 DevOps의 역량이 좋은 조직이였어요. 저는 저 직종을 들은게 작년이였으며, 생각보다 오래되지 않았고 대학에서도 그렇게 중요도를 높게 잡지 않는 거 같아요. 당장은 AI가 중요도를 높게 잡더라고요. 남들 다 AI하는데 혼자 DevOps는 조금 그렇지 않나 싶은데 이미 AI를 해보고 맞지 않아서 접었던 건 잘한 거 같긴합니다. 당장은 여러 가지 시도해보면서 버그도 마주치고 에러도 만나고 서버도 나가고 그럴꺼같지만, 우선은 성장의 한 걸음이 된 점에 있어서 감사한 강의입니다. 온전히 저의 것으로 만들진 못했지만 학습에 큰 도움이 된 점에 있어서 4주간 감사했습니다! 마지막으로 강사님의 200km 달리기는 정말 리스펙합니다.. 러너라고 설마 진짜 달릴 줄은 몰랐는데 저와 같은 대학 동기들에게도 소식을 전해주니 리스펙하고 있습니다!# 내용 정리 BlogHelm과 Kustomize 비교하며 사용-2ArgoCD 빠르게 레벨업-1ArgoCD 빠르게 레벨업-2ArgoCD 빠르게 레벨업-36. ArgoCD Github 업데이트

데브옵스 · 인프라

하얀종이개발자

인프런 워밍업 스터디 클럽 4기 데브옵스 - 4주차 발자국

4주차 요약 정리 섹션 18. Helm과 Kustomize 비교하며 사용하기Helm템플릿 기반 배포 도구로, values.yaml을 통해 반복되는 설정을 효율적으로 관리.helm install, upgrade, rollback 등으로 배포 전 과정을 CLI로 관리.디렉토리 구조(charts, templates, values.yaml)를 이해하고, 복잡한 애플리케이션의 배포를 간결하게 처리 가능.Kustomize오버레이 방식의 선언적 구성 도구로, 리소스를 직접 수정하지 않고 환경별 분리를 가능하게 함.base와 overlay 구조를 활용해 운영/개발 환경에 따라 다른 설정을 적용할 수 있어 유지보수에 유리.비교 및 활용Helm은 복잡한 구조, 패키징 중심에 유리하며, Kustomize는 간단한 환경 분리와 선언적 구성에 적합.YAML 중복, 설정 분리, 관리 효율성을 고려해 실제 파이프라인 설계 시 도구 선택 기준을 학습함. 섹션 19. ArgoCD 빠르게 레벨업 하기ArgoCD 개요GitOps 철학 기반의 배포 도구로, Git 저장소의 상태를 기준으로 Kubernetes 클러스터와 동기화.애플리케이션 상태를 UI 또는 CLI에서 실시간으로 모니터링하고 관리 가능.주요 구성 요소Server: Web UI 및 CLI API 제공Repo Server: Git 저장소로부터 manifest 불러오기Application Controller: Desired vs Live 상태 비교 후 동기화Kube API: Kubernetes 리소스와 통신Redis: 상태 캐싱Dex: SSO 등 외부 인증 연동실습 포인트Application 리소스를 정의해 Git과 쿠버네티스를 연결하고, 자동 배포 구성Argo Image Updater를 활용해 Docker 이미지 태그 변경 시 자동으로 Sync 수행Argo Rollouts로 Blue-Green, Canary 배포 전략 실습 → 트래픽을 점진적으로 전환하며 안정성 확보활용 및 이해수동 배포 없이 Git 상태만으로 자동 배포 관리 가능상태 기반 배포 시스템의 장점: 추적 가능성, 자동 롤백, 변경 이력 관리🌱 발자국 4주차 회고 💡 배운 점Helm과 Kustomize의 철학과 구조적 차이를 명확히 이해하고, 어떤 상황에 어떤 도구가 적합한지 판단할 기준을 마련함ArgoCD를 통해 GitOps 방식의 배포를 실현하고, Rollouts와 Image Updater를 통한 자동화와 고급 배포 전략을 경험함 🙆‍♂ 잘한 점Helm과 Kustomize의 구조 및 설정을 실습하며 실제 프로젝트 적용 가능성을 높임ArgoCD의 구성요소와 배포 흐름을 이해하며 도구 간 연동 방식을 익힘 ⚠ 아쉬운 점Helm/Kustomize의 고급 기능(예: 조건 분기, hooks 등)을 더 실습하지 못한 점Rollouts에서 트래픽 조정, 자동 중단 조건 등 실전 적용 요소를 더 경험해보지 못한 점 🎯 다음 목표실제 업무 환경에 도입할 수 있는 배포 전략을 시뮬레이션 해보기. ☺ 한마디도구는 단지 수단일 뿐,중요한 건 무엇을 위해, 어떻게 사용할 것인지에 대한 것을 많이 배운 시간이었습니다.4주라는 것이 길다면 길고 짧다면 짧은 시간이지만 의미있는 시간이었길 바랍니다.이제 도구들을 사용하는 방법을 알게 되었으니, 내가 어떻게 만들고 어떻게 적용해나갈지 끈임없이 고민해보겠습니다.그동안 너무 감사했습니다. ㅎㅎ 다들 고생 많으셨어요.

백엔드일프로쿠버네티스인프런워밍업데브옵스

rhkdtjd_12

[인프런 워밍업 클럽 4기] DevOps 발자국 4주차 - 일프로 부족할때 (TS러버)

1. Argo CD (Continuous Delivery)GitOps 기반 배포 도구Git 저장소에 있는 Kubernetes manifest를 지속적으로 감시Git의 Desired 상태와 클러스터의 Live 상태 간의 diff를 감지하여 Sync(동기화) 수행핵심 구성요소:Server: 웹 UI & CLI용 API 서버Repo Server: Git에서 manifest 읽어오기Application Controller: manifest 차이 비교 및 동기화KubeAPI: K8s 리소스와 통신Redis: 캐시 저장소Dex: SSO 등 외부 인증 연동2. Argo Image Updater도커 이미지의 태그 변경을 감지하여 ArgoCD에 자동 Sync 요청Helm 또는 Kustomize 기반의 ArgoCD 앱과 연동해야 함ArgoCD 앱과 Docker Registry 정보를 설정하여 자동 배포 가능3. Argo WorkflowsKubernetes 네이티브의 워크플로우 매니지먼트 툴Airflow / Kubeflow 같은 도구와 유사복잡한 배치 작업, ML 파이프라인 실행 등에 적합4. Argo EventsKafka 등과 연결 가능한 이벤트 기반 트리거 시스템Argo Workflow나 다른 시스템을 특정 이벤트 발생 시 자동 실행 가능5. Argo RolloutsArgoCD 없이도 단독 사용 가능K8s Deployment를 확장하여 고급 배포 전략 제공지원 배포 방식:Blue-Green: 프리뷰 환경 만들고, 트래픽 전환Canary: 설정된 step에 따라 점진적 트래픽 이동Service를 자동으로 연결/해제하며 배포 안정성 확보CRD를 기반으로 설정회고요약이번 주는 Argo의 전체 그림과 각 도구의 역할을 연결지어 이해한 소중한 시간이었다.실습이 부족했던 점은 아쉽지만, 개념 정리에 집중한 덕분에 뼈대를 잡을 수 있었다.다음 주는 실습 위주로 구체적인 사용 경험을 쌓는 것을 목표로 삼고 싶다. 

데브옵스 · 인프라쿠버네티스

승현

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

이번주 학습 내용Helm vs Kustomize의 차이를 이어서 학습했다.Helm은 Chart로 환경별 설정이 가능하여 관리가 용이한 점kustomize는 기존 설정을 overlay로 A+B를 결합하는 형식이어서 조금 더 직관적인 느낌만약 처음 선택을 해야 한다면 서비스의 갯수에 따라서 Helm과 kustomize에 대해서 고민할 것 같다. 관리해야 하는 서비스가 많을 수록 kustomize가 주는 장점이 오히려 관리 포인트가 되어 단점이 될 수 있다고 생각이 들고, Helm이 주는 장점이 더 두각되는 것 같다.ArgoCD의 동작방식에 대해서는 아직 학습이 덜되어서 다시 한번 학습이 필요할 것 같다.회고마지막 발자국이라는 것이 믿기지가 않는다, 직장, 학업으로 매주 힘들었지만 같이 해준 팀원 분들에게 너무 감사하고, 좋은 분들을 만날 수 있어서 감사했다. 또한 중간점검을 통해 강사님이 강의에 투자하는 시간을 알게되었는데 높은 품질의 강의는 확실히 다른다는 것을 알게 되었고, 양질의 내용을 제공해주기 위해 준비해주신 강사님께도 감사하다.스터디는 이번주에 종료되지만 여기서 멈추는 것이 아니라 내것으로 만들기 위해 복습하는 시간을 가지려고 한다.💪💪

데브옵스 · 인프라워밍업클럽인프런스터디4기데브옵스Devops

박서연

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

[인프런 워밍업 클럽 스터디 4기 - 백엔드]강의 출처 :Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드4주차 발자국 강의 수강학습 내용 요약Spring & JPA 기반 테스트 : Presentation Layer 테스트(@WebMvcTest, Mock와 Mockito, @MockBean, @RestControllerAdvice, @ExceptionHandler, Spring Bean Validation)Mock을 마주하는 자세 : Mockito로 Stubbing하기, Test Double(Dummy, Fake, Stub-상태검증, Spy, Mock-행위 검증), 순수 Mockito로 검증하기(@Mock, @Spy, InjectMocks), BDDMockito, Classicist VS Mockist더 나은 테스트를 작성하기 위한 구체적 조언 : 한 문단에 한 주제, 완벽하게 제어하기, 테스트 환경의 독립성 보장, 한 눈에 들어오는 Test Fixture 구성(@BeforeAll, @BeforeEach, setUp), Text Fixture 클렌징(deleteAllInBatch, deleteAll), @DynamicTest, 환경 통합하기, private 메서드 테스트 x, 테스트에만 필요한 메서드도 가능함Appendix : 학습 테스트, Spring REST Docs(REST Docs, Swagger, AsciiDoc)Outro : 테스트를 작성하는 마음가짐(시간, 충분한 연습, 테스트 케이스에 대해 생각해보기) 4주차 회고 이번 주는 드디어 테스트 작성 강의를 완강한 주였습니다. Presentation Layer 테스트부터 Mockito를 활용한 테스트 더블까지 다양한 내용을 배우면서, 평소 개념만 어렴풋이 알고 있던 Mock 객체들을 제대로 배울 수 있어서 정말 좋았습니다. 특히 Spring Bean Validation이나 MockMVC 등을 실제로 사용하며 학습하니, 이론만 공부할 때는 애매했던 부분들도 확실히 이해가 되어서 뿌듯했습니다. 또한, 테스트 코드를 작성하면서 자연스럽게 코드 자체의 가독성이나 명확성을 고민하게 되어, 코드 작성뿐 아니라 글쓰기에 대해서도 많이 배울 수 있었습니다.지난 금요일에는 2차 중간 점검 라이브도 진행되었는데, 다른 분들의 Q&A를 들으면서 같이 공부도 되고 다양한 정보도 얻을 수 있어 좋았고, 코드 리뷰를 통해서 다른 분들의 코드를 내 코드와 비교해보고 고칠 점이나 배울 점을 정말 많이 느낄 수 있었습니다. 직접 코드 리뷰를 신청하지는 못했지만, 그럼에도 함께 리뷰하는 것만으로도 여러가지 좋은 아이디어를 얻을 수 있는 시간이었습니다!스스로 칭찬할 점은 이번 주 강의 수강과 미션을 미루지 않고 당일에 잘 해냈다는 점과, 4주간 꾸준히 노력하며 스터디를 무사히 완주했다는 점입니다. 처음에는 제가 잘 따라갈 수 있을 지 조금 걱정되긴 했지만, 포기하지 않고 뭔가를 꾸준히 해내고 성취했다는 사실이 정말 기쁘고 뿌듯했습니다. 아쉬운 점은 스터디를 하면서 좀 더 적극적으로 참여했더라면 좋았을 것 같다는 생각이 듭니다. 특히 코드 리뷰 신청 같은 기회들을 더 적극적으로 활용하지 못한 것이 아쉽습니다. 스터디를 마치면서 돌이켜보면, 그동안 뭔가를 해야 한다고 생각하면서도 자꾸 미루고 시작조차 하지 못하던 나 자신에게 변화를 줄 수 있는 좋은 계기가 된 것 같습니다. 스터디를 통해 클린 코드와 테스트 코드에 대해서도 많이 배웠고, 무엇보다 꾸준히 무언가를 해나가는 습관 또한 형성하게 되어 좋았습니다. 또한, 다른 분들의 발자국을 보며 여러 가지 다양한 생각을 접할 수 있어서 즐겁고 유익했습니다. 이번 스터디를 통해 얻은 것을 앞으로 실제 프로젝트에서도 적극적으로 적용하며 더 성장하고 싶습니다. 미션미션 해결 과정 미션 5 (Day16) : 이번 미션에서는 Layered Architecture의 각 레이어가 어떤 책임을 가지는지 스스로 정리해보았습니다. Controller, Service, Repository 각각의 역할과 테스트 방법을 실제 사용 흐름을 떠올리며 정리했고, MockMvc, 단위 테스트, 통합 테스트가 어느 레이어와 어울리는지도 고민하며 정리했습니다. 단순히 암기하는 게 아니라, 내가 다른 사람에게 직접 설명하듯이 이해 중심으로 써보려고 노력했습니다.미션 6 (Day18) : 이번 미션에서는 Mockito의 주요 어노테이션인 @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 각각의 특징과 차이를 정리해보았습니다. 스프링 환경 여부나 진짜 객체인지 여부, 주입 방식 등 조건에 따라 어떤 상황에서 어떤 어노테이션을 써야 할지 더 정확하게 구분할 수 있게 되었고, 지금까지 흐릿했던 개념들이 많이 정리되었습니다.또 테스트 흐름을 명확히 하려고 각 테스트 항목을 @BeforeEach, given절, when절 구조에 맞게 나누어 배치해봤습니다. 예를 들어 댓글 테스트에서는 공통적으로 필요한 사용자와 게시물 생성을 @BeforeEach에 미리 두고, 테스트마다 필요한 사전 작업은 given에, 핵심 동작은 when에 넣어서 흐름을 더 읽기 쉽게 구성했습니다. 회고이번 주 미션을 하면서 테스트 코드가 단순히 결과를 확인하는 도구가 아니라, 기능을 이해하고 구조를 설계하는 데 꼭 필요한 도구라는 것을 다시 한 번 느꼈습니다. 특히 레이어별 역할을 직접 정리하면서, 내가 작성하는 코드가 어떤 위치에서 어떤 책임을 가져야 하는지 더 명확하게 구분할 수 있었고, 앞으로 코드 작성 시에도 이런 기준을 계속 의식하게 될 것 같습니다.Mockito 어노테이션도 이번 기회에 비교해 정리하면서 그동안 헷갈렸던 부분들이 한 번에 정리되었고, 테스트 흐름을 given-when-then 구조로 나누어 작성해보면서 테스트 코드 자체가 문서처럼 읽히게 만드는 것도 중요하다는 점을 알게 되었습니다. 처음에는 주석으로만 흐름을 표시했던 코드들을 어떻게 정리할지 막막했지만, 테스트의 목적에 집중하며 구조를 잡아가는 과정이 점점 익숙해졌고, 그 안에서 테스트가 곧 설계와 연결된다는 감각도 생겼습니다.그동안 미션을 진행하며 매번 배운 내용을 복습하고, 개념을 내 언어로 정리해보는 과정 자체가 학습에 큰 도움이 되었고, 직접 코드를 작성하고 흐름을 따라가며 '이제는 진짜 이해했다'는 자신감도 생겼습니다. 앞으로도 기능 구현과 테스트를 함께 이어가며, 테스트를 설계의 나침반처럼 활용할 수 있도록 꾸준히 연습하고 습관을 쌓아가고 싶습니다!!

백엔드워밍업클럽

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

Practical Testing: 실용적인 테스트 가이드 강의를 수강하고 작성한 회고입니다.  1. 학습한 내용 및 회고Validation에 대한 책임 분리도메인 로직에 해당하는 것을 Presentation 레이어에서 검증을 하는 게 맞는지 고민해봐야 한다.Test DoubleStub : 요청한 것에 대해 어떤 값을 리턴할 지 만들어놓는 객체 (상태 검증)Spy : Stub처럼 행동하면서, 메서드 호출 내용이나 횟수를 기록Mock : 요청한 방식대로 동작하도록 하는 객체 (행위 검증)관련 어노테이션@Mock : 가짜 객체 (Mock 객체)로 생성하고 싶을 때 사용@Spy : 행위에 대한 기록(몇 번 호출됐는지, 타임아웃 검증)을 하는 객체@InjectMocks : Mock이나 Spy 객체들을 테스트 클래스의 의존성으로 주입한다.BBDMockitoMockito를 BDD 스타일로 바꾼 것으로 기능을 Mockito와 동일하다.Classicist와 MockistClassicist는 협력 객체를 처리할 때 실제 객체를 사용하고, Mock은 꼭 필요한 외부 의존성 정도로만 제한한다.Mockist는 모든 협력 객체를 Mock으로 처리한다. 테스트 대상 객체가 어떤 협력 객체와 어떤 메시지를 주고받는지에 초점을 맞춘다.더 나은 테스트를 작성하기 위해한 문단에는 한 주제가 들어가는 게 좋은데, 그걸 확인하는 방법 중 하나가 'DisplayName을 한 문장으로 할 수 있는가'를 생각해보는 것이다.모든 구조를 개발자가 완벽하게 제어할 수 있어야 한다.현재 시간이나 랜덤값처럼 개발자가 제어할 수 없는 부분은 상위 계층으로 분리하거나 mocking 처리를 하자.Test Fixture@BeforeAll : test class 실행 전에 한번 실행된다.@BeforeEach : test method 실행 전에 실행된다.@AfterEach : 하나의 테스트가 끝날 때마다 실행된다.공유 자원은 되도록이면 쓰지말자.그럼 언제 BeforeEach를 쓰는가 각 테스트 메서드 입장에서 공유 자원을 아예 몰라도 테스트 내용을 이해하는 데에 문제가 없을 때공유 자원을 수정해도 모든 테스트에 영향을 주지 않을 때Test Fixture 클렌징deleteAllInBatch() 바로 DELETE 쿼리 실행한 번의 쿼리로 테이블 전체 삭제deleteAll()엔티티를 하나씩 조회한 후 삭제한다.내부적으로 각 엔티티를 개별적으로 삭제@ParameterizedTest하나의 테스트에서 여러 값에 대한 테스트를 진행하고 싶을 때 사용한다.여러 값에 대해 같은 로직을 테스트하고 싶을 때 분기문보단 @ParameterizedTest로 하는게 더 이해하기 좋다. @DynamicTest같은 환경에서 단계적인 시나리오 테스트할 때 사용한다.만약 private 메서드를 테스트하고 싶다면 객체 분리가 덜 되었는지 생각해보자.  회고강의를 들으면서 테스트에 대한 생각이 많이 바뀌었다. 테스트 코드가 중요한 건 알았지만, 항상 프로젝트를 할 때 시간적 제약이 있기 때문에 테스트 코드는 항상 우선순위가 밀렸었다. '가까이 보면 느리지만, 멀리 보면 가장 빠르다'라는 말처럼 테스트 코드가 우선순위에서 밀려야 하는 존재가 아니라는 걸 깨달았고, 앞으로도 테스트 코드는 꼭 작성해야겠다고 생각했다.Classicist와 Mockist에 대해서도 생각해봤는데, 우빈님과 동일하게 나도 Classicist에 더 가깝다고 생각한다. 모든 협력 객체를 mocking 처리해버리면, 실제 여러 모듈을 결합해서 동작시켰을 때 어떤 문제가 발생할지 알 수 없다. 그리고 classicist 방식이 실제 객체 간의 흐름을 검증할 수 있기 때문에 더 신뢰할 수 있는 테스트를 작성할 수 있다고 생각했다.  이제 4주간의 여정이 끝났다. 그냥 혼자 강의 들으면서 공부할 수도 있는 건데, 이렇게 워밍업 클럽을 통해서 미션도 하고, 라이브 세션도 들으면서 혼자였다면 몰랐던 인사이트도 많이 얻어 가는 것 같다. 아주 의미 있는 4주였다.4주 동안 열심히 공부한 내용을 바탕으로 앞으로의 개발 경험에 잘 활용해나가고 싶다.  2. 미션 회고레이어별 테스트의 특징을 고민해보면서, 테스트 코드를 어떻게 작성해야 할지에 대한 감을 조금씩 잡을 수 있었다. 그리고, 그동안은 @Mock 정도의 간단한 Mocking만 사용해봤는데, 이번에는 @Mock, @Spy 등 다양한 애노테이션을 직접 활용해보면서 각 어노테이션의 역할과 차이를 명확하게 이해할 수 있었다.또한 어떤 상황에서 공유 자원을 사용하는 것이 적절한지에 대해서도 고민해볼 수 있는 좋은 기회였다.  출처Practical Testing: 실용적인 테스트 가이드   

[인프런 워밍업 클럽] 4주차 회고

학습 내용데이터 축적을 위한 기본 개념데이터는 투자가 필요한 자원 : 데이터 축적 정의, 코딩 작업, 툴 등등Event based analytics기본개념 : event, propertyevent taxonomy 설계 과정이름짓는 규칙 PA 주요개념들이벤트 베이스트 애널리틱스이벤트 기반 데이터분석유저와 제품 사이 일어나는 상호작용회원가입, 버튼 클릭, 결제 등등툴을 갖추면 할 수 있는 것들제품 툴을 쓰면 데이터 엔지니어링 역량이 뛰어나지않아도, 데이터 분석가가 없어도 데이터를 분석할 수 있음이벤트 프로퍼티이벤트에 수반되는 상세 정보파라미터, 어트리뷰트라고도 불림 eg : 상품 카테고리, 세부 카테고리, 제품 이름, 제품 id, 쿠폰 적용여부 등등… 데이터 드릴다운을 위해 필수적이다..user property 유저의 세부정보유저 이름, 회원가입일, 나이, 사용 기기 등등상용pa툴에서 자동으로 수집되는 사용자 정보가 있으나 다른 유저 속성은 우리가 직접 정의 필요어느 시점에 업데이트할것인지 정의 필요eg 이메일, 나이, 지역, 누적 구매금액 등등사용자가 행동하는 값에 따라 달라지는 유저 프로퍼티가 최신성을 유지하려면 언제 업데이트 할 것인지 시점도 정의해야한다.DATA TYPES나이는 숫자 타입으로 저장한다 등등numeric(숫자): 사칙연산 등 각종계산 가능datetime 날짜와 시간string 문자열 / 숫자로 이루어진 문자열도 있음boolean : 예/아니오, t/fEnumerated 제한된 집합 안에서 하나의 값을 택하는 경우 > string보다 효율적임client side/server side데이터 트래킹을 위해 데이터 전송 작업 필요client side : 유저 pc, 스마트폰에서 바로 툴 업체 데이터로 전송장점클라이언트 사이드에서만 발생하는 유저 인터랙션 트래킹가능 익명 유저 트래킹가능단점광고차단으로 데이터 유실 가능수정사항 즉시 업뎃 어려움(배포해야됨..)서버 사이드에서만 일어나는 이벤트는 트래킹 불가함server side : 유저 데이터 서버로 전송장점 : 데이터 유실안됨, 수정 즉시반영가능, 서버사이드 트래킹가능단점 : 유저 인터랙션 트래킹 어려움, 익명 유저 트래킹하려면 추가작업 필요-> 그래서 결국 둘 다 쓴다-> 이벤트데이터를 어디서 트래킹 할 것인지 정의해야함 Event taxonomy 설계taxonomy : 분류 체계Event taxonomy 설계 : 이벤트 트래킹 플랜 만들기접근법탑다운 접근 데이터 활용 목적 정의하기비즈니스 퀘스쳔목적 달성을 위해 알아야 할것, 답을 찾아야 할 질문 정의하기이벤트 및 이벤트 프로퍼티 정의하기바텀업 접근제품의 주요 이벤트는 무엇인가에서 시작핵심 이벤트(결제 조회 등등)하지만 주로 탑다운으로 접근하는 것을 추천데이터 설계할 때부터 나중에 어떻게 활용할까 염두해야 한다처음부터 모든 이벤트를 트래킹하지말고 꼭 필요한 이벤트만 트래킹하자처음에는 30~50개부터 시작탑다운 접근법aaerm측면에서 접근하기naming convention일관성띄어쓰기 하이픈 대문자 명사동사 사용 등등명확성여러 사람이 보는 데이터기 때문에 한번에 알아볼 수 있어야함'국제 공용어인 영어 사용이벤트 세분화 : 얼마나 잘게 쪼갤까?정답은 없으나 각 행동이 구분되는 서로 다른 행동인지/본질적으로 같은 행동인지 판단해서 정의이벤트 텍소노미 문서만들기들어가야 할 항목네이밍 컨벤션 :이름 규칙,대소문자 띄어쓰기 하이픈 밑줄 등등이벤트 정보어떤 이벤트? 이벤트 이름, 클라이언트/서버사이드 구분 현재 구현단계 등등이벤트 프로퍼티 정보유저 프로퍼티 정보문서 작성, 관리 할 때기존 템플릿 활용하는 것을 추천데이터베이스 방식으로 정리하기노션처럼 문서에서 관계형 데이터베이스를 활용할 수 있는 툴 사용이벤트와 프로퍼티 사이 관계 정리하기 등등사후관리제품 변화에 따라 이벤트 트래킹/업뎃 필요사후관리 실수…네이밍 컨밴션 규칙 어기기오탈자, 불필요한 공백 넣기 등등새로운 기능을 개발하면서 데이터 트래킹 계획을 세우지 않기이벤트 프로퍼티 넣어놓고 문서 업데이트 안하기★데이터로 일하려면 : 데이터 축적 + 트래킹 + 사후관리 잘하기★ product discovery프로덕트 디스커버리 = 무엇을 만들지 결정하는 과정(decding what to duild)목표 : Valuable, usable, feasible, viable한 솔루션을 찾는 것왜 디스커버리라는 단어를 썼을까?아이디어>제품로드맵>요구사항>디자인>개발이라는 구시대적 제품 개발 프로세스 비판을 위해 등장신약 개발에서 discovery라는 용어를 사용하는 것에 착안하여 사용하기 시작함이터레이션과 실험큰 기회비용 발생 전 미리 assumption 검증해야함아이디어를 개발완료 후 마지막에 검증 XA/B테스트, 사용성 테스트 들은 방법일 뿐 그 자체가 실험은 아님문제 발견과 솔루션 발견솔루션 발견에 더 많은 리소스 투여하기아이디어 개발 완료 후 다음 프로젝트로 넘어가는 것이 아닌 지속적으로 성장시키기 위해 노력해야함Assumptions 검증 방법종류에 따라 적합한 검증 방법이 달라질 수 있음인터뷰, 설문조사, 시제품, 데이터분석, A/B 테스트, 사용성 테스트 등등낮은 비용, 빠르게 검증할 방법을 찾는 것이 가장 중요함100% 확신 가능한 결과를 낼 수 있는 검증방법은 없음 -> 실행을 위한 결단력이 필요함지양해야 할 전략자유롭게 아이디어를 낸다는 차원에서 난사하듯 아이데이션 하고 실행하는 것백로그..ICE scoring 전략의 역할 : 판단과 의사결정의 기준이 되는 Guiding Policy가 된다. -> 전략에 맞는 액션에 선택과 집중 가능무엇을 하지 않을 것인가를 정하는 것전략 방법Opportunity solution treeNorthstar Frame work공통점성과를 위한 비구조적 문제를 구조화하는 방법opportunity space, solution space 충분히 탐색하기의사결정에 도움을 주는 개념적 지도전략 : Opportunity solution tree하나의 목표 정의하기기회(니즈, 욕구, 페인 포인트) 발굴하기기회 맵핑하기 : 그룹핑, 세분화집중할 기회 정하기opportunity space 탐색솔루션 아이데이션 진행솔루션 assumptions 검증 및 실행 전략 : Northstar Frame workoutput-input 지표를 연결하는 metric hirechy에서 확장된 버전6개월~1년 단위로 꾸준히 점검 필요 구성 단계North Star Metric제품 조직이 영향을 끼칠 수 있다고 믿어지는 레벨의 지표중장기적 사업성과, 고객 가치를 제공하지 못하는 지표는 제외Input실험을 통해 인과관계 검증된 경우인과관계 확인 전 가정하여 설정 가능Opportunity고객니즈 페인포인트, 욕구, 솔루션의 방향성intervention솔루션Product Growth전제 : 제품에 확실한 핵심 가치가 있는 경우product worth solving 진행 필요중요한 문제인가?타당한 비즈니스 모델을 만들 수 있는가?시장에서 반응 있는 제품의 성장을 가속화 하는 방법그로스 <> A/B 테스트 여러번 하기실험이 성장에 기여하기 위해 탄탄한 기반이 필요함탄탄한 기반 : 그로스 레버, 그로스 모델에 대한 이해그로스 레버 : Acquisition, Retention, Monetization그로스 레버 : Acquisition마케팅, 세일즈를 통한 획득을 주로 생각하지만 제품을 활용해서도 고객 획득 가능방법레퍼럴 : 사용자들이 다른 사용들을 초대eg : 페이스북, 링크드인 등 소셜미디어, 드롭박스, 에어비앤조건고객이 제품에 만족고객이 제품의 가치를 금방 실감할 수 있다(quick time to value)많은 사람에게 어필할 수 있는 제품(broad value proposition)모든 제품이 바이럴하게 성장할 필요는 없음을 염두하기네트워크 효과 : 사용자가 늘어날수록 사용자가 가치를 얻을 수 있음(eg : 전화, SNS)제품이 광고판이 됨eg : calendly(일정을 잡으려다보니 서비스를 공유하게되고 자연스럽게 홍보가 됨) UGC로 사용자 유입시키기UGC : user generated content사용자가 제품을 이용하면서 자연스럽게 컨텐츠를 만듦사용자가 다른 사용자에게 공유eg : 스티비 뉴스레터, 타입폼검색엔진 노출eg : 핀터레스트, 트립어드바이저 다름 제품과의 연동(integration)eg세일즈포스 - 줌, 링크드인 등 연동 가능grammarly - 웹프로그램에 확장프로그램으로 제공우버 - 구글 지도와 연동B2B제품 : 제품 주도 성장(product-Led Growth, PLG)배경전통적 방식 : 영업자를 통해 고객를 획득해 탑다운 방식으로 유저들이 이용하게 됨최근 : 유저들이 제품 선경험 후 회사에 확산(바텀업)되어 공식 툴이 됨self-serve 방식 : 슬랙, 노션 등단, 세일즈를 대체하지는 않음작은 기업들 확보에 효과 

기획 · PM· PO

이승환

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

학습 내용 요약 Spring & JPA 기반 테스트 (Presentation Layer 테스트 (2) ~)@WebMvcTest를 활용한 컨트롤러의 테스트 방법을 학습했습니다. Mock을 마주하는 자세테스트 하기 어려운 영역을 어떻게 분리하고 처리할 수 있는 지에 대한 방법으로 Mock에 대한 개념을 학습했습니다.Mockito를 활용하여 Test Fixture를 준비하는 과정도 함께 배웠습니다. 더 나은 테스트를 작성하기 위한 구체적 조언가독성 높은 테스트 코드를 작성하는 방법과,스프링 서버 구동 횟수를 줄이는 방법 등을 배울 수 있었습니다.@DynamicTest, @ParameterizedTest 같은 다양한 테스트 방식도 익힐 수 있었습니다. Appendix학습 테스트와 Spring REST Docs에 대해 배웠습니다.설정이 복잡했지만 협업 시 매우 유용할 것이라는 느낌을 받을 수 있었습니다. Outro지금까지 배웠던 내용을 돌이켜보며,테스트가 필요한 이유에 대해 다시 한 번 생각해볼 수 있었습니다.  학습 내용 회고 🌱성취@WebMvcTest를 활용해 컨트롤러 테스트를 작성할 수 있는 역량을 갖추게 되었습니다.Mocking을 통해 테스트하기 어려운 영역을 분리하고 제어하는 방법을 익혔습니다.테스트 실행 환경을 통일해 스프링 서버 실행 시간을 단축할 수 있게 됐습니다. Spring REST Docs로 API 명세서를 작성하는 방법을 익혔습니다. ⚠발견한 약점 및 보완이 필요한 부분Day11 미션에서 전체 테스트를 실행하지 않고 미션을 제출하는 바람에, 깨지는 테스트를 포함한 채 제출했습니다. CSV를 읽어 객체로 변환하는 구현체에 집중하느라, 테스트 포인트 선정에 실패했습니다.CSV 파일은 항상 정해진 숫자만 읽어올텐데, 이런 경우에도 경계값 테스트를 해야할까?CSV 경로에는 null이 들어올 수 없을 것 같은데, null 테스트를 해야 할까?테스트 코드에 구현 레벨의 상세한 계산식을 그대로 드러냈습니다.setUp 메서드를 단순히 중복 제거 용도로만 인식했습니다. 📄보완 계획테스트를 작성 후 제출 전, 반드시 전체 테스트를 실행하겠습니다."이걸 꼭 검증해야할까?" 에 집중하기보단, 미래의 변경 가능성을 고려한 견고한 테스트를 작성하는 것에 집중하겠습니다.프로덕션 코드의 구현을 블랙박스로 보고, 입출력 검증을 중심으로 테스트하겠습니다.setUp은 단순히 중복 제거보다, 도메인에 집중하게 돕는 역할임을 인지하며 테스트 코드를 작성하겠습니다.   미션 내용 회고 Day16 미션 🌱 🏃첫 번째 미션Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다.레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요? 미션 과정각 레이어의 관심사와 경계를 의식하며, 테스트 방법을 최대한 간단히 정리해보려 노력했습니다. 🔗미션 해결 링크https://inf.run/SoKcd  Day18 미션 🌱 🏃첫 번째 미션@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다. 미션 과정강의에서 배운 내용을 기반으로 Mock과 Spy를 복습하며 차이점을 정리했습니다.MockBean과 SpyBean은 검색과 LLM을 활용해 추가 학습 후 요약했습니다. 🏃두 번째 미션아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치) 미션 과정가독성과 중복 제거의 관점으로 미션에 접근했습니다.온라인 세션 피드백을 통해,setUp은 단순 중복 제거가 아닌, 도메인에 집중할 수 있게 만들어 준다는 점을 깨달았습니다. 🔗미션 해결 링크https://inf.run/9LaUQ  느낀점 이번 워밍업 클럽은 저에게 두 번째 도전이었습니다. 작년에 이 강의를 처음 접했었는데,그 당시에 저는 Spring과 JPA를 잘 모르는 상태로 수강을 시작했었습니다."테스트 코드를 작성해보는 게 좋다"는 조언만 듣고 무작정 수강했지만,기초가 튼튼하지 않다보니 내용을 따라가는 것에 급급했고,이해가 부족해 강의 하나를 수강하는 데에도 시간이 세 배는 더 걸렸습니다.결국 완주도 하지 못한 채 아쉬운 마음으로 마무리하게 되었습니다. 그 때 다짐했습니다."이번엔 Spring과 JPA 기본기를 탄탄히 다진 후, 다시 도전해보자." 이후 약 4개월간,김영한 님의 Spring과 JPA 커리큘럼을 꾸준히 학습하며 기초를 탄탄히 쌓는 데 집중했습니다.매일 하루 대부분의 시간을 투자해 결국 전체 커리큘럼을 완주할 수 있었고,마침 워밍업 클럽이 다시 열린다는 소식을 접하게 되었습니다. 이번에는 제대로 이해해보자는 마음으로 워밍업 클럽에 다시 참여했고,스터디에도 적극적으로 임했습니다.덕분에 이전에는 막막하게만 느껴지던 개념들도이제는 한층 명확하게 다가왔고, 강의에서 강조하는 '가독성'에 집중할 수 있게 되었습니다. 이 경험을 통해,"이해는 반복과 지식의 축적 속에서 깊어진다"는 사실을 실감할 수 있었습니다.  [깨달음 1: 이해와 가독성의 연결 고리]수학 강사 시절부터"제대로 이해했다면, 초등학생도 이해할 수 있을 만큼 쉽게 설명할 수 있어야 한다"는 가치를 중요시 여겨왔습니다. 이번 워밍업 클럽에서 배운좋은코드는 쉽게 읽을 수 있어야 한다는 원칙은제 가치관과 맞닿아 있었고,이해와 가독성에 연결 고리가 형성되며가독성의 중요성에 대해 더욱 깊이 깨달을 수 있었습니다.  [깨달음 2: 코드 작성은 글쓰기와 비슷하다]또한 이번 학습을 통해"코드 작성은 글쓰기와 비슷하다"는 말을 처음으로 실감했습니다.그동안은 그저 인용구 같은 느낌으로만 흘려들었지만,이제는 코드와 글쓰기가 가독성의 관점에서 얼마나 닮아있는지 직접 경험하고 이해할 수 있게 되었습니다.  [앞으로의 계획]그래서 저는 앞으로 코드 작성과 더불어 글쓰기 능력도 함께 향상 시키고자 합니다. 우빈님께서 조언해주신 대로,좋은 문장을 필사하고, 구조를 분석하며, 블로그나 Notion에 꾸준히 글을 쓰며표현력과 구조화 능력을 함께 키워나갈 계획입니다. 그리고 코드 작성을 위해 학습할 서적은 다음과 같습니다.《클린 코드》《테스트 주도 개발》《파이브 라인스 오브 코드》그리고 추가로 우빈님께 추천 받은 《클린 아키텍처》 하나씩 차근차근 학습해나가겠습니다!  [마지막으로]온라인 세션을 통해 받은 두 번의 코드 리뷰 경험은 정말 값지고 기억에 남는 경험이었습니다. 항상 좋은 코드 리뷰를 갈망해왔기에이번 피드백은 특히 기억에 남았고,피드백을 통해 성장하는 즐거움도 함께 느낄 수 있었습니다. 반면,"앞으로도 이런 좋은 리뷰를 계속해서 받을 수 있다면 얼마나 좋을까.."하는 갈증도 함께 커졌습니다. 😅 사실 작년 인프런에서 진행된 네트워크 파티에 참여해 끝나갈 무렵, 우빈 님을 잠깐 뵌 적이 있습니다. 이야기를 나눌 수 있었던 시간은 아주 짧았지만,그 순간조차 저에게는 "더 열심히 해야겠다"는 동기 부여가 될 만큼 인상 깊었습니다.그 때 받았던 사인은 저에게 단순한 기념 이상의 의미였고,그 날 이후로 저도 우빈 님처럼 진지하게 학습하고 성장하는 개발자가 되고 싶다는 마음이 커졌습니다. 만약 우수러너로 선정되어 1:1 멘토링 기회를 얻게 된다면,그동안의 배움을 돌아보고 앞으로의 방향을 점검해볼 수 있는 정말 소중한 시간이 될 것 같습니다. 우빈 님께 많은 것을 배운 것처럼,저 또한 제가 받은 배움과 경험을 다른 이들과 나눌 수 있는 개발자,그리고 함께 성장하며 시너지를 만들 수 있는 백엔드 개발자로 나아가고 싶습니다.우빈님께 정말 많은 것을 배웠습니다.진심으로 감사드립니다. ️🙂‍↕️ 강의🔗Practical Testing: 실용적인 테스트 가이드

백엔드워밍업클럽백엔드발자국

인프런 워밍업 클럽 4기 BE - 4번째 발자국

✅ 강의 수강학습 요약Test Double -Mock과 Stub, @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks BDDMockitoMockist vs Classicist 테스트 작성 원칙테스트는 하나의 목적만 가지도록외부 요인에 영향을 받지 않는 고정된 Fixture필요한 데이터는 given절에, 간접 데이터는 @BeforeEach 등에서 처리테스트에 필요한 데이터는 가능한 제어 가능한 값으로 구성테스트 수행도 비용이므로 공통 환경을 모아 관리 학습 테스트 를 통해 익숙하지 않은 라이브러리나 기술을 익힐 수 있음! 학습 회고4주간의 과정 동안 흐릿했던 지식들이 조금씩 뚜렷해지고, 그동안 무심코 사용하던 어노테이션이나 리팩토링과 테스트코드에 대해 왜 그런 식으로 쓰는지, 더 나은 방식은 무엇인지를 고민해볼 수 있었다. 처음에는 단순히 돌아가는 코드에 대한 확인 정도로만 생각했던 테스트를 이제는 책임을 분리하고 안정적인 코드를 만들기 위한 구조적 도구라고 인식하게 되었다. 4주동안 끝까지 놓지 않고 따라온 것 자체가 개인적으로는 작지 않은 성취였다. 리팩토링 / 테스트코드를 막연하게 두려워하기보다 하나씩 풀어나갈 수 있겠다는 마음이 생긴 것에 의미를 두고 싶다. 정말 좋은 기회였다고 느낀다! ✅ 미션해결 과정1번 미션에서는 Mockito와 Spring 환경에서 각각 사용되는 테스트 어노테이션들의 차이를 실제 사용 맥락을 중심으로 정리했다.2번 미션에서는 각 객체 생성 및 동작을 어떤 위치에 배치할지 고민하며, 공통된 준비와 사용자와 게시물은 @BeforeEach에, 댓글을 직접적인 테스트 도메인으로 보고 given절에서 명시적으로 생성하는 구조로 정리했다. 미션 회고어노테이션의 차이를 정리하면서 어떤 상황에서 어떤 걸 써야 좋을지 기준을 명확히 할 수 있었다. 테스트 흐름을 구성해보는 미션을 하면서 결국 핵심 도메인이 무엇인지, 어떤 객체가 테스트의 중심 대상인지 판단하고 구조를 잡는 게 중요하다는 걸 배웠다. 앞으로 테스트를 구성할 때도 도메인 흐름과 역할을 기준으로 구분하는 습관을 유지하고 싶다.

백엔드인프런워밍업클럽

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

미션 내용1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.@MockSpring Context와 무관실제 객체 대신 가짜(mock) 객체 생성@Spy일부는 실제 객체처럼 , 일부는 Stubbing이 가능@InjectMocks테스트 대상 객체에 필요한 Mock들을 자동 주입@MockBeanSpring Context에 등록된 기존 Bean을 Mock으로 교체전체 애플리케이션 컨텍스트가 필요한 통합 테스트에서 주로 사용 @SpyBeanSpring Context에 등록된 기존 Bean을 Spy로 교체  아래 3개의 테스트가 있습니다. ✔ 게시판 게시물에 달리는 댓글을 담당하는 Service Test✔ 댓글을 달기 위해서는 게시물과 사용자가 필요하다.✔ 게시물을 올리기 위해서는 사용자가 필요하다.내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@BeforeEach void setUp() { 사용자 생성에 필요한 내용 준비 사용자 생성 게시물 생성에 필요한 내용 준비 게시물 생성 } @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 검증 }   출처 : 🔗Practical Testing: 실용적인 테스트 가이드

백엔드

Min

인프런 워밍업 클럽 4기 - DevOps 4주차 발자국

k8s 리소스를 직접 배포하는것 보다 heml Kustomize 등의 배포 패키지 툴을 사용하는게 더 좋다.헬름은 Chart라는 패키지 형태로 앱을 관리하며, 템플릿 기반 manifest에, values-{stage}.yaml, Chart.yaml, _hepers.tpl, helm command --set option 등을 활용해 동적으로 값을 주입할 수 있다.반면 Kustomize는 Base 설정에 Overlay 방식으로 특정 환경에 대한 설정들을 덮어서 환경별로 다른 설정을 적용한다.overlay하는 dir에 있는 모든 리소스를 적용하느게 아니라 kustomization.yaml 에 설정된 config에 따라 부분 적용이 가능하다.배포 파이프라인을 만들면서 드는 고민과 답변들배포에 사용되는 여러 툴 인증정보를 어떻게 관리할 것인가 > 배포툴의 Credentials 에 한번 정보를 기입해두고, 각 스테이지 별로 Credential 의 데이터를 가져와 처리하도록 한다. (cicd 서버 디스크에 직접 저장 X)젠킨스 Credential, docker-credential-helpers..)컨테이너 이미지 용량 관리 > 이미지별 LC를 두고 제거k8s 는 도커와 달리 이미지를 처리하는 gc 가 존재해, config 에 따로 설정하지 않으면 자동으로 안쓰는 이미지를 제거해준다.네임 스페이스 관리 > 특히 여러 서비스가 존재하고 독립적으로 관리된다면, NS 설정은 리소스 배포와 독립적으로 두는게 낫다배포 파드가 onService인지 확인 > helm --wait 옵션으로 배포 프로세스중 파드가 잘 뜬 다음 다음 단계로 진행시킬 수 있다.배포시 파드가 업데이트 되지 않는 경우들 > deployments 는 내부 template 프로퍼티 하위값들이 바뀌어야 config에 맞춘 파드를 다시 띄운다.template.spec.metadata에 배포를 위한 값을 넣음으로써, 배포를 강제할 수 있다.이를테면 어플리케이션 컨테이너의 변경때 사용가능!이미지별 태그 관리개발 환경처럼 잦은 배포가 있는 경우 version이 무의미할 수 있다.이 때 latest tag + Always pullPolicy 가 적합그러나 배포 관리가 필요한 운영환경에는 version이 필수이다.이 때 version tag + IfNotPresent pullpoilcy 가 적합k8s 클러스터 내부에 이미지가 있다면 굳이 허브를 거칠 필요없이 바로 세팅 가능하다.비슷하게 개발환경에도 롤백이 필요하다면 버전관리가 필요하고 해당 태그 정책을 가져가면 좋다.Argo k8s 워크플로우 및 배포 관리를 위한 오픈소스 프로젝트 모음으로,  GitOps 방식으로 배포를 관리하며, CNCF 졸업 프로젝트이다.ArgoCD: GitOps 기반 배포관리 툴깃에 k8s manifest를 저장하고, ArgoCD가 이 레포를 모니터링하면서 diff 감지 및 동기화 (Sync) 해주는 툴ImageUpdater: 도커 허브에서 특정 이미지를 모니터링하면서 diff 감지해 자동 배포하는 툴Argo Wrokflow: Airflow, Kubeflow와 같은 워크플로우 매니지먼트 툴Argo Events: 카프카와 같은 이벤트 버스 툴 Argo Rollouts: 블루그린, 카나리 배포등을 지원하는 툴k8s 클러스터에 설치되는 Argo 컴포넌트들은 아래와 같다.Server: API 서버와 대시보드 역할 (웹 UI 및 API(CLI) 서버 역할)Repo Server: 깃 저장소와 연결해 배포할 yaml 가져오고 k8s 배포할 manifest 생성Application Controller: 깃에서 가져온 Desired Manifest 와 k8s 실제 적용된 Manifest를 비교하는 역할 & 필요하면 동기화 작업 수행KubeAPI: k8s 리소스에 명령을 날려주는 역할Redis: argo 시스템내 캐시 처리Dex: 외부 인증 시스템 연동AgoCD로 배포k8s 클러스터에 argoCD 배포argoCD를 통해 배포 관련 레포를 받아 k8s 클러스터에 배포Application이 배포하는 앱 단위이고, Poeject로 그룹핑 가능Source: 깃헙 소스 정보 / Destination: k8s 클러스터 정보General ~ 그룹명, 앱명, SyncPolicy (배포 정책 ~수동/자동), SyncOption(배포 상세 옵션) / PrunePolicy(리소스 삭제 정책)Desired Manifest ~ diff ~ Live Manifest, 로 Git 소스 중심의 차이점 파악IamgeUpdater도커 허브 감지하다가 이미지 업데이트시 ArgoCD에 자동 배포 명령Helm or Kustomize 를 활용해야 사용 가능하며 (내부에서 해당 패키지툴 명령어 사용)argoCD 앱 정보와 도커 허브 연결정보를 IamgeUpdater에 넣어서 감지 및 반영(배포)를 자동화Argo RolloutArgoCD 없이 사용 가능하며, CRD를 통해 k8s  Deployments 리소스를 배포와 관련해 확장해 만듦블루그린 배포시 preview 서비스를 만들고, 자동으로 Service를 붙였다 떼주며.카나리 배포시 특정 조건에 따라 트래픽량 조정이 가능하며, steps를 통해 트래픽 주기 및 기타 설정을 단계별로 적용하며 배포프로세스를 유연하게 가져갈 수 있음4주차 학습 후기헬름 차트, Kustomize, Argo와 안면을 틀 수 있었던, 큰 그림을 이해해볼 수 있었던 시간!특히 헬름차트와 더욱 친해지고 내것으로 만들기위해 더 노력해야겠다!마지막 까지 다들 고생하셨습니다! & 좋은 강의 만들어주셔서 감사합니다 강사님!!

Min 22일 전
Howon Jeong

[인프런 워밍업 클럽 4기] DevOps 발자국 4주차

이번 주차는 Argo CD를 이용한 배포와 Blue/Green, Canary 배포 전략을 간단하게 사용할 수 있는 방법을 공부하고 실습했다. Argo CD는 GitOps 기반으로 관련된 Release Repo에서 수정이 발생하면 주기적으로 데이터를 가져와 운영 중인 서버와 비교하는 일을 자동으로 해줌. Git Repo와 kube-apiserver와도 연결이 되어 있기 때문에, CD 역할을 하면서 각 Resource의 상태를 그래프를 이용해 표시해줘서 이 부분은 Jenkins보다 좋았던 것 같다. 게다가, Argo CD의 ImageUpdater를 통해 Docker Hub의 Image가 업데이트 되는 것을 감지하여 Applications에 Annotations을 주는 것으로 특정 Image가 정해둔 패턴에 맞추어 업데이트가 되면 미리 설정해둔 배포 전략에 맞추어 자동으로 업그레이드가 진행되는 툴이었다. 솔직히, Deployment를 이용한 Blue/Green과 Canary 배포는 강의를 듣고 실습하면서 이것을 실전에 적용해볼 생각을 하니 조금 막막했다.. ㅎㅎ 하지만, Argo Rollouts를 이용한 방식은 너무 간편했고 미리 YAML 파일만 잘 짜두면 클릭 한 번으로 귀찮은 모든 일들이 이루어졌다. (Argo 그는... 신이야)복습 시간을 조금 가지고, 다시 일프로 강사님의 Sprint3과 4를 수강할 생각인데, 아직까지 지상편이라는 생각과 강사님이 첫 강의에서 보여주신 사진 때문에 조금 무섭긴 하다(저기서 익숙한 애들보다 처음 보는 애들이 더 많은 것 같다.. ㅎㅎ)드디어, 모든 강의가 끝나고 워밍업 클럽이 끝났다 !!k8s를 공부해야지.. 항상 생각했지만, 막막함에 계속 미루고 있었는데, 인프런의 워밍업 클럽 덕분에 좋은 강사님을 만나게 되어서 정말 좋은 경험이라고 생각한다

DevOpsInfra워밍업클럽

채널톡 아이콘