블로그
전체 102025. 04. 09.
1
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 후기
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot) 후기 입니다.소개이번 스터디 3기는 백엔드 클린 코드, 테스트 코드를 주제로 진행하였습니다. “Readable Code: 읽기 좋은 코드를 작성하는 사고법”과 “Practical Testing: 실용적인 테스트 가이드” 강의를 함께 학습함으로써, 더 나은 코드 가독성과 안정적인 테스트를 위한 다양한 방법론을 실습할 수 있었습니다.지난 2기 때 Kotlin과 Spring Boot로 백엔드 개발 전반을 경험하며 웹 애플리케이션 동작 원리를 학습했다면, 이번 3기에서는 코드 품질과 테스트라는 핵심 주제를 깊이 파고들어, 보다 깨끗하고 유지보수하기 좋은 코드, 그리고 신뢰도 높은 테스트 코드를 작성하는 법을 익혔습니다.아래에서는 각 주차별 학습 내용을 정리하고, 전체 스터디를 통해 느낀 점과 앞으로의 계획을 공유하고자 합니다. 1주차: 클린 코드를 위한 기초 - 추상과 구체, SOLID 원칙1주차 주요 학습 내용추상과 구체의 개념논리와 사고의 흐름SOLID 원칙 (SRP, OCP, LSP, ISP, DIP)리팩토링 미션 수행1주차에서는 코드 가독성을 높이는 가장 기본이 되는 개념들을 배웠습니다. 특히 추상과 구체의 균형을 어떻게 잡아야 코드가 명확해지고, 협업자가 빠르게 이해할 수 있는지 고민할 수 있었습니다.추상은 핵심 개념과 의도만 드러내도록 하는 장점이 있지만, 너무 과도하면 실제 구현과 괴리가 생겨 혼란을 줄 수 있다는 점을 배웠습니다.구체는 세세한 내용을 모두 표현해 직관적이지만, 과도해지면 복잡해지고 가독성이 떨어질 수 있으므로 주의가 필요했습니다.또한 Early Return이나 부정어 최소화 같은 리팩토링 기법을 적용해 보면서, 간단한 습관만으로도 코드의 가독성이 크게 높아진다는 점을 실감했습니다. 2주차: 코드 구조 및 리팩토링 - 패키지 구조, 주석, 적정 기술2주차 주요 학습 내용효과적인 주석 사용 방식변수와 메서드의 나열 순서패키지 구조 설계와 적정 기술(Over Engineering 지양)스터디카페 이용권 시스템 리팩토링2주차에서는 코드 구조 전반을 깔끔하게 정비하는 방법을 중심으로 학습했습니다.주석은 변동이 잦거나 오해를 일으킬 만한 내용을 담지 말고, 정말 필요할 때만 사용해야 한다는 것을 다시금 확인했습니다.변수와 메서드를 사용하는 순서대로 배치하고, 공개 메서드(public) → 비공개 메서드(private) 순서로 자연스럽게 구성함으로써, 읽는 흐름을 방해하지 않는 것이 중요하다는 점을 배웠습니다.특히 리팩토링 미션을 통해 패키지 구조가 지나치게 단순하거나, 반대로 너무 세분화되어 관리가 어렵지 않은지 돌아볼 수 있었습니다. 적정 기술을 사용하는 태도. 즉, 문제를 해결하는 데 필요한 만큼만 기술 스택과 디자인 패턴을 적용하는 것이 얼마나 중요한지도 체감하게 되었습니다. 3주차: Spring & JPA, 계층형 아키텍처, 통합 테스트3주차 주요 학습 내용Spring & JPA를 이용한 계층형 아키텍처 (Controller, Service, Repository)단위 테스트와 통합 테스트의 차이점 및 작성 방법MockMvc, @DataJpaTest 등을 활용한 테스트 실습Bean Validation과 ExceptionHandler로 유효성 검증 및 예외 처리3주차부터는 실무에서 흔히 쓰이는 Spring & JPA 기반의 계층형 아키텍처에 초점을 맞춰, 전체적인 흐름을 학습했습니다.Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)이러한 구조를 바탕으로, MockMvc를 사용해 Controller부터 Service, Repository까지 이어지는 통합 테스트를 작성해 봄으로써, 애플리케이션 전반이 정상 동작하는지 확인할 수 있었습니다.또한 @NotNull, @Positive 등 Bean Validation 애너테이션과 @RestControllerAdvice를 통한 예외 처리 방식도 연습해보았습니다. 4주차: Mock, Spy, @MockBean / @SpyBean, 레이어별 테스트 전략4주차 주요 학습 내용Mockito에서의 @Mock, @Spy, @InjectMocks스프링 환경에서의 @MockBean, @SpyBean레이어별 테스트 설계: 단위 테스트 vs 통합 테스트테스트 시나리오 설계(given-when-then)와 @BeforeEach 활용마지막 주차에서는 테스트 유지보수와 테스트 코드 설계의 중요성을 배웠습니다.@Mock, @Spy를 통해 순수 단위 테스트 환경에서 외부 의존성을 제거(또는 부분 제거)하여 빠른 피드백을 받을 수 있었습니다.반면 @MockBean, @SpyBean은 스프링 애플리케이션 컨텍스트를 띄운 상태에서 기존 Bean을 모의 객체로 교체할 수 있어, 좀 더 넓은 범위의 통합 테스트에 활용할 수 있었습니다.레이어별 테스트 (Controller, Service, Repository)를 분리해 진행하면 테스트가 더욱 명확해지고, 문제 발생 시 어느 레이어가 원인인지 빠르게 파악할 수 있음을 깨달았습니다.가장 크게 느낀 점은 ‘테스트도 코드의 일부’라는 사실이었습니다. 테스트 코드를 지속적으로 관리하고 개선해야, 유지보수가 필요한 시점에 믿을 수 있는 안전망이 되어준다는 것을 명확히 알게 되었습니다.회고이번 스터디 3기를 통해 "클린 코드와 테스트"라는 주제의 중요성을 다시 한번 실감했습니다. 단순히 기능만 구현하는 것이 아닌, 가독성, 유지보수성, 테스트 커버리지까지 고려해야 진정한 의미의 소프트웨어 품질을 높일 수 있음을 배웠습니다.아쉬웠던 점 & 보완 계획실프로젝트 적용: 강의와 미션 중심의 학습이기에, 실제 현업 수준의 복잡도를 가진 코드에 적용할 기회가 많지 않았습니다. 앞으로 개인/팀 프로젝트에서 적극 활용해 경험치를 높이고자 합니다.테스트 시나리오 다양성: 해피 케이스 중심의 테스트가 많았기에, 예외 상황과 경계값 테스트를 더 폭넓게 다루면 좋았을 것 같습니다. 마무리인프런 워밍업 클럽 스터디 3기를 통해, 클린 코드와 테스트 코드 작성 역량을 크게 향상시킬 수 있었습니다.코드: 명료한 이름 짓기, 중복 제거, 적절한 추상화 등을 통해 읽기 좋고 변화에 강한 코드를 추구하게 되었습니다.테스트: 프로젝트가 확장되더라도, 안정성을 확보할 수 있는 테스트 설계가 얼마나 중요한지 다시금 느끼게 되었습니다. 이번 스터디에서 성실히 참여한 결과, 2기에 이어 3기에서도 우수러너로 선정되는 영광도 누릴 수 있었습니다. 앞으로도 스스로 레거시 코드를 리팩토링하거나, 프로젝트를 확장하면서 배운 내용을 꾸준히 적용할 것입니다. 스터디에서 함께한 분들, 그리고 인프런과 워밍업 클럽 관계자분들께 감사드리며, 지속적인 학습과 공유를 통해 더 나은 개발자가 되도록 노력하겠습니다. 감사합니다!
백엔드
・
인프런
・
인프런워밍업클럽
・
스터디3기
・
워밍업
・
백엔드
・
후기
・
클린
・
테스트
・
코드
2025. 03. 30.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 4주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)4주차 발자국 입니다.학습 내용 요약이번 주에는 Practical Testing: 실용적인 테스트 가이드 강의에서 계층형 아키텍처(Layered Architecture) 의 개념을 다시 한번 정리하고, 스프링 기반 테스트에서 자주 사용하는 Mock/Spy 기법과 관련 애너테이션들을 배웠습니다.Layered Architecture (계층형 아키텍처)Presentation Layer: 사용자 입력과 응답을 담당. API 요청/응답 혹은 프론트엔드 인터페이스를 제공하며, 기본적인 파라미터 검증과 변환만 수행.Business Logic Layer: 애플리케이션의 핵심 비즈니스 로직과 규칙을 구현. 서비스(Service)나 도메인(Domain) 로직이 위치하며, 트랜잭션 관리의 주된 대상이 됨.Persistence Layer: 데이터베이스/파일시스템 등 영속성 저장소와 직접적으로 상호작용. Repository(DAO) 형태로 CRUD를 담당.Mockito & Spring Test 관련 애너테이션@Mock / @Spy / @InjectMocks: 순수 Mockito 환경(스프링 컨텍스트 없이)에서 가짜 객체(또는 부분 가짜 객체)를 만들어 단위 테스트를 쉽게 작성할 수 있게 함.@MockBean / @SpyBean: 스프링 애플리케이션 컨텍스트를 기동하는 통합 테스트 환경에서 특정 Bean을 Mock 또는 Spy로 교체하여, 외부 의존성을 간단히 제어하고자 할 때 활용.테스트 설계 방향각 레이어별로 테스트 범위를 명확히 하여, “Persistence → Business → Presentation” 순으로 책임을 분리해 점진적으로 테스트 커버리지를 넓힘.@MockBean / @SpyBean을 과도하게 사용하지 않도록 주의: 꼭 필요한 부분만 모의(Mock/Spy) 처리하고, 나머지는 실제 로직을 이용해 통합 테스트를 안정적으로 수행.테스트 시나리오별로 @BeforeEach를 활용하는 방법과, 각 테스트 메서드에서 독립적으로 given-when-then을 구성하는 방법을 비교.학습 회고얻은 인사이트레이어별 책임과 역할이 분명해야 복잡한 서비스에서도 테스트 설계가 한결 명확해짐을 느꼈습니다.Mock/Spy를 올바른 상황에서만 사용하면 외부 의존성을 최소화하여 빠른 단위 테스트를 작성할 수 있으나, 반대로 불필요하게 남발하면 테스트 유지보수가 어려워짐을 체감했습니다.어려웠던 점@MockBean과 @SpyBean을 사용해 스프링 컨텍스트를 띄울 때 테스트 속도가 느려지거나, 동일 타입 Bean이 여러 개일 때 어느 Bean을 대체하는지 혼동될 수 있었습니다.레이어별 테스트를 작성하면서, 중복으로 보이는 코드(예: 테스트 준비 로직)를 어느 정도까지 @BeforeEach로 추출해야 할지 고민이 되었습니다.미션🎯 Day16 미션: Layered Architecture 구조의 레이어별 특징과 테스트 방법요구사항Layered Architecture에서 각 레이어가 하는 역할, 특징, 그리고 테스트 방식을 자기만의 언어로 정리하기나만의 정리Presentation Layer역할: 사용자나 외부 시스템의 요청을 받고 응답을 전달. 파라미터 검증, DTO 변환, HTTP 상태 코드 결정 등을 담당.특징: 비즈니스 로직을 직접 수행하지 않고, 유효성 체크 후 Service 호출 → 결과 반환에 집중.테스트 방법: 컨트롤러 통합 테스트(@SpringBootTest, @WebMvcTest + MockMvc)를 통해 실제 요청/응답 형식을 모의하거나, 프론트엔드와의 e2e 테스트를 진행해볼 수도 있음.Business Logic Layer역할: 애플리케이션의 핵심 규칙, 알고리즘, 트랜잭션 등의 로직 담당.특징: 여러 Repository를 조합하여 도메인 로직을 수행하고, 예외 처리, Validation 등의 업무 로직을 포괄적으로 관리.테스트 방법: 단위 테스트를 통해 특정 비즈니스 로직의 정확성을 검증. 필요 시 Repository를 Mock 처리(예: @Mock, @MockBean)하여 DB 의존성을 제거하고 로직만 집중 테스트.Persistence Layer역할: 데이터의 저장/조회/수정/삭제 등 영속성 처리에 집중.특징: 쿼리 작성, DB 연결, CRUD 로직을 추상화한 Repository/DAO 형태로 제공.테스트 방법: 실제 DB 혹은 In-Memory DB(H2 등)를 활용한 통합 테스트(@DataJpaTest). 쿼리 정확도, 트랜잭션 처리, 성능 등을 검증하기에 좋음.🎯 Day18 미션 1: @Mock, @MockBean, @Spy, @SpyBean, @InjectMocks의 차이점 정리요구사항각 애너테이션이 어떤 환경(단위 테스트/통합 테스트)에서 사용되고, 어떤 특징이 있는지 정리하기1) @Mock주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:순수 Mock 객체를 생성하여, 내부 로직 없이 호출 기록/결과(Stubbing)만 지정할 수 있음단위 테스트에서 외부 의존성을 배제하고 특정 로직만 검증할 때 유용2) @MockBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 애플리케이션 컨텍스트에 등록된 실제 Bean을 Mock으로 교체다른 Bean이 해당 Bean을 의존하고 있으면, 그 의존성도 가짜(Mock)로 처리됨@SpringBootTest나 @WebMvcTest 등 스프링 컨텍스트가 뜨는 환경에서 사용3) @Spy주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: X (별도 Bean 아님)특징:부분 가짜(Spy) 객체를 생성기본적으로 실제 메서드 동작을 유지하면서, 필요한 부분만 가짜(Stubbing)로 설정 가능복잡한 객체를 테스트할 때, 일부는 실제 로직을 실행하고 일부만 Mock 처리할 수 있어 유연함4) @SpyBean주로 사용 환경: Spring Boot Test(통합 테스트)스프링 빈 등록 여부: O (빈으로 등록됨)특징:스프링 컨텍스트 내 실제 Bean을 Spy로 교체기본적으로는 실제 로직을 수행하되, 특정 메서드만 가짜로 동작시키거나 호출을 기록할 수 있음통합 테스트 환경에서 부분 Mocking이 필요할 때 적합5) @InjectMocks주로 사용 환경: Mockito (단위 테스트)스프링 빈 등록 여부: 해당 없음특징:@Mock 또는 @Spy로 만들어진 객체들을 자동으로 주입(생성자, 세터, 필드 순)해줌예: @InjectMocks UserService라면, UserService가 의존하는 Repository나 다른 객체들을 @Mock으로 생성해 주입받을 수 있음단위 테스트에서 여러 의존 객체를 편리하게 Mock/Spy로 대체할 수 있도록 지원 🎯 Day18 미션 2: 테스트 시나리오(@BeforeEach, given, when, then) 구성요구사항아래 3개의 테스트 메서드가 있을 때, “어떤 준비 로직을 @BeforeEach로 뽑고, 어떤 부분을 각 테스트의 given절에 둘지”, “when절을 어떻게 구성할지”를 구상해보기예시 테스트 3종사용자가 댓글을 작성할 수 있다.사용자가 댓글을 수정할 수 있다.자신이 작성한 댓글이 아니면 수정할 수 없다.나의 구성@BeforeEach void init() { // 공통 데이터를 미리 생성하지 않습니다. /* * 각 테스트가 각각의 상황(사용자, 게시물, 댓글)을 자유롭게 구성할 수 있도록, * 여기서는 사전에 아무것도 세팅해두지 않습니다. * 테스트마다 필요한 데이터 타입이나 조건이 다를 수 있으므로, * 각 테스트 메서드 내부에서 직접 객체를 생성하고 준비해 주는 방식을 채택했습니다. */ } @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. 사용자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정말 모든 테스트에 공통으로 필요한 세팅(예: DB clean-up, 동일한 테스트 데이터 세팅 등)이면 이곳에 배치.하지만 테스트마다 시나리오가 크게 다른 경우, 오히려 각 테스트 내부(given 절)에서 데이터를 생성하는 편이 가독성과 유연성이 좋아질 수 있음.given 절테스트에 필요한 사전 조건(사용자, 게시물, 댓글 등) 및 Mock/Stubbing이 필요하다면, 해당 로직을 배치.시나리오별로 조금씩 다른 상황을 구성할 때, @BeforeEach보다 각 테스트 내부의 given 부분에 명시적으로 작성하는 방식이 선호됨.when 절실제 테스트 대상 메서드를 호출하는 구간. 한 개의 테스트에서 “정확히 하나의 when 절”을 유지해, 명확히 어떤 동작을 검증하는지 드러내도록 함.then 절결과 검증(Assertions) 수행. 저장된 데이터 확인, 예외 발생 여부 확인, 반환값 확인 등.미션 회고레이어별 특징을 자기 언어로 설명단순히 문서상 개념을 반복하기보다, 실제 현업/프로젝트에서 마주치는 구조를 떠올리며 다시 풀어내보니, 각 계층이 왜 필요한지, 테스트를 어떻게 접근해야 하는지 더 명확해졌습니다.Mock/Spy 애너테이션 정리정리를 해보니 실제로 코드에 적용할 때 어떤 상황에서 무엇을 써야 하는지가 더 분명해졌습니다.@MockBean/@SpyBean이 스프링 컨텍스트를 사용하는 통합 테스트에서 유용하지만, 속도와 Bean 교체 이슈가 있어 주의가 필요하다는 점이 인상적이었습니다.3가지 테스트 시나리오에서 @BeforeEach & given/when/then 배치시나리오에 따라 @BeforeEach를 최소화하거나, 여러 테스트에 공통적으로 필요한 부분만 추출하는 방식이 각각 장단점이 있음을 확인했습니다.테스트 구조를 “given-when-then”으로 고정하면 가독성이 올라가지만, 너무 세세한 단계 분리는 오히려 장황해질 수 있으므로 균형을 잡아야겠습니다.회고칭찬하고 싶은 점레이어별 테스트 기법과 Mock/Spy 전략이 한층 체계적으로 잡혔습니다.실제 코드를 작성하며, “가짜 객체를 어디까지 써야 하나? 외부 연동은 어떻게 테스트하나?” 같은 고민을 많이 해 봐서, 앞으로는 목적에 맞는 테스트를 설계하는 능력이 향상될 것 같습니다.아쉬웠던 점 & 보완 계획Mock 남발 시 발생할 수 있는 문제(설정 복잡도 상승, 실제 로직 변경 시 Stubbing 깨짐)를 더 구체적인 예제로 다뤄보면 좋을 것 같습니다.@BeforeEach와 개별 테스트 내부 given 절의 적절한 균형점을 찾으려면, 실제 현업 수준의 다양한 테스트 케이스를 더 경험해 봐야겠습니다.다음 목표예외 상황 테스트 강화단순 성공 케이스뿐 아니라, 비정상 입력이나 예외 케이스를 좀 더 체계적으로 정리하고 테스트에 반영. 도메인 별로 테스트 슬라이싱@DataJpaTest, @WebMvcTest 등 스프링이 제공하는 슬라이스 테스트 방식을 적극 활용해 보기.테스트 실행 속도 최적화통합 테스트와 단위 테스트를 적절히 조합하여, 빠르면서도 신뢰성 있는 테스트 환경을 구축해 보기.이번 4주차에는 계층형 아키텍처를 다시 한번 복습하면서 Mock/Spy, @MockBean/@SpyBean 등 스프링 테스트 환경에서 자주 쓰이는 기법들을 정리하고 적용해 보았습니다. 학습 내용과 미션을 통해 각 레이어가 가진 의미와 책임이 더욱 또렷해졌고, 가짜 객체를 어떻게 잘 활용해야 하는지 감이 잡힌 것 같습니다.앞으로도 학습 내용을 기록하고 회고하면서, 점점 더 탄탄한 테스트 코드를 작성해 가도록 하겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 23.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 3주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)3주차 발자국 입니다.학습 내용 요약이번 주에는 Spring & JPA 환경에 계층형 아키텍처(Layered Architecture) 및 테스트 기법(통합 테스트, MockMvc, JPA 테스트 등)을 학습했습니다.1⃣ Spring & JPASpring이 제공하는 IoC/DI 개념과 ORM(JPA, Hibernate) 기반 데이터 접근 방식을 이해했습니다.엔티티(Entity) 설계를 통해 도메인 모델링을 하고, 리포지토리(Repository) 계층으로 데이터 CRUD를 담당하도록 분리했습니다. 2⃣ Layered ArchitecturePresentation Layer(Controller)Business Layer(Service)Persistence Layer(Repository)컨트롤러는 요청/응답을 처리하고, 비즈니스 로직은 서비스가 담당하며, 데이터 접근은 리포지토리를 통해 수행합니다.3⃣ 테스트 코드 작성단위 테스트: 특정 클래스나 메서드 단위 검증. 예: JUnit + AssertJ 사용통합 테스트: 스프링 컨텍스트를 로드해 여러 레이어(Controller, Service, Repository)의 협력을 검증.MockMvc를 사용해 테스트하는 방식을 배웠습니다.4⃣ 유효성 검증(Bean Validation)DTO에서 @NotEmpty, @NotNull, @Positive 등 애노테이션을 사용하여 입력값을 검증하고,RestControllerAdvice로 BindException 등을 처리하여 예외 상황 시 API 응답을 표준화했습니다.✨ 학습 회고Spring & JPA 학습을 통해, 계층형 아키텍처 전체 흐름을 이해해야 함을 느꼈습니다.테스트 코드에서, 단위 테스트와 통합 테스트의 경계와 필요성을 체감했습니다.JUnit5 + AssertJ로 간단한 메서드 단위 검증을 할 때는 편리했지만, MockMvc로 검증하거나, @DataJpaTest로 테스트할 때는 더 많은 설정이 필요했습니다. 그만큼 테스트 커버리지가 넓어지며 신뢰도가 높아진다는 장점을 느꼈습니다.Validation과 ExceptionHandler를 통해 잘못된 입력을 막고, 표준화된 에러 응답을 제공하는 프로세스가 얼마나 중요한지 깨달았습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 테스트 미션미션스터디카페 이용권 선택 시스템의 테스트 코드를 작성하는 미션.미션 목표메시지 출력 테스트할인 로직 테스트사물함 선택 테스트 해결 과정OutputHandlerTest환영 메시지 + 공지사항 “프리미엄 스터디카페” 등의 문구가 제대로 찍히는지 확인.주문 내역 요약에 “이용 내역”, “이용권”, “사물함”, “총 결제 금액” 같은 키워드가 포함되는지 검증.StudyCafePassOrderTest할인율 및 가격 계산이 의도대로 동작하는지 (예: (250,000 + 10,000) - 할인액 25,000 = 235,000) 확인.사물함이 있는지 없는지에 따라 Optional가 올바르게 반환되는지 테스트.StudyCafeSeatPassTest할인율이 0일 때 할인 금액이 0원인지, 0.1이면 정상 할인액이 계산되는지.시간권(HOURLY)은 cannotUseLocker() == true, 고정석(FIXED)은 false인지 등을 테스트로 보장. 회고도메인 로직이 잘 분리되어 있어 테스트 작성이 수월했습니다. 만약 복잡한 코드가 여기저기 있었다면, 테스트도 훨씬 어렵고 중복될 뻔했습니다. 또한 예외 처리 부분에 대한 테스트 코드의 필요성도 느껴졌습니다. 예외 상황에 대한 흐름을 명확하게 검증하고, 안정적인 동작을 보장하기 위해서는 예외 처리에 대한 테스트도 함께 구성되어야 한다고 생각했습니다.🔗 테스트 미션 깃허브 링크회고스스로 칭찬하고 싶은 점도메인 흐름 파악: Controller → Service → Repository 순으로 데이터가 흐르는 구조를 더욱 제대로 이해하게 되었습니다. 다양한 스프링 테스트 어노테이션(WebMvcTest, SpringBootTest, DataJpaTest 등)을 직접 적용하며, 각 용도에 맞게 테스트를 구분해본 경험이 유익했습니다.아쉬웠던 점 & 보완할 점테스트 격리: 통합 테스트 시 상태를 초기화하는 과정이 번거로웠고, 테스트 순서에 따라 의존성이 생기지 않도록 더 철저히 관리해야 함을 느꼈습니다.테스트 시나리오 다양성 부족: 대부분 해피 케이스 위주로 검증한 감이 있어, 더욱 폭넓은 예외 상황에 대한 시나리오를 추가 작성하면 안정성이 높아질 것 같습니다.다음 주 학습 목표Mock과 Test Double: Mock과Test Double 개념을 학습하고, 적절히 사용해 볼 예정입니다.테스트 환경 독립성:테스트 시 독립적인 환경을 유지하는 방법을 살펴볼 예정입니다.테스트 개선 기법:조금 더 간결하고 가독성 높은 테스트 작성을 시도해 볼 계획입니다.이번 주에는 Spring & JPA 환경에서의 테스트 코드 작성을 중점적으로 경험 했습니다. 이를 통해 서비스가 단계적으로 확장되더라도, 테스트 코드가 안정적인 작동을 보장할 수 있다는 점을 다시금 깨달았습니다. 앞으로도 테스트 우선 방식으로 새로운 기능이나 리팩토링을 진행해, 안전하고 효율적인 협업 환경을 유지해 나가겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 16.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 2주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)2주차 발자국 입니다.학습 내용 요약이번 주는 클린 코드 작성법에 이어 코드 구조와 리팩토링, 그리고 테스트 코드 작성법을 본격적으로 학습했습니다.1⃣ 주석의 양면성주석은 코드 자체로 표현하기 어려운 의사결정의 히스토리를 기록할 때 사용해야 합니다.자주 변하는 정보는 주석으로 표현하면 안 되고, 주석은 코드와 함께 꾸준히 관리해야 합니다.부정확한 주석은 주석이 없는 코드보다 더 해롭습니다.2⃣ 변수와 메서드의 나열 순서변수는 사용하는 순서대로 나열하고, 공개 메서드를 먼저 배치하여 외부 세계에 제공하는 기능을 명확히 드러냅니다.비공개 메서드는 공개 메서드에서 호출되는 순서로 나열하여 코드 읽기 흐름을 자연스럽게 만듭니다.나열 순서도 의도를 표현하는 중요한 수단이 될 수 있음을 배웠습니다.3⃣ 패키지 나누기패키지 구조는 도메인 문맥을 반영하여 설계하며, 너무 잘게 나누거나 너무 크게 뭉치는 것을 지양해야 합니다.팀 내 합의가 중요하며 초기 설계 단계에서 패키지 구조를 충분히 고민해야 합니다.4⃣ 적정 기술의 사용모든 기술은 적정한 수준과 시점에서 활용해야 하며, 무리한 추상화나 오버 엔지니어링은 오히려 개발 효율성을 떨어뜨립니다.클린 코드는 만능 해결책이 아니며, 상황과 목적에 맞는 균형 잡힌 접근이 중요합니다.5⃣ 테스트 코드와 TDD자동화된 단위 테스트(JUnit5, AssertJ)를 통해 신뢰성과 유지보수성을 높이고, 빠른 피드백을 받을 수 있습니다.TDD(Test Driven Development)를 통한 선(先) 테스트 작성, 후(後) 기능 구현 방식을 이해했습니다.Given-When-Then 구조를 이용한 BDD(Behavior Driven Development) 스타일로 작성하면 도메인 로직을 명확히 표현할 수 있습니다.미션 해결 과정🎯 스터디카페 이용권 선택 시스템 리팩토링 미션미션'스터디 카페 이용권 선택 시스템'의 기존 시스템에 중복된 로직과 혼재된 책임을 명확히 분리하고, 객체지향 원칙을 준수하여 코드 구조를 개선하는 리팩토링 미션입니다.접근 관점추상화 레벨 맞추기: 중복 로직을 별도 메서드로 추출했습니다.객체의 책임 분리: StudyCafeOrder 객체를 추가해 할인 계산 로직을 전담했습니다.DIP(의존성 역전 원칙): 파일 접근 로직을 인터페이스(StudyCafeRepository)로 추상화하여 확장성을 확보했습니다.해결 과정1. 기존의 복잡한 분기 조건(if-else)을 메서드로 깔끔하게 정리했습니다.Before: if (HOURLY) { ... } else if (WEEKLY) { ... } else if (FIXED) { ... } 로직이 길고, 사물함 로직도 그 안에서 처리.After:public void run() { // 1) passType 선택 (시간권/주단위/고정석) StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction(); // 2) pass 선택 StudyCafePass selectedPass = selectPass(passType); // 3) 사물함 선택 (고정석 only) StudyCafeLockerPass lockerPass = maybeSelectLocker(selectedPass); // 4) 주문 객체 생성 & 결과 출력 StudyCafeOrder order = new StudyCafeOrder(selectedPass, lockerPass); outputHandler.showOrderResult(order); }이점: 각 단계가 메서드로 분리되어 이해하기 쉬움. passType별로 중복된 분기 로직이 크게 줄었음. 2. 할인 및 총금액 계산 로직을 전담하는 객체를 만들어 로직을 명확히 했습니다.Before: 할인금액, 총액 계산 로직이 OutputHandler.showPassOrderSummary 안에 위치.After: StudyCafeOrder라는 도메인 객체가 이 로직을 담당.public int getDiscountAmount() { return (int) (selectedPass.getPrice() * selectedPass.getDiscountRate()); } public int getTotalPrice() { int discountPrice = getDiscountAmount(); int lockerPrice = (lockerPass != null) ? lockerPass.getPrice() : 0; return selectedPass.getPrice() - discountPrice + lockerPrice; }이점: 필요 시 “할인 정책”이 바뀌어도 StudyCafeOrder만 수정하면 되며, 출력부는 깔끔하게 유지. 3. 파일 접근 방식을 인터페이스와 구현체로 나누어 유지보수와 확장성을 높였습니다.Before: StudyCafeFileHandler라는 클래스에서 직접 파일 접근 + 변환.After: StudyCafeRepository(추상) + StudyCafeFileRepository(구현).StudyCafePassMachine은 StudyCafeRepository repository에만 의존 → DIP 준수.향후 DBRepo나 InMemoryRepo 추가 시 StudyCafeRepository만 구현하면 됨. 회고리팩토링을 통해 코드 가독성과 유지보수성이 눈에 띄게 향상되었습니다. 특히 객체지향 원칙(SRP, DIP)을 철저히 적용한 결과, 각 객체가 단 하나의 명확한 책임을 가지면서, 향후 변경이 발생해도 최소한의 수정만으로 대응 가능해졌습니다. 규칙의 절대성이 아닌 "적정선"을 찾는 것이 매우 중요하다는 점을 깊이 깨달았습니다.🔗 리팩토링 미션 깃허브 링크회고스스로 칭찬하고 싶은 점학습한 내용을 즉시 코드에 적용하며 원칙을 체화하고자 노력한 점단순한 기능적 개선을 넘어, 구조적이고 근본적인 리팩토링을 고민한 점아쉬웠던 점 & 보완할 점코드 리뷰에서 코드 확장성과 유지보수성을 높이기 위한 설계가 부족했음을 느꼈습니다. 다음에는 코드 확장성과 유지보수성을 고려한 구조를 더욱 더 고민해 보겠습니다.지나치게 추상화에 치우치는 경향을 느꼈고, 다음에는 더 실용적인 균형을 유지하려 합니다.다음 주 학습 목표다음 주부터는 TDD 기반으로 더욱 실질적이고 구체적인 테스트 코드 작성을 연습할 계획입니다.리팩토링과 테스트가 결합된 실습을 통해, 코드를 더욱 안정적으로 관리하는 방법을 익힐 예정입니다.이번 주 스터디를 통해 "코드는 언제나 적정한 수준의 추상과 구체 사이에서 균형을 잡아야 한다"는 중요한 원칙을 다시 한번 실감했습니다. 앞으로도 적절한 추상화와 구체적인 구현 사이의 "적정선"을 끊임없이 고민하며, 클린하고 안정적인 코드를 지속적으로 작성하는 개발자가 되도록 노력하겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
・
3기
2025. 03. 09.
0
인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 1주차 발자국
Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드강의와 함께한 인프런 워밍업 클럽 스터디 3기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)1주차 발자국 입니다.학습 내용 요약이번 주 동안 [Readable Code: 읽기 좋은 코드를 작성하는 사고법] 강의를 통해 크게 두 가지 주제를 학습했습니다.1⃣ 추상과 구체추상(abstract)은 코드나 로직에서 세부 구현을 생략하고 핵심 의미만 전달하는 방식입니다.구체(concrete)는 코드나 로직의 세부적이고 구체적인 구현을 모두 드러내는 표현 방식입니다.이 개념을 코드에 적용하면 다음과 같은 장점이 있습니다.추상화된 이름 짓기와 메서드 분리를 통해 복잡한 코드를 단순화할 수 있습니다.협업 시 팀원에게 필요한 수준의 정보를 전달할 수 있어 효율적입니다.코드 유지보수가 용이해지고 가독성이 높아집니다.2⃣ 논리와 사고의 흐름, SOLID 원칙📌 논리, 사고의 흐름Early Return: 불필요한 else를 제거하고 조건이 맞지 않을 때 즉시 return해 코드 흐름을 명료하게 유지하는 방법입니다.사고의 Depth 줄이기: 중첩 분기문과 반복문의 깊이를 줄이고, 변수를 필요한 곳 가까이에 선언해 인지적 부담을 낮춥니다.부정어 최소화: 긍정적이고 직관적인 표현으로 메서드 이름을 짓고, 부정 연산자(!) 사용을 줄입니다.📌 SOLID 원칙SRP(단일 책임 원칙): 클래스가 하나의 명확한 책임만 가지도록 설계해야 합니다.OCP(개방-폐쇄 원칙): 기존 코드를 수정하지 않고 확장할 수 있도록 설계합니다.LSP(리스코프 치환 원칙): 자식 클래스가 부모 클래스를 대체해도 프로그램이 정상 작동하도록 설계합니다.ISP(인터페이스 분리 원칙): 사용하지 않는 메서드를 구현하도록 강요받지 않도록 인터페이스를 작게 분리합니다.DIP(의존성 역전 원칙): 고수준 모듈과 저수준 모듈 모두 추상화된 인터페이스에 의존하여 유연성을 높입니다.미션 해결 과정 & 회고Day 2 미션: 추상과 구체 예시미션추상과 구체 강의를 듣고, 생각나는 추상과 구체의 예시 작성해결 과정일상적인 예시를 통해 추상과 구체 개념을 명확히 이해하려고 했습니다.회사 출근이나 배달 음식 주문 등 일상 사례를 통해, 간략히 표현된 추상과 세부적인 표현을 비교하여 정리했습니다.관점 및 이유개념을 일상적이고 친숙한 예시로 접근함으로써 이해를 높이고자 했습니다.추상과 구체의 차이를 분명히 보여줄 수 있는 사례를 선택했습니다.회고일상적 예시 덕분에 개념 이해가 훨씬 쉬워졌고, 추상화의 필요성과 구체적 표현의 중요성을 체감할 수 있었습니다. 앞으로 코드 작성 시 이 관점을 계속 유지할 생각입니다.🔗 Day 2 미션 블로그 링크 Day 4 미션 1: 코드 리팩토링미션중첩 if-else 구조의 주문 검증 메서드 리팩토링접근 관점코드의 인지적 복잡성을 줄이기 위해 Early Return 방식을 사용했습니다.각 조건이 독립적이며, 직관적으로 위에서 아래로 읽히도록 조건 검사를 순서대로 배치했습니다.추가로 주문(Order) 객체가 자기 책임을 다하는 방식으로 책임을 위임하는 방법도 고려했습니다.해결 방법public boolean validateOrder(Order order) { // 주문 항목 유무 if (order.getItems() == null || order.getItems().isEmpty()) { log.info("주문 항목이 없습니다."); return false; } // 총 가격 검증 if (order.getTotalPrice() 회고Early Return을 적용한 결과, 가독성이 확연히 높아졌고 코드를 읽는 과정이 더 명확해졌습니다. 앞으로도 복잡한 조건 로직을 만날 때 적극적으로 이 방법을 적용할 생각입니다. Day 4 미션 2: SOLID 원칙 정리미션SOLID 원칙을 자신만의 언어로 정리해결 과정각 원칙을 "왜 이런 원칙이 필요한지" 스스로에게 질문하며 이해했습니다.실제 코드 예시를 작성하여, SOLID 원칙을 위반한 코드와 준수한 코드를 비교하면서 학습했습니다.회고원칙을 "내 언어"로 설명하면서, 이전에 막연히 알고 있던 개념들이 훨씬 더 명확하게 다가왔습니다. 단순히 원칙을 외우는 것과 달리, 직접 코드를 작성하고 리팩토링 과정을 거치며 더 깊이 이해할 수 있었습니다.🔗 Day 4 미션 블로그 링크회고이번 주 스터디를 통해 코드를 작성할 때 "추상과 구체의 적정선을 찾는 것이 중요하다"는 점을 깊이 깨달았습니다. 특히 코드가 너무 구체적이면 가독성이 떨어지고, 너무 추상적이면 구체적인 동작이 모호해진다는 점을 직접 실습과 미션을 통해 경험할 수 있었습니다. 또한 Early Return이나 중첩 최소화 등 작은 코드 개선만으로도 가독성이 크게 높아지는 것을 체감할 수 있어 유익했습니다.스스로 칭찬하고 싶은 점미션을 통해 배운 내용을 바로 적용하여 리팩토링하면서 개념을 내 것으로 만들려고 노력한 점이 뿌듯했습니다.SOLID 원칙을 단순히 암기하는 것이 아니라, 스스로 이해하고 예시 코드까지 작성하면서 명확히 내 언어로 표현한 것이 좋았습니다.아쉬웠던 점 & 보완할 점가끔 너무 추상화에 집중하다 보니 코드가 실제 구현에서 너무 멀어지는 경우가 있어, 구체성과 추상성을 더 균형 있게 다루는 연습이 필요하다고 느꼈습니다.SOLID 원칙을 실제 프로젝트 코드에 적용해 보지 못한 점은 조금 아쉬웠습니다. 앞으로 개인 프로젝트에 적용하여 체득할 계획입니다.다음 주 학습 목표다음 주는 강의의 다음 섹션인 "리팩토링" 부분을 학습할 예정입니다. 특히 리팩토링 실습을 통해 SOLID 원칙을 실제 코드에 적용하고, 객체 지향적 사고방식을 내 것으로 만드는 것을 목표로 삼고 있습니다.소감이번 주 강의를 통해 코드 가독성이라는 주제가 얼마나 중요하고 광범위한지 실감했습니다. 앞으로도 이번 학습 내용을 프로젝트에 적극 적용하며, 읽기 좋고 유지보수하기 좋은 코드를 꾸준히 작성하는 습관을 들이겠습니다.감사합니다!
백엔드
・
인프런
・
워밍업클럽
・
스터디
・
백엔드
・
클린
・
테스트
・
코드
・
발자국
・
회고
2024. 11. 07.
1
인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 후기
입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기강의와 함께한 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 후기 입니다. 소개인프런 워밍업 클럽 스터디 2기를 마무리하며, 한 달 동안 진행된 백엔드 프로젝트와 함께한 학습 여정을 회고해보고자 합니다. 이번 스터디는 Kotlin과 Spring Boot를 활용해 실습 중심의 백엔드 개발 프로젝트를 진행하면서, 많은 성장을 경험할 수 있었습니다. 매주 주어진 미션을 해결하고 강의를 수강하며 그 과정에서 직면했던 도전과 성공, 그리고 배움을 함께 나누겠습니다. 1주차: 웹 개발의 기본과 레이어드 아키텍처의 이해1주차는 Spring Boot를 기반으로 한 웹 개발의 기본 개념을 익히고, MVC 패턴과 레이어드 아키텍처를 학습하는 데 집중했습니다. 웹 서비스의 구조와 클라이언트, 서버, 데이터베이스 간의 상호작용을 이해하며, 클라이언트와 서버 간의 데이터 흐름을 REST API의 개념으로 체계화할 수 있었습니다. MVC 패턴의 중요성과 레이어드 아키텍처의 역할을 명확히 이해하면서, 어떻게 하면 유지보수성이 높은 코드를 작성할 수 있을지 깊이 고민하는 시간이 되었습니다.개인적으로 가장 보람찼던 부분은 이론 학습 후 즉시 실습을 통해 프로젝트에 적용해보면서, 개념을 구체화하고 몸에 익힐 수 있었던 점입니다. 특히 강의 내용을 프로젝트에서 직접 구현해보며, 이론과 실제가 어떻게 연결되는지를 깨닫는 과정이 매우 유익했습니다. 2주차: JPA와 API 설계의 시작2주차에는 JPA를 활용하여 데이터베이스와 상호작용하는 방법을 학습하고, CRUD 기능을 직접 구현해보며 JPA의 기본 개념에 익숙해질 수 있었습니다. 실습 프로젝트를 통해 엔티티 간의 관계 설정과 데이터베이스 초기화를 진행하면서 JPA의 다양한 기능을 경험하였고, 강의를 통해 학습한 내용을 실습에 적용하며 개발자로서의 자신감을 쌓을 수 있었습니다.또한, 이번 주에는 REST API를 설계하고 구현하는 미션을 수행했습니다. 사용자와 게시글, 댓글을 관리하는 여러 API를 설계하면서, RESTful한 접근 방식을 유지하기 위해 고민했습니다. API 설계 시 직관적이고 간결한 엔드포인트를 유지하려고 노력한 덕분에, 이후 테스트 코드 작성 및 검증 과정에서도 큰 어려움 없이 진행할 수 있었습니다. 3주차: 컨트롤러 개발과 API 테스트 코드 작성3주차에는 Spring Boot와 JPA를 사용하여 컨트롤러를 개발하고, 다양한 엔드포인트를 구현했습니다. 이와 함께 Thymeleaf를 활용해 프론트엔드 작업을 진행하며, 백엔드와 프론트엔드의 유기적인 연결을 실습해 볼 수 있었습니다.특히 이번 주에는 테스트 코드 작성의 중요성을 다시 한번 실감하게 되었습니다. API의 동작을 검증하는 테스트 코드를 작성하면서, 예상하지 못했던 예외 상황을 처리하고 기능을 보완하는 과정을 통해 코드의 완성도를 높일 수 있었습니다. Thymeleaf를 사용해 프론트엔드를 구성하는 데 있어 부족함이 있었지만, 이를 보완해가며 더 나은 결과물을 만들 수 있었던 점이 인상 깊었습니다. 4주차: 프론트엔드 템플릿 작업과 배포 경험마지막 4주차에는 Thymeleaf의 fragment 기능을 사용하여 HTML 구조를 모듈화하는 작업을 진행했습니다. 프론트엔드를 구성하며 공통 레이아웃을 재사용 가능한 형태로 분리해 유지보수성을 높였고, Docker와 Nginx를 활용해 배포 작업까지 경험할 수 있었습니다. 직접 Docker 이미지를 빌드하고, MySQL 컨테이너와 연동하여 실제 서비스를 배포하는 경험은 매우 실질적이었고, 개발자로서 한 단계 더 성장할 수 있는 계기가 되었습니다.이번 스터디를 통해 가장 크게 느낀 점은 개발 과정에서 이론과 실습의 균형이 얼마나 중요한지였습니다. 각 주차마다 학습한 내용을 프로젝트에 직접 적용해보고, 그 과정에서 발생한 문제들을 해결하면서 실력을 쌓을 수 있었습니다. 또한, API 설계와 테스트 코드 작성, 그리고 실제 배포까지 전 과정을 경험하면서 백엔드 개발의 흐름을 체계적으로 이해할 수 있었습니다.이번 스터디에서 성실히 참여한 결과, 우수러너로 선정되는 영광도 누릴 수 있었습니다. 앞으로도 이번 스터디에서 얻은 배움을 바탕으로 꾸준히 학습하고 성장해 나가고자 합니다. 인프런 워밍업 클럽 스터디를 통해 함께한 모든 분들께 감사드리며, 이후에도 지속적으로 배움을 나누고 함께 성장해 나가기를 희망합니다.
백엔드
・
인프런
・
인프런워밍업클럽
・
스터디2기
・
워밍업
・
백엔드
・
프로젝트
・
후기