블로그
전체 112025. 06. 15.
0
[워밍업 클럽 4기 - 백엔드] Day 18 미션
미션@Mock, @MockBean, @Spy, @SpyBean, @InjectMocks 정리@Mock 순수 자바 환경의 테스트 단위에서도 사용할 수 있다.가짜 객체를 생성한다. 실제 로직은 없다.행위에 대한 기대를 명세하고 그에 따라 동작하도록 한다.보통 테스트 대상 클래스의 의존 객체를 대체할 때 사용한다. @MockBean스프링 부트 환경 같이 Bean을 사용하는 통합 테스트에서 사용할 수 있다.스프링의 올라가있는 실제 Bean을 Mock 객체로 교환한다. @Spy순수 자바 환경의 테스트 단위에서도 사용할 수 있다.stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체다.일부는 실제 객체처럼 동작시키고, 다른 일부만 stubbing할 수 있다. 즉, 선택적 구현과 mocking을 섞어 사용할 수 있다. @SpyBean스프링 부트 환경 같이 Bean을 사용하는 통합 테스트에서 사용할 수 있다.기존 Bean을 Spy 객체로 감싸 실제 로직을 유지하면서 특정 메서드만 moking 한다. @InjectionMock순수 자바 환경의 테스트 단위에서도 사용할 수 있다.mock 객체를 자동으로 주입해준다.테스트 대상 객체에 mock된 의존성 객체들을 자동으로 주입한다. 즉, 스프링의 도움을 받지 않고 의존성을 주입할 때 사용한다. 테스트 코드 리팩토링@BeforeEach void setUp() { 1-1. 댓글 레포지토리.deleteAllInBatch(); 1-2. 게시글 레포지토리.deleteAllInBatch(); 1-3. 사용자 레포지토리.delteAllInBatch(); } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 1-1. 사용자 생성 - createUser() 호출 1-2. 게시물 생성 - createPost() 호출 // when 1-3. 댓글 생성 - createReply() 호출 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 2-1. 사용자 생성 - createUser() 호출 2-2. 게시물 생성 - createPost() 호출 2-3. 댓글 생성 - createReply() 호출 // when 2-4. 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 3-1. 사용자1 생성 - createUser() 호출 3-2. 게시물1 생성 - createPost() 호출 3-3. 댓글1 생성 - createReply() 호출 3-4. 사용자2 생성 - createUser() 호출 // when 3-5. 사용자2가 사용자1의 댓글 수정 시도 // then 검증 } private User createUser() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 } private Post createPost() { 1-1. 게시물 생성에 필요한 내용 준비 1-2. 게시물 생성 } private Reply createReply() { 1-1. 댓글 생성에 필요한 내용 준비 1-2. 댓글 생성 } 한마디강의에서 배웠던 내용을 잘 적용해볼 수 있었던 미션같아서 한 번 더 정리하는 느낌이라 좋았다. 마지막 미션까지 달릴 수 있어서 뿌듯함 100배 출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
박우빈
・
백엔드
・
테스트코드
2025. 06. 15.
0
[워밍업 클럽 4기 - 백엔드] Day 16 미션
미션MVC 기반에서 가장 많이 사용되는 3티어 레이어별로 특징과 테스트 방법을 정리해보자! Layered Architecture 일반적으로 controller(presentation) -> service(business) -> repository(persistence) 구조로 이루어져있다.레이어를 단계적으로 구분하는 이유는 관심사 분리가 목적이다. persistence LayerDB와 직접적인 소통을 하는 계층이며 CRUD 쿼리 작업을 처리한다.Business 계층에서 넘어온 데이터를 처리한다.주로 JPA와 QueryDSL을 사용한다. 테스트 방법클래스 레벨에서 @SpringBootTest, @DataJpaTest 어노테이션을 사용할 수 있다.@SpringBootTest를 사용하는 것을 더 추천한다(다른 테스트와의 환경 통합을 위해)작성한 쿼리가 의도대로 동작하는지 테스트한다. business Layer비즈니스 로직이 메인이다.presentation에서 데이터를 받으며 중요 비스니스 로직을 수행하고 persistence 계층으로 전달하는 중간다리 역할을 한다. 테스트 방법클래스 레벨에 @SpringBootTest를 사용하거나 Mock을 사용하는 경우 @ExtendWith(MockitoExtension.class)를 사용한다. 이 역시 환경의 통합을 위해 되도록이면 SpringBootTest를 사용하는 것이 좋다. 스트 객체를 생성할 때 관련 테스트 메서드에는 꼭 필요한 데이터만 파라미터로 넘겨받아 빌더 패턴을 활용한다.단위 테스트를 작성할 수도, persistence 계층와 통합해서 테스트할 수도 있다. presentation Layer외부 세계를 제일 처음 맞이하는 계층이다. 또한 외부 세계에 응답 데이터를 전달해주는 계층이다. 테스트 방법사용자 요청의 데이터에 최소한의 검증을 해야한다. (validation) 다만, 타입의 유효성 자체에 대한 검증이 아닌 도메인과 관련된 검증들은 presentation 계층이 아닌 더 내부에서 진행한다.클래스 레벨에 @WebMvcTest(className.class)를 사용한다.mock을 사용한다.직렬화, 역직렬화를 위해 ObjectMapper 객체가 필요하다. 출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
박우빈
・
테스트코드
・
MVC
2025. 06. 15.
1
[워밍업 클럽 4기 - 백엔드] 3주차 발자국
3주차 회고[Day 11] 미션Day 11일에는 테스트 코드를 직접 작성해보는 미션을 진행했다. Readable 강의에서 지뢰찾기와 스터디 카페에 대한 테스트 코드를 3개 이상의 서로 다른 클래스 & 총 7개 이상의 테스트를 작성하는 미션이었다. 객체지향으로 리팩토링 했었던 지난 미션보다는 시간이 적게 걸렸지만, 이번 미션도 여전히 생각할 거리가 너무 많았다.고민을 많이 했던 부분은 전체적으로 테스트를 작성할 수 있는 시간이 물리적으로 부족했기 때문에 선택과 집중을 해서 정말 테스트가 필요할만한 부분에 적용해보고 싶어 어떤 클래스를 선정할지 생각을 많이했다.또, 단위 테스트 위주로 할지 7개만 작성하니까 통합 테스트 격으로 할지도 많이 고민했는데 테스트를 작성하는 실력이 그리 뛰어나진 않아 처음부터 천천히 하기로해서 단위 테스트 위주로 짰던거 같다.이번 미션도 역시 재밌었다! [Day 12] Persistence Layer이번 챕터에서는 전체적인 레이어드 아키텍처 Presentation, Business, Persistence Layer들에 대한 간략한 설명과 통합 테스트의 정의, 그리고 앞으로 개발할 프로젝트의 설계에 대해 배웠다. 강의를 들으면서 느낀 건 강의가 정말 초보자 위주로 짜여져 있다는 것이다(positive). Library와 Framework의 차이점, spring의 대표적인 기술, ORM 등에 대한 설명들을 테스트 강의에서 들을 줄은 몰랐는데 초보자는 아무래도 반복학습이 중요하다보니 이런 기초적인 것들을 하나씩 짚어주셔서 개념을 다시 바로 한 번 더 잡기에 좋았다. 제일 기억에 남는 건 테스트를 작성할 때 클래스 레벨에 @SpringBootTest와 @DataJpaTest 어노테이션이다.SpringBootTest스프링 부트 컨텍스트를 로드한다.웹 환경, 서비스, 레포지토리, 컴포넌트 등 모든 Bean을 실제처럼 사용할 수 있다.DataJpaTestSpringBootTest에서 JPA 관련 컴포넌트만 로드 되게끔 경량화슬라이스 테스트로 빠르게 JPA 레이어만 검증할 때 사용된다.@Service나 @Controller등의 Bean이 로드되지 않는다. [Day 13-14] Business Layer이번 섹션에서는 비즈니스 로직이 메인인 Business Layer에 대해 알아보고, 코드를 구현했다.비즈니스 레이어는 비즈니스 로직을 구현하는 레이어고, Persistence와의 상호작용을 통해 비즈니스 로직을 전개시키며, 트랜잭션을 보장(원자성)해야 한다. 이번 코드 구현에서는 고객이 주문을 할 수 있는 Order, Stock의 전반적인 부분을 개발했다. 코드를 구현하면서 드는 의문점은 여태 다른 강의를 들을 때 상품의 재고 관련한 데이터는 Product에 필드로 관리했는데 이 강의에서는 별도의 엔티티로 따로 빼준게 의아했다. 혼자 생각해봤는데 Stock의 데이터는 최대 품절, 아니면 재고 현황(숫자)만 보여주면 될 것 같았기 때문이다. 그래서 질문을 냅다 올리려다 다른 사람이 비슷한 질문을 한게 있나 찾아봤는데 있어서 읽어봤더니 아하!하게 되었다.관련 질문 : stock을 entity로 분류하는 이유 [Day 15] Presentation Layer(1)이번 섹션에서는 마지막 레이어인 Presentation layer를 구현하고 테스트했다. Presentation Layer는 외부 세계의 요청을 가장 먼저 받아들이는 계층이며, 받은 파라미터에 대해 최소한의 검증을 수행해야한다. 추가적인 요구사항에 대한 테스트를 작성하고 개발을 하면서 @Transaction에 readOnly란 속성에 대해 배웠다.대부분의 서비스는 CRUD 중에 읽기인 'R' 비중이 매우 높다. 그래서 Read와 CUD(command) DB를 슬레이브(R) / 마스터(CUD)로 분리해서 장애를 격리하는 방식을 추천한다고 하셨다.코드 상에서는 일단 클래스 레벨에 무조건 @Transactional을 주는게 아니라 전체적으로 readOnly = true 속성을 먹게끔 작성하고, CUD 작업이 있는 메서드에만 따로 @Transactional을 붙여주는게 성능 상으로도 더 좋다고 말씀해주셨다.@Transactional(readOnly = true) public class service { @Transaction public void save {} } 3주차 회고초보자 입장에서는 TDD로 테스트부터 개발까지 모든 과정을 함께 참여할 수 있어서 나에게 더 좋은 경험으로 다가온 강의였다. 시간은 좀 길었지만 그만큼 얻는 것도 많아 아주 유익했다. 다음에는 강의 없이 혼자서 간단한 프로젝트를 TDD 기반으로 만들어보고싶다! 이번주도 열공😄 출처인프런_워밍업_클럽_4기_BEPractical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
박우빈
・
테스트
2025. 06. 08.
2
[워밍업 클럽 4기 - 백엔드] 2주차 발자국
2주차 회고[Day 6] 리팩토링: 코드 다듬기이번 챕터에서 제일 기억에 남는 건 '주석'이었다. 주석 사용 법은 배웠지만 좋은 주석은 처음 알게된 개념이라 도움이 많이 됐다. 실무에서도 개발하기 급급해서 기존에 있던 주석까지 신경쓰지 않았었는데 주석과 코드는 한 몸이라 생각하고 앞으로 꼼꼼히 봐야겠다고 생각했다.그리고 키워드 정리 시간에 지뢰찾기에 앞으로 추가해볼 수 있는 기능들을 몇몇개 말해주셨는데 내 힘으로 기능들을 추가해보는 시간을 가져도 좋을 것 같다. [Day 7] 클린코드 리팩토링 실습(미션)이번 챕터는 온전히 미션을 위한 날이었다. studycafe 패키지에 작성된 코드를 읽어보고, 추상화 해보는 미션을 진행했다.고작 조각 코드인 몇 줄 짜리가 아니라 더 어렵긴 했지만 더 생각할 수 있었던 것들이 많아서 좋았다.코드를 다 읽어보고 난 후에 여태 배운 추상화 기법들이 머릿속을 스쳐지나갔다. 이 부분에서 뭘 하면 좋을 것 같은데?! 정도의 생각이 들었다. 다만, 구현으로 실천하기가 매우 어려웠다. 생각과 구현 능력이 절실히 필요하다고 생각했다. 시간 상의 문제로 완벽히 추상화를 하진 못했지만 그래도 백지에서 추상화를 진행해본적이 처음이라 어렵지만 재밌었던 시간이었다. 추후 중간 점검 때 코드 리뷰에서 정말 많은 것을 얻었다. 각자의 실력도 다르지만 각자의 생각도 다르고, 관점도 달랐다. 그래서 배운 게 두 배, 아니 세 배 더 많아진 것 같다! [Day 8] 리팩토링 연습 | 기억하면 좋은 조언들studyCafe를 처음부터 끝까지 우빈님과 함께 진행했다. 순전히 내 뇌로는 닿을 수 없는 부분까지 닿을 수 있었던 거 같았다. 이래서 '나를 이끌어 줄 상사가 중요하구나'를 느꼈다.추상화도 어렵지만 오버 리팩토링이 너무 어려운 것 같다. 오버 리팩토링이 되는 시점을 정확히 캐치할 수 없다보니 그냥 둥둥 뇌 속에 떠도는 구름 같은 개념을 보는 것 같다. 우빈님이 말씀하신대로 오버 리팩토링은 경험에 의해서만 얻을 수 있다고하니 추상화를 많이 도전해보는 것이 중요하다고 느꼈다.실무에서도 추상화를 많이 접할 수 있으면 더 좋았을 것 같은데 그러지 못한 환경에 조금 갈증을 느끼며 오늘도 이직 단단히 다짐한다. Readable Code : 읽기 좋은 코드를 작성하는 사고법 강의가 막을 내렸다. 혼자서 도전했으면 어려움에 어지럼증을 느껴 끝까지 완주하지 못했을 것 같은 강의었다. 워밍업 클럽 덕분에 포기하지 않고 한 강의를 완강할 수 있어서 참으로 감사함을 느낀다. [Day 9] 단위 테스트 | TDD | 테스트는 []다새로운 Practical Testing 테스트 강의를 시작했다!이번 챕터에서는 기본적인 테스트에 대해서 알게 되었고, TDD, BDD에 대해 얘기 나누었다. TDD는 말로만 들어봤었는데 강의에서 TDD 방식으로 코드를 짜나간다고 하시니 약간 생각지도 못한 이득 본 것 같아 엄지를 들었다. 강의를 계속 들으면서 느끼는 점은 코딩 실력도 중요하지만 언어 능력이 중요하다는 걸 엄청나게 많이 깨닫는다. 코딩은 사람이 컴퓨터에게 하는 언어라 컴퓨터가 못 알아 들으면 에러도 내주고 테스트 통과도 시켜주는데 사람이 하는 언어는 내가 뱉은 말을 정말 다양하게 해석할 수 있는 여지가 있어 정확하고 확실한 언어로 내 생각과 코드를 전달해야 정말 실력 있는 개발자인 것 같다.앞으로 그런 개발자가 되도록 노력하자! 출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드
백엔드
・
워밍업클럽
・
백엔드
・
박우빈
・
클린코드
・
테스트
2025. 06. 04.
0
[워밍업 클럽 4기 - 백엔드] Day 7 미션
미션[섹션 7. 리팩토링 연습]의 "연습 프로젝트 소개" 강의를 보고, '스터디 카페 이용권 선택 시스템' 프로젝트에서 지금까지 배운 내용을 기반으로 리팩토링을 진행해 봅시다. 브랜치 링크 :https://github.com/techhan/readable-code/tree/day7 나름 해보다가 진도가 더 안나가서 제출합니다!생각은 나름 해봤는데 객체 지향으로 구현하는 능력이 조금 부족하네요!앞으로 구현 부분을 신경써서 들어야할 것 같습니다! 출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
워밍업클럽
・
backend
・
박우빈
・
클린코드
・
객체지향
2025. 06. 01.
2
[워밍업 클럽 4기 - 백엔드] 1주차 발자국
1주차 회고[Day 2] 추상과 구체이번 챕터에서 가장 와 닿았던 키워드는 '읽기'다. 사실 코딩을 하면서 '읽기 좋은 코드'를 작성하기 보다는 별 생각 없이 '원하는 요구사항에 맞춰 개발하면 됐지'라는 생각이 커서 내 코드를 읽을 후손들은 생각지도 못한 채 개발을 했었다. 그렇다보니 강의를 듣는 내내 처음 해보는 생각들이라 시간이 많이 더뎌졌다.추상화 레벨 파트를 들으면서 메서드를 이렇게 잘게 쪼개본 적이 없어서 많이 당황스러웠다. 여태 라인 수에 따라 돈을 내는 것도 아닌데 전체적인 라인 수를 줄이려고 노력했지 메서드를 잘게 쪼개는 형식으로 개발한 건 처음이었다.이론적인 공부도 중요하지만, 코드를 가독성 좋게 잘 짜는 사람의 코드를 경험하고, 이해하고 작성해보는 시간이 개발자에게 엄청난 경험을 안겨주는 것을 실감했다. [Day 2] 미션미션 링크처음 만난 미션은 어려웠다(1번 제외). 추상과 구체를 스스로 생각해본 적이 없었기 때문이다. 흔히 아는 붕어빵, 자동차, 동물 이런 예시들을 듣고, 적용하기만 해봤지 추상과 구체를 자세하게, 특히 레벨 별로 나눠서 생각해 본 적은 정말 없어서 꽤 오랫동안 생각에 잠겼다.내용을 적어 내려가면서 '이게 맞나?'싶었다. 왜냐면 내가 나름의 추상 레벨로 구분 해 놓은 것들이 사실 파고들면 파고들 수록 더 깊게 구체화 시킬 수 있었기 때문이다. 어떻게 나눠야 적정 레벨로 나눌 수 있는지가 참 애매했던 것 같다. 정답은 없는 것 같다고 결론 내렸다. 다만, 이 능력을 키워갈 수는 있을 것 같다. 처음 시작이 어렵지 앞으로 추상과 구체를 레벨 별로 잘 나누어보는 연습을 해야겠다. [Day 3] 논리 사고의 흐름 | 객체 지향 패러다임나에게는 이 챕터가 제일 곤욕이었다. 재직자라 수업을 들을 수 있는 시간은 한정적인데다 다음 날에 미션까지 있어 마음이 너무 조급했다. 퇴근하고 듣는 수업은 눈이 감기기 일쑤였다. 그래서 복습이 매우매우 필요한 챕터라고 생각한다.그래도 기억 나는 것들을 적어보자면 '평소에 내가 생각하지 못했던 것들'이었다. 나는 대단한 개발자가 아니다. 그래서 스스로 'Early return'이나 'depth 줄이기' 등과 같은 내용을 생각하지 못한다. 이번 챕터에서 가장 많이 배웠다고 해도 무리가 없다.객체 지향 패러다임도 많이 배웠다. 단순히 이론만이 아닌 리팩토링하는 실습까지 같이해서 많이 배운 것 같다. 물론 혼자 스스로 적용해보기엔 아직 무리지만. 객체 지향 패러다임에서는 'getter'와 'setter'를 자제하란 내용이 신기했다. Setter의 지양은 너무 많이 알려져있는 내용이라 그러려니 했지만 Getter의 경우는 처음 듣는 얘기었다. Entity를 생성할 때 Lombok으로 @Getter를 만들고 보는 내 입장에선 배울 게 정말 많았다.이번 챕터는 배워서 바로 쓰기에는 조금 어려운 내용이지 않았나 싶다. 앞으로 배운 내용들을 복습하고, 새로운 곳에 적용해보는 연습이 필요할 것 같다. [Day 4] SOLID그 유명한 SOLID에 대해 공부했다. 내용들은 워낙 유명하니 굳이 적진 않겠다. SOLID는 유명하지만 코드에 적용하기는 매우 어려운 내용이다. 각종 블로그에서 보이는 짤막한 코드 조각을 보면 이걸 어떻게 실무에 적용할지 약간 까마득해진다. 우빈님 강의에서는 짧지 않는 코드를 SOLID의 각 내용에 맞춰 점진적으로 리팩토링하니까 더 이해가 잘 됐던 것 같다. 물론 초보가 따라가기엔 어려웠지만 내가 그리 갈망하던 초급 -> 중급으로 넘어갈 수 있는 실력을 좀 갈고 닦은 느낌이 들었다. [Day 4] 미션미션 링크이번 미션도 시간이 오래 걸렸다. 실제로 리팩토링하는 부분이 있어서 많은 고민을 했던 것 같다. 솔직히 다른 분들의 코드를 참고하기도 ㅎ 했다. 뭐.. 그러면서 실력이 느는 것 아닐까?!(뻔뻔) 그래도 양심적으로 조그만 부분만 참고했다. 제시된 메서드만 간단하게 리팩토링해보기엔 내 실력을 믿고 검증할 수가 없어서 코드가 돌아가게끔 최소한으로 class들을 만들고 실행시켜봤더니 마음이 한결 편해졌다.어려웠긴 했지만 코딩을 직접적으로 하는 미션이 너무 재밌어서 시간 가는 줄 모르고 했다.이런 미션이 또 나왔으면 좋겠다. [Day 5] 객체 지향 적용하기나는 '상속' 밖에 몰랐고, '상속'이 객체 지향의 꽃인 줄 알았다. 근데 실무에서는 잘 쓰지 않는다니! 또 충격을 받았다. 충격을 5일째 받으니 머리가 어질어질.. 요즘엔 '상속'보다는 '조합'을 더 많이 쓴다고 한다. 어쩐지 내가 '상속'으로 개발했을 때 조금이라도 변경이 생기면 머리가 아프더라..! 이런 가려운 부분을 잘 긁어주셔서 참으로 감사했다. 종합적인 회고그저 배운게 많았던 한 주였다. 복습이 필요함을 느꼈고, 내가 많이 부족하다는 것도 느꼈다. 이 강의를 통해 좀 더 나은 개발자가 되길 소망한다. 화이팅! 출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
워밍업클럽
・
backend
・
박우빈
2025. 05. 30.
1
[워밍업 클럽 4기 - 백엔드] Day 4 미션
미션아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.[as-is]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; } [to-be]public class Day4 { public boolean validateOrder(Order order) { try { isOrderItemsEmpty(order); isCustomerInfoEmpty(order); isTotalPricePositive(order); System.out.println("유효성 검사를 통과했습니다."); return true; } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); return false; } } private static void isCustomerInfoEmpty(Order order) { if(!order.hasCustomerInfo()) { throw new IllegalArgumentException("사용자 정보가 없습니다."); } } private static void isTotalPricePositive(Order order) { if(order.getTotalPrice() orderItem = new ArrayList(); private Customer customer; public void setItem(Item item) { orderItem.add(item); } public void setCustomer(Customer customer) { this.customer = customer; } public List getItems() { return orderItem; } public int getTotalPrice() { int totalPrice = 0; for(Item item : orderItem) { totalPrice += item.getPrice(); } return totalPrice; } public boolean hasCustomerInfo() { return customer != null; } } class Customer { private String id; public Customer(String id) { this.id = id; } } class Item { private final int price; public Item(int price) { this.price = price; } public int getPrice() { return price; } }수업에서 배운 내용들을 차분히 적용하도록 노력했다. 다음과 같은 순서로 리팩토링을 진행했다.메인인 validateOrder() 메서드 외엔 추상화를 적용하지 못했다. (시간이 없..어서) Early return복잡하게 얽혀있던 if-else문을 뜯어내 if문을 최소화했다. 메서드 추상화메서드의 경우엔 이름이 정말 고민 많았는데, 일단 세 메서드 다 isXXX로 통일시켰다.예외처리예외 처리를 통해 유효성 검사가 통과되지 않았을 때의 로직을 통일했다. return false를 하나하나 써주지 않아도 됐다. 테스트 결과public void run() { System.out.println("======= 시작 ======="); Order order1 = new Order(); validateOrder(order1); // "주문 항목이 없습니다." Order order2 = new Order(); Item item = new Item(1000); order2.setItem(item); validateOrder(order2); // "사용자 정보가 없습니다." Order order3 = new Order(); Item item2 = new Item(-10); order3.setCustomer(new Customer("id3")); order3.setItem(item2); validateOrder(order3);// "올바르지 않은 총 가격입니다." Order order4 = new Order(); Item item4 = new Item(1000); order4.setCustomer(new Customer("id4")); order4.setItem(item4); validateOrder(order4); // "유효성 검사를 통과했습니다." } SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP : Single Responsibility Principle / 단일 책임 원칙이상적인 사회 모습. 회사에서 한 사람 당 하나의 책임만 가졌으면 좋겠는 것 처럼, 객체는 단 하나의 책임만 가져야 한다. 하나의 객체가 여러 책임을 가질 경우 변경 시 파급력이 크다. OCP : Open-Closed Principle / 개방-폐쇄 원칙열린교회 닫힘. 새 신도는 환영하면서 문을 열어주지만 나갈때는 아니란다. 소프트웨어 요소는 확장에는 열려있어야 하고 변경에는 닫혀있어야 한다. 추가적인 요구사항에 유연하게 확장할 수 있어야한다. 기능 추가 시 최소한의 코드를 수정해야 함(물론 로직에 직접적인 영향을 주는 코드는 수정 X) LSP : Liskov Substitution Principle / 리스코프 치환 원칙자식은 부모의 거울이다! 자식 클래스는 부모 클래스의 기능을 정확하게 확장해야한다. ISP : Interface Segregation Principle / 인터페이스 분리 원칙객체와 똑같다. 하나의 인터페이스에 너무 많은 기능을 넣으면 원치않는 의존성이 생길 수 있다. DIP : Dependency Inversion Principle / 의존 역전 원칙구현체가 아닌 추상에 의존해야 한다. 구현체에 의존하게 되면 어떠한 인터페이스에 대한 구현체가 바뀔 때마다 다른 코드에 영향을 줄 수 있다 !! 결합도를 낮추고 유연한 구조를 만든다. 출처인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
클린코드
・
워밍업클럽
・
박우빈
2025. 05. 27.
1
[워밍업 클럽 4기 - 백엔드] Day 2 미션
미션강의에서 안내하는 것처럼, 프로젝트를 개인 계정으로 fork하고 강의를 수강해 주세요.github 주소 : https://github.com/techhan/readable-code.git 추상과 구체" 강의를 듣고, 생각나는 추상과 구체의 예시가 있다면 한번 3~5문장 정도로 적어봅시다.추상 : 택배를 받았다.구체 과정고객이 주문한다.판매자가 확인 후 택배 송장을 뽑는다.계약된 회사가 택배를 수거하러 방문한다.택배를 수거해서 한 번 이상의 상하차를 거친다.택배 배달원이 고객에게 택배를 전달한다. 짧은 회고두 번째 문제를 처음 맞닥뜨렸을 때는 좀 당황스러웠다. 추상과 구체. 프로그래밍을 배우면서 추상과 구체에 대해 들어본 적은 있었다. 단순히 '동물-강아지' 이런 수준. 들어만 봤지 내가 직접 생각해 본 적은 별로 없었고, 더군다나 3~5문장으로 적어 본 적은 더 없어서 꽤나 생각하는데 애를 먹었다. 그래도 한 번 생각의 물꼬가 터지니, 위에서 도출해 낸 구체 과정에서도 완벽한 구체가 아닌, 그 안에 또 구체, 또 구체, 또 구체...가 있는 걸 알게 됐다. 예를 들면 고객이 주문하는 과정에서도 '핸드폰 켜기 > 사이트 들어가기 > 장바구니에 담기 > 결제하기 > 완료하기' 등등의 구체들이 숨어 있다는 게 별 거 아닌 것 같지만 흥미로웠다. 추상과 구체는 기억에 매우 오래 남을 것 같다. 출처 인프런_워밍업_클럽_4기_BEReadable Code: 읽기 좋은 코드를 작성하는 사고법
백엔드
・
인프런
・
워밍업클럽4기백엔드
・
박우빈
・
ReadableCode
・
CleanCode
・
Backend
2024. 03. 10.
1
[인프런 워밍업 클럽_0기] 3주차, 세 번째 발자국 #3
3주차 요약 : 직장인은 집중하기 힘든 코스인 걸 느꼈다..!강의 요약은 notion에 정리해두었다. https://www.notion.so/d2e9b3e27b3348abbde60994cf627ebd Day 11. 객체지향과 JPA 연관관계기존 코드를 리팩토링하면서 객체지향적인 개발이 무엇인지 조금이나마 체험할 수 있었다.객체지향의 사실과 오해에서 나왔던 '객체의 메시지'를 드디어 체험하게 되었다.JPA 연관관계 및 연관관계의 주인이 되는 기준에 대해서 알게되었다.연관관계에 주인이 아닌 쪽에는 mappedBy 옵션을 줘야한다.개념은 언뜻 알고있었지만 연관관계의 주인을 정하는 기준을 이렇게 정확하게 알게된 건 처음이다.연관관계의 주인은 Table을 보았을 때 관계의 주도권을 쥐고있는 쪽이 연관 계의 주인이 된다.연관관계 주인에 의해서 객체가 연결되는 기준이 된다.연관관계의 주인 객체에서 연결되는 다른 객체의 setter를 만들어 저장하면 데이터 저장이 정상적으로 잘 된다....!연관관계를 setter로 연결 후 Transactional이 아직 끝나지 않은 시점에서 그 반대인 객체에서 주인 객체를 getter로 가져오면 null이 반환된다. -> DB 상에서는 아직 저장이 되지 않아 데이터가 없기 때문해당 문제를 해결하기 위해서는 Setter를 한 번에 둘 다 연결해주면 된다.cascade 옵션과 orphanRemoval 옵션에 대해 알게되었다.도메인 계층에 비즈니스 로직이 들어가도 된다는 걸 알게되었다. 뭔가 도메인 계층은 깨끗한 상태로 getter와 생성자 외에는 추가적인 메서드가 있으면 안될 것 같았는데 그러지 않아도 되는 것 같다.리팩토링 후 Service 계층에서 오만가지 도메인을 불러와 직접 처리해주었던걸 리팩토링 하면서 연관 관계를 사용해 최대한 도메인들끼리 직접 협력할 수 있게 코드를 변경한 진귀한 경험을 했다. -> 객체지향의 사실과 오해..! 짱지연로딩의 개념과 다양한 옵션들에 대해 알게되었다.연관관계 사용이 100% 정답이 아닌 걸 알게되었다.비즈니스 요구사항, 기술적인 요구사항, 도메인 아키텍처 등 여러 부분을 고민해서 연관관계 사용을 선택해야 함.실무에서 경험이 쌓여야 판단이 가능할 것으로 보인다. 과제는 따로 못했다..! 야근이 날 괴롭혀서 강의도 겨우 들었기 때문....ㅜDay12. 기본적인 배포를 위한 준비배포의 개념에 대해서 알게되었다.profile 기능을 사용해서 H2와 Mysql 관련 설정을 분리하였다.git과 github의 개념과 차이점을 알게되었다.git의 기초 사용법을 다시 한 번 리마인드하는 시간이 되었다.git은 사용해봐서 기초 사용법을 알고 있는 상태였다.AWS의 EC2를 난생 처음 사용해봤다. 과제는 따로 하지 못했다.. 이날도 아마 야근했던 거 같다... 😢Day13. AWS와 EC2 배포AWS에서 리눅스 명령어를 다뤄봤다.AWS에 콘솔에 접근하는 방법을 알게되었다.리눅스 명령어 중에 권한 관련 명령어가 좀 어려운 것 같다. 추가적인 공부가 필요한듯.배포를 위한 프로그램 설치는 Java, mysql, git이었는데 mysql을 설치할때 좀 곤욕이었다. 강의 영상과 현재 ec2 리눅스 버전이 달라서 그런건지 강의에 나오는 명령어를 입력하면 에러가 나서 블로그 이것저것을 검색해봤다. 난생 처음 CLI로 빌드를해봤다. 빌드할 때도 영상을 그대로 따라서 하면 에러가 발생했다. 영상에서는 java 11을 설치했는데 내 프로젝트는 java가 17이라 java를 다시 설치해주었더니 정상적으로 빌드가 됐다.foreground와 background의 개념을 알게되었다.난생 처음 도메인을 구입해봤다.Day14. Spring Boot 설정, 버전업 이해하기build.gradle에 대해서 알게되었다.dependency configuration에 대해서 알게되었다.Spring과 Spring boot의 역사에 대해 간략하게 알게되었다.YAML 문법에 대해 알게되었다.Day15. 마무리 및 추가 꿀팁 영상공부 방향성에 대한 얘기 중 spring의 원리 및 클린 코드를 공부해야겠다고 생각했다.코틀린에 대한 관심도가 올라갔다. 조만간 코틀린에 대해서도 공부할지도?스프링 배치가 궁금해서 관련 강의를 사놨었는데 이것도 얼른 수강해야겠다.Spring boot에서 Mybatis를 사용하는 법을 알게되었다. 우리 회사 같은 경우는 옛날 기술들을 많이 쓰고 최신 기술에 대한 거부감이 있는데 이런 걸 보여줌으로써 차근차근 최신 기술로 안내하는 것도 나쁘지 않을 것 같다Client-Side Rendering과 Server-Side Rendering의 개념을 알게되었다. 완강 후기자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]를 완강했다! 🥳🥳🥳🥳뭔가 강의 제목을 딱 봤을때 진짜 엄청난 기초의 강의겠구나 했는데 강의를 들으면 들을수록 어느정도 깊이가 있어 조금 놀랐다. 오히려 기초가 없는 사람은 약간 따라하기 버거울 수도 있다는 생각이 들었다.기존 코드를 객체지향적으로 리팩토리하는 부분에서 많은 인사이트를 얻어서 뭔가 최태현님의 다른 강의들까지 들으면 남들과 조금은 다른 개발자가 될 수 있지 않을까란 생각도 들었다. 아무래도 서적에서 공부하는 것과 실무가 약간 버무러진 코드와 깊이 있는 코드를 경험하는 것은 너무 다르니까..! 아무튼 너무 기초적이지 않는 강의에 만족도가 높았다.직장인이라 잦은 야근과 피로감에 미니 프로젝트의 진도가 더디게 나가서 매우 아쉬웠다. 인프런 워밍업 클럽이 끝나도 미니 프로젝트는 개발을 계속 시도해봐야겠다.정말 유익하고 열정적인 3주였다. 나 포함 참여한 인프러너와 코치님까지!모두 3주 동안 너무 고생하셨습니다!오프라인 수료식에 참여하고 싶었지만 평일인데에다 판교라 참여하지 못하는 것이 너무 아쉽네요.온라인으로라도 참여하겠습니다열정적인 개발자분들 앞으로도 화이팅하세요🥳그리고 하루에 과제가 하나씩 있었을 때는 정말 미리 해놓지 않으면 직장인은 따박따박 당일에 수료하기 어려운 코스인 거 같아 이런 부분만 조금 개선되었으면 좋을 거 같습니다! ex) 과제는 미리 내놓되 완료 체크는 주말에 하기 같은..참고로 미니 프로젝트는 현재진행중입니다! 평일에는 시간이 없어 주말에 후다닥 개발을 하려고 하는 중입니다.https://devhan.tistory.com/327
백엔드
・
인프런워밍업클럽
2024. 03. 03.
1
[인프런 워밍업 클럽_0기] 2주차, 두 번째 발자국 #2
2주차, 두 번째 발자국2주차는 계속 되는 야근으로 인해 시간에 쫓기듯이 공부하고, 과제했던 주였다. 속도가 급하면 체하기 마련! 그래서 오타 등의 실수도 많이해서 삽질로 많은 시간을 보내기도 했다.이전 발자국에서도 남겼지만 강의 별 요약본은 notion에 있다.https://abalone-copper-ebe.notion.site/d2e9b3e27b3348abbde60994cf627ebd?pvs=4 Day 7. 스프링 컨테이너의 의미와 사용 방법스프링 컨테이너의 역할을 정확히알게 되었다.스프링 컨테이너 : 서로 필요한 관계(의존 관계)에 있는 스프링 빈을 연결해주는 역할스프링 컨테이너의 동작 과정을 알게 되었다.스프링이 서버를 시작한다 > 스프링 컨테이너가 시작된다 > 기본적으로 설정된 스프링 빈들이 등록된다 > 개발자가 설정한 스프링 빈이 등록된다 > 필요한 의존성이 자동으로 설정된다.스프링에서 등록한 스프링 빈을 어떤식으로 땡겨와야하는지 알게 되었다.등록된 스프링 빈을 사용하려면 해당 클래스 파일을 스프링 빈에 등록해야 땡겨올 수 있다!Interface를 이용하여 코드 수정 시 수정하는 부분을 최소화 할 수 있다.다른 클래스에서 Interface를 상속받게하고 생성자에서 교체하고 싶은 구현체를 변경만해주면 되기 때문!하지만 위의 방법도 어찌저찌 됐든 생성자를 수정해야하기 때문에 수정은 불가피하다! 이런 걸 해결할 수 있는 어노테이션이 @Primary 와 @Qualifier를 사용하면 구현체 주입 우선순위를 설정해줄 수 있다.참고로 Primary와 Qualifier 중 우선순위가 더 높은 건 Qualifier이다. 과제https://devhan.tistory.com/323기존에 작성했던 코드를 Controller, Service, Repository 3단계로 분리하는 과제였다. 다른 건 다 괜찮았는데 Repository가 Interface로 변경되고 MemoryRepository와 MySQLRepository로 구현체를 나뉘어서 개발하는 부분에 시간을 너무 많이 쏟았던 기억이 난다.그래도 개발하면서 기존에 작성했던 코드를 리팩토링 하는 과정도 거칠 수 있어서 더 재밌게 코딩할 수 있었던 거 같다. Day 8. Spring Data JPA를 사용한 데이터베이스 조작8일차에는 String으로 작성했던 쿼리 방식의 단점을 알아보고 변경하는 시간이었다.Java의 객체와 Database의 테이블의 패러다임이 다르다는걸 정확하게 알게되었다.또한 Java의 상속을 Database로 표현하기 어려운 부분이 존재한다는걸 알게되었다.JPA는 단순한 규칙이라는걸 알게되었다.JPA의 전반적인 설정 방법과 사용 방법들을 알게되었다.람다식이 우르르 나와서 해당 관련 공부를 또 뼈저리게 느꼈다. 과제https://devhan.tistory.com/324이번 과제도 기존에 작성했던 코드 중 문자열로 작성된 쿼리를 JPA로 변경하는 과제였다.warehousingDate 필드가 제대로 맵핑되지 않는 문제때문에 골머리를 앓았다. 찾아보니 스프링에서는 카멜케이스로 작성된 필드는 DB 컬럼에서는 '_'로 단어를 구분한다고 한다. ex) warehousingDate -> warehousing_date이 에러는 ddl-auto를 create로 만들어주고 처음부터 다시 매핑해서 해결되었다.그 외에는 딱히 어려운 점은 없었다! Day 9. 트랜잭션과 영속성 컨텍스트트랜잭션의 이론에 대해서 알게 되었다. 트랜잭션이 뭘 하는지는 대략적으로 알았는데 딱 정의할 수는 없었다. 트랜잭션 -> 쪼갤 수 없는 업무의 최소 단위@Transactional의 사용 범위와 readOnly 옵션에 대해 알게 되었다.Select 쿼리에서는 readOnly 옵션을 사용하는게 성능상 더 좋다영속성 컨텍스트에 대해 알게 되었다.테이블과 매핑된 엔티티 객체를 관리하고 보관스프링에서는 트랜잭션을 사용하면 영속성 컨텍스트가 생겨남트랜잭션을 종료하면 영속성 컨텍스트가 종료됨영속성 컨텍스트의 4가지 장점변경 감지(Dirty Check) : 영속성 컨텍스트 안에서 불러와진 객체는 명시적으로 save 하지 않더라도, 기존 정보와의 변경을 감지해 자동으로 저장된다.쓰기 지연 : DB의 INSERT, UPDATE, DELETE SQL을 바로바로 날리는게 아니라 트랜잭션이 commit될 때 한번에 모아서 한 번만 날린다.1차 캐싱 : 아이디를 기준으로 객체를 기억하고, 해당 객체가 DB에서 다시 불려와질 때 변경된 정보가 없다면 굳이 DB를 통하지 않고 영속성 컨텍스트에서 해당 정보를 돌려준다.캐싱된 객체는 완전히 동일하다 -> 객체의 인스턴스 주소까지 완전 동일 과제이 날부터 미니 프로젝트가 시작되었다.첫 날이라 유의미한 코딩을 한 건 아니었고 프로젝트 생성부터 설정까지 완료하였다.그리고 앞으로 어떤식으로 개발할지 전체적인 틀을 그렸던 것 같다! Day 10. 조금 더 복잡한 기능을 API로 구성하기책 생성, 대출, 반납 기능을 개발하면서 전체적인 개념을 다시 잡기도하고 좀 더 객체지향적으로 코딩하는 방법을 알았던 날이다.되게 좋았던 건 얼마 전에 객체지향의 사실과 오해라는 책을 읽었었는데 사실 책에서는 예시 코드가 나오지 않아 약간 실무에서는 어떤식으로 접근해야할까에 대한 의문이 좀 남아있었다. 책을 읽어도 약간 반만 알겠는 느낌..? 근데 이 강의에서 딱 객체지향적으로 코딩을 해볼 수 있어서 그래도 느낌을 좀 알 것 같았다. 평소 회사에서 전혀 객체지향적으로 개발하지 않아 domain에 비즈니스 로직을 직접 넣어도 되는지 조차 몰랐었는데 이번 강의에서 그 부분에 대한 갈증을 조금 해소할 수 있어서 아주 마음에 들었다. 과제https://devhan.tistory.com/326어느정도 개발이 끝났던 시기였던 거 같다. 과제를 하면서 Team과 Employee의 연관관계를 담은 테이블을 강의처럼 따로 만들어야할지 말지 고민이 많았었고 또, Team의 ID를 Long 타입의 id로 하느냐, String 타입의 name으로 하느냐의 고민도 많았다..! Long 타입의 id로 하면 나중에 Employee를 조회할 때마다 Team의 객체를 가져와서 name을 또 Response 객체에 저장해줘야할 것 같은 번거로움 때문에 그냥 String name을 primary key로 지정했다. 코드에 대한 많은 생각을 할 수 있어서 좋은 시간이었던 거 같다.
백엔드
・
워밍업클럽
・
백엔드
・
스터티