게시글
질문&답변
test오류
되게 오래전에 답글을 남겼었는데, 알림이 와서 새롭게 해결했던? 방안을 가지고 왔습니다!물론, 기존의 운영 데이터 단에서는 사용하지 않는 것이 좋고 개발을 진행하는데에 있어서 초기 테스트 DB용에서만 이 방법을 택해주시면 좋을 것 같습니다~h2 데이터베이스에서 h2 drop all objects 를 입력해주시면 저장되어있던 테이블이 싹 다 날라가게 됩니다.h2 데이터베이스도 Intellij Build 와 동일하게 증분 빌드를 하는 과정에서 삭제 된 데이터(테이블)에 대해서는 관리를 하지 않는 것처럼 보였습니다. 이러한 이슈는 일대다 읽기 전용 테이블이 생성되었다가 해당 연관관계를 삭제하더라도 테이블이 계속해서 h2 데이터베이스에 남아있을 때에도 동일하게 적용되었습니다!
- 0
- 8
- 4.1K
질문&답변
test오류
로그를 보시면 Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not instantiate id generator [entity-name=jpabook.jpashop.Member] 가 보이고, Column "start_value" not found 가 보이는데요.아마도 H2 데이터베이스를 사용하시면서 엔티티 테이블의 기본키 생성 때 @GeneratedValue(strategy = GenerationType.IDENTITY)를 사용하신 적이 있을 경우에, 발생하는 오류인데요.https://stackoverflow.com/questions/64615730/how-to-fix-error-with-h2-plugin-version-1-4-200-when-run-spring-tests-jdbcsql해당 글 참고 하시면 H2 방언 추가해줘서 해결됐다는 경우가 있어요.추가적으로 만약 DB가 초기화되어도 문제가 없으시다면 create로 하면 테이블이 초기화 되면서 해당 문제가 해결이 될거에요. 만약에 DB의 값을 유지하고 싶으면 저도 mysql DB로 한 번 테스트 해봤다가 H2로 데이터베이스 바꾸면서 동일한 문제가 발생중인데요.spring: jpa: hibernate: ddl-auto: update데이터는 유지하고 싶어서 update를 사용하였는데, 스프링 부트 내부 로직에서 참조 시에 이전에 추가된 start_value를 계속 찾는 것 같아요... 찾을 수 있으면 좋을텐데요.. 만약에 찾게되면 알려드리겠습니다!
- 0
- 8
- 4.1K
질문&답변
Introduce Variable 자동 final
Introduce Variable 사용 시 톱니바퀴 모양(option+command+O)이 따로 있어서 확인해보니 Declare final 이 체크가 되어 있었습니다! 해당 체크를 푸니 바로 해결 되었습니다!!!
- 1
- 2
- 779
질문&답변
joinColumn, referencedColumnName
아하 성능 최적화때문이었군요! 감사합니다!!
- 0
- 2
- 222
블로그
전체 42024. 10. 28.
0
뒤늦게 작성하는 2주차 발자국
미션을 수행하고, 메모에만 옮겨놓고 완수 메모를 남기려고 확인해보니 2주차 발자국을 안 적었었다.2주차에는 실제로 객체지향을 적용하는데 필요한 테크닉과 실제로 리팩토링을 실행해보는 과정그리고, Practical Testing 강의를 시작하는 파트였다.이전에, 우빈님의 라이브에서 내 코드가 피드백 부분에 있었는데 라이브를 듣지 못해서 너무 아쉬웠다.해당 방식이 오버엔지니어링이라는 피드백을 받았는데, 그 부분에 있어서 타당한 근거가 있었다.하지만 요구사항보다는 더 들어갔기에 오버엔지니어링은 맞을 것이다. 그렇다면 우리는 언제쯤 하나로 통합하여 관리 포인트를 줄이는 작업을 할 수 있을까?Value ObjectValue Object는 늘 불변을 유지해야 한다.어떠한 로직이 들어가더라도 새로운 값이 반환이 되어야 한다. 즉, 새로이 객체를 생성하여야 한다.Value Object는 Entity와 달리 식별자가 없기 때문이다.객체의 책임과 응집도이 부분은 항상 고민이 많다. 어느정도로 책임을 줄여야하지? 이정도면 다른데서는 수정 될 이유가 없을까?정말 수정되지 않으려면 정말 작게 만들어야 하는데 그 부분이 참 어렵다. 단위 테스트Junit을 통한 단위테스트와 테스트 개념, 그리고 인터페이스를 활용한 테스트하기 어려운 영역을 분리하는 개념에 대해서 배웠다. 이 부분은 늘 테스트를 짜면서 실천하고 있기에 더 중요하다는 것을 인지하는 계기가 되었다.
클린코드
・
테스트코드
2024. 10. 28.
0
인프런 워밍업 클럽 스터디 백엔드 클린 코드, 테스트 코드를 마치며
먼저, 이번 주차에 학습한 내용에 대해서 정리를 하고 시작한다.클린 코드도, 테스트 코드도 모두 지금 나의 회사가 처해있는 코드의 상황에서 더 어떻게 개선해나갈 수 있을까? 를 고민을 하고 해당 스터디를 진행하면서 이러한 고민거리를 해소하고자 하였다.그리고, 스터디를 진행하면서 많은 부분이 해소가 되기도 하였다.특히, Mock을 대하는 자세가 회사에 들어오기 전과 후가 조금 달라졌다.실제로 회사내에서 Facade 코드를 다루기 위해 Mock이 많이 이용된다. 이 부분은 리팩토링을 위해 테스트를 작성하고자 이루어졌다고 생각한다. 현재 있는 코드에서 리팩토링을 할 계획인데 단위 테스트를 전부 작성을 하게 된다면 리팩토링을 하는 과정에서 테스트 코드가 지속적으로 수정 될 것이기 때문이다.그렇기에, 비슷한 상황에서 테스트 코드가 빠르게 돌아가는 상황이 필요했는데, 지금 현재 있는 상황에서는 더 개선을 하려면 Application Context Cache 를 유지하면서 테스트 코드의 Spring Container를 유지하여 테스트 코드가 빠르게 돌아가도록 하여 리팩토링을 하는게 좋을 것 같다고 생각한다.추가적으로, Presentation Layer에서의 테스트와 영속성 계층의 테스트에 대해서 궁금한 부분이 있어 우빈님에게 질문을 드렸고 많은 인사이트를 얻어 갈 수 있었다.해당 질문의 답변은 다음에서 찾아 볼 수 있다.https://www.inflearn.com/community/questions/1409734/%EC%98%81%EC%86%8D%EC%84%B1-%EA%B3%84%EC%B8%B5%EA%B3%BC-e2e-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%97%90-%EB%8C%80%ED%95%B4-%EC%A7%88%EB%AC%B8%EC%9D%B4-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4 마치며이번 스터디는 내가 회사를 위해 더 나은 회사로 나아가기 위해 나 자신의 성장뿐만 아니라 회사의 성장을 위해서 신청을 하게 되었고, 많은 부분 코드 습관이 고쳐지게 되었다. 특히, private 메서드를 사용하는 것을 지양을 하였던 부분(모든 메서드를 테스트 하고 싶은 마음에)에서 요즘은 오히려 읽기 쉬운 코드로 작성하고자 하고 있다.이 부분은 꼭 테스트가 필요한 걸까? 에 대해서 많은 질문을 던지며 코드를 리팩토링 해나가고 있다.회사를 다니면서 스터디를 참가하는 것이 쉽지는 않았지만 끝 마칠 수 있었던 나 자신에게 칭찬 한마디 던지고, 잘 이끌어준 우빈님에게 칭찬 백마디 전달하고 싶다.
백엔드
・
테스트코드
2024. 10. 20.
0
Practical Testing 3주차 발자국
서문테스트에 대해서는 늘 많은 고민이 있었다.테스트를 작성하다보니 많은 선택지들이 있었는데 이번 3주차 간의 강의를 통해서 이전에 수강했던 강의를 복습하는 차원에서 다시 한 번 바라보았다.특히나 처음에 강의를 들었을 경우에 가장 와닿고 도움이 많이 되었던 말은 테스트 코드는 좋은 구조를 설계해나가는 과정이라는 것이다.테스트를 작성해나가다 보면 내가 짜놓은 코드의 설계가 확장성 있게 작성이 되지는 않았는지, BDD 기반으로 테스트 코드를 작성해 나가면서 내가 짠 메서드의 행위가 테스트 하기 쉽도록 작성되어 있는지에 대해서 쉽게 파악 할 수 있었다.취업 전에 이 강의를 보고 난 이후의 경험과 현재 취업을 하고 나서 여러가지 테스트 서적을 보고 난 이후의 경험이 합쳐져서 더 많은 도움이 되고는 한다.단위 테스트기존에는 단위 테스트의 기준을 항상 비즈니스 레이어 기준으로 잡았었다. 단위 테스트는 책 한권으로 다룰 만큼 되게 폭 넓은 분야인데 그렇게 넓게 다룰 필요가 있는가? 라고 생각을 하기도 하였다.하지만, Effective Software Testing 책에서 읽어 본 바로는 하나의 계층만 테스트 할 수 있는 구조라면 단위 테스트이다.라는 말이 나의 모호함을 해결해주었다. 다른 말로, 통합 테스트와 단위 테스트의 기준은 무엇을 바라보아야 하지? 라고 했을 때, 다른 계층과의 결합이 생기는 순간 이는 통합 테스트를 바라보면 된다는 것이다.이 관점에서 통합 테스트를 작성 할 때 Mocking 하는 것을 더 생각해 볼 필요가 있다. Mock은 주어진 행위에 대한 결과가 내가 예측한 값을 사용하게 된다. 이는, 실제로 그 값이 내려오지 않을 수 있음을 시사하는데 이로써 해당 계층에 대한 테스트가 신뢰도 있는 테스트가 작성되지 않는다는 것이다.따라서, Mock을 사용한 테스트를 하게 되면 이는 단위 테스트를 작성하는 형태가 된다. 내 계층에서 어떠한 메서드가 이 값을 받았을 때 해당 계층의 결과는 이 결과가 나와야 해. 라는 의미를 가지게 되는 것이다.프레젠테이션 레이어이 부분은 강의를 보면서도 아직 고민이 많다.우리는 프레젠테이션 레이어 테스트를 작성하면서 강의에서도 컨트롤러 영역에서의 서비스 계층 영역을 mocking 하여 결과 값을 가지고 오는 것을 볼 수 있다. 물론 전제는 서비스가 테스트되었기 때문에 서비스 계층에서의 결과 값은 예측 값이 맞겠지만, 이는 서비스 영역에서 결과 값이 필드가 바뀌는 경우에 놓칠 수 있는 경우를 시사하게 된다.물론 이 부분은 개발자가 신경써야 하는 부분이기도 하지만 휴먼 에러를 방치하는 것은 또 좋지 않다고 생각을 하긴 한다. 이 방법을 해결하기 위해서는 결국 E2E (End-To-End) 테스트를 진행하기 위해서 assured 테스트를 사용해보기도 하였는데, 이럴 경우에 Persistent 레이어까지 전부 test container를 띄우면서 실 상황과 유사한 상황으로 테스트를 진행하다보니 테스트에 많은 시간이 소요되게 된다.물론, 우빈님이 소개해주신 스프링 부트 컨테이너의 빈 목록을 한데 모아서 관리한다면 속도 개선은 많은 부분 이루어지지만, 그 이전에 테스트를 실행 할 때마다 해당 테스트에 필요한 데이터들을 새로이 만들어주는 과정이 많은 시간을 잡게 되고, 이는 결국 비즈니스 개발에 많은 시간을 잡아먹게 된다.추가적으로, E2E로 작성하게 되는 경우에 나타나는 문제점은 어찌됐든 서비스 영역의 테스트 결과 값이 서비스 계층의 비즈니스가 완성되기전까지 문서화를 위하여 Dummy 형태의 응답 값을 가지게 된다는 것이다.이러한 여러가지 상황 속에서 아직까지 나만의 답을 찾지 못하였으니 계속 경험을 가지면서 적절한 나만의 기준을 찾는 것이 좋을 것 같다. 해당 내용에 대해서는 우빈님께 질문을 드릴 예정이다.영속성 레이어이 부분도 많은 고민을 가지고 있었다. 취업 준비를 할 때에는 H2 인메모리 DB를 사용하여 많은 부분 영속성 레이어의 테스트를 구성 할 수 있었으나, 실제로 현업에 왔을 때에는 H2에서는 정상 동작하나 실제 DB에서는 작성하지 않는 상황들이 적지 않게 발생하는 것을 볼 수 있었다. 이를 대체 할 방법이 Test Container를 띄워서 테스트를 작성하는 것인데, 시간이 생각보다 오래 걸린다. 아직 이 부분도 해결이 되지 않은 것 같다. 우빈님 강의 뿐만이 아니라 더 많은 서적들을 보면서 영속성 계층을 어떻게 처리하는게 좋을지 이 부분도 경험치를 쌓아나가야 하는 것 같다.모든 상황에서는 은탄환은 존재하지 않는다. 실제로 어떻게 하는지 지금 내 레벨에서는 최대한 카피하여 많은 경험치를 쌓아가는 과정이 중요하다고 생각한다. 또 좋은 기술이 나오지 않을까? 라는 생각이 들기도 한다.마무리옛날에는 테스트를 짜는데 시간이 오래걸린다는 말이 어느정도 맞는 말이라고 생각한다. 하지만, 현재에는 기술이 너무 좋아졌다. copilot을 사용하면서 테스트를 짜는 시간을 거의 300%는 더 빠르게 작성 할 수 있게 해주는 것 같다.기술을 적절히 활용하면서 회사 내에 테스트를 짜는 문화를 좀 더 거부감 없게 만들어나가고 싶다.
소프트웨어 테스트
・
테스트
・
단위테스트
・
회고
2024. 10. 06.
0
읽기 좋은 코드를 작성하는 사고법
강의 돌아보기스터디 1주차에 접어들면서 내가 오해하고 있었던 부분들과 모호한 부분들을 맞춰 갈 수 있는 주차였다.요즈음에는 어떤 지식을 습득하고 적용을 하기 위해서 가장 좋은 방법이 무엇일까에 대해서 고민을 하고 있다.그 와중에 마인드 맵으로 카드 형태로 정리하는 방식이 내가 공부한 지식을 나만의 방식대로 기억 할 수 있게 해주는 좋은 도구가 아닐까 하여 옵시디언을 사용하였다.누군가는 이것이 무엇을 의미할까? 라는 생각이 들 수 있다.하지만 확실히 카드 형태로 공부한 내용을 정리했을 때 해당 키워드를 바라보면 그때 공부했던 그 장면이 머릿속에 재생된다.하지만, 아직까지는 마인드 맵에 너무 많은 세부 내용들이 노출되고 있다라는 느낌이 든다. 세부내용을 "별도의 페이지를 만들어서 노출시키는 것은 어떨까?" 라는 생각이 든다. 이것 또한 추상화가 아닐까.핵심특히, 가장 코드를 짜면서 헉! 했던 강의 파트는 추상화 레벨이다.누군가의 코드를 보다보면 뭔가 어색하다라고 느껴지는 상황이 있다. 그리고 그 상황이 바로 추상화 레벨이 다르기 때문이 벌어진 일이었다.어딘가는 외부 세계를 만들어서 객체로 처리하고 있는데 어딘가에는 private method로 밖에서 처리하고, 어딘가는 로직의 구현체가 내부에 있다보니 내가 어떤 방향으로 코드를 읽고 따라가야 하는지에 대한 방향을 잃었다고 생각이 든다.미션 회고1주차에는 두 가지 미션이 있었다. 추상을 구체화 하기코드를 적절한 추상화 레벨에 맞춰 리팩토링 하기추상을 구체화 하기미션을 수행하면서 최근에 굉장히 중요하다고 생각하고 있는 CS 지식을 얼마나 잘 알고 있는가. 그리고 실제로 나는 어떠한 사고를 가지고 있는가를 판단하기 위해 미션에 접목했다.세상에 모든 일은 추상화 되어있다고 생각한다. 그리고 고수준 레벨의 언어, 프레임워크가 잘 만들어지게 되면서 이제는 코드를 짤 때 내부의 구체를 갈수록 보기 어려운 형태가 되어가고 있다.과연 어떠한 우리가 웹에서 어떠한 작업을 할 때 얼마나 많은 일들이 발생하고 빨리 처리되고 있는 것일까? 를 기준으로 수행했다.하지만, 추상화 레벨을 너무 높게 잡아서 였을까? 추상화에 1뎁스 구체 -> 2뎁스 구체 -> 3뎁스 구체 너무 깊은 레벨의 구체까지 바라보게 되는 느낌이 있었다. "코드의 컴파일 단계까지 적어야하나?" 라는 생각도 들었는데, 그건 너무 깊게 갔다고 생각하여 해당 구체 레벨까지 내려가지는 않았다.이 미션을 수행하면서도 사람이 읽기 좋도록 추상화 레벨을 신경쓰는 것이 좋다. 라는 생각이 한편으로 들었다.추상화 레벨에 맞춰 리팩토링 하기미션을 보고 가장 먼저 눈이 갔던 부분은 boolean 타입의 반환 타입이었다. "객체의 상태 검증을 외부로 노출하는 것이 맞는 것일까?", "이 객체는 어떻게 존재 할 수 있지?" 를 관점으로 코드를 바라봤다. 특히, 경험적인 부분이 많이 반영이 되기도 했다.회사 내에서 금액은 보통 대부분의 도메인이 회사 내부적으로 금액을 우리가 어떻게 처리 할 것인가로 처리를 하게 되는 상황이 많았다. 예를 들어, 소수점은 없앤다거나, 100원 단위는 절삭한다. 같은 비즈니스 요구사항이 다음과 같은 예이다.적절하게 책임을 가질 수 있는 객체로 분리하고 코드를 작성했다. 미션에서 검증해야 할 요구사항은 객체의 생명주기에 따른 사전 조건에서 검증을 해야 된다고 생각하고 생성자에서 검증을 하도록 하였다.하지만, 그 부분에서 궁금증이 생긴 부분이 있었다. "어떠한 경우에는 객체에게 지금 나의 상태에 대해서 물어 볼 상황이 생길 수도 있지 않을까? 그렇다면, 그 경우는 언제지?"명확한 기준이 잡혀있지 않았고, 해당 내용을 우빈님에게 질문을 하게 되었다.다음과 같은 내용으로 질문을 드렸고, 우빈님께서 '유효성 검증', '상태 확인'의 개념을 분리하는 것이 좋을 것 같다는 피드백을 주셨습니다. 말씀을 듣고 보니 결국 이것도 비즈니스 요구사항에 따라서 도메인의 생명 주기가 어떻게 이루어져야하는가에 따라서 달라질 수 있다는 결론을 내렸습니다.아쉬운 점totalPrice를 계산하는 부분을 메서드로 호출 할 때마다 연산하는 형태로 구현을 해두었습니다.return orderItems.stream() .map(OrderItem::getMoney) .reduce(Money.from(BigDecimal.ZERO), Money::add)호출 할 때마다 매번 연산 로직이 수행 될 것입니다. 추후에는 totalPrice가 coupon에 적용되는 비율이 달라지는 등 여러 형태로도 사용 될 수 있겠다는 생각이 들었어요. 아래와 같은 방법으로 하는 것은 어땠을까? 라는 생각이 들었습니다.private Order(List orderItems, ...) { this.totalPrice = calcTotalPrice(orderItems); } private Money calcTotalPrice(List orderItems) { return orderItems.stream() .map(OrderItem::getMoney) .reduce(Money.from(BigDecimal.ZERO), Money::add) }위와 같이 작성한다면 객체의 생성 시점에 한 번 totalPrice를 생성하고 여러 방면으로 재활용 할 수 있지 않을까 라는 생각이 들었습니다.결론다음 미션도 너무나도 기대되고, 해당 스터디를 하게 되어 정말 다행이라고 생각하고 있습니다.한 가지 아쉬운 점은 스터디의 의존도가 멘토 분들에게 많이 의존되어 있다는 생각이 들었습니다. 같이 스터디를 하는 많은 분들이 많은 의견을 공유 할 수 있는 환경이 되었으면 좋겠다? 라는 개인적인 아쉬움이 조금 있었네요.
백엔드
・
발자국
・
클린코드