블로그
전체 8#카테고리
- 백엔드
#태그
- 워밍업클럽
- 백엔드
- 발자국
- 미션
2025. 06. 21.
1
[워밍업 클럽 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: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
발자국
2025. 06. 19.
0
[워밍업 클럽 4기 백엔드] Day18 미션
📖미션 1. @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다.2. 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치) 📄첫 번째 미션 @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 의 차이를 한번 정리해 봅시다. 차이점@Mock@Mock private MailSendClient mailSendClient;완전한 가짜 객체로 만듭니다.동작을 정의해놓은 메서드만 수행됩니다. @Spy@Spy private MailSendHistoryRepository mailSendHistoryRepository;실제 객체를 한 번 감싸서 가짜 객체로 만듭니다.동작을 정의해놓은 메서드는 정의대로 수행되지만그렇지 않은 메서드는 실제 객체의 행동을 수행합니다. @InjectMocks@Mock private MailSendClient mailSendClient; @Spy private MailSendHistoryRepository mailSendHistoryRepository; @InjectMocks private MailService mailService;@Mock이나 @Spy로 가짜 객체를 만들었다면이 가짜 객체를 필드에 주입해 줄 수 있습니다.예를 들어,C c = new C(A, B)인데A와 B를 가짜 객체로 만들었다면C에 @InjectMocks를 붙여서 A와 B를 필드로 갖는 가짜 객체를 만들 수 있습니다. @MockBean@MockitoBean private OrderService orderService;기존에 스프링 컨테이너에 등록되어 있던 빈을가짜 빈으로 바꿔줍니다.예를 들어, OrderRepository가 실제 빈으로 등록되어있었다면@SpringBootTest에선 가짜 OrderRepoistory 빈으로 바꿔치기 해주는 식 @SpyBean@MockitoSpyBean private ProductService productService;마찬가지로 스프링 컨테이너에 등록되어 있는 빈을가짠 빈으로 바꿔줍니다.이 때, 실제 빈을 한 번 감싸서 가짜 빈을 만들어줍니다. Spring Boot 3.4 부터 @MockBean과 @SpyBean이 deprecated 처리되었으며Spring Boot 4.0부터 삭제 될 예정이라고 합니다.대신 @MockitoBean과 @MockitoSpyBean을 사용하라고 명시되어있습니다. 🫡 🔗spring.io 문서 참고 📄두 번째 미션 아래 3개의 테스트가 있습니다. 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치하고 싶으신가요?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치) 중복 제거를 우선 시 할 경우@BeforeEach void setUp() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 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 void setUp() { } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 // when 1-6. 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 // when 2-7. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-1. 사용자 생성에 필요한 내용 준비 3-2. 사용자 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 // when 3-9. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 } 미션 후기@Mock과 @Spy의 차이점에 대해 더 상세히 인지할 수 있었습니다.가독성과 코드 중복 제거 관점에서 setUp을 사용하는 게 좋을 지 아닐지 고민할 수 있었습니다.읽어주셔서 감사합니다. 🙂↕ 강의🔗Practical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
미션
2025. 06. 17.
0
[워밍업 클럽 4기 백엔드] Day16 미션
미션Layered Architecture 구조의 레이어별 테스트 작성법을 알아보았습니다.레이어별로 1) 어떤 특징이 있고, 2) 어떻게 테스트를 하면 좋을지, 자기만의 언어로 다시 한번 정리해 볼까요? 🛢Persistence LayerDB와 직접 상호작용하는 레이어로,Spring Data JPA의 쿼리 메서드나 커스텀 네이티브 쿼리를 정의합니다.Spring Data JPA의 기본 메서드로 해결되지 않는 요구사항이 생기면,Repository에 쿼리 메서드를 직접 정의하고 이에 대한 테스트 코드를 작성해줘야합니다. 테스트 방식@SpringBootTest를 사용해 통합 테스트를 수행합니다.Repository 하나만 테스트하는 경우가 많기 때문에통합 테스트지만 단위 테스트에 가까운 성격을 갖습니다. 주의할 점요구사항이 바뀌거나 추가되는 경우가 잦기 때문에,쿼리를 추가할 때마다 누락 없이 테스트를 작성해줘야 합니다. ⚙Business Layer비즈니스 로직을 처리하는 핵심 레이어로, Persistence Layer에 의존합니다.비즈니스 로직은 트랜잭션을 전제로 하기 때문에,@Transactional을 통해 트랜잭션 경계를 명확히 지정해줘야 원하는 동작을 보장할 수 있습니다. 테스트 방식@SpringBootTest로 통합 테스트를 진행합니다.Repository를 활용해 데이터를 저장하고,이를 기반으로 비즈니스 로직이 의도대로 동작하는지 검증합니다. 주의할 점요구사항 추가에 따라 도메인 로직과 Repository가 자주 변경되는데,Business 테스트에 앞서도메인 로직에 대한 단위 테스트와 Persistence 테스트를 먼저 작성해줘야합니다. 동시성 이슈가 발생할 수 있는 경우,멀티스레드를 고려한 동시성 테스트도 고민이 필요합니다. 💻Presentation LayerAPI로 전달받은 Request 값의 유효성 검증을 수행합니다.입력값 자체의 예외(타입 에러 등)와 비즈니스 예외를 명확히 구분해야 합니다. 테스트 방식@WebMvcTest로 통합 테스트를 진행합니다.@MockitoBean으로 Service를 주입하여 실제 구현을 대체합니다.MockMvc를 통해 API 요청을 시뮬레이션하고, JSON 응답 구조와 상태 코드를 검증합니다.Spring bean Validation을 활용해 Request DTO에 유효성 검증 로직을 추가하고 테스트합니다. 주의할 점Presentation Layer는 Business Layer에 의존하지만,Business Layer는 Presentation Layer를 몰라야 확장 가능한 구조가 됩니다.따라서 Request DTO는 컨트롤러용과 서비스용으로 분리하는 것이 좋습니다. JSON 역직렬화 시 getter와 기본 생성자는 필수입니다.만약 없다면 Jackson 라이브러리를 사용할 수 없으니 반드시 추가해줍니다. 미션 후기레이어마다 관심사와 책임이 명확히 분리되어 있기 때문에,테스트 전략도 각 레이어의 특성에 맞게 달라져야 한다는 점이 인상 깊었습니다.단위 테스트와 통합 테스트의 구조를 익히고 나니,다음 단계인 인수 테스트는 어떻게 설계되고 진행되는지 궁금해집니다! 강의🔗Practical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
미션
2025. 06. 15.
1
[워밍업 클럽 4기 백엔드] 3주 차 발자국 👣
학습 내용 요약 Spring & JPA 기반 테스트 (Presentation Layer 테스트 (1) 까지)레이어드 아키텍처에서 각 레이어가 분리된 이유를 테스트 관점에서 이해할 수 있었습니다.cafeKiosk 프로젝트에 요구사항을 하나씩 추가해가며,Persistence → Business → Presentation Layer순서로 TDD 방식을 적용해 기능을 구현해보는 실습을 진행했습니다. 학습 내용 회고 🌱성취미션을 통해 총 15개의 단위 테스트를 작성하며, 테스트 작성에 익숙해질 수 있었습니다.boolean 변수명을 지을 때 자동사/타동사 구분을 고려하며, 더 명확한 이름을 짓게 되었습니다.레이어드 아키텍처의 레이어 분리 이유를 인식하고, 이를 기반으로 테스트를 구성할 수 있게 됐습니다.기능 전체를 TDD 방식으로 구현해보며 테스트 주도 개발에 대한 감을 익혔습니다.트랜잭션 전파 개념을 떠올리며 수동 클렌징과 자동 클렌징의 차이를 이해하게 되었습니다.@DataJpaTest와 @SpringBootTest의 차이점을 학습했고, profile을 통한 테스트 환경 분리에 대해서도 이해하게 되었습니다. ⚠발견한 약점 및 보완이 필요한 부분TDD로 기능을 구현하는 과정에서, 중간 단계 테스트 작성을 종종 놓친 실수가 있었습니다.트랜잭션 전파, Spring AOP 개념 중 기억이 흐릿한 부분을 발견했습니다.@DisplayName의 명확하게 표현하는 데 어려움을 느꼈습니다.Day11 미션에서 Minesweeper 프로젝트의 단위 테스트 작성을 추가로 진행하지 못했습니다. 📄보완 계획TDD에 더욱 익숙해지기 위해 예제 프로젝트를 통한 실습을 추가로 진행할 예정입니다. Spring과 JPA의 작동 원리를 복습하며, 다음 항목들을 중점적으로 학습할 계획입니다.트랜잭션 전파Spring AOP JPA의 영속성 컨텍스트아직 작성하지 못한 Minesweeper 프로젝트의 단위 테스트를 작성합니다.예제를 통해 @DisplayName 작성에 더 익숙해지도록 합니다. 미션 내용 회고 Day11 미션 🌱 🏃첫 번째 미션[Readable Code] 강의의 두 프로젝트(지뢰찾기, 스터디카페) 중 하나를 골라, 단위 테스트를 작성해 봅시다.조건은 아래와 같습니다.✔각 프로젝트 모두 강의 중에 작성한 tobe 패키지 코드를 기준으로 함 (lesson 6-4 가 가장 마지막 버전)✔3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트 작성 (시간이 된다면 더 많이 작성해보면 좋겠죠? 😉)➡ 단, 같은 인터페이스를 구현하고 있는 구현체들은 1개 클래스로 간주한다.(ex. LandMineCell, NumberCell, EmptyCell에 각자 테스트를 작성했어도, 1개 클래스로 간주.)✔무엇을 테스트하고자 했는지를 잘 나타낸 @DisplayName 작성하기✔BDD(given/when/then) 스타일 따르기 (주석으로 표기) 🔗작업 브랜치 링크https://github.com/sonic8-8/readable-code/tree/day11-mission 미션 과정Day7 미션과 동일하게 사고 과정을 모두 Notion에 기록했습니다.다만 이번 미션에서는 문서 구조 자체의 가독성에 더욱 신경을 썼습니다.구체적으로는 다음 원칙을 적용했습니다.사고 과정은 하나의 흐름으로 기록하기 위해 Notion 문서에 작성한다.테스트 코드에는 주석을 통해 질문과 근거를 남긴다. 단위 테스트 작성 사고 과정🔗https://able-brick-82b.notion.site/Day-11-studycafe-20d2947a96118098986ad7e54c362460?source=copy_link 느낀점테스트 코드 역시 결국 읽기 좋은 코드여야 한다는 관점으로 미션을 수행했습니다.코드뿐만 아니라 문서 작성에서도 가독성을 어떻게 높일 수 있을지에 집중하다 보니,Notion이나 블로그 같은 일반적인 글쓰기에서도가독성을 높일 수 있는 방법에 대해 자연스럽게 고민하게 되었습니다. 주말 동안엔 이런 가독성 있는 코드 작성 방법에 대한 힌트를 얻고자 서점에 들렀고,클린 코드, 테스트 주도 개발, 파이브 라인스 오브 코드 등 책을 조금씩 읽어보며여러가지 인사이트를 얻을 수 있었습니다.특히 추상과 구체를 어떻게 분리해내는지에 초점을 두고 책을 읽으려했는데이 과정에서 코드의 구조를 바라보는 시야가 조금씩 확장되고 있다는 느낌을 받을 수 있었습니다. 다만, 책 분량이 많다 보니 단기간에 읽어내긴 쉽지 않다는 아쉬움도 있었습니다.그래서 워밍업 클럽이 끝난 이후에는 우빈님께 추천받은 위 서적들을 차근차근 학습하며,추상과 구체에 대해 더 고민하고 코드 작성 기본기를 탄탄히 다져나갈 계획입니다. 그리고 학습한 내용은 블로그에 저만의 방식으로 정리하고 포스팅 해서다른 사람에게도 도움이 될 수 있는 콘텐츠로 발전시켜 보고자 합니다. 🫡 강의🔗Practical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
발자국
2025. 06. 08.
1
[워밍업 클럽 4기 백엔드] 2주 차 발자국 👣
학습 내용 요약 Readable Code 코드 다듬기디테일한 부분까지 리팩토링하며 읽기 좋은 코드를 작성하는 방법을 배웠습니다.주석, 패키지를 이용한 맥락 제공, 변수와 메서드 배치 순서 등에 대해 고민하며 리팩토링 연습을 진행했습니다. 리팩토링 연습리팩토링에 필요한 지식을 갖춘 상태에서 StudyCafe 예제를 리팩토링해보며,실제로 적용하는 연습을 했습니다.Day7 미션을 수행하면서 리팩토링의 어려움과 중요성을 체감할 수 있었습니다. 기억하면 좋은 조언들능동적인 코드 읽기의 중요성을 느꼈습니다.특정 개념을 배운 뒤 그 개념만 맹신하거나 추종하지 말아야 한다는 점을 배웠습니다.좋은 리팩토링은 결국 경험에서 비롯된다는 사실을 알게됐습니다. 다양한 예제를 통해 경험을 쌓을 계획입니다. Practical Testing 테스트는 왜 필요할까?테스트가 필요한 이유를 예시를 통해 이해할 수 있었습니다. 단위 테스트수동 테스트와 자동 테스트를 비교하며, 테스트 코드의 중요성을 체감했습니다.테스트 케이스라는 개념을 통해, 케이스를 어떻게 나눌지 고민할 수 있는 시야를 조금이나마 갖게 됐습니다. TDDTDD의 핵심 가치를 배웠고, TDD를 도입하는 것 만으로도 관점에 변화가 생김을 알 수 있었습니다. 테스트는 []다.테스트도 읽기 좋아야 한다는 메시지가 인상 깊었습니다.결국 개발자는 팀으로 일하기 때문에 팀원들과의 협업이 수월하도록항상 읽기 좋은 코드 작성에 신경 써야 함을 배웠습니다. 학습 내용 회고 🌱성취StudyCafe 예제를 통해 리팩토링 경험을 쌓았습니다.코드 리뷰를 통해 부족한 점을 파악하고 개선할 수 있었습니다.오버 엔지니어링 여부를 고민하고 더 나은 방향이 없을지 생각할 수 있게 됐습니다.단위 테스트 작성법을 익혔습니다.TDD를 설명할 수 있게 됐습니다. 읽기 좋은 테스트 코드에 대해 고민할 수 있게 됐습니다. ⚠발견한 약점 및 보완이 필요한 부분출력 로직과 패스의 책임 분리를 인지하지 못했습니다.isFixedType(selectedPass)처럼 매개변수로 객체를 넘기면서도,해당 구조의 문제점을 인지하지 못했습니다.boolean 메서드명을 무조건 is로 시작하는 습관이 있었습니다.테스트 코드 작성이 아직 익숙하지 않습니다. 📄보완 계획출력 로직과 패스 예시를 통해, 책임 분리가 제대로 되었는지 끊임없이 의심하는 시야를 갖도록 노력하겠습니다.이상적으로는 좋은 개발자 분에게 꾸준히 코드 리뷰를 받아 시야를 확장해나가는 것이 가장 좋겠지만, 현실적으로 어려움이 있어 이를 보완할 방안을 고민 중입니다.selectedPass.isFixedType()처럼 객체에게 묻는 방식에 익숙해지기 위해, 객체 중심 사고를 염두에 두고 리팩토링을 연습할 계획입니다.boolean 메서드라고 해서 무조건 is로 시작하기보다는, 자동사/타동사의 구분을 통해 명확한 이름을 짓는 연습을 하겠습니다. 헷갈리는 경우에는 검색을 통해 확인하며 감각을 익힐 예정입니다.TDD를 바로 시도하기보다는, 먼저 테스트 코드 작성 자체에 익숙해지는 것을 목표로 하고 있습니다. 이를 위해, Readable Code 예제로 단위 테스트를 연습할 계획입니다.(Day11 미션은 둘 중 하나만 골라서 단위 테스트를 작성하면 되지만, 저는 둘 다 해볼 예정입니다!) 미션 내용 회고 Day7 미션 🌱 🏃첫 번째 미션[섹션 7. 리팩토링 연습]의 "연습 프로젝트 소개" 강의를 보고, '스터디 카페 이용권 선택 시스템' 프로젝트에서 지금까지 배운 내용을 기반으로 리팩토링을 진행해 봅시다. 🔗작업 브랜치 링크https://github.com/sonic8-8/readable-code/tree/day7-mission 미션 과정어떤 클래스부터 접근할지, 리팩토링의 최적 순서가 있는지, 어떤 기준을 세워야 좋을지 등 많은 고민이 있었습니다.책임과 응집도, 적절한 메서드명까지 생각하다 보니 점점 복잡해지고 혼란도 생겼습니다.하지만 강의에서 제시된 키워드를 따라가며 하나씩 풀어 나가니, 리팩토링을 차근차근 시도해볼 수 있었고 좋은 훈련이 되었습니다.처음엔 낯설고 어려웠지만, 혼자 시도해보는 경험 자체가 큰 배움이 되었습니다. 리팩토링하며 사고한 모든 과정은 Notion에 정리해 두었습니다.(부끄럽지만 리팩토링하며 혼란스러워하는 흔적도 곳곳에 남아있습니다 😅) 리팩토링 사고 과정🔗https://able-brick-82b.notion.site/Day-7-studycafe-2062947a96118052949be0ae38983e68?source=copy_link 느낀점읽기 쉬운 코드를 작성하는 것 자체가 생각보다 훨씬 어렵다는 사실을 느꼈습니다.더 좋은 표현과 적절한 동사를 익히기 위해서는 결국 많이 접해보고 직접 써보는 수밖에 없다는 것도 알게 되었습니다.만약 더 디테일한 코드 리뷰를 받을 수 있는 기회가 있다면 적극 참여하고 싶다는 생각이 들었습니다.리팩토링은 결국 경험을 통해 길러야하는 영역이기에, 더 많은 예제를 통해 리팩토링을 연습할 계획입니다.충분히 연습한 후에는 지금의 코드와 사고 과정을 돌아보며 스스로 피드백도 해보고 싶습니다. 함께 스터디에 참여하시는 분들 덕분에 많은 동기부여를 받았고, 의지도 더 다져지는 것 같습니다.모두 완주까지 힘내봐요! 🏃 마지막으로 코드 리뷰를 해주신 우빈 코치님께 감사드립니다!온라인 세션을 통해 궁금했던 점과 고민을 해결하고, 부족한 점을 돌아볼 수 있는 정말 뜻깊은 시간이었습니다.귀중한 시간을 내어 코드 리뷰 해 주셔서 진심으로 감사합니다! 🙂↕ 강의🔗Readable Code: 읽기 좋은 코드를 작성하는 사고법🔗Practical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
발자국
2025. 06. 01.
1
[워밍업 클럽 4기 백엔드] 1주 차 발자국 👣
학습 내용 요약 추상추상과 구체의 차이를 인식하고, 읽기 좋은 코드를 만들기 위한 추상화 기법을 배웠습니다. 논리, 사고의 흐름변수명 리팩토링, 매직 넘버/매직 스트링 상수 추출 등추상화된 데이터를 활용해, 최소한의 사고로도 이해할 수 있는 코드를 만드는 방법을 익혔습니다. 객체 지향 패러다임추상과 객체 지향의 관계를 고민해볼 수 있었고,SOLID 원칙을 코드에 어떻게 적용할 수 있는지 구체적인 방법을 배웠습니다. 객체 지향 적용하기가독성을 높이기 위해 객체를 더 잘게 나누고, 의미 단위로 분리하는 기법들을 배웠습니다.아직 익숙하지 않아서 많은 연습이 필요하겠지만, 굉장히 강력한 도구라는 점을 느꼈습니다. 학습 내용 회고 성취클린 코드가 왜 중요한지 근거를 들어 설명할 수 있게 되었습니다.추상화 기법을 데이터와 코드 관점으로 나누어 설명할 수 있게 되었습니다.SOLID 원칙을 이해하고, 설명할 수 있게 되었습니다.코드에 객체 지향과 추상화를 적용하고 있는지 스스로 의심하고 점검하는 눈이 조금씩 생기고 있습니다. 발견한 약점 및 보완이 필요한 부분좋은 추상화인지 판단하는 기준이 부족해, 메서드 이름을 지을 때 망설이게 되는 경우가 있었습니다.Java 기본 문법 중 일부가 기억이 흐릿하거나 헷갈리는 부분이 있었습니다.final 키워드를 사용하는 이유와 적용 범위stream API 중 flatMap() 처럼 자주 사용하지 않던 메서드의 반환 결과를 헷갈림강의에서 소개하는 리팩토링의 근거를 내가 제대로 이해하고 적용하고 있는지 점검할 기준이 필요하다고 느꼈습니다. 보완 계획좋은 추상화에 대한 판단 기준을 오픈 소스, 실무 코드, ChatGPT, 현직자 의견 등을 통해 배우고 정리할 예정입니다.헷갈리는 Java 문법은 노션에 기록하며 정리하고, Stream API는 워밍업 클럽 이후 강의를 통해 추가 학습할 계획입니다.강의에서 알려주는 리팩토링 근거를 제대로 인지하며 따라하고 있는지, 평가 기준과 프로세스를 만들어 적용 중입니다.강의가 끝난 후 기록해둔 리팩토링 근거를 보며 리팩토링 연습같은 내용을 근거를 보지 않고 리팩토링 시도 (근거가 떠오르지 않을 경우 체크해두기)리팩토링 흐름을 떠올리며 기록해보기근거를 모두 떠올리고 적용할 수 있다면 통과 미션 내용 회고 Day2 미션 🌱 첫 번째 미션 🏃추상과 구체의 예시를 표현해보기 미션 과정BBQ 자메이카 통다리 구이를 배달시켜 먹는 과정을 추상으로 정의하고, 구체로 표현해봤습니다. 느낀점일상 속 많은 개념이 이미 추상화 되어있다는 것을 새삼 느꼈습니다.정보통신 기술로 문제를 해결하거나 사업을 하기 위해선,이런 일상 속 추상화를 인식하고, 구체로 바꿔보는 훈련이 반드시 필요하다는 것을 체감했습니다. Day4 미션🌱 첫 번째 미션 🏃예시 코드를 [섹션 3. 논리, 사고의 흐름 내용]을 중심으로 읽기 좋은 코드로 리팩토링하기 미션 과정“뇌 메모리에 최소한의 정보만 있더라도 코드를 이해할 수 있는가?”를 기준으로 리팩토링을 진행했습니다.강의에서 언급된 기법들을 코드에 실제로 적용해보며 다음과 같은 고민을 하게 되었습니다.올바른 근거를 가지고 추상화를 하고 있는가?이 메서드명이 실무에서 사용하는 방식일까? 아니라면 실제로는 어떤 방식을 사용할까?Spring 프레임워크를 사용할 때와 순수 Java 프로젝트에서 추상화 방식에 차이가 있을까? 차이가 있다면 어떤 차이일까?검색, ChatGPT, GitHub 소스 코드, 그리고 사람들의 의견을 통해 이 고민들을 풀어나가며 많은 배움을 얻었습니다. [워밍업 클럽 4기 백엔드] Day4 미션Day4 미션 해결 과정 느낀점리팩토링 과정에서 고민 끝에 스스로 찾아가며 얻은 지식이라 더 오래 기억에 남을 것 같습니다.워밍업 클럽이 끝난 이후에도 스터디를 통해 더 많은 예제를 읽기 좋은 코드로 리팩토링해볼 계획입니다! 🫡 두 번째 미션 🏃SOLID를 자기만의 언어로 정리하기 미션 과정SOLID 원칙을 영단어의 어원과 이미지로 해석해보고 저만의 언어로 정리해보았습니다. 느낀점평소 학습할 때 원리와 배경을 이해하는 것을 중요하게 생각하는 편인데, 이번 시도는 특히 이해에 큰 도움이 되었습니다.한글로는 뜬금없이 느껴지던 개념들도, 영단어의 어원과 이미지를 통해 숨겨진 의미를 이해할 수 있다는 점이 정말 흥미로웠습니다.앞으로도 개념을 학습할 때, 단어나 용어에 담긴 이미지와 어원, 뉘앙스를 통해 더 깊이 이해하려는 시도를 꾸준히 이어나갈 계획입니다. 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
워밍업클럽
・
백엔드
・
발자국
2025. 05. 30.
0
[워밍업 클럽 4기 백엔드] Day4 미션
미션1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다. [리팩토링 전]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; } 순서early return 적용 Order 도메인 생성 및 구현OrderService, Item, CustomerInfo 생성 및 구현조건문의 조건을 메서드 추출해 추상화 레벨 맞추기 OrderService의 위 메서드 명이 validateOrder()일 경우 boolean 반환을 void 반환으로 변경로그 출력을 예외 처리로 변경if 분기문을 하나씩 메서드 추출try-catch로 감싸기 or AOP를 이용한 Exception 처리 후보OrderService의 위 메서드가 boolean을 반환할 경우 메서드 명을 isValidOrder()로 변경로그 출력 형태 유지 +is와 hasis는 상태를 확인하기 위해 사용했고 (empty, enabled, active 등)has는 특정 정보나 구성 요소가 있는지 확인하기 위해 사용했습니다. [리팩토링 후]OrderServicepublic void validateOrder(Order order) { try { validateItemsExist(order); validateTotalPrice(order); validateCustomerInfo(order); } catch (InvalidOrderException e) { log.error("검증 실패: {}", e.getMessage(), e); } catch (Exception e) { log.error("서비스에 문제가 발생했습니다.", e); } } private static void validateCustomerInfo(Order order) { if (order.hasNoCustomerInfo()) { throw new InvalidOrderException("사용자 정보가 없습니다."); } } private static void validateTotalPrice(Order order) { if (order.isInvalidTotalPrice()) { throw new InvalidOrderException("올바르지 않은 총 가격입니다."); } } private static void validateItemsExist(Order order) { if (order.hasNoItems()) { throw new InvalidOrderException("주문 항목이 없습니다."); } } Orderpublic class Order { private List items; private CustomerInfo customerInfo; private int totalPrice; public boolean hasNoCustomerInfo() { return customerInfo == null; } public boolean hasNoItems() { return getItems().isEmpty(); } public boolean isInvalidTotalPrice() { return calculateTotalPrice() getItems() { return items; } } Item, CustomerInfo (클래스만 생성했습니다)public class Item { } public class CustomerInfo { } InvalidOrderExceptionpublic class InvalidOrderException extends RuntimeException { public InvalidOrderException(String message) { super(message); } } 2. SOLID에 대하여 자기만의 언어로 정리해 봅시다. SOLID를 직관적으로 이해하고 싶어영단어의 어원과 이미지를 찾아보며 정리해봤습니다 SRP (Single Responsibility Principle)"A class should have only one reason to change"단일 책임의 원칙 spondere: 그렇게 하겠소라고 약속하는, 대답하는 이미지re-: 순환해서 다시 돌아오는 이미지respondere: 상대방이 물어보거나 요구해서 (변화나 반응을 일으킨다고 이해함) 약속하겠다라고 대답을 돌려주는 이미지able: 어떤 일을 할 자격과 조건이 갖추어진 만반의 상태 responsibility: 상대방이 나에게 무언가를 요구하거나 질문했을 때, (변화 요구라고 이해했습니다)이를 들어주겠다 대답할 능력과 준비가 되어있는 만반의 상태 정리Single Responsibility Principle은오직 한 종류의 변화(요구)에 대해 들어주겠다며 대답할 능력과 준비가 돼있어야한다는 것으로 이해했습니다. OCP (Open-closed Principle)"Software entities should be open for extension, but closed for modification"개방-폐쇄 원칙 extension: 밖에 덧붙이는 이미지modification: 안쪽을 다 뜯어고치는 이미지 정리OCP는 "변화가 왔을때 어떻게 대응할 것인가"에 대한 규칙이라고 생각됩니다.변화가 왔을때 기존의 것을 뜯어고치는게 아닌 덧붙이는 방식이라고 이해했습니다. LSP (Liskov Substitution Principle)"Subtypes must be substitutable for their base types"리스코프-치환 법칙 sub-: under, below, 원래의 것에 속해있거나 보조하는 식으로 위계질서의 하단부 or 물리적으로 아래에 있는 이미지stituere: to place (무엇인가를 세워놓은 느낌) substitute: 원래 있던 자리에 다른 것을 대신해서 세워놓는 이미지 정리서브 타입을 원래의 것 대신 세울 수 있어야한다. 자식이 부모를 대신할 수 있어야 한다.위 둘을 집합의 개념으로 이해했습니다. ISP (Interface Segregation Principle)"Clients should not be forced to depend on interfaces they do not use"인터페이스 분리 법칙 se-: 떨어지는 느낌gregare: 무리짓다, 그룹짓다 segregate: 무리(같은 타입)에서 떨어트려, 새로운 무리로 구분하는 이미지 정리사용하지 않는 인터페이스 기능이 있다면 쪼개서, 사용하는 기능과 사용하지 않는 기능으로 인터페이스로 분리하고 그것에 의존하는 것이라고 이해했습니다. DIP (Dependency Inversion Principle)"High-level modules should not depend on low-level modules. Both should depend on abstractions""Abstractions should not depend on details. Details should depend on abstractions"의존성 역전 원칙 de-: down, away (위에서 아래로 떨어지는 이미지, 방향을 가짐)pendere: to hang (매달려있는 이미지) depend: 아래로 축 쳐진 상태에서 매달려 있는 이미지 in-: 안쪽에 있는 기준점으로 향하는 이미지vertere: to turn 축(선)을 기준으로 180도 회전하는 이미지 inverse: 밖으로 가고있던 방향을 180도 뒤집어서, 안쪽에 있는 기준점으로 향하는 느낌 정리depend와 inverse를 조합하면위에서 아래로 매달리던 것을, 아래에서 위로 매달리도록 방향을 180도 뒤집는 이미지입니다.그러나 실제로는 중간에 인터페이스를 둬서 위에선 중간에 의존하고, 아래에서도 중간에 의존하는 이미지라방향 전환이라고만 생각한다면 한 번 생각을 거치게 되는 것 같습니다.따라서 의존성 추상화 원칙(Dependency Abstraction Principle)이라고 생각하는 것도 직관적이고 이해에 좋을 것 같습니다. 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법
워밍업클럽
2025. 05. 28.
0
[워밍업 클럽 4기 백엔드] Day2 미션
미션"추상과 구체" 강의를 듣고, 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어봅시다. 추상배달의 민족으로 BBQ 자메이카 통다리 구이를 시켜먹는다. 🍗 구체스마트폰으로 음식을 주문할 수 있는 모바일 서비스에 접속한다.오토바이나 차량이 배달 올 수 있을 정도로 가까운 거리안에 있는 BBQ 매장을 선택한다.BBQ의 음식 메뉴 중 자메이카 통다리 구이를 선택한다.주문 방식과 주문 요청 사항을 입력하고 결제 방식으로 카드를 선택해 결제한다.요청한 주문이 가게에 전달되고 가게에서는 주문을 수락해 주문이 완료된다.주문 상태가 음식 조리중으로 변경된다.BBQ 매장에서 자메이카 통다리 구이를 만든다.음식이 완성되면 배달 기사를 요청하고 음식을 픽업하기까지 기다린다.배달기사가 음식을 픽업하고 주문 상태가 배달 시작으로 변경된다.배달 기사가 음식을 갖고 내가 있는 장소까지 이동한다.배달 기사가 음식을 배달 완료하고 주문 상태가 배달 완료로 변경된다.배달 완료 알람을 보고 음식을 집으로 옮긴다.포장을 뜯고 자메이카 통다리 구이를 맛있게 먹는다. 강의Readable Code: 읽기 좋은 코드를 작성하는 사고법
워밍업클럽