블로그
전체 9#카테고리
- 데브옵스 · 인프라
- 백엔드
#태그
- 쿠버네티스어나더클래스
- 지상편
- sprint1
2025. 06. 08.
1
[워밍업 클럽 4기 DevOps] - 2주차 발자국
강의 수강학습내용k8s Object별 기능 실습k8s 아키텍처를 바탕으로 주요 기능 동작 이해 회고실습을 통해 명령어 기반으로 k8s 사용에 익숙해졌다.Deployment에 configMap을 volume 설정 중 Delpoyment에 설정이 생각했던 것과 달리 적용되어 시간을 낭비한 것 같다.문제현상 : Deployment - spec.volumes - configmap 설정 시 "emptyDir: {}" 생성원인 : Key값을 잘못 입력 (대소문자)정상 : configMap에러 : configmap해결 : spec.volumes에 configMap 설정 후 정상 확인 미션ProbestartupProbe, livenessProbe, readinessProbe 각각의 역할여러가지 상태체크 방법 (http, exec)ConfigMap, SecretConfigMap - 파일 마운트Secret - OS 환경변수 설정PV/PVC, Deployment, Service, HPAPV/PVC : PVC, hostPath별 동작Deployment : rollingUpdate와 RecreateService : Deployment와 port 매핑HPA : 매트릭 설정에 따른 scale out 학습내용 블로그https://velog.io/@moongchi18/series/kubernetes
2025. 06. 01.
1
[워밍업 클럽 4기 DevOps] - 1주차 발자국
강의 수강학습내용k8s를 이용한 인프라 환경 아키텍쳐k8s 내부 구조와 각 Object별 역할회고일과 병행하다보니 계획했던 것 보다 적게 학습하여 아쉬웠다.실무에서 잘 모르고 사용했던 것들에 대해 이해하게 되어 보람찼다grafana 대시보드에 no data로 출력되는 문제를 해결하는데 시간이 지체되어 아쉬웠다.문제현상 : grafana 대시보드 No Data 출력원인 : promtail이 loki에 로그 전달에 실패로그 : [root@k8s-master ~]# kubectl logs -n loki-stack loki-stack-promtail-tcr24 | grep error level=warn ts=2025-05-29T11:47:46.91056902Z caller=client.go:379 component=client host=loki-stack:3100 msg="error sending batch, will retry" status=-1 error="Post \"http://loki-stack:3100/loki/api/v1/push\": dial tcp 10.105.97.128:3100: connect: connection refused"해결 : calico 재시작 후 정상 확인 미션실습 내용 : https://velog.io/@moongchi18/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%AC%B4%EA%B2%8C%EA%B0%90-%EC%9E%88%EA%B2%8C-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-%EC%84%A4%EC%B9%98-%ED%99%95%EC%9D%B8강의에서 vagrant 실행하는 것으로는 이해되지 않았으나 미션을 진행하면서 k8s에 대해 조금이나마 이해하게 되었다. 학습내용 블로그https://velog.io/@moongchi18/series/kubernetes
데브옵스 · 인프라
・
쿠버네티스어나더클래스
・
지상편
・
sprint1
2024. 10. 28.
0
[Readable Code: 읽기 좋은 코드를 작성하는 사고법 회고 2주차
출처 : Readable Code: 읽기 좋은 코드를 작성하는 사고법 / 박우빈 학습 내용 요약코드 다듬기(섹션6)주석단순 로직 설명 주석은 지양(소스코드 처럼 버전 관리 필요)정책, 히스토리 등 해당 소스코드가 왜 만들어졌는지 설명은 OK메서드 나열 순서public > private사용 순서 패키지클래스 이해에 필요한 문맥으로 활용 가능대규모 변경인 경우 팀에 공유 후 진행 필요IDE 도움 받기SonarLint노란줄이 발생하는 원인을 찾다보면 배울점이 많음 리팩토링 연습 기억하면 좋은 조언들
2024. 10. 24.
0
[인프런-워밍업클럽 BE 2기] Day 18 미션
1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이@Mock타겟 객체의 껍데기를 생성다른 객체에 의존성을 주입하려면 해당 객체에 @InjectMocks 필요@MockBean껍데기를 Bean으로 등록테스트 클래스에 @SpringBootTest, 주입 대상에 @Autowired 필요@Spy객체의 기능 일부만 stub하고 나머지는 실제 기능 호출@SpyBean@Spy Bean으로 등록테스트 클래스에 @SpringBootTest, 주입 대상에 @Autowired 필요@SpyBean 대상이 인터페이스인 경우 구현체가 스프링 컨텍스트에 등록되어있어야함@InjectMocks@SpringBootTest가 아닌 경우 @Mock, @Spy 설정된 객체를 타겟 객체에 주입 2. 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요? (@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)@BeforeEach void setUp() { beforeEach-1. 사용자1 생성에 필요한 내용 준비 beforeEach-2. 사용자1 생성 beforeEach-3. 사용자2 생성에 필요한 내용 준비 beforeEach-4. 사용자2 생성 beforeEach-5. 사용자1의 게시물 생성에 필요한 내용 준비 beforeEach-6. 사용자1의 게시물 생성 beforeEach-7. 사용자1의 댓글 생성에 필요한 내용 준비 beforeEach-8. 사용자1의 댓글 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { 활용 - beforeEach-2. 사용자1 생성 활용 - beforeEach-6. 사용자1의 게시물 생성 1-1. 댓글 생성에 필요한 내용 준비 // given 1-2. 댓글 생성 // when // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { 활용 - beforeEach-2. 사용자1 생성 활용 - beforeEach-8. 사용자1의 댓글 생성 // given 2-7. 댓글 수정 // when // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 활용 - beforeEach-4. 사용자2 생성 활용 - beforeEach-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 }
2024. 10. 22.
0
[미션 Day15] Layered Architecture
Layered Architecture 특징 및 테스트 방법Persistence LayerRDBMS 등 외부 저장소와 CRUD 테스트비즈니스 로직은 포함시키지 않는다Business Layer데이터 가공에 대한 부분 테스트주어진 데이터를 가공하고 예상 결과가 만들어지는지 테스트한다.Test 클래스에 @Transactional 사용 주의 Presentation Layer요청 데이터를 검증하고 처리 결과 응답Persistence, Business Layer는 모킹한다.요청 파라미터를 검증하고 요청 데이터별 예상 응답을 테스트한다.
2024. 10. 22.
0
[Practical Testing: 실용적인 테스트 가이드] 회고 4주차
출처 : Practical Testing: 실용적인 테스트 가이드 학습 내용 요약섹션6 Spring & JPA 기반 테스트테스트케이스 세분화Presentation Layer파라미터 검증Persistence, Business Layer는 Mocking 처리 후 테스트@Transactional /* * readOnly = true : 읽기 전용 * CRUD에서 CUD 동작 X / only Read * JPA : CUD 스냅샷 저장, 변경감지 X (성능 향상) * * CQRS - Command(CUD) / Read Query 분리 * Read 빈도가 훨씬 많음 * Read에 의해 CUD가 영향 받아도 안되고 CUD에 의해 Read Query가 영향 받아서도 안됨 * */Dummy : 아무 것도 하지 않는 깡통 객체Fake : 단순한 형태로 동일한 기능은 수행하나, 프로덕션에 쓰기에는 부족한 객체(FakeRepository)stubbing(상태 검증)미리 준비한 결과를 제공하는 객체, 그 외에는 응답하지 않음mock 객체의 행위에 원하는 응답 입력Spy : Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체, 일부는 실제 객체처럼 동작시키고 일부만 stubbing 할 수 있다Mock(행위 검증) : 행위에 대한 기대를 명세하고 그에 따라 동작하도록 만들어진 객체 @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이@Mock타겟 객체의 껍데기를 생성다른 객체에 의존성을 주입하려면 해당 객체에 @InjectMocks 필요테스트 클래스에 @ExtendWith(MockitoExtension.class) 필요Mockito.when(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString())) .thenReturn(true); BDDMockito.given(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString())) .willReturn(true); BDDMockito는 Mockito.when이 given에 해당하는 내용이지만 네이밍이 when이로 발생하는 부조화 방지@MockBean껍데기를 Bean으로 등록테스트 클래스에 @SpringBootTest, 주입 대상에 @Autowired 필요@Spy객체의 기능 일부만 stub하고 나머지는 실제 기능 호출테스트 클래스에 @ExtendWith(MockitoExtension.class) 필요doReturn(true) .when(mailSendClient) .sendEmail(anyString(), anyString(), anyString(), anyString());@SpyBean@Spy Bean으로 등록테스트 클래스에 @SpringBootTest, 주입 대상에 @Autowired 필요@SpyBean 대상이 인터페이스인 경우 구현체가 스프링 컨텍스트에 등록되어있어야함@InjectMocks@SpringBootTest가 아닌 경우 @Mock, @Spy 설정된 객체를 타겟 객체에 주입Classicist vs MockistClassicist실제 기능 테스트 필요외부 시스템은 모킹처리 필요내부 기능 테스트라면 실제 기능 확인Stubbing이 누락된 경우?Mockist개별 레이어별로 모킹하여 테스트하면 충분 더 나은 테스트를 위한 구체적인 조언(섹션8)한 문단에 한 주제if 문 지양단위 테스트의 목적이 분명해야함 완벽하게 제어하기메서드 내에서 변경될 수 있는 값은 외부에서 주입하기LocalDateTime.now() 등테스트에서는 LocalDateTime.now() 지양 (영향도 검토 없이 무분별하게 사용되는 문제) 테스트 환경의 독립성 보장given 절에서 테스트가 실패하지 않도록 구성하기생성자를 이용한 given절 구성 지향 (팩토리 메서드는 지양) 테스트 간 독립성 보장테스트별 공유자원 사용 지양 (테스트별 다른 given절 활용)테스트 순서에 무관하게 같은 결과 발생객체의 여러 상태 변경을 확인해야하는 테스트는 @DynamicTest 활용 한 눈에 들어오는 Test Fixture 구성Test Fixture테스트를 위해 원하는 상태로 고정시킨 일련의 객체given절에서 활용됨Fixture 생성 메서드는 테스트에 필요한 메서드만 파라미터로 받기 (나머지는 고정값)@BeforeAll모든 테스트 실행 전 1번 수행@BeforeEach각 테스트 실행 전 수행given절 생성 지양 최하단 메서드에서 해당 내용 확인하려면 긴 스크롤 이동 필요모든 테스트에 영향 발생각 테스트 입장에서 아예 몰라도 테스트 이해하는 데에 문제가 없는 사항수정해도 모든 테스트에 영향을 주지 않는 코드@AfterAll모든 테스트 실행 후 1번 수행@AfterEach각 테스트 실행 후 수행data.sql 등을 활용한 given 절 생성 지양 (테스트 파편화)Test Fixture 생성 빌더 구성 클래스는 비추천 (난잡화) Test Fixture 클렌징deleteAll()테이블의 전체 데이터 조회 후 건별 삭제데이터가 많은 경우 성능 이슈 발생 가능성 있음순서 고려 필요deleteAllInBatch()delete from tableA테이블의 필드가 B테이블에 외래키로 활용되고 있는 경우 B테이블 데이터 삭제 후 A테이블 삭제 필요 (순서 중요)@TransactionalSide Effect 고려 필요 @ParameterizedTest특정 데이터들을 바꿔가며 테스트하고 싶을 때 활용CsvSource@ParameterizedTest @CsvSource({ "HANDMADE, false", "BOTTLE, true" , "BAKERY, true" , }) @DisplayName("재고타입인지 테스트") public void test2(ProductType productType, boolean expected) { // given // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); }MethodSource메서드의 내용이 given에 해당하므로 테스트 위에 위치시킨다.private static Stream ofProductTypes(){ return Stream.of( Arguments.arguments(ProductType.HANDMADE, false), Arguments.arguments(ProductType.BOTTLE, true), Arguments.arguments(ProductType.BAKERY, true) ); } @ParameterizedTest @MethodSource("ofProductTypes") @DisplayName("재고 관련 상품타입 확인") void initializeGame(ProductType productType, boolean expected) { // given // when boolean result = ProductType.containsStockType(productType); // then assertThat(result).isEqualTo(expected); } @DynamicTest공유 변수를 사용하여 테스트하는 것은 지양해야하지만 단계별 시나리오 테스트가 필요한 경우 활용@DisplayName("재고 차감 시나리오 테스트") @TestFactory Collection stockDeductionDynamicTest(){ 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("차감할 재고 수량이 없습니다."); }) ); } 테스트 수행도 비용이다. 환경 통합@SpringBootTest는 상이한 환경마다 테스트 서버 기동이 필요하여 공통 환경을 추출하여 서버 기동 횟수 줄이기@ActiveProfiles, @MockBean 등 공통 요소를 Layer별 구성 또는 필요에 따라 설정@WebMvcTest(controllers = { OrderController.class, ProductController.class, }) public abstract class ControllerTestSupport { @Autowired protected MockMvc mockMvc; @Autowired protected ObjectMapper objectMapper; @MockBean protected OrderService orderService; @MockBean protected ProductService service; } class OrderControllerTest extends ControllerTestSupport { @Test ... }private 메서드 테스트는 어떻게?테스트 지양 - public 메서드를 통해서 검증하면 충분꼭 테스트 필요해보인다면 객체를 분리할 시점인지 고민 테스트에만 필요한 메서드는 어떻게? (프로덕션 코드에 필요없는 메서드)Test Fixture 생성 메서드 등만들어도 되지만 최대한 지양해야함 부록(섹션9)학습 테스트잘 모르는 기능, 라이브러리, 프레임워크를 학습하기 위해 작성하는 테스트여러 테스트 케이스를 정의하고 검증하면서 구체적인 동작과 기능 학습 가능팀적으로 학습이 필요하다면 작성 후 공유 Spring REST Docs테스트 코드를 통한 API 문서 자동화 도구API 명세를 문서로 만들고 외부에 제공함으로써 협업을 원활하게 한다.AsciiDoc(MarkDown)을 사용하여 문서 작성됨테스트를 통과해야 문서가 만들어져 신뢰도가 높음프로덕션 코드에 비침투적이다.코드 양이 많다.설정이 어렵다. Swagger적용이 쉽다.문서에서 바로 API 호출 수행 가능프로덕션 코드에 침투적테스트와 무관하여 신뢰도가 떨어짐
2024. 10. 14.
0
[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을 사용하는 경우 테스트 신뢰성이 깨질 수 있음
2024. 10. 03.
0
[클린 코드 & 테스트 코드 가이드 로드맵, 미션 Day4] 코드 리팩토링 및 SOLID
[미션]1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.#TO-BEpublic boolean validate(Order order) { if (Objects.isNull(order)){ return false; } if (order.isItemsEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.notValidateTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.notHasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }메서드 시그니처메서드명 변경 (validateOrder -> validate)파라미터 타입이 있어서 Order 삭제 구현부파라미터 검증 추가order이 null인 경우에도 유효하지 않다고 응답추상화 레벨에 맞춰 order == null이 아닌 Objects.isNull(order) 적용Early Return 적용실제 검증항목은 아이템 개수, 아이템 가격합계, 고객 정보 유무 3가지이고, 각각 독립적이므로 불필요한 else, else if 구문 삭제부정연산자(!) 대신 메서드명에 명시반환타입수정 없음 #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; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP공통적인 것들로 묶어주기같은 기능 : 메서드같은 도메인 : 클래스OCP동적으로 활용할 수 있게 설계LSP부모의 기능을 자식들이 공통으로 쓸 수 없다면 분리하기ISP인터페이스는 최소 단위별로 생성DIP추상에 의존
백엔드
2024. 10. 03.
0
[Readable Code: 읽기 좋은 코드를 작성하는 사고법 회고 1주차
출처 : Readable Code: 읽기 좋은 코드를 작성하는 사고법 / 박우빈 1. 회고저자께서 회고 방식에 대한 아티클을 소개해주셨고 KPT로 진행 후 주차별로 다른 회고 방식을 적용해보려고 합니다. Keep(만족했고 지속할 부분)단순히 따라치는 수준에 그치더라도 직접 코드 작성 미션 전 수강 내용 훑어보고 진행하기 Problem(부정적 또는 아쉬웠던 부분)강의 수강 전에 목표 달성 실패목표 : 코드 분석 및 수강 전 리팩토링한 코드와 수강 후 리팩토링된 코드 비교실제 : 코드 분석은 했으나 리팩토링까지는 진행하지 못함 Trying(Problem 해결 방식으로 다음에 시도해볼 점)목표 설정 후 시간제한 두기시간제한 없이 루즈하게 진행하여 목표 달성도 실패했고, 결과물도 만족스럽지 못함 2. 학습 내용 요약추상(섹션1~2)이름짓기단복수에 의미 부여줄임말 사용하지 않기도메인 언어 사용(유행어, 은어, 방언 금지)좋은 코드 보고 습득필드 이름은 추상화하지 않음메서드명은 내부 처리 내용을 추상화 메서드 선언부메서드 선언부 : 반환타입 + 메서드명 + 파라미터메서드 시그니처 : 메서드명 + 파라미터반환타입에 대한 고민 : 반환할만한 값이 있는지? boolean이라면 true/false가 무엇을 의미할지?파라미터가 날짜 형태라면 String 형태보다는 LocalDate가 적절해보임전치사 잘 활용하기메서드 내에서 동일한 추상화 레벨로 묶여야함매직넘버 / 매직스트링 : 상수로 추출되지 않은 숫자나 문자 -> 리팩토링할 때 실제 의미가 같은 데이터만 변경했는지 확인 필요 논리, 사고의 흐름Early Returnif(){}else if(){}else{}구조 보다는 개별 if문과 return 사용(if의 조건을 else 까지 기억하기 어려움) 사고의 depth 줄이기중첩 분기문 / 중첩 반복문코드 분석이 어렵다면 depth 1로 줄이는 것 고려, 그러나 모든 것을 메서드로 추출하면 오히려 가독성이 떨어질 수 있음사용할 변수는 가깝게 선언하기공백 라인을 대하는 자세비슷한 역할단위로 끊어주기 부정어를 대하는 자세!isLeft=> isRight, isNotLeft ( 부정 연산자 !의 가독성이 떨어짐) 해피 케이스와 예외 처리예외 발생 가능성 낮추기 - 외부와의 접점의도한 예외와 예상치 못한 예외 구분return null 자제,Optional 사용 고려(단, Optional은 비싼 객체이므로 꼭 필요한 경우에만 사용)Optional을 파라미터로 받는 것은 안티패턴(Optional이 가진 데이터 null, optional null)Optional 해소 - isPresent().orElseGet(), orElseThrow(), ifPresent(), ifPresentOrElse() 사용++ orElse(), orElseGet(), orElseThrow() 차이 숙지 객체 지향 패러다임(섹션4)객체지향 설계객체와 외부 세계는 공개 메서드로 소통함책임 분리1개의 관심사로 명확하게 책임 정의setter 사용 자제객체에서 데이터를 꺼내어(getter) 분기처리하는 것이 아니라 공개 메서드에 필요한 정보를 입력하고 true/false 확인필드 수는 적을 수록 좋음 (totalPrice 같은 것은 성능상 이유가 아니라면 필요할 때마다 계산하여 반환 SOLIDSRP하나의 클래스는 하나의 변경 이유(책임)만 가져야 한다OCP확장에는 열려있고 수정에는 닫혀있다 -> 기존 코드 변경 없이 시스템 기능 확장LSP부모 클래스가 자식 클래스를 완전히 대체할 수 있어야한다.자식 클래스끼리 호환되지 않는 기능은 부모 클래스에서 제외ISP사용하지 않는 인터페이스에 의존하면 안됨(3개 중 2개는 쓰는데 1개는 사용하지 않는 경우 인터페이스를 분리해야함)DIP의존성의 순방향 : 고수준 모듈이 저수준 모듈 참조의존성의 역방향 : 고수준(고추상), 저수준(저추상) 모듈 모두 추상화에 의존구현체에 의존하는 것이 아니라 구현체의 추상(인터페이스)에 의존++ 참고DIP(Dependency Inversion Principle)DI(Dependency Injection) - "3" 제 3자가 의존성 주입IoC(Inversion of Control) 객체지향 적용하기(섹션5)상속과 조합상속보다는 조합상속은 결합도가 높음인터페이스 활용부모의 필드를 직접 참조하지 않는 것이 좋음Value Object도메인의 특정 개념을 추상화하여 표현한 객체불변성(final), 동등성(equals(), hashCode() 등, 내부 값이 같으면 같은 객체 취급), 유효성 검증(객체 생성 시점) 등 보장 필요++ VS EntityEntity단일 필드 식별자식별자만 같으면 다른 필드 값이 달라도 동등한 객체 취급VO전체 필드가 식별자모든 필드가 같아야 동등한 객체취급일급 컬렉션일급시민(ex 함수)변수로 할당 가능파라미터로 전달 가능함수의 결과로 반환 가능일급 컬렉션컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 객체와 동등한 레벨로 다루기 위함단 하나의 컬렉션 필드만 가짐가공 로직 및 테스트 작성 가능반환할 때는 getter로 객체 주소를 반환하는 것이 아니라 새로운 컬렉션을 반환해야함 Enum의 특성과 활용상수와 상수 관련 로직 집합도메인 개념과 기능 명시 가능변경이 잦은 개념은 DB로 관리 다형성 활용하기OCP조건을 만족하는가?만족하면 행위 수행 숨겨져 있는 도메인 개념 도출하기도메인 지식은 발견하는 것객체지향은 현실을 흉내내는 것현실에서 인지하기 어려운 개념도 도출해서 사용할 때가 있음설계할 때는 최대한 미래를 예측해서 진행하고 틀렸을 때 언제든 돌아올 수 있도록 코드를 만들어야함
백엔드