블로그
전체 82025. 06. 19.
1
[워밍업 클럽 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: 실용적인 테스트 가이드
2025. 06. 19.
0
[워밍업클럽 4기 백엔드] Day18 미션
1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이@Mock실제 객체가 아닌 가짜 객체(Mock 객체)로 생성하고 싶을 때 사용한다.아래 코드처럼 선언한다.@Mock 어노테이션을 사용한다면 테스트 클래스 상단에 @ExtendWith(MockitoExtension.class)를 붙여 Mockito 환경임을 명시한다.MailSendClient mailSendClient = Mockito.mock(MailSendClient.class); 또는 @Mock private MailSendClient mailsendClient;@MockBean스프링 컨텍스트에 있는 빈을 원하는 Mock 객체로 바꾸고 싶을 때 사용한다.@Mock과 @MockBean의 차이@Mock은 단위 테스트에서 사용하고, @MockBean은 스프링 통합 테스트에서 사용한다.@InjectMocksMock이나 Spy 객체들을 테스트 클래스의 의존성으로 주입해준다.아래 코드처럼 선언한다.MailService mailService = new MailService(mailSendClient); //mailSendClienst는 Mock 객체 또는 @InjectMocks private MailService mailService;@Spy실제 객체를 기반으로 만들어지며, 일부만 stub 처리하고 싶을 때 사용한다.예를 들어, 하나의 클래스에 a(), b(), c(), d() 메서드가 있을 때, a()만 stub 처리하고, 나머지는 실제 동작으로 실행하고 싶을 때 사용한다.@Mock과 달리 실제 객체를 기반으로 생성되기 때문에 doReturn(), doNothing()과 같은 Spy 전용 문법을 사용한다.@SpyBean스프링 컨텍스트에 있는 실제 빈을 Spy 객체로 바꾸고 싶을 때 사용한다.@Spy과 @SpyBean의 차이@Spy 는 단위 테스트에서 사용하고, @SpyBean은 스프링 통합 테스트에서 사용한다. 2. @BeforeEach, given 절, when 절 배치@BeforeEach void setUp() { 1-3. 2-3. 게시물 생성에 필요한 내용 준비 1-4. 2-4. 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-5. 댓글 작성에 필요한 내용 준비 // when 1-6. 댓글 작성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-1. 사용자1 생성에 필요한 내용 준비 3-2. 사용자1 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 }댓글을 테스트할 때 게시물을 이미 있어도 테스트 내용을 이해하는 데에 지장이 없고, 댓글을 테스트하는 데에 게시물의 내용을 몰라도 문제가 안되기 때문에 @BeforeEach에서 게시물을 생성해 주었다.사용자에 따라 댓글을 수정할 수 있는지, 없는지에 지장이 가기 때문에 사용자 생성은 @BeforeEach에 넣지 않았다. cannotUpdateCommentWhenUserIsNotWriter() 에서는 작성자가 반드시 사용자1이어야 하기 때문에 @BeforeEach에서 생성한 공통 게시물을 사용하지 않았다. 출처Practical Testing: 실용적인 테스트 가이드
2025. 06. 17.
0
[워밍업클럽 4기 백엔드] Day16 미션
Layered Architecture 구조의 레이어별 테스트 작성법각 레이어별로 어떤 특징이 있고, 어떻게 테스트하면 좋을지 정리한다. ✅ Persistence LayerPersistence Layer의 특징DB와 가장 가깝게 통신하며 데이터를 주고받는 레이어이다.데이터의 CRUD 작업을 담당하는 역할이다.비즈니스 로직이 포함되지 않는다.Persistence Layer 테스트@DataJpaTest를 사용한다. @SpringBootTest는 전체 빈을 다 로딩하지만, @DataJpaTest는 Repository와 관련된 Bean만 로딩하기 때문에 이렇게 빠르고 가볍게 테스트하는 Persistence Layer에서는 @DataJpaTest를 주로 사용한다.save, find, delete 등 데이터에 대한 CRUD가 잘 동작하는지 테스트한다.데이터를 조회할 때 해당 데이터가 없을 경우(null이나 빈 리스트) 올바르게 동작하는지 테스트한다. ✅ Business LayerBusiness Layer의 특징말그대로 비즈니스 로직을 구현한다.즉, '데이터를 가지고 어떤 걸 할지'를 구현한다.트랜잭션이 중요하다.Business Layer 테스트@SpringBootTest를 사용해 전체 빈을 다 로딩하도록 한다.필요한 의존성을 @Autowired로 주입한다.복잡한 비즈니스 로직을 테스트한다.정상 동작은 물론, 예외 케이스도 필요하다면 테스트한다.@Transactional은 테스트 코드에서만 있으면 문제가 발생해도 뒤늦게 발견할 수 있기 때문에 주의해야 한다. ✅ Presentation LayerPresentation Layer의 특징가장 앞쪽에서 사용자와 시스템 간의 입출력을 담당한다.일반적으로 Controller가 담당한다.비즈니스 로직은 포함하지 않는다.Validation에 대한 책임 분리를 생각하자.도메인 로직에 해당하는 validation은 Presentation Layer에서 하지 않는다.Presentation Layer 테스트Controller 레이어만 가볍게 테스트하기 위해서 @WebMvcTest를 사용한다. Request Validation에 대한 테스트를 진행한다. Service 계층은 Mocking이 필요하다. 출처Practical Testing: 실용적인 테스트 가이드
2025. 06. 14.
1
[워밍업 클럽 4기 백엔드] 3주차 발자국
Practical Testing: 실용적인 테스트 가이드 강의를 수강하고 작성한 회고입니다. 1. 학습한 내용 및 회고통합 테스트 (Integration Test)여러 모듈이 협력하는 기능을 통합적으로 검증하는 테스트라이브러리와 프레임워크의 차이라이브러리는 외부에서 가져와서 내 코드에 쓰인다. 즉, 내 코드가 주체가 된다.프레임워크는 이미 갖춰진 환경이 주워지며, 내 코드가 수동적으로 프레임워크 안으로 들어가게 된다.Persistenc Layer 테스트에서는 데이터에 대한 CRUD 작업의 테스트를 진행하기 때문에 비즈니스 로직이 들어가서는 안 된다.Business Layer 테스트는 비즈니스 로직을 테스트하며, 트랜잭션을 보장해야 한다.@Transactional이 테스트 코드에만 있으면 문제가 될 수 있음을 뒤늦게 발견할 수 있기 때문에 주의해야 한다.Presentation Layer 테스트는 Controller 테스트로, 파라미터에 대한 최소한의 검증을 한다.그리고 Presentation Layer 테스트를 할 때, Business Layer와 Persistence Layer는 Mocking을 해서 테스트를 진행한다.@Transactional을 쓸 때 Command와 Query를 분리하자.Command상태를 변경하는 작업(CUD)을 한다.메서드 단위로 @Transactional(readOnley 기본값 false) 필요Query상태를 조회하는 작업(R)을 한다.@Transactional(readOnly = true) 필요 변경 작업(Command)은 데이터의 무결성을 위해 쓰기 트랜잭션이 필요하고, 조회 작업(Query)은 성능을 위해 읽기 전용 트랜잭션이 적합하기 때문인다. 회고부끄러운 이야기이지만 지금까지 @Transactional을 쓰면서 그냥 다 필요한 거 아닌가? 하면서 트랜잭션을 거는 조건을 생각하지 않은 채 사용했었다. 실제로는 트랜잭션의 성격과 사용 목적에 따라 명확하게 구분해야 하고, Command와 Query를 나누는 설계 자체가 데이터 무결성과 성능 모두에 영향을 미친다는 것을 알게 되었다.이제는 습관적으로 @Transactional을 붙이는 게 아니라, '어떤 목적으로 트랜잭션이 필요한가?'를 먼저 생각하고 적용할 수 있도록 노력해야겠다.그리고 'Spring과 Spring Boot 모두 프레임워크라고 불리는데 이 둘의 차이는 뭘까'라는 궁금증이 생겨 찾아봤다.Spring은 애플리케이션의 핵심 기능(예: 웹, 데이터 접근, 보안 등)을 개발할 수 있도록 다양한 모듈과 기능을 제공하는 프레임워크이고, Spring Boot는 이러한 Spring의 여러 기능을 더 쉽고 빠르게 설정하고 실행할 수 있도록 도와주는 프레임워크이다.그래서 이 둘의 역할은 다르지만, 개발자가 정해진 구조 안에 코드를 작성하고 실행하게 만든다는 점에서 둘 다 프레임워크라고 할 수 있다고 한다. 2. 단위 테스트 코드 작성 미션 회고 프로젝트는 스터디 카페 프로젝트로 선택했고, StudyCafePassType, StudyCafeSeatPass, StudyCafePassOrder의 단위 테스트를 작성했다.작은 단위부터 테스트를 하기 위해 StudyCafePassType, StudyCafeSeatPass, StudyCafePassOrder 순서대로 진행했다.StudyCafePassTypeTest에서는 도메인 규칙을 테스트하기 위해 StudyCafePassType.FIXED가 사물함 사용이 가능한 타입인지 테스트했고,StudyCafeSeatPassTest에서는 비즈니스 조건을 테스트하기 위해 고정석 이용권이 사물한 이용 가능 조건인지, 시간 단위 이용권은 사물함을 이용할 수 없는지, 사물함 이용권과 좌석 이용권의 타입과 기간이 같은지, 할인율에 따라 할인 금액이 올바르게 계산되는지 테스트했다.StudyCafePassOrder에서는 결제 금액 계산 로직이 정확하게 작동하는지 테스트했다.이번 미션을 통해서 단순히 코드가 잘 작동되는가를 확인하는 걸 넘어서, 도메인 규칙과 비즈니스 로직이 제대로 구현되었는지 테스트하는 수단이라는 걸 느낄 수 있었다. 출처Practical Testing: 실용적인 테스트 가이드
2025. 06. 11.
0
[워밍업 클럽 4기 백엔드] 2주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드 강의를 수강하고 작성한 회고입니다. 1. 학습한 내용 및 회고 Readable Code좋은 주석을 달자의도를 최대한 코드로 표현해야 한다.변수와 메서드 순서도 중요하다.중요도가 높은 변수 또는 메서드를 먼저 배치한다.Practical Testing단위 테스트작은 코드 단위를 독립적으로 검증하는 테스트클래스나 메서드를 검증하는 테스트이다.테스트 케이스해피 케이스와 예외 케이스 모두를 생각해 테스트 케이스를 세분화해야 한다.외부로 분리할수록 테스트하기 쉬워진다.TDD: Test Driven Development 프로덕션 코드보다 테스트 코드를 먼저 작성한다.테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하게 한다.red(실패하는 테스트 코드 작성) -> green(테스트 통과 최소한의 코딩) -> refactor(구현 코드를 개선하여 테스트 통과) 순서로 구현한다.쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.테스트는 프로덕션 기능을 설명하는 문서가 될 수 있다.DisplayName에 도메인 용어를 사용해서 테스트에 대한 설명을 적는다.BDD: Bdehavior Driven DevelopmentGiven : 시나리오 진행 준비 과정When : 시나리오 행동 진행Then : 결과 명시 및 검증2주차 회고지금까지 개인적으로 프로젝트를 진행할 때마다 기능 구현이 급했기 때문에 테스트 코드는 꼭 나중으로 미루게 되었다. '예외 케이스를 충분히 생각해서 기능 구현을 했고, 실제로 잘 돌아가는데 테스트 코드가 꼭 필요한 건가?' 라는 생각을 했었다. 강의를 들으면서 이러한 나의 생각이 많이 바뀌었다. 테스트는 프로덕션 기능을 설명하는 문서가 될 수 있다는 말을 들었을 때 '아 이래서 테스트 코드를 작성해야 하는 구나!'라고 생각했고, 기능 구현만큼 테스트도 참 중요하다는 걸 알게 되었다. 2. 리팩토링 미션 회고'스터디 카페 이용권 선택 시스템' 프로젝트를 리팩토링하는 미션을 진행하였다. 생각보다 변수명, 메서드명을 생각하는 게 쉽지 않았다. 어떻게 이름을 지어야 의도를 잘 드러낼 수 있을지 고민이 많았다. 객체를 어떻게 나눌 건지, 추상화 레벨은 어느 정도로 할 건지도 고민을 해보았고, 일급 컬렉션도 적용해보았다. 이번 리팩토링 미션을 하면서 내가 처음부터 설계해서 짜는 코드보다, 기존 레거시 코드를 리팩토링하는 게 더 어렵다고 느꼈다. 많은 연습을 해야겠다고 생각했다. 그리고 중간점검 때 다른 분들의 코드 피드백도 많은 도움이 되었다. 출처Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드
2025. 06. 01.
1
[워밍업 클럽 4기 백엔드] 1주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의를 수강하고 작성한 회고입니다. 1. 학습한 내용📍추상클린 코드가 중요한 이유가 뭘까가독성이 좋아진다.즉, 시간과 자원이 절약된다.클린 코드에서 가장 중요한 건 추상화이다.그럼 추상화는 무엇일까구현 세부사항은 감추고, 중요한 정보만 남긴다.즉, ''어떻게"할지는 숨기고, "무엇"을 할지는 보여준다.그렇기 때문에 추상은 구체에 기반해서 생긴다.추상화의 가장 대표적인 행위는 '이름짓기'이다.이름 짓기'이름'이 중요한 이유는그럼 이름은 어떻게 지어야 할까단수인지 복수인지 생각하기관용어처럼 쓰는 말이 아니라면, 줄임말 사용하지 않기도메인 용어 사용하기좋은 코드에서 어떻게 이름이 지어지는지 보기메서드메서드 이름, 파라미터, 반환 타입으로 구체적인 내용을 추상화할 수 있어야 한다.매직 넘버, 매직 스트링의미가 불분명하고 어떤 기준으로 쓰이는 지 알기 어려운 숫자나 문자열을 매직 넘버, 매직 스트링이라고 한다.상수로 추출해서 어떤 의미를 쓰이는지 잘 나타낼 수 있는 이름을 부여해야 한다. 📍논리, 사고의 흐름Early returnEarly return으로 else 및 switch 사용을 지양해야 한다.사고의 depth 줄이기중첩 분기문, 중첩 반복문을 사용할 때 추상화를 통한 사고 과정으로 depth를 줄이자.무조건 1 depth 줄이자는 건 아님!공백 라인공백 라인도 의미를 가진다.부정어구부정어구가 쓰지 않아도 되는 상황인지 생각해보고, 메서드명으로 부정을 표현하자.해피 케이스와 예외 처리사용자에게 보여줄 예외와, 개발자가 보고 처리해야 할 예외를 구분해야 한다.Optional은 비싼 객체이기 때문에 최대한 지양하고, 만약 사용해야 한다면 빠르게 해소해야 한다.e.printStackTrace() 사용을 지양하고, 로그 시스템을 통해서 로그를 남기자. 📍객체 지향 패러다임객체로 추상화하기공개 메서드 선언부를 통해 외부 세계와 소통한다.관심사가 하나의 객체로 모이기 때문에 유지보수성이 올라간다.그래서 새로운 객체를 만들 때는 하나의 관심사로 명확하게 책임을 정의하고 있는지를 생각해야 한다.getter/setter 사용을 지양하자.SOLIDSRP: Single Responsibility Principle하나의 클래스가 하나의 책임을 가지도록 설계하자.이렇게 설계하려면 '책임'을 볼 줄 아는 눈이 필요하다.OCP: Open-Closed Principle기존 코드의 변경 없이, 새로운 기능을 추가할 수 있어야 한다.추상화와 다형성을 활용하자.LSP: Liskov Substitution Principle상속 구조에서, 자식은 부모를 대체할 수 있어야 한다.만약 자식에서 부모에는 없는 예상치 못한 예외를 던진다면 instanceOf와 같은 불필요한 처리를 해줘야 할 수도 있다.ISP: Interface Segregation Principle사용하는 인터페이스에만 의존하자.인터페이스 안에 필요하지 않은 기능이 있다면 인터페이스를 쪼개야 한다.DIP: Dependency Inversion Principle추상화 레벨이 높은 고수준 모듈에서 구체적인 저수준을 바로 의존해서는 안 되고, 추상화 즉, 인터페이스에 의존해야 한다. 📍객체 지향 적용하기상속보다는 조합상속은 부모가 변경될 때 자식이 영향을 받는다.그래서 상속보다는 조합을 권장한다.Value Object값 객체란, 도메인의 어떤 개념을 추상화하여 표현한 객체불변성을 가지고, 값 자체로 동등성을 가진다.일급 컬렉션일급 컬렉션이란, 하나의 컬렉션(List, Set 등)을 필드로 가지는 객체이다.컬렉션을 직접 노출하지 않고, 객체로 감싸 의미를 부여하는 방식이다.만약 해당 컬렉션을 반환해야 한다면, 꼭 새로운 컬렉션으로 만들어서 반환해야 한다.EnumEnum은 상수를 모아놓은 것이고, 그 상수와 관련된 로직을 담을 수 있다.다형성 활용하기복잡한 if문이 있을 때, 다형성과 OCP를 활용해서 단순하게 만들자. 📍1주차 회고추상, SOLID 등 여러 개념들을 그동안은 단순히 정의만 외우고 정확히 알지는 못했지만, 지금은 실제 코드에서 어떻게 활용되고, 어떻게 생각하고 설계를 해야 하는지 감을 잡을 수 있게 되었다. 그리고, 일주일 동안 공부한 내용을 이렇게 발자국으로 정리한 게 큰 도움이 되었다. 직접 정리도 해보고 한 번 더 생각을 해보니 내가 어떤 걸 모르고, 어떤 부분이 부족한지 확인할 수 있었다. 앞으로도 꾸준히 발자국을 남길 생각이다. 2. 미션코드 리팩토링 미션해결 과정 중첩 조건문의 depth를 줄여 가독성을 개선조건별 early return으로 흐름을 단순화시킴조건문의 의미를 Order 내부 메서드로 만들어 메서드명 자체로 의도를 명화하게 함부정 연산자를 사용하지 않도록 hasNoCustomerInfo()의 네이밍으로 변경 회고메서드명, 변수명 등 이름의 중요성을 깨달았다. 사실 이전엔 코드를 작성할 때 네이밍에 대해 엄청 깊게 고민하지 않았고, 부정연산자도 종종 사용하곤 했는데, 이번 미션으로 이름 하나만으로도 코드의 의도를 명확하게 할 수 있다는 점을 알게되었다. 출처Readable Code: 읽기 좋은 코드를 작성하는 사고법
2025. 05. 30.
0
[워밍업클럽 4기 백엔드] Day4 미션
1. 코드 리팩토링📍리팩토링 전 코드public 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; } 📍리팩토링 후 코드public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } if (order.isInvalidTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasNoCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }리팩토링 한 내용중첩 조건문의 depth를 줄여 가독성을 개선조건별 early return으로 흐름을 단순화시킴조건문의 의미를 Order 내부 메서드로 만들어 메서드명 자체로 의도를 명화하게 함부정 연산자를 사용하지 않도록 hasNoCustomerInfo()의 네이밍으로 변경 2. SOLID에 대해 나만의 언어로 정리하기SRP: Single Responsibility Principle하나의 클래스는 하나의 역할만 가지고 있어야 한다. OCP: Open-Closed Principle확장에는 열려 있고, 수정에는 닫혀 있어야 한다.요구사항이 추가되었을 때 기존 코드를 수정하지 않고, 기능을 확장(추가)할 수 있어야 한다.LSP: Liskov Substitution Principle 상속 구조에서, 자식은 부모를 대체할 수 있어야 한다.만약 자식에서 부모에는 없는 예상치 못한 예외를 던진다면 instanceOf와 같은 불필요한 처리를 해줘야 할 수도 있다. ISP: Interface Segregation Principle사용하는 인터페이스에만 의존하자.인터페이스 안에 필요하지 않은 기능이 있다면 인터페이스를 쪼개야 한다.DIP: Dependency Inversion Principle추상화 레벨이 높은 고수준 모듈에서 구체적인 저수준을 바로 의존해서는 안 되고, 추상화 즉, 인터페이스에 의존해야 한다. 출처Readable Code: 읽기 좋은 코드를 작성하는 사고법
2025. 05. 27.
0
[워밍업클럽 4기 백엔드] Day2 미션
구체와 추상의 예시추상기술 블로그를 쓴다.구체1. 글 주제를 정한다.2. 주제에 대해 조사한다.3. 내용 구조를 잡는다.4. 예제 코드를 작성하고 테스트한다.5. 초안을 작성한다.6. 그림, 도표 등 시각자료를 추가한다.7. 블로그 에디터에 접속한다.8. 에디터에 글을 작성한다. 9. 오탈자를 확인하고 수정한다.10. 태그 및 카테고리 설정 후 발행한다. 출처Readable Code: 읽기 좋은 코드를 작성하는 사고법