블로그
전체 62025. 06. 16.
0
워밍업 클럽 4기 - Day 16 미션: Layered Architecture Testig
Controller Layer특징클라이언트와 통신을 담당하는 계층입니다. 테스트 방법@WebMvcTest를 사용하여 Controller Bean만 등록한 뒤 테스트 합니다.(Service Layer 부터는 mock을 사용합니다.) Request Body의 값이 유효한지를 체크합니다. Service Layer특징핵심 비즈니스 로직을 수행하는 계층입니다.테스트 방법핵심 로직을 테스트 하는 것이기에, 실제 환경과 비슷한 환경에서 기능이 동작함을 확인하는 게 좋습니다. @SpringBootTest 어노테이션을 사용하여 실제 Spring Context를 띄우고 테스트합니다.mock 객체의 사용을 최대한 자제합니다. 외부와 통신하는 경우(DB 제외)에 mock 객체를 사용하여 동작을 mocking합니다.DB에서 값을 읽거나 저장하지 않는 경우, @SpringBootTest를 사용하지 않고 객체를 직접 만들어서 사용해도 좋습니다.(예: 할인 정책에 따라 음료의 종류와 갯수를 입력받아서 주문 금액을 계산하는 로직)DB에 값을 입력하고 출력하는 로직이 들어가는 경우가 많습니다. DB에 이전 테스트에서 입력한 값이 남아있으면 다음 테스트에 영향을 줄 가능성이 높습니다. 따라서 테스트의 독립성을 보장하기 위해 각 테스트가 끝난 후 DB 값을 비우는 작업이 필요합니다.(@Transactional어노테이션을 사용하거나, 직업 DB를 비우는 작업을 함으로 DB를 비울 수 있습니다.) Repository Layer특징서버와 DB 사이의 통신을 담당하는 계층입니다. 테스트 방법Jpa를 사용하는 경우 @DataJpaTest를 사용하여 Repository Layer 빈만 등록하여 테스트할 수 있습니다.레포지토리가 JdbcTemplate만 사용해서 구현한 경우 @JdbcTest를 적용하여 테스트할 수 있습니다.
2025. 06. 15.
1
워밍업 클럽 4기 - 3주차 발자국
학습편무엇을 학습했는가?테스트 코드에 관한 관점, 테스트 코드 범위에 대한 생각을 학습했습니다.개인적으로 좋았던 부분은 JPA를 사용하면서 코드 자체가 JPA에 귀속되어 Hexagonal 아키텍처가 등장한 배경을 설명해주신 부분이었습니다. 어떻게 학습했는가?이전 강의(Readable Code)의 경우에는 강의에서 치는 코드를 같이 쳐가면서 학습했습니다.실제로 리팩토링을 진행하면서 코드에 감을 익히기 위함이였습니다.코드를 쳐가면서 코드를 작성하는 법을 체득하기 위함입니다.이번 강의에서는 코드를 치기보단 강의를 듣는 방식으로 학습하고 있습니다. 테스트 코드를 작성하는 게 낯설지가 않아서 이 방법을 택했습니다.이번 주는 이전에 들었던 강의를 다시 들으면서 쉬어가는 주였습니다.2주차 때 많이 무리한 것 같습니다... 출근해야하는데 새벽 1시까지 강의를 들었으니... 잘한 점지치지 않고 주말에 꾸준히 강의를 듣는다는 점이 잘했습니다. Readable Code와 학습 방식을 바꿨는데, 바꾼 방법으로 학습해도 이해가 잘 됩니다. 아쉬운 점주중에 강의를 계속 못듣는다는 점이 아쉽습니다. 다만 제 상황(PR 리뷰, 회사)상 어쩔 수 없는 부분이라고 생각합니다. 개선할 점이대로 계속 학습하면 무사히 완주할 수 있을 것 같습니다. 미션편어떤 미션을 수행했는가? Readable Code의 지뢰찾기 게임에 테스트 코드를 작성했습니다. 어떻게 미션을 접근했는가?이번 미션에서는 핵심 비즈니스 로직을 테스트 하려고 했습니다. 스스로 난이도를 높여서 테스트 하기 힘든 Board를 테스트했습니다.지뢰 Cell을 선택하는 로직이 로직이 Board안에 포함되어 테스트하기가 힘들었고, 지뢰 선택 방법을 Interface로 선언, 리팩토링하여 외부에서 지뢰 선택 방법을 주입하는 방식으로 변경했습니다. 잘한 점테스트 코드 미션이였지만 Readable Code 강의에서 배운 걸 적용하여 지뢰찾기 로직을 리팩토링했습니다. 코드를 보는 눈이 좀 나아진걸지도...?스스로 난이도를 상향시켜서 로직을 테스트 했습니다.제 나름의 미션이긴한데, 회사의 결제 로직을 통합했습니다. Readable Code에서 배웠던 것들을 바로 적용해 볼 수 있어서 좋았습니다. 아쉬운 점이제는 인정해야겠습니다. 절대적인 미션 수행 시간이 부족합니다.(미션 떄문에 연차를 쓰기는 좀...)이번 미션은 2시간 동안 수행했는데, 리팩토링할 때 시간을 많이 사용해 시간 내 미션 제출을 못할 뻔했습니다. 난이도를 높여서 미션을 수행한 점은 좋았으나, 시간 분배에 실패하여 미션 제출을 못할 뻔했습니다.개선할 점시간이 없는걸 이제 그만 인정하고 시간에 맞는 미션을 수행합시다.
2025. 06. 08.
1
워밍업 클럽 4기 - 2주차 발자국
학습 부분 무엇을 배웠는가?(학습 중 아하 모먼트 위주)1. 인터페이스 이름 짓기섹션 7의 48강 리팩토링(3) - 관점의 차이로 달라지는 추상화에서 인터페이스 네이밍에 관해서 아래와 같은 기준이 있습니다.- 어떤 데이터를 가져와야 하는가?- 데이터를 어디에서부터 어떻게 가져올 것인가?이 부분이 인상 깊었는데, 인상 깊었던 이유는 제가 인터페이스를 만들고 확장하려고 할 때 막혔던 부분과 똑같기 때문이었습니다.제가 만들었던 인터페이스의 이름이 무엇을 반환할 지가 아니라 어떻게 반환할 지에 초점이 맞춰져 있었습니다.회사 코드라 자세한 사항은 말할 수 없지만, 인터페이스를 이름 지을 때 어떤 데이터를 가지고 와야 하는지를 중점으로 생각하면 좋다는 인사이트를 얻었습니다. 2. 인터페이스의 추상메서드로 getter가 있어도 괜찮을까?Cell 인터페이스에서 Snapshot getSnapShot()이라는 메서드를 선언해서 구현을 했습니다.저는 처음 이 추상 메서드를 봤을 때 객체의 필드를 반환하는 단순한 메서드라고 생각을 했습니다.이 단순한 메서드를 "인터페이스에 선언하여, 모든 구체에서 메서드를 구현해야하나?"라는 의문이 들었습니다. 그런데 생각해보니, 단순한 메서드가 아닐 수 있을 거라는 생각이 들었습니다. 구현체가 Snapshot을 필드로 가지고 있다면 getSnapshot()메서드가 필드를 반환하는 단순한 메서드이다.구현체가 Snapshot을 필드로 가지고 있지 않다면 직접 SnapShot을 만들어 줘야 한다.상속을 사용하여 부모의 필드를 getter로 내려주는 것보다, getter를 인터페이스의 추상 메서드로 만들어서 유연성이 높은 코드를 작성할 수 있겠다는 생각이 들었습니다.(상속이 나쁘다는 의미는 전혀 아닙니다.) 3. 예외는 비싼 객체이다.중간 점검에서 우빈님께서 "예외는 비용이 큰 객체이므로 사용에 신중해야 한다"는 말을 하셨습니다.예외 객체에는 예외가 발생한 위치와 경로를 담은 stack trace 정보가 포함되며, 이를 생성할 때 추가적인 연산이 필요합니다.따라서 꼭 필요한 경우가 아니라면 예외를 던지는 것을 지양하는 것이 좋습니다. 4. 리팩토링을 단계별로 하는법회사에서 결제 로직을 추상화, 통합하는 리팩토링을 진행했는데, 처음에 도메인 객체를 먼저 변경하는 바람에 전파 범위가 넓어지고 변경 부분이 파편화되는 어려움이 있었습니다.우빈님의 강의를 보니, 전파 범위가 작은 부분부터 점진적으로 리팩토링하는 방식을 취하셨습니다.(메서드 추출 -> 인터페이스 선언 및 구현)클래스 내부의 리팩토링을 마치고 나서 클래스 간의 리팩토링을 진행하니, 변경 범위를 보다 명확하게 파악할 수 있었고, 안정적으로 리팩토링을 진행할 수 있었습니다. 앞으로는 전파 범위를 고려해, 영향도가 적은 부분부터 점진적으로 리팩토링하는 게 좋다는 생각이 들었습니다. 5. Enum implements interfaceenum에서 인터페이스를 구현할 수 있다는 점이 신기했습니다. 다만 enum을 단순한 상수로 볼지, 하나의 상태를 가진 객체로 볼 지에 따라 인터페이스 구현의 필요성은 달라질 수 있다고 생각합니다. 어떻게 학습했나?최대한 휴일(화요일 금요일)을 이용하려고 했습니다.화요일이 바빴는데, Readable Code 4강 중반 ~ 6강까지 들었습니다.미션 수행은 날짜게 맞춰서 수요일에 제출했습니다.목, 금, 토는 쉬었습니다.일요일에는 Readable Code 강의를 마무리 지었습니다.메모장을 사용해 강의를 들으면서 좋았던 부분, 키워드를 메모하면서 아이디어가 날라가는 걸 방지했습니다. 좋았던 점화요일에 결국 세웠던 목표(6강 마무리)를 달성했습니다.미션도 제출하고 금요일에 중간 점검도 참석했습니다.메모장을 사용해 강의에서 와닿았던 키워드를 적으니, 무엇을 배웠는지 정리하기가 수월했습니다. 아쉬운 점화, 수요일에 많은 에너지를 써서 목, 금, 토는 손을 놓은 것 같습니다.화요일에 새벽까지 강의를 듣다보니 머리 속에 코드 잔상이 남아 수면의 질이 떨어졌습니다. 개선할 점10시 ~ 11시에 자고, 일찍 일어나서 강의를 들어서 밤새 강의를 듣는 걸 방지해보려고 합니다.휴일이라고 해도, 강의를 몰아 듣는 건 지양해야 겠습니다. 미션 수행 부분어떤 미션을 했는가?이번 주는 코드 리팩토링 미션을 했습니다. 어떻게 접근 & 수행했는가?화요일에 관련 강의를 다 듣고, 수요일에 미션을 수행했습니다.화요일에 강의를 듣기 바빠서 리팩토링할 코드는 수요일 9시 반에 처음 봤고, 12시까지 리팩토링 미션을 진행했습니다.빨리 끝내야겠다는 마음이 앞서다 보니 코드를 한번 훓고 바로 리팩토링을 수행했습니다.핵심 로직부터 변경해야겠다는 생각이 들어서, 도메인(StudyCafePass) 변경부터 시작했습니다. 좋은 점은?직접 강의에서 들었던 내용을 적용하다보니 코드가 깔끔해졌습니다. 아쉬운 점은?시간의 압박을 느껴 전체적인 리팩토링 포인트를 잡지 않고 바로 코드를 수정했습니다.어디를 어떻게 변경할지, 어떤 부분을 먼저 변경할지 전략을 짰으면 시간을 효율적으로 사용할 수 있을 것 같습니다.전체적인 질도 만족스럽지 않습니다. 리팩토링할 부분이 보이는데 하지 않았습니다. 어떻게 개선하고 싶은가?월요일에 테스트 코드 작성 미션이 있는데, 내일부터 미션을 분석하고 수행하는 방식으로 개선하고 싶습니다.
2025. 06. 01.
1
워밍업 클럽 4기 - 1주차 발자국
학습 회고무엇을 학습했는가?진도로는 섹션 4 SOLID의 중간까지 수강을 마쳤습니다. 잘 읽히는 코드(Readable Code, 읽을 때 뇌를 덜 쓰는 코드)가 어떤 코드를 의미하는지를 배웠습니다. early return 부분이 인상 깊었습니다. 메서드 추출을 통해 early return을 적용한다면, 코드의 로직이 깔끔해지겠다는 인사이트를 얻었습니다. 변수의 선언과 변수를 사용하는 부분을 가깝게 놓자는 부분도 좋았습니다. 실제로 코드에 적용했는데, 로직이 깔끔해졌습니다. 잘 읽히는 코드의 필요성을 배웠습니다.내가 짠 코드는 지금의 나는 잘 이해하지만, 미래의 나 혹은 미래의 동료가 읽기가 어려울 수 있다는 걸 배웠습니다. 안전한 리팩토링 방법리팩토링을 할 떄 컴파일이 깨지지 않으면서 리팩토링하는 법을 배웠습니다. 어떻게 학습했는가?이번 주는 클럽이 시작하는 주여서 적응하는 주로 설정했습니다.출근 시간에 강의를 팟캐스트처럼 듣기, 퇴근 후 강의를 보면서 코드를 직접 치는 방식으로 학습했습니다.측정 가능한 목표는 미션을 시간 내에 완수하는 것이었습니다. 학습에 대한 피드백좋았던 점미션을 다 완수했습니다. 이 회고를 올린다면 이번 주에 세운 정량적인 목표는 달성합니다.준비 운동 없이 바로 이번 주부터 개인 공부를 시작했는데, 버겁지만 포기하지 않았습니다.퇴근한 이후에도 많이 쳐지지 않고 의자 앞에 앉아 있었습니다. 아쉬웠던 점인프런 워밍업 클럽 기간 동안 코드 리뷰어로 활동하는데, 코드 리뷰 시간과 학습 시간의 균형을 잡지 못했습니다.워밍업 클럽 이전에 들었던 강의를 제외하면 이번 주 들은 강의는 2개가 안되는 것 같습니다. 보완할 점활동적인 학습하면서 사람들과 교류하고 싶습니다. 간단한 스터디를 하나 만들까 하는 생각이 있습니다.회사에 다녀오면 개인 시간이 얼마 나오지 않습니다. 6월 1번째 주는 휴일이 많아서 이 때 강의를 많이 들으려고 합니다.리뷰 시간과 학습 시간을 분리하여 시간 관리를 시작해보려고 합니다. 미션 회고어떻게 접근했는가?이번 주 미션들은 코드를 작성하기 보단, 추상, 구체, 객체라는 개념들을 학습했는지 체크하는 미션이라고 생각했습니다.추상과 구체 미션에서는 제 관심사인 미식축구와 미션을 연관지어 하나의 미식축구 플레이를 구성하는 세부적인 요소들을 분석했습니다.이 미션은 개념을 실제로 적용해보는 과정이라고 생각했고, 저에게 익숙한 주제를 활용해 개념을 더욱 깊이 이해하고자 했습니다. SOLID 미션에서는 먼저 미션을 읽고, 2일 동안 SOLID 개념을 머릿속에서 굴리고 미션 마감일(금요일)에 미션을 작성했습니다.SOLID 미션은 어떤 개념을 나만의 언어로 말하는 연습을 하는 미션이라고 생각했습니다. 그래서 개념을 충분히 머리속에서 숙성시키고, 미션을 작성하는 방식으로 수행했습니다. 미션 해결 피드백좋았던 점추상과 구체 미션의 경우 평소 관심사와 미션을 결합하다 보니, 미션을 즐기면서 해결했습니다.개인적인 관심사의 이해도와 추상이라는 개념의 이해도를 동시에 향상시켰습니다.SOLID 미션을 작성할 때 머리속의 추상적인 개념을 대충 써보고, 수정해가면서 제 답을 발전시킨 점이 좋았습니다. 아쉬웠던 점SOLID 미션 수행할 때 2일간 머리에서만 생각을 하고 당일에 미션을 작성했는데, 고민 중 떠올랐던 좋은 아이디어가 날라간 점이 아쉬웠습니다. 보완하고 싶은 점미션 생각을 하면서 아이디어가 떠오를 때 카톡에 적어놓으려고 합니다. 아이디어가 사라지는 걸 방지하고 싶습니다.
2025. 05. 30.
0
워밍업 클럽 4기 - 백엔드 Day 4 미션: SOLID
1. 코드 리팩토링public boolean validateOrder(Order order) { if (order.isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.hasZeroOrNegativeTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasEmptyCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } public class Order { // items, totalPrice, customerInfo는 있다고 가정 public boolean isEmpty() { return this.items.size() == 0; } public boolean hasZeroOrNegativeTotalPrice() { return this.totalPrice 2. SOLID를 나만의 언어로 표현하기 SRP(단일 책임 원칙):객체의 책임을 하나의 홑문장으로 설명할 수 있어야 한다.서술어가 2번 나온다는 말은 동작이 2번 나온다는 말이고, 과한 책임을 지고있거나 구체적인 특징만 나열한 경우일 수 있다.(예: 쿼터백은 패스 게임에서는 공을 던지고, 러닝 게임에서는 공을 러닝백에네 넘겨주고, 코치의 작전을 다른 공격 선수들에게 전달한다.)객체를 설명하는 말의 추상화 정도가 과하면 객체를 적절히 설명할 수 없다.(예: 쿼터백은 미식축구 포지션이다.)미식축구에서 쿼터백은 공격 전술을 지휘하는 포지션이다. OCP(개방 폐쇄 원칙): 기능을 변경할 때는 코드를 고치는 게 아니라 코드를 더하는 방식으로 구현해야 한다. 변경해야 할 코드와 그대로 유지되어야 할 코드가 섞여 있으면, 기능을 수정할 때마다 기존 코드를 계속 수정하게 되고, 이로 인해 변경이 다른 코드에까지 영향을 미칠 수 있다. 따라서 변경이 필요한 부분과 보존해야 할 부분을 명확히 분리하고, 새로운 기능은 기존 코드를 건드리지 않고 덧붙이는 방식으로 구현해야 한다. LSP(리스코프 치환 원칙): 부모 클래스가 자식 클래스에게 물려준 기능은 보존해야한다. 부모 클래스를 자식 클래스로 교체한다는 것은, 부모의 기능을 자식이 할 수 있다는 전제가 깔려있다. 그런데 만약 자식 클래스가 부모 클래스의 기능을 제대로 수행하지 못한다면, 예기치 못한 동작을 하거나 에러가 발생한다.그렇다면 부모 클래스를 자식 클래스로 교체하는데 추가적인 리소스가 든다.따라서 자식 클래스는 부모 클래스의 기능을 보존해야한다. ISP(인터페이스 분리 원칙): 응집도 높은 인터페이스를 설계해야 한다. 인터페이스는 객체의 설계도다. 그리고 인터페이스가 변경된다면, 모든 구현체도 변경되어야 한다.인터페이스의 응집도가 낮아 여러 책임이 뒤섞여 있으면 일부 기능 변경으로 인해 구현체는 불필요한 변경을 할 수 있다.따라서 인터페이스는 관련 기능들로 묶인, 응집도 높은 형태로 설계해야 한다. DIP(의존 역전 원칙): 고수준 모듈과 저수준 모듈이 맺는 하나의 의존 관계를 2개의 의존 관계로 분리하여 결합도를 낮춘다. 고수준 모듈과 저수준 모듈이 직접 연결되어 있으면, 저수준 모듈의 변경 사항은 고수준 모듈까지 전파될 수 있다.하지만 저수준 모듈을 추상화하면, 아래 같이 2개의 의존 관계로 쪼개진다.고수준 모듈은 추상화 모듈 의존저수준 모듈은 추상화 모듈 의존(의존성 역전)이렇게 2개로 쪼개진 의존 관계에서는, 저수준 모듈의 변경이 고수준 모듈에 영향을 주지 못해 코드의 유지보수성이 향상된다. %추가% DI(의존성 주입): 어떤 포지션에 어떤 선수를 넣을지 말지는 감독의 책임이다.(스포츠 관점)
2025. 05. 28.
0
워밍업 클럽 4기 - 백엔드 Day 2 미션: 추상과 구체
유튜브 참고 링크(https://youtu.be/C_3w8UVUCKo?si=P4rySBHQgQMlKmWS&t=149) 추상: 미식 축구의 쿼터백이 패스를 던져서 46야드를 전진하고 터치다운을 한다.구체:1. 빨간 팀(공격팀)의 쿼터백이 공을 받으면서 플레이가 시작된다.2. 플레이가 시작되자 하얀 팀(수비팀) 중 왼쪽에서 4명, 오른쪽에서 1명이 쿼터백을 압박하고 4명이 리시버를 대인 방어한다. 그리고 최후방 1명이 장거리 패스를 견제한다.3. 공격 팀의 4명의 리시버는 오른쪽에서 3명, 왼쪽에서 1명이 출발한다. 양 끝의 리시버는 밖으로 나갔다가 멈추고 오른쪽 안쪽 2명의 리시버는 직진하다가 왼쪽으로 꺾는다4. 리시버를 대인마크 하는 수비수는 리시버가 밖으로 나갈 것이라 예상해 몸이 밖으로 틀어져 있었고, 리시버는 안쪽으로 꺾어 빈틈이 생긴다.5. 쿼터백은 중앙에 있는 2명의 수비수 사이로 패스를 던지고 리시버가 공을 잡는다.6. 공을 잡은 리시버는 왼쪽으로 뛰기 시작하고 수비팀의 최후방 수비수는 오른쪽에 치우쳐 있어 리시버를 잡지 못한다.7. 터치다운
백엔드